pax_lang/interpreter/
computable.rs

1use 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
12/// Trait for expression types that can be computed to a value
13pub 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}