From 76b1d4a93f6d045901f17db46d82a97c9f407703 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Fri, 28 Jan 2022 20:02:42 +0100 Subject: [PATCH] Parse `show` and `wrap` expressions --- src/diag.rs | 5 ++++- src/eval/mod.rs | 18 ++++++++++++++++ src/parse/incremental.rs | 5 +++++ src/parse/mod.rs | 33 +++++++++++++++++++++++++++++- src/parse/tokens.rs | 3 +++ src/syntax/ast.rs | 44 ++++++++++++++++++++++++++++++++++++++++ src/syntax/highlight.rs | 5 +++++ src/syntax/mod.rs | 15 ++++++++++++++ src/syntax/pretty.rs | 22 ++++++++++++++++++++ tests/typ/style/show.typ | 6 ++++++ tests/typ/style/wrap.typ | 6 ++++++ 11 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 tests/typ/style/show.typ create mode 100644 tests/typ/style/wrap.typ diff --git a/src/diag.rs b/src/diag.rs index be431e12f..bd91df267 100644 --- a/src/diag.rs +++ b/src/diag.rs @@ -76,7 +76,10 @@ pub trait At { fn at(self, span: Span) -> TypResult; } -impl At for StrResult { +impl At for Result +where + S: Into, +{ fn at(self, span: Span) -> TypResult { self.map_err(|message| Error::boxed(span, message)) } diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 58400f5a2..2023004f1 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -383,6 +383,8 @@ impl Eval for Expr { Self::Binary(v) => v.eval(ctx), Self::Let(v) => v.eval(ctx), Self::Set(v) => v.eval(ctx), + Self::Show(v) => v.eval(ctx), + Self::Wrap(v) => v.eval(ctx), Self::If(v) => v.eval(ctx), Self::While(v) => v.eval(ctx), Self::For(v) => v.eval(ctx), @@ -759,6 +761,22 @@ impl Eval for SetExpr { } } +impl Eval for ShowExpr { + type Output = Value; + + fn eval(&self, _: &mut EvalContext) -> TypResult { + Err("show rules are not yet implemented").at(self.span()) + } +} + +impl Eval for WrapExpr { + type Output = Value; + + fn eval(&self, _: &mut EvalContext) -> TypResult { + Err("wrap is not yet implemented").at(self.span()) + } +} + impl Eval for IfExpr { type Output = Value; diff --git a/src/parse/incremental.rs b/src/parse/incremental.rs index 4c82f158b..2edb84ba5 100644 --- a/src/parse/incremental.rs +++ b/src/parse/incremental.rs @@ -466,10 +466,13 @@ impl NodeKind { // how far the expression would go. Self::Let | Self::Set + | Self::Show + | Self::Wrap | Self::If | Self::Else | Self::For | Self::In + | Self::As | Self::While | Self::Break | Self::Continue @@ -542,6 +545,8 @@ impl NodeKind { | Self::IfExpr | Self::LetExpr | Self::SetExpr + | Self::ShowExpr + | Self::WrapExpr | Self::ImportExpr | Self::IncludeExpr => SuccessionRule::AtomicPrimary, diff --git a/src/parse/mod.rs b/src/parse/mod.rs index a97526453..72d38b462 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -208,6 +208,8 @@ fn markup_node(p: &mut Parser, at_start: &mut bool) { NodeKind::Ident(_) | NodeKind::Let | NodeKind::Set + | NodeKind::Show + | NodeKind::Wrap | NodeKind::If | NodeKind::While | NodeKind::For @@ -275,7 +277,14 @@ 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, NodeKind::Let | NodeKind::Set | NodeKind::Import); + let stmt = matches!( + token, + NodeKind::Let + | NodeKind::Set + | NodeKind::Show + | NodeKind::Wrap + | NodeKind::Import + ); let group = if stmt { Group::Stmt } else { Group::Expr }; p.start_group(group); @@ -388,6 +397,8 @@ fn primary(p: &mut Parser, atomic: bool) -> ParseResult { // Keywords. Some(NodeKind::Let) => let_expr(p), Some(NodeKind::Set) => set_expr(p), + Some(NodeKind::Show) => show_expr(p), + Some(NodeKind::Wrap) => wrap_expr(p), Some(NodeKind::If) => if_expr(p), Some(NodeKind::While) => while_expr(p), Some(NodeKind::For) => for_expr(p), @@ -714,6 +725,26 @@ fn set_expr(p: &mut Parser) -> ParseResult { }) } +/// Parse a show expression. +fn show_expr(p: &mut Parser) -> ParseResult { + p.perform(NodeKind::ShowExpr, |p| { + p.eat_assert(&NodeKind::Show); + expr(p)?; + p.eat_expect(&NodeKind::As)?; + expr(p) + }) +} + +/// Parse a wrap expression. +fn wrap_expr(p: &mut Parser) -> ParseResult { + p.perform(NodeKind::WrapExpr, |p| { + p.eat_assert(&NodeKind::Wrap); + ident(p)?; + p.eat_expect(&NodeKind::In)?; + expr(p) + }) +} + /// Parse an if expresion. fn if_expr(p: &mut Parser) -> ParseResult { p.perform(NodeKind::IfExpr, |p| { diff --git a/src/parse/tokens.rs b/src/parse/tokens.rs index 840a68e11..eef7a72d8 100644 --- a/src/parse/tokens.rs +++ b/src/parse/tokens.rs @@ -540,10 +540,13 @@ fn keyword(ident: &str) -> Option { "with" => NodeKind::With, "let" => NodeKind::Let, "set" => NodeKind::Set, + "show" => NodeKind::Show, + "wrap" => NodeKind::Wrap, "if" => NodeKind::If, "else" => NodeKind::Else, "for" => NodeKind::For, "in" => NodeKind::In, + "as" => NodeKind::As, "while" => NodeKind::While, "break" => NodeKind::Break, "continue" => NodeKind::Continue, diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index bea4ef000..39854052f 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -217,6 +217,10 @@ pub enum Expr { Let(LetExpr), /// A set expression: `set text(...)`. Set(SetExpr), + /// A show expression: `show heading(body) as [*{body}*]`. + Show(ShowExpr), + /// A wrap expression: `wrap body in columns(2, body)`. + Wrap(WrapExpr), /// An if-else expression: `if x { y } else { z }`. If(IfExpr), /// A while loop expression: `while x { y }`. @@ -245,6 +249,8 @@ impl TypedNode for Expr { NodeKind::WithExpr => node.cast().map(Self::With), NodeKind::LetExpr => node.cast().map(Self::Let), NodeKind::SetExpr => node.cast().map(Self::Set), + NodeKind::ShowExpr => node.cast().map(Self::Show), + NodeKind::WrapExpr => node.cast().map(Self::Wrap), NodeKind::IfExpr => node.cast().map(Self::If), NodeKind::WhileExpr => node.cast().map(Self::While), NodeKind::ForExpr => node.cast().map(Self::For), @@ -270,6 +276,8 @@ impl TypedNode for Expr { Self::With(v) => v.as_red(), Self::Let(v) => v.as_red(), Self::Set(v) => v.as_red(), + Self::Show(v) => v.as_red(), + Self::Wrap(v) => v.as_red(), Self::If(v) => v.as_red(), Self::While(v) => v.as_red(), Self::For(v) => v.as_red(), @@ -288,6 +296,8 @@ impl Expr { | Self::Call(_) | Self::Let(_) | Self::Set(_) + | Self::Show(_) + | Self::Wrap(_) | Self::If(_) | Self::While(_) | Self::For(_) @@ -913,6 +923,40 @@ impl IncludeExpr { } } +node! { + /// A show expression: `show heading(body) as [*{body}*]`. + ShowExpr +} + +impl ShowExpr { + /// The pattern that decides which node's appearence to redefine. + pub fn pattern(&self) -> Expr { + self.0.cast_first_child().expect("show expression is missing pattern") + } + + /// The expression that defines the node's appearence. + pub fn body(&self) -> Expr { + self.0.cast_last_child().expect("show expression is missing body") + } +} + +node! { + /// A wrap expression: wrap body in columns(2, body)`. + WrapExpr +} + +impl WrapExpr { + /// The binding to assign the remaining markup to. + pub fn binding(&self) -> Ident { + self.0.cast_first_child().expect("wrap expression is missing binding") + } + + /// The expression to evaluate. + pub fn body(&self) -> Expr { + self.0.cast_last_child().expect("wrap expression is missing body") + } +} + node! { /// An if-else expression: `if x { y } else { z }`. IfExpr diff --git a/src/syntax/highlight.rs b/src/syntax/highlight.rs index 0f1ee89dc..c399b487b 100644 --- a/src/syntax/highlight.rs +++ b/src/syntax/highlight.rs @@ -136,11 +136,14 @@ impl Category { NodeKind::With => Some(Category::Keyword), NodeKind::Let => Some(Category::Keyword), NodeKind::Set => Some(Category::Keyword), + NodeKind::Show => Some(Category::Keyword), + NodeKind::Wrap => Some(Category::Keyword), NodeKind::If => Some(Category::Keyword), NodeKind::Else => Some(Category::Keyword), NodeKind::While => Some(Category::Keyword), NodeKind::For => Some(Category::Keyword), NodeKind::In => Some(Category::Keyword), + NodeKind::As => Some(Category::Keyword), NodeKind::Break => Some(Category::Keyword), NodeKind::Continue => Some(Category::Keyword), NodeKind::Return => Some(Category::Keyword), @@ -211,6 +214,8 @@ impl Category { NodeKind::WithExpr => None, NodeKind::LetExpr => None, NodeKind::SetExpr => None, + NodeKind::ShowExpr => None, + NodeKind::WrapExpr => None, NodeKind::IfExpr => None, NodeKind::WhileExpr => None, NodeKind::ForExpr => None, diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 3a0f3a5e0..74a642cac 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -550,6 +550,10 @@ pub enum NodeKind { Let, /// The `set` keyword. Set, + /// The `show` keyword. + Show, + /// The `wrap` keyword. + Wrap, /// The `if` keyword. If, /// The `else` keyword. @@ -572,6 +576,8 @@ pub enum NodeKind { Include, /// The `from` keyword. From, + /// The `as` keyword. + As, /// Template markup of which all lines must start in some column. Markup(usize), /// One or more whitespace characters. @@ -665,6 +671,10 @@ pub enum NodeKind { LetExpr, /// A set expression: `set text(...)`. SetExpr, + /// A show expression: `show heading(body) as [*{body}*]`. + ShowExpr, + /// A wrap expression: `wrap body in columns(2, body)`. + WrapExpr, /// An if-else expression: `if x { y } else { z }`. IfExpr, /// A while loop expression: `while x { ... }`. @@ -822,10 +832,13 @@ impl NodeKind { Self::Auto => "`auto`", Self::Let => "keyword `let`", Self::Set => "keyword `set`", + Self::Show => "keyword `show`", + Self::Wrap => "keyword `wrap`", Self::If => "keyword `if`", Self::Else => "keyword `else`", Self::For => "keyword `for`", Self::In => "keyword `in`", + Self::As => "keyword `as`", Self::While => "keyword `while`", Self::Break => "keyword `break`", Self::Continue => "keyword `continue`", @@ -875,6 +888,8 @@ impl NodeKind { Self::WithExpr => "`with` expression", Self::LetExpr => "`let` expression", Self::SetExpr => "`set` expression", + Self::ShowExpr => "`show` expression", + Self::WrapExpr => "`wrap` expression", Self::IfExpr => "`if` expression", Self::WhileExpr => "while-loop expression", Self::ForExpr => "for-loop expression", diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index 4a05755f7..f8285f0c0 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -227,6 +227,8 @@ impl Pretty for Expr { Self::With(v) => v.pretty(p), Self::Let(v) => v.pretty(p), Self::Set(v) => v.pretty(p), + Self::Show(v) => v.pretty(p), + Self::Wrap(v) => v.pretty(p), Self::If(v) => v.pretty(p), Self::While(v) => v.pretty(p), Self::For(v) => v.pretty(p), @@ -456,6 +458,24 @@ impl Pretty for SetExpr { } } +impl Pretty for ShowExpr { + fn pretty(&self, p: &mut Printer) { + p.push_str("show "); + self.pattern().pretty(p); + p.push_str(" as "); + self.body().pretty(p); + } +} + +impl Pretty for WrapExpr { + fn pretty(&self, p: &mut Printer) { + p.push_str("wrap "); + self.binding().pretty(p); + p.push_str(" in "); + self.body().pretty(p); + } +} + impl Pretty for IfExpr { fn pretty(&self, p: &mut Printer) { p.push_str("if "); @@ -652,6 +672,8 @@ mod tests { roundtrip("#let x = 1 + 2"); roundtrip("#let f(x) = y"); roundtrip("#set text(size: 12pt)"); + roundtrip("#show heading(body) as [*{body}*]"); + roundtrip("#wrap body in columns(2, body)"); roundtrip("#if x [y] else [z]"); roundtrip("#if x {} else if y {} else {}"); roundtrip("#while x {y}"); diff --git a/tests/typ/style/show.typ b/tests/typ/style/show.typ new file mode 100644 index 000000000..42a35f27b --- /dev/null +++ b/tests/typ/style/show.typ @@ -0,0 +1,6 @@ +// Test show rules. +// Ref: false + +--- +// Error: 1-34 show rules are not yet implemented +#show heading(body) as [*{body}*] diff --git a/tests/typ/style/wrap.typ b/tests/typ/style/wrap.typ new file mode 100644 index 000000000..c665a9d29 --- /dev/null +++ b/tests/typ/style/wrap.typ @@ -0,0 +1,6 @@ +// Test wrap. +// Ref: false + +--- +// Error: 1-31 wrap is not yet implemented +#wrap body in columns(2, body)