1use core::fmt;
4use std::{
5 collections::BTreeMap,
6 ops::{Deref, Range},
7 sync::Arc,
8};
9
10use rustc_hash::FxHashMap;
11use serde::{Deserialize, Serialize};
12use tinymist_derive::DeclEnum;
13use tinymist_std::DefId;
14use tinymist_world::package::PackageSpec;
15use typst::{
16 foundations::{Element, Func, Module, Type, Value},
17 syntax::{Span, SyntaxNode, VirtualRoot},
18 utils::LazyHash,
19};
20
21use crate::{
22 adt::interner::impl_internable,
23 docs::DocString,
24 prelude::*,
25 ty::{InsTy, Interned, SelectTy, Ty, TypeVar},
26};
27
28use super::{ExprDescriber, ExprPrinter};
29
30#[derive(Debug, Clone, Hash)]
35pub struct ExprInfo(Arc<LazyHash<ExprInfoRepr>>);
36
37impl ExprInfo {
38 pub fn new(repr: ExprInfoRepr) -> Self {
44 Self(Arc::new(LazyHash::new(repr)))
45 }
46}
47
48impl Deref for ExprInfo {
49 type Target = Arc<LazyHash<ExprInfoRepr>>;
50
51 fn deref(&self) -> &Self::Target {
52 &self.0
53 }
54}
55
56#[derive(Debug)]
61pub struct ExprInfoRepr {
62 pub fid: TypstFileId,
64 pub revision: usize,
66 pub source: Source,
68 pub root: Expr,
70 pub module_docstring: Arc<DocString>,
72 pub exports: Arc<LazyHash<LexicalScope>>,
74 pub imports: FxHashMap<TypstFileId, Arc<LazyHash<LexicalScope>>>,
76 pub exprs: FxHashMap<Span, Expr>,
78 pub resolves: FxHashMap<Span, Interned<RefExpr>>,
80 pub docstrings: FxHashMap<DeclExpr, Arc<DocString>>,
82 pub module_items: FxHashMap<Interned<Decl>, ModuleItemLayout>,
84}
85
86impl std::hash::Hash for ExprInfoRepr {
87 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
88 self.revision.hash(state);
91 self.source.hash(state);
92 self.root.hash(state);
93 self.exports.hash(state);
94 let mut resolves = self.resolves.iter().collect::<Vec<_>>();
95 resolves.sort_by_key(|(fid, _)| fid.into_raw());
96 resolves.hash(state);
97 let mut imports = self.imports.iter().collect::<Vec<_>>();
98 imports.sort_by_key(|(fid, _)| fid.into_raw());
99 imports.hash(state);
100 let mut module_items = self.module_items.iter().collect::<Vec<_>>();
101 module_items.sort_by_key(|(decl, _)| decl.span().into_raw());
102 module_items.hash(state);
103 }
104}
105
106impl ExprInfoRepr {
107 pub fn get_def(&self, decl: &Interned<Decl>) -> Option<Expr> {
109 if decl.is_def() {
110 return Some(Expr::Decl(decl.clone()));
111 }
112 let resolved = self.resolves.get(&decl.span())?;
113 Some(Expr::Ref(resolved.clone()))
114 }
115
116 pub fn get_refs(
118 &self,
119 decl: Interned<Decl>,
120 ) -> impl Iterator<Item = (&Span, &Interned<RefExpr>)> {
121 let of = Some(Expr::Decl(decl.clone()));
122 self.resolves
123 .iter()
124 .filter(move |(_, r)| match (decl.as_ref(), r.decl.as_ref()) {
125 (Decl::Label(..), Decl::Label(..))
126 | (Decl::Label(..), Decl::ContentRef(..))
127 | (Decl::ContentRef(..), Decl::Label(..))
128 | (Decl::ContentRef(..), Decl::ContentRef(..)) => r.decl.name() == decl.name(),
129 (Decl::Label(..), _) => false,
130 _ => r.decl == decl || r.root == of,
131 })
132 }
133
134 pub fn is_exported(&self, decl: &Interned<Decl>) -> bool {
136 let of = Expr::Decl(decl.clone());
137 self.exports
138 .get(decl.name())
139 .is_some_and(|export| match export {
140 Expr::Ref(ref_expr) => ref_expr.root == Some(of),
141 exprt => *exprt == of,
142 })
143 }
144
145 #[allow(dead_code)]
147 fn show(&self) {
148 use std::io::Write;
149 let vpath = self
150 .fid
151 .vpath()
152 .realize(Path::new("target/exprs/"))
153 .expect("expression dump path must be realizable");
154 let root = vpath.with_extension("root.expr");
155 std::fs::create_dir_all(root.parent().unwrap()).unwrap();
156 std::fs::write(root, format!("{}", self.root)).unwrap();
157 let scopes = vpath.with_extension("scopes.expr");
158 std::fs::create_dir_all(scopes.parent().unwrap()).unwrap();
159 {
160 let mut scopes = std::fs::File::create(scopes).unwrap();
161 for (span, expr) in self.exprs.iter() {
162 writeln!(scopes, "{span:?} -> {expr}").unwrap();
163 }
164 }
165 let imports = vpath.with_extension("imports.expr");
166 std::fs::create_dir_all(imports.parent().unwrap()).unwrap();
167 std::fs::write(imports, format!("{:#?}", self.imports)).unwrap();
168 let exports = vpath.with_extension("exports.expr");
169 std::fs::create_dir_all(exports.parent().unwrap()).unwrap();
170 std::fs::write(exports, format!("{:#?}", self.exports)).unwrap();
171 }
172}
173
174#[derive(Debug, Clone, Hash)]
176pub struct ModuleItemLayout {
177 pub parent: Interned<Decl>,
179 pub item_range: Range<usize>,
181 pub binding_range: Range<usize>,
183}
184
185#[derive(Debug, Clone, PartialEq, Eq, Hash)]
190pub enum Expr {
191 Block(Interned<Vec<Expr>>),
193 Array(Interned<ArgsExpr>),
195 Dict(Interned<ArgsExpr>),
197 Args(Interned<ArgsExpr>),
199 Pattern(Interned<Pattern>),
201 Element(Interned<ElementExpr>),
203 Unary(Interned<UnExpr>),
205 Binary(Interned<BinExpr>),
207 Apply(Interned<ApplyExpr>),
209 Func(Interned<FuncExpr>),
211 Let(Interned<LetExpr>),
213 Show(Interned<ShowExpr>),
215 Set(Interned<SetExpr>),
217 Ref(Interned<RefExpr>),
219 ContentRef(Interned<ContentRefExpr>),
221 Select(Interned<SelectExpr>),
223 Import(Interned<ImportExpr>),
225 Include(Interned<IncludeExpr>),
227 Contextual(Interned<Expr>),
229 Conditional(Interned<IfExpr>),
231 WhileLoop(Interned<WhileExpr>),
233 ForLoop(Interned<ForExpr>),
235 Type(Ty),
237 Decl(DeclExpr),
239 Star,
241}
242
243impl Expr {
244 pub fn repr(&self) -> EcoString {
246 let mut s = EcoString::new();
247 let _ = ExprDescriber::new(&mut s).write_expr(self);
248 s
249 }
250
251 pub fn span(&self) -> Span {
253 match self {
254 Expr::Decl(decl) => decl.span(),
255 Expr::Select(select) => select.span,
256 Expr::Apply(apply) => apply.span,
257 _ => Span::detached(),
258 }
259 }
260
261 pub fn file_id(&self) -> Option<TypstFileId> {
263 match self {
264 Expr::Decl(decl) => decl.file_id(),
265 _ => self.span().id(),
266 }
267 }
268
269 pub fn is_defined(&self) -> bool {
271 match self {
272 Expr::Ref(refs) => refs.root.is_some() || refs.term.is_some(),
273 Expr::Decl(decl) => decl.is_def(),
274 _ => false,
276 }
277 }
278}
279
280impl fmt::Display for Expr {
281 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
282 ExprPrinter::new(f).write_expr(self)
283 }
284}
285
286pub type LexicalScope = rpds::RedBlackTreeMapSync<Interned<str>, Expr>;
290
291#[derive(Debug, Clone)]
296pub enum ExprScope {
297 Lexical(LexicalScope),
299 Module(Module),
301 Func(Func),
303 Type(Type),
305}
306
307impl ExprScope {
308 pub fn empty() -> Self {
310 ExprScope::Lexical(LexicalScope::default())
311 }
312
313 pub fn is_empty(&self) -> bool {
315 match self {
316 ExprScope::Lexical(scope) => scope.is_empty(),
317 ExprScope::Module(module) => is_empty_scope(module.scope()),
318 ExprScope::Func(func) => func.scope().is_none_or(is_empty_scope),
319 ExprScope::Type(ty) => is_empty_scope(ty.scope()),
320 }
321 }
322
323 pub fn get(&self, name: &Interned<str>) -> (Option<Expr>, Option<Ty>) {
326 let (of, val) = match self {
327 ExprScope::Lexical(scope) => {
328 crate::log_debug_ct!("evaluating: {name:?} in {scope:?}");
329 (scope.get(name).cloned(), None)
330 }
331 ExprScope::Module(module) => {
332 let v = module.scope().get(name);
333 (None, v)
337 }
338 ExprScope::Func(func) => (None, func.scope().unwrap().get(name)),
339 ExprScope::Type(ty) => (None, ty.scope().get(name)),
340 };
341
342 (
346 of,
347 val.cloned()
348 .map(|val| Ty::Value(InsTy::new(val.read().to_owned()))),
349 )
350 }
351
352 pub fn merge_into(&self, exports: &mut LexicalScope) {
354 match self {
355 ExprScope::Lexical(scope) => {
356 for (name, expr) in scope.iter() {
357 exports.insert_mut(name.clone(), expr.clone());
358 }
359 }
360 ExprScope::Module(module) => {
361 crate::log_debug_ct!("imported: {module:?}");
362 let v = Interned::new(Ty::Value(InsTy::new(Value::Module(module.clone()))));
363 for (name, _) in module.scope().iter() {
364 let name: Interned<str> = name.into();
365 exports.insert_mut(name.clone(), select_of(v.clone(), name));
366 }
367 }
368 ExprScope::Func(func) => {
369 if let Some(scope) = func.scope() {
370 let v = Interned::new(Ty::Value(InsTy::new(Value::Func(func.clone()))));
371 for (name, _) in scope.iter() {
372 let name: Interned<str> = name.into();
373 exports.insert_mut(name.clone(), select_of(v.clone(), name));
374 }
375 }
376 }
377 ExprScope::Type(ty) => {
378 let v = Interned::new(Ty::Value(InsTy::new(Value::Type(*ty))));
379 for (name, _) in ty.scope().iter() {
380 let name: Interned<str> = name.into();
381 exports.insert_mut(name.clone(), select_of(v.clone(), name));
382 }
383 }
384 }
385 }
386}
387
388fn select_of(source: Interned<Ty>, name: Interned<str>) -> Expr {
389 Expr::Type(Ty::Select(SelectTy::new(source, name)))
390}
391
392#[derive(Debug, Default, Clone, Copy, Hash, Serialize, Deserialize)]
394#[serde(rename_all = "camelCase")]
395pub enum DefKind {
396 #[default]
398 Constant,
399 Function,
401 Variable,
403 Module,
405 Struct,
407 Reference,
409}
410
411impl fmt::Display for DefKind {
412 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413 match self {
414 Self::Constant => write!(f, "constant"),
415 Self::Function => write!(f, "function"),
416 Self::Variable => write!(f, "variable"),
417 Self::Module => write!(f, "module"),
418 Self::Struct => write!(f, "struct"),
419 Self::Reference => write!(f, "reference"),
420 }
421 }
422}
423
424pub type DeclExpr = Interned<Decl>;
426
427#[derive(Clone, PartialEq, Eq, Hash, DeclEnum)]
429pub enum Decl {
430 Func(SpannedDecl),
432 ImportAlias(SpannedDecl),
434 Var(SpannedDecl),
436 IdentRef(SpannedDecl),
438 Module(ModuleDecl),
440 ModuleAlias(SpannedDecl),
442 PathStem(SpannedDecl),
444 ImportPath(SpannedDecl),
446 IncludePath(SpannedDecl),
448 Import(SpannedDecl),
450 ContentRef(SpannedDecl),
452 Label(SpannedDecl),
454 StrName(SpannedDecl),
456 ModuleImport(SpanDecl),
458 Closure(SpanDecl),
460 Pattern(SpanDecl),
462 Spread(SpanDecl),
464 Content(SpanDecl),
466 Constant(SpanDecl),
468 BibEntry(NameRangeDecl),
470 Docs(DocsDecl),
472 Generated(GeneratedDecl),
474}
475
476impl Decl {
477 pub fn func(ident: ast::Ident) -> Self {
479 Self::Func(SpannedDecl {
480 name: ident.get().into(),
481 at: ident.span(),
482 })
483 }
484
485 pub fn lit(name: &str) -> Self {
487 Self::Var(SpannedDecl {
488 name: name.into(),
489 at: Span::detached(),
490 })
491 }
492
493 pub fn lit_(name: Interned<str>) -> Self {
495 Self::Var(SpannedDecl {
496 name,
497 at: Span::detached(),
498 })
499 }
500
501 pub fn var(ident: ast::Ident) -> Self {
503 Self::Var(SpannedDecl {
504 name: ident.get().into(),
505 at: ident.span(),
506 })
507 }
508
509 pub fn import_alias(ident: ast::Ident) -> Self {
511 Self::ImportAlias(SpannedDecl {
512 name: ident.get().into(),
513 at: ident.span(),
514 })
515 }
516
517 pub fn ident_ref(ident: ast::Ident) -> Self {
519 Self::IdentRef(SpannedDecl {
520 name: ident.get().into(),
521 at: ident.span(),
522 })
523 }
524
525 pub fn math_ident_ref(ident: ast::MathIdent) -> Self {
527 Self::IdentRef(SpannedDecl {
528 name: ident.get().into(),
529 at: ident.span(),
530 })
531 }
532
533 pub fn module(fid: TypstFileId) -> Self {
535 let name = {
536 let stem = fid.vpath().as_rooted_path_compat().file_stem();
537 stem.and_then(|s| Some(Interned::new_str(s.to_str()?)))
538 .unwrap_or_default()
539 };
540 Self::Module(ModuleDecl { name, fid })
541 }
542
543 pub fn module_with_name(name: Interned<str>, fid: TypstFileId) -> Self {
545 Self::Module(ModuleDecl { name, fid })
546 }
547
548 pub fn module_alias(ident: ast::Ident) -> Self {
550 Self::ModuleAlias(SpannedDecl {
551 name: ident.get().into(),
552 at: ident.span(),
553 })
554 }
555
556 pub fn import(ident: ast::Ident) -> Self {
558 Self::Import(SpannedDecl {
559 name: ident.get().into(),
560 at: ident.span(),
561 })
562 }
563
564 pub fn label(name: &str, at: Span) -> Self {
566 Self::Label(SpannedDecl {
567 name: name.into(),
568 at,
569 })
570 }
571
572 pub fn ref_(ident: ast::Ref) -> Self {
574 Self::ContentRef(SpannedDecl {
575 name: ident.target().into(),
576 at: {
577 let marker_span = ident
578 .to_untyped()
579 .children()
580 .find(|child| child.kind() == SyntaxKind::RefMarker)
581 .map(|child| child.span());
582
583 marker_span.unwrap_or(ident.span())
584 },
585 })
586 }
587
588 pub fn str_name(s: SyntaxNode, name: &str) -> Decl {
590 Self::StrName(SpannedDecl {
591 name: name.into(),
592 at: s.span(),
593 })
594 }
595
596 pub fn calc_path_stem(s: &str) -> Interned<str> {
602 use std::str::FromStr;
603 let name = if s.starts_with('@') {
604 let spec = PackageSpec::from_str(s).ok();
605 spec.map(|spec| Interned::new_str(spec.name.as_str()))
606 } else {
607 let stem = Path::new(s).file_stem();
608 stem.and_then(|stem| Some(Interned::new_str(stem.to_str()?)))
609 };
610 name.unwrap_or_default()
611 }
612
613 pub fn path_stem(s: SyntaxNode, name: Interned<str>) -> Self {
615 Self::PathStem(SpannedDecl { name, at: s.span() })
616 }
617
618 pub fn import_path(s: Span, name: Interned<str>) -> Self {
620 Self::ImportPath(SpannedDecl { name, at: s })
621 }
622
623 pub fn include_path(s: Span, name: Interned<str>) -> Self {
625 Self::IncludePath(SpannedDecl { name, at: s })
626 }
627
628 pub fn module_import(s: Span) -> Self {
630 Self::ModuleImport(SpanDecl(s))
631 }
632
633 pub fn closure(s: Span) -> Self {
635 Self::Closure(SpanDecl(s))
636 }
637
638 pub fn pattern(s: Span) -> Self {
640 Self::Pattern(SpanDecl(s))
641 }
642
643 pub fn spread(s: Span) -> Self {
645 Self::Spread(SpanDecl(s))
646 }
647
648 pub fn content(s: Span) -> Self {
650 Self::Content(SpanDecl(s))
651 }
652
653 pub fn constant(s: Span) -> Self {
655 Self::Constant(SpanDecl(s))
656 }
657
658 pub fn docs(base: Interned<Decl>, var: Interned<TypeVar>) -> Self {
661 Self::Docs(DocsDecl { base, var })
662 }
663
664 pub fn generated(def_id: DefId) -> Self {
666 Self::Generated(GeneratedDecl(def_id))
667 }
668
669 pub fn bib_entry(
671 name: Interned<str>,
672 fid: TypstFileId,
673 name_range: Range<usize>,
674 range: Option<Range<usize>>,
675 ) -> Self {
676 Self::BibEntry(NameRangeDecl {
677 name,
678 at: Box::new((fid, name_range, range)),
679 })
680 }
681
682 pub fn is_def(&self) -> bool {
685 matches!(
686 self,
687 Self::Func(..)
688 | Self::BibEntry(..)
689 | Self::Closure(..)
690 | Self::Var(..)
691 | Self::Label(..)
692 | Self::StrName(..)
693 | Self::Module(..)
694 | Self::ModuleImport(..)
695 | Self::PathStem(..)
696 | Self::ImportPath(..)
697 | Self::IncludePath(..)
698 | Self::Spread(..)
699 | Self::Generated(..)
700 )
701 }
702
703 pub fn kind(&self) -> DefKind {
705 use Decl::*;
706 match self {
707 ModuleAlias(..) | Module(..) | PathStem(..) | ImportPath(..) | IncludePath(..) => {
708 DefKind::Module
709 }
710 Func(..) | Closure(..) => DefKind::Function,
712 Label(..) | BibEntry(..) | ContentRef(..) => DefKind::Reference,
713 IdentRef(..) | ImportAlias(..) | Import(..) | Var(..) => DefKind::Variable,
714 Pattern(..) | Docs(..) | Generated(..) | Constant(..) | StrName(..)
715 | ModuleImport(..) | Content(..) | Spread(..) => DefKind::Constant,
716 }
717 }
718
719 pub fn file_id(&self) -> Option<TypstFileId> {
721 match self {
722 Self::Module(ModuleDecl { fid, .. }) => Some(*fid),
723 Self::BibEntry(NameRangeDecl { at, .. }) => Some(at.0),
724 Self::Docs(DocsDecl { base, .. }) => base.file_id(),
725 that => that.span().id(),
726 }
727 }
728
729 pub fn full_range(&self) -> Option<Range<usize>> {
731 if let Decl::BibEntry(decl) = self {
732 return decl.at.2.clone();
733 }
734
735 None
736 }
737
738 pub fn as_def(this: &Interned<Self>, val: Option<Ty>) -> Interned<RefExpr> {
740 let def: Expr = this.clone().into();
741 Interned::new(RefExpr {
742 decl: this.clone(),
743 step: Some(def.clone()),
744 root: Some(def),
745 term: val,
746 })
747 }
748}
749
750impl Ord for Decl {
751 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
752 let base = match (self, other) {
753 (Self::Generated(l), Self::Generated(r)) => l.0.0.cmp(&r.0.0),
754 (Self::Module(l), Self::Module(r)) => l.fid.into_raw().cmp(&r.fid.into_raw()),
755 (Self::Docs(l), Self::Docs(r)) => l.var.cmp(&r.var).then_with(|| l.base.cmp(&r.base)),
756 _ => self.span().into_raw().cmp(&other.span().into_raw()),
757 };
758
759 base.then_with(|| self.name().cmp(other.name()))
760 }
761}
762
763trait StrictCmp {
764 fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering;
767}
768
769impl Decl {
770 pub fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering {
772 let base = match (self, other) {
773 (Self::Generated(l), Self::Generated(r)) => l.0.0.cmp(&r.0.0),
774 (Self::Module(l), Self::Module(r)) => l.fid.strict_cmp(&r.fid),
775 (Self::Docs(l), Self::Docs(r)) => l
776 .var
777 .strict_cmp(&r.var)
778 .then_with(|| l.base.strict_cmp(&r.base)),
779 _ => self.span().strict_cmp(&other.span()),
780 };
781
782 base.then_with(|| self.name().cmp(other.name()))
783 }
784}
785
786impl StrictCmp for TypstFileId {
787 fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering {
788 fn root_cmp(left: &VirtualRoot, right: &VirtualRoot) -> std::cmp::Ordering {
789 match (left, right) {
790 (VirtualRoot::Project, VirtualRoot::Project) => std::cmp::Ordering::Equal,
791 (VirtualRoot::Project, VirtualRoot::Package(_)) => std::cmp::Ordering::Less,
792 (VirtualRoot::Package(_), VirtualRoot::Project) => std::cmp::Ordering::Greater,
793 (VirtualRoot::Package(left), VirtualRoot::Package(right)) => left
794 .namespace
795 .cmp(&right.namespace)
796 .then_with(|| left.name.cmp(&right.name))
797 .then_with(|| left.version.cmp(&right.version)),
798 }
799 }
800
801 root_cmp(self.root(), other.root()).then_with(|| {
802 self.vpath()
803 .get_with_slash()
804 .cmp(other.vpath().get_with_slash())
805 })
806 }
807}
808impl<T: StrictCmp> StrictCmp for Option<T> {
809 fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering {
810 match (self, other) {
811 (Some(l), Some(r)) => l.strict_cmp(r),
812 (Some(_), None) => std::cmp::Ordering::Greater,
813 (None, Some(_)) => std::cmp::Ordering::Less,
814 (None, None) => std::cmp::Ordering::Equal,
815 }
816 }
817}
818
819impl StrictCmp for Span {
820 fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering {
821 self.id()
822 .strict_cmp(&other.id())
823 .then_with(|| self.into_raw().cmp(&other.into_raw()))
824 }
825}
826
827impl PartialOrd for Decl {
828 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
829 Some(self.cmp(other))
830 }
831}
832
833impl From<Decl> for Expr {
834 fn from(decl: Decl) -> Self {
835 Expr::Decl(decl.into())
836 }
837}
838
839impl From<DeclExpr> for Expr {
840 fn from(decl: DeclExpr) -> Self {
841 Expr::Decl(decl)
842 }
843}
844
845#[derive(Clone, PartialEq, Eq, Hash)]
847pub struct SpannedDecl {
848 name: Interned<str>,
850 at: Span,
852}
853
854impl SpannedDecl {
855 fn name(&self) -> &Interned<str> {
857 &self.name
858 }
859
860 fn span(&self) -> Span {
862 self.at
863 }
864}
865
866impl fmt::Debug for SpannedDecl {
867 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
868 f.write_str(self.name.as_ref())
869 }
870}
871
872#[derive(Clone, PartialEq, Eq, Hash)]
874pub struct NameRangeDecl {
875 pub name: Interned<str>,
877 pub at: Box<(TypstFileId, Range<usize>, Option<Range<usize>>)>,
879}
880
881impl NameRangeDecl {
882 fn name(&self) -> &Interned<str> {
884 &self.name
885 }
886
887 fn span(&self) -> Span {
889 Span::detached()
890 }
891}
892
893impl fmt::Debug for NameRangeDecl {
894 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
895 f.write_str(self.name.as_ref())
896 }
897}
898
899#[derive(Clone, PartialEq, Eq, Hash)]
901pub struct ModuleDecl {
902 pub name: Interned<str>,
904 pub fid: TypstFileId,
906}
907
908impl ModuleDecl {
909 fn name(&self) -> &Interned<str> {
911 &self.name
912 }
913
914 fn span(&self) -> Span {
916 Span::detached()
917 }
918}
919
920impl fmt::Debug for ModuleDecl {
921 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
922 f.write_str(self.name.as_ref())
923 }
924}
925
926#[derive(Clone, PartialEq, Eq, Hash)]
928pub struct DocsDecl {
929 base: Interned<Decl>,
930 var: Interned<TypeVar>,
931}
932
933impl DocsDecl {
934 fn name(&self) -> &Interned<str> {
936 Interned::empty()
937 }
938
939 fn span(&self) -> Span {
941 Span::detached()
942 }
943}
944
945impl fmt::Debug for DocsDecl {
946 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
947 write!(f, "{:?}, {:?}", self.base, self.var)
948 }
949}
950
951#[derive(Clone, PartialEq, Eq, Hash)]
953pub struct SpanDecl(Span);
954
955impl SpanDecl {
956 fn name(&self) -> &Interned<str> {
958 Interned::empty()
959 }
960
961 fn span(&self) -> Span {
963 self.0
964 }
965}
966
967impl fmt::Debug for SpanDecl {
968 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
969 write!(f, "..")
970 }
971}
972
973#[derive(Clone, PartialEq, Eq, Hash)]
975pub struct GeneratedDecl(DefId);
976
977impl GeneratedDecl {
978 fn name(&self) -> &Interned<str> {
980 Interned::empty()
981 }
982
983 fn span(&self) -> Span {
985 Span::detached()
986 }
987}
988
989impl fmt::Debug for GeneratedDecl {
990 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
991 self.0.fmt(f)
992 }
993}
994
995pub type UnExpr = UnInst<Expr>;
997pub type BinExpr = BinInst<Expr>;
999
1000pub type ExportMap = BTreeMap<Interned<str>, Expr>;
1004
1005#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1009pub enum ArgExpr {
1010 Pos(Expr),
1012 Named(Box<(DeclExpr, Expr)>),
1014 NamedRt(Box<(Expr, Expr)>),
1016 Spread(Expr),
1018}
1019
1020#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1022pub enum Pattern {
1023 Expr(Expr),
1026 Simple(Interned<Decl>),
1028 Sig(Box<PatternSig>),
1030}
1031
1032impl fmt::Display for Pattern {
1033 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1034 ExprPrinter::new(f).write_pattern(self)
1035 }
1036}
1037
1038impl Pattern {
1039 pub fn repr(&self) -> EcoString {
1041 let mut s = EcoString::new();
1042 let _ = ExprDescriber::new(&mut s).write_pattern(self);
1043 s
1044 }
1045}
1046
1047#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1052pub struct PatternSig {
1053 pub pos: EcoVec<Interned<Pattern>>,
1055 pub named: EcoVec<(DeclExpr, Interned<Pattern>)>,
1057 pub spread_left: Option<(DeclExpr, Interned<Pattern>)>,
1059 pub spread_right: Option<(DeclExpr, Interned<Pattern>)>,
1061}
1062
1063impl Pattern {}
1064
1065impl_internable!(Decl,);
1066
1067#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1071pub struct ContentSeqExpr {
1072 pub ty: Ty,
1074}
1075
1076#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1121pub struct RefExpr {
1122 pub decl: DeclExpr,
1127
1128 pub step: Option<Expr>,
1138
1139 pub root: Option<Expr>,
1143
1144 pub term: Option<Ty>,
1152}
1153
1154#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1156pub struct ContentRefExpr {
1157 pub ident: DeclExpr,
1159 pub of: Option<DeclExpr>,
1161 pub body: Option<Expr>,
1163}
1164
1165#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1167pub struct SelectExpr {
1168 pub lhs: Expr,
1170 pub key: DeclExpr,
1172 pub span: Span,
1174}
1175
1176impl SelectExpr {
1177 pub fn new(key: DeclExpr, lhs: Expr) -> Interned<Self> {
1179 Interned::new(Self {
1180 key,
1181 lhs,
1182 span: Span::detached(),
1183 })
1184 }
1185}
1186
1187#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1189pub struct ArgsExpr {
1190 pub args: Vec<ArgExpr>,
1192 pub span: Span,
1194}
1195
1196impl ArgsExpr {
1197 pub fn new(span: Span, args: Vec<ArgExpr>) -> Interned<Self> {
1199 Interned::new(Self { args, span })
1200 }
1201}
1202
1203#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1205pub struct ElementExpr {
1206 pub elem: Element,
1208 pub content: EcoVec<Expr>,
1210}
1211
1212#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1214pub struct ApplyExpr {
1215 pub callee: Expr,
1217 pub args: Expr,
1219 pub span: Span,
1221}
1222
1223#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1225pub struct FuncExpr {
1226 pub decl: DeclExpr,
1228 pub params: PatternSig,
1230 pub body: Expr,
1232}
1233
1234#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1236pub struct LetExpr {
1237 pub span: Span,
1239 pub pattern: Interned<Pattern>,
1241 pub body: Option<Expr>,
1243}
1244
1245#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1247pub struct ShowExpr {
1248 pub selector: Option<Expr>,
1250 pub edit: Expr,
1252}
1253
1254#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1256pub struct SetExpr {
1257 pub target: Expr,
1259 pub args: Expr,
1261 pub cond: Option<Expr>,
1263}
1264
1265#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1267pub struct ImportExpr {
1268 pub source: Expr,
1270 pub decl: Interned<RefExpr>,
1272}
1273
1274#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1276pub struct IncludeExpr {
1277 pub source: Expr,
1279}
1280
1281#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1283pub struct IfExpr {
1284 pub cond: Expr,
1286 pub then: Expr,
1288 pub else_: Expr,
1290}
1291
1292#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1294pub struct WhileExpr {
1295 pub cond: Expr,
1297 pub body: Expr,
1299}
1300
1301#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1303pub struct ForExpr {
1304 pub pattern: Interned<Pattern>,
1306 pub iter: Expr,
1308 pub body: Expr,
1310}
1311
1312#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
1314pub enum UnaryOp {
1315 Pos,
1318 Neg,
1321 Not,
1324 Return,
1327 Context,
1330 Spread,
1333 NotElementOf,
1336 ElementOf,
1339 TypeOf,
1342}
1343
1344#[derive(Debug, Hash, Clone, PartialEq, Eq)]
1346pub struct UnInst<T> {
1347 pub lhs: T,
1349 pub op: UnaryOp,
1351}
1352
1353impl<T: Ord> PartialOrd for UnInst<T> {
1354 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1355 Some(self.cmp(other))
1356 }
1357}
1358
1359impl<T: Ord> Ord for UnInst<T> {
1360 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1361 let op_as_int = self.op as u8;
1362 let other_op_as_int = other.op as u8;
1363 op_as_int
1364 .cmp(&other_op_as_int)
1365 .then_with(|| self.lhs.cmp(&other.lhs))
1366 }
1367}
1368
1369impl UnInst<Expr> {
1370 pub fn new(op: UnaryOp, lhs: Expr) -> Interned<Self> {
1372 Interned::new(Self { lhs, op })
1373 }
1374}
1375
1376impl<T> UnInst<T> {
1377 pub fn operands(&self) -> [&T; 1] {
1379 [&self.lhs]
1380 }
1381}
1382
1383pub type BinaryOp = ast::BinOp;
1385
1386#[derive(Debug, Hash, Clone, PartialEq, Eq)]
1388pub struct BinInst<T> {
1389 pub operands: (T, T),
1391 pub op: BinaryOp,
1393}
1394
1395impl<T: Ord> PartialOrd for BinInst<T> {
1396 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1397 Some(self.cmp(other))
1398 }
1399}
1400
1401impl<T: Ord> Ord for BinInst<T> {
1402 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1403 let op_as_int = self.op as u8;
1404 let other_op_as_int = other.op as u8;
1405 op_as_int
1406 .cmp(&other_op_as_int)
1407 .then_with(|| self.operands.cmp(&other.operands))
1408 }
1409}
1410
1411impl BinInst<Expr> {
1412 pub fn new(op: BinaryOp, lhs: Expr, rhs: Expr) -> Interned<Self> {
1414 Interned::new(Self {
1415 operands: (lhs, rhs),
1416 op,
1417 })
1418 }
1419}
1420
1421impl<T> BinInst<T> {
1422 pub fn operands(&self) -> [&T; 2] {
1424 [&self.operands.0, &self.operands.1]
1425 }
1426}
1427
1428fn is_empty_scope(scope: &typst::foundations::Scope) -> bool {
1430 scope.iter().next().is_none()
1431}
1432
1433impl_internable!(
1434 Expr,
1435 ArgsExpr,
1436 ElementExpr,
1437 ContentSeqExpr,
1438 RefExpr,
1439 ContentRefExpr,
1440 SelectExpr,
1441 ImportExpr,
1442 IncludeExpr,
1443 IfExpr,
1444 WhileExpr,
1445 ForExpr,
1446 FuncExpr,
1447 LetExpr,
1448 ShowExpr,
1449 SetExpr,
1450 Pattern,
1451 EcoVec<(Decl, Expr)>,
1452 Vec<ArgExpr>,
1453 Vec<Expr>,
1454 UnInst<Expr>,
1455 BinInst<Expr>,
1456 ApplyExpr,
1457);
1458
1459#[cfg(test)]
1460mod tests {
1461 use std::cmp::Ordering;
1462 use std::str::FromStr;
1463
1464 use typst::syntax::{FileId, RootedPath, VirtualPath, VirtualRoot};
1465
1466 use super::{Decl, StrictCmp};
1467 use crate::adt::interner::Interned;
1468 use crate::prelude::TypstFileId;
1469 use crate::ty::TypeVar;
1470
1471 fn package(spec: &str) -> typst::syntax::package::PackageSpec {
1472 typst::syntax::package::PackageSpec::from_str(spec).expect("valid package spec")
1473 }
1474
1475 fn rooted_path(root: VirtualRoot, path: &str) -> RootedPath {
1476 RootedPath::new(root, VirtualPath::new(path).expect("valid virtual path"))
1477 }
1478
1479 fn file_id(root: VirtualRoot, path: &str) -> TypstFileId {
1480 FileId::new(rooted_path(root, path))
1481 }
1482
1483 fn unique_file_id(root: VirtualRoot, path: &str) -> TypstFileId {
1484 FileId::unique(rooted_path(root, path))
1485 }
1486
1487 #[test]
1488 fn strict_file_id_cmp_eq_for_same_project_path() {
1489 let left = file_id(VirtualRoot::Project, "/main.typ");
1490 let right = file_id(VirtualRoot::Project, "/main.typ");
1491
1492 assert_eq!(left, right);
1493 assert_eq!(left.strict_cmp(&right), Ordering::Equal);
1494 assert_eq!(
1495 Decl::module(left).strict_cmp(&Decl::module(right)),
1496 Ordering::Equal
1497 );
1498 }
1499
1500 #[test]
1501 fn strict_file_id_cmp_eq_for_same_package_and_path() {
1502 let root = VirtualRoot::Package(package("@preview/example:0.1.0"));
1503 let left = file_id(root.clone(), "/lib.typ");
1504 let right = file_id(root, "/lib.typ");
1505
1506 assert_eq!(left, right);
1507 assert_eq!(left.strict_cmp(&right), Ordering::Equal);
1508 assert_eq!(
1509 Decl::module(left).strict_cmp(&Decl::module(right)),
1510 Ordering::Equal
1511 );
1512 }
1513
1514 #[test]
1515 fn strict_file_id_cmp_ignores_unique_raw_id_for_same_root_and_path() {
1516 let root = VirtualRoot::Package(package("@preview/example:0.1.0"));
1517 let left = unique_file_id(root.clone(), "/lib.typ");
1518 let right = unique_file_id(root, "/lib.typ");
1519
1520 assert_ne!(left.into_raw(), right.into_raw());
1521 assert_eq!(left.root(), right.root());
1522 assert_eq!(
1523 left.vpath().get_with_slash(),
1524 right.vpath().get_with_slash()
1525 );
1526 assert_eq!(left.strict_cmp(&right), Ordering::Equal);
1527 assert_eq!(
1528 Decl::module(left).strict_cmp(&Decl::module(right)),
1529 Ordering::Equal
1530 );
1531 }
1532
1533 #[test]
1534 fn strict_file_id_cmp_distinguishes_package_or_path() {
1535 let package_root = VirtualRoot::Package(package("@preview/example:0.1.0"));
1536 let same_path = "/lib.typ";
1537 let project_file = file_id(VirtualRoot::Project, same_path);
1538 let package_file = file_id(package_root.clone(), same_path);
1539 let other_package_file = file_id(
1540 VirtualRoot::Package(package("@preview/other:0.1.0")),
1541 same_path,
1542 );
1543 let other_path = file_id(package_root, "/other.typ");
1544
1545 assert_ne!(project_file.strict_cmp(&package_file), Ordering::Equal);
1546 assert_ne!(
1547 package_file.strict_cmp(&other_package_file),
1548 Ordering::Equal
1549 );
1550 assert_ne!(package_file.strict_cmp(&other_path), Ordering::Equal);
1551 }
1552
1553 #[test]
1554 fn docs_decl_inherits_base_file_id() {
1555 let fid = file_id(VirtualRoot::Project, "/main.typ");
1556 let base: Interned<Decl> = Decl::module(fid).into();
1557 let var = TypeVar::new("input".into(), base.clone());
1558 let docs = Decl::docs(base, var);
1559
1560 assert_eq!(docs.file_id(), Some(fid));
1561 }
1562}