1mod annotation;
2pub mod chain;
3pub mod definition;
4pub mod error;
5pub mod expr;
6pub mod extra;
7pub mod lexer;
8pub mod literal;
9pub mod pattern;
10pub mod token;
11mod utils;
12
13use crate::{ast, line_numbers::LineNumbers};
14pub use annotation::parser as annotation;
15use chumsky::prelude::*;
16pub use definition::{import::parser as import, parser as definition};
17use error::ParseError;
18pub use expr::parser as expression;
19use extra::ModuleExtra;
20use indexmap::IndexMap;
21pub use pattern::parser as pattern;
22
23pub fn module(
24 src: &str,
25 kind: ast::ModuleKind,
26) -> Result<(ast::UntypedModule, ModuleExtra), Vec<ParseError>> {
27 let lexer::LexInfo { tokens, extra } = lexer::run(src)?;
28
29 let stream = chumsky::Stream::from_iter(ast::Span::create(tokens.len(), 1), tokens.into_iter());
30
31 let definitions = import()
32 .repeated()
33 .map(|imports| {
34 let mut store = IndexMap::new();
35
36 for import in imports.into_iter() {
37 let key = (import.module, import.as_name);
38 match store.remove(&key) {
39 None => {
40 store.insert(key, (import.location, import.unqualified));
41 }
42 Some((location, unqualified)) => {
43 let mut merged_unqualified = Vec::new();
44 merged_unqualified.extend(unqualified.1);
45 merged_unqualified.extend(import.unqualified.1);
46 store.insert(key, (location, (unqualified.0, merged_unqualified)));
47 }
48 }
49 }
50
51 store
52 .into_iter()
53 .map(|((module, as_name), (location, unqualified))| {
54 ast::Definition::Use(ast::Use {
55 module,
56 as_name,
57 location,
58 unqualified,
59 package: (),
60 })
61 })
62 .collect::<Vec<ast::UntypedDefinition>>()
63 })
64 .then(definition().repeated())
65 .map(|(imports, others)| {
66 let mut defs = Vec::new();
67 defs.extend(imports);
68 defs.extend(others);
69 defs
70 })
71 .then_ignore(end())
72 .parse(stream)?;
73
74 let lines = LineNumbers::new(src);
75
76 let module = ast::UntypedModule {
77 kind,
78 lines,
79 definitions,
80 docs: vec![],
81 name: "".to_string(),
82 type_info: (),
83 };
84
85 Ok((module, extra))
86}
87
88#[cfg(test)]
89mod tests {
90 use crate::assert_module;
91
92 #[test]
93 fn merge_imports() {
94 assert_module!(
95 r#"
96 use aiken/list.{bar, foo}
97 use aiken/list.{baz}
98 "#
99 );
100 }
101
102 #[test]
103 fn windows_newline() {
104 assert_module!("use aiken/list\r\n");
105 }
106
107 #[test]
108 fn can_handle_comments_at_end_of_file() {
109 assert_module!(
110 r#"
111 use aiken
112
113 // some comment
114 // more comments"#
115 );
116 }
117
118 #[test]
119 fn function_ambiguous_sequence() {
120 assert_module!(
121 r#"
122 fn foo_1() {
123 let a = bar
124 (40)
125 }
126
127 fn foo_2() {
128 let a = bar
129 {40}
130 }
131
132 fn foo_3() {
133 let a = (40+2)
134 }
135
136 fn foo_4() {
137 let a = bar(42)
138 (a + 14) * 42
139 }
140 "#
141 );
142 }
143
144 #[test]
145 fn parse_unicode_offset_1() {
146 assert_module!(
147 r#"
148 fn foo() {
149 let x = "★"
150 x
151 }
152 "#
153 );
154 }
155
156 #[test]
157 fn parse_unicode_offset_2() {
158 assert_module!(
159 r#"
160 fn foo() {
161 let x = "*"
162 x
163 }
164 "#
165 );
166 }
167}