Skip to main content

expr/ast/
postfix_operator.rs

1use crate::ast::node::Node;
2use crate::Rule;
3use crate::{bail, Result};
4use crate::{ContextProvider, Environment, Value};
5use log::trace;
6use pest::iterators::Pair;
7use std::iter::once;
8
9#[derive(Debug, Clone, strum::Display)]
10pub enum PostfixOperator {
11    Index { idx: Box<Node>, optional: bool },
12    Range {
13        start: Option<Box<Node>>,
14        end: Option<Box<Node>>,
15    },
16    Default(Box<Node>),
17    Pipe(Box<Node>),
18    Ternary { left: Box<Node>, right: Box<Node> },
19    Method { ident: String, args: Vec<Node> },
20}
21
22impl PostfixOperator {
23    pub(crate) fn contains_hash_ident(&self) -> bool {
24        match self {
25            PostfixOperator::Index { idx, .. } => idx.contains_hash_ident(),
26            PostfixOperator::Default(node) | PostfixOperator::Pipe(node) => {
27                node.contains_hash_ident()
28            }
29            PostfixOperator::Ternary { left, right } => {
30                left.contains_hash_ident() || right.contains_hash_ident()
31            }
32            PostfixOperator::Method { args, .. } => {
33                args.iter().any(Node::contains_hash_ident)
34            }
35            PostfixOperator::Range { start, end } => start
36                .iter()
37                .chain(end)
38                .any(|node| node.contains_hash_ident()),
39        }
40    }
41}
42
43impl From<Pair<'_, Rule>> for PostfixOperator {
44    fn from(pair: Pair<Rule>) -> Self {
45        trace!("{:?}={}", pair.as_rule(), pair.as_str());
46        match pair.as_rule() {
47            Rule::index_op => PostfixOperator::Index {
48                idx: Box::new(pair.into_inner().into()),
49                optional: false,
50            },
51            Rule::membership_op => PostfixOperator::Index {
52                idx: Box::new(Node::Value(Value::String(
53                    pair.into_inner().next().unwrap().as_str().to_string(),
54                ))),
55                optional: false,
56            },
57            Rule::opt_index_op => PostfixOperator::Index {
58                idx: Box::new(pair.into_inner().into()),
59                optional: true,
60            },
61            Rule::opt_membership_op => PostfixOperator::Index {
62                idx: Box::new(Node::Value(Value::String(
63                    pair.into_inner().next().unwrap().as_str().to_string(),
64                ))),
65                optional: true,
66            },
67            Rule::range_start_op => {
68                let mut inner = pair.into_inner();
69                let start = inner.next().map(|p| Box::new(p.into()));
70                let end = inner.next().map(|p| Box::new(p.into()));
71                PostfixOperator::Range { start, end }
72            }
73            Rule::range_end_op => {
74                let mut inner = pair.into_inner();
75                let end = inner.next().map(|p| Box::new(p.into()));
76                PostfixOperator::Range { start: None, end }
77            }
78            Rule::default_op => PostfixOperator::Default(Box::new(pair.into_inner().into())),
79            Rule::ternary => {
80                let mut inner = pair.into_inner();
81                let left = Box::new(inner.next().unwrap().into());
82                let right = Box::new(inner.next().unwrap().into());
83                PostfixOperator::Ternary { left, right }
84            }
85            Rule::pipe => PostfixOperator::Pipe(Box::new(pair.into_inner().into())),
86            Rule::method_call => {
87                let mut inner = pair.into_inner();
88                PostfixOperator::Method {
89                    ident: inner.next().expect("method name").as_str().to_string(),
90                    args: inner.map(Node::from).collect(),
91                }
92            }
93            rule => unreachable!("Unexpected rule: {rule:?}"),
94        }
95    }
96}
97
98impl Environment<'_> {
99    pub fn eval_postfix_operator(
100        &self,
101        ctx: &dyn ContextProvider,
102        operator: &PostfixOperator,
103        node: &Node,
104    ) -> Result<Value> {
105        let value = self.eval_expr(ctx, node)?;
106        let result = match operator {
107            PostfixOperator::Index { idx, optional } => {
108                let key = self.eval_expr(ctx, idx)?;
109                match (&key, value) {
110                    (Value::Integer(idx), Value::Array(arr)) => {
111                        let idx = i64_to_idx(*idx, arr.len());
112                        arr.get(idx).cloned().unwrap_or(Value::Nil)
113                    }
114                    (Value::Integer(idx), Value::Bytes(bytes)) => {
115                        let idx = i64_to_idx(*idx, bytes.len());
116                        bytes
117                            .get(idx)
118                            .map(|byte| Value::Integer((*byte).into()))
119                            .unwrap_or(Value::Nil)
120                    }
121                    (Value::String(key), Value::Map(map)) => {
122                        map.get(key).cloned().unwrap_or(Value::Nil)
123                    }
124                    (key, Value::KeyedMap(map)) => map
125                        .into_iter()
126                        .find(|(candidate, _)| {
127                            crate::ast::operator::map_keys_equal(key, candidate)
128                        })
129                        .map(|(_, value)| value)
130                        .unwrap_or(Value::Nil),
131                    (_, _) if *optional => Value::Nil,
132                    _ => bail!("Invalid operand for operator []: {key:?}"),
133                }
134            },
135            PostfixOperator::Range { start, end } => {
136                let start = self.eval_slice_bound(ctx, start.as_deref())?;
137                let end = self.eval_slice_bound(ctx, end.as_deref())?;
138                match value {
139                    Value::Array(arr) => {
140                        let (start, end) = slice_bounds(start, end, arr.len());
141                        let result = arr.get(start..end).unwrap_or_default().to_vec();
142                        Value::Array(result)
143                    }
144                    Value::Bytes(bytes) => {
145                        let (start, end) = slice_bounds(start, end, bytes.len());
146                        Value::Bytes(bytes.get(start..end).unwrap_or_default().to_vec())
147                    }
148                    Value::String(string) => {
149                        let (start, end) = slice_bounds(start, end, string.len());
150                        Value::String(
151                            String::from_utf8_lossy(&string.as_bytes()[start..end]).into(),
152                        )
153                    }
154                    _ => bail!("Invalid operand for operator []"),
155                }
156            },
157            PostfixOperator::Default(default) => match value {
158                Value::Nil => self.eval_expr(ctx, default)?,
159                value => value,
160            },
161            PostfixOperator::Ternary { left, right } => match value {
162                Value::Bool(true) => self.eval_expr(ctx, left)?,
163                Value::Bool(false) => self.eval_expr(ctx, right)?,
164                value => bail!("Invalid condition for ?: {value:?}"),
165            },
166            PostfixOperator::Pipe(func) => {
167                if let Node::Func {
168                    ident,
169                    args,
170                    predicate,
171                } = func.as_ref()
172                {
173                    let args = once(Ok(value))
174                        .chain(args.iter().map(|arg| self.eval_expr(ctx, arg)))
175                        .collect::<Result<Vec<Value>>>()?;
176                    self.eval_func(ctx, ident, args, predicate.as_deref())?
177                } else {
178                    bail!("Invalid operand for operator |");
179                }
180            }
181            PostfixOperator::Method { ident, args } => {
182                let args = args
183                    .iter()
184                    .map(|arg| self.eval_expr(ctx, arg))
185                    .collect::<Result<Vec<_>>>()?;
186                crate::functions::temporal::eval_method(value, ident, args)?
187            }
188        };
189
190        Ok(result)
191    }
192
193    fn eval_slice_bound(
194        &self,
195        ctx: &dyn ContextProvider,
196        bound: Option<&Node>,
197    ) -> Result<Option<i64>> {
198        match bound {
199            Some(bound) => match self.eval_expr(ctx, bound)? {
200                Value::Integer(value) => Ok(Some(value)),
201                value => bail!("Invalid slice index: {value:?}"),
202            },
203            None => Ok(None),
204        }
205    }
206}
207
208fn i64_to_idx(idx: i64, len: usize) -> usize {
209    if idx < 0 {
210        (len as i64 + idx) as usize
211    } else {
212        idx as usize
213    }
214}
215
216fn slice_idx(idx: i64, len: usize) -> usize {
217    if idx < 0 {
218        len.saturating_sub(idx.unsigned_abs() as usize)
219    } else {
220        usize::try_from(idx).unwrap_or(usize::MAX).min(len)
221    }
222}
223
224fn slice_bounds(start: Option<i64>, end: Option<i64>, len: usize) -> (usize, usize) {
225    let start = slice_idx(start.unwrap_or(0), len);
226    let end = slice_idx(end.unwrap_or(len as i64), len);
227    (start.min(end), end)
228}