typstyle_core/liteval/
mod.rs1use typst_syntax::ast::*;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum Value {
9 None,
10 Auto,
11 Int(i64),
12 Array(usize),
14}
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub enum EvalError {
18 NotSupported,
19 InvalidOperation,
20}
21
22pub type EvalResult = Result<Value, EvalError>;
23
24pub trait Liteval {
25 fn liteval(&self) -> EvalResult;
26}
27
28impl Liteval for Expr<'_> {
29 fn liteval(&self) -> EvalResult {
30 match self {
31 Expr::None(v) => v.liteval(),
32 Expr::Auto(v) => v.liteval(),
33 Expr::Int(v) => v.liteval(),
34 Expr::Parenthesized(v) => v.liteval(),
35 Expr::Array(v) => v.liteval(),
36 Expr::Unary(v) => v.liteval(),
37 Expr::Binary(v) => v.liteval(),
38 _ => Err(EvalError::NotSupported),
39 }
40 }
41}
42
43impl Liteval for None<'_> {
44 fn liteval(&self) -> EvalResult {
45 Ok(Value::None)
46 }
47}
48
49impl Liteval for Auto<'_> {
50 fn liteval(&self) -> EvalResult {
51 Ok(Value::Auto)
52 }
53}
54
55impl Liteval for Int<'_> {
56 fn liteval(&self) -> EvalResult {
57 Ok(Value::Int(self.get()))
58 }
59}
60
61impl Liteval for Parenthesized<'_> {
62 fn liteval(&self) -> EvalResult {
63 self.expr().liteval()
64 }
65}
66
67impl Liteval for Array<'_> {
68 fn liteval(&self) -> EvalResult {
69 Ok(Value::Array(self.items().count()))
70 }
71}
72
73impl Liteval for Unary<'_> {
74 fn liteval(&self) -> EvalResult {
75 let expr = self.expr().liteval()?;
76 match self.op() {
77 UnOp::Pos => match expr {
78 Value::Int(i) => Ok(Value::Int(i)),
79 _ => Err(EvalError::InvalidOperation),
80 },
81 UnOp::Neg => match expr {
82 Value::Int(i) => Ok(Value::Int(-i)),
83 _ => Err(EvalError::InvalidOperation),
84 },
85 UnOp::Not => Err(EvalError::NotSupported),
86 }
87 }
88}
89impl Liteval for Binary<'_> {
90 fn liteval(&self) -> EvalResult {
91 let lhs = self.lhs().liteval()?;
92 let rhs = self.rhs().liteval()?;
93 match self.op() {
94 BinOp::Add => match (lhs, rhs) {
95 (Value::Int(l), Value::Int(r)) => Ok(Value::Int(l + r)),
96 (Value::Array(l), Value::Array(r)) => Ok(Value::Array(l + r)),
97 _ => Err(EvalError::InvalidOperation),
98 },
99 BinOp::Sub => match (lhs, rhs) {
100 (Value::Int(l), Value::Int(r)) => Ok(Value::Int(l - r)),
101 _ => Err(EvalError::InvalidOperation),
102 },
103 BinOp::Mul => match (lhs, rhs) {
104 (Value::Int(l), Value::Int(r)) => Ok(Value::Int(l * r)),
105 (Value::Array(l), Value::Int(r)) if r >= 0 => Ok(Value::Array(l * r as usize)),
106 (Value::Int(l), Value::Array(r)) if l >= 0 => Ok(Value::Array(l as usize * r)),
107 _ => Err(EvalError::InvalidOperation),
108 },
109 BinOp::Div => match (lhs, rhs) {
110 (Value::Int(l), Value::Int(r)) if r != 0 => Ok(Value::Int(l / r)),
111 _ => Err(EvalError::InvalidOperation),
112 },
113 _ => Err(EvalError::NotSupported),
114 }
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 fn test_liteval(code: &str, expected: Value) {
123 let root = typst_syntax::parse_code(code);
124 let expr = root.cast::<Code>().unwrap().exprs().next().unwrap();
125 assert_eq!(expr.liteval(), Ok(expected), "expr: {expr:#?}");
126 }
127
128 #[test]
129 fn test_simple_expr() {
130 use Value::*;
131
132 test_liteval("none", None);
133 test_liteval("auto", Auto);
134 test_liteval("0", Int(0));
135 test_liteval("1 + 2", Int(3));
136 test_liteval("1 * 2", Int(2));
137 test_liteval("1 - 2", Int(-1));
138 test_liteval("(1 + 2) * 3", Int(9));
139 test_liteval("(1fr,)", Array(1));
140 test_liteval("(1pt, 2em) * 3", Array(6));
141 test_liteval("(1, 2) + (3, 4, 5)", Array(5));
142 test_liteval("(1,) * 2 + 2 * (3, 4)", Array(6));
143 test_liteval("((1,) * 2 + 2 * (3,)) * 4", Array(16));
144 }
145}