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 self.strict_cmp(other)
758 }
759}
760
761pub(crate) trait StrictCmp {
762 fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering;
766}
767
768impl Decl {
769 pub fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering {
771 let base = match (self, other) {
772 (Self::Generated(l), Self::Generated(r)) => l.0.0.cmp(&r.0.0),
773 (Self::Module(l), Self::Module(r)) => l.fid.strict_cmp(&r.fid),
774 (Self::Docs(l), Self::Docs(r)) => l
775 .var
776 .strict_cmp(&r.var)
777 .then_with(|| l.base.strict_cmp(&r.base)),
778 _ => self.span().strict_cmp(&other.span()),
779 };
780
781 base.then_with(|| self.name().cmp(other.name()))
782 }
783}
784
785impl StrictCmp for TypstFileId {
786 fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering {
787 if self == other {
791 return std::cmp::Ordering::Equal;
792 }
793
794 fn root_cmp(left: &VirtualRoot, right: &VirtualRoot) -> std::cmp::Ordering {
795 match (left, right) {
796 (VirtualRoot::Project, VirtualRoot::Project) => std::cmp::Ordering::Equal,
797 (VirtualRoot::Project, VirtualRoot::Package(_)) => std::cmp::Ordering::Less,
798 (VirtualRoot::Package(_), VirtualRoot::Project) => std::cmp::Ordering::Greater,
799 (VirtualRoot::Package(left), VirtualRoot::Package(right)) => left
800 .namespace
801 .cmp(&right.namespace)
802 .then_with(|| left.name.cmp(&right.name))
803 .then_with(|| left.version.cmp(&right.version)),
804 }
805 }
806
807 root_cmp(self.root(), other.root()).then_with(|| {
808 self.vpath()
809 .get_with_slash()
810 .cmp(other.vpath().get_with_slash())
811 })
812 }
813}
814impl<T: StrictCmp> StrictCmp for Option<T> {
815 fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering {
816 match (self, other) {
817 (Some(l), Some(r)) => l.strict_cmp(r),
818 (Some(_), None) => std::cmp::Ordering::Greater,
819 (None, Some(_)) => std::cmp::Ordering::Less,
820 (None, None) => std::cmp::Ordering::Equal,
821 }
822 }
823}
824
825impl StrictCmp for Span {
826 fn strict_cmp(&self, other: &Self) -> std::cmp::Ordering {
827 self.id()
828 .strict_cmp(&other.id())
829 .then_with(|| self.into_raw().cmp(&other.into_raw()))
830 }
831}
832
833impl PartialOrd for Decl {
834 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
835 Some(self.cmp(other))
836 }
837}
838
839impl From<Decl> for Expr {
840 fn from(decl: Decl) -> Self {
841 Expr::Decl(decl.into())
842 }
843}
844
845impl From<DeclExpr> for Expr {
846 fn from(decl: DeclExpr) -> Self {
847 Expr::Decl(decl)
848 }
849}
850
851#[derive(Clone, PartialEq, Eq, Hash)]
853pub struct SpannedDecl {
854 name: Interned<str>,
856 at: Span,
858}
859
860impl SpannedDecl {
861 fn name(&self) -> &Interned<str> {
863 &self.name
864 }
865
866 fn span(&self) -> Span {
868 self.at
869 }
870}
871
872impl fmt::Debug for SpannedDecl {
873 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
874 f.write_str(self.name.as_ref())
875 }
876}
877
878#[derive(Clone, PartialEq, Eq, Hash)]
880pub struct NameRangeDecl {
881 pub name: Interned<str>,
883 pub at: Box<(TypstFileId, Range<usize>, Option<Range<usize>>)>,
885}
886
887impl NameRangeDecl {
888 fn name(&self) -> &Interned<str> {
890 &self.name
891 }
892
893 fn span(&self) -> Span {
895 Span::detached()
896 }
897}
898
899impl fmt::Debug for NameRangeDecl {
900 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
901 f.write_str(self.name.as_ref())
902 }
903}
904
905#[derive(Clone, PartialEq, Eq, Hash)]
907pub struct ModuleDecl {
908 pub name: Interned<str>,
910 pub fid: TypstFileId,
912}
913
914impl ModuleDecl {
915 fn name(&self) -> &Interned<str> {
917 &self.name
918 }
919
920 fn span(&self) -> Span {
922 Span::detached()
923 }
924}
925
926impl fmt::Debug for ModuleDecl {
927 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
928 f.write_str(self.name.as_ref())
929 }
930}
931
932#[derive(Clone, PartialEq, Eq, Hash)]
934pub struct DocsDecl {
935 base: Interned<Decl>,
936 var: Interned<TypeVar>,
937}
938
939impl DocsDecl {
940 fn name(&self) -> &Interned<str> {
942 Interned::empty()
943 }
944
945 fn span(&self) -> Span {
947 Span::detached()
948 }
949}
950
951impl fmt::Debug for DocsDecl {
952 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
953 write!(f, "{:?}, {:?}", self.base, self.var)
954 }
955}
956
957#[derive(Clone, PartialEq, Eq, Hash)]
959pub struct SpanDecl(Span);
960
961impl SpanDecl {
962 fn name(&self) -> &Interned<str> {
964 Interned::empty()
965 }
966
967 fn span(&self) -> Span {
969 self.0
970 }
971}
972
973impl fmt::Debug for SpanDecl {
974 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
975 write!(f, "..")
976 }
977}
978
979#[derive(Clone, PartialEq, Eq, Hash)]
981pub struct GeneratedDecl(DefId);
982
983impl GeneratedDecl {
984 fn name(&self) -> &Interned<str> {
986 Interned::empty()
987 }
988
989 fn span(&self) -> Span {
991 Span::detached()
992 }
993}
994
995impl fmt::Debug for GeneratedDecl {
996 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
997 self.0.fmt(f)
998 }
999}
1000
1001pub type UnExpr = UnInst<Expr>;
1003pub type BinExpr = BinInst<Expr>;
1005
1006pub type ExportMap = BTreeMap<Interned<str>, Expr>;
1010
1011#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1015pub enum ArgExpr {
1016 Pos(Expr),
1018 Named(Box<(DeclExpr, Expr)>),
1020 NamedRt(Box<(Expr, Expr)>),
1022 Spread(Expr),
1024}
1025
1026#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1028pub enum Pattern {
1029 Expr(Expr),
1032 Simple(Interned<Decl>),
1034 Sig(Box<PatternSig>),
1036}
1037
1038impl fmt::Display for Pattern {
1039 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1040 ExprPrinter::new(f).write_pattern(self)
1041 }
1042}
1043
1044impl Pattern {
1045 pub fn repr(&self) -> EcoString {
1047 let mut s = EcoString::new();
1048 let _ = ExprDescriber::new(&mut s).write_pattern(self);
1049 s
1050 }
1051}
1052
1053#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1058pub struct PatternSig {
1059 pub pos: EcoVec<Interned<Pattern>>,
1061 pub named: EcoVec<(DeclExpr, Interned<Pattern>)>,
1063 pub spread_left: Option<(DeclExpr, Interned<Pattern>)>,
1065 pub spread_right: Option<(DeclExpr, Interned<Pattern>)>,
1067}
1068
1069impl Pattern {}
1070
1071impl_internable!(Decl,);
1072
1073#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1077pub struct ContentSeqExpr {
1078 pub ty: Ty,
1080}
1081
1082#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1127pub struct RefExpr {
1128 pub decl: DeclExpr,
1133
1134 pub step: Option<Expr>,
1144
1145 pub root: Option<Expr>,
1149
1150 pub term: Option<Ty>,
1158}
1159
1160#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1162pub struct ContentRefExpr {
1163 pub ident: DeclExpr,
1165 pub of: Option<DeclExpr>,
1167 pub body: Option<Expr>,
1169}
1170
1171#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1173pub struct SelectExpr {
1174 pub lhs: Expr,
1176 pub key: DeclExpr,
1178 pub span: Span,
1180}
1181
1182impl SelectExpr {
1183 pub fn new(key: DeclExpr, lhs: Expr) -> Interned<Self> {
1185 Interned::new(Self {
1186 key,
1187 lhs,
1188 span: Span::detached(),
1189 })
1190 }
1191}
1192
1193#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1195pub struct ArgsExpr {
1196 pub args: Vec<ArgExpr>,
1198 pub span: Span,
1200}
1201
1202impl ArgsExpr {
1203 pub fn new(span: Span, args: Vec<ArgExpr>) -> Interned<Self> {
1205 Interned::new(Self { args, span })
1206 }
1207}
1208
1209#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1211pub struct ElementExpr {
1212 pub elem: Element,
1214 pub content: EcoVec<Expr>,
1216}
1217
1218#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1220pub struct ApplyExpr {
1221 pub callee: Expr,
1223 pub args: Expr,
1225 pub span: Span,
1227}
1228
1229#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1231pub struct FuncExpr {
1232 pub decl: DeclExpr,
1234 pub params: PatternSig,
1236 pub body: Expr,
1238}
1239
1240#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1242pub struct LetExpr {
1243 pub span: Span,
1245 pub pattern: Interned<Pattern>,
1247 pub body: Option<Expr>,
1249}
1250
1251#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1253pub struct ShowExpr {
1254 pub selector: Option<Expr>,
1256 pub edit: Expr,
1258}
1259
1260#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1262pub struct SetExpr {
1263 pub target: Expr,
1265 pub args: Expr,
1267 pub cond: Option<Expr>,
1269}
1270
1271#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1273pub struct ImportExpr {
1274 pub source: Expr,
1276 pub decl: Interned<RefExpr>,
1278}
1279
1280#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1282pub struct IncludeExpr {
1283 pub source: Expr,
1285}
1286
1287#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1289pub struct IfExpr {
1290 pub cond: Expr,
1292 pub then: Expr,
1294 pub else_: Expr,
1296}
1297
1298#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1300pub struct WhileExpr {
1301 pub cond: Expr,
1303 pub body: Expr,
1305}
1306
1307#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1309pub struct ForExpr {
1310 pub pattern: Interned<Pattern>,
1312 pub iter: Expr,
1314 pub body: Expr,
1316}
1317
1318#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
1320pub enum UnaryOp {
1321 Pos,
1324 Neg,
1327 Not,
1330 Return,
1333 Context,
1336 Spread,
1339 NotElementOf,
1342 ElementOf,
1345 TypeOf,
1348}
1349
1350#[derive(Debug, Hash, Clone, PartialEq, Eq)]
1352pub struct UnInst<T> {
1353 pub lhs: T,
1355 pub op: UnaryOp,
1357}
1358
1359impl<T: Ord> PartialOrd for UnInst<T> {
1360 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1361 Some(self.cmp(other))
1362 }
1363}
1364
1365impl<T: Ord> Ord for UnInst<T> {
1366 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1367 let op_as_int = self.op as u8;
1368 let other_op_as_int = other.op as u8;
1369 op_as_int
1370 .cmp(&other_op_as_int)
1371 .then_with(|| self.lhs.cmp(&other.lhs))
1372 }
1373}
1374
1375impl UnInst<Expr> {
1376 pub fn new(op: UnaryOp, lhs: Expr) -> Interned<Self> {
1378 Interned::new(Self { lhs, op })
1379 }
1380}
1381
1382impl<T> UnInst<T> {
1383 pub fn operands(&self) -> [&T; 1] {
1385 [&self.lhs]
1386 }
1387}
1388
1389pub type BinaryOp = ast::BinOp;
1391
1392#[derive(Debug, Hash, Clone, PartialEq, Eq)]
1394pub struct BinInst<T> {
1395 pub operands: (T, T),
1397 pub op: BinaryOp,
1399}
1400
1401impl<T: Ord> PartialOrd for BinInst<T> {
1402 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1403 Some(self.cmp(other))
1404 }
1405}
1406
1407impl<T: Ord> Ord for BinInst<T> {
1408 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1409 let op_as_int = self.op as u8;
1410 let other_op_as_int = other.op as u8;
1411 op_as_int
1412 .cmp(&other_op_as_int)
1413 .then_with(|| self.operands.cmp(&other.operands))
1414 }
1415}
1416
1417impl BinInst<Expr> {
1418 pub fn new(op: BinaryOp, lhs: Expr, rhs: Expr) -> Interned<Self> {
1420 Interned::new(Self {
1421 operands: (lhs, rhs),
1422 op,
1423 })
1424 }
1425}
1426
1427impl<T> BinInst<T> {
1428 pub fn operands(&self) -> [&T; 2] {
1430 [&self.operands.0, &self.operands.1]
1431 }
1432}
1433
1434fn is_empty_scope(scope: &typst::foundations::Scope) -> bool {
1436 scope.iter().next().is_none()
1437}
1438
1439impl_internable!(
1440 Expr,
1441 ArgsExpr,
1442 ElementExpr,
1443 ContentSeqExpr,
1444 RefExpr,
1445 ContentRefExpr,
1446 SelectExpr,
1447 ImportExpr,
1448 IncludeExpr,
1449 IfExpr,
1450 WhileExpr,
1451 ForExpr,
1452 FuncExpr,
1453 LetExpr,
1454 ShowExpr,
1455 SetExpr,
1456 Pattern,
1457 EcoVec<(Decl, Expr)>,
1458 Vec<ArgExpr>,
1459 Vec<Expr>,
1460 UnInst<Expr>,
1461 BinInst<Expr>,
1462 ApplyExpr,
1463);
1464
1465#[cfg(test)]
1466mod tests {
1467 use std::cmp::Ordering;
1468 use std::str::FromStr;
1469
1470 use typst::syntax::{FileId, RootedPath, VirtualPath, VirtualRoot};
1471
1472 use super::{Decl, StrictCmp};
1473 use crate::adt::interner::Interned;
1474 use crate::prelude::TypstFileId;
1475 use crate::ty::TypeVar;
1476
1477 fn package(spec: &str) -> typst::syntax::package::PackageSpec {
1478 typst::syntax::package::PackageSpec::from_str(spec).expect("valid package spec")
1479 }
1480
1481 fn rooted_path(root: VirtualRoot, path: &str) -> RootedPath {
1482 RootedPath::new(root, VirtualPath::new(path).expect("valid virtual path"))
1483 }
1484
1485 fn file_id(root: VirtualRoot, path: &str) -> TypstFileId {
1486 FileId::new(rooted_path(root, path))
1487 }
1488
1489 fn unique_file_id(root: VirtualRoot, path: &str) -> TypstFileId {
1490 FileId::unique(rooted_path(root, path))
1491 }
1492
1493 #[test]
1494 fn strict_file_id_cmp_eq_for_same_project_path() {
1495 let left = file_id(VirtualRoot::Project, "/main.typ");
1496 let right = file_id(VirtualRoot::Project, "/main.typ");
1497
1498 assert_eq!(left, right);
1499 assert_eq!(left.strict_cmp(&right), Ordering::Equal);
1500 assert_eq!(
1501 Decl::module(left).strict_cmp(&Decl::module(right)),
1502 Ordering::Equal
1503 );
1504 }
1505
1506 #[test]
1507 fn strict_file_id_cmp_eq_for_same_package_and_path() {
1508 let root = VirtualRoot::Package(package("@preview/example:0.1.0"));
1509 let left = file_id(root.clone(), "/lib.typ");
1510 let right = file_id(root, "/lib.typ");
1511
1512 assert_eq!(left, right);
1513 assert_eq!(left.strict_cmp(&right), Ordering::Equal);
1514 assert_eq!(
1515 Decl::module(left).strict_cmp(&Decl::module(right)),
1516 Ordering::Equal
1517 );
1518 }
1519
1520 #[test]
1521 fn strict_file_id_cmp_ignores_unique_raw_id_for_same_root_and_path() {
1522 let root = VirtualRoot::Package(package("@preview/example:0.1.0"));
1523 let left = unique_file_id(root.clone(), "/lib.typ");
1524 let right = unique_file_id(root, "/lib.typ");
1525
1526 assert_ne!(left.into_raw(), right.into_raw());
1527 assert_eq!(left.root(), right.root());
1528 assert_eq!(
1529 left.vpath().get_with_slash(),
1530 right.vpath().get_with_slash()
1531 );
1532 assert_eq!(left.strict_cmp(&right), Ordering::Equal);
1533 assert_eq!(
1534 Decl::module(left).strict_cmp(&Decl::module(right)),
1535 Ordering::Equal
1536 );
1537 }
1538
1539 #[test]
1540 fn strict_file_id_cmp_distinguishes_package_or_path() {
1541 let package_root = VirtualRoot::Package(package("@preview/example:0.1.0"));
1542 let same_path = "/lib.typ";
1543 let project_file = file_id(VirtualRoot::Project, same_path);
1544 let package_file = file_id(package_root.clone(), same_path);
1545 let other_package_file = file_id(
1546 VirtualRoot::Package(package("@preview/other:0.1.0")),
1547 same_path,
1548 );
1549 let other_path = file_id(package_root, "/other.typ");
1550
1551 assert_ne!(project_file.strict_cmp(&package_file), Ordering::Equal);
1552 assert_ne!(
1553 package_file.strict_cmp(&other_package_file),
1554 Ordering::Equal
1555 );
1556 assert_ne!(package_file.strict_cmp(&other_path), Ordering::Equal);
1557 }
1558
1559 #[test]
1560 fn docs_decl_inherits_base_file_id() {
1561 let fid = file_id(VirtualRoot::Project, "/main.typ");
1562 let base: Interned<Decl> = Decl::module(fid).into();
1563 let var = TypeVar::new("input".into(), base.clone());
1564 let docs = Decl::docs(base, var);
1565
1566 assert_eq!(docs.file_id(), Some(fid));
1567 }
1568}