Skip to main content

pax_language/interpreter/
mod.rs

1#[cfg(feature = "parser")]
2use computable::Computable;
3use pax_runtime_api::PaxValue;
4#[cfg(feature = "parser")]
5use pest::{
6    iterators::{Pair, Pairs},
7    pratt_parser::PrattParser,
8};
9#[cfg(feature = "parser")]
10use property_resolution::IdentifierResolver;
11use serde::{Deserialize, Serialize};
12use std::fmt::Display;
13#[cfg(feature = "parser")]
14use std::rc::Rc;
15
16#[cfg(feature = "parser")]
17use crate::{deserializer::from_pax_ast, get_pax_pratt_parser, parse_pax_pairs, Rule};
18
19pub(crate) mod computable;
20pub mod property_resolution;
21#[cfg(test)]
22mod tests;
23
24#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
25/// PAXEL expression AST node.
26pub enum PaxExpression {
27    Primary(Box<PaxPrimary>),
28    Prefix(Box<PaxPrefix>),
29    Infix(Box<PaxInfix>),
30    Postfix(Box<PaxPostfix>),
31    Ternary(Box<PaxTernary>),
32    NullCoalesce(Box<PaxNullCoalesce>),
33}
34
35impl Default for PaxExpression {
36    fn default() -> Self {
37        Self::Primary(Box::new(PaxPrimary::default()))
38    }
39}
40
41impl PaxExpression {
42    /// Construct a prefix expression.
43    pub fn prefix(operator: impl Into<String>, rhs: PaxExpression) -> Self {
44        Self::Prefix(Box::new(PaxPrefix {
45            operator: PaxOperator::new(operator),
46            rhs: Box::new(rhs),
47        }))
48    }
49
50    /// Construct an infix expression.
51    pub fn infix(lhs: PaxExpression, operator: impl Into<String>, rhs: PaxExpression) -> Self {
52        Self::Infix(Box::new(PaxInfix {
53            operator: PaxOperator::new(operator),
54            lhs: Box::new(lhs),
55            rhs: Box::new(rhs),
56        }))
57    }
58
59    /// Construct a postfix expression.
60    pub fn postfix(lhs: PaxExpression, operator: impl Into<String>) -> Self {
61        Self::Postfix(Box::new(PaxPostfix {
62            operator: PaxOperator::new(operator),
63            lhs: Box::new(lhs),
64        }))
65    }
66
67    /// Construct a ternary expression.
68    pub fn ternary(
69        condition: PaxExpression,
70        then_branch: PaxExpression,
71        else_branch: PaxExpression,
72    ) -> Self {
73        Self::Ternary(Box::new(PaxTernary {
74            condition: Box::new(condition),
75            then_branch: Box::new(then_branch),
76            else_branch: Box::new(else_branch),
77        }))
78    }
79
80    /// Construct a null-coalescing expression.
81    pub fn null_coalesce(lhs: PaxExpression, rhs: PaxExpression) -> Self {
82        Self::NullCoalesce(Box::new(PaxNullCoalesce {
83            lhs: Box::new(lhs),
84            rhs: Box::new(rhs),
85        }))
86    }
87}
88
89impl PaxExpression {
90    // Mirror parsing.rs (including the outer grammar) when displaying ASTs
91    // constructed without explicit Grouped nodes, e.g. by cartridge generation.
92    fn precedence(&self) -> u8 {
93        match self {
94            Self::Ternary(_) => 1,
95            Self::NullCoalesce(_) => 2,
96            Self::Infix(i) => match i.operator.name.as_str() {
97                "||" => 3,
98                "&&" => 4,
99                "==" | "!=" | "<" | "<=" | ">" | ">=" => 5,
100                "+" | "-" => 6,
101                "*" | "/" | "%%" => 7,
102                "^" => 8,
103                _ => 0,
104            },
105            Self::Prefix(p) if p.operator.name == "-" => 9,
106            Self::Prefix(_) => 10,
107            Self::Postfix(_) | Self::Primary(_) => 11,
108        }
109    }
110
111    fn fmt_operand(&self, f: &mut std::fmt::Formatter<'_>, minimum: u8) -> std::fmt::Result {
112        if self.precedence() < minimum {
113            write!(f, "({self})")
114        } else {
115            write!(f, "{self}")
116        }
117    }
118}
119
120impl Display for PaxExpression {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        match self {
123            PaxExpression::Primary(p) => write!(f, "{}", p),
124            PaxExpression::Prefix(p) => {
125                write!(f, "{}", p.operator.name)?;
126                p.rhs.fmt_operand(f, self.precedence())
127            }
128            PaxExpression::Infix(i) => {
129                let right_associative = i.operator.name == "^";
130                i.lhs.fmt_operand(f, self.precedence() + u8::from(right_associative))?;
131                write!(f, " {} ", i.operator.name)?;
132                i.rhs.fmt_operand(f, self.precedence() + u8::from(!right_associative))
133            }
134            PaxExpression::Postfix(p) => write!(f, "{}{}", p.lhs, p.operator.name),
135            PaxExpression::Ternary(t) => {
136                t.condition.fmt_operand(f, self.precedence() + 1)?;
137                write!(f, " ? {} : {}", t.then_branch, t.else_branch)
138            }
139            PaxExpression::NullCoalesce(n) => {
140                n.lhs.fmt_operand(f, self.precedence() + 1)?;
141                write!(f, " ?? ")?;
142                n.rhs.fmt_operand(f, self.precedence())
143            }
144        }
145    }
146}
147
148#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
149/// Primary expression forms: literals, symbols, object/list/tuple literals, calls, and ranges.
150pub enum PaxPrimary {
151    Literal(PaxValue),
152    Grouped(Box<PaxExpression>, Option<PaxUnit>),
153    Identifier(PaxIdentifier, Vec<PaxAccessor>),
154    Object(Vec<(String, PaxExpression)>),
155    FunctionOrEnum(String, String, Vec<PaxExpression>),
156    Range(PaxExpression, PaxExpression),
157    Tuple(Vec<PaxExpression>),
158    List(Vec<PaxExpression>),
159}
160
161impl Display for PaxPrimary {
162    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163        match self {
164            PaxPrimary::Literal(l) => write!(f, "{}", l),
165            PaxPrimary::Grouped(e, u) => {
166                if let Some(u) = u {
167                    write!(
168                        f,
169                        "({}){}",
170                        e,
171                        match u {
172                            PaxUnit::Percent => "%",
173                            PaxUnit::Pixels => "px",
174                            PaxUnit::Radians => "rad",
175                            PaxUnit::Degrees => "deg",
176                            PaxUnit::Milliseconds => "ms",
177                            PaxUnit::Seconds => "s",
178                            PaxUnit::Frames => "f",
179                        }
180                    )
181                } else {
182                    write!(f, "({})", e)
183                }
184            }
185            PaxPrimary::Identifier(i, a) => {
186                write!(f, "{}", i.name)?;
187                for accessor in a {
188                    match accessor {
189                        PaxAccessor::Tuple(i) => write!(f, ".{}", i)?,
190                        PaxAccessor::List(e) => write!(f, "[{}]", e)?,
191                        PaxAccessor::Struct(s) => write!(f, ".{}", s)?,
192                    }
193                }
194                Ok(())
195            }
196            PaxPrimary::Object(o) => {
197                write!(f, "{{")?;
198                let mut o = o.iter().collect::<Vec<_>>();
199                o.sort_by(|a, b| a.0.cmp(&b.0));
200                for (i, (key, val)) in o.iter().enumerate() {
201                    write!(f, "{}: {}", key, val)?;
202                    if i != o.len() - 1 {
203                        write!(f, ", ")?;
204                    }
205                }
206                write!(f, "}}")?;
207                Ok(())
208            }
209            PaxPrimary::FunctionOrEnum(name, e, a) => {
210                if name == "Color" {
211                    write!(f, "{}", e)?;
212                } else {
213                    write!(f, "{}::{}", name, e)?;
214                }
215                if !a.is_empty() {
216                    write!(f, "(")?;
217                    for (i, arg) in a.iter().enumerate() {
218                        write!(f, "{}", arg)?;
219                        if i != a.len() - 1 {
220                            write!(f, ", ")?;
221                        }
222                    }
223                    write!(f, ")")?;
224                }
225                Ok(())
226            }
227            PaxPrimary::Range(s, e) => write!(f, "{}..{}", s, e),
228            PaxPrimary::Tuple(t) => {
229                write!(f, "(")?;
230                for (i, e) in t.iter().enumerate() {
231                    write!(f, "{}", e)?;
232                    if i != t.len() - 1 {
233                        write!(f, ", ")?;
234                    }
235                }
236                write!(f, ")")?;
237                Ok(())
238            }
239            PaxPrimary::List(l) => {
240                write!(f, "[")?;
241                for (i, e) in l.iter().enumerate() {
242                    write!(f, "{}", e)?;
243                    if i != l.len() - 1 {
244                        write!(f, ", ")?;
245                    }
246                }
247                write!(f, "]")?;
248                Ok(())
249            }
250        }
251    }
252}
253
254impl Default for PaxPrimary {
255    fn default() -> Self {
256        Self::Literal(PaxValue::default())
257    }
258}
259
260#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
261/// Unit suffix attached to a grouped numeric expression.
262pub enum PaxUnit {
263    Percent,
264    Pixels,
265    Radians,
266    Degrees,
267    Milliseconds,
268    Seconds,
269    Frames,
270}
271
272#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
273/// Access path applied after an identifier, such as `.field`, `.0`, or `[index]`.
274pub enum PaxAccessor {
275    Tuple(usize),
276    List(PaxExpression),
277    Struct(String),
278}
279
280#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
281/// Prefix operation such as numeric negation or boolean not.
282pub struct PaxPrefix {
283    operator: PaxOperator,
284    rhs: Box<PaxExpression>,
285}
286
287impl PaxPrefix {
288    /// Name of the prefix operator.
289    pub fn operator_name(&self) -> &str {
290        &self.operator.name
291    }
292
293    /// Right-hand expression.
294    pub fn rhs(&self) -> &PaxExpression {
295        &self.rhs
296    }
297}
298
299#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
300/// Binary infix operation with left and right expression operands.
301pub struct PaxInfix {
302    operator: PaxOperator,
303    lhs: Box<PaxExpression>,
304    rhs: Box<PaxExpression>,
305}
306
307impl PaxInfix {
308    /// Name of the infix operator.
309    pub fn operator_name(&self) -> &str {
310        &self.operator.name
311    }
312
313    /// Left-hand expression.
314    pub fn lhs(&self) -> &PaxExpression {
315        &self.lhs
316    }
317
318    /// Right-hand expression.
319    pub fn rhs(&self) -> &PaxExpression {
320        &self.rhs
321    }
322}
323
324#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
325/// Postfix operation node.
326pub struct PaxPostfix {
327    operator: PaxOperator,
328    lhs: Box<PaxExpression>,
329}
330
331impl PaxPostfix {
332    /// Name of the postfix operator.
333    pub fn operator_name(&self) -> &str {
334        &self.operator.name
335    }
336
337    /// Left-hand expression.
338    pub fn lhs(&self) -> &PaxExpression {
339        &self.lhs
340    }
341}
342
343#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
344/// Conditional expression with a boolean condition and selected true/false branch.
345pub struct PaxTernary {
346    condition: Box<PaxExpression>,
347    then_branch: Box<PaxExpression>,
348    else_branch: Box<PaxExpression>,
349}
350
351#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
352/// Short-circuiting fallback expression. `Some(value) ?? fallback` evaluates to
353/// `value`, `None ?? fallback` evaluates the fallback, and non-option left
354/// operands pass through unchanged.
355pub struct PaxNullCoalesce {
356    lhs: Box<PaxExpression>,
357    rhs: Box<PaxExpression>,
358}
359
360impl PaxTernary {
361    /// Condition expression.
362    pub fn condition(&self) -> &PaxExpression {
363        &self.condition
364    }
365
366    /// Expression evaluated when the condition is true.
367    pub fn then_branch(&self) -> &PaxExpression {
368        &self.then_branch
369    }
370
371    /// Expression evaluated when the condition is false.
372    pub fn else_branch(&self) -> &PaxExpression {
373        &self.else_branch
374    }
375}
376
377impl PaxNullCoalesce {
378    /// Left-hand expression.
379    pub fn lhs(&self) -> &PaxExpression {
380        &self.lhs
381    }
382
383    /// Right-hand fallback expression.
384    pub fn rhs(&self) -> &PaxExpression {
385        &self.rhs
386    }
387}
388
389#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
390/// Parsed operator token, stored by display name.
391pub struct PaxOperator {
392    name: String,
393}
394
395impl PaxOperator {
396    /// Construct an operator from its source spelling.
397    pub fn new(name: impl Into<String>) -> Self {
398        Self { name: name.into() }
399    }
400
401    /// Source spelling for this operator.
402    pub fn name(&self) -> &str {
403        &self.name
404    }
405}
406
407#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
408/// Symbol reference in a PAXEL expression.
409pub struct PaxIdentifier {
410    pub name: String,
411}
412
413impl Display for PaxIdentifier {
414    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
415        write!(f, "{}", self.name)
416    }
417}
418
419impl PaxIdentifier {
420    /// Construct an identifier from its source spelling.
421    pub fn new(name: &str) -> Self {
422        Self {
423            name: name.to_string(),
424        }
425    }
426}
427
428/// Parse a pax expression into a computable AST
429#[cfg(feature = "parser")]
430pub fn parse_pax_expression(expr: &str) -> Result<PaxExpression, String> {
431    let parsed_expr = parse_pax_pairs(Rule::expression_body, expr)
432        .map_err(|e| format!("Failed to parse expression: {}", e))?;
433    let pratt_parser = get_pax_pratt_parser();
434    recurse_pratt_parse(parsed_expr, &pratt_parser)
435}
436
437/// Parse an already-produced pest pair into a PAXEL expression AST.
438#[cfg(feature = "parser")]
439pub fn parse_pax_expression_from_pair(expr: Pair<Rule>) -> Result<PaxExpression, String> {
440    let pratt_parser = get_pax_pratt_parser();
441    recurse_pratt_parse(Pairs::single(expr), &pratt_parser)
442}
443
444#[cfg(feature = "parser")]
445fn recurse_pratt_parse(
446    expr: Pairs<Rule>,
447    pratt_parser: &PrattParser<Rule>,
448) -> Result<PaxExpression, String> {
449    pratt_parser
450        .map_primary(move |primary| match primary.as_rule() {
451            Rule::xo_literal => {
452                let inner = primary.into_inner().next().unwrap();
453                match inner.as_rule() {
454                    Rule::literal_value => {
455                        let pax_value = from_pax_ast(inner)
456                            .map_err(|e| format!("Failed to parse literal value: {}", e))?;
457                        let value = PaxPrimary::Literal(pax_value);
458                        let exp = PaxExpression::Primary(Box::new(value));
459                        Ok(exp)
460                    }
461                    Rule::literal_tuple_access => {
462                        let mut inner = inner.into_inner();
463                        let ident = inner.next().unwrap().as_str().trim().to_string();
464                        let index = inner.next().unwrap().as_str().parse::<usize>().unwrap();
465                        let value = PaxPrimary::Identifier(
466                            PaxIdentifier { name: ident },
467                            vec![PaxAccessor::Tuple(index)],
468                        );
469                        let exp = PaxExpression::Primary(Box::new(value));
470                        Ok(exp)
471                    }
472                    Rule::literal_list_access => {
473                        let mut inner = inner.into_inner();
474                        let ident = inner.next().unwrap().as_str().trim().to_string();
475                        let index = inner.next().unwrap().as_str();
476                        let index = parse_pax_expression(index)?;
477                        let value = PaxPrimary::Identifier(
478                            PaxIdentifier { name: ident },
479                            vec![PaxAccessor::List(index)],
480                        );
481                        let exp = PaxExpression::Primary(Box::new(value));
482                        Ok(exp)
483                    }
484                    _ => {
485                        return Err(format!("Unexpected rule: {:?}", inner.as_rule()));
486                    }
487                }
488            }
489            Rule::expression_body | Rule::expression_binary => {
490                recurse_pratt_parse(primary.into_inner(), pratt_parser)
491            }
492            Rule::expression_coalesce => {
493                let mut inner = primary.into_inner();
494                let lhs = recurse_pratt_parse(Pairs::single(inner.next().unwrap()), pratt_parser)?;
495                if inner.next().is_some() {
496                    let rhs =
497                        recurse_pratt_parse(Pairs::single(inner.next().unwrap()), pratt_parser)?;
498                    Ok(PaxExpression::NullCoalesce(Box::new(PaxNullCoalesce {
499                        lhs: Box::new(lhs),
500                        rhs: Box::new(rhs),
501                    })))
502                } else {
503                    Ok(lhs)
504                }
505            }
506            Rule::expression_ternary => {
507                let mut inner = primary.into_inner();
508                let condition =
509                    recurse_pratt_parse(Pairs::single(inner.next().unwrap()), pratt_parser)?;
510                if inner.next().is_some() {
511                    let then_branch =
512                        recurse_pratt_parse(Pairs::single(inner.next().unwrap()), pratt_parser)?;
513                    inner.next();
514                    let else_branch =
515                        recurse_pratt_parse(Pairs::single(inner.next().unwrap()), pratt_parser)?;
516                    Ok(PaxExpression::Ternary(Box::new(PaxTernary {
517                        condition: Box::new(condition),
518                        then_branch: Box::new(then_branch),
519                        else_branch: Box::new(else_branch),
520                    })))
521                } else {
522                    Ok(condition)
523                }
524            }
525            Rule::expression_grouped => {
526                let mut inner = primary.clone().into_inner();
527                let expr = inner.next().unwrap();
528                let expr_val = recurse_pratt_parse(expr.into_inner(), pratt_parser)?;
529                let ret: Result<PaxExpression, String> = if let Some(unit) = inner.next() {
530                    let unit = unit.as_str().trim().trim_start_matches(')');
531                    match unit {
532                        "%" => Ok(PaxExpression::Primary(Box::new(PaxPrimary::Grouped(
533                            Box::new(expr_val),
534                            Some(PaxUnit::Percent),
535                        )))),
536                        "px" => Ok(PaxExpression::Primary(Box::new(PaxPrimary::Grouped(
537                            Box::new(expr_val),
538                            Some(PaxUnit::Pixels),
539                        )))),
540                        "rad" => Ok(PaxExpression::Primary(Box::new(PaxPrimary::Grouped(
541                            Box::new(expr_val),
542                            Some(PaxUnit::Radians),
543                        )))),
544                        "deg" => Ok(PaxExpression::Primary(Box::new(PaxPrimary::Grouped(
545                            Box::new(expr_val),
546                            Some(PaxUnit::Degrees),
547                        )))),
548                        "ms" => Ok(PaxExpression::Primary(Box::new(PaxPrimary::Grouped(
549                            Box::new(expr_val),
550                            Some(PaxUnit::Milliseconds),
551                        )))),
552                        "s" => Ok(PaxExpression::Primary(Box::new(PaxPrimary::Grouped(
553                            Box::new(expr_val),
554                            Some(PaxUnit::Seconds),
555                        )))),
556                        "f" => Ok(PaxExpression::Primary(Box::new(PaxPrimary::Grouped(
557                            Box::new(expr_val),
558                            Some(PaxUnit::Frames),
559                        )))),
560                        _ => Err(format!("Unsupported unit: {}", unit)),
561                    }
562                } else {
563                    Ok(PaxExpression::Primary(Box::new(PaxPrimary::Grouped(
564                        Box::new(expr_val),
565                        None,
566                    ))))
567                };
568                ret
569            }
570            Rule::xo_enum_or_function_call => {
571                let mut inner = primary.into_inner();
572                while inner.len() > 3 {
573                    inner.next();
574                }
575                let scope = inner.next().unwrap().as_str().trim().to_string();
576                let function_name = inner.next().unwrap().as_str().trim().to_string();
577
578                let args = if let Some(args) = inner.next() {
579                    args.into_inner()
580                        .map(|a| recurse_pratt_parse(a.into_inner(), pratt_parser))
581                        .collect::<Result<Vec<PaxExpression>, String>>()?
582                } else {
583                    vec![]
584                };
585                let exp = PaxExpression::Primary(Box::new(PaxPrimary::FunctionOrEnum(
586                    scope,
587                    function_name,
588                    args,
589                )));
590                Ok(exp)
591            }
592            Rule::xo_color_space_func => {
593                let func = primary.as_str().trim().split("(").next().unwrap();
594                let inner = primary.into_inner();
595                let args = inner
596                    .map(|a| recurse_pratt_parse(a.into_inner(), pratt_parser))
597                    .collect::<Result<Vec<PaxExpression>, String>>()?;
598                let exp = PaxExpression::Primary(Box::new(PaxPrimary::FunctionOrEnum(
599                    "Color".to_string(),
600                    func.to_string(),
601                    args,
602                )));
603                Ok(exp)
604            }
605            Rule::xo_object => {
606                let mut inner = primary.into_inner();
607                while inner.peek().unwrap().as_rule() == Rule::identifier {
608                    inner.next();
609                }
610                let mut obj = vec![];
611                for pair in inner {
612                    let mut pair = pair.into_inner();
613                    // settings_key = { identifier ~ (":" | "=") }
614                    let key = pair
615                        .next()
616                        .unwrap()
617                        .as_str()
618                        .trim()
619                        .trim_end_matches(':')
620                        .trim_end_matches('=')
621                        .to_string();
622                    let value = pair.next().unwrap();
623                    let value = recurse_pratt_parse(value.into_inner(), pratt_parser)?;
624                    obj.push((key, value));
625                }
626                let value = PaxPrimary::Object(obj);
627                let exp = PaxExpression::Primary(Box::new(value));
628                Ok(exp)
629            }
630            Rule::xo_range => {
631                let mut inner = primary.into_inner();
632                let start_rule = Pairs::single(inner.next().unwrap());
633                let start = recurse_pratt_parse(start_rule, pratt_parser)?;
634                // xo_range_exclusive
635                inner.next();
636                let end_rule = Pairs::single(inner.next().unwrap());
637                let end = recurse_pratt_parse(end_rule, pratt_parser)?;
638                let value = PaxPrimary::Range(start, end);
639                let exp = PaxExpression::Primary(Box::new(value));
640                Ok(exp)
641            }
642            Rule::xo_tuple => {
643                let inner = primary.into_inner();
644                let tuple = inner
645                    .map(|e| recurse_pratt_parse(e.into_inner(), pratt_parser))
646                    .collect::<Result<Vec<PaxExpression>, String>>()?;
647                let value = PaxPrimary::Tuple(tuple);
648                let exp = PaxExpression::Primary(Box::new(value));
649                Ok(exp)
650            }
651            Rule::xo_list => {
652                let inner = primary.into_inner();
653                let list = inner
654                    .map(|e| recurse_pratt_parse(e.into_inner(), pratt_parser))
655                    .collect::<Result<Vec<PaxExpression>, String>>()?;
656                let value = PaxPrimary::List(list);
657                let exp = PaxExpression::Primary(Box::new(value));
658                Ok(exp)
659            }
660            Rule::xo_symbol => {
661                let builtins_prefix = if primary.as_str().starts_with("$") {
662                    "$"
663                } else {
664                    ""
665                }
666                .to_string();
667
668                let mut symbols = primary.into_inner();
669
670                // skip self or this
671                let peek = symbols.peek().unwrap();
672                if peek.as_str().trim() == "self" || peek.as_str().trim() == "this" {
673                    symbols.next();
674                }
675
676                let identifier = builtins_prefix + symbols.next().unwrap().as_str().trim();
677                let mut accessors = vec![];
678                for symbol in symbols {
679                    let accessor = match symbol.as_rule() {
680                        // list access
681                        Rule::expression_body => {
682                            let expr = recurse_pratt_parse(symbol.into_inner(), pratt_parser)?;
683                            PaxAccessor::List(expr)
684                        }
685                        // field access .field
686                        Rule::identifier => {
687                            let field = symbol.as_str().trim().to_string();
688                            PaxAccessor::Struct(field)
689                        }
690                        _ => {
691                            return Err(format!("Unexpected rule: {:?}", symbol.as_rule()));
692                        }
693                    };
694                    accessors.push(accessor);
695                }
696                let value = PaxPrimary::Identifier(PaxIdentifier { name: identifier }, accessors);
697                let exp = PaxExpression::Primary(Box::new(value));
698                Ok(exp)
699            }
700            _ => {
701                return Err(format!(
702                    "Unexpected rule: {:?}, str: {} ",
703                    primary.as_rule(),
704                    primary.as_str()
705                ));
706            }
707        })
708        .map_prefix(|op, rhs| match op.as_rule() {
709            _ => {
710                let a = PaxOperator {
711                    name: op.as_str().trim().to_string(),
712                };
713                let r = rhs?;
714                let exp = PaxExpression::Prefix(Box::new(PaxPrefix {
715                    operator: a,
716                    rhs: Box::new(r),
717                }));
718                Ok(exp)
719            }
720        })
721        .map_postfix(|lhs, op| match op.as_rule() {
722            _ => {
723                let a = PaxOperator {
724                    name: op.as_str().trim().to_string(),
725                };
726                let l = lhs?;
727                let exp = PaxExpression::Postfix(Box::new(PaxPostfix {
728                    operator: a,
729                    lhs: Box::new(l),
730                }));
731                Ok(exp)
732            }
733        })
734        .map_infix(|lhs, op, rhs| match op.as_rule() {
735            _ => {
736                let a = PaxOperator {
737                    name: op.as_str().trim().to_string(),
738                };
739                let l = lhs?;
740                let r = rhs?;
741                let exp = PaxExpression::Infix(Box::new(PaxInfix {
742                    operator: a,
743                    lhs: Box::new(l),
744                    rhs: Box::new(r),
745                }));
746                Ok(exp)
747            }
748        })
749        .parse(expr)
750}
751
752/// Compute a pax expression to a PaxValue
753#[cfg(feature = "parser")]
754pub fn compute_paxel(expr: &str, idr: Rc<dyn IdentifierResolver>) -> Result<PaxValue, String> {
755    let expr = parse_pax_expression(expr)?;
756    expr.compute(idr)
757}