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#[derive(Debug, Clone, PartialEq)]
54pub enum Expression {
55 List(List),
57 Dict(Dict),
59 Conditional(Box<Expression>, Box<Expression>, Box<Expression>),
62 Literal(Literal),
64 TemplateString(TemplateString),
66 BinaryOperation(Box<BinaryOperation>),
68 PrefixOperation(Box<PrefixOperation>),
70 PostfixOperation(Box<PostfixOperation>),
72 Import(Import),
74 ListComprehension(Box<ListComprehension>),
76 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#[derive(Debug, Clone, Default, PartialEq)]
253pub struct Dict {
254 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#[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#[derive(Debug, Clone, PartialEq)]
390pub struct KeyValue {
391 pub key: Rc<str>,
393 pub value: Expression,
395 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}