aiken_lang/parser/
utils.rs1use 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 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}