Skip to main content

matugen_parser/engine/
parser.rs

1use chumsky::{error::Rich, prelude::*, span::SimpleSpan};
2
3use crate::{
4    Engine, SpannedValue, Value,
5    engine::{BinaryOperator, EngineSyntax, Expression, SpannedBinaryOperator, SpannedExpr},
6};
7
8impl Engine {
9    pub fn parser<'src>(
10        syntax: &'src EngineSyntax,
11    ) -> impl Parser<'src, &'src str, Vec<Box<SpannedExpr>>, extra::Err<Rich<'src, char>>> {
12        recursive(|expr| {
13            // Dotted identifier as a sequence of spans
14            let numeric_key_prefixed = just('_').ignore_then(text::int(10));
15
16            let dotted_ident = text::ident()
17                .or(numeric_key_prefixed)
18                .map_with(|_, e| e.span())
19                .separated_by(just('.').padded())
20                .at_least(1)
21                .collect::<Vec<SimpleSpan>>()
22                .map_with(|spans, e| {
23                    Box::new(SpannedExpr {
24                        expr: Expression::Access { keywords: spans },
25                        span: e.span(),
26                    })
27                });
28
29            let plain_ident = text::ident().map(|s: &str| Value::Ident(s.to_string()));
30
31            let escape = just('\\').ignore_then(just('"').or(just('\\')));
32
33            let inner = escape
34                .or(none_of("\"\\"))
35                .repeated()
36                .collect::<String>()
37                .map(Value::Ident);
38
39            let quoted_ident = inner.delimited_by(just('"'), just('"'));
40
41            let ident = quoted_ident.or(plain_ident);
42
43            let sign = just('-').or(just('+')).or_not();
44
45            let int = sign
46                .then(text::int(10))
47                .map(|(sign, digits): (Option<char>, &str)| {
48                    let number = format!("{}{}", sign.unwrap_or('+'), digits);
49                    Value::Int(number.parse::<i64>().unwrap())
50                });
51
52            let float = sign
53                .then(text::int(10))
54                .then_ignore(just('.'))
55                .then(text::digits(10).to_slice())
56                .map(
57                    |((sign, int_part), frac_part): ((Option<char>, &str), &str)| {
58                        let number = format!("{}{}.{}", sign.unwrap_or('+'), int_part, frac_part);
59                        Value::Float(number.parse::<f64>().unwrap())
60                    },
61                );
62
63            let range = int
64                .then_ignore(just(".."))
65                .then(int)
66                .map_with(|(start, end), e| {
67                    Box::new(SpannedExpr {
68                        expr: Expression::Range {
69                            start: start.get_int().expect("Failed to get int from range"),
70                            end: end.get_int().expect("Failed to get int from range"),
71                        },
72                        span: e.span(),
73                    })
74                });
75
76            let boolean = just("true")
77                .to(Value::Bool(true))
78                .or(just("false").to(Value::Bool(false)));
79
80            let spanned_ident = ident.map_with(|value, e| SpannedValue::new(value, e.span()));
81
82            let literal = float.or(int).or(ident).or(boolean).map_with(|value, e| {
83                Box::new(SpannedExpr {
84                    expr: Expression::LiteralValue {
85                        value: SpannedValue {
86                            value,
87                            span: e.span(),
88                        },
89                    },
90                    span: e.span(),
91                })
92            });
93
94            let op = just('+')
95                .to(BinaryOperator::Add)
96                .or(just('-').to(BinaryOperator::Sub))
97                .or(just('*').to(BinaryOperator::Mul))
98                .or(just('/').to(BinaryOperator::Div))
99                .map_with(|op, e| SpannedBinaryOperator { op, span: e.span() });
100
101            let arg = literal
102                .clone()
103                .or(expr.clone())
104                .padded()
105                .clone()
106                .then(op.padded())
107                .then(literal.clone().or(expr.clone()).padded())
108                .map_with(|((a, b), c), e| {
109                    Box::new(SpannedExpr {
110                        expr: Expression::BinaryOp {
111                            lhs: a,
112                            op: b,
113                            rhs: c,
114                        },
115                        span: e.span(),
116                    })
117                });
118
119            let filter = text::ident()
120                .map_with(|_, e| e.span())
121                .then(
122                    just(':')
123                        .padded()
124                        .ignore_then(
125                            literal
126                                .clone()
127                                .or(arg.clone())
128                                .or(expr.clone())
129                                .padded()
130                                .separated_by(just(',').padded())
131                                .collect::<Vec<Box<SpannedExpr>>>(),
132                        )
133                        .or_not(),
134                )
135                .map_with(
136                    |(name, args), e: &mut chumsky::input::MapExtra<'_, '_, _, _>| SpannedExpr {
137                        expr: Expression::Filter {
138                            name,
139                            args: args.unwrap_or_default(),
140                        },
141                        span: e.span(),
142                    },
143                );
144
145            let filters = just('|')
146                .padded()
147                .ignore_then(filter.padded())
148                .repeated()
149                .collect::<Vec<_>>();
150
151            let full_expr = dotted_ident
152                .or(literal.clone())
153                .or(arg)
154                .or(expr.clone())
155                .padded()
156                .then(filters)
157                .map(|(access, filters)| {
158                    let keyword = SpannedExpr {
159                        span: access.span.clone(),
160                        expr: Expression::Keyword { keywords: access },
161                    };
162                    if filters.is_empty() {
163                        keyword
164                    } else {
165                        let span = SimpleSpan::new(
166                            (),
167                            keyword.span.start
168                                ..filters
169                                    .last()
170                                    .map(|f| f.span.end)
171                                    .unwrap_or(keyword.span.end),
172                        );
173                        SpannedExpr {
174                            expr: Expression::KeywordWithFilters {
175                                keyword: Box::new(keyword),
176                                filters,
177                            },
178                            span,
179                        }
180                    }
181                });
182
183            let keyword_full = full_expr
184                .padded()
185                .delimited_by(
186                    just(syntax.keyword_left.as_str()),
187                    just(syntax.keyword_right.as_str()),
188                )
189                .map_with(|expr, e| {
190                    Box::new(SpannedExpr {
191                        expr: expr.expr,
192                        span: e.span(),
193                    })
194                });
195
196            let escaped_raw = just("\\").ignore_then(
197                just(syntax.keyword_left.as_str())
198                    .or(just(syntax.block_left.as_str()))
199                    .or(just(syntax.keyword_right.as_str()))
200                    .or(just(syntax.block_right.as_str()))
201                    .or(just("\\"))
202                    .map_with(|_, e| e.span()),
203            );
204
205            let generic_raw = any()
206                .and_is(
207                    just(syntax.keyword_left.as_str())
208                        .map(|_| ())
209                        .or(just(syntax.block_left.as_str()).map(|_| ()))
210                        .or(escaped_raw.map(|_| ()))
211                        .not(),
212                )
213                .repeated()
214                .at_least(1)
215                .map_with(|_, e| e.span());
216
217            let raw = choice((escaped_raw, generic_raw)).map(|span| {
218                Box::new(SpannedExpr {
219                    expr: Expression::Raw { value: span },
220                    span,
221                })
222            });
223
224            let include = just("include")
225                .padded()
226                .ignore_then(spanned_ident.padded())
227                .delimited_by(
228                    just(syntax.block_left.as_str()),
229                    just(syntax.block_right.as_str()),
230                )
231                .map_with(|name, e| {
232                    Box::new(SpannedExpr {
233                        expr: Expression::Include { name },
234                        span: e.span(),
235                    })
236                });
237
238            let negation = just("not")
239                .padded()
240                .to(true)
241                .or_not()
242                .map(|n| n.unwrap_or(false));
243
244            let if_statement = just("if")
245                .padded()
246                .ignore_then(negation)
247                .then(keyword_full.clone().padded())
248                .then_ignore(just(syntax.block_right.as_str()).padded())
249                .then(expr.clone().repeated().collect())
250                .then(
251                    just(syntax.block_left.as_str())
252                        .padded()
253                        .ignore_then(just("else").padded())
254                        .ignore_then(just(syntax.block_right.as_str()).padded())
255                        .ignore_then(expr.clone().repeated().collect())
256                        .or_not(),
257                )
258                .delimited_by(
259                    just(syntax.block_left.as_str()),
260                    just("endif").padded().delimited_by(
261                        just(syntax.block_left.as_str()),
262                        just(syntax.block_right.as_str()),
263                    ),
264                )
265                .map_with(|(((negated, condition), then_branch), else_branch), e| {
266                    Box::new(SpannedExpr {
267                        expr: Expression::If {
268                            condition: condition,
269                            then_branch: then_branch,
270                            else_branch: else_branch,
271                            negated: negated,
272                        },
273                        span: e.span(),
274                    })
275                });
276
277            let for_loop = just("for")
278                .padded()
279                .ignore_then(
280                    spanned_ident
281                        .separated_by(just(',').padded())
282                        .at_least(1)
283                        .collect::<Vec<SpannedValue>>(),
284                )
285                .padded()
286                .then_ignore(just("in").padded())
287                .then(dotted_ident.or(range).padded())
288                .then_ignore(just(syntax.block_right.as_str()))
289                .then(expr.repeated().collect())
290                .delimited_by(
291                    just(syntax.block_left.as_str()),
292                    just("endfor").padded().delimited_by(
293                        just(syntax.block_left.as_str()),
294                        just(syntax.block_right.as_str()),
295                    ),
296                )
297                .map_with(|((var, iter), body), e| {
298                    Box::new(SpannedExpr {
299                        expr: Expression::ForLoop { var, iter, body },
300                        span: e.span(),
301                    })
302                });
303
304            choice((raw, keyword_full, for_loop, if_statement, include))
305        })
306        .repeated()
307        .collect::<Vec<Box<SpannedExpr>>>()
308    }
309}