1use crate::ast::{Ast, Comparator, KeyValuePair};
7use crate::lexer::{Token, TokenTuple, tokenize};
8use crate::{ErrorReason, JmespathError};
9
10pub type ParseResult = Result<Ast, JmespathError>;
12
13pub fn parse(expr: &str) -> ParseResult {
15 let tokens = tokenize(expr)?;
16 Parser::new(tokens, expr).parse()
17}
18
19const PROJECTION_STOP: usize = 10;
21
22const MAX_PARSE_DEPTH: usize = 128;
30
31struct Parser<'a> {
32 tokens: Vec<TokenTuple<'a>>,
33 cursor: usize,
34 expr: &'a str,
35 offset: usize,
36 depth: usize,
37}
38
39impl<'a> Parser<'a> {
40 fn new(tokens: Vec<TokenTuple<'a>>, expr: &'a str) -> Parser<'a> {
41 Parser {
42 tokens,
43 cursor: 0,
44 expr,
45 offset: 0,
46 depth: 0,
47 }
48 }
49
50 #[inline]
51 fn parse(&mut self) -> ParseResult {
52 self.expr(0).and_then(|result| match self.peek(0) {
53 &Token::Eof => Ok(result),
54 t => Err(self.err(t, "Did not parse the complete expression", true)),
55 })
56 }
57
58 #[inline]
59 fn advance(&mut self) -> Token<'a> {
60 self.advance_with_pos().1
61 }
62
63 #[inline]
64 fn advance_with_pos(&mut self) -> (usize, Token<'a>) {
65 if self.cursor < self.tokens.len() {
66 let (pos, tok) = self.tokens[self.cursor].clone();
67 self.cursor += 1;
68 self.offset = pos;
69 (pos, tok)
70 } else {
71 (self.offset, Token::Eof)
72 }
73 }
74
75 #[inline]
76 fn peek(&self, lookahead: usize) -> &Token<'a> {
77 let idx = self.cursor + lookahead;
78 if idx < self.tokens.len() {
79 &self.tokens[idx].1
80 } else {
81 &Token::Eof
82 }
83 }
84
85 fn err(&self, current_token: &Token<'_>, error_msg: &str, is_peek: bool) -> JmespathError {
86 let mut actual_pos = self.offset;
87 let mut buff = error_msg.to_string();
88 buff.push_str(&format!(" -- found {current_token:?}"));
89 if is_peek && self.cursor < self.tokens.len() {
90 actual_pos = self.tokens[self.cursor].0;
91 }
92 JmespathError::new(self.expr, actual_pos, ErrorReason::Parse(buff))
93 }
94
95 fn expr(&mut self, rbp: usize) -> ParseResult {
96 let mut left = self.nud();
97 let mut chain = 0usize;
104 while rbp < self.peek(0).lbp() {
105 chain += 1;
106 if chain > MAX_PARSE_DEPTH {
107 return Err(self.err(
108 self.peek(0),
109 &format!("Expression nesting exceeds maximum depth of {MAX_PARSE_DEPTH}"),
110 true,
111 ));
112 }
113 left = self.led(Box::new(left?));
114 }
115 left
116 }
117
118 fn nud(&mut self) -> ParseResult {
119 self.depth += 1;
120 if self.depth > MAX_PARSE_DEPTH {
121 self.depth -= 1;
122 return Err(self.err(
123 self.peek(0),
124 &format!("Expression nesting exceeds maximum depth of {MAX_PARSE_DEPTH}"),
125 true,
126 ));
127 }
128 let result = self.nud_inner();
129 self.depth -= 1;
130 result
131 }
132
133 fn nud_inner(&mut self) -> ParseResult {
134 let (offset, token) = self.advance_with_pos();
135 match token {
136 Token::At => Ok(Ast::Identity { offset }),
137 #[cfg(feature = "let-expr")]
138 Token::Identifier(value) if *value == *"let" => self.parse_let(offset),
139 Token::Identifier(value) => Ok(Ast::Field {
140 name: value.to_owned(),
141 offset,
142 }),
143 Token::QuotedIdentifier(value) => match self.peek(0) {
144 Token::Lparen => {
145 let message = "Quoted strings can't be a function name";
146 Err(self.err(&Token::Lparen, message, true))
147 }
148 _ => Ok(Ast::Field {
149 name: value,
150 offset,
151 }),
152 },
153 Token::Star => self.parse_wildcard_values(Box::new(Ast::Identity { offset })),
154 Token::Literal(value) => Ok(Ast::Literal { value, offset }),
155 Token::Lbracket => match self.peek(0) {
156 &Token::Number(_) | &Token::Colon => self.parse_index(),
157 &Token::Star if self.peek(1) == &Token::Rbracket => {
158 self.advance();
159 self.parse_wildcard_index(Box::new(Ast::Identity { offset }))
160 }
161 _ => self.parse_multi_list(),
162 },
163 Token::Flatten => self.parse_flatten(Box::new(Ast::Identity { offset })),
164 Token::Lbrace => {
165 let mut pairs = vec![];
166 loop {
167 pairs.push(self.parse_kvp()?);
168 match self.advance() {
169 Token::Rbrace => break,
170 Token::Comma => continue,
171 ref t => return Err(self.err(t, "Expected '}' or ','", false)),
172 }
173 }
174 Ok(Ast::MultiHash {
175 elements: pairs,
176 offset,
177 })
178 }
179 t @ Token::Ampersand => {
180 let rhs = self.expr(t.lbp())?;
181 Ok(Ast::Expref {
182 ast: Box::new(rhs),
183 offset,
184 })
185 }
186 t @ Token::Not => Ok(Ast::Not {
187 node: Box::new(self.expr(t.lbp())?),
188 offset,
189 }),
190 Token::Filter => self.parse_filter(Box::new(Ast::Identity { offset })),
191 Token::Lparen => {
192 let result = self.expr(0)?;
193 match self.advance() {
194 Token::Rparen => Ok(result),
195 ref t => Err(self.err(t, "Expected ')' to close '('", false)),
196 }
197 }
198 #[cfg(feature = "let-expr")]
199 Token::Variable(name) => Ok(Ast::VariableRef {
200 name: name.to_owned(),
201 offset,
202 }),
203 ref t => Err(self.err(t, "Unexpected nud token", false)),
204 }
205 }
206
207 fn led(&mut self, left: Box<Ast>) -> ParseResult {
208 let (offset, token) = self.advance_with_pos();
209 match token {
210 t @ Token::Dot => {
211 if self.peek(0) == &Token::Star {
212 self.advance();
213 self.parse_wildcard_values(left)
214 } else {
215 let rhs = self.parse_dot(t.lbp())?;
216 Ok(Ast::Subexpr {
217 offset,
218 lhs: left,
219 rhs: Box::new(rhs),
220 })
221 }
222 }
223 Token::Lbracket => {
224 if match self.peek(0) {
225 &Token::Number(_) | &Token::Colon => true,
226 &Token::Star => false,
227 t => return Err(self.err(t, "Expected number, ':', or '*'", true)),
228 } {
229 Ok(Ast::Subexpr {
230 offset,
231 lhs: left,
232 rhs: Box::new(self.parse_index()?),
233 })
234 } else {
235 self.advance();
236 self.parse_wildcard_index(left)
237 }
238 }
239 t @ Token::Or => {
240 let rhs = self.expr(t.lbp())?;
241 Ok(Ast::Or {
242 offset,
243 lhs: left,
244 rhs: Box::new(rhs),
245 })
246 }
247 t @ Token::And => {
248 let rhs = self.expr(t.lbp())?;
249 Ok(Ast::And {
250 offset,
251 lhs: left,
252 rhs: Box::new(rhs),
253 })
254 }
255 t @ Token::Pipe => {
256 let rhs = self.expr(t.lbp())?;
257 Ok(Ast::Subexpr {
258 offset,
259 lhs: left,
260 rhs: Box::new(rhs),
261 })
262 }
263 Token::Lparen => match *left {
264 Ast::Field { name: v, .. } => Ok(Ast::Function {
265 offset,
266 name: v,
267 args: self.parse_list(Token::Rparen)?,
268 }),
269 _ => Err(self.err(self.peek(0), "Invalid function name", true)),
270 },
271 Token::Flatten => self.parse_flatten(left),
272 Token::Filter => self.parse_filter(left),
273 Token::Eq => self.parse_comparator(Comparator::Equal, left),
274 Token::Ne => self.parse_comparator(Comparator::NotEqual, left),
275 Token::Gt => self.parse_comparator(Comparator::GreaterThan, left),
276 Token::Gte => self.parse_comparator(Comparator::GreaterThanEqual, left),
277 Token::Lt => self.parse_comparator(Comparator::LessThan, left),
278 Token::Lte => self.parse_comparator(Comparator::LessThanEqual, left),
279 ref t => Err(self.err(t, "Unexpected led token", false)),
280 }
281 }
282
283 #[cfg(feature = "let-expr")]
284 fn parse_let(&mut self, offset: usize) -> ParseResult {
285 let mut bindings = vec![];
286 loop {
287 match self.peek(0) {
288 Token::Variable(_) => {
289 let var_name = match self.advance() {
290 Token::Variable(name) => name.to_owned(),
291 _ => unreachable!(),
292 };
293 match self.advance() {
294 Token::Assign => {}
295 ref t => {
296 return Err(self.err(
297 t,
298 "Expected '=' after variable in let binding",
299 false,
300 ));
301 }
302 }
303 let value = self.parse_let_binding_expr()?;
304 bindings.push((var_name, value));
305 match self.peek(0) {
306 Token::Comma => {
307 self.advance();
308 }
309 Token::Identifier(s) if *s == "in" => {
310 break;
311 }
312 t => {
313 return Err(self.err(
314 t,
315 "Expected ',' or 'in' after let binding",
316 true,
317 ));
318 }
319 }
320 }
321 t => {
322 return Err(self.err(t, "Expected variable binding ($name) after 'let'", true));
323 }
324 }
325 }
326 match self.advance() {
327 Token::Identifier(s) if *s == *"in" => {}
328 ref t => {
329 return Err(self.err(t, "Expected 'in' keyword after let bindings", false));
330 }
331 }
332 let body = self.expr(0)?;
333 Ok(Ast::Let {
334 offset,
335 bindings,
336 expr: Box::new(body),
337 })
338 }
339
340 #[cfg(feature = "let-expr")]
341 fn parse_let_binding_expr(&mut self) -> ParseResult {
342 self.parse_let_binding_expr_bp(0)
343 }
344
345 #[cfg(feature = "let-expr")]
346 fn parse_let_binding_expr_bp(&mut self, rbp: usize) -> ParseResult {
347 let mut left = self.nud();
348 let mut chain = 0usize;
349 loop {
350 match self.peek(0) {
351 Token::Comma => break,
352 Token::Identifier(s) if *s == "in" => break,
353 _ => {}
354 }
355 if rbp >= self.peek(0).lbp() {
356 break;
357 }
358 chain += 1;
359 if chain > MAX_PARSE_DEPTH {
360 return Err(self.err(
361 self.peek(0),
362 &format!("Expression nesting exceeds maximum depth of {MAX_PARSE_DEPTH}"),
363 true,
364 ));
365 }
366 left = self.led(Box::new(left?));
367 }
368 left
369 }
370
371 fn parse_kvp(&mut self) -> Result<KeyValuePair, JmespathError> {
372 match self.advance() {
373 Token::Identifier(value) => {
374 if self.peek(0) == &Token::Colon {
375 self.advance();
376 Ok(KeyValuePair {
377 key: value.to_owned(),
378 value: self.expr(0)?,
379 })
380 } else {
381 Err(self.err(self.peek(0), "Expected ':' to follow key", true))
382 }
383 }
384 Token::QuotedIdentifier(value) => {
385 if self.peek(0) == &Token::Colon {
386 self.advance();
387 Ok(KeyValuePair {
388 key: value,
389 value: self.expr(0)?,
390 })
391 } else {
392 Err(self.err(self.peek(0), "Expected ':' to follow key", true))
393 }
394 }
395 ref t => Err(self.err(t, "Expected Field to start key value pair", false)),
396 }
397 }
398
399 fn parse_filter(&mut self, lhs: Box<Ast>) -> ParseResult {
400 let condition_lhs = Box::new(self.expr(0)?);
401 match self.advance() {
402 Token::Rbracket => {
403 let condition_rhs = Box::new(self.projection_rhs(Token::Filter.lbp())?);
404 Ok(Ast::Projection {
405 offset: self.offset,
406 lhs,
407 rhs: Box::new(Ast::Condition {
408 offset: self.offset,
409 predicate: condition_lhs,
410 then: condition_rhs,
411 }),
412 })
413 }
414 ref t => Err(self.err(t, "Expected ']'", false)),
415 }
416 }
417
418 fn parse_flatten(&mut self, lhs: Box<Ast>) -> ParseResult {
419 let rhs = Box::new(self.projection_rhs(Token::Flatten.lbp())?);
420 Ok(Ast::Projection {
421 offset: self.offset,
422 lhs: Box::new(Ast::Flatten {
423 offset: self.offset,
424 node: lhs,
425 }),
426 rhs,
427 })
428 }
429
430 fn parse_comparator(&mut self, cmp: Comparator, lhs: Box<Ast>) -> ParseResult {
431 let rhs = Box::new(self.expr(Token::Eq.lbp())?);
432 Ok(Ast::Comparison {
433 offset: self.offset,
434 comparator: cmp,
435 lhs,
436 rhs,
437 })
438 }
439
440 fn parse_dot(&mut self, lbp: usize) -> ParseResult {
441 if match self.peek(0) {
442 &Token::Lbracket => true,
443 &Token::Identifier(_)
444 | &Token::QuotedIdentifier(_)
445 | &Token::Star
446 | &Token::Lbrace
447 | &Token::Ampersand => false,
448 t => return Err(self.err(t, "Expected identifier, '*', '{', '[', '&', or '[?'", true)),
449 } {
450 self.advance();
451 self.parse_multi_list()
452 } else {
453 self.expr(lbp)
454 }
455 }
456
457 fn projection_rhs(&mut self, lbp: usize) -> ParseResult {
458 if match self.peek(0) {
459 &Token::Dot => true,
460 &Token::Lbracket | &Token::Filter => false,
461 t if t.lbp() < PROJECTION_STOP => {
462 return Ok(Ast::Identity {
463 offset: self.offset,
464 });
465 }
466 t => {
467 return Err(self.err(t, "Expected '.', '[', or '[?'", true));
468 }
469 } {
470 self.advance();
471 self.parse_dot(lbp)
472 } else {
473 self.expr(lbp)
474 }
475 }
476
477 fn parse_wildcard_index(&mut self, lhs: Box<Ast>) -> ParseResult {
478 match self.advance() {
479 Token::Rbracket => {
480 let rhs = Box::new(self.projection_rhs(Token::Star.lbp())?);
481 Ok(Ast::Projection {
482 offset: self.offset,
483 lhs,
484 rhs,
485 })
486 }
487 ref t => Err(self.err(t, "Expected ']' for wildcard index", false)),
488 }
489 }
490
491 fn parse_wildcard_values(&mut self, lhs: Box<Ast>) -> ParseResult {
492 let rhs = Box::new(self.projection_rhs(Token::Star.lbp())?);
493 Ok(Ast::Projection {
494 offset: self.offset,
495 lhs: Box::new(Ast::ObjectValues {
496 offset: self.offset,
497 node: lhs,
498 }),
499 rhs,
500 })
501 }
502
503 fn parse_index(&mut self) -> ParseResult {
504 let mut parts = [None, None, None];
505 let mut pos = 0;
506 loop {
507 match self.advance() {
508 Token::Number(value) => {
509 parts[pos] = Some(value);
510 match self.peek(0) {
511 &Token::Colon | &Token::Rbracket => (),
512 t => return Err(self.err(t, "Expected ':', or ']'", true)),
513 };
514 }
515 Token::Rbracket => break,
516 Token::Colon if pos >= 2 => {
517 return Err(self.err(&Token::Colon, "Too many colons in slice expr", false));
518 }
519 Token::Colon => {
520 pos += 1;
521 match self.peek(0) {
522 &Token::Number(_) | &Token::Colon | &Token::Rbracket => continue,
523 t => return Err(self.err(t, "Expected number, ':', or ']'", true)),
524 };
525 }
526 ref t => return Err(self.err(t, "Expected number, ':', or ']'", false)),
527 }
528 }
529
530 if pos == 0 {
531 Ok(Ast::Index {
532 offset: self.offset,
533 idx: parts[0].ok_or_else(|| {
534 JmespathError::new(
535 self.expr,
536 self.offset,
537 ErrorReason::Parse(
538 "Expected parts[0] to be Some; but found None".to_owned(),
539 ),
540 )
541 })?,
542 })
543 } else {
544 Ok(Ast::Projection {
545 offset: self.offset,
546 lhs: Box::new(Ast::Slice {
547 offset: self.offset,
548 start: parts[0],
549 stop: parts[1],
550 step: parts[2].unwrap_or(1),
551 }),
552 rhs: Box::new(self.projection_rhs(Token::Star.lbp())?),
553 })
554 }
555 }
556
557 fn parse_multi_list(&mut self) -> ParseResult {
558 Ok(Ast::MultiList {
559 offset: self.offset,
560 elements: self.parse_list(Token::Rbracket)?,
561 })
562 }
563
564 fn parse_list(&mut self, closing: Token<'_>) -> Result<Vec<Ast>, JmespathError> {
565 let mut nodes = vec![];
566 while self.peek(0) != &closing {
567 nodes.push(self.expr(0)?);
568 if self.peek(0) == &Token::Comma {
569 self.advance();
570 if self.peek(0) == &closing {
571 return Err(self.err(self.peek(0), "invalid token after ','", true));
572 }
573 }
574 }
575 self.advance();
576 Ok(nodes)
577 }
578}
579
580#[cfg(test)]
581mod tests {
582 use super::*;
583 use crate::ast::Comparator;
584
585 #[test]
586 fn parse_field() {
587 let ast = parse("foo").unwrap();
588 assert!(matches!(ast, Ast::Field { name, .. } if name == "foo"));
589 }
590
591 #[test]
592 fn parse_identity() {
593 let ast = parse("@").unwrap();
594 assert!(matches!(ast, Ast::Identity { .. }));
595 }
596
597 #[test]
598 fn parse_subexpr() {
599 let ast = parse("foo.bar").unwrap();
600 assert!(matches!(ast, Ast::Subexpr { .. }));
601 }
602
603 #[test]
604 fn parse_deeply_nested_subexpr() {
605 let ast = parse("a.b.c.d.e").unwrap();
606 assert!(matches!(ast, Ast::Subexpr { .. }));
607 }
608
609 #[test]
610 fn parse_index_positive() {
611 let ast = parse("[0]").unwrap();
612 assert!(matches!(ast, Ast::Index { idx: 0, .. }));
613 }
614
615 #[test]
616 fn parse_index_negative() {
617 let ast = parse("[-1]").unwrap();
618 assert!(matches!(ast, Ast::Index { idx: -1, .. }));
619 }
620
621 #[test]
622 fn parse_slice_basic() {
623 let ast = parse("[0:5]").unwrap();
624 match ast {
625 Ast::Projection { lhs, .. } => {
626 assert!(matches!(
627 *lhs,
628 Ast::Slice {
629 start: Some(0),
630 stop: Some(5),
631 step: 1,
632 ..
633 }
634 ));
635 }
636 _ => panic!("expected Projection with Slice lhs"),
637 }
638 }
639
640 #[test]
641 fn parse_slice_with_step() {
642 let ast = parse("[::2]").unwrap();
643 match ast {
644 Ast::Projection { lhs, .. } => {
645 assert!(matches!(
646 *lhs,
647 Ast::Slice {
648 start: None,
649 stop: None,
650 step: 2,
651 ..
652 }
653 ));
654 }
655 _ => panic!("expected Projection with Slice lhs"),
656 }
657 }
658
659 #[test]
660 fn parse_slice_negative_step() {
661 let ast = parse("[::-1]").unwrap();
662 match ast {
663 Ast::Projection { lhs, .. } => {
664 assert!(matches!(*lhs, Ast::Slice { step: -1, .. }));
665 }
666 _ => panic!("expected Projection with Slice lhs"),
667 }
668 }
669
670 #[test]
671 fn parse_wildcard_values() {
672 let ast = parse("*").unwrap();
673 assert!(matches!(ast, Ast::Projection { .. }));
674 }
675
676 #[test]
677 fn parse_wildcard_index() {
678 let ast = parse("[*]").unwrap();
679 assert!(matches!(ast, Ast::Projection { .. }));
680 }
681
682 #[test]
683 fn parse_flatten() {
684 let ast = parse("[]").unwrap();
685 match ast {
686 Ast::Projection { lhs, .. } => {
687 assert!(matches!(*lhs, Ast::Flatten { .. }));
688 }
689 _ => panic!("expected Projection with Flatten"),
690 }
691 }
692
693 #[test]
694 fn parse_filter() {
695 let ast = parse("[?a > `1`]").unwrap();
696 assert!(matches!(ast, Ast::Projection { .. }));
697 }
698
699 #[test]
700 fn parse_or() {
701 let ast = parse("a || b").unwrap();
702 assert!(matches!(ast, Ast::Or { .. }));
703 }
704
705 #[test]
706 fn parse_and() {
707 let ast = parse("a && b").unwrap();
708 assert!(matches!(ast, Ast::And { .. }));
709 }
710
711 #[test]
712 fn parse_not() {
713 let ast = parse("!a").unwrap();
714 assert!(matches!(ast, Ast::Not { .. }));
715 }
716
717 #[test]
718 fn parse_pipe() {
719 let ast = parse("a | b").unwrap();
720 assert!(matches!(ast, Ast::Subexpr { .. }));
721 }
722
723 #[test]
724 fn parse_function_call() {
725 let ast = parse("length(@)").unwrap();
726 match ast {
727 Ast::Function { name, args, .. } => {
728 assert_eq!(name, "length");
729 assert_eq!(args.len(), 1);
730 }
731 _ => panic!("expected Function"),
732 }
733 }
734
735 #[test]
736 fn parse_multi_list() {
737 let ast = parse("[a, b, c]").unwrap();
738 match ast {
739 Ast::MultiList { elements, .. } => {
740 assert_eq!(elements.len(), 3);
741 }
742 _ => panic!("expected MultiList"),
743 }
744 }
745
746 #[test]
747 fn parse_multi_hash() {
748 let ast = parse("{a: b, c: d}").unwrap();
749 match ast {
750 Ast::MultiHash { elements, .. } => {
751 assert_eq!(elements.len(), 2);
752 }
753 _ => panic!("expected MultiHash"),
754 }
755 }
756
757 #[test]
758 fn parse_literal_string() {
759 let ast = parse("`\"hello\"`").unwrap();
760 assert!(matches!(ast, Ast::Literal { .. }));
761 }
762
763 #[test]
764 fn parse_literal_number() {
765 let ast = parse("`42`").unwrap();
766 assert!(matches!(ast, Ast::Literal { .. }));
767 }
768
769 #[test]
770 fn parse_literal_null() {
771 let ast = parse("`null`").unwrap();
772 assert!(matches!(ast, Ast::Literal { .. }));
773 }
774
775 #[test]
776 fn parse_raw_string() {
777 let ast = parse("'hello'").unwrap();
778 match ast {
779 Ast::Literal { value, .. } => {
780 assert_eq!(value, serde_json::json!("hello"));
781 }
782 _ => panic!("expected Literal from raw string"),
783 }
784 }
785
786 #[test]
787 fn parse_expref() {
788 let ast = parse("&foo").unwrap();
789 assert!(matches!(ast, Ast::Expref { .. }));
790 }
791
792 #[test]
793 fn parse_all_comparators() {
794 for (expr, cmp) in [
795 ("a == b", Comparator::Equal),
796 ("a != b", Comparator::NotEqual),
797 ("a > b", Comparator::GreaterThan),
798 ("a >= b", Comparator::GreaterThanEqual),
799 ("a < b", Comparator::LessThan),
800 ("a <= b", Comparator::LessThanEqual),
801 ] {
802 let ast = parse(expr).unwrap();
803 match ast {
804 Ast::Comparison { comparator, .. } => assert_eq!(comparator, cmp, "for {expr}"),
805 _ => panic!("expected Comparison for {expr}"),
806 }
807 }
808 }
809
810 #[test]
811 fn parse_quoted_identifier() {
812 let ast = parse("\"foo bar\"").unwrap();
813 match ast {
814 Ast::Field { name, .. } => assert_eq!(name, "foo bar"),
815 _ => panic!("expected Field from quoted identifier"),
816 }
817 }
818
819 #[test]
820 fn parse_parenthesized_expression() {
821 let ast = parse("(a)").unwrap();
822 assert!(matches!(ast, Ast::Field { .. }));
823 }
824
825 #[test]
826 fn error_empty_expression() {
827 let result = parse("");
828 assert!(result.is_err());
829 }
830
831 #[test]
832 fn error_unclosed_bracket() {
833 let result = parse("[0");
834 assert!(result.is_err());
835 }
836
837 #[test]
838 fn error_trailing_garbage() {
839 let result = parse("foo bar");
840 assert!(result.is_err());
841 }
842
843 #[test]
844 fn error_quoted_string_as_function() {
845 let result = parse("\"foo\"()");
846 assert!(result.is_err());
847 }
848
849 #[test]
850 fn error_trailing_comma_in_list() {
851 let result = parse("[a, b,]");
852 assert!(result.is_err());
853 }
854
855 #[test]
856 fn error_on_excessive_not_nesting() {
857 let expr = format!("{}a", "!".repeat(300));
860 let result = parse(&expr);
861 assert!(result.is_err(), "deep nesting should error, not abort");
862 let msg = format!("{}", result.unwrap_err());
863 assert!(msg.contains("nesting"), "unexpected message: {msg}");
864 }
865
866 #[test]
867 fn error_on_excessive_paren_nesting() {
868 let expr = format!("{}a{}", "(".repeat(500), ")".repeat(500));
869 assert!(parse(&expr).is_err());
870 }
871
872 #[test]
873 fn error_on_excessive_subexpr_chain() {
874 let expr = format!("a{}", ".a".repeat(300));
878 let result = parse(&expr);
879 assert!(
880 result.is_err(),
881 "deep chain should error, not build a deep AST"
882 );
883 let msg = format!("{}", result.unwrap_err());
884 assert!(msg.contains("nesting"), "unexpected message: {msg}");
885 }
886
887 #[test]
888 fn parse_nesting_within_limit_ok() {
889 assert!(parse(&format!("{}a", "!".repeat(100))).is_ok());
890 assert!(parse(&format!("a{}", ".a".repeat(100))).is_ok());
891 }
892
893 #[cfg(feature = "let-expr")]
894 #[test]
895 fn parse_let_expression() {
896 let ast = parse("let $x = `1` in $x").unwrap();
897 assert!(matches!(ast, Ast::Let { .. }));
898 }
899
900 #[cfg(feature = "let-expr")]
901 #[test]
902 fn parse_variable_ref() {
903 let ast = parse("$x").unwrap();
905 assert!(matches!(ast, Ast::VariableRef { .. }));
906 }
907
908 #[cfg(feature = "let-expr")]
909 #[test]
910 fn error_let_missing_in() {
911 let result = parse("let $x = `1` $x");
912 assert!(result.is_err());
913 }
914}