pax_lang/interpreter/
computable.rs1use std::{collections::HashMap, rc::Rc};
2
3use pax_runtime_api::{
4 functions::call_function, CoercionRules, Functions, Numeric, PaxValue, Percent, Rotation, Size,
5};
6
7use super::{
8 property_resolution::IdentifierResolver, PaxAccessor, PaxExpression, PaxIdentifier, PaxInfix,
9 PaxPostfix, PaxPrefix, PaxPrimary, PaxUnit,
10};
11
12pub trait Computable {
14 fn compute(&self, idr: Rc<dyn IdentifierResolver>) -> Result<PaxValue, String>;
15}
16
17impl Computable for PaxExpression {
18 fn compute(&self, idr: Rc<dyn IdentifierResolver>) -> Result<PaxValue, String> {
19 match self {
20 PaxExpression::Primary(p) => p.compute(idr),
21 PaxExpression::Prefix(p) => p.compute(idr),
22 PaxExpression::Infix(p) => p.compute(idr),
23 PaxExpression::Postfix(p) => p.compute(idr),
24 }
25 }
26}
27
28impl Computable for PaxPrimary {
29 fn compute(&self, idr: Rc<dyn IdentifierResolver>) -> Result<PaxValue, String> {
30 match self {
31 PaxPrimary::Literal(v) => Ok(v.clone()),
32 PaxPrimary::Identifier(i, accessors) => {
33 let mut value = i.compute(idr.clone())?;
34 for accessor in accessors {
35 match accessor {
36 PaxAccessor::Tuple(index) => {
37 if let PaxValue::Vec(v) = value {
38 value = v.into_iter().nth(*index).ok_or_else(|| {
39 format!(
40 "paxel interpreter: tuple index out of bounds: {:?}",
41 index
42 )
43 })?;
44 } else {
45 return Err("Tuple access must be performed on a tuple".to_string());
46 }
47 }
48 PaxAccessor::List(index) => {
49 if let PaxValue::Vec(v) = value {
50 let index = Numeric::try_coerce(index.compute(idr.clone())?)?
51 .to_int() as usize;
52 value = v.into_iter().nth(index).ok_or_else(|| {
53 format!(
54 "paxel interpreter: list index out of bounds: {:?}",
55 index
56 )
57 })?;
58 } else {
59 return Err("List access must be performed on a list".to_string());
60 }
61 }
62 PaxAccessor::Struct(field) => {
63 if let PaxValue::Object(obj) = value {
64 value = obj
65 .into_iter()
66 .find_map(|(n, v)| (&n == field).then_some(v))
67 .ok_or(format!("Field not found: {}", field))?;
68 } else {
69 return Err(
70 "Struct access must be performed on an object".to_string()
71 );
72 }
73 }
74 }
75 }
76 Ok(value)
77 }
78 PaxPrimary::Object(o) => Ok(PaxValue::Object(
79 o.into_iter()
80 .map(|(k, v)| Result::<_, String>::Ok((k.clone(), v.compute(idr.clone())?)))
81 .collect::<Result<_, _>>()?,
82 )),
83 PaxPrimary::Range(start, end) => {
84 let start = start.compute(idr.clone())?;
85 let end = end.compute(idr)?;
86 Ok(PaxValue::Range(Box::new(start), Box::new(end)))
87 }
88 PaxPrimary::Tuple(t) => {
89 let tuple = t
90 .iter()
91 .map(|e| e.compute(idr.clone()))
92 .collect::<Result<Vec<PaxValue>, String>>()?;
93 Ok(PaxValue::Vec(tuple))
94 }
95 PaxPrimary::List(l) => {
96 let list = l
97 .iter()
98 .map(|e| e.compute(idr.clone()))
99 .collect::<Result<Vec<PaxValue>, String>>()?;
100 Ok(PaxValue::Vec(list))
101 }
102 PaxPrimary::Grouped(expr, unit) => {
103 let expr_val = expr.compute(idr.clone())?;
104 if let Some(unit) = unit {
105 match Numeric::try_coerce(expr_val) {
106 Ok(n) => match unit {
107 PaxUnit::Percent => Ok(PaxValue::Percent(Percent(n))),
108 PaxUnit::Pixels => Ok(PaxValue::Size(Size::Pixels(n))),
109 PaxUnit::Radians => Ok(PaxValue::Rotation(Rotation::Radians(n))),
110 PaxUnit::Degrees => Ok(PaxValue::Rotation(Rotation::Degrees(n))),
111 },
112 Err(e) => Err(format!(
113 "A grouped expression with a unit must be of type numeric: {e:?}"
114 )),
115 }
116 } else {
117 Ok(expr_val)
118 }
119 }
120 PaxPrimary::FunctionOrEnum(scope, name_or_variant, args) => {
121 let args = args
122 .iter()
123 .map(|a| a.compute(idr.clone()))
124 .collect::<Result<Vec<PaxValue>, String>>()?;
125
126 if Functions::has_function(scope, name_or_variant) {
127 return call_function(scope.clone(), name_or_variant.clone(), args);
128 }
129
130 Ok(PaxValue::Enum(scope.clone(), name_or_variant.clone(), args))
131 }
132 }
133 }
134}
135
136impl Computable for PaxPrefix {
137 fn compute(&self, idr: Rc<dyn IdentifierResolver>) -> Result<PaxValue, String> {
138 let rhs = self.rhs.compute(idr)?;
139 let operator = &self.operator.name;
140 call_function("Math".to_string(), operator.to_string(), vec![rhs])
141 }
142}
143
144impl Computable for PaxPostfix {
145 fn compute(&self, idr: Rc<dyn IdentifierResolver>) -> Result<PaxValue, String> {
146 let lhs = self.lhs.compute(idr)?;
147 let operator = &self.operator.name;
148 call_function("Math".to_string(), operator.to_string(), vec![lhs])
149 }
150}
151
152impl Computable for PaxInfix {
153 fn compute(&self, idr: Rc<dyn IdentifierResolver>) -> Result<PaxValue, String> {
154 let lhs = self.lhs.compute(idr.clone())?;
155 let rhs = self.rhs.compute(idr)?;
156 let operator = &self.operator.name;
157 call_function("Math".to_string(), operator.to_string(), vec![lhs, rhs])
158 }
159}
160
161impl Computable for PaxIdentifier {
162 fn compute(&self, idr: Rc<dyn IdentifierResolver>) -> Result<PaxValue, String> {
163 idr.resolve(self.name.clone())
164 }
165}