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