ryan/parser/
expression.rs

1use indexmap::IndexMap;
2use pest::{
3    iterators::Pairs,
4    pratt_parser::{Op, PrattParser},
5};
6use std::{cell::RefCell, fmt::Display, rc::Rc};
7
8use crate::{rc_world, utils::QuotedStr};
9
10use super::State;
11use super::{comprehension::DictComprehension, ErrorLogger};
12use super::{comprehension::ListComprehension, operation::BinaryOperator};
13use super::{import::Import, operation::BinaryOperation};
14use super::{
15    literal::Literal,
16    operation::{PrefixOperation, PrefixOperator},
17};
18use super::{
19    operation::{PostfixOperation, PostfixOperator},
20    value::Value,
21};
22use super::{template_string::TemplateString, Rule};
23
24lazy_static::lazy_static! {
25    static ref PRATT_PARSER: PrattParser<Rule> = {
26        use pest::pratt_parser::Assoc::*;
27
28        PrattParser::new()
29            .op(Op::infix(Rule::orOp, Left))
30            .op(Op::infix(Rule::andOp, Left))
31            .op(Op::prefix(Rule::notOp))
32            .op(
33                Op::infix(Rule::equalsOp, Left)
34                | Op::infix(Rule::notEqualsOp, Left)
35                | Op::infix(Rule::typeMatchesOp, Left)
36                | Op::infix(Rule::greaterOp, Left)
37                | Op::infix(Rule::greaterEqualOp, Left)
38                | Op::infix(Rule::lesserOp, Left)
39                | Op::infix(Rule::lesserEqualOp, Left)
40                | Op::infix(Rule::isContainedOp, Left)
41            )
42            .op(Op::infix(Rule::plusOp, Left) | Op::infix(Rule::minusOp, Left))
43            .op(Op::infix(Rule::remainderOp, Left))
44            .op(Op::infix(Rule::timesOp, Left) | Op::infix(Rule::dividedOp, Left))
45            .op(Op::infix(Rule::defaultOp, Left))
46            .op(Op::infix(Rule::juxtapositionOp, Right))
47            .op(Op::postfix(Rule::accessOp))
48            .op(Op::postfix(Rule::castInt) | Op::postfix(Rule::castFloat) | Op::postfix(Rule::castText))
49    };
50}
51
52/// Transformations of Ryan values.
53#[derive(Debug, Clone, PartialEq)]
54pub enum Expression {
55    /// Builds a list of Ryan values.
56    List(List),
57    /// Builds a dictionary of Ryan values.
58    Dict(Dict),
59    /// Based on an expressing returning a `bool`, executes either of the supplied
60    /// expressions.
61    Conditional(Box<Expression>, Box<Expression>, Box<Expression>),
62    /// Builds a Ryan value from a literal.
63    Literal(Literal),
64    /// Builds a Ryan template string.
65    TemplateString(TemplateString),
66    /// Builds a Ryan value of a binary operation over two Ryan values.
67    BinaryOperation(Box<BinaryOperation>),
68    /// Builds a Ryan value of a prefix operator applied on a Ryan value.
69    PrefixOperation(Box<PrefixOperation>),
70    /// Builds a Ryan value of a postfix operator applied on a Ryan value.
71    PostfixOperation(Box<PostfixOperation>),
72    /// Produces a Ryan value from an `import` statement.
73    Import(Import),
74    /// Creates a Ryan value from a list comprehension.
75    ListComprehension(Box<ListComprehension>),
76    /// Creates a Ryan value from a dict comprehension.
77    DictComprehension(Box<DictComprehension>),
78}
79
80impl Default for Expression {
81    fn default() -> Self {
82        Expression::Literal(Literal::default())
83    }
84}
85
86impl Display for Expression {
87    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88        match self {
89            Self::List(list) => {
90                write!(f, "[")?;
91                crate::utils::fmt_list(f, &list.items)?;
92                write!(f, "]")?;
93            }
94            Self::Dict(dict) => {
95                write!(f, "{{")?;
96                crate::utils::fmt_list(f, &dict.items)?;
97                write!(f, "}}")?;
98            }
99            Self::Literal(lit) => write!(f, "{lit}")?,
100            Self::TemplateString(template) => write!(f, "{template}")?,
101            Self::BinaryOperation(op) => write!(f, "{op}")?,
102            Self::PrefixOperation(op) => write!(f, "{op}")?,
103            Self::PostfixOperation(op) => write!(f, "{op}")?,
104            Self::Conditional(r#if, then, r#else) => {
105                write!(f, "if {} then {} else {}", r#if, r#then, r#else)?
106            }
107            Self::Import(import) => write!(f, "{import}")?,
108            Self::ListComprehension(comprehension) => write!(f, "{comprehension}")?,
109            Self::DictComprehension(comprehension) => write!(f, "{comprehension}")?,
110        }
111
112        Ok(())
113    }
114}
115
116impl Expression {
117    pub(super) fn parse(logger: &mut ErrorLogger, pairs: Pairs<'_, Rule>) -> Self {
118        let logger_cell = Rc::new(RefCell::new(logger));
119        let logger_cell_postfix = logger_cell.clone();
120
121        PRATT_PARSER
122            .map_primary(|pair| match pair.as_rule() {
123                Rule::list => {
124                    Expression::List(List::parse(*logger_cell.borrow_mut(), pair.into_inner()))
125                }
126                Rule::dict => {
127                    Expression::Dict(Dict::parse(*logger_cell.borrow_mut(), pair.into_inner()))
128                }
129                Rule::conditional => {
130                    let mut pairs = pair.into_inner();
131                    let mut next = || {
132                        Expression::parse(
133                            *logger_cell.borrow_mut(),
134                            pairs
135                                .next()
136                                .expect("clause in conditional was expected")
137                                .into_inner(),
138                        )
139                    };
140                    Expression::Conditional(Box::new(next()), Box::new(next()), Box::new(next()))
141                }
142                Rule::literal => Expression::Literal(Literal::parse(
143                    *logger_cell.borrow_mut(),
144                    pair.into_inner(),
145                )),
146                Rule::import => {
147                    Expression::Import(Import::parse(*logger_cell.borrow_mut(), pair.into_inner()))
148                }
149                Rule::expression => Self::parse(*logger_cell.borrow_mut(), pair.into_inner()),
150                Rule::templateString => Expression::TemplateString(TemplateString::parse(
151                    *logger_cell.borrow_mut(),
152                    pair.into_inner(),
153                )),
154                Rule::listComprehension => Expression::ListComprehension(Box::new(
155                    ListComprehension::parse(*logger_cell.borrow_mut(), pair.into_inner()),
156                )),
157                Rule::dictComprehension => Expression::DictComprehension(Box::new(
158                    DictComprehension::parse(*logger_cell.borrow_mut(), pair.into_inner()),
159                )),
160                _ => unreachable!(),
161            })
162            .map_infix(|left, op, right| {
163                Expression::BinaryOperation(Box::new(BinaryOperation {
164                    left,
165                    op: BinaryOperator::parse(op),
166                    right,
167                }))
168            })
169            .map_prefix(|op, right| {
170                Expression::PrefixOperation(Box::new(PrefixOperation {
171                    op: PrefixOperator::parse(op),
172                    right,
173                }))
174            })
175            .map_postfix(move |left, op| {
176                Expression::PostfixOperation(Box::new(PostfixOperation {
177                    op: PostfixOperator::parse(*logger_cell_postfix.borrow_mut(), op),
178                    left,
179                }))
180            })
181            .parse(pairs)
182    }
183
184    #[must_use]
185    pub(super) fn capture(
186        &self,
187        state: &mut State<'_>,
188        provided: &mut [Rc<str>],
189        values: &mut IndexMap<Rc<str>, Value>,
190    ) -> Option<()> {
191        match self {
192            Self::List(list) => list.capture(state, provided, values)?,
193            Self::Dict(dict) => dict.capture(state, provided, values)?,
194            Self::Conditional(r#if, then, r#else) => {
195                r#if.capture(state, provided, values)?;
196                then.capture(state, provided, values)?;
197                r#else.capture(state, provided, values)?;
198            }
199            Self::Literal(lit) => {
200                lit.capture(state, provided, values)?;
201            }
202            Self::TemplateString(template) => {
203                template.capture(state, provided, values)?;
204            }
205            Self::BinaryOperation(op) => {
206                op.left.capture(state, provided, values)?;
207                op.right.capture(state, provided, values)?;
208            }
209            Self::PrefixOperation(op) => op.right.capture(state, provided, values)?,
210            Self::PostfixOperation(op) => op.left.capture(state, provided, values)?,
211            Self::Import(_) => {}
212            Self::ListComprehension(comprehension) => {
213                comprehension.capture(state, provided, values)?
214            }
215            Self::DictComprehension(comprehension) => {
216                comprehension.capture(state, provided, values)?
217            }
218        };
219
220        Some(())
221    }
222
223    pub(super) fn eval(&self, state: &mut State<'_>) -> Option<Value> {
224        let returned = match self {
225            Self::List(list) => list.eval(state)?,
226            Self::Dict(dict) => dict.eval(state)?,
227            Self::Conditional(r#if, then, r#else) => {
228                let if_evalued = r#if.eval(state)?;
229                let to_eval = if state.absorb(if_evalued.is_true())? {
230                    then
231                } else {
232                    r#else
233                };
234
235                to_eval.eval(state)?
236            }
237            Self::Literal(lit) => lit.eval(state)?,
238            Self::TemplateString(template) => template.eval(state)?,
239            Self::BinaryOperation(op) => op.eval(state)?,
240            Self::PrefixOperation(op) => op.eval(state)?,
241            Self::PostfixOperation(op) => op.eval(state)?,
242            Self::Import(import) => import.eval(state)?,
243            Self::ListComprehension(comprehension) => comprehension.eval(state)?,
244            Self::DictComprehension(comprehension) => comprehension.eval(state)?,
245        };
246
247        Some(returned)
248    }
249}
250
251/// An association of string values to Ryan values.
252#[derive(Debug, Clone, Default, PartialEq)]
253pub struct Dict {
254    /// The entries of this association.
255    pub items: Vec<DictItem>,
256}
257
258impl Dict {
259    fn parse(logger: &mut ErrorLogger, pairs: Pairs<'_, Rule>) -> Self {
260        let mut items = vec![];
261
262        for pair in pairs {
263            match pair.as_rule() {
264                Rule::dictItem => items.push(DictItem::parse(logger, pair.into_inner())),
265                _ => unreachable!(),
266            }
267        }
268
269        Dict { items }
270    }
271
272    #[must_use]
273    pub(super) fn capture(
274        &self,
275        state: &mut State<'_>,
276        provided: &mut [Rc<str>],
277        values: &mut IndexMap<Rc<str>, Value>,
278    ) -> Option<()> {
279        for item in &self.items {
280            item.capture(state, provided, values)?;
281        }
282
283        Some(())
284    }
285
286    pub(super) fn eval(&self, state: &mut State<'_>) -> Option<Value> {
287        let mut evald = IndexMap::new();
288
289        for item in &self.items {
290            match item {
291                DictItem::KeyValue(kv) => {
292                    if let Some(g) = &kv.guard {
293                        let tested = g.eval(state)?.is_true();
294                        if !state.absorb(tested)? {
295                            continue;
296                        }
297                    }
298
299                    evald.insert(rc_world::str_to_rc(&kv.key), kv.value.eval(state)?);
300                }
301                DictItem::FlattenExpression(expr) => {
302                    let returned = expr.eval(state)?;
303                    match returned {
304                        Value::Map(map) => {
305                            for (key, value) in &*map {
306                                evald.insert(key.clone(), value.clone());
307                            }
308                        }
309                        Value::List(list) => {
310                            for item in &*list {
311                                match item {
312                                    Value::List(pair) if pair.len() == 2 => {
313                                        if let Value::Text(key) = &pair[0] {
314                                            evald.insert(key.clone(), pair[1].clone());
315                                        } else {
316                                            state.raise(format!(
317                                                "First element of key-pair list must be text, got {}",
318                                                pair[0].canonical_type()
319                                            ))?;
320                                        }
321                                    }
322                                    _ => state.raise(format!(
323                                        "Key-pair list must be [text, any], got {}",
324                                        item.canonical_type(),
325                                    ))?,
326                                }
327                            }
328                        }
329                        val => state.raise(format!(
330                            "Flatten expression must be either a map or list of key-value pairs, got {}",
331                            val.canonical_type(),
332                        ))?,
333                    }
334                }
335            }
336        }
337
338        Some(Value::Map(Rc::new(evald)))
339    }
340}
341
342///
343#[derive(Debug, Clone, PartialEq)]
344pub enum DictItem {
345    KeyValue(KeyValue),
346    FlattenExpression(Expression),
347}
348
349impl Display for DictItem {
350    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
351        match self {
352            DictItem::KeyValue(kv) => write!(f, "{kv}")?,
353            DictItem::FlattenExpression(expr) => write!(f, "...{expr}")?,
354        }
355
356        Ok(())
357    }
358}
359
360impl DictItem {
361    fn parse(logger: &mut ErrorLogger, mut pairs: Pairs<'_, Rule>) -> Self {
362        let inner = pairs.next().expect("a dict item always has a token");
363        match inner.as_rule() {
364            Rule::keyValue => DictItem::KeyValue(KeyValue::parse(logger, inner.into_inner())),
365            Rule::flatExpression => {
366                DictItem::FlattenExpression(Expression::parse(logger, inner.into_inner()))
367            }
368            _ => unreachable!(),
369        }
370    }
371
372    #[must_use]
373    pub(super) fn capture(
374        &self,
375        state: &mut State<'_>,
376        provided: &mut [Rc<str>],
377        values: &mut IndexMap<Rc<str>, Value>,
378    ) -> Option<()> {
379        match self {
380            DictItem::KeyValue(kv) => kv.capture(state, provided, values)?,
381            DictItem::FlattenExpression(expr) => expr.capture(state, provided, values)?,
382        }
383
384        Some(())
385    }
386}
387
388/// An entry of a dictionary expression.
389#[derive(Debug, Clone, PartialEq)]
390pub struct KeyValue {
391    /// The string value associated with the Ryan value.
392    pub key: Rc<str>,
393    /// The expression that evaluates to the value of this association.
394    pub value: Expression,
395    /// An optional `if` guard. If the supplied expression evaluates to `false`, the
396    /// current key-value pair is not inserted in the final dictionary.
397    pub guard: Option<Expression>,
398}
399
400impl Display for KeyValue {
401    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
402        if let Some(g) = &self.guard {
403            write!(f, "{}: {} if {}", QuotedStr(&self.key), self.value, g)
404        } else {
405            write!(f, "{}: {}", QuotedStr(&self.key), self.value)
406        }
407    }
408}
409
410impl KeyValue {
411    fn parse(logger: &mut ErrorLogger, pairs: Pairs<'_, Rule>) -> Self {
412        let mut key = None;
413        let mut value = None;
414        let mut guard = None;
415
416        for pair in pairs {
417            match pair.as_rule() {
418                Rule::identifier => key = Some(rc_world::str_to_rc(pair.as_str())),
419                Rule::text => {
420                    key = Some(rc_world::string_to_rc(
421                        logger.absorb(&pair, crate::utils::unescape(pair.as_str())),
422                    ));
423                }
424                Rule::expression => value = Some(Expression::parse(logger, pair.into_inner())),
425                Rule::ifGuard => {
426                    guard = Some(Expression::parse(
427                        logger,
428                        pair.into_inner()
429                            .next()
430                            .expect("there is always an expression in an if guard")
431                            .into_inner(),
432                    ))
433                }
434                _ => unreachable!(),
435            }
436        }
437
438        let key = key.expect("there is always a key in dict item");
439
440        KeyValue {
441            value: value.unwrap_or_else(|| Expression::Literal(Literal::Identifier(key.clone()))),
442            key,
443            guard,
444        }
445    }
446
447    #[must_use]
448    pub(super) fn capture(
449        &self,
450        state: &mut State<'_>,
451        provided: &mut [Rc<str>],
452        values: &mut IndexMap<Rc<str>, Value>,
453    ) -> Option<()> {
454        self.value.capture(state, provided, values)?;
455        if let Some(g) = &self.guard {
456            g.capture(state, provided, values)?;
457        }
458
459        Some(())
460    }
461}
462
463#[derive(Debug, Clone, PartialEq)]
464pub struct List {
465    items: Vec<ListItem>,
466}
467
468impl List {
469    fn parse(logger: &mut ErrorLogger, pairs: Pairs<'_, Rule>) -> Self {
470        let mut items = vec![];
471
472        for pair in pairs {
473            match pair.as_rule() {
474                Rule::listItem => items.push(ListItem::parse(logger, pair.into_inner())),
475                _ => unreachable!(),
476            }
477        }
478
479        List { items }
480    }
481
482    #[must_use]
483    pub(super) fn capture(
484        &self,
485        state: &mut State<'_>,
486        provided: &mut [Rc<str>],
487        values: &mut IndexMap<Rc<str>, Value>,
488    ) -> Option<()> {
489        for item in &self.items {
490            item.capture(state, provided, values)?;
491        }
492
493        Some(())
494    }
495
496    pub(super) fn eval(&self, state: &mut State<'_>) -> Option<Value> {
497        let mut evald = vec![];
498
499        for item in &self.items {
500            match item {
501                ListItem::Item(item) => {
502                    evald.push(item.eval(state)?);
503                }
504                ListItem::FlattenExpression(expr) => {
505                    let returned = expr.eval(state)?;
506                    match returned {
507                        Value::List(list) => evald.extend(list.iter().cloned()),
508                        Value::Map(map) => {
509                            for (key, value) in &*map {
510                                evald.push(Value::List(
511                                    vec![Value::Text(key.clone()), value.clone()].into(),
512                                ));
513                            }
514                        }
515                        val => state.raise(format!(
516                            "Flatten expression must be either a map or a list, got {}",
517                            val.canonical_type(),
518                        ))?,
519                    }
520                }
521            }
522        }
523
524        Some(Value::List(evald.into()))
525    }
526}
527
528#[derive(Debug, Clone, PartialEq)]
529pub enum ListItem {
530    Item(Expression),
531    FlattenExpression(Expression),
532}
533
534impl Display for ListItem {
535    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
536        match self {
537            ListItem::Item(item) => write!(f, "{item}")?,
538            ListItem::FlattenExpression(expr) => write!(f, "...{expr}")?,
539        }
540
541        Ok(())
542    }
543}
544
545impl ListItem {
546    fn parse(logger: &mut ErrorLogger, mut pairs: Pairs<'_, Rule>) -> Self {
547        let inner = pairs.next().expect("a dict item always has a token");
548        match inner.as_rule() {
549            Rule::expression => ListItem::Item(Expression::parse(logger, inner.into_inner())),
550            Rule::flatExpression => {
551                ListItem::FlattenExpression(Expression::parse(logger, inner.into_inner()))
552            }
553            _ => unreachable!(),
554        }
555    }
556
557    #[must_use]
558    pub(super) fn capture(
559        &self,
560        state: &mut State<'_>,
561        provided: &mut [Rc<str>],
562        values: &mut IndexMap<Rc<str>, Value>,
563    ) -> Option<()> {
564        match self {
565            ListItem::Item(item) => item.capture(state, provided, values),
566            ListItem::FlattenExpression(expr) => expr.capture(state, provided, values),
567        }
568    }
569}