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 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}