From e8470824352064fefbb5d0d30a728211d11cea53 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Tue, 26 Jan 2021 21:13:52 +0100 Subject: [PATCH] =?UTF-8?q?Multi-expression=20blocks=20=F0=9F=9B=8D?= =?UTF-8?q?=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/eval/mod.rs | 28 ++++-- src/eval/ops.rs | 3 + src/geom/relative.rs | 8 ++ src/parse/mod.rs | 147 +++++++++++++++++--------------- src/parse/parser.rs | 34 +++++--- src/syntax/expr.rs | 51 +++++++---- src/syntax/span.rs | 3 + tests/lang/ref/blocks.png | Bin 640 -> 515 bytes tests/lang/ref/if.png | Bin 3970 -> 3840 bytes tests/lang/typ/blocks.typ | 14 ++- tests/lang/typ/bracket-call.typ | 18 ++-- tests/lang/typ/if.typ | 19 ++--- 12 files changed, 193 insertions(+), 132 deletions(-) diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 8563520eb..eaa0b08fe 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -68,7 +68,7 @@ impl Eval for &[Spanned] { fn eval(self, ctx: &mut EvalContext) -> Self::Output { for node in self { - node.as_ref().eval(ctx); + node.eval(ctx); } } } @@ -175,8 +175,8 @@ impl Eval for Spanned<&Expr> { Expr::Array(v) => Value::Array(v.with_span(self.span).eval(ctx)), Expr::Dict(v) => Value::Dict(v.with_span(self.span).eval(ctx)), Expr::Template(v) => Value::Template(v.clone()), - Expr::Group(v) => v.as_ref().eval(ctx), - Expr::Block(v) => v.as_ref().eval(ctx), + Expr::Group(v) => v.eval(ctx), + Expr::Block(v) => v.with_span(self.span).eval(ctx), Expr::Call(v) => v.with_span(self.span).eval(ctx), Expr::Unary(v) => v.with_span(self.span).eval(ctx), Expr::Binary(v) => v.with_span(self.span).eval(ctx), @@ -190,7 +190,7 @@ impl Eval for Spanned<&ExprArray> { type Output = ValueArray; fn eval(self, ctx: &mut EvalContext) -> Self::Output { - self.v.iter().map(|expr| expr.as_ref().eval(ctx)).collect() + self.v.iter().map(|expr| expr.eval(ctx)).collect() } } @@ -200,16 +200,28 @@ impl Eval for Spanned<&ExprDict> { fn eval(self, ctx: &mut EvalContext) -> Self::Output { self.v .iter() - .map(|Named { name, expr }| (name.v.0.clone(), expr.as_ref().eval(ctx))) + .map(|Named { name, expr }| (name.v.0.clone(), expr.eval(ctx))) .collect() } } +impl Eval for Spanned<&ExprBlock> { + type Output = Value; + + fn eval(self, ctx: &mut EvalContext) -> Self::Output { + let mut output = Value::None; + for expr in &self.v.exprs { + output = expr.eval(ctx); + } + output + } +} + impl Eval for Spanned<&ExprUnary> { type Output = Value; fn eval(self, ctx: &mut EvalContext) -> Self::Output { - let value = self.v.expr.as_ref().eval(ctx); + let value = self.v.expr.eval(ctx); if value == Value::Error { return Value::Error; } @@ -327,8 +339,8 @@ impl Eval for Spanned<&ExprLet> { type Output = Value; fn eval(self, ctx: &mut EvalContext) -> Self::Output { - let value = match &self.v.expr { - Some(expr) => expr.as_ref().eval(ctx), + let value = match &self.v.init { + Some(expr) => expr.eval(ctx), None => Value::None, }; ctx.scopes.define(self.v.pat.v.as_str(), value); diff --git a/src/eval/ops.rs b/src/eval/ops.rs index 939445f0a..56d6687b4 100644 --- a/src/eval/ops.rs +++ b/src/eval/ops.rs @@ -109,10 +109,13 @@ pub fn div(lhs: Value, rhs: Value) -> Value { (Float(a), Float(b)) => Float(a / b), (Length(a), Int(b)) => Length(a / b as f64), (Length(a), Float(b)) => Length(a / b), + (Length(a), Length(b)) => Float(a / b), (Angle(a), Int(b)) => Angle(a / b as f64), (Angle(a), Float(b)) => Angle(a / b), + (Angle(a), Angle(b)) => Float(a / b), (Relative(a), Int(b)) => Relative(a / b as f64), (Relative(a), Float(b)) => Relative(a / b), + (Relative(a), Relative(b)) => Float(a / b), (Linear(a), Int(b)) => Linear(a / b as f64), (Linear(a), Float(b)) => Linear(a / b), _ => Error, diff --git a/src/geom/relative.rs b/src/geom/relative.rs index cea7e4e3c..1d040c728 100644 --- a/src/geom/relative.rs +++ b/src/geom/relative.rs @@ -94,6 +94,14 @@ impl Div for Relative { } } +impl Div for Relative { + type Output = f64; + + fn div(self, other: Self) -> f64 { + self.0 / other.0 + } +} + assign_impl!(Relative += Relative); assign_impl!(Relative -= Relative); assign_impl!(Relative *= f64); diff --git a/src/parse/mod.rs b/src/parse/mod.rs index a3a387759..f731cd17e 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -43,7 +43,8 @@ fn tree(p: &mut Parser) -> Tree { /// Parse a syntax node. fn node(p: &mut Parser, at_start: &mut bool) -> Option { - let node = match p.peek()? { + let token = p.peek()?; + let node = match token { // Bracket call. Token::LeftBracket => { return Some(Node::Expr(bracket_call(p)?)); @@ -75,8 +76,21 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option { Token::UnicodeEscape(t) => Node::Text(unicode_escape(p, t)), // Keywords. - Token::Let => return Some(Node::Expr(stmt_let(p)?)), - Token::If => return Some(Node::Expr(expr_if(p)?)), + Token::Let | Token::If | Token::For => { + let stmt = token == Token::Let; + let group = if stmt { Group::Stmt } else { Group::Expr }; + + p.start_group(group, TokenMode::Code); + let expr = primary(p); + if stmt && expr.is_some() && !p.eof() { + p.expected_at("semicolon or line break", p.last_end()); + } + p.end_group(); + + // Uneat spaces we might have eaten eagerly. + p.jump(p.last_end()); + return expr.map(Node::Expr); + } // Comments. Token::LineComment(_) | Token::BlockComment(_) => { @@ -97,7 +111,7 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option { fn heading(p: &mut Parser) -> NodeHeading { // Count hashtags. let mut level = p.span(|p| { - p.eat_assert(Token::Hash); + p.assert(Token::Hash); let mut level = 0u8; while p.eat_if(Token::Hash) { @@ -194,10 +208,6 @@ fn bracket_subheader(p: &mut Parser) -> Option { p.start_group(Group::Subheader, TokenMode::Code); let name = p.span_if(ident); - if name.is_none() { - p.expected("function name"); - } - let args = p.span(arguments); p.end_group(); @@ -215,14 +225,6 @@ fn bracket_body(p: &mut Parser) -> Tree { tree } -/// Parse an identifier. -fn ident(p: &mut Parser) -> Option { - p.eat_map(|token| match token { - Token::Ident(id) => Some(Ident(id.into())), - _ => None, - }) -} - /// Parse an expression. fn expr(p: &mut Parser) -> Option { expr_with(p, 0) @@ -314,6 +316,10 @@ fn primary(p: &mut Parser) -> Option { Some(Token::Color(color)) => Expr::Color(color), Some(Token::Str(token)) => Expr::Str(string(p, token)), + // Keywords. + Some(Token::Let) => return expr_let(p), + Some(Token::If) => return expr_if(p), + // No value. _ => { p.expected("expression"); @@ -335,12 +341,20 @@ fn template(p: &mut Parser) -> Expr { /// Parse a block expression: `{...}`. fn block(p: &mut Parser) -> Option { p.start_group(Group::Brace, TokenMode::Code); - let expr = p.span_if(expr); + let mut exprs = vec![]; while !p.eof() { - p.unexpected(); + p.start_group(Group::Stmt, TokenMode::Code); + if let Some(expr) = p.span_if(expr) { + exprs.push(expr); + if !p.eof() { + p.expected_at("semicolon or line break", p.last_end()); + } + } + p.end_group(); + p.skip_white(); } p.end_group(); - Some(Expr::Block(Box::new(expr?))) + Some(Expr::Block(ExprBlock { exprs })) } /// Parse a parenthesized function call. @@ -363,72 +377,69 @@ fn string(p: &mut Parser, token: TokenStr) -> String { resolve::resolve_string(token.string) } -/// Parse a let statement. -fn stmt_let(p: &mut Parser) -> Option { - p.start_group(Group::Stmt, TokenMode::Code); - p.eat_assert(Token::Let); +/// Parse a let expression. +fn expr_let(p: &mut Parser) -> Option { + p.assert(Token::Let); - let pat = match p.span_if(ident) { - Some(pat) => pat, - None => { - p.expected("identifier"); - p.end_group(); - return None; + let mut expr_let = None; + if let Some(pat) = p.span_if(ident) { + let mut init = None; + if p.eat_if(Token::Eq) { + init = p.span_if(expr); } - }; - let rhs = if p.eat_if(Token::Eq) { p.span_if(expr) } else { None }; - - if !p.eof() { - p.expected_at("semicolon or line break", p.last_end()); + expr_let = Some(Expr::Let(ExprLet { pat, init: init.map(Box::new) })) } - p.end_group(); - - Some(Expr::Let(ExprLet { pat, expr: rhs.map(Box::new) })) + expr_let } /// Parse an if expresion. fn expr_if(p: &mut Parser) -> Option { - p.start_group(Group::Expr, TokenMode::Code); - p.eat_assert(Token::If); + p.assert(Token::If); - let condition = match p.span_if(expr) { - Some(condition) => Box::new(condition), - None => { - p.end_group(); - return None; + let mut expr_if = None; + if let Some(condition) = p.span_if(expr) { + if let Some(if_body) = p.span_if(body) { + let mut else_body = None; + if p.eat_if(Token::Else) { + else_body = p.span_if(body); + } + + expr_if = Some(Expr::If(ExprIf { + condition: Box::new(condition), + if_body: Box::new(if_body), + else_body: else_body.map(Box::new), + })); } - }; + } - p.end_group(); - - let if_body = Box::new(control_body(p)?); - - let start = p.last_end(); - p.skip_white(); - - let else_body = if p.eat_if(Token::Else) { - control_body(p).map(Box::new) - } else { - p.jump(start); - None - }; - - Some(Expr::If(ExprIf { condition, if_body, else_body })) + expr_if } -/// Parse a control flow body. -fn control_body(p: &mut Parser) -> Option> { - let start = p.last_end(); - p.skip_white(); + +/// Parse an identifier. +fn ident(p: &mut Parser) -> Option { match p.peek() { - Some(Token::LeftBracket) => Some(p.span(template)), - Some(Token::LeftBrace) => p.span_if(block), + Some(Token::Ident(id)) => { + p.eat(); + Some(Ident(id.into())) + } _ => { - p.expected_at("body", start); - p.jump(start); + p.expected("identifier"); + None + } + } +} + +/// Parse a control flow body. +fn body(p: &mut Parser) -> Option { + match p.peek() { + Some(Token::LeftBracket) => Some(template(p)), + Some(Token::LeftBrace) => block(p), + _ => { + p.expected_at("body", p.last_end()); None } } diff --git a/src/parse/parser.rs b/src/parse/parser.rs index f9ced34f5..906d9e623 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -103,10 +103,11 @@ impl<'s> Parser<'s> { self.groups.push(group); self.repeek(); + match group { - Group::Paren => self.eat_assert(Token::LeftParen), - Group::Bracket => self.eat_assert(Token::LeftBracket), - Group::Brace => self.eat_assert(Token::LeftBrace), + Group::Paren => self.assert(Token::LeftParen), + Group::Bracket => self.assert(Token::LeftBracket), + Group::Brace => self.assert(Token::LeftBrace), Group::Subheader => {} Group::Stmt => {} Group::Expr => {} @@ -199,8 +200,18 @@ impl<'s> Parser<'s> { mapped } + /// Consume the next token if it is the given one and produce an error if + /// not. + pub fn expect(&mut self, t: Token) -> bool { + let eaten = self.eat_if(t); + if !eaten { + self.expected(t.name()); + } + eaten + } + /// Consume the next token, debug-asserting that it is the given one. - pub fn eat_assert(&mut self, t: Token) { + pub fn assert(&mut self, t: Token) { let next = self.eat(); debug_assert_eq!(next, Some(t)); } @@ -277,6 +288,7 @@ impl<'s> Parser<'s> { scanner } + /// Move to the next token, skipping whitespace and comments in code mode. fn bump(&mut self) { self.last_end = self.tokens.pos(); self.next_start = self.tokens.pos(); @@ -286,11 +298,7 @@ impl<'s> Parser<'s> { TokenMode::Markup => {} TokenMode::Code => loop { match self.next { - Some(Token::Space(n)) => { - if n >= 1 && self.groups.last() == Some(&Group::Stmt) { - break; - } - } + Some(Token::Space(n)) if n < 1 || !self.in_line_group() => {} Some(Token::LineComment(_)) => {} Some(Token::BlockComment(_)) => {} _ => break, @@ -304,6 +312,7 @@ impl<'s> Parser<'s> { self.repeek(); } + /// Take another look at the next token to recheck whether it ends a group. fn repeek(&mut self) { self.peeked = self.next; let token = match self.next { @@ -316,13 +325,18 @@ impl<'s> Parser<'s> { Token::RightBracket if self.groups.contains(&Group::Bracket) => {} Token::RightBrace if self.groups.contains(&Group::Brace) => {} Token::Semicolon if self.groups.contains(&Group::Stmt) => {} - Token::Space(n) if n >= 1 && self.groups.last() == Some(&Group::Stmt) => {} + Token::Space(n) if n >= 1 && self.in_line_group() => {} Token::Pipe if self.groups.contains(&Group::Subheader) => {} _ => return, } self.peeked = None; } + + /// Whether the active group ends at a newline. + fn in_line_group(&self) -> bool { + matches!(self.groups.last(), Some(&Group::Stmt) | Some(&Group::Expr)) + } } impl Debug for Parser<'_> { diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index 6be9d632b..afeac9884 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -75,11 +75,7 @@ impl Pretty for Expr { v.v.pretty(p); p.push_str(")"); } - Self::Block(v) => { - p.push_str("{"); - v.v.pretty(p); - p.push_str("}"); - } + Self::Block(v) => v.pretty(p), Self::Unary(v) => v.pretty(p), Self::Binary(v) => v.pretty(p), Self::Call(v) => v.pretty(p), @@ -139,10 +135,28 @@ impl Pretty for Named { pub type ExprTemplate = Tree; /// A grouped expression: `(1 + 2)`. -pub type ExprGroup = Box>; +pub type ExprGroup = SpanBox; /// A block expression: `{1 + 2}`. -pub type ExprBlock = Box>; +#[derive(Debug, Clone, PartialEq)] +pub struct ExprBlock { + /// The list of expressions contained in the block. + pub exprs: SpanVec, +} + +impl Pretty for ExprBlock { + fn pretty(&self, p: &mut Printer) { + p.push_str("{"); + if self.exprs.len() > 1 { + p.push_str(" "); + } + p.join(&self.exprs, "; ", |expr, p| expr.v.pretty(p)); + if self.exprs.len() > 1 { + p.push_str(" "); + } + p.push_str("}"); + } +} /// A unary operation: `-x`. #[derive(Debug, Clone, PartialEq)] @@ -150,7 +164,7 @@ pub struct ExprUnary { /// The operator: `-`. pub op: Spanned, /// The expression to operator on: `x`. - pub expr: Box>, + pub expr: SpanBox, } impl Pretty for ExprUnary { @@ -213,11 +227,11 @@ impl Pretty for UnOp { #[derive(Debug, Clone, PartialEq)] pub struct ExprBinary { /// The left-hand side of the operation: `a`. - pub lhs: Box>, + pub lhs: SpanBox, /// The operator: `+`. pub op: Spanned, /// The right-hand side of the operation: `b`. - pub rhs: Box>, + pub rhs: SpanBox, } impl Pretty for ExprBinary { @@ -376,7 +390,7 @@ pub enum Associativity { #[derive(Debug, Clone, PartialEq)] pub struct ExprCall { /// The callee of the function. - pub callee: Box>, + pub callee: SpanBox, /// The arguments to the function. pub args: Spanned, } @@ -466,17 +480,17 @@ impl Pretty for Argument { pub struct ExprLet { /// The pattern to assign to. pub pat: Spanned, - /// The expression to assign to the pattern. - pub expr: Option>>, + /// The expression the pattern is initialized with. + pub init: Option>, } impl Pretty for ExprLet { fn pretty(&self, p: &mut Printer) { p.push_str("#let "); p.push_str(&self.pat.v); - if let Some(expr) = &self.expr { + if let Some(init) = &self.init { p.push_str(" = "); - expr.v.pretty(p); + init.v.pretty(p); } } } @@ -485,11 +499,11 @@ impl Pretty for ExprLet { #[derive(Debug, Clone, PartialEq)] pub struct ExprIf { /// The condition which selects the body to evaluate. - pub condition: Box>, + pub condition: SpanBox, /// The expression to evaluate if the condition is true. - pub if_body: Box>, + pub if_body: SpanBox, /// The expression to evaluate if the condition is false. - pub else_body: Option>>, + pub else_body: Option>, } impl Pretty for ExprIf { @@ -520,6 +534,7 @@ mod tests { #[test] fn test_pretty_print_expressions() { // Unary and binary operations. + test_pretty("{}", "{}"); test_pretty("{1 +}", "{1}"); test_pretty("{1++1}", "{1 + +1}"); test_pretty("{+-1}", "{+-1}"); diff --git a/src/syntax/span.rs b/src/syntax/span.rs index 21fa9ab8c..5087dffa2 100644 --- a/src/syntax/span.rs +++ b/src/syntax/span.rs @@ -34,6 +34,9 @@ impl Offset for SpanVec { } } +/// A box of a spanned value of type `T`. +pub type SpanBox = Box>; + /// A value with the span it corresponds to in the source code. #[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] diff --git a/tests/lang/ref/blocks.png b/tests/lang/ref/blocks.png index bc96be95a11b9f54f20ca200f6416dba69537a86..8dd7f9404800d9344d506d45b673559d9f9f898f 100644 GIT binary patch literal 515 zcmeAS@N?(olHy`uVBq!ia0y~yU}OQZ7jggz29~4q?12KOJY5_^DsH{K?OS@tL8R^B zYlT7vJ|Xr84OSj9TlkFlv=mDPWKv{$T0A;h)^{K3W8K^^ulW7%yu`0EX`hV6jg^34 zk$Uh32cX#k_=uKz*At89E>iYMyC}FMH+He|pIT{zj+Wyb(OxI}G|x^Hymq_&ULB8( zS2F_>he88Z;?QnEm8l!w+k54-+~hmFbfc+>levdh8Bgy9?@4;wTJH&~cuqe4HK6-K z^`4Rtr8QVB3Cnu6((PyOPAfiVY1DK*{ft)2 z%eWV|2eWb53v|#P@r5y$uRd11vYBJjwu@)hd^Ou2*1n;>WU-J}Riu{sz*LMME zUze+Ww@&4jgSVe9EDJiiCUy_w6{VclcXo!XxFh_{Sz1={SH%0=ebMv9zV-!Py4OD` z~VbjJl6@+CPx}q$mBk&#Tg7Bi4RdM;3#1K%d}>iQR9qeQ&1E$ Nc)I$ztaD0e0sz*Oxz_*y delta 508 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 JFwo8s{|0?PsU-ja literal 3970 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