summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-01-12 18:55:12 +0100
committerLaurenz <laurmaedje@gmail.com>2021-01-12 18:55:12 +0100
commitd2ba1b705ed7a532266294aa100f19423bb07f4d (patch)
tree1f06bf618f2190a3aabc26d69f0fb819e970341c
parent105cda0e698fe86266d706f4e3bacc081e65c2aa (diff)
Group and block expressions, unary plus 🧩
-rw-r--r--src/eval/mod.rs200
-rw-r--r--src/eval/ops.rs183
-rw-r--r--src/eval/value.rs24
-rw-r--r--src/parse/collection.rs4
-rw-r--r--src/parse/mod.rs29
-rw-r--r--src/parse/tests.rs198
-rw-r--r--src/syntax/expr.rs50
-rw-r--r--src/syntax/node.rs48
8 files changed, 396 insertions, 340 deletions
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index efa74596..32a7d6ba 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<Spanned<T>>
-where
- Spanned<&'a T>: Eval,
-{
- type Output = <Spanned<&'a T> as Eval>::Output;
-
- fn eval(self, ctx: &mut EvalContext) -> Self::Output {
- (**self).as_ref().eval(ctx)
- }
-}
-
impl Eval for &[Spanned<Node>] {
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<T, A>(mut a: T, b: T) -> T
-where
- T: Extend<A> + IntoIterator<Item = A>,
-{
- a.extend(b);
- a
-}
diff --git a/src/eval/ops.rs b/src/eval/ops.rs
new file mode 100644
index 00000000..0a273da5
--- /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<T, A>(mut a: T, b: T) -> T
+where
+ T: Extend<A> + IntoIterator<Item = A>,
+{
+ a.extend(b);
+ a
+}
diff --git a/src/eval/value.rs b/src/eval/value.rs
index d3b2688b..13548c87 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 98d4219f..ca05b998 100644
--- a/src/parse/collection.rs
+++ b/src/parse/collection.rs
@@ -59,7 +59,7 @@ fn argument(p: &mut Parser) -> Option<Argument> {
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 ee799159..a66660e5 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -51,12 +51,12 @@ fn node(p: &mut Parser, at_start: bool) -> Option<Node> {
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<Expr> {
+/// Parse a block expression: `{...}`.
+fn block(p: &mut Parser) -> Option<Expr> {
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<Expr> {
}
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<Expr> {
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<Expr> {
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<Expr> {
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<Expr> {
}
// 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<Ident>) -> ExprCall {
+fn paren_call(p: &mut Parser, name: Spanned<Ident>) -> 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 a8897c1c..b9a3d301 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<Spanned<UnOp>>, expr: impl Into<Spanned<Expr>>) -> 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 {
+macro_rules! Call {
+ (@@$name:expr) => {
+ Call!(@@$name, Args![])
+ };
+ (@@$name:expr, $args:expr) => {
+ ExprCall {
name: into!($name).map(|s: &str| Ident(s.into())),
- expr: into!($expr)
- }),*]
+ 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! Array {
+ (@$($expr:expr),* $(,)?) => {
+ vec![$(into!($expr)),*]
+ };
+ ($($tts:tt)*) => {
+ Expr::Array(Array![@$($tts)*])
+ };
+}
+
+macro_rules! Dict {
+ (@$($name:expr => $expr:expr),* $(,)?) => {
+ vec![$(Named {
+ name: into!($name).map(|s: &str| Ident(s.into())),
+ expr: into!($expr)
+ }),*]
+ };
+ ($($tts:tt)*) => {
+ Expr::Dict(Dict![@$($tts)*])
+ };
+}
+
macro_rules! Template {
- (@$($node:expr),* $(,)?) => (vec![$(into!($node)),*]);
- ($($tts:tt)*) => (Expr::Template(Template![@$($tts)*]));
+ (@$($node:expr),* $(,)?) => {
+ vec![$(into!($node)),*]
+ };
+ ($($tts:tt)*) => {
+ Expr::Template(Template![@$($tts)*])
+ };
}
-macro_rules! Call {
- (@@$name:expr) => {
- Call!(@@$name, Args![])
+macro_rules! Block {
+ (@$expr:expr) => {
+ Expr::Block(Box::new($expr))
};
- (@@$name:expr, $args:expr) => {
- ExprCall {
- name: into!($name).map(|s: &str| Ident(s.into())),
- args: into!($args),
- }
+ ($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 cb09041c..09472df4 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<Expr>),
+ /// A block expression: `{1 + 2}`.
+ Block(Box<Expr>),
}
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 e7091a40..5c4c22b7 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]