diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 72d38b462..67e35a5aa 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -276,24 +276,25 @@ fn enum_node(p: &mut Parser, at_start: bool) { /// Parse an expression within markup mode. fn markup_expr(p: &mut Parser) { - if let Some(token) = p.peek() { - let stmt = matches!( - token, + // Does the expression need termination or can content follow directly? + let stmt = matches!( + p.peek(), + Some( NodeKind::Let | NodeKind::Set | NodeKind::Show | NodeKind::Wrap | NodeKind::Import - ); - let group = if stmt { Group::Stmt } else { Group::Expr }; + | NodeKind::Include + ) + ); - p.start_group(group); - let res = expr_prec(p, true, 0); - if stmt && res.is_ok() && !p.eof() { - p.expected_at("semicolon or line break"); - } - p.end_group(); + p.start_group(Group::Expr); + let res = expr_prec(p, true, 0); + if stmt && res.is_ok() && !p.eof() { + p.expected_at("semicolon or line break"); } + p.end_group(); } /// Parse an expression. @@ -626,7 +627,7 @@ fn block(p: &mut Parser) { p.perform(NodeKind::Block, |p| { p.start_group(Group::Brace); while !p.eof() { - p.start_group(Group::Stmt); + p.start_group(Group::Expr); if expr(p).is_ok() && !p.eof() { p.expected_at("semicolon or line break"); } diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 4e5b277d2..0184c1983 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -248,7 +248,6 @@ impl<'s> Parser<'s> { Group::Paren => self.eat_assert(&NodeKind::LeftParen), Group::Bracket => self.eat_assert(&NodeKind::LeftBracket), Group::Brace => self.eat_assert(&NodeKind::LeftBrace), - Group::Stmt => {} Group::Expr => {} Group::Imports => {} } @@ -274,8 +273,7 @@ impl<'s> Parser<'s> { Group::Paren => Some((NodeKind::RightParen, true)), Group::Bracket => Some((NodeKind::RightBracket, true)), Group::Brace => Some((NodeKind::RightBrace, true)), - Group::Stmt => Some((NodeKind::Semicolon, false)), - Group::Expr => None, + Group::Expr => Some((NodeKind::Semicolon, false)), Group::Imports => None, } { if self.current.as_ref() == Some(&end) { @@ -324,7 +322,7 @@ impl<'s> Parser<'s> { Some(NodeKind::RightParen) => self.inside(Group::Paren), Some(NodeKind::RightBracket) => self.inside(Group::Bracket), Some(NodeKind::RightBrace) => self.inside(Group::Brace), - Some(NodeKind::Semicolon) => self.inside(Group::Stmt), + Some(NodeKind::Semicolon) => self.inside(Group::Expr), Some(NodeKind::From) => self.inside(Group::Imports), Some(NodeKind::Space(n)) => *n >= 1 && self.stop_at_newline(), Some(_) => false, @@ -352,7 +350,7 @@ impl<'s> Parser<'s> { fn stop_at_newline(&self) -> bool { matches!( self.groups.last().map(|group| group.kind), - Some(Group::Stmt | Group::Expr | Group::Imports) + Some(Group::Expr | Group::Imports) ) } @@ -488,8 +486,6 @@ pub enum Group { /// A parenthesized group: `(...)`. Paren, /// A group ended by a semicolon or a line break: `;`, `\n`. - Stmt, - /// A group for a single expression, ended by a line break. Expr, /// A group for import items, ended by a semicolon, line break or `from`. Imports, diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 39854052f..6732aa40e 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -875,54 +875,6 @@ impl SetExpr { } } -node! { - /// An import expression: `import a, b, c from "utils.typ"`. - ImportExpr -} - -impl ImportExpr { - /// The items to be imported. - pub fn imports(&self) -> Imports { - self.0 - .children() - .find_map(|node| match node.kind() { - NodeKind::Star => Some(Imports::Wildcard), - NodeKind::ImportItems => { - let items = node.children().filter_map(RedRef::cast).collect(); - Some(Imports::Items(items)) - } - _ => None, - }) - .expect("import is missing items") - } - - /// The location of the importable file. - pub fn path(&self) -> Expr { - self.0.cast_last_child().expect("import is missing path") - } -} - -/// The items that ought to be imported from a file. -#[derive(Debug, Clone, PartialEq)] -pub enum Imports { - /// All items in the scope of the file should be imported. - Wildcard, - /// The specified items from the file should be imported. - Items(Vec), -} - -node! { - /// An include expression: `include "chapter1.typ"`. - IncludeExpr -} - -impl IncludeExpr { - /// The location of the file to be included. - pub fn path(&self) -> Expr { - self.0.cast_last_child().expect("include is missing path") - } -} - node! { /// A show expression: `show heading(body) as [*{body}*]`. ShowExpr @@ -1041,6 +993,54 @@ impl ForPattern { } } +node! { + /// An import expression: `import a, b, c from "utils.typ"`. + ImportExpr +} + +impl ImportExpr { + /// The items to be imported. + pub fn imports(&self) -> Imports { + self.0 + .children() + .find_map(|node| match node.kind() { + NodeKind::Star => Some(Imports::Wildcard), + NodeKind::ImportItems => { + let items = node.children().filter_map(RedRef::cast).collect(); + Some(Imports::Items(items)) + } + _ => None, + }) + .expect("import is missing items") + } + + /// The location of the importable file. + pub fn path(&self) -> Expr { + self.0.cast_last_child().expect("import is missing path") + } +} + +/// The items that ought to be imported from a file. +#[derive(Debug, Clone, PartialEq)] +pub enum Imports { + /// All items in the scope of the file should be imported. + Wildcard, + /// The specified items from the file should be imported. + Items(Vec), +} + +node! { + /// An include expression: `include "chapter1.typ"`. + IncludeExpr +} + +impl IncludeExpr { + /// The location of the file to be included. + pub fn path(&self) -> Expr { + self.0.cast_last_child().expect("include is missing path") + } +} + node! { /// An identifier. Ident: NodeKind::Ident(_) diff --git a/tests/typ/code/include.typ b/tests/typ/code/include.typ index 1e5d5827b..3510cb063 100644 --- a/tests/typ/code/include.typ +++ b/tests/typ/code/include.typ @@ -26,3 +26,7 @@ // The variables of the file should not appear in this scope. // Error: 1-6 unknown variable #name + +--- +// Error: 18 expected semicolon or line break +#include "hi.typ" Hi