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}