pax_language/interpreter/
computable.rs1use std::rc::Rc;
2
3use pax_runtime_api::{
4 functions::call_function, CoercionRules, Duration, Functions, Numeric, PaxValue, Percent,
5 Rotation, Size,
6};
7
8use super::{
9 property_resolution::IdentifierResolver, PaxAccessor, PaxExpression, PaxIdentifier, PaxInfix,
10 PaxNullCoalesce, PaxPostfix, PaxPrefix, PaxPrimary, PaxTernary, PaxUnit,
11};
12
13pub trait Computable {
15 fn compute(&self, idr: Rc<dyn IdentifierResolver>) -> Result<PaxValue, String>;
16}
17
18impl Computable for PaxExpression {
19 fn compute(&self, idr: Rc<dyn IdentifierResolver>) -> Result<PaxValue, String> {
20 match self {
21 PaxExpression::Primary(p) => p.compute(idr),
22 PaxExpression::Prefix(p) => p.compute(idr),
23 PaxExpression::Infix(p) => p.compute(idr),
24 PaxExpression::Postfix(p) => p.compute(idr),
25 PaxExpression::Ternary(p) => p.compute(idr),
26 PaxExpression::NullCoalesce(p) => p.compute(idr),
27 }
28 }
29}
30
31impl Computable for PaxPrimary {
32 fn compute(&self, idr: Rc<dyn IdentifierResolver>) -> Result<PaxValue, String> {
33 match self {
34 PaxPrimary::Literal(v) => Ok(v.clone()),
35 PaxPrimary::Identifier(i, accessors) => idr
36 .resolve(i.name.clone())?
37 .read_pax_value_ref(|mut value| {
38 for accessor in accessors {
39 match accessor {
40 PaxAccessor::Tuple(index) => {
41 if let PaxValue::Vec(v) = value {
42 value = v.into_iter().nth(*index).ok_or_else(|| {
43 format!(
44 "paxel interpreter: tuple index out of bounds: {:?}",
45 index
46 )
47 })?;
48 } else {
49 return Err(
50 "Tuple access must be performed on a tuple".to_string()
51 );
52 }
53 }
54 PaxAccessor::List(index) => {
55 if let PaxValue::Vec(v) = value {
56 let index = Numeric::try_coerce(index.compute(idr.clone())?)?
57 .to_int()
58 as usize;
59 value = v.into_iter().nth(index).ok_or_else(|| {
60 format!(
61 "paxel interpreter: list index out of bounds: {:?}",
62 index
63 )
64 })?;
65 } else {
66 return Err(
67 "List access must be performed on a list".to_string()
68 );
69 }
70 }
71 PaxAccessor::Struct(field) => {
72 if field == "color" {
73 match value {
74 PaxValue::Enum(layer) if layer.0 == "Stroke" && layer.1 == "__layer" => return Err("Stroke.color was renamed to paint".into()),
75 PaxValue::Vec(layers) if layers.iter().any(|layer| matches!(layer, PaxValue::Enum(layer) if layer.0 == "Stroke" && layer.1 == "__layer")) => return Err("stroke is now a stack; select an entry (stroke[0].paint). Stroke.color was renamed to paint".into()),
76 _ => {}
77 }
78 }
79 if let PaxValue::Enum(contents) = value {
80 if (contents.0 == "Fill" || contents.0 == "Stroke")
81 && contents.1 == "__layer"
82 && contents.2.len() == 1
83 {
84 value = &contents.2[0];
85 }
86 }
87 if let PaxValue::Object(obj) = value {
88 value = obj
89 .into_iter()
90 .find_map(|(n, v)| (n == field).then_some(v))
91 .ok_or(format!("Field not found: {}", field))?;
92 } else {
93 return Err(
94 "Struct access must be performed on an object".to_string()
95 );
96 }
97 }
98 }
99 }
100 Ok(value.clone())
101 }),
102 PaxPrimary::Object(o) => Ok(PaxValue::Object(
103 o.into_iter()
104 .map(|(k, v)| Result::<_, String>::Ok((k.clone(), v.compute(idr.clone())?)))
105 .collect::<Result<_, _>>()?,
106 )),
107 PaxPrimary::Range(start, end) => {
108 let start = start.compute(idr.clone())?;
109 let end = end.compute(idr)?;
110 Ok(PaxValue::Range(Box::new(start), Box::new(end)))
111 }
112 PaxPrimary::Tuple(t) => {
113 let tuple = t
114 .iter()
115 .map(|e| e.compute(idr.clone()))
116 .collect::<Result<Vec<PaxValue>, String>>()?;
117 Ok(PaxValue::Vec(tuple))
118 }
119 PaxPrimary::List(l) => {
120 let list = l
121 .iter()
122 .map(|e| e.compute(idr.clone()))
123 .collect::<Result<Vec<PaxValue>, String>>()?;
124 Ok(PaxValue::Vec(list))
125 }
126 PaxPrimary::Grouped(expr, unit) => {
127 let expr_val = expr.compute(idr.clone())?;
128 if let Some(unit) = unit {
129 match Numeric::try_coerce(expr_val) {
130 Ok(n) => match unit {
131 PaxUnit::Percent => Ok(PaxValue::Percent(Percent(n))),
132 PaxUnit::Pixels => Ok(PaxValue::Size(Size::Pixels(n))),
133 PaxUnit::Radians => Ok(PaxValue::Rotation(Rotation::Radians(n))),
134 PaxUnit::Degrees => Ok(PaxValue::Rotation(Rotation::Degrees(n))),
135 PaxUnit::Milliseconds => {
136 Ok(PaxValue::Duration(Duration::Milliseconds(n)))
137 }
138 PaxUnit::Seconds => Ok(PaxValue::Duration(Duration::Seconds(n))),
139 PaxUnit::Frames => Ok(PaxValue::Duration(Duration::Frames(n))),
140 },
141 Err(e) => Err(format!(
142 "A grouped expression with a unit must be of type numeric: {e:?}"
143 )),
144 }
145 } else {
146 Ok(expr_val)
147 }
148 }
149 PaxPrimary::FunctionOrEnum(scope, name_or_variant, args) => {
150 let args = args
151 .iter()
152 .map(|a| a.compute(idr.clone()))
153 .collect::<Result<Vec<PaxValue>, String>>()?;
154
155 if Functions::has_function(scope, name_or_variant) {
156 return call_function(scope.clone(), name_or_variant.clone(), args);
157 }
158
159 Ok(PaxValue::Enum(Box::new((
160 scope.clone(),
161 name_or_variant.clone(),
162 args,
163 ))))
164 }
165 }
166 }
167}
168
169impl Computable for PaxPrefix {
170 fn compute(&self, idr: Rc<dyn IdentifierResolver>) -> Result<PaxValue, String> {
171 let rhs = self.rhs.compute(idr)?;
172 let operator = &self.operator.name;
173 call_function("Math".to_string(), operator.to_string(), vec![rhs])
174 }
175}
176
177impl Computable for PaxPostfix {
178 fn compute(&self, idr: Rc<dyn IdentifierResolver>) -> Result<PaxValue, String> {
179 let lhs = self.lhs.compute(idr)?;
180 let operator = &self.operator.name;
181 call_function("Math".to_string(), operator.to_string(), vec![lhs])
182 }
183}
184
185impl Computable for PaxInfix {
186 fn compute(&self, idr: Rc<dyn IdentifierResolver>) -> Result<PaxValue, String> {
187 let lhs = self.lhs.compute(idr.clone())?;
188 let operator = &self.operator.name;
189 match (operator.as_str(), &lhs) {
192 ("&&", PaxValue::Bool(false)) | ("||", PaxValue::Bool(true)) => return Ok(lhs),
193 _ => {}
194 }
195 let rhs = self.rhs.compute(idr)?;
196 call_function("Math".to_string(), operator.to_string(), vec![lhs, rhs])
197 }
198}
199
200impl Computable for PaxTernary {
201 fn compute(&self, idr: Rc<dyn IdentifierResolver>) -> Result<PaxValue, String> {
202 match self.condition.compute(idr.clone())? {
203 PaxValue::Bool(true) => self.then_branch.compute(idr),
204 PaxValue::Bool(false) => self.else_branch.compute(idr),
205 value => Err(format!(
206 "Ternary condition must compute to a boolean, got: {:?}",
207 value
208 )),
209 }
210 }
211}
212
213impl Computable for PaxNullCoalesce {
214 fn compute(&self, idr: Rc<dyn IdentifierResolver>) -> Result<PaxValue, String> {
215 match self.lhs.compute(idr.clone())? {
216 PaxValue::Option(opt) => match *opt {
217 Some(value) => Ok(value),
218 None => self.rhs.compute(idr),
219 },
220 value => Ok(value),
221 }
222 }
223}
224
225impl Computable for PaxIdentifier {
226 fn compute(&self, idr: Rc<dyn IdentifierResolver>) -> Result<PaxValue, String> {
227 Ok(idr.resolve(self.name.clone())?.get_as_pax_value())
228 }
229}