1use core::mem;
4
5use nom::{
6 branch::alt,
7 bytes::complete::{tag, take_while1},
8 character::complete::{char as tag_char, one_of},
9 combinator::{cut, map, map_res, opt},
10 error::context,
11 multi::{many0, separated_list0},
12 sequence::{delimited, preceded, terminated},
13 Err as NomErr, Input, Parser,
14};
15
16use super::{
17 block, fn_def,
18 helpers::{comma_sep, is_valid_variable_name, mandatory_ws, var_name, ws, GrammarType},
19};
20use crate::{
21 alloc::{vec, Box, Vec},
22 grammars::{Features, Grammar, Parse, ParseLiteral},
23 spans::{unite_spans, with_span},
24 BinaryOp, Context, Error, ErrorKind, Expr, InputSpan, NomResult, ObjectExpr, Spanned,
25 SpannedExpr, UnaryOp,
26};
27
28fn fn_args<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, (Vec<SpannedExpr<'_, T::Base>>, bool)>
34where
35 T: Parse,
36 Ty: GrammarType,
37{
38 let maybe_comma = map(opt(preceded(ws::<Ty>, tag_char(','))), |c| c.is_some());
39
40 preceded(
41 terminated(tag_char('('), ws::<Ty>),
42 cut((
44 separated_list0(delimited(ws::<Ty>, tag_char(','), ws::<Ty>), expr::<T, Ty>),
45 terminated(maybe_comma, (ws::<Ty>, tag_char(')'))),
46 )),
47 )
48 .parse(input)
49}
50
51pub(super) fn paren_expr<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, SpannedExpr<'_, T::Base>>
54where
55 T: Parse,
56 Ty: GrammarType,
57{
58 with_span(fn_args::<T, Ty>)
59 .parse(input)
60 .and_then(|(rest, parsed)| {
61 let comma_terminated = parsed.extra.1;
62 let terms = parsed.map_extra(|terms| terms.0);
63
64 match (terms.extra.len(), comma_terminated) {
65 (1, false) => Ok((
66 rest,
67 terms.map_extra(|mut terms| terms.pop().unwrap().extra),
68 )),
69 _ => {
70 if T::FEATURES.contains(Features::TUPLES) {
71 Ok((rest, terms.map_extra(Expr::Tuple)))
72 } else {
73 Err(NomErr::Failure(
74 ErrorKind::UnexpectedTerm { context: None }.with_span(&terms),
75 ))
76 }
77 }
78 }
79 })
80}
81
82fn block_expr<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, SpannedExpr<'_, T::Base>>
84where
85 T: Parse,
86 Ty: GrammarType,
87{
88 map(with_span(block::<T, Ty>), |spanned| {
89 spanned.map_extra(Expr::Block)
90 })
91 .parse(input)
92}
93
94fn object_expr_field<T, Ty>(
95 input: InputSpan<'_>,
96) -> NomResult<'_, (Spanned<'_>, Option<SpannedExpr<'_, T::Base>>)>
97where
98 T: Parse,
99 Ty: GrammarType,
100{
101 let colon_sep = delimited(ws::<Ty>, tag_char(':'), ws::<Ty>);
102 (
103 map(var_name, Spanned::from),
104 opt(preceded(colon_sep, expr::<T, Ty>)),
105 )
106 .parse(input)
107}
108
109pub(super) fn object_expr<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, SpannedExpr<'_, T::Base>>
110where
111 T: Parse,
112 Ty: GrammarType,
113{
114 let object = preceded(
115 terminated(tag("#{"), ws::<Ty>),
116 cut(terminated(
117 terminated(
118 separated_list0(comma_sep::<Ty>, object_expr_field::<T, Ty>),
119 opt(comma_sep::<Ty>),
120 ),
121 preceded(ws::<Ty>, tag_char('}')),
122 )),
123 );
124
125 map(with_span(object), |spanned| {
126 spanned.map_extra(|fields| Expr::Object(ObjectExpr { fields }))
127 })
128 .parse(input)
129}
130
131fn fn_def_expr<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, SpannedExpr<'_, T::Base>>
133where
134 T: Parse,
135 Ty: GrammarType,
136{
137 map(with_span(fn_def::<T, Ty>), |spanned| {
138 spanned.map_extra(Expr::FnDefinition)
139 })
140 .parse(input)
141}
142
143fn simplest_expr<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, SpannedExpr<'_, T::Base>>
148where
149 T: Parse,
150 Ty: GrammarType,
151{
152 fn error<T>(input: InputSpan<'_>) -> NomResult<'_, SpannedExpr<'_, T::Base>>
153 where
154 T: Parse,
155 {
156 let e = ErrorKind::Leftovers.with_span(&input.into());
157 Err(NomErr::Error(e))
158 }
159
160 let block_parser = if T::FEATURES.contains(Features::BLOCKS) {
161 block_expr::<T, Ty>
162 } else {
163 error::<T>
164 };
165 let object_parser = if T::FEATURES.contains(Features::OBJECTS) {
166 object_expr::<T, Ty>
167 } else {
168 error::<T>
169 };
170 let fn_def_parser = if T::FEATURES.contains(Features::FN_DEFINITIONS) {
171 fn_def_expr::<T, Ty>
172 } else {
173 error::<T>
174 };
175
176 alt((
177 map(with_span(<T::Base>::parse_literal), |span| {
178 span.map_extra(Expr::Literal)
179 }),
180 map(with_span(var_name), |span| {
181 span.copy_with_extra(Expr::Variable)
182 }),
183 fn_def_parser,
184 map(
185 with_span((
186 terminated(with_span(one_of("-!")), ws::<Ty>),
187 expr_with_calls::<T, Ty>,
188 )),
189 |spanned| {
190 spanned.map_extra(|(op, inner)| Expr::Unary {
191 op: UnaryOp::from_span(op),
192 inner: Box::new(inner),
193 })
194 },
195 ),
196 block_parser,
197 object_parser,
198 paren_expr::<T, Ty>,
199 ))
200 .parse(input)
201}
202
203#[derive(Debug)]
204struct MethodOrFnCall<'a, T: Grammar> {
205 separator: Option<Spanned<'a>>,
206 fn_name: Option<SpannedExpr<'a, T>>,
207 args: Option<Vec<SpannedExpr<'a, T>>>,
208}
209
210impl<T: Grammar> MethodOrFnCall<'_, T> {
211 fn is_method(&self) -> bool {
212 self.fn_name.is_some() && self.args.is_some()
213 }
214}
215
216type MethodParseResult<'a, T> = NomResult<'a, MethodOrFnCall<'a, <T as Parse>::Base>>;
217
218fn fn_call<T, Ty>(input: InputSpan<'_>) -> MethodParseResult<'_, T>
219where
220 T: Parse,
221 Ty: GrammarType,
222{
223 map(fn_args::<T, Ty>, |(args, _)| MethodOrFnCall {
224 separator: None,
225 fn_name: None,
226 args: Some(args),
227 })
228 .parse(input)
229}
230
231fn method_or_fn_call<T, Ty>(input: InputSpan<'_>) -> MethodParseResult<'_, T>
232where
233 T: Parse,
234 Ty: GrammarType,
235{
236 let var_name_or_digits = alt((var_name, take_while1(|c: char| c.is_ascii_digit())));
237 let method_or_field_expr = alt((
238 map(with_span(var_name_or_digits), |span| {
239 span.copy_with_extra(Expr::Variable)
240 }),
241 block_expr::<T, Ty>,
242 ));
243 let method_or_field_access_parser = map_res(
244 (
245 method_or_field_expr,
246 opt(preceded(ws::<Ty>, fn_args::<T, Ty>)),
247 ),
248 |(fn_name, maybe_args)| {
249 if maybe_args.is_some() {
250 let is_bogus_name = matches!(fn_name.extra, Expr::Variable)
251 && !is_valid_variable_name(fn_name.fragment());
252 if is_bogus_name {
253 return Err(ErrorKind::LiteralName);
254 }
255 }
256 Ok(MethodOrFnCall {
257 separator: None,
258 fn_name: Some(fn_name),
259 args: maybe_args.map(|(args, _)| args),
260 })
261 },
262 );
263 let method_or_field_access_parser = map(
264 (
265 terminated(with_span(tag_char('.')), ws::<Ty>),
266 cut(method_or_field_access_parser),
267 ),
268 |(separator, mut call)| {
269 call.separator = Some(separator.with_no_extra());
270 call
271 },
272 );
273
274 alt((method_or_field_access_parser, fn_call::<T, Ty>)).parse(input)
275}
276
277fn expr_with_calls<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, SpannedExpr<'_, T::Base>>
279where
280 T: Parse,
281 Ty: GrammarType,
282{
283 let method_or_fn_call = if T::FEATURES.contains(Features::METHODS) {
284 method_or_fn_call::<T, Ty>
285 } else {
286 fn_call::<T, Ty>
287 };
288
289 let mut parser = (
290 simplest_expr::<T, Ty>,
291 many0(with_span(preceded(ws::<Ty>, method_or_fn_call))),
292 );
293 parser.parse(input).and_then(|(rest, (base, calls))| {
294 fold_args(input, base, calls).map(|folded| (rest, folded))
295 })
296}
297
298pub(super) fn simple_expr<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, SpannedExpr<'_, T::Base>>
300where
301 T: Parse,
302 Ty: GrammarType,
303{
304 let as_keyword = delimited(ws::<Ty>, tag("as"), mandatory_ws::<Ty>);
305 let parser = (
306 expr_with_calls::<T, Ty>,
307 many0(preceded(as_keyword, cut(with_span(<T::Base>::parse_type)))),
308 );
309
310 map(parser, |(base, casts)| {
311 casts.into_iter().fold(base, |value, ty| {
312 let united_span = unite_spans(input, &value, &ty);
313 united_span.copy_with_extra(Expr::TypeCast {
314 value: Box::new(value),
315 ty,
316 })
317 })
318 })
319 .parse(input)
320}
321
322#[allow(clippy::option_if_let_else, clippy::range_plus_one)]
323fn fold_args<'a, T: Grammar>(
325 input: InputSpan<'a>,
326 mut base: SpannedExpr<'a, T>,
327 calls: Vec<Spanned<'a, MethodOrFnCall<'a, T>>>,
328) -> Result<SpannedExpr<'a, T>, NomErr<Error>> {
329 let mut maybe_reordered_op = None;
338
339 if matches!(base.extra, Expr::Literal(_)) {
340 match calls.first() {
341 Some(call) if !call.extra.is_method() => {
342 let e = ErrorKind::LiteralName.with_span(&base);
344 return Err(NomErr::Failure(e));
345 }
346 Some(_) => maybe_reordered_op = UnaryOp::try_from_byte(base.fragment().as_bytes()[0]),
348 None => { }
349 }
350 }
351
352 let reordered_op = if let Some(reordered_op) = maybe_reordered_op {
353 let lit_start = base.location_offset() - input.location_offset();
354 let unsigned_lit_input = input.take_from(lit_start + 1).take(base.fragment().len());
355
356 if let Ok((_, unsigned_lit)) = T::parse_literal(unsigned_lit_input) {
357 base = SpannedExpr::new(unsigned_lit_input, Expr::Literal(unsigned_lit));
358
359 let op_span = input.take_from(lit_start).take(1);
362 Some(Spanned::new(op_span, reordered_op))
363 } else {
364 None
365 }
366 } else {
367 None
368 };
369
370 let folded = calls.into_iter().fold(base, |name, call| {
371 let united_span = unite_spans(input, &name, &call);
372
373 let expr = if let Some(fn_name) = call.extra.fn_name {
376 if let Some(args) = call.extra.args {
377 Expr::Method {
378 name: fn_name.into(),
379 receiver: Box::new(name),
380 separator: call.extra.separator.unwrap(), args,
382 }
383 } else {
384 Expr::FieldAccess {
385 name: fn_name.into(),
386 receiver: Box::new(name),
387 }
388 }
389 } else {
390 Expr::Function {
391 name: Box::new(name),
392 args: call.extra.args.expect("Args must be present for functions"),
393 }
394 };
395 united_span.copy_with_extra(expr)
396 });
397
398 Ok(if let Some(unary_op) = reordered_op {
399 unite_spans(input, &unary_op, &folded).copy_with_extra(Expr::Unary {
400 op: unary_op,
401 inner: Box::new(folded),
402 })
403 } else {
404 folded
405 })
406}
407
408pub(super) fn binary_expr<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, SpannedExpr<'_, T::Base>>
411where
412 T: Parse,
413 Ty: GrammarType,
414{
415 #[rustfmt::skip]
420 let binary_ops = alt((
421 tag("+"), tag("-"), tag("*"), tag("/"), tag("^"), tag("=="), tag("!="), tag("&&"), tag("||"), tag(">="), tag("<="), tag(">"), tag("<"), ));
426 let mut binary_ops = map(binary_ops, BinaryOp::from_span);
427
428 let full_binary_ops = move |input| {
429 let (rest, spanned_op) = binary_ops.parse(input)?;
430 if spanned_op.extra.is_supported(T::FEATURES) {
431 Ok((rest, spanned_op))
432 } else {
433 let err = ErrorKind::UnsupportedOp(spanned_op.extra.into());
435 let spanned_err = err.with_span(&spanned_op);
436 Err(NomErr::Failure(spanned_err))
437 }
438 };
439
440 let mut binary_parser = (
441 simple_expr::<T, Ty>,
442 many0((
443 delimited(ws::<Ty>, full_binary_ops, ws::<Ty>),
444 cut(simple_expr::<T, Ty>),
445 )),
446 );
447
448 let (remaining_input, (first, rest)) = binary_parser.parse(input)?;
449 let folded = fold_binary_expr(input, first, rest).map_err(NomErr::Failure)?;
450 Ok((remaining_input, folded))
451}
452
453fn fold_binary_expr<'a, T: Grammar>(
484 input: InputSpan<'a>,
485 first: SpannedExpr<'a, T>,
486 rest: Vec<(Spanned<'a, BinaryOp>, SpannedExpr<'a, T>)>,
487) -> Result<SpannedExpr<'a, T>, Error> {
488 let mut right_contour: Vec<BinaryOp> = vec![];
489
490 rest.into_iter().try_fold(first, |mut acc, (new_op, expr)| {
491 let united_span = unite_spans(input, &acc, &expr);
492
493 let insert_pos = right_contour
494 .iter()
495 .position(|past_op| past_op.priority() >= new_op.extra.priority())
496 .unwrap_or(right_contour.len());
497
498 let chained_comparison = right_contour
500 .get(insert_pos)
501 .is_some_and(|past_op| past_op.is_comparison() && new_op.extra.is_comparison());
502
503 right_contour.truncate(insert_pos);
504 right_contour.push(new_op.extra);
505
506 if insert_pos == 0 {
507 if chained_comparison {
508 return Err(ErrorKind::ChainedComparison.with_span(&united_span));
509 }
510
511 Ok(united_span.copy_with_extra(Expr::Binary {
512 lhs: Box::new(acc),
513 op: new_op,
514 rhs: Box::new(expr),
515 }))
516 } else {
517 let mut parent = &mut acc;
518 for _ in 1..insert_pos {
519 parent = match &mut parent.extra {
520 Expr::Binary { rhs, .. } => rhs,
521 _ => unreachable!(),
522 };
523 }
524
525 *parent = unite_spans(input, parent, &expr).copy_with_extra(parent.extra.clone());
526 if let Expr::Binary { rhs, .. } = &mut parent.extra {
527 let rhs_span = unite_spans(input, rhs, &expr);
528 if chained_comparison {
529 return Err(ErrorKind::ChainedComparison.with_span(&rhs_span));
530 }
531
532 let dummy = Box::new(rhs.copy_with_extra(Expr::Variable));
533 let old_rhs = mem::replace(rhs, dummy);
534 let new_expr = Expr::Binary {
535 lhs: old_rhs,
536 op: new_op,
537 rhs: Box::new(expr),
538 };
539 *rhs = Box::new(rhs_span.copy_with_extra(new_expr));
540 }
541 acc = united_span.copy_with_extra(acc.extra);
542 Ok(acc)
543 }
544 })
545}
546
547pub(super) fn expr<T, Ty>(input: InputSpan<'_>) -> NomResult<'_, SpannedExpr<'_, T::Base>>
548where
549 T: Parse,
550 Ty: GrammarType,
551{
552 context(Context::Expr.to_str(), binary_expr::<T, Ty>).parse(input)
553}