1use typst_library::diag::{At, DeprecationSink, HintedStrResult, SourceResult};
2use typst_library::foundations::{ops, IntoValue, Value};
3use typst_syntax::ast::{self, AstNode};
4
5use crate::{access_dict, Access, Eval, Vm};
6
7impl Eval for ast::Unary<'_> {
8 type Output = Value;
9
10 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
11 let value = self.expr().eval(vm)?;
12 let result = match self.op() {
13 ast::UnOp::Pos => ops::pos(value),
14 ast::UnOp::Neg => ops::neg(value),
15 ast::UnOp::Not => ops::not(value),
16 };
17 result.at(self.span())
18 }
19}
20
21impl Eval for ast::Binary<'_> {
22 type Output = Value;
23
24 fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> {
25 match self.op() {
26 ast::BinOp::Add => apply_binary_with_sink(self, vm, ops::add),
27 ast::BinOp::Sub => apply_binary(self, vm, ops::sub),
28 ast::BinOp::Mul => apply_binary(self, vm, ops::mul),
29 ast::BinOp::Div => apply_binary(self, vm, ops::div),
30 ast::BinOp::And => apply_binary(self, vm, ops::and),
31 ast::BinOp::Or => apply_binary(self, vm, ops::or),
32 ast::BinOp::Eq => apply_binary_with_sink(self, vm, ops::eq),
33 ast::BinOp::Neq => apply_binary_with_sink(self, vm, ops::neq),
34 ast::BinOp::Lt => apply_binary(self, vm, ops::lt),
35 ast::BinOp::Leq => apply_binary(self, vm, ops::leq),
36 ast::BinOp::Gt => apply_binary(self, vm, ops::gt),
37 ast::BinOp::Geq => apply_binary(self, vm, ops::geq),
38 ast::BinOp::In => apply_binary_with_sink(self, vm, ops::in_),
39 ast::BinOp::NotIn => apply_binary_with_sink(self, vm, ops::not_in),
40 ast::BinOp::Assign => apply_assignment(self, vm, |_, b| Ok(b)),
41 ast::BinOp::AddAssign => apply_assignment_with_sink(self, vm, ops::add),
42 ast::BinOp::SubAssign => apply_assignment(self, vm, ops::sub),
43 ast::BinOp::MulAssign => apply_assignment(self, vm, ops::mul),
44 ast::BinOp::DivAssign => apply_assignment(self, vm, ops::div),
45 }
46 }
47}
48
49fn apply_binary(
51 binary: ast::Binary,
52 vm: &mut Vm,
53 op: fn(Value, Value) -> HintedStrResult<Value>,
54) -> SourceResult<Value> {
55 let lhs = binary.lhs().eval(vm)?;
56
57 if (binary.op() == ast::BinOp::And && lhs == false.into_value())
59 || (binary.op() == ast::BinOp::Or && lhs == true.into_value())
60 {
61 return Ok(lhs);
62 }
63
64 let rhs = binary.rhs().eval(vm)?;
65 op(lhs, rhs).at(binary.span())
66}
67
68fn apply_binary_with_sink(
70 binary: ast::Binary,
71 vm: &mut Vm,
72 op: impl Fn(Value, Value, &mut dyn DeprecationSink) -> HintedStrResult<Value>,
73) -> SourceResult<Value> {
74 let span = binary.span();
75 let lhs = binary.lhs().eval(vm)?;
76 let rhs = binary.rhs().eval(vm)?;
77 op(lhs, rhs, &mut (&mut vm.engine, span)).at(span)
78}
79
80fn apply_assignment(
82 binary: ast::Binary,
83 vm: &mut Vm,
84 op: fn(Value, Value) -> HintedStrResult<Value>,
85) -> SourceResult<Value> {
86 let rhs = binary.rhs().eval(vm)?;
87 let lhs = binary.lhs();
88
89 if binary.op() == ast::BinOp::Assign {
92 if let ast::Expr::FieldAccess(access) = lhs {
93 let dict = access_dict(vm, access)?;
94 dict.insert(access.field().get().clone().into(), rhs);
95 return Ok(Value::None);
96 }
97 }
98
99 let location = binary.lhs().access(vm)?;
100 let lhs = std::mem::take(&mut *location);
101 *location = op(lhs, rhs).at(binary.span())?;
102 Ok(Value::None)
103}
104
105fn apply_assignment_with_sink(
107 binary: ast::Binary,
108 vm: &mut Vm,
109 op: fn(Value, Value, &mut dyn DeprecationSink) -> HintedStrResult<Value>,
110) -> SourceResult<Value> {
111 let rhs = binary.rhs().eval(vm)?;
112 let location = binary.lhs().access(vm)?;
113 let lhs = std::mem::take(&mut *location);
114 let mut sink = vec![];
115 let span = binary.span();
116 *location = op(lhs, rhs, &mut (&mut sink, span)).at(span)?;
117 if !sink.is_empty() {
118 for warning in sink {
119 vm.engine.sink.warn(warning);
120 }
121 }
122 Ok(Value::None)
123}