expr/ast/
postfix_operator.rs1use 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}