summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-01-28 20:02:42 +0100
committerLaurenz <laurmaedje@gmail.com>2022-01-28 20:02:42 +0100
commit76b1d4a93f6d045901f17db46d82a97c9f407703 (patch)
treef851460a038a4c543e3900352ec1a2903b6c9849 /src
parent2d97d406aced1f1ab7d3bc459e31bb0eff92b892 (diff)
Parse `show` and `wrap` expressions
Diffstat (limited to 'src')
-rw-r--r--src/diag.rs5
-rw-r--r--src/eval/mod.rs18
-rw-r--r--src/parse/incremental.rs5
-rw-r--r--src/parse/mod.rs33
-rw-r--r--src/parse/tokens.rs3
-rw-r--r--src/syntax/ast.rs44
-rw-r--r--src/syntax/highlight.rs5
-rw-r--r--src/syntax/mod.rs15
-rw-r--r--src/syntax/pretty.rs22
9 files changed, 148 insertions, 2 deletions
diff --git a/src/diag.rs b/src/diag.rs
index be431e12..bd91df26 100644
--- a/src/diag.rs
+++ b/src/diag.rs
@@ -76,7 +76,10 @@ pub trait At<T> {
fn at(self, span: Span) -> TypResult<T>;
}
-impl<T> At<T> for StrResult<T> {
+impl<T, S> At<T> for Result<T, S>
+where
+ S: Into<String>,
+{
fn at(self, span: Span) -> TypResult<T> {
self.map_err(|message| Error::boxed(span, message))
}
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 58400f5a..2023004f 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<Self::Output> {
+ Err("show rules are not yet implemented").at(self.span())
+ }
+}
+
+impl Eval for WrapExpr {
+ type Output = Value;
+
+ fn eval(&self, _: &mut EvalContext) -> TypResult<Self::Output> {
+ 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 4c82f158..2edb84ba 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 a9752645..72d38b46 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 840a68e1..eef7a72d 100644
--- a/src/parse/tokens.rs
+++ b/src/parse/tokens.rs
@@ -540,10 +540,13 @@ fn keyword(ident: &str) -> Option<NodeKind> {
"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 bea4ef00..39854052 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(_)
@@ -914,6 +924,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 0f1ee89d..c399b487 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 3a0f3a5e..74a642ca 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 4a05755f..f8285f0c 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}");