aiken_lang/parser/
utils.rs

1use super::{error::ParseError, token::Token};
2use chumsky::prelude::*;
3
4pub fn optional_flag(token: Token) -> impl Parser<Token, bool, Error = ParseError> {
5    just(token).ignored().or_not().map(|v| v.is_some())
6}
7
8pub fn type_name_with_args() -> impl Parser<Token, (String, Option<Vec<String>>), Error = ParseError>
9{
10    just(Token::Type).ignore_then(
11        select! {Token::UpName { name } => name}.then(
12            select! {Token::Name { name } => name}
13                .separated_by(just(Token::Comma))
14                .allow_trailing()
15                .delimited_by(just(Token::Less), just(Token::Greater))
16                .or_not(),
17        ),
18    )
19}
20
21#[macro_export]
22macro_rules! assert_expr {
23    ($code:expr) => {
24        use chumsky::Parser;
25
26        let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run(indoc::indoc! { $code }).unwrap();
27
28        let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len(), 1), tokens.into_iter());
29
30        let result = $crate::parser::expr::sequence().parse(stream);
31
32        match result {
33            Ok(expr) => {
34                insta::with_settings!({
35                    description => concat!("Code:\n\n", indoc::indoc! { $code }),
36                    prepend_module_to_snapshot => false,
37                    omit_expression => true
38                }, {
39                    insta::assert_debug_snapshot!(expr);
40                })
41            },
42            Err(err) => {
43                insta::with_settings!({
44                    description => concat!("Invalid code (parse error):\n\n", indoc::indoc! { $code }),
45                    prepend_module_to_snapshot => false,
46                    omit_expression => true
47                }, {
48                    insta::assert_snapshot!(err.into_iter().map(|e| e.to_string()).collect::<Vec<_>>().join("\n"));
49                })
50            }
51        }
52    };
53}
54
55#[macro_export]
56macro_rules! assert_annotation {
57    ($code:expr) => {
58        use chumsky::Parser;
59
60        let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run(indoc::indoc! { $code }).unwrap();
61
62        let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len(), 1), tokens.into_iter());
63
64        let result = $crate::parser::annotation().parse(stream).unwrap();
65
66        insta::with_settings!({
67            description => concat!("Code:\n\n", indoc::indoc! { $code }),
68            prepend_module_to_snapshot => false,
69            omit_expression => true
70        }, {
71            insta::assert_debug_snapshot!(result);
72        });
73    };
74}
75
76#[macro_export]
77macro_rules! assert_pattern {
78    ($code:expr) => {
79        use chumsky::Parser;
80
81        let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run(indoc::indoc! { $code }).unwrap();
82
83        let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len(), 1), tokens.into_iter());
84
85        let result = $crate::parser::pattern().parse(stream).unwrap();
86
87        insta::with_settings!({
88            description => concat!("Code:\n\n", indoc::indoc! { $code }),
89            prepend_module_to_snapshot => false,
90            omit_expression => true
91        }, {
92            insta::assert_debug_snapshot!(result);
93        });
94    };
95}
96
97#[macro_export]
98macro_rules! assert_module {
99    ($code:expr) => {
100        let (module, _) =
101            $crate::parser::module(indoc::indoc!{ $code }, $crate::ast::ModuleKind::Validator).expect("Failed to parse code");
102
103        insta::with_settings!({
104            description => concat!("Code:\n\n", indoc::indoc! { $code }),
105            prepend_module_to_snapshot => false,
106            omit_expression => true
107        }, {
108            insta::assert_debug_snapshot!(module);
109        });
110    };
111}
112
113#[macro_export]
114macro_rules! assert_definition {
115    ($code:expr) => {
116        use chumsky::Parser;
117
118        let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run(indoc::indoc! { $code }).unwrap();
119
120        let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len(), 1), tokens.into_iter());
121
122        let result = $crate::parser::definition().parse(stream).unwrap();
123
124        insta::with_settings!({
125            description => concat!("Code:\n\n", indoc::indoc! { $code }),
126            prepend_module_to_snapshot => false,
127            omit_expression => true
128        }, {
129            insta::assert_debug_snapshot!(result);
130        });
131    };
132}
133
134#[macro_export]
135macro_rules! assert_import {
136    ($code:expr) => {
137        use chumsky::Parser;
138
139        let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run(indoc::indoc! { $code }).unwrap();
140
141        let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len(), 1), tokens.into_iter());
142
143        let result = $crate::parser::import().parse(stream).unwrap();
144
145        insta::with_settings!({
146            description => concat!("Code:\n\n", indoc::indoc! { $code }),
147            prepend_module_to_snapshot => false,
148            omit_expression => true
149        }, {
150            insta::assert_debug_snapshot!(result);
151        });
152    };
153}
154
155#[macro_export]
156macro_rules! assert_format {
157    ($code:expr) => {
158        let src = indoc::indoc! { $code };
159
160        let (module, extra) =
161            $crate::parser::module(src, $crate::ast::ModuleKind::Lib).expect("Failed to parse code");
162
163        let mut out = String::new();
164        $crate::format::pretty(&mut out, module, extra, &src);
165
166        insta::with_settings!({
167            description => concat!("Code:\n\n", indoc::indoc! { $code }),
168            prepend_module_to_snapshot => false,
169            omit_expression => true
170        }, {
171            insta::assert_snapshot!(out);
172        });
173
174        // Check if formatting is imdepotent
175        let (module2, extra2) = $crate::parser::module(&out, $crate::ast::ModuleKind::Lib).unwrap();
176        let mut out2 = String::new();
177        $crate::format::pretty(&mut out2, module2, extra2, &out);
178        pretty_assertions::assert_eq!(out, out2, "formatting isn't idempotent");
179    };
180}