prql_parser/
lib.rs

1mod expr;
2mod interpolation;
3pub mod lexer;
4mod stmt;
5
6use chumsky::{prelude::*, Stream};
7use error::convert_lexer_error;
8use error::convert_parser_error;
9pub use error::Error;
10
11use self::lexer::Token;
12
13use prql_ast::stmt::*;
14use prql_ast::Span;
15
16mod error;
17mod span;
18
19use span::ParserSpan;
20
21use common::PError;
22
23/// Build PRQL AST from a PRQL query string.
24pub fn parse_source(source: &str, source_id: u16) -> Result<Vec<Stmt>, Vec<Error>> {
25    let mut errors = Vec::new();
26
27    let (tokens, lex_errors) = ::chumsky::Parser::parse_recovery(&lexer::lexer(), source);
28
29    errors.extend(
30        lex_errors
31            .into_iter()
32            .map(|err| convert_lexer_error(source, err, source_id)),
33    );
34
35    let ast = if let Some(tokens) = tokens {
36        let stream = prepare_stream(tokens, source, source_id);
37
38        let (ast, parse_errors) = ::chumsky::Parser::parse_recovery(&stmt::source(), stream);
39
40        errors.extend(parse_errors.into_iter().map(convert_parser_error));
41
42        ast
43    } else {
44        None
45    };
46
47    if errors.is_empty() {
48        Ok(ast.unwrap_or_default())
49    } else {
50        Err(errors)
51    }
52}
53
54/// Helper that does not track source_ids
55#[cfg(test)]
56pub fn parse_single(source: &str) -> Result<Vec<Stmt>, Vec<Error>> {
57    parse_source(source, 0)
58}
59
60fn prepare_stream(
61    tokens: Vec<(Token, std::ops::Range<usize>)>,
62    source: &str,
63    source_id: u16,
64) -> Stream<Token, ParserSpan, impl Iterator<Item = (Token, ParserSpan)> + Sized> {
65    let tokens = tokens
66        .into_iter()
67        .map(move |(t, s)| (t, ParserSpan::new(source_id, s)));
68    let len = source.chars().count();
69    let eoi = ParserSpan(Span {
70        start: len,
71        end: len + 1,
72        source_id,
73    });
74    Stream::from_iter(eoi, tokens)
75}
76
77mod common {
78    use chumsky::prelude::*;
79
80    use super::{lexer::Token, span::ParserSpan};
81    use prql_ast::expr::*;
82    use prql_ast::stmt::*;
83
84    pub type PError = Simple<Token, ParserSpan>;
85
86    pub fn ident_part() -> impl Parser<Token, String, Error = PError> {
87        select! { Token::Ident(ident) => ident }.map_err(|e: PError| {
88            Simple::expected_input_found(
89                e.span(),
90                [Some(Token::Ident("".to_string()))],
91                e.found().cloned(),
92            )
93        })
94    }
95
96    pub fn keyword(kw: &'static str) -> impl Parser<Token, (), Error = PError> + Clone {
97        just(Token::Keyword(kw.to_string())).ignored()
98    }
99
100    pub fn new_line() -> impl Parser<Token, (), Error = PError> + Clone {
101        just(Token::NewLine).ignored()
102    }
103
104    pub fn ctrl(char: char) -> impl Parser<Token, (), Error = PError> + Clone {
105        just(Token::Control(char)).ignored()
106    }
107
108    pub fn into_stmt((annotations, kind): (Vec<Annotation>, StmtKind), span: ParserSpan) -> Stmt {
109        Stmt {
110            kind,
111            span: Some(span.0),
112            annotations,
113        }
114    }
115
116    pub fn into_expr(kind: ExprKind, span: ParserSpan) -> Expr {
117        Expr {
118            span: Some(span.0),
119            ..Expr::new(kind)
120        }
121    }
122}
123
124#[cfg(test)]
125mod test {
126
127    use super::*;
128    use insta::assert_yaml_snapshot;
129    use prql_ast::expr::{Expr, FuncCall};
130
131    fn parse_expr(source: &str) -> Result<Expr, Vec<Error>> {
132        let tokens = Parser::parse(&lexer::lexer(), source).map_err(|errs| {
133            errs.into_iter()
134                .map(|err| convert_lexer_error(source, err, 0))
135                .collect::<Vec<_>>()
136        })?;
137
138        let stream = prepare_stream(tokens, source, 0);
139        Parser::parse(&expr::expr_call().then_ignore(end()), stream)
140            .map_err(|errs| errs.into_iter().map(convert_parser_error).collect())
141    }
142
143    #[test]
144    fn test_pipeline_parse_tree() {
145        assert_yaml_snapshot!(parse_single(
146            r#"
147from employees
148filter country == "USA"                      # Each line transforms the previous result.
149derive {                                     # This adds columns / variables.
150  gross_salary = salary + payroll_tax,
151  gross_cost = gross_salary + benefits_cost  # Variables can use other variables.
152}
153filter gross_cost > 0
154group {title, country} (                     # For each group use a nested pipeline
155  aggregate {                                # Aggregate each group to a single row
156    average salary,
157    average gross_salary,
158    sum salary,
159    sum gross_salary,
160    average gross_cost,
161    sum_gross_cost = sum gross_cost,
162    ct = count salary,
163  }
164)
165sort sum_gross_cost
166filter ct > 200
167take 20
168        "#
169        )
170        .unwrap());
171    }
172
173    #[test]
174    fn test_take() {
175        parse_single("take 10").unwrap();
176
177        assert_yaml_snapshot!(parse_single(r#"take 10"#).unwrap(), @r###"
178        ---
179        - Main:
180            FuncCall:
181              name:
182                Ident:
183                  - take
184              args:
185                - Literal:
186                    Integer: 10
187          annotations: []
188        "###);
189
190        assert_yaml_snapshot!(parse_single(r#"take ..10"#).unwrap(), @r###"
191        ---
192        - Main:
193            FuncCall:
194              name:
195                Ident:
196                  - take
197              args:
198                - Range:
199                    start: ~
200                    end:
201                      Literal:
202                        Integer: 10
203          annotations: []
204        "###);
205
206        assert_yaml_snapshot!(parse_single(r#"take 1..10"#).unwrap(), @r###"
207        ---
208        - Main:
209            FuncCall:
210              name:
211                Ident:
212                  - take
213              args:
214                - Range:
215                    start:
216                      Literal:
217                        Integer: 1
218                    end:
219                      Literal:
220                        Integer: 10
221          annotations: []
222        "###);
223    }
224
225    #[test]
226    fn test_ranges() {
227        assert_yaml_snapshot!(parse_expr(r#"3..5"#).unwrap(), @r###"
228        ---
229        Range:
230          start:
231            Literal:
232              Integer: 3
233          end:
234            Literal:
235              Integer: 5
236        "###);
237
238        assert_yaml_snapshot!(parse_expr(r#"-2..-5"#).unwrap(), @r###"
239        ---
240        Range:
241          start:
242            Unary:
243              op: Neg
244              expr:
245                Literal:
246                  Integer: 2
247          end:
248            Unary:
249              op: Neg
250              expr:
251                Literal:
252                  Integer: 5
253        "###);
254
255        assert_yaml_snapshot!(parse_expr(r#"(-2..(-5 | abs))"#).unwrap(), @r###"
256        ---
257        Range:
258          start:
259            Unary:
260              op: Neg
261              expr:
262                Literal:
263                  Integer: 2
264          end:
265            Pipeline:
266              exprs:
267                - Unary:
268                    op: Neg
269                    expr:
270                      Literal:
271                        Integer: 5
272                - Ident:
273                    - abs
274        "###);
275
276        assert_yaml_snapshot!(parse_expr(r#"(2 + 5)..'a'"#).unwrap(), @r###"
277        ---
278        Range:
279          start:
280            Binary:
281              left:
282                Literal:
283                  Integer: 2
284              op: Add
285              right:
286                Literal:
287                  Integer: 5
288          end:
289            Literal:
290              String: a
291        "###);
292
293        assert_yaml_snapshot!(parse_expr(r#"1.6..rel.col"#).unwrap(), @r###"
294        ---
295        Range:
296          start:
297            Literal:
298              Float: 1.6
299          end:
300            Ident:
301              - rel
302              - col
303        "###);
304
305        assert_yaml_snapshot!(parse_expr(r#"6.."#).unwrap(), @r###"
306        ---
307        Range:
308          start:
309            Literal:
310              Integer: 6
311          end: ~
312        "###);
313        assert_yaml_snapshot!(parse_expr(r#"..7"#).unwrap(), @r###"
314        ---
315        Range:
316          start: ~
317          end:
318            Literal:
319              Integer: 7
320        "###);
321
322        assert_yaml_snapshot!(parse_expr(r#".."#).unwrap(), @r###"
323        ---
324        Range:
325          start: ~
326          end: ~
327        "###);
328
329        assert_yaml_snapshot!(parse_expr(r#"@2020-01-01..@2021-01-01"#).unwrap(), @r###"
330        ---
331        Range:
332          start:
333            Literal:
334              Date: 2020-01-01
335          end:
336            Literal:
337              Date: 2021-01-01
338        "###);
339    }
340
341    #[test]
342    fn test_basic_exprs() {
343        assert_yaml_snapshot!(parse_expr(r#"country == "USA""#).unwrap(), @r###"
344        ---
345        Binary:
346          left:
347            Ident:
348              - country
349          op: Eq
350          right:
351            Literal:
352              String: USA
353        "###);
354        assert_yaml_snapshot!(parse_expr("select {a, b, c}").unwrap(), @r###"
355        ---
356        FuncCall:
357          name:
358            Ident:
359              - select
360          args:
361            - Tuple:
362                - Ident:
363                    - a
364                - Ident:
365                    - b
366                - Ident:
367                    - c
368        "###);
369        assert_yaml_snapshot!(parse_expr(
370            "group {title, country} (
371                aggregate {sum salary}
372            )"
373        ).unwrap(), @r###"
374        ---
375        FuncCall:
376          name:
377            Ident:
378              - group
379          args:
380            - Tuple:
381                - Ident:
382                    - title
383                - Ident:
384                    - country
385            - FuncCall:
386                name:
387                  Ident:
388                    - aggregate
389                args:
390                  - Tuple:
391                      - FuncCall:
392                          name:
393                            Ident:
394                              - sum
395                          args:
396                            - Ident:
397                                - salary
398        "###);
399        assert_yaml_snapshot!(parse_expr(
400            r#"    filter country == "USA""#
401        ).unwrap(), @r###"
402        ---
403        FuncCall:
404          name:
405            Ident:
406              - filter
407          args:
408            - Binary:
409                left:
410                  Ident:
411                    - country
412                op: Eq
413                right:
414                  Literal:
415                    String: USA
416        "###);
417        assert_yaml_snapshot!(parse_expr("{a, b, c,}").unwrap(), @r###"
418        ---
419        Tuple:
420          - Ident:
421              - a
422          - Ident:
423              - b
424          - Ident:
425              - c
426        "###);
427        assert_yaml_snapshot!(parse_expr(
428            r#"{
429  gross_salary = salary + payroll_tax,
430  gross_cost   = gross_salary + benefits_cost
431}"#
432        ).unwrap(), @r###"
433        ---
434        Tuple:
435          - Binary:
436              left:
437                Ident:
438                  - salary
439              op: Add
440              right:
441                Ident:
442                  - payroll_tax
443            alias: gross_salary
444          - Binary:
445              left:
446                Ident:
447                  - gross_salary
448              op: Add
449              right:
450                Ident:
451                  - benefits_cost
452            alias: gross_cost
453        "###);
454        // Currently not putting comments in our parse tree, so this is blank.
455        assert_yaml_snapshot!(parse_single(
456            r#"# this is a comment
457        select a"#
458        ).unwrap(), @r###"
459        ---
460        - Main:
461            FuncCall:
462              name:
463                Ident:
464                  - select
465              args:
466                - Ident:
467                    - a
468          annotations: []
469        "###);
470        assert_yaml_snapshot!(parse_expr(
471            "join side:left country (id==employee_id)"
472        ).unwrap(), @r###"
473        ---
474        FuncCall:
475          name:
476            Ident:
477              - join
478          args:
479            - Ident:
480                - country
481            - Binary:
482                left:
483                  Ident:
484                    - id
485                op: Eq
486                right:
487                  Ident:
488                    - employee_id
489          named_args:
490            side:
491              Ident:
492                - left
493        "###);
494        assert_yaml_snapshot!(parse_expr("1  + 2").unwrap(), @r###"
495        ---
496        Binary:
497          left:
498            Literal:
499              Integer: 1
500          op: Add
501          right:
502            Literal:
503              Integer: 2
504        "###)
505    }
506
507    #[test]
508    fn test_string() {
509        let double_quoted_ast = parse_expr(r#"" U S A ""#).unwrap();
510        assert_yaml_snapshot!(double_quoted_ast, @r###"
511        ---
512        Literal:
513          String: " U S A "
514        "###);
515
516        let single_quoted_ast = parse_expr(r#"' U S A '"#).unwrap();
517        assert_eq!(single_quoted_ast, double_quoted_ast);
518
519        // Single quotes within double quotes should produce a string containing
520        // the single quotes (and vice versa).
521        assert_yaml_snapshot!(parse_expr(r#""' U S A '""#).unwrap(), @r###"
522        ---
523        Literal:
524          String: "' U S A '"
525        "###);
526        assert_yaml_snapshot!(parse_expr(r#"'" U S A "'"#).unwrap(), @r###"
527        ---
528        Literal:
529          String: "\" U S A \""
530        "###);
531
532        parse_expr(r#"" U S A"#).unwrap_err();
533        parse_expr(r#"" U S A '"#).unwrap_err();
534
535        assert_yaml_snapshot!(parse_expr(r#"" \nU S A ""#).unwrap(), @r###"
536        ---
537        Literal:
538          String: " \nU S A "
539        "###);
540
541        assert_yaml_snapshot!(parse_expr(r#"r" \nU S A ""#).unwrap(), @r###"
542        ---
543        Literal:
544          String: " \\nU S A "
545        "###);
546
547        let multi_double = parse_expr(
548            r#""""
549''
550Canada
551"
552
553""""#,
554        )
555        .unwrap();
556        assert_yaml_snapshot!(multi_double, @r###"
557        ---
558        Literal:
559          String: "\n''\nCanada\n\"\n\n"
560        "###);
561
562        let multi_single = parse_expr(
563            r#"'''
564Canada
565"
566"""
567
568'''"#,
569        )
570        .unwrap();
571        assert_yaml_snapshot!(multi_single, @r###"
572        ---
573        Literal:
574          String: "\nCanada\n\"\n\"\"\"\n\n"
575        "###);
576
577        assert_yaml_snapshot!(
578          parse_expr("''").unwrap(),
579          @r###"
580        ---
581        Literal:
582          String: ""
583        "###);
584    }
585
586    #[test]
587    fn test_s_string() {
588        assert_yaml_snapshot!(parse_expr(r#"s"SUM({col})""#).unwrap(), @r###"
589        ---
590        SString:
591          - String: SUM(
592          - Expr:
593              expr:
594                Ident:
595                  - col
596              format: ~
597          - String: )
598        "###);
599        assert_yaml_snapshot!(parse_expr(r#"s"SUM({rel.`Col name`})""#).unwrap(), @r###"
600        ---
601        SString:
602          - String: SUM(
603          - Expr:
604              expr:
605                Ident:
606                  - rel
607                  - Col name
608              format: ~
609          - String: )
610        "###)
611    }
612
613    #[test]
614    fn test_s_string_braces() {
615        assert_yaml_snapshot!(parse_expr(r#"s"{{?crystal_var}}""#).unwrap(), @r###"
616        ---
617        SString:
618          - String: "{?crystal_var}"
619        "###);
620        assert_yaml_snapshot!(parse_expr(r#"s"foo{{bar""#).unwrap(), @r###"
621        ---
622        SString:
623          - String: "foo{bar"
624        "###);
625        parse_expr(r#"s"foo{{bar}""#).unwrap_err();
626    }
627
628    #[test]
629    fn test_tuple() {
630        assert_yaml_snapshot!(parse_expr(r#"{1 + 1, 2}"#).unwrap(), @r###"
631        ---
632        Tuple:
633          - Binary:
634              left:
635                Literal:
636                  Integer: 1
637              op: Add
638              right:
639                Literal:
640                  Integer: 1
641          - Literal:
642              Integer: 2
643        "###);
644        assert_yaml_snapshot!(parse_expr(r#"{1 + (f 1), 2}"#).unwrap(), @r###"
645        ---
646        Tuple:
647          - Binary:
648              left:
649                Literal:
650                  Integer: 1
651              op: Add
652              right:
653                FuncCall:
654                  name:
655                    Ident:
656                      - f
657                  args:
658                    - Literal:
659                        Integer: 1
660          - Literal:
661              Integer: 2
662        "###);
663        // Line breaks
664        assert_yaml_snapshot!(parse_expr(
665            r#"{1,
666
667                2}"#
668        ).unwrap(), @r###"
669        ---
670        Tuple:
671          - Literal:
672              Integer: 1
673          - Literal:
674              Integer: 2
675        "###);
676        // Function call in a tuple
677        let ab = parse_expr(r#"{a b}"#).unwrap();
678        let a_comma_b = parse_expr(r#"{a, b}"#).unwrap();
679        assert_yaml_snapshot!(ab, @r###"
680        ---
681        Tuple:
682          - FuncCall:
683              name:
684                Ident:
685                  - a
686              args:
687                - Ident:
688                    - b
689        "###);
690        assert_yaml_snapshot!(a_comma_b, @r###"
691        ---
692        Tuple:
693          - Ident:
694              - a
695          - Ident:
696              - b
697        "###);
698        assert_ne!(ab, a_comma_b);
699
700        assert_yaml_snapshot!(parse_expr(r#"{amount, +amount, -amount}"#).unwrap(), @r###"
701        ---
702        Tuple:
703          - Ident:
704              - amount
705          - Unary:
706              op: Add
707              expr:
708                Ident:
709                  - amount
710          - Unary:
711              op: Neg
712              expr:
713                Ident:
714                  - amount
715        "###);
716        // Operators in tuple items
717        assert_yaml_snapshot!(parse_expr(r#"{amount, +amount, -amount}"#).unwrap(), @r###"
718        ---
719        Tuple:
720          - Ident:
721              - amount
722          - Unary:
723              op: Add
724              expr:
725                Ident:
726                  - amount
727          - Unary:
728              op: Neg
729              expr:
730                Ident:
731                  - amount
732        "###);
733    }
734
735    #[test]
736    fn test_number() {
737        assert_yaml_snapshot!(parse_expr(r#"23"#).unwrap(), @r###"
738        ---
739        Literal:
740          Integer: 23
741        "###);
742        assert_yaml_snapshot!(parse_expr(r#"2_3_4.5_6"#).unwrap(), @r###"
743        ---
744        Literal:
745          Float: 234.56
746        "###);
747        assert_yaml_snapshot!(parse_expr(r#"23.6"#).unwrap(), @r###"
748        ---
749        Literal:
750          Float: 23.6
751        "###);
752        assert_yaml_snapshot!(parse_expr(r#"23.0"#).unwrap(), @r###"
753        ---
754        Literal:
755          Float: 23
756        "###);
757        assert_yaml_snapshot!(parse_expr(r#"2 + 2"#).unwrap(), @r###"
758        ---
759        Binary:
760          left:
761            Literal:
762              Integer: 2
763          op: Add
764          right:
765            Literal:
766              Integer: 2
767        "###);
768
769        // Underscores at the beginning are parsed as ident
770        assert!(parse_expr("_2").unwrap().kind.into_ident().is_ok());
771        assert!(parse_expr("_").unwrap().kind.into_ident().is_ok());
772
773        // We don't allow trailing periods
774        assert!(parse_expr(r#"add 1. 2"#).is_err());
775
776        assert!(parse_expr("_2.3").is_err());
777
778        assert_yaml_snapshot!(parse_expr(r#"2e3"#).unwrap(), @r###"
779        ---
780        Literal:
781          Float: 2000
782        "###);
783
784        // expr_of_string("2_").unwrap_err(); // TODO
785        // expr_of_string("2.3_").unwrap_err(); // TODO
786    }
787
788    #[test]
789    fn test_filter() {
790        assert_yaml_snapshot!(
791            parse_single(r#"filter country == "USA""#).unwrap(), @r###"
792        ---
793        - Main:
794            FuncCall:
795              name:
796                Ident:
797                  - filter
798              args:
799                - Binary:
800                    left:
801                      Ident:
802                        - country
803                    op: Eq
804                    right:
805                      Literal:
806                        String: USA
807          annotations: []
808        "###);
809
810        assert_yaml_snapshot!(
811            parse_single(r#"filter (upper country) == "USA""#).unwrap(), @r###"
812        ---
813        - Main:
814            FuncCall:
815              name:
816                Ident:
817                  - filter
818              args:
819                - Binary:
820                    left:
821                      FuncCall:
822                        name:
823                          Ident:
824                            - upper
825                        args:
826                          - Ident:
827                              - country
828                    op: Eq
829                    right:
830                      Literal:
831                        String: USA
832          annotations: []
833        "###
834        );
835    }
836
837    #[test]
838    fn test_aggregate() {
839        let aggregate = parse_single(
840            r"group {title} (
841                aggregate {sum salary, count}
842              )",
843        )
844        .unwrap();
845        assert_yaml_snapshot!(
846            aggregate, @r###"
847        ---
848        - Main:
849            FuncCall:
850              name:
851                Ident:
852                  - group
853              args:
854                - Tuple:
855                    - Ident:
856                        - title
857                - FuncCall:
858                    name:
859                      Ident:
860                        - aggregate
861                    args:
862                      - Tuple:
863                          - FuncCall:
864                              name:
865                                Ident:
866                                  - sum
867                              args:
868                                - Ident:
869                                    - salary
870                          - Ident:
871                              - count
872          annotations: []
873        "###);
874        let aggregate = parse_single(
875            r"group {title} (
876                aggregate {sum salary}
877              )",
878        )
879        .unwrap();
880        assert_yaml_snapshot!(
881            aggregate, @r###"
882        ---
883        - Main:
884            FuncCall:
885              name:
886                Ident:
887                  - group
888              args:
889                - Tuple:
890                    - Ident:
891                        - title
892                - FuncCall:
893                    name:
894                      Ident:
895                        - aggregate
896                    args:
897                      - Tuple:
898                          - FuncCall:
899                              name:
900                                Ident:
901                                  - sum
902                              args:
903                                - Ident:
904                                    - salary
905          annotations: []
906        "###);
907    }
908
909    #[test]
910    fn test_derive() {
911        assert_yaml_snapshot!(
912            parse_expr(r#"derive {x = 5, y = (-x)}"#).unwrap()
913        , @r###"
914        ---
915        FuncCall:
916          name:
917            Ident:
918              - derive
919          args:
920            - Tuple:
921                - Literal:
922                    Integer: 5
923                  alias: x
924                - Unary:
925                    op: Neg
926                    expr:
927                      Ident:
928                        - x
929                  alias: y
930        "###);
931    }
932
933    #[test]
934    fn test_select() {
935        assert_yaml_snapshot!(
936            parse_expr(r#"select x"#).unwrap()
937        , @r###"
938        ---
939        FuncCall:
940          name:
941            Ident:
942              - select
943          args:
944            - Ident:
945                - x
946        "###);
947
948        assert_yaml_snapshot!(
949            parse_expr(r#"select !{x}"#).unwrap()
950        , @r###"
951        ---
952        FuncCall:
953          name:
954            Ident:
955              - select
956          args:
957            - Unary:
958                op: Not
959                expr:
960                  Tuple:
961                    - Ident:
962                        - x
963        "###);
964
965        assert_yaml_snapshot!(
966            parse_expr(r#"select {x, y}"#).unwrap()
967        , @r###"
968        ---
969        FuncCall:
970          name:
971            Ident:
972              - select
973          args:
974            - Tuple:
975                - Ident:
976                    - x
977                - Ident:
978                    - y
979        "###);
980    }
981
982    #[test]
983    fn test_expr() {
984        assert_yaml_snapshot!(
985            parse_expr(r#"country == "USA""#).unwrap()
986        , @r###"
987        ---
988        Binary:
989          left:
990            Ident:
991              - country
992          op: Eq
993          right:
994            Literal:
995              String: USA
996        "###);
997        assert_yaml_snapshot!(parse_expr(
998                r#"{
999  gross_salary = salary + payroll_tax,
1000  gross_cost   = gross_salary + benefits_cost,
1001}"#).unwrap(), @r###"
1002        ---
1003        Tuple:
1004          - Binary:
1005              left:
1006                Ident:
1007                  - salary
1008              op: Add
1009              right:
1010                Ident:
1011                  - payroll_tax
1012            alias: gross_salary
1013          - Binary:
1014              left:
1015                Ident:
1016                  - gross_salary
1017              op: Add
1018              right:
1019                Ident:
1020                  - benefits_cost
1021            alias: gross_cost
1022        "###);
1023        assert_yaml_snapshot!(
1024            parse_expr(
1025                "(salary + payroll_tax) * (1 + tax_rate)"
1026            ).unwrap(),
1027            @r###"
1028        ---
1029        Binary:
1030          left:
1031            Binary:
1032              left:
1033                Ident:
1034                  - salary
1035              op: Add
1036              right:
1037                Ident:
1038                  - payroll_tax
1039          op: Mul
1040          right:
1041            Binary:
1042              left:
1043                Literal:
1044                  Integer: 1
1045              op: Add
1046              right:
1047                Ident:
1048                  - tax_rate
1049        "###);
1050    }
1051
1052    #[test]
1053    fn test_regex() {
1054        assert_yaml_snapshot!(
1055            parse_expr(
1056                "'oba' ~= 'foobar'"
1057            ).unwrap(),
1058            @r###"
1059        ---
1060        Binary:
1061          left:
1062            Literal:
1063              String: oba
1064          op: RegexSearch
1065          right:
1066            Literal:
1067              String: foobar
1068        "###);
1069    }
1070
1071    #[test]
1072    fn test_function() {
1073        assert_yaml_snapshot!(parse_single("let plus_one = x ->  x + 1\n").unwrap(), @r###"
1074        ---
1075        - VarDef:
1076            name: plus_one
1077            value:
1078              Func:
1079                return_ty: ~
1080                body:
1081                  Binary:
1082                    left:
1083                      Ident:
1084                        - x
1085                    op: Add
1086                    right:
1087                      Literal:
1088                        Integer: 1
1089                params:
1090                  - name: x
1091                    default_value: ~
1092                named_params: []
1093            ty_expr: ~
1094            kind: Let
1095          annotations: []
1096        "###);
1097        assert_yaml_snapshot!(parse_single("let identity = x ->  x\n").unwrap()
1098        , @r###"
1099        ---
1100        - VarDef:
1101            name: identity
1102            value:
1103              Func:
1104                return_ty: ~
1105                body:
1106                  Ident:
1107                    - x
1108                params:
1109                  - name: x
1110                    default_value: ~
1111                named_params: []
1112            ty_expr: ~
1113            kind: Let
1114          annotations: []
1115        "###);
1116        assert_yaml_snapshot!(parse_single("let plus_one = x ->  (x + 1)\n").unwrap()
1117        , @r###"
1118        ---
1119        - VarDef:
1120            name: plus_one
1121            value:
1122              Func:
1123                return_ty: ~
1124                body:
1125                  Binary:
1126                    left:
1127                      Ident:
1128                        - x
1129                    op: Add
1130                    right:
1131                      Literal:
1132                        Integer: 1
1133                params:
1134                  - name: x
1135                    default_value: ~
1136                named_params: []
1137            ty_expr: ~
1138            kind: Let
1139          annotations: []
1140        "###);
1141        assert_yaml_snapshot!(parse_single("let plus_one = x ->  x + 1\n").unwrap()
1142        , @r###"
1143        ---
1144        - VarDef:
1145            name: plus_one
1146            value:
1147              Func:
1148                return_ty: ~
1149                body:
1150                  Binary:
1151                    left:
1152                      Ident:
1153                        - x
1154                    op: Add
1155                    right:
1156                      Literal:
1157                        Integer: 1
1158                params:
1159                  - name: x
1160                    default_value: ~
1161                named_params: []
1162            ty_expr: ~
1163            kind: Let
1164          annotations: []
1165        "###);
1166
1167        assert_yaml_snapshot!(parse_single("let foo = x -> some_func (foo bar + 1) (plax) - baz\n").unwrap()
1168        , @r###"
1169        ---
1170        - VarDef:
1171            name: foo
1172            value:
1173              Func:
1174                return_ty: ~
1175                body:
1176                  FuncCall:
1177                    name:
1178                      Ident:
1179                        - some_func
1180                    args:
1181                      - FuncCall:
1182                          name:
1183                            Ident:
1184                              - foo
1185                          args:
1186                            - Binary:
1187                                left:
1188                                  Ident:
1189                                    - bar
1190                                op: Add
1191                                right:
1192                                  Literal:
1193                                    Integer: 1
1194                      - Binary:
1195                          left:
1196                            Ident:
1197                              - plax
1198                          op: Sub
1199                          right:
1200                            Ident:
1201                              - baz
1202                params:
1203                  - name: x
1204                    default_value: ~
1205                named_params: []
1206            ty_expr: ~
1207            kind: Let
1208          annotations: []
1209        "###);
1210
1211        assert_yaml_snapshot!(parse_single("func return_constant ->  42\n").unwrap(), @r###"
1212        ---
1213        - Main:
1214            Func:
1215              return_ty: ~
1216              body:
1217                Literal:
1218                  Integer: 42
1219              params:
1220                - name: return_constant
1221                  default_value: ~
1222              named_params: []
1223          annotations: []
1224        "###);
1225
1226        assert_yaml_snapshot!(parse_single(r#"let count = X -> s"SUM({X})"
1227        "#).unwrap(), @r###"
1228        ---
1229        - VarDef:
1230            name: count
1231            value:
1232              Func:
1233                return_ty: ~
1234                body:
1235                  SString:
1236                    - String: SUM(
1237                    - Expr:
1238                        expr:
1239                          Ident:
1240                            - X
1241                        format: ~
1242                    - String: )
1243                params:
1244                  - name: X
1245                    default_value: ~
1246                named_params: []
1247            ty_expr: ~
1248            kind: Let
1249          annotations: []
1250        "###);
1251
1252        assert_yaml_snapshot!(parse_single(
1253            r#"
1254            let lag_day = x ->  (
1255                window x
1256                by sec_id
1257                sort date
1258                lag 1
1259            )
1260        "#
1261        )
1262        .unwrap(), @r###"
1263        ---
1264        - VarDef:
1265            name: lag_day
1266            value:
1267              Func:
1268                return_ty: ~
1269                body:
1270                  Pipeline:
1271                    exprs:
1272                      - FuncCall:
1273                          name:
1274                            Ident:
1275                              - window
1276                          args:
1277                            - Ident:
1278                                - x
1279                      - FuncCall:
1280                          name:
1281                            Ident:
1282                              - by
1283                          args:
1284                            - Ident:
1285                                - sec_id
1286                      - FuncCall:
1287                          name:
1288                            Ident:
1289                              - sort
1290                          args:
1291                            - Ident:
1292                                - date
1293                      - FuncCall:
1294                          name:
1295                            Ident:
1296                              - lag
1297                          args:
1298                            - Literal:
1299                                Integer: 1
1300                params:
1301                  - name: x
1302                    default_value: ~
1303                named_params: []
1304            ty_expr: ~
1305            kind: Let
1306          annotations: []
1307        "###);
1308
1309        assert_yaml_snapshot!(parse_single("let add = x to:a ->  x + to\n").unwrap(), @r###"
1310        ---
1311        - VarDef:
1312            name: add
1313            value:
1314              Func:
1315                return_ty: ~
1316                body:
1317                  Binary:
1318                    left:
1319                      Ident:
1320                        - x
1321                    op: Add
1322                    right:
1323                      Ident:
1324                        - to
1325                params:
1326                  - name: x
1327                    default_value: ~
1328                named_params:
1329                  - name: to
1330                    default_value:
1331                      Ident:
1332                        - a
1333            ty_expr: ~
1334            kind: Let
1335          annotations: []
1336        "###);
1337    }
1338
1339    #[test]
1340    fn test_func_call() {
1341        // Function without argument
1342        let ast = parse_expr(r#"count"#).unwrap();
1343        let ident = ast.kind.into_ident().unwrap();
1344        assert_yaml_snapshot!(
1345            ident, @r###"
1346        ---
1347        - count
1348        "###);
1349
1350        let ast = parse_expr(r#"s 'foo'"#).unwrap();
1351        assert_yaml_snapshot!(
1352            ast, @r###"
1353        ---
1354        FuncCall:
1355          name:
1356            Ident:
1357              - s
1358          args:
1359            - Literal:
1360                String: foo
1361        "###);
1362
1363        // A non-friendly option for #154
1364        let ast = parse_expr(r#"count s'*'"#).unwrap();
1365        let func_call: FuncCall = ast.kind.into_func_call().unwrap();
1366        assert_yaml_snapshot!(
1367            func_call, @r###"
1368        ---
1369        name:
1370          Ident:
1371            - count
1372        args:
1373          - SString:
1374              - String: "*"
1375        "###);
1376
1377        parse_expr("plus_one x:0 x:0 ").unwrap_err();
1378
1379        let ast = parse_expr(r#"add bar to=3"#).unwrap();
1380        assert_yaml_snapshot!(
1381            ast, @r###"
1382        ---
1383        FuncCall:
1384          name:
1385            Ident:
1386              - add
1387          args:
1388            - Ident:
1389                - bar
1390            - Literal:
1391                Integer: 3
1392              alias: to
1393        "###);
1394    }
1395
1396    #[test]
1397    fn test_op_precedence() {
1398        assert_yaml_snapshot!(parse_expr(r#"1 + 2 - 3 - 4"#).unwrap(), @r###"
1399        ---
1400        Binary:
1401          left:
1402            Binary:
1403              left:
1404                Binary:
1405                  left:
1406                    Literal:
1407                      Integer: 1
1408                  op: Add
1409                  right:
1410                    Literal:
1411                      Integer: 2
1412              op: Sub
1413              right:
1414                Literal:
1415                  Integer: 3
1416          op: Sub
1417          right:
1418            Literal:
1419              Integer: 4
1420        "###);
1421
1422        assert_yaml_snapshot!(parse_expr(r#"1 / 2 - 3 * 4 + 1"#).unwrap(), @r###"
1423        ---
1424        Binary:
1425          left:
1426            Binary:
1427              left:
1428                Binary:
1429                  left:
1430                    Literal:
1431                      Integer: 1
1432                  op: DivFloat
1433                  right:
1434                    Literal:
1435                      Integer: 2
1436              op: Sub
1437              right:
1438                Binary:
1439                  left:
1440                    Literal:
1441                      Integer: 3
1442                  op: Mul
1443                  right:
1444                    Literal:
1445                      Integer: 4
1446          op: Add
1447          right:
1448            Literal:
1449              Integer: 1
1450        "###);
1451
1452        assert_yaml_snapshot!(parse_expr(r#"a && b || !c && d"#).unwrap(), @r###"
1453        ---
1454        Binary:
1455          left:
1456            Binary:
1457              left:
1458                Ident:
1459                  - a
1460              op: And
1461              right:
1462                Ident:
1463                  - b
1464          op: Or
1465          right:
1466            Binary:
1467              left:
1468                Unary:
1469                  op: Not
1470                  expr:
1471                    Ident:
1472                      - c
1473              op: And
1474              right:
1475                Ident:
1476                  - d
1477        "###);
1478
1479        assert_yaml_snapshot!(parse_expr(r#"a && b + c || (d e) && f"#).unwrap(), @r###"
1480        ---
1481        Binary:
1482          left:
1483            Binary:
1484              left:
1485                Ident:
1486                  - a
1487              op: And
1488              right:
1489                Binary:
1490                  left:
1491                    Ident:
1492                      - b
1493                  op: Add
1494                  right:
1495                    Ident:
1496                      - c
1497          op: Or
1498          right:
1499            Binary:
1500              left:
1501                FuncCall:
1502                  name:
1503                    Ident:
1504                      - d
1505                  args:
1506                    - Ident:
1507                        - e
1508              op: And
1509              right:
1510                Ident:
1511                  - f
1512        "###);
1513    }
1514
1515    #[test]
1516    fn test_var_def() {
1517        assert_yaml_snapshot!(parse_single(
1518            "let newest_employees = (from employees)"
1519        ).unwrap(), @r###"
1520        ---
1521        - VarDef:
1522            name: newest_employees
1523            value:
1524              FuncCall:
1525                name:
1526                  Ident:
1527                    - from
1528                args:
1529                  - Ident:
1530                      - employees
1531            ty_expr: ~
1532            kind: Let
1533          annotations: []
1534        "###);
1535
1536        assert_yaml_snapshot!(parse_single(
1537            r#"
1538        let newest_employees = (
1539          from employees
1540          group country (
1541            aggregate {
1542                average_country_salary = average salary
1543            }
1544          )
1545          sort tenure
1546          take 50
1547        )"#.trim()).unwrap(),
1548         @r###"
1549        ---
1550        - VarDef:
1551            name: newest_employees
1552            value:
1553              Pipeline:
1554                exprs:
1555                  - FuncCall:
1556                      name:
1557                        Ident:
1558                          - from
1559                      args:
1560                        - Ident:
1561                            - employees
1562                  - FuncCall:
1563                      name:
1564                        Ident:
1565                          - group
1566                      args:
1567                        - Ident:
1568                            - country
1569                        - FuncCall:
1570                            name:
1571                              Ident:
1572                                - aggregate
1573                            args:
1574                              - Tuple:
1575                                  - FuncCall:
1576                                      name:
1577                                        Ident:
1578                                          - average
1579                                      args:
1580                                        - Ident:
1581                                            - salary
1582                                    alias: average_country_salary
1583                  - FuncCall:
1584                      name:
1585                        Ident:
1586                          - sort
1587                      args:
1588                        - Ident:
1589                            - tenure
1590                  - FuncCall:
1591                      name:
1592                        Ident:
1593                          - take
1594                      args:
1595                        - Literal:
1596                            Integer: 50
1597            ty_expr: ~
1598            kind: Let
1599          annotations: []
1600        "###);
1601
1602        assert_yaml_snapshot!(parse_single(r#"
1603            let e = s"SELECT * FROM employees"
1604            "#).unwrap(), @r###"
1605        ---
1606        - VarDef:
1607            name: e
1608            value:
1609              SString:
1610                - String: SELECT * FROM employees
1611            ty_expr: ~
1612            kind: Let
1613          annotations: []
1614        "###);
1615
1616        assert_yaml_snapshot!(parse_single(
1617          "let x = (
1618
1619            from x_table
1620
1621            select only_in_x = foo
1622
1623          )
1624
1625          from x"
1626        ).unwrap(), @r###"
1627        ---
1628        - VarDef:
1629            name: x
1630            value:
1631              Pipeline:
1632                exprs:
1633                  - FuncCall:
1634                      name:
1635                        Ident:
1636                          - from
1637                      args:
1638                        - Ident:
1639                            - x_table
1640                  - FuncCall:
1641                      name:
1642                        Ident:
1643                          - select
1644                      args:
1645                        - Ident:
1646                            - foo
1647                          alias: only_in_x
1648            ty_expr: ~
1649            kind: Let
1650          annotations: []
1651        - Main:
1652            FuncCall:
1653              name:
1654                Ident:
1655                  - from
1656              args:
1657                - Ident:
1658                    - x
1659          annotations: []
1660        "###);
1661    }
1662
1663    #[test]
1664    fn test_inline_pipeline() {
1665        assert_yaml_snapshot!(parse_expr("(salary | percentile 50)").unwrap(), @r###"
1666        ---
1667        Pipeline:
1668          exprs:
1669            - Ident:
1670                - salary
1671            - FuncCall:
1672                name:
1673                  Ident:
1674                    - percentile
1675                args:
1676                  - Literal:
1677                      Integer: 50
1678        "###);
1679        assert_yaml_snapshot!(parse_single("let median = x -> (x | percentile 50)\n").unwrap(), @r###"
1680        ---
1681        - VarDef:
1682            name: median
1683            value:
1684              Func:
1685                return_ty: ~
1686                body:
1687                  Pipeline:
1688                    exprs:
1689                      - Ident:
1690                          - x
1691                      - FuncCall:
1692                          name:
1693                            Ident:
1694                              - percentile
1695                          args:
1696                            - Literal:
1697                                Integer: 50
1698                params:
1699                  - name: x
1700                    default_value: ~
1701                named_params: []
1702            ty_expr: ~
1703            kind: Let
1704          annotations: []
1705        "###);
1706    }
1707
1708    #[test]
1709    fn test_sql_parameters() {
1710        assert_yaml_snapshot!(parse_single(r#"
1711        from mytable
1712        filter {
1713          first_name == $1,
1714          last_name == $2.name
1715        }
1716        "#).unwrap(), @r###"
1717        ---
1718        - Main:
1719            Pipeline:
1720              exprs:
1721                - FuncCall:
1722                    name:
1723                      Ident:
1724                        - from
1725                    args:
1726                      - Ident:
1727                          - mytable
1728                - FuncCall:
1729                    name:
1730                      Ident:
1731                        - filter
1732                    args:
1733                      - Tuple:
1734                          - Binary:
1735                              left:
1736                                Ident:
1737                                  - first_name
1738                              op: Eq
1739                              right:
1740                                Param: "1"
1741                          - Binary:
1742                              left:
1743                                Ident:
1744                                  - last_name
1745                              op: Eq
1746                              right:
1747                                Param: 2.name
1748          annotations: []
1749        "###);
1750    }
1751
1752    #[test]
1753    fn test_tab_characters() {
1754        // #284
1755        parse_single(
1756            "from c_invoice
1757join doc:c_doctype (==c_invoice_id)
1758select [
1759\tinvoice_no,
1760\tdocstatus
1761]",
1762        )
1763        .unwrap();
1764    }
1765
1766    #[test]
1767    fn test_backticks() {
1768        let prql = "
1769from `a/*.parquet`
1770aggregate {max c}
1771join `schema.table` (==id)
1772join `my-proj.dataset.table`
1773join `my-proj`.`dataset`.`table`
1774";
1775
1776        assert_yaml_snapshot!(parse_single(prql).unwrap(), @r###"
1777        ---
1778        - Main:
1779            Pipeline:
1780              exprs:
1781                - FuncCall:
1782                    name:
1783                      Ident:
1784                        - from
1785                    args:
1786                      - Ident:
1787                          - a/*.parquet
1788                - FuncCall:
1789                    name:
1790                      Ident:
1791                        - aggregate
1792                    args:
1793                      - Tuple:
1794                          - FuncCall:
1795                              name:
1796                                Ident:
1797                                  - max
1798                              args:
1799                                - Ident:
1800                                    - c
1801                - FuncCall:
1802                    name:
1803                      Ident:
1804                        - join
1805                    args:
1806                      - Ident:
1807                          - schema.table
1808                      - Unary:
1809                          op: EqSelf
1810                          expr:
1811                            Ident:
1812                              - id
1813                - FuncCall:
1814                    name:
1815                      Ident:
1816                        - join
1817                    args:
1818                      - Ident:
1819                          - my-proj.dataset.table
1820                - FuncCall:
1821                    name:
1822                      Ident:
1823                        - join
1824                    args:
1825                      - Ident:
1826                          - my-proj
1827                          - dataset
1828                          - table
1829          annotations: []
1830        "###);
1831    }
1832
1833    #[test]
1834    fn test_sort() {
1835        assert_yaml_snapshot!(parse_single("
1836        from invoices
1837        sort issued_at
1838        sort (-issued_at)
1839        sort {issued_at}
1840        sort {-issued_at}
1841        sort {issued_at, -amount, +num_of_articles}
1842        ").unwrap(), @r###"
1843        ---
1844        - Main:
1845            Pipeline:
1846              exprs:
1847                - FuncCall:
1848                    name:
1849                      Ident:
1850                        - from
1851                    args:
1852                      - Ident:
1853                          - invoices
1854                - FuncCall:
1855                    name:
1856                      Ident:
1857                        - sort
1858                    args:
1859                      - Ident:
1860                          - issued_at
1861                - FuncCall:
1862                    name:
1863                      Ident:
1864                        - sort
1865                    args:
1866                      - Unary:
1867                          op: Neg
1868                          expr:
1869                            Ident:
1870                              - issued_at
1871                - FuncCall:
1872                    name:
1873                      Ident:
1874                        - sort
1875                    args:
1876                      - Tuple:
1877                          - Ident:
1878                              - issued_at
1879                - FuncCall:
1880                    name:
1881                      Ident:
1882                        - sort
1883                    args:
1884                      - Tuple:
1885                          - Unary:
1886                              op: Neg
1887                              expr:
1888                                Ident:
1889                                  - issued_at
1890                - FuncCall:
1891                    name:
1892                      Ident:
1893                        - sort
1894                    args:
1895                      - Tuple:
1896                          - Ident:
1897                              - issued_at
1898                          - Unary:
1899                              op: Neg
1900                              expr:
1901                                Ident:
1902                                  - amount
1903                          - Unary:
1904                              op: Add
1905                              expr:
1906                                Ident:
1907                                  - num_of_articles
1908          annotations: []
1909        "###);
1910    }
1911
1912    #[test]
1913    fn test_dates() {
1914        assert_yaml_snapshot!(parse_single("
1915        from employees
1916        derive {age_plus_two_years = (age + 2years)}
1917        ").unwrap(), @r###"
1918        ---
1919        - Main:
1920            Pipeline:
1921              exprs:
1922                - FuncCall:
1923                    name:
1924                      Ident:
1925                        - from
1926                    args:
1927                      - Ident:
1928                          - employees
1929                - FuncCall:
1930                    name:
1931                      Ident:
1932                        - derive
1933                    args:
1934                      - Tuple:
1935                          - Binary:
1936                              left:
1937                                Ident:
1938                                  - age
1939                              op: Add
1940                              right:
1941                                Literal:
1942                                  ValueAndUnit:
1943                                    n: 2
1944                                    unit: years
1945                            alias: age_plus_two_years
1946          annotations: []
1947        "###);
1948
1949        assert_yaml_snapshot!(parse_expr("@2011-02-01").unwrap(), @r###"
1950        ---
1951        Literal:
1952          Date: 2011-02-01
1953        "###);
1954        assert_yaml_snapshot!(parse_expr("@2011-02-01T10:00").unwrap(), @r###"
1955        ---
1956        Literal:
1957          Timestamp: "2011-02-01T10:00"
1958        "###);
1959        assert_yaml_snapshot!(parse_expr("@14:00").unwrap(), @r###"
1960        ---
1961        Literal:
1962          Time: "14:00"
1963        "###);
1964        // assert_yaml_snapshot!(parse_expr("@2011-02-01T10:00<datetime>").unwrap(), @"");
1965
1966        parse_expr("@2020-01-0").unwrap_err();
1967
1968        parse_expr("@2020-01-011").unwrap_err();
1969
1970        parse_expr("@2020-01-01T111").unwrap_err();
1971    }
1972
1973    #[test]
1974    fn test_multiline_string() {
1975        assert_yaml_snapshot!(parse_single(r##"
1976        derive x = r#"r-string test"#
1977        "##).unwrap(), @r###"
1978        ---
1979        - Main:
1980            FuncCall:
1981              name:
1982                Ident:
1983                  - derive
1984              args:
1985                - Ident:
1986                    - r
1987                  alias: x
1988          annotations: []
1989        "### )
1990    }
1991
1992    #[test]
1993    fn test_coalesce() {
1994        assert_yaml_snapshot!(parse_single(r###"
1995        from employees
1996        derive amount = amount ?? 0
1997        "###).unwrap(), @r###"
1998        ---
1999        - Main:
2000            Pipeline:
2001              exprs:
2002                - FuncCall:
2003                    name:
2004                      Ident:
2005                        - from
2006                    args:
2007                      - Ident:
2008                          - employees
2009                - FuncCall:
2010                    name:
2011                      Ident:
2012                        - derive
2013                    args:
2014                      - Binary:
2015                          left:
2016                            Ident:
2017                              - amount
2018                          op: Coalesce
2019                          right:
2020                            Literal:
2021                              Integer: 0
2022                        alias: amount
2023          annotations: []
2024        "### )
2025    }
2026
2027    #[test]
2028    fn test_literal() {
2029        assert_yaml_snapshot!(parse_single(r###"
2030        derive x = true
2031        "###).unwrap(), @r###"
2032        ---
2033        - Main:
2034            FuncCall:
2035              name:
2036                Ident:
2037                  - derive
2038              args:
2039                - Literal:
2040                    Boolean: true
2041                  alias: x
2042          annotations: []
2043        "###)
2044    }
2045
2046    #[test]
2047    fn test_allowed_idents() {
2048        assert_yaml_snapshot!(parse_single(r###"
2049        from employees
2050        join _salary (==employee_id) # table with leading underscore
2051        filter first_name == $1
2052        select {_employees._underscored_column}
2053        "###).unwrap(), @r###"
2054        ---
2055        - Main:
2056            Pipeline:
2057              exprs:
2058                - FuncCall:
2059                    name:
2060                      Ident:
2061                        - from
2062                    args:
2063                      - Ident:
2064                          - employees
2065                - FuncCall:
2066                    name:
2067                      Ident:
2068                        - join
2069                    args:
2070                      - Ident:
2071                          - _salary
2072                      - Unary:
2073                          op: EqSelf
2074                          expr:
2075                            Ident:
2076                              - employee_id
2077                - FuncCall:
2078                    name:
2079                      Ident:
2080                        - filter
2081                    args:
2082                      - Binary:
2083                          left:
2084                            Ident:
2085                              - first_name
2086                          op: Eq
2087                          right:
2088                            Param: "1"
2089                - FuncCall:
2090                    name:
2091                      Ident:
2092                        - select
2093                    args:
2094                      - Tuple:
2095                          - Ident:
2096                              - _employees
2097                              - _underscored_column
2098          annotations: []
2099        "###)
2100    }
2101
2102    #[test]
2103    fn test_gt_lt_gte_lte() {
2104        assert_yaml_snapshot!(parse_single(r###"
2105        from people
2106        filter age >= 100
2107        filter num_grandchildren <= 10
2108        filter salary > 0
2109        filter num_eyes < 2
2110        "###).unwrap(), @r###"
2111        ---
2112        - Main:
2113            Pipeline:
2114              exprs:
2115                - FuncCall:
2116                    name:
2117                      Ident:
2118                        - from
2119                    args:
2120                      - Ident:
2121                          - people
2122                - FuncCall:
2123                    name:
2124                      Ident:
2125                        - filter
2126                    args:
2127                      - Binary:
2128                          left:
2129                            Ident:
2130                              - age
2131                          op: Gte
2132                          right:
2133                            Literal:
2134                              Integer: 100
2135                - FuncCall:
2136                    name:
2137                      Ident:
2138                        - filter
2139                    args:
2140                      - Binary:
2141                          left:
2142                            Ident:
2143                              - num_grandchildren
2144                          op: Lte
2145                          right:
2146                            Literal:
2147                              Integer: 10
2148                - FuncCall:
2149                    name:
2150                      Ident:
2151                        - filter
2152                    args:
2153                      - Binary:
2154                          left:
2155                            Ident:
2156                              - salary
2157                          op: Gt
2158                          right:
2159                            Literal:
2160                              Integer: 0
2161                - FuncCall:
2162                    name:
2163                      Ident:
2164                        - filter
2165                    args:
2166                      - Binary:
2167                          left:
2168                            Ident:
2169                              - num_eyes
2170                          op: Lt
2171                          right:
2172                            Literal:
2173                              Integer: 2
2174          annotations: []
2175        "###)
2176    }
2177
2178    #[test]
2179    fn test_assign() {
2180        assert_yaml_snapshot!(parse_single(r###"
2181from employees
2182join s=salaries (==id)
2183        "###).unwrap(), @r###"
2184        ---
2185        - Main:
2186            Pipeline:
2187              exprs:
2188                - FuncCall:
2189                    name:
2190                      Ident:
2191                        - from
2192                    args:
2193                      - Ident:
2194                          - employees
2195                - FuncCall:
2196                    name:
2197                      Ident:
2198                        - join
2199                    args:
2200                      - Ident:
2201                          - salaries
2202                        alias: s
2203                      - Unary:
2204                          op: EqSelf
2205                          expr:
2206                            Ident:
2207                              - id
2208          annotations: []
2209        "###);
2210    }
2211
2212    #[test]
2213    fn test_ident_with_keywords() {
2214        assert_yaml_snapshot!(parse_expr(r"select {andrew, orion, lettuce, falsehood, null0}").unwrap(), @r###"
2215        ---
2216        FuncCall:
2217          name:
2218            Ident:
2219              - select
2220          args:
2221            - Tuple:
2222                - Ident:
2223                    - andrew
2224                - Ident:
2225                    - orion
2226                - Ident:
2227                    - lettuce
2228                - Ident:
2229                    - falsehood
2230                - Ident:
2231                    - null0
2232        "###);
2233
2234        assert_yaml_snapshot!(parse_expr(r"{false}").unwrap(), @r###"
2235        ---
2236        Tuple:
2237          - Literal:
2238              Boolean: false
2239        "###);
2240    }
2241
2242    #[test]
2243    fn test_case() {
2244        assert_yaml_snapshot!(parse_expr(r#"case {
2245            nickname != null => nickname,
2246            true => null
2247        }"#).unwrap(), @r###"
2248        ---
2249        Case:
2250          - condition:
2251              Binary:
2252                left:
2253                  Ident:
2254                    - nickname
2255                op: Ne
2256                right:
2257                  Literal: "Null"
2258            value:
2259              Ident:
2260                - nickname
2261          - condition:
2262              Literal:
2263                Boolean: true
2264            value:
2265              Literal: "Null"
2266        "###);
2267    }
2268
2269    #[test]
2270    fn test_params() {
2271        assert_yaml_snapshot!(parse_expr(r#"$2"#).unwrap(), @r###"
2272        ---
2273        Param: "2"
2274        "###);
2275
2276        assert_yaml_snapshot!(parse_expr(r#"$2_any_text"#).unwrap(), @r###"
2277        ---
2278        Param: 2_any_text
2279        "###);
2280    }
2281
2282    #[test]
2283    fn test_unicode() {
2284        let source = "from tète";
2285        assert_yaml_snapshot!(parse_single(source).unwrap(), @r###"
2286        ---
2287        - Main:
2288            FuncCall:
2289              name:
2290                Ident:
2291                  - from
2292              args:
2293                - Ident:
2294                    - tète
2295          annotations: []
2296        "###);
2297    }
2298
2299    #[test]
2300    fn test_var_defs() {
2301        assert_yaml_snapshot!(parse_single(r#"
2302        let a = (
2303            x
2304        )
2305        "#).unwrap(), @r###"
2306        ---
2307        - VarDef:
2308            name: a
2309            value:
2310              Ident:
2311                - x
2312            ty_expr: ~
2313            kind: Let
2314          annotations: []
2315        "###);
2316
2317        assert_yaml_snapshot!(parse_single(r#"
2318        x
2319        into a
2320        "#).unwrap(), @r###"
2321        ---
2322        - VarDef:
2323            name: a
2324            value:
2325              Ident:
2326                - x
2327            ty_expr: ~
2328            kind: Into
2329          annotations: []
2330        "###);
2331
2332        assert_yaml_snapshot!(parse_single(r#"
2333        x
2334        "#).unwrap(), @r###"
2335        ---
2336        - Main:
2337            Ident:
2338              - x
2339          annotations: []
2340        "###);
2341    }
2342
2343    #[test]
2344    fn test_array() {
2345        assert_yaml_snapshot!(parse_single(r#"
2346        let a = [1, 2,]
2347        let a = [false, "hello"]
2348        "#).unwrap(), @r###"
2349        ---
2350        - VarDef:
2351            name: a
2352            value:
2353              Array:
2354                - Literal:
2355                    Integer: 1
2356                - Literal:
2357                    Integer: 2
2358            ty_expr: ~
2359            kind: Let
2360          annotations: []
2361        - VarDef:
2362            name: a
2363            value:
2364              Array:
2365                - Literal:
2366                    Boolean: false
2367                - Literal:
2368                    String: hello
2369            ty_expr: ~
2370            kind: Let
2371          annotations: []
2372        "###);
2373    }
2374
2375    #[test]
2376    fn test_annotation() {
2377        assert_yaml_snapshot!(parse_single(r#"
2378        @{binding_strength=1}
2379        let add = a b -> a + b
2380        "#).unwrap(), @r###"
2381        ---
2382        - VarDef:
2383            name: add
2384            value:
2385              Func:
2386                return_ty: ~
2387                body:
2388                  Binary:
2389                    left:
2390                      Ident:
2391                        - a
2392                    op: Add
2393                    right:
2394                      Ident:
2395                        - b
2396                params:
2397                  - name: a
2398                    default_value: ~
2399                  - name: b
2400                    default_value: ~
2401                named_params: []
2402            ty_expr: ~
2403            kind: Let
2404          annotations:
2405            - expr:
2406                Tuple:
2407                  - Literal:
2408                      Integer: 1
2409                    alias: binding_strength
2410        "###);
2411        parse_single(
2412            r#"
2413        @{binding_strength=1} let add = a b -> a + b
2414        "#,
2415        )
2416        .unwrap();
2417
2418        parse_single(
2419            r#"
2420        @{binding_strength=1}
2421        # comment
2422        let add = a b -> a + b
2423        "#,
2424        )
2425        .unwrap();
2426
2427        parse_single(
2428            r#"
2429        @{binding_strength=1}
2430
2431
2432        let add = a b -> a + b
2433        "#,
2434        )
2435        .unwrap();
2436    }
2437
2438    #[test]
2439    fn check_valid_version() {
2440        let stmt = format!(
2441            r#"
2442        prql version:"{}"
2443        "#,
2444            env!("CARGO_PKG_VERSION_MAJOR")
2445        );
2446        assert!(parse_single(&stmt).is_ok());
2447
2448        let stmt = format!(
2449            r#"
2450            prql version:"{}.{}"
2451            "#,
2452            env!("CARGO_PKG_VERSION_MAJOR"),
2453            env!("CARGO_PKG_VERSION_MINOR")
2454        );
2455        assert!(parse_single(&stmt).is_ok());
2456
2457        let stmt = format!(
2458            r#"
2459            prql version:"{}.{}.{}"
2460            "#,
2461            env!("CARGO_PKG_VERSION_MAJOR"),
2462            env!("CARGO_PKG_VERSION_MINOR"),
2463            env!("CARGO_PKG_VERSION_PATCH"),
2464        );
2465        assert!(parse_single(&stmt).is_ok());
2466    }
2467
2468    #[test]
2469    fn check_invalid_version() {
2470        let stmt = format!(
2471            "prql version:{}\n",
2472            env!("CARGO_PKG_VERSION_MAJOR").parse::<usize>().unwrap() + 1
2473        );
2474        assert!(parse_single(&stmt).is_err());
2475    }
2476
2477    #[test]
2478    fn test_target() {
2479        assert_yaml_snapshot!(parse_single(
2480            r#"
2481          prql target:sql.sqlite
2482
2483          from film
2484          remove film2
2485        "#,
2486        )
2487        .unwrap(), @r###"
2488        ---
2489        - QueryDef:
2490            version: ~
2491            other:
2492              target: sql.sqlite
2493          annotations: []
2494        - Main:
2495            Pipeline:
2496              exprs:
2497                - FuncCall:
2498                    name:
2499                      Ident:
2500                        - from
2501                    args:
2502                      - Ident:
2503                          - film
2504                - FuncCall:
2505                    name:
2506                      Ident:
2507                        - remove
2508                    args:
2509                      - Ident:
2510                          - film2
2511          annotations: []
2512        "###);
2513    }
2514}