tinymist_query/syntax/
expr.rs

1use std::ops::DerefMut;
2
3use parking_lot::Mutex;
4use rpds::RedBlackTreeMapSync;
5use rustc_hash::FxHashMap;
6use std::ops::Deref;
7use tinymist_analysis::adt::interner::Interned;
8use tinymist_std::hash::hash128;
9use typst::{
10    foundations::{Element, NativeElement, Type, Value},
11    model::{EmphElem, EnumElem, HeadingElem, ListElem, ParbreakElem, StrongElem, TermsElem},
12    syntax::{Span, SyntaxNode, ast::MathTextKind},
13    text::LinebreakElem,
14    utils::LazyHash,
15};
16
17use crate::{
18    analysis::{QueryStatGuard, SharedContext},
19    docs::DocString,
20    prelude::*,
21    syntax::{DefKind, find_module_level_docs, resolve_id_by_path},
22    ty::{BuiltinTy, InsTy, Ty},
23};
24
25use super::{DocCommentMatcher, InterpretMode, def::*};
26
27/// Maps file identifiers to their lexical scopes for expression analysis
28/// routing.
29pub type ExprRoute = FxHashMap<TypstFileId, Option<Arc<LazyHash<LexicalScope>>>>;
30
31/// Analyzes expressions in a source file and produces expression information.
32///
33/// Processes the source file to extract expression analysis data including
34/// resolves, imports, docstrings, and lexical scoping information.
35#[typst_macros::time(span = source.root().span())]
36pub(crate) fn expr_of(
37    ctx: Arc<SharedContext>,
38    source: Source,
39    route: &mut ExprRoute,
40    guard: QueryStatGuard,
41    prev: Option<ExprInfo>,
42) -> ExprInfo {
43    crate::log_debug_ct!("expr_of: {:?}", source.id());
44
45    route.insert(source.id(), None);
46
47    let cache_hit = prev.and_then(|prev| {
48        if prev.source.len_bytes() != source.len_bytes()
49            || hash128(&prev.source) != hash128(&source)
50        {
51            return None;
52        }
53        for (fid, prev_exports) in &prev.imports {
54            let ei = ctx.exports_of(&ctx.source_by_id(*fid).ok()?, route);
55
56            // If there is a cycle, the expression will be stable as the source is
57            // unchanged.
58            if let Some(exports) = ei
59                && (prev_exports.size() != exports.size()
60                    || hash128(&prev_exports) != hash128(&exports))
61            {
62                return None;
63            }
64        }
65
66        Some(prev)
67    });
68
69    if let Some(prev) = cache_hit {
70        route.remove(&source.id());
71        return prev;
72    }
73    guard.miss();
74
75    let revision = ctx.revision();
76
77    let resolves_base = Arc::new(Mutex::new(vec![]));
78    let resolves = resolves_base.clone();
79
80    // todo: cache docs capture
81    let docstrings_base = Arc::new(Mutex::new(FxHashMap::default()));
82    let docstrings = docstrings_base.clone();
83
84    let exprs_base = Arc::new(Mutex::new(FxHashMap::default()));
85    let exprs = exprs_base.clone();
86
87    let imports_base = Arc::new(Mutex::new(FxHashMap::default()));
88    let imports = imports_base.clone();
89
90    let module_docstring = find_module_level_docs(&source)
91        .and_then(|docs| ctx.compute_docstring(source.id(), docs, DefKind::Module))
92        .unwrap_or_default();
93
94    let (exports, root) = {
95        let mut w = ExprWorker {
96            fid: source.id(),
97            ctx,
98            imports,
99            docstrings,
100            exprs,
101            import_buffer: Vec::new(),
102            lexical: LexicalContext::default(),
103            resolves,
104            buffer: vec![],
105            init_stage: true,
106            comment_matcher: DocCommentMatcher::default(),
107            route,
108        };
109
110        let root = source.root().cast::<ast::Markup>().unwrap();
111        w.check_root_scope(root.to_untyped().children());
112        let root_scope = Arc::new(LazyHash::new(w.summarize_scope()));
113        w.route.insert(w.fid, Some(root_scope.clone()));
114
115        w.lexical = LexicalContext::default();
116        w.comment_matcher.reset();
117        w.buffer.clear();
118        w.import_buffer.clear();
119        let root = w.check_in_mode(root.to_untyped().children(), InterpretMode::Markup);
120        let root_scope = Arc::new(LazyHash::new(w.summarize_scope()));
121
122        w.collect_buffer();
123        (root_scope, root)
124    };
125
126    let info = ExprInfoRepr {
127        fid: source.id(),
128        revision,
129        source: source.clone(),
130        resolves: HashMap::from_iter(std::mem::take(resolves_base.lock().deref_mut())),
131        module_docstring,
132        docstrings: std::mem::take(docstrings_base.lock().deref_mut()),
133        imports: HashMap::from_iter(std::mem::take(imports_base.lock().deref_mut())),
134        exports,
135        exprs: std::mem::take(exprs_base.lock().deref_mut()),
136        root,
137    };
138    crate::log_debug_ct!("expr_of end {:?}", source.id());
139
140    route.remove(&info.fid);
141    ExprInfo::new(info)
142}
143
144type ConcolicExpr = (Option<Expr>, Option<Ty>);
145type ResolveVec = Vec<(Span, Interned<RefExpr>)>;
146type SyntaxNodeChildren<'a> = std::slice::Iter<'a, SyntaxNode>;
147
148#[derive(Debug, Clone)]
149struct LexicalContext {
150    mode: InterpretMode,
151    scopes: EcoVec<ExprScope>,
152    last: ExprScope,
153}
154
155impl Default for LexicalContext {
156    fn default() -> Self {
157        LexicalContext {
158            mode: InterpretMode::Markup,
159            scopes: eco_vec![],
160            last: ExprScope::Lexical(RedBlackTreeMapSync::default()),
161        }
162    }
163}
164
165/// Worker for processing expressions during source file analysis.
166pub(crate) struct ExprWorker<'a> {
167    fid: TypstFileId,
168    ctx: Arc<SharedContext>,
169    imports: Arc<Mutex<FxHashMap<TypstFileId, Arc<LazyHash<LexicalScope>>>>>,
170    import_buffer: Vec<(TypstFileId, Arc<LazyHash<LexicalScope>>)>,
171    docstrings: Arc<Mutex<FxHashMap<DeclExpr, Arc<DocString>>>>,
172    exprs: Arc<Mutex<FxHashMap<Span, Expr>>>,
173    resolves: Arc<Mutex<ResolveVec>>,
174    buffer: ResolveVec,
175    lexical: LexicalContext,
176    init_stage: bool,
177
178    route: &'a mut ExprRoute,
179    comment_matcher: DocCommentMatcher,
180}
181
182impl ExprWorker<'_> {
183    fn with_scope<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
184        self.lexical.scopes.push(std::mem::replace(
185            &mut self.lexical.last,
186            ExprScope::empty(),
187        ));
188        let len = self.lexical.scopes.len();
189        let result = f(self);
190        self.lexical.scopes.truncate(len);
191        self.lexical.last = self.lexical.scopes.pop().unwrap();
192        result
193    }
194
195    fn push_scope(&mut self, scope: ExprScope) {
196        let last = std::mem::replace(&mut self.lexical.last, scope);
197        if !last.is_empty() {
198            self.lexical.scopes.push(last);
199        }
200    }
201
202    #[must_use]
203    fn scope_mut(&mut self) -> &mut LexicalScope {
204        if matches!(self.lexical.last, ExprScope::Lexical(_)) {
205            return self.lexical_scope_unchecked();
206        }
207        self.lexical.scopes.push(std::mem::replace(
208            &mut self.lexical.last,
209            ExprScope::empty(),
210        ));
211        self.lexical_scope_unchecked()
212    }
213
214    fn lexical_scope_unchecked(&mut self) -> &mut LexicalScope {
215        let scope = &mut self.lexical.last;
216        if let ExprScope::Lexical(scope) = scope {
217            scope
218        } else {
219            unreachable!()
220        }
221    }
222
223    fn check_docstring(&mut self, decl: &DeclExpr, docs: Option<String>, kind: DefKind) {
224        if let Some(docs) = docs {
225            let docstring = self.ctx.compute_docstring(self.fid, docs, kind);
226            if let Some(docstring) = docstring {
227                self.docstrings.lock().insert(decl.clone(), docstring);
228            }
229        }
230    }
231
232    fn summarize_scope(&self) -> LexicalScope {
233        let mut exports = LexicalScope::default();
234        for scope in std::iter::once(&self.lexical.last).chain(self.lexical.scopes.iter()) {
235            scope.merge_into(&mut exports);
236        }
237        exports
238    }
239
240    fn check(&mut self, m: ast::Expr) -> Expr {
241        let s = m.span();
242        let ret = self.do_check(m);
243        self.exprs.lock().insert(s, ret.clone());
244        ret
245    }
246
247    fn do_check(&mut self, m: ast::Expr) -> Expr {
248        use ast::Expr::*;
249        match m {
250            None(_) => Expr::Type(Ty::Builtin(BuiltinTy::None)),
251            Auto(..) => Expr::Type(Ty::Builtin(BuiltinTy::Auto)),
252            Bool(bool) => Expr::Type(Ty::Value(InsTy::new(Value::Bool(bool.get())))),
253            Int(int) => Expr::Type(Ty::Value(InsTy::new(Value::Int(int.get())))),
254            Float(float) => Expr::Type(Ty::Value(InsTy::new(Value::Float(float.get())))),
255            Numeric(numeric) => Expr::Type(Ty::Value(InsTy::new(Value::numeric(numeric.get())))),
256            Str(s) => Expr::Type(Ty::Value(InsTy::new(Value::Str(s.get().into())))),
257
258            Equation(equation) => self.check_math(equation.body().to_untyped().children()),
259            Math(math) => self.check_math(math.to_untyped().children()),
260            Code(code_block) => self.check_code(code_block.body()),
261            Content(content_block) => self.check_markup(content_block.body()),
262
263            Ident(ident) => self.check_ident(ident),
264            MathIdent(math_ident) => self.check_math_ident(math_ident),
265            Label(label) => self.check_label(label),
266            Ref(ref_node) => self.check_ref(ref_node),
267
268            Let(let_binding) => self.check_let(let_binding),
269            Closure(closure) => self.check_closure(closure),
270            Import(module_import) => self.check_module_import(module_import),
271            Include(module_include) => self.check_module_include(module_include),
272
273            Parenthesized(paren_expr) => self.check(paren_expr.expr()),
274            Array(array) => self.check_array(array),
275            Dict(dict) => self.check_dict(dict),
276            Unary(unary) => self.check_unary(unary),
277            Binary(binary) => self.check_binary(binary),
278            FieldAccess(field_access) => self.check_field_access(field_access),
279            FuncCall(func_call) => self.check_func_call(func_call),
280            DestructAssign(destruct_assignment) => self.check_destruct_assign(destruct_assignment),
281            Set(set_rule) => self.check_set(set_rule),
282            Show(show_rule) => self.check_show(show_rule),
283            Contextual(contextual) => {
284                Expr::Unary(UnInst::new(UnaryOp::Context, self.defer(contextual.body())))
285            }
286            Conditional(conditional) => self.check_conditional(conditional),
287            While(while_loop) => self.check_while_loop(while_loop),
288            For(for_loop) => self.check_for_loop(for_loop),
289            Break(..) => Expr::Type(Ty::Builtin(BuiltinTy::Break)),
290            Continue(..) => Expr::Type(Ty::Builtin(BuiltinTy::Continue)),
291            Return(func_return) => Expr::Unary(UnInst::new(
292                UnaryOp::Return,
293                func_return
294                    .body()
295                    .map_or_else(none_expr, |body| self.check(body)),
296            )),
297
298            Text(..) => Expr::Type(Ty::Builtin(BuiltinTy::Content(Some(Element::of::<
299                typst::text::TextElem,
300            >())))),
301            MathText(t) => Expr::Type(Ty::Builtin(BuiltinTy::Content(Some({
302                match t.get() {
303                    MathTextKind::Character(..) => Element::of::<typst::foundations::SymbolElem>(),
304                    MathTextKind::Number(..) => Element::of::<typst::foundations::SymbolElem>(),
305                }
306            })))),
307            Raw(..) => Expr::Type(Ty::Builtin(BuiltinTy::Content(Some(Element::of::<
308                typst::text::RawElem,
309            >())))),
310            Link(..) => Expr::Type(Ty::Builtin(BuiltinTy::Content(Some(Element::of::<
311                typst::model::LinkElem,
312            >())))),
313            Space(..) => Expr::Type(Ty::Builtin(BuiltinTy::Space)),
314            Linebreak(..) => Expr::Type(Ty::Builtin(BuiltinTy::Content(Some(Element::of::<
315                LinebreakElem,
316            >())))),
317            Parbreak(..) => Expr::Type(Ty::Builtin(BuiltinTy::Content(Some(Element::of::<
318                ParbreakElem,
319            >())))),
320            Escape(..) => Expr::Type(Ty::Builtin(BuiltinTy::Content(Some(Element::of::<
321                typst::text::TextElem,
322            >())))),
323            Shorthand(..) => Expr::Type(Ty::Builtin(BuiltinTy::Type(Type::of::<
324                typst::foundations::Symbol,
325            >()))),
326            SmartQuote(..) => Expr::Type(Ty::Builtin(BuiltinTy::Content(Some(Element::of::<
327                typst::text::SmartQuoteElem,
328            >())))),
329
330            Strong(strong) => {
331                let body = self.check_inline_markup(strong.body());
332                self.check_element::<StrongElem>(eco_vec![body])
333            }
334            Emph(emph) => {
335                let body = self.check_inline_markup(emph.body());
336                self.check_element::<EmphElem>(eco_vec![body])
337            }
338            Heading(heading) => {
339                let body = self.check_markup(heading.body());
340                self.check_element::<HeadingElem>(eco_vec![body])
341            }
342            List(item) => {
343                let body = self.check_markup(item.body());
344                self.check_element::<ListElem>(eco_vec![body])
345            }
346            Enum(item) => {
347                let body = self.check_markup(item.body());
348                self.check_element::<EnumElem>(eco_vec![body])
349            }
350            Term(item) => {
351                let term = self.check_markup(item.term());
352                let description = self.check_markup(item.description());
353                self.check_element::<TermsElem>(eco_vec![term, description])
354            }
355
356            MathAlignPoint(..) => Expr::Type(Ty::Builtin(BuiltinTy::Content(Some(Element::of::<
357                typst::math::AlignPointElem,
358            >(
359            ))))),
360            MathShorthand(..) => Expr::Type(Ty::Builtin(BuiltinTy::Type(Type::of::<
361                typst::foundations::Symbol,
362            >()))),
363            MathDelimited(math_delimited) => {
364                self.check_math(math_delimited.body().to_untyped().children())
365            }
366            MathAttach(attach) => {
367                let base = attach.base().to_untyped().clone();
368                let bottom = attach.bottom().unwrap_or_default().to_untyped().clone();
369                let top = attach.top().unwrap_or_default().to_untyped().clone();
370                self.check_math([base, bottom, top].iter())
371            }
372            MathPrimes(..) => Expr::Type(Ty::Builtin(BuiltinTy::None)),
373            MathFrac(frac) => {
374                let num = frac.num().to_untyped().clone();
375                let denom = frac.denom().to_untyped().clone();
376                self.check_math([num, denom].iter())
377            }
378            MathRoot(root) => self.check(root.radicand()),
379        }
380    }
381
382    fn check_label(&mut self, label: ast::Label) -> Expr {
383        Expr::Decl(Decl::label(label.get(), label.span()).into())
384    }
385
386    fn check_element<T: NativeElement>(&mut self, content: EcoVec<Expr>) -> Expr {
387        let elem = Element::of::<T>();
388        Expr::Element(ElementExpr { elem, content }.into())
389    }
390
391    fn check_let(&mut self, typed: ast::LetBinding) -> Expr {
392        match typed.kind() {
393            ast::LetBindingKind::Closure(..) => {
394                typed.init().map_or_else(none_expr, |expr| self.check(expr))
395            }
396            ast::LetBindingKind::Normal(pat) => {
397                let docs = self.comment_matcher.collect();
398                // Check init expression before pattern checking
399                let body = typed.init().map(|init| self.defer(init));
400
401                let span = pat.span();
402                let decl = Decl::pattern(span).into();
403                self.check_docstring(&decl, docs, DefKind::Variable);
404                let pattern = self.check_pattern(pat);
405                Expr::Let(Interned::new(LetExpr {
406                    span,
407                    pattern,
408                    body,
409                }))
410            }
411        }
412    }
413
414    fn check_closure(&mut self, typed: ast::Closure) -> Expr {
415        let docs = self.comment_matcher.collect();
416        let decl = match typed.name() {
417            Some(name) => Decl::func(name).into(),
418            None => Decl::closure(typed.span()).into(),
419        };
420        self.check_docstring(&decl, docs, DefKind::Function);
421        self.resolve_as(Decl::as_def(&decl, None));
422
423        let (params, body) = self.with_scope(|this| {
424            this.scope_mut()
425                .insert_mut(decl.name().clone(), decl.clone().into());
426            let mut inputs = eco_vec![];
427            let mut names = eco_vec![];
428            let mut spread_left = None;
429            let mut spread_right = None;
430            for arg in typed.params().children() {
431                match arg {
432                    ast::Param::Pos(arg) => {
433                        inputs.push(this.check_pattern(arg));
434                    }
435                    ast::Param::Named(arg) => {
436                        let key: DeclExpr = Decl::var(arg.name()).into();
437                        let val = Pattern::Expr(this.check(arg.expr())).into();
438                        names.push((key.clone(), val));
439
440                        this.resolve_as(Decl::as_def(&key, None));
441                        this.scope_mut().insert_mut(key.name().clone(), key.into());
442                    }
443                    ast::Param::Spread(s) => {
444                        let decl: DeclExpr = if let Some(ident) = s.sink_ident() {
445                            Decl::var(ident).into()
446                        } else {
447                            Decl::spread(s.span()).into()
448                        };
449
450                        let spread = Pattern::Expr(this.check(s.expr())).into();
451                        if inputs.is_empty() {
452                            spread_left = Some((decl.clone(), spread));
453                        } else {
454                            spread_right = Some((decl.clone(), spread));
455                        }
456
457                        this.resolve_as(Decl::as_def(&decl, None));
458                        this.scope_mut()
459                            .insert_mut(decl.name().clone(), decl.into());
460                    }
461                }
462            }
463
464            if inputs.is_empty() {
465                spread_right = spread_left.take();
466            }
467
468            let pattern = PatternSig {
469                pos: inputs,
470                named: names,
471                spread_left,
472                spread_right,
473            };
474
475            (pattern, this.defer(typed.body()))
476        });
477
478        self.scope_mut()
479            .insert_mut(decl.name().clone(), decl.clone().into());
480        Expr::Func(FuncExpr { decl, params, body }.into())
481    }
482
483    fn check_pattern(&mut self, typed: ast::Pattern) -> Interned<Pattern> {
484        match typed {
485            ast::Pattern::Normal(expr) => self.check_pattern_expr(expr),
486            ast::Pattern::Placeholder(..) => Pattern::Expr(Expr::Star).into(),
487            ast::Pattern::Parenthesized(paren_expr) => self.check_pattern(paren_expr.pattern()),
488            ast::Pattern::Destructuring(destructing) => {
489                let mut inputs = eco_vec![];
490                let mut names = eco_vec![];
491                let mut spread_left = None;
492                let mut spread_right = None;
493
494                for item in destructing.items() {
495                    match item {
496                        ast::DestructuringItem::Pattern(pos) => {
497                            inputs.push(self.check_pattern(pos));
498                        }
499                        ast::DestructuringItem::Named(named) => {
500                            let key = Decl::var(named.name()).into();
501                            let val = self.check_pattern_expr(named.expr());
502                            names.push((key, val));
503                        }
504                        ast::DestructuringItem::Spread(spreading) => {
505                            let decl: DeclExpr = if let Some(ident) = spreading.sink_ident() {
506                                Decl::var(ident).into()
507                            } else {
508                                Decl::spread(spreading.span()).into()
509                            };
510
511                            if inputs.is_empty() {
512                                spread_left =
513                                    Some((decl, self.check_pattern_expr(spreading.expr())));
514                            } else {
515                                spread_right =
516                                    Some((decl, self.check_pattern_expr(spreading.expr())));
517                            }
518                        }
519                    }
520                }
521
522                if inputs.is_empty() {
523                    spread_right = spread_left.take();
524                }
525
526                let pattern = PatternSig {
527                    pos: inputs,
528                    named: names,
529                    spread_left,
530                    spread_right,
531                };
532
533                Pattern::Sig(Box::new(pattern)).into()
534            }
535        }
536    }
537
538    fn check_pattern_expr(&mut self, typed: ast::Expr) -> Interned<Pattern> {
539        match typed {
540            ast::Expr::Ident(ident) => {
541                let decl = Decl::var(ident).into();
542                self.resolve_as(Decl::as_def(&decl, None));
543                self.scope_mut()
544                    .insert_mut(decl.name().clone(), decl.clone().into());
545                Pattern::Simple(decl).into()
546            }
547            ast::Expr::Parenthesized(parenthesized) => self.check_pattern(parenthesized.pattern()),
548            _ => Pattern::Expr(self.check(typed)).into(),
549        }
550    }
551
552    fn check_module_import(&mut self, typed: ast::ModuleImport) -> Expr {
553        let is_wildcard_import = matches!(typed.imports(), Some(ast::Imports::Wildcard));
554
555        let source = typed.source();
556        let mod_expr = self.check_import(typed.source(), true, is_wildcard_import);
557        crate::log_debug_ct!("checking import: {source:?} => {mod_expr:?}");
558
559        let mod_var = typed.new_name().map(Decl::module_alias).or_else(|| {
560            typed.imports().is_none().then(|| {
561                let name = match mod_expr.as_ref()? {
562                    Expr::Decl(decl) if matches!(decl.as_ref(), Decl::Module { .. }) => {
563                        decl.name().clone()
564                    }
565                    _ => return None,
566                };
567                // todo: package stem
568                Some(Decl::path_stem(source.to_untyped().clone(), name))
569            })?
570        });
571
572        let creating_mod_var = mod_var.is_some();
573        let mod_var = Interned::new(mod_var.unwrap_or_else(|| Decl::module_import(typed.span())));
574        let mod_ref = RefExpr {
575            decl: mod_var.clone(),
576            step: mod_expr.clone(),
577            root: mod_expr.clone(),
578            term: None,
579        };
580        crate::log_debug_ct!("create import variable: {mod_ref:?}");
581        let mod_ref = Interned::new(mod_ref);
582        if creating_mod_var {
583            self.scope_mut()
584                .insert_mut(mod_var.name().clone(), Expr::Ref(mod_ref.clone()));
585        }
586
587        self.resolve_as(mod_ref.clone());
588
589        let fid = mod_expr.as_ref().and_then(|mod_expr| match mod_expr {
590            Expr::Type(Ty::Value(v)) => match &v.val {
591                Value::Module(m) => m.file_id(),
592                _ => None,
593            },
594            Expr::Decl(decl) => {
595                if matches!(decl.as_ref(), Decl::Module { .. }) {
596                    decl.file_id()
597                } else {
598                    None
599                }
600            }
601            _ => None,
602        });
603
604        // Prefetch Type Check Information
605        if let Some(fid) = fid {
606            crate::log_debug_ct!("prefetch type check: {fid:?}");
607            self.ctx.prefetch_type_check(fid);
608        }
609
610        let scope = if let Some(fid) = &fid {
611            Some(ExprScope::Lexical(self.exports_of(*fid)))
612        } else {
613            match &mod_expr {
614                Some(Expr::Type(Ty::Value(v))) => match &v.val {
615                    Value::Module(m) => Some(ExprScope::Module(m.clone())),
616                    Value::Func(func) => {
617                        if func.scope().is_some() {
618                            Some(ExprScope::Func(func.clone()))
619                        } else {
620                            None
621                        }
622                    }
623                    Value::Type(s) => Some(ExprScope::Type(*s)),
624                    _ => None,
625                },
626                _ => None,
627            }
628        };
629
630        let scope = if let Some(scope) = scope {
631            scope
632        } else {
633            log::warn!(
634                "cannot analyze import on: {typed:?}, expr {mod_expr:?}, in file {:?}",
635                typed.span().id()
636            );
637            ExprScope::empty()
638        };
639
640        if let Some(imports) = typed.imports() {
641            match imports {
642                ast::Imports::Wildcard => {
643                    crate::log_debug_ct!("checking wildcard: {mod_expr:?}");
644                    self.push_scope(scope);
645                }
646                ast::Imports::Items(items) => {
647                    let module = Expr::Decl(mod_var.clone());
648                    self.import_decls(&scope, module, items);
649                }
650            }
651        };
652
653        Expr::Import(ImportExpr { decl: mod_ref }.into())
654    }
655
656    fn check_import(
657        &mut self,
658        source: ast::Expr,
659        is_import: bool,
660        is_wildcard_import: bool,
661    ) -> Option<Expr> {
662        let src = self.eval_expr(source, InterpretMode::Code);
663        let src_expr = self.fold_expr_and_val(src).or_else(|| {
664            self.ctx
665                .analyze_expr(source.to_untyped())
666                .into_iter()
667                .find_map(|(v, _)| match v {
668                    Value::Str(s) => Some(Expr::Type(Ty::Value(InsTy::new(Value::Str(s))))),
669                    _ => None,
670                })
671        })?;
672
673        crate::log_debug_ct!("checking import source: {src_expr:?}");
674        let const_res = match &src_expr {
675            Expr::Type(Ty::Value(val)) => {
676                self.check_import_source_val(source, &val.val, Some(&src_expr), is_import)
677            }
678            Expr::Decl(decl) if matches!(decl.as_ref(), Decl::Module { .. }) => {
679                return Some(src_expr.clone());
680            }
681
682            _ => None,
683        };
684        const_res
685            .or_else(|| self.check_import_by_def(&src_expr))
686            .or_else(|| is_wildcard_import.then(|| self.check_import_dyn(source, &src_expr))?)
687    }
688
689    fn check_import_dyn(&mut self, source: ast::Expr, src_expr: &Expr) -> Option<Expr> {
690        let src_or_module = self.ctx.analyze_import(source.to_untyped());
691        crate::log_debug_ct!("checking import source dyn: {src_or_module:?}");
692
693        match src_or_module {
694            (_, Some(Value::Module(m))) => {
695                // todo: dyn resolve src_expr
696                match m.file_id() {
697                    Some(fid) => Some(Expr::Decl(
698                        Decl::module(m.name().unwrap().into(), fid).into(),
699                    )),
700                    None => Some(Expr::Type(Ty::Value(InsTy::new(Value::Module(m))))),
701                }
702            }
703            (_, Some(v)) => Some(Expr::Type(Ty::Value(InsTy::new(v)))),
704            (Some(s), _) => self.check_import_source_val(source, &s, Some(src_expr), true),
705            (None, None) => None,
706        }
707    }
708
709    fn check_import_source_val(
710        &mut self,
711        source: ast::Expr,
712        src: &Value,
713        src_expr: Option<&Expr>,
714        is_import: bool,
715    ) -> Option<Expr> {
716        match &src {
717            _ if src.scope().is_some() => src_expr
718                .cloned()
719                .or_else(|| Some(Expr::Type(Ty::Value(InsTy::new(src.clone()))))),
720            Value::Str(s) => self.check_import_by_str(source, s.as_str(), is_import),
721            _ => None,
722        }
723    }
724
725    fn check_import_by_str(
726        &mut self,
727        source: ast::Expr,
728        src: &str,
729        is_import: bool,
730    ) -> Option<Expr> {
731        let fid = resolve_id_by_path(&self.ctx.world, self.fid, src)?;
732        let name = Decl::calc_path_stem(src);
733        let module = Expr::Decl(Decl::module(name.clone(), fid).into());
734
735        let import_path = if is_import {
736            Decl::import_path(source.span(), name)
737        } else {
738            Decl::include_path(source.span(), name)
739        };
740
741        let ref_expr = RefExpr {
742            decl: import_path.into(),
743            step: Some(module.clone()),
744            root: Some(module.clone()),
745            term: None,
746        };
747        self.resolve_as(ref_expr.into());
748        Some(module)
749    }
750
751    fn check_import_by_def(&mut self, src_expr: &Expr) -> Option<Expr> {
752        match src_expr {
753            Expr::Decl(m) if matches!(m.kind(), DefKind::Module) => Some(src_expr.clone()),
754            Expr::Ref(r) => r.root.clone(),
755            _ => None,
756        }
757    }
758
759    fn import_decls(&mut self, scope: &ExprScope, module: Expr, items: ast::ImportItems) {
760        crate::log_debug_ct!("import scope {scope:?}");
761
762        for item in items.iter() {
763            let (path_ast, old, rename) = match item {
764                ast::ImportItem::Simple(path) => {
765                    let old: DeclExpr = Decl::import(path.name()).into();
766                    (path, old, None)
767                }
768                ast::ImportItem::Renamed(renamed) => {
769                    let path = renamed.path();
770                    let old: DeclExpr = Decl::import(path.name()).into();
771                    let new: DeclExpr = Decl::import_alias(renamed.new_name()).into();
772                    (path, old, Some(new))
773                }
774            };
775
776            let mut path = Vec::with_capacity(1);
777            for seg in path_ast.iter() {
778                let seg = Interned::new(Decl::ident_ref(seg));
779                path.push(seg);
780            }
781            // todo: import path
782            let (mut root, val) = match path.last().map(|decl| decl.name()) {
783                Some(name) => scope.get(name),
784                None => (None, None),
785            };
786
787            crate::log_debug_ct!("path {path:?} -> {root:?} {val:?}");
788            if root.is_none() && val.is_none() {
789                let mut sel = module.clone();
790                for seg in path.into_iter() {
791                    sel = Expr::Select(SelectExpr::new(seg, sel));
792                }
793                root = Some(sel)
794            }
795
796            let (root, step) = extract_ref(root);
797            let mut ref_expr = Interned::new(RefExpr {
798                decl: old.clone(),
799                root,
800                step,
801                term: val,
802            });
803            self.resolve_as(ref_expr.clone());
804
805            if let Some(new) = &rename {
806                ref_expr = Interned::new(RefExpr {
807                    decl: new.clone(),
808                    root: ref_expr.root.clone(),
809                    step: Some(ref_expr.decl.clone().into()),
810                    term: ref_expr.term.clone(),
811                });
812                self.resolve_as(ref_expr.clone());
813            }
814
815            // final resolves
816            let name = rename.as_ref().unwrap_or(&old).name().clone();
817            let expr = Expr::Ref(ref_expr);
818            self.scope_mut().insert_mut(name, expr.clone());
819        }
820    }
821
822    fn check_module_include(&mut self, typed: ast::ModuleInclude) -> Expr {
823        let _mod_expr = self.check_import(typed.source(), false, false);
824        let source = self.check(typed.source());
825        Expr::Include(IncludeExpr { source }.into())
826    }
827
828    fn check_array(&mut self, typed: ast::Array) -> Expr {
829        let mut items = vec![];
830        for item in typed.items() {
831            match item {
832                ast::ArrayItem::Pos(item) => {
833                    items.push(ArgExpr::Pos(self.check(item)));
834                }
835                ast::ArrayItem::Spread(s) => {
836                    items.push(ArgExpr::Spread(self.check(s.expr())));
837                }
838            }
839        }
840
841        Expr::Array(ArgsExpr::new(typed.span(), items))
842    }
843
844    fn check_dict(&mut self, typed: ast::Dict) -> Expr {
845        let mut items = vec![];
846        for item in typed.items() {
847            match item {
848                ast::DictItem::Named(item) => {
849                    let key = Decl::ident_ref(item.name()).into();
850                    let val = self.check(item.expr());
851                    items.push(ArgExpr::Named(Box::new((key, val))));
852                }
853                ast::DictItem::Keyed(item) => {
854                    let val = self.check(item.expr());
855                    let key = item.key();
856                    let analyzed = self.const_eval_expr(key);
857                    let analyzed = match &analyzed {
858                        Some(Value::Str(s)) => Some(s),
859                        _ => None,
860                    };
861                    let Some(analyzed) = analyzed else {
862                        let key = self.check(key);
863                        items.push(ArgExpr::NamedRt(Box::new((key, val))));
864                        continue;
865                    };
866                    let key = Decl::str_name(key.to_untyped().clone(), analyzed).into();
867                    items.push(ArgExpr::Named(Box::new((key, val))));
868                }
869                ast::DictItem::Spread(s) => {
870                    items.push(ArgExpr::Spread(self.check(s.expr())));
871                }
872            }
873        }
874
875        Expr::Dict(ArgsExpr::new(typed.span(), items))
876    }
877
878    fn check_args(&mut self, typed: ast::Args) -> Expr {
879        let mut args = vec![];
880        for arg in typed.items() {
881            match arg {
882                ast::Arg::Pos(arg) => {
883                    args.push(ArgExpr::Pos(self.check(arg)));
884                }
885                ast::Arg::Named(arg) => {
886                    let key = Decl::ident_ref(arg.name()).into();
887                    let val = self.check(arg.expr());
888                    args.push(ArgExpr::Named(Box::new((key, val))));
889                }
890                ast::Arg::Spread(s) => {
891                    args.push(ArgExpr::Spread(self.check(s.expr())));
892                }
893            }
894        }
895        Expr::Args(ArgsExpr::new(typed.span(), args))
896    }
897
898    fn check_unary(&mut self, typed: ast::Unary) -> Expr {
899        let op = match typed.op() {
900            ast::UnOp::Pos => UnaryOp::Pos,
901            ast::UnOp::Neg => UnaryOp::Neg,
902            ast::UnOp::Not => UnaryOp::Not,
903        };
904        let lhs = self.check(typed.expr());
905        Expr::Unary(UnInst::new(op, lhs))
906    }
907
908    fn check_binary(&mut self, typed: ast::Binary) -> Expr {
909        let lhs = self.check(typed.lhs());
910        let rhs = self.check(typed.rhs());
911        Expr::Binary(BinInst::new(typed.op(), lhs, rhs))
912    }
913
914    fn check_destruct_assign(&mut self, typed: ast::DestructAssignment) -> Expr {
915        let pat = Expr::Pattern(self.check_pattern(typed.pattern()));
916        let val = self.check(typed.value());
917        let inst = BinInst::new(ast::BinOp::Assign, pat, val);
918        Expr::Binary(inst)
919    }
920
921    fn check_field_access(&mut self, typed: ast::FieldAccess) -> Expr {
922        let lhs = self.check(typed.target());
923        let key = Decl::ident_ref(typed.field()).into();
924        let span = typed.span();
925        Expr::Select(SelectExpr { lhs, key, span }.into())
926    }
927
928    fn check_func_call(&mut self, typed: ast::FuncCall) -> Expr {
929        let callee = self.check(typed.callee());
930        let args = self.check_args(typed.args());
931        let span = typed.span();
932        Expr::Apply(ApplyExpr { callee, args, span }.into())
933    }
934
935    fn check_set(&mut self, typed: ast::SetRule) -> Expr {
936        let target = self.check(typed.target());
937        let args = self.check_args(typed.args());
938        let cond = typed.condition().map(|cond| self.check(cond));
939        Expr::Set(SetExpr { target, args, cond }.into())
940    }
941
942    fn check_show(&mut self, typed: ast::ShowRule) -> Expr {
943        let selector = typed.selector().map(|selector| self.check(selector));
944        let edit = self.defer(typed.transform());
945        Expr::Show(ShowExpr { selector, edit }.into())
946    }
947
948    fn check_conditional(&mut self, typed: ast::Conditional) -> Expr {
949        let cond = self.check(typed.condition());
950        let then = self.defer(typed.if_body());
951        let else_ = typed
952            .else_body()
953            .map_or_else(none_expr, |expr| self.defer(expr));
954        Expr::Conditional(IfExpr { cond, then, else_ }.into())
955    }
956
957    fn check_while_loop(&mut self, typed: ast::WhileLoop) -> Expr {
958        let cond = self.check(typed.condition());
959        let body = self.defer(typed.body());
960        Expr::WhileLoop(WhileExpr { cond, body }.into())
961    }
962
963    fn check_for_loop(&mut self, typed: ast::ForLoop) -> Expr {
964        self.with_scope(|this| {
965            let pattern = this.check_pattern(typed.pattern());
966            let iter = this.check(typed.iterable());
967            let body = this.defer(typed.body());
968            Expr::ForLoop(
969                ForExpr {
970                    pattern,
971                    iter,
972                    body,
973                }
974                .into(),
975            )
976        })
977    }
978
979    fn check_inline_markup(&mut self, markup: ast::Markup) -> Expr {
980        self.check_in_mode(markup.to_untyped().children(), InterpretMode::Markup)
981    }
982
983    fn check_markup(&mut self, markup: ast::Markup) -> Expr {
984        self.with_scope(|this| this.check_inline_markup(markup))
985    }
986
987    fn check_code(&mut self, code: ast::Code) -> Expr {
988        self.with_scope(|this| {
989            this.check_in_mode(code.to_untyped().children(), InterpretMode::Code)
990        })
991    }
992
993    fn check_math(&mut self, children: SyntaxNodeChildren) -> Expr {
994        self.check_in_mode(children, InterpretMode::Math)
995    }
996
997    fn check_root_scope(&mut self, children: SyntaxNodeChildren) {
998        self.init_stage = true;
999        self.check_in_mode(children, InterpretMode::Markup);
1000        self.init_stage = false;
1001    }
1002
1003    fn check_in_mode(&mut self, children: SyntaxNodeChildren, mode: InterpretMode) -> Expr {
1004        let old_mode = self.lexical.mode;
1005        self.lexical.mode = mode;
1006
1007        // collect all comments before the definition
1008        self.comment_matcher.reset();
1009
1010        let mut items = Vec::with_capacity(4);
1011        for n in children {
1012            if let Some(expr) = n.cast::<ast::Expr>() {
1013                items.push(self.check(expr));
1014                self.comment_matcher.reset();
1015                continue;
1016            }
1017            if !self.init_stage && self.comment_matcher.process(n) {
1018                self.comment_matcher.reset();
1019            }
1020        }
1021
1022        self.lexical.mode = old_mode;
1023        Expr::Block(items.into())
1024    }
1025
1026    fn check_ref(&mut self, ref_node: ast::Ref) -> Expr {
1027        let ident = Interned::new(Decl::ref_(ref_node));
1028        let body = ref_node
1029            .supplement()
1030            .map(|block| self.check(ast::Expr::Content(block)));
1031        let ref_expr = ContentRefExpr {
1032            ident: ident.clone(),
1033            of: None,
1034            body,
1035        };
1036        self.resolve_as(
1037            RefExpr {
1038                decl: ident,
1039                step: None,
1040                root: None,
1041                term: None,
1042            }
1043            .into(),
1044        );
1045        Expr::ContentRef(ref_expr.into())
1046    }
1047
1048    fn check_ident(&mut self, ident: ast::Ident) -> Expr {
1049        self.resolve_ident(Decl::ident_ref(ident).into(), InterpretMode::Code)
1050    }
1051
1052    fn check_math_ident(&mut self, ident: ast::MathIdent) -> Expr {
1053        self.resolve_ident(Decl::math_ident_ref(ident).into(), InterpretMode::Math)
1054    }
1055
1056    fn resolve_as(&mut self, r: Interned<RefExpr>) {
1057        self.resolve_as_(r.decl.span(), r);
1058    }
1059
1060    fn resolve_as_(&mut self, s: Span, r: Interned<RefExpr>) {
1061        self.buffer.push((s, r.clone()));
1062    }
1063
1064    fn resolve_ident(&mut self, decl: DeclExpr, mode: InterpretMode) -> Expr {
1065        let r: Interned<RefExpr> = self.resolve_ident_(decl, mode).into();
1066        let s = r.decl.span();
1067        self.buffer.push((s, r.clone()));
1068        Expr::Ref(r)
1069    }
1070
1071    fn resolve_ident_(&mut self, decl: DeclExpr, mode: InterpretMode) -> RefExpr {
1072        let (step, val) = self.eval_ident(decl.name(), mode);
1073        let (root, step) = extract_ref(step);
1074
1075        RefExpr {
1076            decl,
1077            root,
1078            step,
1079            term: val,
1080        }
1081    }
1082
1083    fn defer(&mut self, expr: ast::Expr) -> Expr {
1084        if self.init_stage {
1085            Expr::Star
1086        } else {
1087            self.check(expr)
1088        }
1089    }
1090
1091    fn collect_buffer(&mut self) {
1092        let mut resolves = self.resolves.lock();
1093        resolves.extend(self.buffer.drain(..));
1094        drop(resolves);
1095        let mut imports = self.imports.lock();
1096        imports.extend(self.import_buffer.drain(..));
1097    }
1098
1099    fn const_eval_expr(&self, expr: ast::Expr) -> Option<Value> {
1100        SharedContext::const_eval(expr)
1101    }
1102
1103    fn eval_expr(&mut self, expr: ast::Expr, mode: InterpretMode) -> ConcolicExpr {
1104        if let Some(term) = self.const_eval_expr(expr) {
1105            return (None, Some(Ty::Value(InsTy::new(term))));
1106        }
1107        crate::log_debug_ct!("checking expr: {expr:?}");
1108
1109        match expr {
1110            ast::Expr::FieldAccess(field_access) => {
1111                let field = Decl::ident_ref(field_access.field());
1112
1113                let (expr, term) = self.eval_expr(field_access.target(), mode);
1114                let term = term.and_then(|v| {
1115                    // todo: use type select
1116                    // v.select(field.name()).ok()
1117                    match v {
1118                        Ty::Value(val) => {
1119                            Some(Ty::Value(InsTy::new(val.val.field(field.name(), ()).ok()?)))
1120                        }
1121                        _ => None,
1122                    }
1123                });
1124                let sel = expr.map(|expr| Expr::Select(SelectExpr::new(field.into(), expr)));
1125                (sel, term)
1126            }
1127            ast::Expr::Ident(ident) => {
1128                let expr_term = self.eval_ident(&ident.get().into(), mode);
1129                crate::log_debug_ct!("checking expr: {expr:?} -> res: {expr_term:?}");
1130                expr_term
1131            }
1132            _ => (None, None),
1133        }
1134    }
1135
1136    fn eval_ident(&self, name: &Interned<str>, mode: InterpretMode) -> ConcolicExpr {
1137        let res = self.lexical.last.get(name);
1138        if res.0.is_some() || res.1.is_some() {
1139            return res;
1140        }
1141
1142        for scope in self.lexical.scopes.iter().rev() {
1143            let res = scope.get(name);
1144            if res.0.is_some() || res.1.is_some() {
1145                return res;
1146            }
1147        }
1148
1149        let scope = match mode {
1150            InterpretMode::Math => self.ctx.world.library.math.scope(),
1151            InterpretMode::Markup | InterpretMode::Code => self.ctx.world.library.global.scope(),
1152            _ => return (None, None),
1153        };
1154
1155        let val = scope
1156            .get(name)
1157            .cloned()
1158            .map(|val| Ty::Value(InsTy::new(val.read().clone())));
1159        if let Some(val) = val {
1160            return (None, Some(val));
1161        }
1162
1163        if name.as_ref() == "std" {
1164            let val = Ty::Value(InsTy::new(self.ctx.world.library.std.read().clone()));
1165            return (None, Some(val));
1166        }
1167
1168        (None, None)
1169    }
1170
1171    fn fold_expr_and_val(&mut self, src: ConcolicExpr) -> Option<Expr> {
1172        crate::log_debug_ct!("folding cc: {src:?}");
1173        match src {
1174            (None, Some(val)) => Some(Expr::Type(val)),
1175            (expr, _) => self.fold_expr(expr),
1176        }
1177    }
1178
1179    fn fold_expr(&mut self, expr: Option<Expr>) -> Option<Expr> {
1180        crate::log_debug_ct!("folding cc: {expr:?}");
1181        match expr {
1182            Some(Expr::Decl(decl)) if !decl.is_def() => {
1183                crate::log_debug_ct!("folding decl: {decl:?}");
1184                let (x, y) = self.eval_ident(decl.name(), InterpretMode::Code);
1185                self.fold_expr_and_val((x, y))
1186            }
1187            Some(Expr::Ref(r)) => {
1188                crate::log_debug_ct!("folding ref: {r:?}");
1189                self.fold_expr_and_val((r.root.clone(), r.term.clone()))
1190            }
1191            Some(Expr::Select(r)) => {
1192                let lhs = self.fold_expr(Some(r.lhs.clone()));
1193                crate::log_debug_ct!("folding select: {r:?} ([{lhs:?}].[{:?}])", r.key);
1194                self.syntax_level_select(lhs?, &r.key, r.span)
1195            }
1196            Some(expr) => {
1197                crate::log_debug_ct!("folding expr: {expr:?}");
1198                Some(expr)
1199            }
1200            _ => None,
1201        }
1202    }
1203
1204    fn syntax_level_select(&mut self, lhs: Expr, key: &Interned<Decl>, span: Span) -> Option<Expr> {
1205        match &lhs {
1206            Expr::Decl(decl) => match decl.as_ref() {
1207                Decl::Module(module) => {
1208                    let exports = self.exports_of(module.fid);
1209                    let selected = exports.get(key.name())?;
1210                    let select_ref = Interned::new(RefExpr {
1211                        decl: key.clone(),
1212                        root: Some(lhs.clone()),
1213                        step: Some(selected.clone()),
1214                        term: None,
1215                    });
1216                    self.resolve_as(select_ref.clone());
1217                    self.resolve_as_(span, select_ref);
1218                    Some(selected.clone())
1219                }
1220                _ => None,
1221            },
1222            _ => None,
1223        }
1224    }
1225
1226    fn exports_of(&mut self, fid: TypstFileId) -> LexicalScope {
1227        let imported = self
1228            .ctx
1229            .source_by_id(fid)
1230            .ok()
1231            .and_then(|src| self.ctx.exports_of(&src, self.route))
1232            .unwrap_or_default();
1233        let res = imported.as_ref().deref().clone();
1234        self.import_buffer.push((fid, imported));
1235        res
1236    }
1237}
1238
1239fn extract_ref(step: Option<Expr>) -> (Option<Expr>, Option<Expr>) {
1240    match step {
1241        Some(Expr::Ref(r)) => (r.root.clone(), Some(r.decl.clone().into())),
1242        step => (step.clone(), step),
1243    }
1244}
1245
1246fn none_expr() -> Expr {
1247    Expr::Type(Ty::Builtin(BuiltinTy::None))
1248}
1249
1250#[cfg(test)]
1251mod tests {
1252    #[test]
1253    fn test_expr_size() {
1254        use super::*;
1255        assert!(size_of::<Expr>() <= size_of::<usize>() * 2);
1256    }
1257}