Skip to main content

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        match $crate::parser::lexer::run(indoc::indoc! { $code }) {
27            Ok($crate::parser::lexer::LexInfo { tokens, .. }) => {
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            Err(err) => {
54                insta::with_settings!({
55                    description => concat!("Invalid code (lexer error):\n\n", indoc::indoc! { $code }),
56                    prepend_module_to_snapshot => false,
57                    omit_expression => true
58                }, {
59                    insta::assert_snapshot!(err.into_iter().map(|e| e.to_string()).collect::<Vec<_>>().join("\n"));
60                })
61            }
62        }
63    };
64}
65
66#[macro_export]
67macro_rules! assert_annotation {
68    ($code:expr) => {
69        use chumsky::Parser;
70
71        let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run(indoc::indoc! { $code }).unwrap();
72
73        let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len(), 1), tokens.into_iter());
74
75        let result = $crate::parser::annotation().parse(stream).unwrap();
76
77        insta::with_settings!({
78            description => concat!("Code:\n\n", indoc::indoc! { $code }),
79            prepend_module_to_snapshot => false,
80            omit_expression => true
81        }, {
82            insta::assert_debug_snapshot!(result);
83        });
84    };
85}
86
87#[macro_export]
88macro_rules! assert_pattern {
89    ($code:expr) => {
90        use chumsky::Parser;
91
92        let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run(indoc::indoc! { $code }).unwrap();
93
94        let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len(), 1), tokens.into_iter());
95
96        let result = $crate::parser::pattern().parse(stream).unwrap();
97
98        insta::with_settings!({
99            description => concat!("Code:\n\n", indoc::indoc! { $code }),
100            prepend_module_to_snapshot => false,
101            omit_expression => true
102        }, {
103            insta::assert_debug_snapshot!(result);
104        });
105    };
106}
107
108#[macro_export]
109macro_rules! assert_module {
110    ($code:expr) => {
111        let (module, _) =
112            $crate::parser::module(indoc::indoc!{ $code }, $crate::ast::ModuleKind::Validator).expect("Failed to parse code");
113
114        insta::with_settings!({
115            description => concat!("Code:\n\n", indoc::indoc! { $code }),
116            prepend_module_to_snapshot => false,
117            omit_expression => true
118        }, {
119            insta::assert_debug_snapshot!(module);
120        });
121    };
122}
123
124#[macro_export]
125macro_rules! assert_definition {
126    ($code:expr) => {
127        use chumsky::Parser;
128
129        let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run(indoc::indoc! { $code }).unwrap();
130
131        let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len(), 1), tokens.into_iter());
132
133        let result = $crate::parser::definition().parse(stream);
134
135        match result {
136            Ok(expr) => {
137                insta::with_settings!({
138                    description => concat!("Code:\n\n", indoc::indoc! { $code }),
139                    prepend_module_to_snapshot => false,
140                    omit_expression => true
141                }, {
142                    insta::assert_debug_snapshot!(expr);
143                })
144            },
145            Err(err) => {
146                insta::with_settings!({
147                    description => concat!("Invalid code (parse error):\n\n", indoc::indoc! { $code }),
148                    prepend_module_to_snapshot => false,
149                    omit_expression => true
150                }, {
151                    insta::assert_snapshot!(err.into_iter().map(|e| e.to_string()).collect::<Vec<_>>().join("\n"));
152                })
153            }
154        }
155    };
156}
157
158#[macro_export]
159macro_rules! assert_import {
160    ($code:expr) => {
161        use chumsky::Parser;
162
163        let $crate::parser::lexer::LexInfo { tokens, .. } = $crate::parser::lexer::run(indoc::indoc! { $code }).unwrap();
164
165        let stream = chumsky::Stream::from_iter($crate::ast::Span::create(tokens.len(), 1), tokens.into_iter());
166
167        let result = $crate::parser::import().parse(stream).unwrap();
168
169        insta::with_settings!({
170            description => concat!("Code:\n\n", indoc::indoc! { $code }),
171            prepend_module_to_snapshot => false,
172            omit_expression => true
173        }, {
174            insta::assert_debug_snapshot!(result);
175        });
176    };
177}
178
179#[macro_export]
180macro_rules! assert_format {
181    ($code:expr) => {
182        let src = indoc::indoc! { $code };
183
184        let (module, extra) =
185            $crate::parser::module(src, $crate::ast::ModuleKind::Lib).expect("Failed to parse code");
186
187        let mut out = String::new();
188        $crate::format::pretty(&mut out, module, extra, &src);
189
190        insta::with_settings!({
191            description => concat!("Code:\n\n", indoc::indoc! { $code }),
192            prepend_module_to_snapshot => false,
193            omit_expression => true
194        }, {
195            insta::assert_snapshot!(out);
196        });
197
198        // Check if formatting is imdepotent
199        let (module2, extra2) = $crate::parser::module(&out, $crate::ast::ModuleKind::Lib).unwrap();
200        let mut out2 = String::new();
201        $crate::format::pretty(&mut out2, module2, extra2, &out);
202        pretty_assertions::assert_eq!(out, out2, "formatting isn't idempotent");
203    };
204}