summaryrefslogtreecommitdiff
path: root/src/syntax/ast.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-12-31 09:37:55 +0100
committerLaurenz <laurmaedje@gmail.com>2022-12-31 09:52:14 +0100
commit7c683db3673187260ef2fa84d779ff81cb05aba8 (patch)
tree5ecfe3845b528a7d5ec67dfc1c93eb0dee3fa951 /src/syntax/ast.rs
parenta6d90c1bf1e9fefa0af04206909a40e112d6bb14 (diff)
Merge `MarkupNode` and `MathNode` into `Expr`
Diffstat (limited to 'src/syntax/ast.rs')
-rw-r--r--src/syntax/ast.rs386
1 files changed, 163 insertions, 223 deletions
diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs
index 6483f7cc..ccad77c2 100644
--- a/src/syntax/ast.rs
+++ b/src/syntax/ast.rs
@@ -1,6 +1,6 @@
//! A typed layer over the untyped syntax tree.
//!
-//! The AST is rooted in the [`MarkupNode`].
+//! The AST is rooted in the [`Markup`] node.
use std::num::NonZeroUsize;
use std::ops::Deref;
@@ -54,26 +54,26 @@ node! {
}
impl Markup {
- /// The children.
- pub fn children(&self) -> impl DoubleEndedIterator<Item = MarkupNode> + '_ {
+ /// The expressions.
+ pub fn exprs(&self) -> impl DoubleEndedIterator<Item = Expr> + '_ {
let mut was_stmt = false;
self.0
.children()
.filter(move |node| {
- // Ignore linebreak directly after statements without semicolons.
+ // Ignore newline directly after statements without semicolons.
let kind = node.kind();
let keep =
!was_stmt || !matches!(kind, SyntaxKind::Space { newlines: 1 });
was_stmt = kind.is_stmt();
keep
})
- .filter_map(SyntaxNode::cast)
+ .filter_map(Expr::cast_with_space)
}
}
-/// A single piece of markup.
-#[derive(Debug, Clone, PartialEq)]
-pub enum MarkupNode {
+/// An expression in markup, math or code.
+#[derive(Debug, Clone, PartialEq, Hash)]
+pub enum Expr {
/// Whitespace.
Space(Space),
/// A forced line break: `\`.
@@ -107,14 +107,78 @@ pub enum MarkupNode {
Enum(EnumItem),
/// An item in a term list: `/ Term: Details`.
Term(TermItem),
- /// An expression.
- Expr(Expr),
+ /// A math formula: `$x$`, `$ x^2 $`.
+ Math(Math),
+ /// An atom in a math formula: `x`, `+`, `12`.
+ Atom(Atom),
+ /// A base with optional sub- and superscripts in a math formula: `a_1^2`.
+ Script(Script),
+ /// A fraction in a math formula: `x/2`.
+ Frac(Frac),
+ /// An alignment point in a math formula: `&`, `&&`.
+ AlignPoint(AlignPoint),
+ /// A literal: `1`, `true`, ...
+ Lit(Lit),
+ /// An identifier: `left`.
+ Ident(Ident),
+ /// A code block: `{ let x = 1; x + 2 }`.
+ Code(CodeBlock),
+ /// A content block: `[*Hi* there!]`.
+ Content(ContentBlock),
+ /// A grouped expression: `(1 + 2)`.
+ Parenthesized(Parenthesized),
+ /// An array: `(1, "hi", 12cm)`.
+ Array(Array),
+ /// A dictionary: `(thickness: 3pt, pattern: dashed)`.
+ Dict(Dict),
+ /// A unary operation: `-x`.
+ Unary(Unary),
+ /// A binary operation: `a + b`.
+ Binary(Binary),
+ /// A field access: `properties.age`.
+ FieldAccess(FieldAccess),
+ /// An invocation of a function: `f(x, y)`.
+ FuncCall(FuncCall),
+ /// An invocation of a method: `array.push(v)`.
+ MethodCall(MethodCall),
+ /// A closure: `(x, y) => z`.
+ Closure(Closure),
+ /// A let binding: `let x = 1`.
+ Let(LetBinding),
+ /// A set rule: `set text(...)`.
+ Set(SetRule),
+ /// A show rule: `show heading: it => [*{it.body}*]`.
+ Show(ShowRule),
+ /// An if-else conditional: `if x { y } else { z }`.
+ Conditional(Conditional),
+ /// A while loop: `while x { y }`.
+ While(WhileLoop),
+ /// A for loop: `for x in y { z }`.
+ For(ForLoop),
+ /// A module import: `import a, b, c from "utils.typ"`.
+ Import(ModuleImport),
+ /// A module include: `include "chapter1.typ"`.
+ Include(ModuleInclude),
+ /// A break from a loop: `break`.
+ Break(LoopBreak),
+ /// A continue in a loop: `continue`.
+ Continue(LoopContinue),
+ /// A return from a function: `return`, `return x + 1`.
+ Return(FuncReturn),
}
-impl AstNode for MarkupNode {
- fn from_untyped(node: &SyntaxNode) -> Option<Self> {
+impl Expr {
+ fn cast_with_space(node: &SyntaxNode) -> Option<Self> {
match node.kind() {
SyntaxKind::Space { .. } => node.cast().map(Self::Space),
+ _ => Self::from_untyped(node),
+ }
+ }
+}
+
+impl AstNode for Expr {
+ fn from_untyped(node: &SyntaxNode) -> Option<Self> {
+ match node.kind() {
SyntaxKind::Linebreak => node.cast().map(Self::Linebreak),
SyntaxKind::Text(_) => node.cast().map(Self::Text),
SyntaxKind::Escape(_) => node.cast().map(Self::Escape),
@@ -130,7 +194,35 @@ impl AstNode for MarkupNode {
SyntaxKind::ListItem => node.cast().map(Self::List),
SyntaxKind::EnumItem => node.cast().map(Self::Enum),
SyntaxKind::TermItem => node.cast().map(Self::Term),
- _ => node.cast().map(Self::Expr),
+ SyntaxKind::Math => node.cast().map(Self::Math),
+ SyntaxKind::Atom(_) => node.cast().map(Self::Atom),
+ SyntaxKind::Script => node.cast().map(Self::Script),
+ SyntaxKind::Frac => node.cast().map(Self::Frac),
+ SyntaxKind::AlignPoint => node.cast().map(Self::AlignPoint),
+ SyntaxKind::Ident(_) => node.cast().map(Self::Ident),
+ SyntaxKind::CodeBlock => node.cast().map(Self::Code),
+ SyntaxKind::ContentBlock => node.cast().map(Self::Content),
+ SyntaxKind::Parenthesized => node.cast().map(Self::Parenthesized),
+ SyntaxKind::Array => node.cast().map(Self::Array),
+ SyntaxKind::Dict => node.cast().map(Self::Dict),
+ SyntaxKind::Unary => node.cast().map(Self::Unary),
+ SyntaxKind::Binary => node.cast().map(Self::Binary),
+ SyntaxKind::FieldAccess => node.cast().map(Self::FieldAccess),
+ SyntaxKind::FuncCall => node.cast().map(Self::FuncCall),
+ SyntaxKind::MethodCall => node.cast().map(Self::MethodCall),
+ SyntaxKind::Closure => node.cast().map(Self::Closure),
+ SyntaxKind::LetBinding => node.cast().map(Self::Let),
+ SyntaxKind::SetRule => node.cast().map(Self::Set),
+ SyntaxKind::ShowRule => node.cast().map(Self::Show),
+ SyntaxKind::Conditional => node.cast().map(Self::Conditional),
+ SyntaxKind::WhileLoop => node.cast().map(Self::While),
+ SyntaxKind::ForLoop => node.cast().map(Self::For),
+ SyntaxKind::ModuleImport => node.cast().map(Self::Import),
+ SyntaxKind::ModuleInclude => node.cast().map(Self::Include),
+ SyntaxKind::LoopBreak => node.cast().map(Self::Break),
+ SyntaxKind::LoopContinue => node.cast().map(Self::Continue),
+ SyntaxKind::FuncReturn => node.cast().map(Self::Return),
+ _ => node.cast().map(Self::Lit),
}
}
@@ -152,11 +244,58 @@ impl AstNode for MarkupNode {
Self::List(v) => v.as_untyped(),
Self::Enum(v) => v.as_untyped(),
Self::Term(v) => v.as_untyped(),
- Self::Expr(v) => v.as_untyped(),
+ Self::Math(v) => v.as_untyped(),
+ Self::Atom(v) => v.as_untyped(),
+ Self::Script(v) => v.as_untyped(),
+ Self::Frac(v) => v.as_untyped(),
+ Self::AlignPoint(v) => v.as_untyped(),
+ Self::Lit(v) => v.as_untyped(),
+ Self::Code(v) => v.as_untyped(),
+ Self::Content(v) => v.as_untyped(),
+ Self::Ident(v) => v.as_untyped(),
+ Self::Array(v) => v.as_untyped(),
+ Self::Dict(v) => v.as_untyped(),
+ Self::Parenthesized(v) => v.as_untyped(),
+ Self::Unary(v) => v.as_untyped(),
+ Self::Binary(v) => v.as_untyped(),
+ Self::FieldAccess(v) => v.as_untyped(),
+ Self::FuncCall(v) => v.as_untyped(),
+ Self::MethodCall(v) => v.as_untyped(),
+ Self::Closure(v) => v.as_untyped(),
+ Self::Let(v) => v.as_untyped(),
+ Self::Set(v) => v.as_untyped(),
+ Self::Show(v) => v.as_untyped(),
+ Self::Conditional(v) => v.as_untyped(),
+ Self::While(v) => v.as_untyped(),
+ Self::For(v) => v.as_untyped(),
+ Self::Import(v) => v.as_untyped(),
+ Self::Include(v) => v.as_untyped(),
+ Self::Break(v) => v.as_untyped(),
+ Self::Continue(v) => v.as_untyped(),
+ Self::Return(v) => v.as_untyped(),
}
}
}
+impl Expr {
+ /// Whether the expression can be shortened in markup with a hashtag.
+ pub fn has_short_form(&self) -> bool {
+ matches!(
+ self,
+ Self::Ident(_)
+ | Self::FuncCall(_)
+ | Self::Let(_)
+ | Self::Set(_)
+ | Self::Show(_)
+ | Self::Conditional(_)
+ | Self::While(_)
+ | Self::For(_)
+ | Self::Import(_)
+ | Self::Include(_)
+ )
+ }
+}
+
node! {
/// Whitespace.
Space
@@ -418,78 +557,15 @@ node! {
}
impl Math {
- /// The children.
- pub fn children(&self) -> impl DoubleEndedIterator<Item = MathNode> + '_ {
- self.0.children().filter_map(SyntaxNode::cast)
+ /// The expressions the formula consists of.
+ pub fn exprs(&self) -> impl DoubleEndedIterator<Item = Expr> + '_ {
+ self.0.children().filter_map(Expr::cast_with_space)
}
/// Whether the formula should be displayed as a separate block.
pub fn block(&self) -> bool {
- matches!(self.children().next(), Some(MathNode::Space(_)))
- && matches!(self.children().last(), Some(MathNode::Space(_)))
- }
-}
-
-/// A single piece of a math formula.
-#[derive(Debug, Clone, PartialEq, Hash)]
-pub enum MathNode {
- /// Whitespace.
- Space(Space),
- /// A forced line break: `\`.
- Linebreak(Linebreak),
- /// An escape sequence: `\#`, `\u{1F5FA}`.
- Escape(Escape),
- /// A shorthand for a unicode codepoint. For example, `->` for a right
- /// arrow.
- Shorthand(Shorthand),
- /// An atom: `x`, `+`, `12`.
- Atom(Atom),
- /// Symbol notation: `:arrow:l:` or `arrow:l`. Notations without any colons
- /// are parsed as identifier expression and handled during evaluation.
- Symbol(Symbol),
- /// A base with optional sub- and superscripts: `a_1^2`.
- Script(Script),
- /// A fraction: `x/2`.
- Frac(Frac),
- /// An alignment point: `&`, `&&`.
- AlignPoint(AlignPoint),
- /// Grouped mathematical material.
- Group(Math),
- /// An expression.
- Expr(Expr),
-}
-
-impl AstNode for MathNode {
- fn from_untyped(node: &SyntaxNode) -> Option<Self> {
- match node.kind() {
- SyntaxKind::Space { .. } => node.cast().map(Self::Space),
- SyntaxKind::Linebreak => node.cast().map(Self::Linebreak),
- SyntaxKind::Escape(_) => node.cast().map(Self::Escape),
- SyntaxKind::Shorthand(_) => node.cast().map(Self::Shorthand),
- SyntaxKind::Atom(_) => node.cast().map(Self::Atom),
- SyntaxKind::Symbol(_) => node.cast().map(Self::Symbol),
- SyntaxKind::Script => node.cast().map(Self::Script),
- SyntaxKind::Frac => node.cast().map(Self::Frac),
- SyntaxKind::AlignPoint => node.cast().map(Self::AlignPoint),
- SyntaxKind::Math => node.cast().map(Self::Group),
- _ => node.cast().map(Self::Expr),
- }
- }
-
- fn as_untyped(&self) -> &SyntaxNode {
- match self {
- Self::Space(v) => v.as_untyped(),
- Self::Linebreak(v) => v.as_untyped(),
- Self::Escape(v) => v.as_untyped(),
- Self::Shorthand(v) => v.as_untyped(),
- Self::Atom(v) => v.as_untyped(),
- Self::Symbol(v) => v.as_untyped(),
- Self::Script(v) => v.as_untyped(),
- Self::Frac(v) => v.as_untyped(),
- Self::AlignPoint(v) => v.as_untyped(),
- Self::Group(v) => v.as_untyped(),
- Self::Expr(v) => v.as_untyped(),
- }
+ matches!(self.exprs().next(), Some(Expr::Space(_)))
+ && matches!(self.exprs().last(), Some(Expr::Space(_)))
}
}
@@ -515,12 +591,12 @@ node! {
impl Script {
/// The base of the script.
- pub fn base(&self) -> MathNode {
+ pub fn base(&self) -> Expr {
self.0.cast_first_child().expect("script node is missing base")
}
/// The subscript.
- pub fn sub(&self) -> Option<MathNode> {
+ pub fn sub(&self) -> Option<Expr> {
self.0
.children()
.skip_while(|node| !matches!(node.kind(), SyntaxKind::Underscore))
@@ -529,7 +605,7 @@ impl Script {
}
/// The superscript.
- pub fn sup(&self) -> Option<MathNode> {
+ pub fn sup(&self) -> Option<Expr> {
self.0
.children()
.skip_while(|node| !matches!(node.kind(), SyntaxKind::Hat))
@@ -545,12 +621,12 @@ node! {
impl Frac {
/// The numerator.
- pub fn num(&self) -> MathNode {
+ pub fn num(&self) -> Expr {
self.0.cast_first_child().expect("fraction is missing numerator")
}
/// The denominator.
- pub fn denom(&self) -> MathNode {
+ pub fn denom(&self) -> Expr {
self.0.cast_last_child().expect("fraction is missing denominator")
}
}
@@ -572,142 +648,6 @@ impl AlignPoint {
}
}
-/// An expression.
-#[derive(Debug, Clone, PartialEq, Hash)]
-pub enum Expr {
- /// A literal: `1`, `true`, ...
- Lit(Lit),
- /// An identifier: `left`.
- Ident(Ident),
- /// A code block: `{ let x = 1; x + 2 }`.
- Code(CodeBlock),
- /// A content block: `[*Hi* there!]`.
- Content(ContentBlock),
- /// A math formula: `$x$`, `$ x^2 $`.
- Math(Math),
- /// A grouped expression: `(1 + 2)`.
- Parenthesized(Parenthesized),
- /// An array: `(1, "hi", 12cm)`.
- Array(Array),
- /// A dictionary: `(thickness: 3pt, pattern: dashed)`.
- Dict(Dict),
- /// A unary operation: `-x`.
- Unary(Unary),
- /// A binary operation: `a + b`.
- Binary(Binary),
- /// A field access: `properties.age`.
- FieldAccess(FieldAccess),
- /// An invocation of a function: `f(x, y)`.
- FuncCall(FuncCall),
- /// An invocation of a method: `array.push(v)`.
- MethodCall(MethodCall),
- /// A closure: `(x, y) => z`.
- Closure(Closure),
- /// A let binding: `let x = 1`.
- Let(LetBinding),
- /// A set rule: `set text(...)`.
- Set(SetRule),
- /// A show rule: `show heading: it => [*{it.body}*]`.
- Show(ShowRule),
- /// An if-else conditional: `if x { y } else { z }`.
- Conditional(Conditional),
- /// A while loop: `while x { y }`.
- While(WhileLoop),
- /// A for loop: `for x in y { z }`.
- For(ForLoop),
- /// A module import: `import a, b, c from "utils.typ"`.
- Import(ModuleImport),
- /// A module include: `include "chapter1.typ"`.
- Include(ModuleInclude),
- /// A break from a loop: `break`.
- Break(LoopBreak),
- /// A continue in a loop: `continue`.
- Continue(LoopContinue),
- /// A return from a function: `return`, `return x + 1`.
- Return(FuncReturn),
-}
-
-impl AstNode for Expr {
- fn from_untyped(node: &SyntaxNode) -> Option<Self> {
- match node.kind() {
- SyntaxKind::Ident(_) => node.cast().map(Self::Ident),
- SyntaxKind::CodeBlock => node.cast().map(Self::Code),
- SyntaxKind::ContentBlock => node.cast().map(Self::Content),
- SyntaxKind::Math => node.cast().map(Self::Math),
- SyntaxKind::Parenthesized => node.cast().map(Self::Parenthesized),
- SyntaxKind::Array => node.cast().map(Self::Array),
- SyntaxKind::Dict => node.cast().map(Self::Dict),
- SyntaxKind::Unary => node.cast().map(Self::Unary),
- SyntaxKind::Binary => node.cast().map(Self::Binary),
- SyntaxKind::FieldAccess => node.cast().map(Self::FieldAccess),
- SyntaxKind::FuncCall => node.cast().map(Self::FuncCall),
- SyntaxKind::MethodCall => node.cast().map(Self::MethodCall),
- SyntaxKind::Closure => node.cast().map(Self::Closure),
- SyntaxKind::LetBinding => node.cast().map(Self::Let),
- SyntaxKind::SetRule => node.cast().map(Self::Set),
- SyntaxKind::ShowRule => node.cast().map(Self::Show),
- SyntaxKind::Conditional => node.cast().map(Self::Conditional),
- SyntaxKind::WhileLoop => node.cast().map(Self::While),
- SyntaxKind::ForLoop => node.cast().map(Self::For),
- SyntaxKind::ModuleImport => node.cast().map(Self::Import),
- SyntaxKind::ModuleInclude => node.cast().map(Self::Include),
- SyntaxKind::LoopBreak => node.cast().map(Self::Break),
- SyntaxKind::LoopContinue => node.cast().map(Self::Continue),
- SyntaxKind::FuncReturn => node.cast().map(Self::Return),
- _ => node.cast().map(Self::Lit),
- }
- }
-
- fn as_untyped(&self) -> &SyntaxNode {
- match self {
- Self::Lit(v) => v.as_untyped(),
- Self::Code(v) => v.as_untyped(),
- Self::Content(v) => v.as_untyped(),
- Self::Math(v) => v.as_untyped(),
- Self::Ident(v) => v.as_untyped(),
- Self::Array(v) => v.as_untyped(),
- Self::Dict(v) => v.as_untyped(),
- Self::Parenthesized(v) => v.as_untyped(),
- Self::Unary(v) => v.as_untyped(),
- Self::Binary(v) => v.as_untyped(),
- Self::FieldAccess(v) => v.as_untyped(),
- Self::FuncCall(v) => v.as_untyped(),
- Self::MethodCall(v) => v.as_untyped(),
- Self::Closure(v) => v.as_untyped(),
- Self::Let(v) => v.as_untyped(),
- Self::Set(v) => v.as_untyped(),
- Self::Show(v) => v.as_untyped(),
- Self::Conditional(v) => v.as_untyped(),
- Self::While(v) => v.as_untyped(),
- Self::For(v) => v.as_untyped(),
- Self::Import(v) => v.as_untyped(),
- Self::Include(v) => v.as_untyped(),
- Self::Break(v) => v.as_untyped(),
- Self::Continue(v) => v.as_untyped(),
- Self::Return(v) => v.as_untyped(),
- }
- }
-}
-
-impl Expr {
- /// Whether the expression can be shortened in markup with a hashtag.
- pub fn has_short_form(&self) -> bool {
- matches!(
- self,
- Self::Ident(_)
- | Self::FuncCall(_)
- | Self::Let(_)
- | Self::Set(_)
- | Self::Show(_)
- | Self::Conditional(_)
- | Self::While(_)
- | Self::For(_)
- | Self::Import(_)
- | Self::Include(_)
- )
- }
-}
-
node! {
/// A literal: `1`, `true`, ...
Lit: SyntaxKind::None