expr/ast/
postfix_operator.rs

1use std::iter::once;
2use crate::ast::node::Node;
3use crate::Rule;
4use crate::{bail, Result};
5use crate::{Context, Parser, Value};
6use log::trace;
7use pest::iterators::Pair;
8
9#[derive(Debug, Clone, strum::Display)]
10pub enum PostfixOperator {
11    Index { idx: Box<Node>, optional: bool },
12    Range(Option<i64>, Option<i64>),
13    Default(Box<Node>),
14    Pipe(Box<Node>),
15    Ternary { left: Box<Node>, right: Box<Node> },
16}
17
18impl From<Pair<'_, Rule>> for PostfixOperator {
19    fn from(pair: Pair<Rule>) -> Self {
20        trace!("{:?}={}", pair.as_rule(), pair.as_str());
21        match pair.as_rule() {
22            Rule::index_op | Rule::membership_op => PostfixOperator::Index {
23                idx: Box::new(pair.into_inner().into()),
24                optional: false,
25            },
26            Rule::opt_index_op | Rule::opt_membership_op => PostfixOperator::Index {
27                idx: Box::new(pair.into_inner().into()),
28                optional: true,
29            },
30            Rule::range_start_op => {
31                let mut inner = pair.into_inner();
32                let start = inner.next().map(|p| p.as_str().parse().unwrap());
33                let end = inner.next().map(|p| p.as_str().parse().unwrap());
34                PostfixOperator::Range(start, end)
35            }
36            Rule::range_end_op => {
37                let mut inner = pair.into_inner();
38                let end = inner.next().map(|p| p.as_str().parse().unwrap());
39                PostfixOperator::Range(None, end)
40            }
41            Rule::default_op => PostfixOperator::Default(Box::new(pair.into_inner().into())),
42            Rule::ternary => {
43                let mut inner = pair.into_inner();
44                let left = Box::new(inner.next().unwrap().into());
45                let right = Box::new(inner.next().unwrap().into());
46                PostfixOperator::Ternary { left, right }
47            }
48            Rule::pipe => PostfixOperator::Pipe(Box::new(pair.into_inner().into())),
49            rule => unreachable!("Unexpected rule: {rule:?}"),
50        }
51    }
52}
53
54impl Parser<'_> {
55    pub fn eval_postfix_operator(
56        &self,
57        ctx: &Context,
58        operator: PostfixOperator,
59        node: Node,
60    ) -> Result<Value> {
61        let value = self.eval_expr(ctx, node)?;
62        let result = match operator {
63            PostfixOperator::Index { idx, optional } => match self.eval_index_key(ctx, *idx)? {
64                Value::Number(idx) => match value {
65                    Value::Array(arr) => {
66                        let idx = i64_to_idx(idx, arr.len());
67                        arr.get(idx).cloned().unwrap_or(Value::Nil)
68                    }
69                    _ if optional => Value::Nil,
70                    _ => bail!("Invalid operand for operator []"),
71                },
72                Value::String(key) => match value {
73                    Value::Map(map) => map.get(&key).cloned().unwrap_or(Value::Nil),
74                    _ if optional => Value::Nil,
75                    _ => bail!("Invalid operand for operator []"),
76                },
77                v => bail!("Invalid operand for operator []: {v:?}"),
78            },
79            PostfixOperator::Range(start, end) => match value {
80                Value::Array(arr) => {
81                    let start = i64_to_idx(start.unwrap_or(0), arr.len());
82                    let end = i64_to_idx(end.unwrap_or(arr.len() as i64), arr.len());
83                    let result = arr[start..end].to_vec();
84                    Value::Array(result)
85                }
86                _ => bail!("Invalid operand for operator []"),
87            },
88            PostfixOperator::Default(default) => match value {
89                Value::Nil => self.eval_expr(ctx, *default)?,
90                value => value,
91            },
92            PostfixOperator::Ternary { left, right } => match value {
93                Value::Bool(true) => self.eval_expr(ctx, *left)?,
94                Value::Bool(false) => self.eval_expr(ctx, *right)?,
95                value => bail!("Invalid condition for ?: {value:?}"),
96            },
97            PostfixOperator::Pipe(func) => {
98                if let Node::Func {
99                    ident,
100                    args,
101                    predicate,
102                } = *func
103                {
104                    let args = args.into_iter()
105                        .map(|arg| self.eval_expr(ctx, arg))
106                        .chain(once(Ok(value)))
107                        .collect::<Result<Vec<Value>>>()?;
108                    self.exec_func(ctx, ident, args, predicate.map(|p| *p))?
109                } else {
110                    bail!("Invalid operand for operator |");
111                }
112            }
113        };
114
115        Ok(result)
116    }
117
118    fn eval_index_key(&self, ctx: &Context, idx: Node) -> Result<Value> {
119        match idx {
120            Node::Value(v) => Ok(v),
121            Node::Ident(id) => Ok(Value::String(id)),
122            idx => self.eval_expr(ctx, idx),
123        }
124    }
125}
126
127fn i64_to_idx(idx: i64, len: usize) -> usize {
128    if idx < 0 {
129        (len as i64 + idx) as usize
130    } else {
131        idx as usize
132    }
133}