mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
Group and block expressions, unary plus 🧩
This commit is contained in:
parent
105cda0e69
commit
d2ba1b705e
200
src/eval/mod.rs
200
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
|
||||
}
|
||||
|
183
src/eval/ops.rs
Normal file
183
src/eval/ops.rs
Normal file
@ -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
|
||||
}
|
@ -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)"),
|
||||
|
@ -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),
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
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}"
|
||||
|
@ -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]
|
||||
|
@ -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]
|
||||
|
Loading…
x
Reference in New Issue
Block a user