diff --git a/src/eval/mod.rs b/src/eval/mod.rs index efa74596e..32a7d6ba0 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -4,6 +4,7 @@ mod value; mod call; mod context; +mod ops; mod scope; mod state; @@ -45,17 +46,6 @@ pub trait Eval { fn eval(self, ctx: &mut EvalContext) -> Self::Output; } -impl<'a, T> Eval for &'a Box> -where - Spanned<&'a T>: Eval, -{ - type Output = as Eval>::Output; - - fn eval(self, ctx: &mut EvalContext) -> Self::Output { - (**self).as_ref().eval(ctx) - } -} - impl Eval for &[Spanned] { type Output = (); @@ -171,6 +161,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().with_span(self.span).eval(ctx), + Expr::Block(v) => v.as_ref().with_span(self.span).eval(ctx), } } } @@ -179,7 +171,7 @@ impl Eval for Spanned<&ExprUnary> { type Output = Value; fn eval(self, ctx: &mut EvalContext) -> Self::Output { - let value = self.v.expr.eval(ctx); + let value = (*self.v.expr).as_ref().eval(ctx); if let Value::Error = value { return Value::Error; @@ -187,7 +179,8 @@ impl Eval for Spanned<&ExprUnary> { let span = self.v.op.span.join(self.v.expr.span); match self.v.op.v { - UnOp::Neg => neg(ctx, span, value), + UnOp::Pos => ops::pos(ctx, span, value), + UnOp::Neg => ops::neg(ctx, span, value), } } } @@ -196,8 +189,8 @@ impl Eval for Spanned<&ExprBinary> { type Output = Value; fn eval(self, ctx: &mut EvalContext) -> Self::Output { - let lhs = self.v.lhs.eval(ctx); - let rhs = self.v.rhs.eval(ctx); + let lhs = (*self.v.lhs).as_ref().eval(ctx); + let rhs = (*self.v.rhs).as_ref().eval(ctx); if lhs == Value::Error || rhs == Value::Error { return Value::Error; @@ -205,10 +198,10 @@ impl Eval for Spanned<&ExprBinary> { let span = self.v.lhs.span.join(self.v.rhs.span); match self.v.op.v { - BinOp::Add => add(ctx, span, lhs, rhs), - BinOp::Sub => sub(ctx, span, lhs, rhs), - BinOp::Mul => mul(ctx, span, lhs, rhs), - BinOp::Div => div(ctx, span, lhs, rhs), + BinOp::Add => ops::add(ctx, span, lhs, rhs), + BinOp::Sub => ops::sub(ctx, span, lhs, rhs), + BinOp::Mul => ops::mul(ctx, span, lhs, rhs), + BinOp::Div => ops::div(ctx, span, lhs, rhs), } } } @@ -231,172 +224,3 @@ impl Eval for Spanned<&ExprDict> { .collect() } } - -/// Compute the negation of a value. -fn neg(ctx: &mut EvalContext, span: Span, value: Value) -> Value { - use Value::*; - match value { - Int(v) => Int(-v), - Float(v) => Float(-v), - Length(v) => Length(-v), - Relative(v) => Relative(-v), - Linear(v) => Linear(-v), - v => { - ctx.diag(error!(span, "cannot negate {}", v.type_name())); - Value::Error - } - } -} - -/// Compute the sum of two values. -fn add(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value { - use Value::*; - match (lhs, rhs) { - // Numbers to themselves. - (Int(a), Int(b)) => Int(a + b), - (Int(a), Float(b)) => Float(a as f64 + b), - (Float(a), Int(b)) => Float(a + b as f64), - (Float(a), Float(b)) => Float(a + b), - - // Lengths, relatives and linears to themselves. - (Length(a), Length(b)) => Length(a + b), - (Length(a), Relative(b)) => Linear(a + b), - (Length(a), Linear(b)) => Linear(a + b), - - (Relative(a), Length(b)) => Linear(a + b), - (Relative(a), Relative(b)) => Relative(a + b), - (Relative(a), Linear(b)) => Linear(a + b), - - (Linear(a), Length(b)) => Linear(a + b), - (Linear(a), Relative(b)) => Linear(a + b), - (Linear(a), Linear(b)) => Linear(a + b), - - // Complex data types to themselves. - (Str(a), Str(b)) => Str(a + &b), - (Array(a), Array(b)) => Array(concat(a, b)), - (Dict(a), Dict(b)) => Dict(concat(a, b)), - (Template(a), Template(b)) => Template(concat(a, b)), - - (a, b) => { - ctx.diag(error!( - span, - "cannot add {} and {}", - a.type_name(), - b.type_name() - )); - Value::Error - } - } -} - -/// Compute the difference of two values. -fn sub(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value { - use Value::*; - match (lhs, rhs) { - // Numbers from themselves. - (Int(a), Int(b)) => Int(a - b), - (Int(a), Float(b)) => Float(a as f64 - b), - (Float(a), Int(b)) => Float(a - b as f64), - (Float(a), Float(b)) => Float(a - b), - - // Lengths, relatives and linears from themselves. - (Length(a), Length(b)) => Length(a - b), - (Length(a), Relative(b)) => Linear(a - b), - (Length(a), Linear(b)) => Linear(a - b), - (Relative(a), Length(b)) => Linear(a - b), - (Relative(a), Relative(b)) => Relative(a - b), - (Relative(a), Linear(b)) => Linear(a - b), - (Linear(a), Length(b)) => Linear(a - b), - (Linear(a), Relative(b)) => Linear(a - b), - (Linear(a), Linear(b)) => Linear(a - b), - - (a, b) => { - ctx.diag(error!( - span, - "cannot subtract {1} from {0}", - a.type_name(), - b.type_name() - )); - Value::Error - } - } -} - -/// Compute the product of two values. -fn mul(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value { - use Value::*; - match (lhs, rhs) { - // Numbers with themselves. - (Int(a), Int(b)) => Int(a * b), - (Int(a), Float(b)) => Float(a as f64 * b), - (Float(a), Int(b)) => Float(a * b as f64), - (Float(a), Float(b)) => Float(a * b), - - // Lengths, relatives and linears with numbers. - (Length(a), Int(b)) => Length(a * b as f64), - (Length(a), Float(b)) => Length(a * b), - (Int(a), Length(b)) => Length(a as f64 * b), - (Float(a), Length(b)) => Length(a * b), - (Relative(a), Int(b)) => Relative(a * b as f64), - (Relative(a), Float(b)) => Relative(a * b), - (Int(a), Relative(b)) => Relative(a as f64 * b), - (Float(a), Relative(b)) => Relative(a * b), - (Linear(a), Int(b)) => Linear(a * b as f64), - (Linear(a), Float(b)) => Linear(a * b), - (Int(a), Linear(b)) => Linear(a as f64 * b), - (Float(a), Linear(b)) => Linear(a * b), - - // Integers with strings. - (Int(a), Str(b)) => Str(b.repeat(0.max(a) as usize)), - (Str(a), Int(b)) => Str(a.repeat(0.max(b) as usize)), - - (a, b) => { - ctx.diag(error!( - span, - "cannot multiply {} with {}", - a.type_name(), - b.type_name() - )); - Value::Error - } - } -} - -/// Compute the quotient of two values. -fn div(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value { - use Value::*; - match (lhs, rhs) { - // Numbers by themselves. - (Int(a), Int(b)) => Float(a as f64 / b as f64), - (Int(a), Float(b)) => Float(a as f64 / b), - (Float(a), Int(b)) => Float(a / b as f64), - (Float(a), Float(b)) => Float(a / b), - - // Lengths by numbers. - (Length(a), Int(b)) => Length(a / b as f64), - (Length(a), Float(b)) => Length(a / b), - (Relative(a), Int(b)) => Relative(a / b as f64), - (Relative(a), Float(b)) => Relative(a / b), - (Linear(a), Int(b)) => Linear(a / b as f64), - (Linear(a), Float(b)) => Linear(a / b), - - (a, b) => { - ctx.diag(error!( - span, - "cannot divide {} by {}", - a.type_name(), - b.type_name() - )); - Value::Error - } - } -} - -/// Concatenate two collections. -fn concat(mut a: T, b: T) -> T -where - T: Extend + IntoIterator, -{ - a.extend(b); - a -} diff --git a/src/eval/ops.rs b/src/eval/ops.rs new file mode 100644 index 000000000..0a273da56 --- /dev/null +++ b/src/eval/ops.rs @@ -0,0 +1,183 @@ +use super::*; + +/// Apply plus operator to a value. +pub fn pos(ctx: &mut EvalContext, span: Span, value: Value) -> Value { + if value.is_numeric() { + value + } else { + ctx.diag(error!( + span, + "cannot apply plus operator to {}", + value.type_name() + )); + Value::Error + } +} + +/// Compute the negation of a value. +pub fn neg(ctx: &mut EvalContext, span: Span, value: Value) -> Value { + use Value::*; + match value { + Int(v) => Int(-v), + Float(v) => Float(-v), + Length(v) => Length(-v), + Angle(v) => Angle(-v), + Relative(v) => Relative(-v), + Linear(v) => Linear(-v), + v => { + ctx.diag(error!(span, "cannot negate {}", v.type_name())); + Value::Error + } + } +} + +/// Compute the sum of two values. +pub fn add(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value { + use Value::*; + match (lhs, rhs) { + // Numeric types to themselves. + (Int(a), Int(b)) => Int(a + b), + (Int(a), Float(b)) => Float(a as f64 + b), + (Float(a), Int(b)) => Float(a + b as f64), + (Float(a), Float(b)) => Float(a + b), + (Angle(a), Angle(b)) => Angle(a + b), + (Length(a), Length(b)) => Length(a + b), + (Length(a), Relative(b)) => Linear(a + b), + (Length(a), Linear(b)) => Linear(a + b), + (Relative(a), Length(b)) => Linear(a + b), + (Relative(a), Relative(b)) => Relative(a + b), + (Relative(a), Linear(b)) => Linear(a + b), + (Linear(a), Length(b)) => Linear(a + b), + (Linear(a), Relative(b)) => Linear(a + b), + (Linear(a), Linear(b)) => Linear(a + b), + + // Complex data types to themselves. + (Str(a), Str(b)) => Str(a + &b), + (Array(a), Array(b)) => Array(concat(a, b)), + (Dict(a), Dict(b)) => Dict(concat(a, b)), + (Template(a), Template(b)) => Template(concat(a, b)), + + (a, b) => { + ctx.diag(error!( + span, + "cannot add {} and {}", + a.type_name(), + b.type_name() + )); + Value::Error + } + } +} + +/// Compute the difference of two values. +pub fn sub(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value { + use Value::*; + match (lhs, rhs) { + // Numbers from themselves. + (Int(a), Int(b)) => Int(a - b), + (Int(a), Float(b)) => Float(a as f64 - b), + (Float(a), Int(b)) => Float(a - b as f64), + (Float(a), Float(b)) => Float(a - b), + (Angle(a), Angle(b)) => Angle(a - b), + (Length(a), Length(b)) => Length(a - b), + (Length(a), Relative(b)) => Linear(a - b), + (Length(a), Linear(b)) => Linear(a - b), + (Relative(a), Length(b)) => Linear(a - b), + (Relative(a), Relative(b)) => Relative(a - b), + (Relative(a), Linear(b)) => Linear(a - b), + (Linear(a), Length(b)) => Linear(a - b), + (Linear(a), Relative(b)) => Linear(a - b), + (Linear(a), Linear(b)) => Linear(a - b), + + (a, b) => { + ctx.diag(error!( + span, + "cannot subtract {1} from {0}", + a.type_name(), + b.type_name() + )); + Value::Error + } + } +} + +/// Compute the product of two values. +pub fn mul(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value { + use Value::*; + match (lhs, rhs) { + // Numeric types with numbers. + (Int(a), Int(b)) => Int(a * b), + (Int(a), Float(b)) => Float(a as f64 * b), + (Float(a), Int(b)) => Float(a * b as f64), + (Float(a), Float(b)) => Float(a * b), + (Length(a), Int(b)) => Length(a * b as f64), + (Length(a), Float(b)) => Length(a * b), + (Int(a), Length(b)) => Length(a as f64 * b), + (Float(a), Length(b)) => Length(a * b), + (Angle(a), Int(b)) => Angle(a * b as f64), + (Angle(a), Float(b)) => Angle(a * b), + (Int(a), Angle(b)) => Angle(a as f64 * b), + (Float(a), Angle(b)) => Angle(a * b), + (Relative(a), Int(b)) => Relative(a * b as f64), + (Relative(a), Float(b)) => Relative(a * b), + (Int(a), Relative(b)) => Relative(a as f64 * b), + (Float(a), Relative(b)) => Relative(a * b), + (Linear(a), Int(b)) => Linear(a * b as f64), + (Linear(a), Float(b)) => Linear(a * b), + (Int(a), Linear(b)) => Linear(a as f64 * b), + (Float(a), Linear(b)) => Linear(a * b), + + // Integers with strings. + (Int(a), Str(b)) => Str(b.repeat(0.max(a) as usize)), + (Str(a), Int(b)) => Str(a.repeat(0.max(b) as usize)), + + (a, b) => { + ctx.diag(error!( + span, + "cannot multiply {} with {}", + a.type_name(), + b.type_name() + )); + Value::Error + } + } +} + +/// Compute the quotient of two values. +pub fn div(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value { + use Value::*; + match (lhs, rhs) { + // Numeric types by numbers. + (Int(a), Int(b)) => Float(a as f64 / b as f64), + (Int(a), Float(b)) => Float(a as f64 / b), + (Float(a), Int(b)) => Float(a / b as f64), + (Float(a), Float(b)) => Float(a / b), + (Length(a), Int(b)) => Length(a / b as f64), + (Length(a), Float(b)) => Length(a / b), + (Angle(a), Int(b)) => Angle(a / b as f64), + (Angle(a), Float(b)) => Angle(a / b), + (Relative(a), Int(b)) => Relative(a / b as f64), + (Relative(a), Float(b)) => Relative(a / b), + (Linear(a), Int(b)) => Linear(a / b as f64), + (Linear(a), Float(b)) => Linear(a / b), + + (a, b) => { + ctx.diag(error!( + span, + "cannot divide {} by {}", + a.type_name(), + b.type_name() + )); + Value::Error + } + } +} + +/// Concatenate two collections. +fn concat(mut a: T, b: T) -> T +where + T: Extend + IntoIterator, +{ + a.extend(b); + a +} diff --git a/src/eval/value.rs b/src/eval/value.rs index d3b2688b0..13548c87e 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -8,7 +8,7 @@ use super::{Args, Eval, EvalContext}; use crate::color::Color; use crate::geom::{Angle, Length, Linear, Relative}; use crate::pretty::{pretty, Pretty, Printer}; -use crate::syntax::{pretty_template_expr, Spanned, Tree, WithSpan}; +use crate::syntax::{Spanned, Tree, WithSpan}; /// A computational value. #[derive(Debug, Clone, PartialEq)] @@ -77,6 +77,18 @@ impl Value { Self::Error => "error", } } + + /// Whether the value is numeric. + pub fn is_numeric(&self) -> bool { + matches!(self, + Value::Int(_) + | Value::Float(_) + | Value::Length(_) + | Value::Angle(_) + | Value::Relative(_) + | Value::Linear(_) + ) + } } impl Eval for &Value { @@ -112,9 +124,13 @@ impl Pretty for Value { Value::Linear(v) => write!(p, "{}", v).unwrap(), Value::Color(v) => write!(p, "{}", v).unwrap(), Value::Str(v) => write!(p, "{:?}", v).unwrap(), - Value::Array(array) => array.pretty(p), - Value::Dict(dict) => dict.pretty(p), - Value::Template(template) => pretty_template_expr(template, p), + Value::Array(v) => v.pretty(p), + Value::Dict(v) => v.pretty(p), + Value::Template(v) => { + p.push_str("["); + v.pretty(p); + p.push_str("]"); + } Value::Func(v) => v.pretty(p), Value::Any(v) => v.pretty(p), Value::Error => p.push_str("(error)"), diff --git a/src/parse/collection.rs b/src/parse/collection.rs index 98d4219f7..ca05b9985 100644 --- a/src/parse/collection.rs +++ b/src/parse/collection.rs @@ -59,7 +59,7 @@ fn argument(p: &mut Parser) -> Option { p.deco(Deco::Name.with_span(name.span)); Some(Argument::Named(Named { name, expr })) } else { - p.diag(error!(first.span, "name must be identifier")); + p.diag(error!(first.span, "expected identifier")); expr(p); None } @@ -93,7 +93,7 @@ impl State { fn into_expr(self) -> Expr { match self { Self::Unknown => Expr::Array(vec![]), - Self::Expr(expr) => expr.v, + Self::Expr(expr) => Expr::Group(Box::new(expr.v)), Self::Array(array) => Expr::Array(array), Self::Dict(dict) => Expr::Dict(dict), } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index ee7991590..a66660e56 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -51,12 +51,12 @@ fn node(p: &mut Parser, at_start: bool) -> Option { let node = match p.peek()? { // Bracket call. Token::LeftBracket => { - return Some(Node::Expr(Expr::Call(bracket_call(p)))); + return Some(Node::Expr(bracket_call(p))); } // Code block. Token::LeftBrace => { - return Some(Node::Expr(code_block(p)?)); + return Some(Node::Expr(block(p)?)); } // Markup. @@ -154,7 +154,7 @@ fn unicode_escape(p: &mut Parser, token: TokenUnicodeEscape) -> String { } /// Parse a bracketed function call. -fn bracket_call(p: &mut Parser) -> ExprCall { +fn bracket_call(p: &mut Parser) -> Expr { p.push_mode(TokenMode::Code); p.start_group(Group::Bracket); @@ -184,7 +184,7 @@ fn bracket_call(p: &mut Parser) -> ExprCall { inner = top; } - inner.v + Expr::Call(inner.v) } /// Parse one subheader of a bracketed function call. @@ -218,8 +218,8 @@ fn bracket_body(p: &mut Parser) -> Tree { tree } -/// Parse a code block: `{...}`. -fn code_block(p: &mut Parser) -> Option { +/// Parse a block expression: `{...}`. +fn block(p: &mut Parser) -> Option { p.push_mode(TokenMode::Code); p.start_group(Group::Brace); let expr = expr(p); @@ -228,7 +228,7 @@ fn code_block(p: &mut Parser) -> Option { } p.pop_mode(); p.end_group(); - expr + Some(Expr::Block(Box::new(expr?))) } /// Parse an expression: `term (+ term)*`. @@ -277,6 +277,7 @@ fn binops( /// Parse a factor of the form `-?value`. fn factor(p: &mut Parser) -> Option { let op = |token| match token { + Token::Plus => Some(UnOp::Pos), Token::Hyph => Some(UnOp::Neg), _ => None, }; @@ -294,12 +295,12 @@ fn value(p: &mut Parser) -> Option { let expr = match p.peek() { // Template. Some(Token::LeftBracket) => { - return Some(Expr::Template(template(p))); + return Some(template(p)); } // Nested block. Some(Token::LeftBrace) => { - return code_block(p); + return block(p); } // Dictionary or just a parenthesized expression. @@ -313,7 +314,7 @@ fn value(p: &mut Parser) -> Option { let ident = Ident(id.into()); if p.peek() == Some(Token::LeftParen) { let name = ident.with_span(p.peek_span()); - return Some(Expr::Call(paren_call(p, name))); + return Some(paren_call(p, name)); } else { return Some(Expr::Ident(ident)); } @@ -341,21 +342,21 @@ fn value(p: &mut Parser) -> Option { } // Parse a template value: `[...]`. -fn template(p: &mut Parser) -> Tree { +fn template(p: &mut Parser) -> Expr { p.push_mode(TokenMode::Markup); p.start_group(Group::Bracket); let tree = tree(p); p.pop_mode(); p.end_group(); - tree + Expr::Template(tree) } /// Parse a parenthesized function call. -fn paren_call(p: &mut Parser, name: Spanned) -> ExprCall { +fn paren_call(p: &mut Parser, name: Spanned) -> Expr { p.start_group(Group::Paren); let args = p.span(arguments); p.end_group(); - ExprCall { name, args } + Expr::Call(ExprCall { name, args }) } /// Parse an identifier. diff --git a/src/parse/tests.rs b/src/parse/tests.rs index a8897c1cb..b9a3d3015 100644 --- a/src/parse/tests.rs +++ b/src/parse/tests.rs @@ -10,8 +10,8 @@ use crate::syntax::*; use BinOp::*; use Expr::{Angle, Bool, Color, Float, Int, Length, Percent}; -use Node::{Emph, Expr as Block, Linebreak, Parbreak, Space, Strong}; -use UnOp::*; +use Node::{Emph, Linebreak, Parbreak, Space, Strong}; +use UnOp::{Neg, Pos}; macro_rules! t { ($src:literal @@ -126,21 +126,26 @@ fn Unary(op: impl Into>, expr: impl Into>) -> Expr { }) } -macro_rules! Array { - (@$($expr:expr),* $(,)?) => { - vec![$(into!($expr)),*] - }; - ($($tts:tt)*) => (Expr::Array(Array![@$($tts)*])); +fn Group(expr: Expr) -> Expr { + Expr::Group(Box::new(expr)) } -macro_rules! Dict { - (@$($name:expr => $expr:expr),* $(,)?) => { - vec![$(Named { - name: into!($name).map(|s: &str| Ident(s.into())), - expr: into!($expr) - }),*] +macro_rules! Call { + (@@$name:expr) => { + Call!(@@$name, Args![]) + }; + (@@$name:expr, $args:expr) => { + ExprCall { + name: into!($name).map(|s: &str| Ident(s.into())), + args: into!($args), + } + }; + (@$($tts:tt)*) => { + Expr::Call(Call!(@@$($tts)*)) + }; + ($($tts:tt)*) => { + Node::Expr(Call!(@$($tts)*)) }; - ($($tts:tt)*) => (Expr::Dict(Dict![@$($tts)*])); } macro_rules! Args { @@ -158,23 +163,43 @@ macro_rules! Args { }; } -macro_rules! Template { - (@$($node:expr),* $(,)?) => (vec![$(into!($node)),*]); - ($($tts:tt)*) => (Expr::Template(Template![@$($tts)*])); +macro_rules! Array { + (@$($expr:expr),* $(,)?) => { + vec![$(into!($expr)),*] + }; + ($($tts:tt)*) => { + Expr::Array(Array![@$($tts)*]) + }; } -macro_rules! Call { - (@@$name:expr) => { - Call!(@@$name, Args![]) - }; - (@@$name:expr, $args:expr) => { - ExprCall { +macro_rules! Dict { + (@$($name:expr => $expr:expr),* $(,)?) => { + vec![$(Named { name: into!($name).map(|s: &str| Ident(s.into())), - args: into!($args), - } + expr: into!($expr) + }),*] + }; + ($($tts:tt)*) => { + Expr::Dict(Dict![@$($tts)*]) + }; +} + +macro_rules! Template { + (@$($node:expr),* $(,)?) => { + vec![$(into!($node)),*] + }; + ($($tts:tt)*) => { + Expr::Template(Template![@$($tts)*]) + }; +} + +macro_rules! Block { + (@$expr:expr) => { + Expr::Block(Box::new($expr)) + }; + ($expr:expr) => { + Node::Expr(Block!(@$expr)) }; - (@$($tts:tt)*) => (Expr::Call(Call!(@@$($tts)*))); - ($($tts:tt)*) => (Node::Expr(Call!(@$($tts)*))); } #[test] @@ -247,7 +272,7 @@ fn test_parse_headings() { // Continued heading. t!("# a{\n1\n}b" Heading(0, Template![ - @Space, Text("a"), Block(Int(1)), Text("b") + @Space, Text("a"), Block!(Int(1)), Text("b") ])); t!("# a[f][\n\n]d" Heading(0, Template![@ Space, Text("a"), Call!("f", Args![Template![Parbreak]]), Text("d"), @@ -294,7 +319,7 @@ fn test_parse_escape_sequences() { fn test_parse_groups() { // Test paren group. t!("{({1) + 3}" - nodes: [Block(Binary(Int(1), Add, Int(3)))], + nodes: [Block!(Binary(Group(Block!(@Int(1))), Add, Int(3)))], errors: [S(4..4, "expected closing brace")]); // Test bracket group. @@ -309,7 +334,7 @@ fn test_parse_groups() { // Test brace group. t!("{1 + [}" - nodes: [Block(Binary(Int(1), Add, Template![]))], + nodes: [Block!(Binary(Int(1), Add, Template![]))], errors: [S(6..6, "expected closing bracket")]); // Test subheader group. @@ -322,11 +347,11 @@ fn test_parse_groups() { #[test] fn test_parse_blocks() { // Basic with spans. - t!("{1}" nodes: [S(0..3, Block(Int(1)))], spans: true); + t!("{1}" nodes: [S(0..3, Block!(Int(1)))], spans: true); // Function calls. - t!("{f()}" Call!("f")); - t!("{[[f]]}" Block(Template![Call!("f")])); + t!("{f()}" Block!(Call!(@"f"))); + t!("{[[f]]}" Block!(Template![Call!("f")])); // Missing or bad value. t!("{}{1u}" @@ -336,7 +361,7 @@ fn test_parse_blocks() { // Too much stuff. t!("{1 #{} end" - nodes: [Block(Int(1)), Space, Text("end")], + nodes: [Block!(Int(1)), Space, Text("end")], errors: [S(3..4, "unexpected hex value"), S(4..5, "unexpected opening brace")]); } @@ -424,7 +449,7 @@ fn test_parse_arguments() { t!("[v a:2]" Call!("v", Args!["a" => Int(2)])); // Parenthesized function with nested array literal. - t!(r#"{f(1, a: (2, 3), #004, b: "five")}"# Block(Call!(@"f", Args![ + t!(r#"{f(1, a: (2, 3), #004, b: "five")}"# Block!(Call!(@"f", Args![ Int(1), "a" => Array![Int(2), Int(3)], Color(RgbaColor::new(0, 0, 0x44, 0xff)), @@ -449,30 +474,35 @@ fn test_parse_arguments() { // Name has to be identifier. t!("[v 1:]" nodes: [Call!("v", Args![])], - errors: [S(3..4, "name must be identifier"), + errors: [S(3..4, "expected identifier"), S(5..5, "expected expression")]); // Name has to be identifier. t!("[v 1:2]" nodes: [Call!("v", Args![])], - errors: [S(3..4, "name must be identifier")]); + errors: [S(3..4, "expected identifier")]); + + // Name has to be identifier. + t!("[v (x):1]" + nodes: [Call!("v", Args![])], + errors: [S(3..6, "expected identifier")]); } #[test] fn test_parse_arrays() { // Empty array. - t!("{()}" Block(Array![])); + t!("{()}" Block!(Array![])); // Array with one item and trailing comma + spans. t!("{-(1,)}" - nodes: [S(0..7, Block(Unary( + nodes: [S(0..7, Block!(Unary( S(1..2, Neg), S(2..6, Array![S(3..4, Int(1))]) )))], spans: true); // Array with three items and trailing comma. - t!(r#"{("one", 2, #003,)}"# Block(Array![ + t!(r#"{("one", 2, #003,)}"# Block!(Array![ Str("one"), Int(2), Color(RgbaColor::new(0, 0, 0x33, 0xff)) @@ -480,44 +510,44 @@ fn test_parse_arrays() { // Unclosed. t!("{(}" - nodes: [Block(Array![])], + nodes: [Block!(Array![])], errors: [S(2..2, "expected closing paren")]); // Missing comma + invalid token. t!("{(1*/2)}" - nodes: [Block(Array![Int(1), Int(2)])], + nodes: [Block!(Array![Int(1), Int(2)])], errors: [S(3..5, "expected expression, found end of block comment"), S(3..3, "expected comma")]); // Invalid token. t!("{(1, 1u 2)}" - nodes: [Block(Array![Int(1), Int(2)])], + nodes: [Block!(Array![Int(1), Int(2)])], errors: [S(5..7, "expected expression, found invalid token")]); // Coerced to expression with leading comma. t!("{(,1)}" - nodes: [Block(Int(1))], + nodes: [Block!(Group(Int(1)))], errors: [S(2..3, "expected expression, found comma")]); // Missing expression after name makes this an array. t!("{(a:)}" - nodes: [Block(Array![])], + nodes: [Block!(Array![])], errors: [S(4..4, "expected expression")]); // Expected expression, found named pair. t!("{(1, b: 2)}" - nodes: [Block(Array![Int(1)])], + nodes: [Block!(Array![Int(1)])], errors: [S(5..9, "expected expression, found named pair")]); } #[test] fn test_parse_dictionaries() { // Empty dictionary. - t!("{(:)}" Block(Dict![])); + t!("{(:)}" Block!(Dict![])); // Dictionary with two pairs + spans. t!("{(one: 1, two: 2)}" - nodes: [S(0..18, Block(Dict![ + nodes: [S(0..18, Block!(Dict![ S(2..5, "one") => S(7..8, Int(1)), S(10..13, "two") => S(15..16, Int(2)), ]))], @@ -525,49 +555,50 @@ fn test_parse_dictionaries() { // Expected named pair, found expression. t!("{(a: 1, b)}" - nodes: [Block(Dict!["a" => Int(1)])], + nodes: [Block!(Dict!["a" => Int(1)])], errors: [S(8..9, "expected named pair, found expression")]); // Dictionary marker followed by more stuff. t!("{(:1 b:[], true::)}" - nodes: [Block(Dict!["b" => Template![]])], + nodes: [Block!(Dict!["b" => Template![]])], errors: [S(3..4, "expected named pair, found expression"), S(4..4, "expected comma"), - S(11..15, "name must be identifier"), + S(11..15, "expected identifier"), S(16..17, "expected expression, found colon")]); } #[test] fn test_parse_expressions() { // Parentheses. - t!("{(x)}{(1)}" Block(Id("x")), Block(Int(1))); + t!("{(x)}{(1)}" Block!(Group(Id("x"))), Block!(Group(Int(1)))); // Unary operations. - t!("{-1}" Block(Unary(Neg, Int(1)))); - t!("{--1}" Block(Unary(Neg, Unary(Neg, Int(1))))); + t!("{+1}" Block!(Unary(Pos, Int(1)))); + t!("{-1}" Block!(Unary(Neg, Int(1)))); + t!("{--1}" Block!(Unary(Neg, Unary(Neg, Int(1))))); // Binary operations. - t!(r#"{"x"+"y"}"# Block(Binary(Str("x"), Add, Str("y")))); - t!("{1-2}" Block(Binary(Int(1), Sub, Int(2)))); - t!("{a * b}" Block(Binary(Id("a"), Mul, Id("b")))); - t!("{12pt/.4}" Block(Binary(Length(12.0, LengthUnit::Pt), Div, Float(0.4)))); + t!(r#"{"x"+"y"}"# Block!(Binary(Str("x"), Add, Str("y")))); + t!("{1-2}" Block!(Binary(Int(1), Sub, Int(2)))); + t!("{a * b}" Block!(Binary(Id("a"), Mul, Id("b")))); + t!("{12pt/.4}" Block!(Binary(Length(12.0, LengthUnit::Pt), Div, Float(0.4)))); // Associativity. - t!("{1+2+3}" Block(Binary(Binary(Int(1), Add, Int(2)), Add, Int(3)))); - t!("{1/2*3}" Block(Binary(Binary(Int(1), Div, Int(2)), Mul, Int(3)))); + t!("{1+2+3}" Block!(Binary(Binary(Int(1), Add, Int(2)), Add, Int(3)))); + t!("{1/2*3}" Block!(Binary(Binary(Int(1), Div, Int(2)), Mul, Int(3)))); // Precedence. - t!("{1+2*-3}" Block(Binary( + t!("{1+2*-3}" Block!(Binary( Int(1), Add, Binary(Int(2), Mul, Unary(Neg, Int(3))), ))); // Confusion with floating-point literal. - t!("{1e-3-4e+4}" Block(Binary(Float(1e-3), Sub, Float(4e+4)))); + t!("{1e-3-4e+4}" Block!(Binary(Float(1e-3), Sub, Float(4e+4)))); // Spans + parentheses winning over precedence. t!("{(1+2)*3}" - nodes: [S(0..9, Block(Binary( - S(1..6, Binary(S(2..3, Int(1)), S(3..4, Add), S(4..5, Int(2)))), + nodes: [S(0..9, Block!(Binary( + S(1..6, Group(Binary(S(2..3, Int(1)), S(3..4, Add), S(4..5, Int(2))))), S(6..7, Mul), S(7..8, Int(3)), )))], @@ -575,7 +606,7 @@ fn test_parse_expressions() { // Errors. t!("{-}{1+}{2*}" - nodes: [Block(Int(1)), Block(Int(2))], + nodes: [Block!(Int(1)), Block!(Int(2))], errors: [S(2..2, "expected expression"), S(6..6, "expected expression"), S(10..10, "expected expression")]); @@ -584,37 +615,36 @@ fn test_parse_expressions() { #[test] fn test_parse_values() { // Basics. - t!("{_}" Block(Id("_"))); - t!("{name}" Block(Id("name"))); - t!("{ke-bab}" Block(Id("ke-bab"))); - t!("{α}" Block(Id("α"))); - t!("{none}" Block(Expr::None)); - t!("{true}" Block(Bool(true))); - t!("{false}" Block(Bool(false))); - t!("{1.0e-4}" Block(Float(1e-4))); - t!("{3.15}" Block(Float(3.15))); - t!("{50%}" Block(Percent(50.0))); - t!("{4.5cm}" Block(Length(4.5, LengthUnit::Cm))); - t!("{12e1pt}" Block(Length(12e1, LengthUnit::Pt))); - t!("{13rad}" Block(Angle(13.0, AngularUnit::Rad))); - t!("{45deg}" Block(Angle(45.0, AngularUnit::Deg))); + t!("{_}" Block!(Id("_"))); + t!("{name}" Block!(Id("name"))); + t!("{ke-bab}" Block!(Id("ke-bab"))); + t!("{α}" Block!(Id("α"))); + t!("{none}" Block!(Expr::None)); + t!("{true}" Block!(Bool(true))); + t!("{false}" Block!(Bool(false))); + t!("{1.0e-4}" Block!(Float(1e-4))); + t!("{3.15}" Block!(Float(3.15))); + t!("{50%}" Block!(Percent(50.0))); + t!("{4.5cm}" Block!(Length(4.5, LengthUnit::Cm))); + t!("{12e1pt}" Block!(Length(12e1, LengthUnit::Pt))); + t!("{13rad}" Block!(Angle(13.0, AngularUnit::Rad))); + t!("{45deg}" Block!(Angle(45.0, AngularUnit::Deg))); // Strings. - t!(r#"{"hi"}"# Block(Str("hi"))); - t!(r#"{"a\n[]\"\u{1F680}string"}"# Block(Str("a\n[]\"🚀string"))); + t!(r#"{"hi"}"# Block!(Str("hi"))); + t!(r#"{"a\n[]\"\u{1F680}string"}"# Block!(Str("a\n[]\"🚀string"))); // Colors. - t!("{#f7a20500}" Block(Color(RgbaColor::new(0xf7, 0xa2, 0x05, 0)))); + t!("{#f7a20500}" Block!(Color(RgbaColor::new(0xf7, 0xa2, 0x05, 0)))); t!("{#a5}" - nodes: [Block(Color(RgbaColor::new(0, 0, 0, 0xff)))], + nodes: [Block!(Color(RgbaColor::new(0, 0, 0, 0xff)))], errors: [S(1..4, "invalid color")]); // Content. - t!("{[*Hi*]}" Block(Template![Strong, Text("Hi"), Strong])); + t!("{[*Hi*]}" Block!(Template![Strong, Text("Hi"), Strong])); // Nested blocks. - t!("{{1}}" Block(Int(1))); - t!("{{{1+2}}}" Block(Binary(Int(1), Add, Int(2)))); + t!("{{1}}" Block!(Block!(@Int(1)))); // Invalid tokens. t!("{1u}" diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index cb09041ce..09472df41 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -40,6 +40,10 @@ pub enum Expr { Dict(ExprDict), /// A template expression: `[*Hi* there!]`. Template(ExprTemplate), + /// A grouped expression: `(1 + 2)`. + Group(Box), + /// A block expression: `{1 + 2}`. + Block(Box), } impl Pretty for Expr { @@ -54,24 +58,31 @@ impl Pretty for Expr { Self::Angle(v, u) => write!(p, "{}{}", v, u).unwrap(), Self::Percent(v) => write!(p, "{}%", v).unwrap(), Self::Color(v) => write!(p, "{}", v).unwrap(), - Self::Str(s) => write!(p, "{:?}", &s).unwrap(), - Self::Call(call) => call.pretty(p), - Self::Unary(unary) => unary.pretty(p), - Self::Binary(binary) => binary.pretty(p), - Self::Array(array) => array.pretty(p), - Self::Dict(dict) => dict.pretty(p), - Self::Template(template) => pretty_template_expr(template, p), + Self::Str(v) => write!(p, "{:?}", &v).unwrap(), + Self::Call(v) => v.pretty(p), + Self::Unary(v) => v.pretty(p), + Self::Binary(v) => v.pretty(p), + Self::Array(v) => v.pretty(p), + Self::Dict(v) => v.pretty(p), + Self::Template(v) => { + p.push_str("["); + v.pretty(p); + p.push_str("]"); + } + Self::Group(v) => { + p.push_str("("); + v.pretty(p); + p.push_str(")"); + } + Self::Block(v) => { + p.push_str("{"); + v.pretty(p); + p.push_str("}"); + } } } } -/// Pretty print a template in an expression context. -pub fn pretty_template_expr(tree: &Tree, p: &mut Printer) { - p.push_str("["); - tree.pretty(p); - p.push_str("]"); -} - /// An invocation of a function: `[foo ...]`, `foo(...)`. #[derive(Debug, Clone, PartialEq)] pub struct ExprCall { @@ -197,6 +208,8 @@ impl Pretty for ExprUnary { /// A unary operator. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum UnOp { + /// The plus operator: `+`. + Pos, /// The negation operator: `-`. Neg, } @@ -204,6 +217,7 @@ pub enum UnOp { impl Pretty for UnOp { fn pretty(&self, p: &mut Printer) { p.push_str(match self { + Self::Pos => "+", Self::Neg => "-", }); } @@ -302,7 +316,11 @@ mod tests { fn test_pretty_print_expressions() { // Unary and binary operations. test_pretty("{1 +}", "{1}"); + test_pretty("{1++1}", "{1 + +1}"); + test_pretty("{+-1}", "{+-1}"); test_pretty("{1 + func(-2)}", "{1 + func(-2)}"); + test_pretty("{1+2*3}", "{1 + 2 * 3}"); + test_pretty("{(1+2)*3}", "{(1 + 2) * 3}"); // Array. test_pretty("(-5,)", "(-5,)"); @@ -314,6 +332,10 @@ mod tests { // Content expression. test_pretty("[v [[f]], 1]", "[v [[f]], 1]"); + + // Parens and blocks. + test_pretty("{(1)}", "{(1)}"); + test_pretty("{{1}}", "{{1}}"); } #[test] diff --git a/src/syntax/node.rs b/src/syntax/node.rs index e7091a404..5c4c22b7d 100644 --- a/src/syntax/node.rs +++ b/src/syntax/node.rs @@ -34,29 +34,14 @@ impl Pretty for Node { Self::Text(text) => p.push_str(&text), Self::Heading(heading) => heading.pretty(p), Self::Raw(raw) => raw.pretty(p), - Self::Expr(expr) => pretty_expr_node(expr, p), - } - } -} - -/// Pretty print an expression in a node context. -pub fn pretty_expr_node(expr: &Expr, p: &mut Printer) { - match expr { - // Prefer bracket calls over expression blocks with just a single paren - // call. - // - // Example: Transforms "{v()}" => "[v]". - Expr::Call(call) => pretty_bracket_call(call, p, false), - - // Remove unncessary nesting of content and expression blocks. - // - // Example: Transforms "{[Hi]}" => "Hi". - Expr::Template(template) => template.pretty(p), - - _ => { - p.push_str("{"); - expr.pretty(p); - p.push_str("}"); + Self::Expr(expr) => { + if let Expr::Call(call) = expr { + // Format bracket calls appropriately. + pretty_bracket_call(call, p, false) + } else { + expr.pretty(p); + } + } } } } @@ -178,18 +163,13 @@ mod tests { use super::super::tests::test_pretty; #[test] - fn test_pretty_print_removes_nesting() { - // Nesting does not matter. - test_pretty("{{x}}", "{x}"); - test_pretty("{{{x}}}", "{x}"); - } - - #[test] - fn test_pretty_print_prefers_bracket_calls() { - // All reduces to a simple bracket call. - test_pretty("{v()}", "[v]"); + fn test_pretty_print_bracket_calls() { + // Top-level call expression formatted as bracket call. test_pretty("[v]", "[v]"); - test_pretty("{[[v]]}", "[v]"); + + // Blocks are preserved. + test_pretty("{v()}", "{v()}"); + test_pretty("{[[v]]}", "{[[v]]}"); } #[test]