pax_lang/interpreter/
mod.rs

1use computable::Computable;
2use pax_runtime_api::PaxValue;
3use pest::{
4    iterators::{Pair, Pairs},
5    pratt_parser::PrattParser,
6};
7use property_resolution::IdentifierResolver;
8use serde::{Deserialize, Serialize};
9use std::fmt::Display;
10use std::rc::Rc;
11
12use crate::{deserializer::from_pax_ast, get_pax_pratt_parser, parse_pax_pairs, Rule};
13
14pub(crate) mod computable;
15pub mod property_resolution;
16#[cfg(test)]
17mod tests;
18
19#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
20pub enum PaxExpression {
21    Primary(Box<PaxPrimary>),
22    Prefix(Box<PaxPrefix>),
23    Infix(Box<PaxInfix>),
24    Postfix(Box<PaxPostfix>),
25}
26
27impl Default for PaxExpression {
28    fn default() -> Self {
29        Self::Primary(Box::new(PaxPrimary::default()))
30    }
31}
32
33impl Display for PaxExpression {
34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        match self {
36            PaxExpression::Primary(p) => write!(f, "{}", p),
37            PaxExpression::Prefix(p) => write!(f, "{}{}", p.operator.name, p.rhs),
38            PaxExpression::Infix(i) => write!(f, "{} {} {}", i.lhs, i.operator.name, i.rhs),
39            PaxExpression::Postfix(p) => write!(f, "{}{}", p.lhs, p.operator.name),
40        }
41    }
42}
43
44#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
45pub enum PaxPrimary {
46    Literal(PaxValue),
47    Grouped(Box<PaxExpression>, Option<PaxUnit>),
48    Identifier(PaxIdentifier, Vec<PaxAccessor>),
49    Object(Vec<(String, PaxExpression)>),
50    FunctionOrEnum(String, String, Vec<PaxExpression>),
51    Range(PaxExpression, PaxExpression),
52    Tuple(Vec<PaxExpression>),
53    List(Vec<PaxExpression>),
54}
55
56impl Display for PaxPrimary {
57    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58        match self {
59            PaxPrimary::Literal(l) => write!(f, "{}", l),
60            PaxPrimary::Grouped(e, u) => {
61                if let Some(u) = u {
62                    write!(
63                        f,
64                        "({}){}",
65                        e,
66                        match u {
67                            PaxUnit::Percent => "%",
68                            PaxUnit::Pixels => "px",
69                            PaxUnit::Radians => "rad",
70                            PaxUnit::Degrees => "deg",
71                        }
72                    )
73                } else {
74                    write!(f, "({})", e)
75                }
76            }
77            PaxPrimary::Identifier(i, a) => {
78                write!(f, "{}", i.name)?;
79                for accessor in a {
80                    match accessor {
81                        PaxAccessor::Tuple(i) => write!(f, ".{}", i)?,
82                        PaxAccessor::List(e) => write!(f, "[{}]", e)?,
83                        PaxAccessor::Struct(s) => write!(f, ".{}", s)?,
84                    }
85                }
86                Ok(())
87            }
88            PaxPrimary::Object(o) => {
89                write!(f, "{{")?;
90                let mut o = o.iter().collect::<Vec<_>>();
91                o.sort_by(|a, b| a.0.cmp(&b.0));
92                for (i, (key, val)) in o.iter().enumerate() {
93                    write!(f, "{}: {}", key, val)?;
94                    if i != o.len() - 1 {
95                        write!(f, ", ")?;
96                    }
97                }
98                write!(f, "}}")?;
99                Ok(())
100            }
101            PaxPrimary::FunctionOrEnum(name, e, a) => {
102                if name == "Color" {
103                    write!(f, "{}", e)?;
104                } else {
105                    write!(f, "{}::{}", name, e)?;
106                }
107                if !a.is_empty() {
108                    write!(f, "(")?;
109                    for (i, arg) in a.iter().enumerate() {
110                        write!(f, "{}", arg)?;
111                        if i != a.len() - 1 {
112                            write!(f, ", ")?;
113                        }
114                    }
115                    write!(f, ")")?;
116                }
117                Ok(())
118            }
119            PaxPrimary::Range(s, e) => write!(f, "{}..{}", s, e),
120            PaxPrimary::Tuple(t) => {
121                write!(f, "(")?;
122                for (i, e) in t.iter().enumerate() {
123                    write!(f, "{}", e)?;
124                    if i != t.len() - 1 {
125                        write!(f, ", ")?;
126                    }
127                }
128                write!(f, ")")?;
129                Ok(())
130            }
131            PaxPrimary::List(l) => {
132                write!(f, "[")?;
133                for (i, e) in l.iter().enumerate() {
134                    write!(f, "{}", e)?;
135                    if i != l.len() - 1 {
136                        write!(f, ", ")?;
137                    }
138                }
139                write!(f, "]")?;
140                Ok(())
141            }
142        }
143    }
144}
145
146impl Default for PaxPrimary {
147    fn default() -> Self {
148        Self::Literal(PaxValue::default())
149    }
150}
151
152#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
153pub enum PaxUnit {
154    Percent,
155    Pixels,
156    Radians,
157    Degrees,
158}
159
160#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
161pub enum PaxAccessor {
162    Tuple(usize),
163    List(PaxExpression),
164    Struct(String),
165}
166
167#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
168pub struct PaxPrefix {
169    operator: PaxOperator,
170    rhs: Box<PaxExpression>,
171}
172
173#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
174pub struct PaxInfix {
175    operator: PaxOperator,
176    lhs: Box<PaxExpression>,
177    rhs: Box<PaxExpression>,
178}
179
180#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
181pub struct PaxPostfix {
182    operator: PaxOperator,
183    lhs: Box<PaxExpression>,
184}
185
186#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
187pub struct PaxOperator {
188    name: String,
189}
190
191#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
192pub struct PaxIdentifier {
193    pub name: String,
194}
195
196impl Display for PaxIdentifier {
197    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198        write!(f, "{}", self.name)
199    }
200}
201
202impl PaxIdentifier {
203    pub fn new(name: &str) -> Self {
204        Self {
205            name: name.to_string(),
206        }
207    }
208}
209
210/// Parse a pax expression into a computable AST
211pub fn parse_pax_expression(expr: &str) -> Result<PaxExpression, String> {
212    let parsed_expr = parse_pax_pairs(Rule::expression_body, expr)
213        .map_err(|e| format!("Failed to parse expression: {}", e))?;
214    let pratt_parser = get_pax_pratt_parser();
215    recurse_pratt_parse(parsed_expr, &pratt_parser)
216}
217
218pub fn parse_pax_expression_from_pair(expr: Pair<Rule>) -> Result<PaxExpression, String> {
219    let pratt_parser = get_pax_pratt_parser();
220    recurse_pratt_parse(Pairs::single(expr), &pratt_parser)
221}
222
223fn recurse_pratt_parse(
224    expr: Pairs<Rule>,
225    pratt_parser: &PrattParser<Rule>,
226) -> Result<PaxExpression, String> {
227    pratt_parser
228        .map_primary(move |primary| match primary.as_rule() {
229            Rule::xo_literal => {
230                let inner = primary.into_inner().next().unwrap();
231                match inner.as_rule() {
232                    Rule::literal_value => {
233                        let pax_value = from_pax_ast(inner)
234                            .map_err(|e| format!("Failed to parse literal value: {}", e))?;
235                        let value = PaxPrimary::Literal(pax_value);
236                        let exp = PaxExpression::Primary(Box::new(value));
237                        Ok(exp)
238                    }
239                    Rule::literal_tuple_access => {
240                        let mut inner = inner.into_inner();
241                        let ident = inner.next().unwrap().as_str().trim().to_string();
242                        let index = inner.next().unwrap().as_str().parse::<usize>().unwrap();
243                        let value = PaxPrimary::Identifier(
244                            PaxIdentifier { name: ident },
245                            vec![PaxAccessor::Tuple(index)],
246                        );
247                        let exp = PaxExpression::Primary(Box::new(value));
248                        Ok(exp)
249                    }
250                    Rule::literal_list_access => {
251                        let mut inner = inner.into_inner();
252                        let ident = inner.next().unwrap().as_str().trim().to_string();
253                        let index = inner.next().unwrap().as_str();
254                        let index = parse_pax_expression(index)?;
255                        let value = PaxPrimary::Identifier(
256                            PaxIdentifier { name: ident },
257                            vec![PaxAccessor::List(index)],
258                        );
259                        let exp = PaxExpression::Primary(Box::new(value));
260                        Ok(exp)
261                    }
262                    _ => {
263                        return Err(format!("Unexpected rule: {:?}", inner.as_rule()));
264                    }
265                }
266            }
267            Rule::expression_body => recurse_pratt_parse(primary.into_inner(), pratt_parser),
268            Rule::expression_grouped => {
269                let mut inner = primary.clone().into_inner();
270                let expr = inner.next().unwrap();
271                let expr_val = recurse_pratt_parse(expr.into_inner(), pratt_parser)?;
272                let ret: Result<PaxExpression, String> = if let Some(unit) = inner.next() {
273                    let unit = unit.as_str().trim();
274                    match unit {
275                        "%" => Ok(PaxExpression::Primary(Box::new(PaxPrimary::Grouped(
276                            Box::new(expr_val),
277                            Some(PaxUnit::Percent),
278                        )))),
279                        "px" => Ok(PaxExpression::Primary(Box::new(PaxPrimary::Grouped(
280                            Box::new(expr_val),
281                            Some(PaxUnit::Pixels),
282                        )))),
283                        "rad" => Ok(PaxExpression::Primary(Box::new(PaxPrimary::Grouped(
284                            Box::new(expr_val),
285                            Some(PaxUnit::Radians),
286                        )))),
287                        "deg" => Ok(PaxExpression::Primary(Box::new(PaxPrimary::Grouped(
288                            Box::new(expr_val),
289                            Some(PaxUnit::Degrees),
290                        )))),
291                        _ => Err(format!("Unsupported unit: {}", unit)),
292                    }
293                } else {
294                    Ok(PaxExpression::Primary(Box::new(PaxPrimary::Grouped(
295                        Box::new(expr_val),
296                        None,
297                    ))))
298                };
299                ret
300            }
301            Rule::xo_enum_or_function_call => {
302                let mut inner = primary.into_inner();
303                while inner.len() > 3 {
304                    inner.next();
305                }
306                let scope = inner.next().unwrap().as_str().trim().to_string();
307                let function_name = inner.next().unwrap().as_str().trim().to_string();
308
309                let args = if let Some(args) = inner.next() {
310                    args.into_inner()
311                        .map(|a| recurse_pratt_parse(a.into_inner(), pratt_parser))
312                        .collect::<Result<Vec<PaxExpression>, String>>()?
313                } else {
314                    vec![]
315                };
316                let exp = PaxExpression::Primary(Box::new(PaxPrimary::FunctionOrEnum(
317                    scope,
318                    function_name,
319                    args,
320                )));
321                Ok(exp)
322            }
323            Rule::xo_color_space_func => {
324                let func = primary.as_str().trim().split("(").next().unwrap();
325                let inner = primary.into_inner();
326                let args = inner
327                    .map(|a| recurse_pratt_parse(a.into_inner(), pratt_parser))
328                    .collect::<Result<Vec<PaxExpression>, String>>()?;
329                let exp = PaxExpression::Primary(Box::new(PaxPrimary::FunctionOrEnum(
330                    "Color".to_string(),
331                    func.to_string(),
332                    args,
333                )));
334                Ok(exp)
335            }
336            Rule::xo_object => {
337                let mut inner = primary.into_inner();
338                while inner.peek().unwrap().as_rule() == Rule::identifier {
339                    inner.next();
340                }
341                let mut obj = vec![];
342                for pair in inner {
343                    let mut pair = pair.into_inner();
344                    // settings_key = { identifier ~ (":" | "=") }
345                    let key = pair
346                        .next()
347                        .unwrap()
348                        .as_str()
349                        .trim()
350                        .trim_end_matches(':')
351                        .trim_end_matches('=')
352                        .to_string();
353                    let value = pair.next().unwrap();
354                    let value = recurse_pratt_parse(value.into_inner(), pratt_parser)?;
355                    obj.push((key, value));
356                }
357                let value = PaxPrimary::Object(obj);
358                let exp = PaxExpression::Primary(Box::new(value));
359                Ok(exp)
360            }
361            Rule::xo_range => {
362                let mut inner = primary.into_inner();
363                let start_rule = Pairs::single(inner.next().unwrap());
364                let start = recurse_pratt_parse(start_rule, pratt_parser)?;
365                // xo_range_exclusive
366                inner.next();
367                let end_rule = Pairs::single(inner.next().unwrap());
368                let end = recurse_pratt_parse(end_rule, pratt_parser)?;
369                let value = PaxPrimary::Range(start, end);
370                let exp = PaxExpression::Primary(Box::new(value));
371                Ok(exp)
372            }
373            Rule::xo_tuple => {
374                let inner = primary.into_inner();
375                let tuple = inner
376                    .map(|e| recurse_pratt_parse(e.into_inner(), pratt_parser))
377                    .collect::<Result<Vec<PaxExpression>, String>>()?;
378                let value = PaxPrimary::Tuple(tuple);
379                let exp = PaxExpression::Primary(Box::new(value));
380                Ok(exp)
381            }
382            Rule::xo_list => {
383                let inner = primary.into_inner();
384                let list = inner
385                    .map(|e| recurse_pratt_parse(e.into_inner(), pratt_parser))
386                    .collect::<Result<Vec<PaxExpression>, String>>()?;
387                let value = PaxPrimary::List(list);
388                let exp = PaxExpression::Primary(Box::new(value));
389                Ok(exp)
390            }
391            Rule::xo_symbol => {
392                let builtins_prefix = if primary.as_str().starts_with("$") {
393                    "$"
394                } else {
395                    ""
396                }
397                .to_string();
398
399                let mut symbols = primary.into_inner();
400
401                // skip self or this
402                let peek = symbols.peek().unwrap();
403                if peek.as_str().trim() == "self" || peek.as_str().trim() == "this" {
404                    symbols.next();
405                }
406
407                let identifier = builtins_prefix + symbols.next().unwrap().as_str().trim();
408                let mut accessors = vec![];
409                for symbol in symbols {
410                    let accessor = match symbol.as_rule() {
411                        // list access
412                        Rule::expression_body => {
413                            let expr = recurse_pratt_parse(symbol.into_inner(), pratt_parser)?;
414                            PaxAccessor::List(expr)
415                        }
416                        // field access .field
417                        Rule::identifier => {
418                            let field = symbol.as_str().trim().to_string();
419                            PaxAccessor::Struct(field)
420                        }
421                        _ => {
422                            return Err(format!("Unexpected rule: {:?}", symbol.as_rule()));
423                        }
424                    };
425                    accessors.push(accessor);
426                }
427                let value = PaxPrimary::Identifier(PaxIdentifier { name: identifier }, accessors);
428                let exp = PaxExpression::Primary(Box::new(value));
429                Ok(exp)
430            }
431            _ => {
432                return Err(format!(
433                    "Unexpected rule: {:?}, str: {} ",
434                    primary.as_rule(),
435                    primary.as_str()
436                ));
437            }
438        })
439        .map_prefix(|op, rhs| match op.as_rule() {
440            _ => {
441                let a = PaxOperator {
442                    name: op.as_str().trim().to_string(),
443                };
444                let r = rhs?;
445                let exp = PaxExpression::Prefix(Box::new(PaxPrefix {
446                    operator: a,
447                    rhs: Box::new(r),
448                }));
449                Ok(exp)
450            }
451        })
452        .map_postfix(|lhs, op| match op.as_rule() {
453            _ => {
454                let a = PaxOperator {
455                    name: op.as_str().trim().to_string(),
456                };
457                let l = lhs?;
458                let exp = PaxExpression::Postfix(Box::new(PaxPostfix {
459                    operator: a,
460                    lhs: Box::new(l),
461                }));
462                Ok(exp)
463            }
464        })
465        .map_infix(|lhs, op, rhs| match op.as_rule() {
466            _ => {
467                let a = PaxOperator {
468                    name: op.as_str().trim().to_string(),
469                };
470                let l = lhs?;
471                let r = rhs?;
472                let exp = PaxExpression::Infix(Box::new(PaxInfix {
473                    operator: a,
474                    lhs: Box::new(l),
475                    rhs: Box::new(r),
476                }));
477                Ok(exp)
478            }
479        })
480        .parse(expr)
481}
482
483/// Compute a pax expression to a PaxValue
484pub fn compute_paxel(expr: &str, idr: Rc<dyn IdentifierResolver>) -> Result<PaxValue, String> {
485    let expr = parse_pax_expression(expr)?;
486    expr.compute(idr)
487}