1use crate::{
12 aim::{ContextLike, WriterLike, Writer},
13 expressions::{
14 Expression, ExpressionLike, Primary, Reference, parse_argument_list, parse_expression,
15 parse_arc_identifier, parse_primary,
16 },
17 literals::Literal,
18 values::{CriticalError, Value},
19};
20use anyhow::{Result, anyhow};
21use nom::{
22 Err as NomErr, IResult, Parser,
23 branch::alt,
24 bytes::complete::tag,
25 character::complete::{char, multispace0},
26 combinator::{map, opt},
27 error::Error,
28 multi::many0,
29 sequence::delimited,
30};
31use std::{
32 fmt, sync::Arc
33};
34
35#[derive(Debug, Clone, PartialEq)]
43pub enum Postfix {
44 Empty,
46 Function(Arc<str>, Box<Expression>),
48 Index(Box<Postfix>, Box<Expression>),
50 Method(Box<Postfix>, Arc<str>, Box<Expression>),
52 Inference(Arc<Reference>, Box<Expression>),
54 Critical(Box<Postfix>),
56 Primary(Box<Primary>),
58}
59
60impl Postfix {
61 pub fn is_empty(&self) -> bool {
63 match self {
64 Postfix::Empty => true,
65 _ => false,
66 }
67 }
68
69 pub fn print(&self, writer: &mut Writer) {
70 match self {
71 Postfix::Empty => {
73 writer.write_char('_');
74 }
75 Postfix::Function(identifier, expression) => {
76 writer.write_str(&identifier);
77 writer.write_char('(');
78 expression.print(writer);
79 writer.write_char(')');
80 }
81 Postfix::Index(postfix, expression) => {
82 postfix.print(writer);
83 writer.write_char('[');
84 expression.print(writer);
85 writer.write_char(']');
86 }
87 Postfix::Method(postfix, identifier, expression) => {
88 postfix.print(writer);
89 writer.write_char('.');
90 writer.write_str(&identifier);
91 writer.write_char('(');
92 expression.print(writer);
93 writer.write_char(')');
94 }
95 Postfix::Inference(reference, expression) => {
96 reference.print(writer);
97 writer.write_char('(');
98 expression.print(writer);
99 writer.write_char(')');
100 }
101 Postfix::Critical(postfix) => {
102 postfix.print(writer);
103 writer.write_str("!!");
104 }
105 Postfix::Primary(primary) => primary.print(writer),
106 }
107 }
108}
109
110enum PostfixOp {
112 Index(Expression),
114 Method(Arc<str>, Expression),
116}
117
118pub fn parse_accessor(input: &str) -> IResult<&str, &str> {
120 delimited(multispace0, tag("."), multispace0).parse(input)
121}
122
123fn index_postfix(input: &str) -> IResult<&str, PostfixOp> {
125 let (input, expression) = delimited(char('['), parse_expression, char(']')).parse(input)?;
126 Ok((input, PostfixOp::Index(expression)))
127}
128
129fn parse_argument_postfix(input: &str) -> IResult<&str, Expression> {
131 delimited(
132 char('('),
133 alt((
134 map(parse_argument_list, |expr| expr),
135 map(multispace0, |_| Expression::Empty),
136 )),
137 char(')'),
138 )
139 .parse(input)
140}
141
142fn parse_function(input: &str) -> IResult<&str, Postfix> {
144 let (input, name) = parse_arc_identifier(input)?;
145 let (input, _) = multispace0(input)?;
146 if &*name == "_" {
147 return Ok((input, Postfix::Empty));
148 }
149 let (input, args) = parse_argument_postfix(input)?;
150 let function = Postfix::Function(name, Box::new(args));
151 Ok((input, function))
152}
153
154fn parse_method(input: &str) -> IResult<&str, PostfixOp> {
167 map(
168 (
169 parse_accessor,
170 parse_arc_identifier,
171 multispace0,
172 parse_argument_postfix,
173 ),
174 |(_, name, _, args)| PostfixOp::Method(name, args),
175 ).parse(input)
176}
177
178fn parse_critical(input: &str, postfix: Postfix) -> IResult<&str, Postfix> {
179 let (input, _) = multispace0(input)?;
180 let (input, opt_critical) = opt(tag("!!")).parse(input)?;
181 let result = if opt_critical.is_some() {
182 Postfix::Critical(Box::new(postfix))
183 } else {
184 postfix
185 };
186 Ok((input, result))
187}
188
189pub fn parse_postfix(input: &str) -> IResult<&str, Postfix> {
191 let (input, first) = if let Ok((input, function)) = parse_function(input) {
192 (input, function)
193 } else {
194 let (input, primary) = parse_primary(input)?;
195 if input.starts_with('(') {
196 let (input, args) = parse_argument_postfix(input)?;
197 match primary {
198 Primary::Reference(reference) => {
199 let postfix = Postfix::Inference(reference, Box::new(args));
200 return parse_critical(input, postfix);
201 }
202 _ => {
203 return Err(NomErr::Failure(Error::new(
204 input,
205 nom::error::ErrorKind::Fail,
206 )));
207 }
208 }
209 }
210 (input, Postfix::Primary(Box::new(primary)))
211 };
212
213 let (input, rest) = many0(delimited(
214 multispace0,
215 alt((index_postfix, parse_method)),
216 multispace0,
217 ))
218 .parse(input)?;
219
220 let result = rest.into_iter().fold(first, |acc, op| match op {
222 PostfixOp::Index(expression) => Postfix::Index(Box::new(acc), Box::new(expression)),
223 PostfixOp::Method(name, args) => Postfix::Method(Box::new(acc), name, Box::new(args)),
224 });
225 return parse_critical(input, result);
226}
227
228impl ExpressionLike for Postfix {
229 fn evaluate(&self, context: &mut dyn ContextLike) -> Result<Arc<Value>> {
235 match self {
236 Postfix::Empty => Ok(Value::empty()),
237 Postfix::Function(name, expression) => {
238 let arg = expression.evaluate(context)?;
240 if arg.is_error() {
241 return Ok(arg);
242 }
243 context.function_call(name.clone(), arg)
244 }
245 Postfix::Index(primary, expression) => {
246 let primary_val = primary.evaluate(context)?;
247 if primary_val.is_error() {
248 return Ok(primary_val);
249 }
250 let index_val = expression.evaluate(context)?;
251 if index_val.is_error() {
252 return Ok(index_val);
253 }
254 let found = index_val.type_as_string();
255 match (&*primary_val, &*index_val) {
257 (Value::Array(array), Value::Literal(Literal::Number(index))) => {
259 let index = *index as usize;
260 if index < array.len() {
261 Ok(array[index].clone())
262 } else {
263 Err(anyhow!(
264 "Index out of bounds: {} >= {}~{}",
265 index,
266 array.len(),
267 self.to_formula()
268 ))
269 }
270 }
271 (Value::Array(_), _) => Err(anyhow!(
272 "Array index must be a number ~{}",
273 self.to_formula()
274 )),
275
276 (Value::Collection(pool), Value::Literal(Literal::Text(text))) => {
278 if let Some(expr) = pool.get(text.as_ref()) {
279 expr.evaluate(context)
280 } else {
281 Ok(Value::empty())
282 }
283 }
284
285 (Value::Collection(pool), _) => {
287 let as_text = index_val.as_text()?;
288 match &*as_text {
289 Value::Literal(Literal::Text(text)) => {
290 if let Some(expr) = pool.get(text) {
291 expr.evaluate(context)
292 } else {
293 Ok(Value::empty())
294 }
295 }
296 _ => Ok(Value::empty()),
297 }
298 }
299
300 _ => Err(anyhow!(
301 "Expected Array or Collection for index, found {}~{}",
302 found,
303 self.to_formula()
304 )),
305 }
306 }
307 Postfix::Method(primary, name, expression) => {
308 let value = primary.evaluate(context)?;
310 if value.is_error() {
311 return Ok(value);
312 }
313 let arg = expression.evaluate(context)?;
314 if arg.is_error() {
315 return Ok(arg);
316 }
317 context.method_call(name.clone(), value, arg)
318 }
319 Postfix::Inference(reference, expression) => {
320 let arg = expression.evaluate(context)?;
322 context.inference_call(reference.clone(), arg)
323 }
324 Postfix::Critical(postfix) => match postfix.evaluate(context) {
325 Ok(value) => {
326 if let Value::Errata(errata) = &*value {
327 Err(CriticalError::new(errata.reason()).into())
328 } else {
329 Ok(value)
330 }
331 }
332 Err(e) => Err(CriticalError::new(Arc::from(e.to_string())).into()),
333 },
334 Postfix::Primary(primary) => primary.evaluate(context),
335 }
336 }
337
338 fn to_formula(&self) -> String {
340 let mut writer = Writer::formulizer();
341 self.print(&mut writer);
342 writer.finish()
343 }
344}
345
346impl WriterLike for Postfix {
347 fn write(&self, writer: &mut Writer) {
348 self.print(writer);
349 }
350}
351
352impl fmt::Display for Postfix {
353 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354 write!(f, "{}", self.to_stringized())
355 }
356}