typst/src/syntax/expr.rs

387 lines
10 KiB
Rust

use super::*;
use crate::color::RgbaColor;
use crate::geom::{AngularUnit, LengthUnit};
/// An expression.
#[derive(Debug, Clone, PartialEq)]
pub enum Expr {
/// The none literal: `none`.
None,
/// A identifier literal: `left`.
Ident(Ident),
/// A boolean literal: `true`, `false`.
Bool(bool),
/// An integer literal: `120`.
Int(i64),
/// A floating-point literal: `1.2`, `10e-4`.
Float(f64),
/// A length literal: `12pt`, `3cm`.
Length(f64, LengthUnit),
/// An angle literal: `1.5rad`, `90deg`.
Angle(f64, AngularUnit),
/// A percent literal: `50%`.
///
/// _Note_: `50%` is stored as `50.0` here, but as `0.5` in the
/// corresponding [value](crate::geom::Relative).
Percent(f64),
/// A color literal: `#ffccee`.
Color(RgbaColor),
/// A string literal: `"hello!"`.
Str(String),
/// An array expression: `(1, "hi", 12cm)`.
Array(ExprArray),
/// A dictionary expression: `(color: #f79143, pattern: dashed)`.
Dict(ExprDict),
/// A template expression: `[*Hi* there!]`.
Template(ExprTemplate),
/// A unary operation: `-x`.
Unary(ExprUnary),
/// A binary operation: `a + b`, `a / b`.
Binary(ExprBinary),
/// An invocation of a function: `[foo ...]`, `foo(...)`.
Call(ExprCall),
/// A grouped expression: `(1 + 2)`.
Group(ExprGroup),
/// A block expression: `{1 + 2}`.
Block(ExprBlock),
/// A let expression: `let x = 1`.
Let(ExprLet),
}
impl Pretty for Expr {
fn pretty(&self, p: &mut Printer) {
match self {
Self::None => p.push_str("none"),
Self::Ident(v) => p.push_str(&v),
Self::Bool(v) => write!(p, "{}", v).unwrap(),
Self::Int(v) => p.push_str(itoa::Buffer::new().format(*v)),
Self::Float(v) => p.push_str(ryu::Buffer::new().format(*v)),
Self::Length(v, u) => write!(p, "{}{}", v, u).unwrap(),
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(v) => write!(p, "{:?}", &v).unwrap(),
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::Unary(v) => v.pretty(p),
Self::Binary(v) => v.pretty(p),
Self::Call(v) => v.pretty(p),
Self::Group(v) => {
p.push_str("(");
v.v.pretty(p);
p.push_str(")");
}
Self::Block(v) => {
p.push_str("{");
v.v.pretty(p);
p.push_str("}");
}
Self::Let(v) => v.pretty(p),
}
}
}
/// An array expression: `(1, "hi", 12cm)`.
pub type ExprArray = SpanVec<Expr>;
impl Pretty for ExprArray {
fn pretty(&self, p: &mut Printer) {
p.push_str("(");
p.join(self, ", ", |item, p| item.v.pretty(p));
if self.len() == 1 {
p.push_str(",");
}
p.push_str(")");
}
}
/// A dictionary expression: `(color: #f79143, pattern: dashed)`.
pub type ExprDict = Vec<Named>;
impl Pretty for ExprDict {
fn pretty(&self, p: &mut Printer) {
p.push_str("(");
if self.is_empty() {
p.push_str(":");
} else {
p.join(self, ", ", |named, p| named.pretty(p));
}
p.push_str(")");
}
}
/// A template expression: `[*Hi* there!]`.
pub type ExprTemplate = Tree;
/// An invocation of a function: `[foo ...]`, `foo(...)`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprCall {
/// The name of the function.
pub name: Spanned<Ident>,
/// The arguments to the function.
pub args: Spanned<ExprArgs>,
}
impl Pretty for ExprCall {
fn pretty(&self, p: &mut Printer) {
p.push_str(&self.name.v);
p.push_str("(");
self.args.v.pretty(p);
p.push_str(")");
}
}
/// Pretty print a bracketed function call, with body or chaining when possible.
pub fn pretty_bracket_call(call: &ExprCall, p: &mut Printer, chained: bool) {
if chained {
p.push_str(" | ");
} else {
p.push_str("[");
}
// Function name.
p.push_str(&call.name.v);
// Find out whether this can be written with a body or as a chain.
//
// Example: Transforms "[v [Hi]]" => "[v][Hi]".
if let [head @ .., Argument::Pos(Spanned { v: Expr::Template(template), .. })] =
call.args.v.as_slice()
{
// Previous arguments.
if !head.is_empty() {
p.push_str(" ");
p.join(head, ", ", |item, p| item.pretty(p));
}
// Find out whether this can written as a chain.
//
// Example: Transforms "[v][[f]]" => "[v | f]".
if let [Spanned { v: Node::Expr(Expr::Call(call)), .. }] = template.as_slice() {
return pretty_bracket_call(call, p, true);
} else {
p.push_str("][");
template.pretty(p);
}
} else if !call.args.v.is_empty() {
p.push_str(" ");
call.args.v.pretty(p);
}
// Either end of header or end of body.
p.push_str("]");
}
/// The arguments to a function: `12, draw: false`.
///
/// In case of a bracketed invocation with a body, the body is _not_
/// included in the span for the sake of clearer error messages.
pub type ExprArgs = Vec<Argument>;
impl Pretty for Vec<Argument> {
fn pretty(&self, p: &mut Printer) {
p.join(self, ", ", |item, p| item.pretty(p));
}
}
/// An argument to a function call: `12` or `draw: false`.
#[derive(Debug, Clone, PartialEq)]
pub enum Argument {
/// A positional arguments.
Pos(Spanned<Expr>),
/// A named argument.
Named(Named),
}
impl Pretty for Argument {
fn pretty(&self, p: &mut Printer) {
match self {
Self::Pos(expr) => expr.v.pretty(p),
Self::Named(named) => named.pretty(p),
}
}
}
/// A pair of a name and an expression: `pattern: dashed`.
#[derive(Debug, Clone, PartialEq)]
pub struct Named {
/// The name: `pattern`.
pub name: Spanned<Ident>,
/// The right-hand side of the pair: `dashed`.
pub expr: Spanned<Expr>,
}
impl Pretty for Named {
fn pretty(&self, p: &mut Printer) {
p.push_str(&self.name.v);
p.push_str(": ");
self.expr.v.pretty(p);
}
}
/// A unary operation: `-x`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprUnary {
/// The operator: `-`.
pub op: Spanned<UnOp>,
/// The expression to operator on: `x`.
pub expr: Box<Spanned<Expr>>,
}
impl Pretty for ExprUnary {
fn pretty(&self, p: &mut Printer) {
self.op.v.pretty(p);
self.expr.v.pretty(p);
}
}
/// A unary operator.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum UnOp {
/// The plus operator: `+`.
Pos,
/// The negation operator: `-`.
Neg,
}
impl Pretty for UnOp {
fn pretty(&self, p: &mut Printer) {
p.push_str(match self {
Self::Pos => "+",
Self::Neg => "-",
});
}
}
/// A binary operation: `a + b`, `a / b`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprBinary {
/// The left-hand side of the operation: `a`.
pub lhs: Box<Spanned<Expr>>,
/// The operator: `+`.
pub op: Spanned<BinOp>,
/// The right-hand side of the operation: `b`.
pub rhs: Box<Spanned<Expr>>,
}
impl Pretty for ExprBinary {
fn pretty(&self, p: &mut Printer) {
self.lhs.v.pretty(p);
p.push_str(" ");
self.op.v.pretty(p);
p.push_str(" ");
self.rhs.v.pretty(p);
}
}
/// A binary operator.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum BinOp {
/// The addition operator: `+`.
Add,
/// The subtraction operator: `-`.
Sub,
/// The multiplication operator: `*`.
Mul,
/// The division operator: `/`.
Div,
}
impl Pretty for BinOp {
fn pretty(&self, p: &mut Printer) {
p.push_str(match self {
Self::Add => "+",
Self::Sub => "-",
Self::Mul => "*",
Self::Div => "/",
});
}
}
/// A grouped expression: `(1 + 2)`.
pub type ExprGroup = Box<Spanned<Expr>>;
/// A block expression: `{1 + 2}`.
pub type ExprBlock = Box<Spanned<Expr>>;
/// A let expression: `let x = 1`.
#[derive(Debug, Clone, PartialEq)]
pub struct ExprLet {
/// The pattern to assign to.
pub pat: Spanned<Ident>,
/// The expression to assign to the pattern.
pub expr: Option<Box<Spanned<Expr>>>,
}
impl Pretty for ExprLet {
fn pretty(&self, p: &mut Printer) {
p.push_str("#let ");
p.push_str(&self.pat.v);
if let Some(expr) = &self.expr {
p.push_str(" = ");
expr.v.pretty(p);
}
}
}
#[cfg(test)]
mod tests {
use super::super::tests::test_pretty;
#[test]
fn test_pretty_print_chaining() {
// All equivalent.
test_pretty("[v [[f]]]", "[v | f]");
test_pretty("[v][[f]]", "[v | f]");
test_pretty("[v | f]", "[v | f]");
}
#[test]
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,)");
test_pretty("(1, 2, 3)", "(1, 2, 3)");
// Dictionary.
test_pretty("{(:)}", "{(:)}");
test_pretty("{(percent: 5%)}", "{(percent: 5%)}");
// Content expression.
test_pretty("[v [[f]], 1]", "[v [[f]], 1]");
// Parens and blocks.
test_pretty("{(1)}", "{(1)}");
test_pretty("{{1}}", "{{1}}");
// Let binding.
test_pretty("#let x=1+2", "#let x = 1 + 2");
}
#[test]
fn test_pretty_print_literals() {
test_pretty("{none}", "{none}");
test_pretty("{true}", "{true}");
test_pretty("{25}", "{25}");
test_pretty("{2.50}", "{2.5}");
test_pretty("{1e2}", "{100.0}");
test_pretty("{12pt}", "{12pt}");
test_pretty("{90.0deg}", "{90deg}");
test_pretty("{50%}", "{50%}");
test_pretty("{#fff}", "{#ffffff}");
test_pretty(r#"{"hi\n"}"#, r#"{"hi\n"}"#);
}
}