Skip to main content

expr/ast/
postfix_operator.rs

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