1use nash_region::{Located, Region};
11use nash_source::{Alias, Docs, Exposing, Import, Infix, Module, Union, Value};
12
13use crate::Parser;
14use crate::declaration::Decl;
15use crate::error;
16
17impl<'a> Parser<'a> {
18 pub fn module_header(
27 &mut self,
28 ) -> Result<(&'a Located<&'a str>, &'a Located<Exposing<'a>>), error::Module<'a>> {
29 self.keyword_module(error::Module::Problem)?;
31
32 self.chomp_and_check_indent(error::Module::Space, error::Module::Name)?;
33
34 let start = self.get_position();
36 let name = self.module_name(error::Module::Name)?;
37 let module_name = self.add_end(start, name);
38
39 self.chomp(error::Module::Space)?;
41
42 self.keyword_exposing(|row, col| {
44 error::Module::Exposing(self.bump.alloc(error::Exposing::Start(row, col)), row, col)
45 })?;
46
47 self.chomp_and_check_indent(error::Module::Space, |row, col| {
48 error::Module::Exposing(
49 self.bump.alloc(error::Exposing::IndentValue(row, col)),
50 row,
51 col,
52 )
53 })?;
54
55 let exposing_start = self.get_position();
57 let exposing = self.specialize(
58 |bump, err, row, col| error::Module::Exposing(bump.alloc(err), row, col),
59 |p| p.exposing(),
60 )?;
61 let exposing_located = self.add_end(exposing_start, exposing);
62
63 Ok((module_name, exposing_located))
64 }
65
66 fn imports(&mut self) -> Result<&'a [&'a Import<'a>], error::Module<'a>> {
79 let mut imports = Vec::new();
80
81 loop {
82 let state = self.save_state();
84
85 match self.import() {
86 Ok(import) => {
87 imports.push(import);
88 }
90 Err(_) => {
91 if self.pos == state.pos {
93 self.restore_state(state);
94 break;
95 }
96 return Err(error::Module::ImportStart(self.row, self.col));
98 }
99 }
100 }
101
102 Ok(self.alloc_slice_copy(&imports))
103 }
104
105 fn declarations(&mut self) -> Result<Vec<Decl<'a>>, error::Module<'a>> {
119 let mut decls = Vec::new();
120
121 loop {
122 let state = self.save_state();
124
125 match self.specialize(
127 |bump, err, row, col| error::Module::Declarations(bump.alloc(err), row, col),
128 |p| p.declaration(),
129 ) {
130 Ok((decl, _end)) => {
131 decls.push(decl);
132
133 self.chomp(error::Module::Space)?;
135
136 if self.is_eof() {
138 break;
139 }
140
141 if self.col != 1 {
143 break;
144 }
145 }
146 Err(_) => {
147 if self.pos == state.pos {
149 self.restore_state(state);
150 break;
151 }
152 return Err(error::Module::Declarations(
154 self.bump.alloc(error::Decl::Start(self.row, self.col)),
155 self.row,
156 self.col,
157 ));
158 }
159 }
160 }
161
162 Ok(decls)
163 }
164
165 fn infixes(&mut self) -> Result<Vec<&'a Located<Infix<'a>>>, error::Module<'a>> {
169 let mut infixes = Vec::new();
170
171 loop {
172 let state = self.save_state();
173
174 match self.infix_decl() {
175 Ok(infix) => {
176 infixes.push(infix);
177 }
179 Err(_) => {
180 if self.pos == state.pos {
182 self.restore_state(state);
183 break;
184 }
185 return Err(error::Module::Infix(self.row, self.col));
186 }
187 }
188 }
189
190 Ok(infixes)
191 }
192
193 pub fn module(&mut self) -> Result<Module<'a>, error::Module<'a>> {
200 self.chomp(error::Module::Space)?;
202
203 let start_pos = self.get_position();
204
205 let (name, exports) = {
207 let state = self.save_state();
208 match self.module_header() {
209 Ok((n, e)) => {
210 self.chomp(error::Module::Space)?;
212 self.check_fresh_line(error::Module::FreshLine)?;
213 (Some(n), e)
214 }
215 Err(_) => {
216 if self.pos == state.pos {
218 self.restore_state(state);
219 }
220 let default_exports = self.alloc(Located::at(Region::one(), Exposing::Open));
222 (None, default_exports)
223 }
224 }
225 };
226
227 let imports = self.imports()?;
229
230 let infix_vec = self.infixes()?;
232 let binops = self.alloc_slice_copy(&infix_vec);
233
234 let decls = self.declarations()?;
236
237 let (values, unions, aliases) = self.categorize_decls(decls);
239
240 let docs = self.alloc(Docs::NoDocs(Region::new(start_pos, self.get_position())));
242
243 Ok(Module {
244 name,
245 exports,
246 docs,
247 imports,
248 values,
249 unions,
250 aliases,
251 binops,
252 })
253 }
254
255 #[allow(clippy::type_complexity)]
257 fn categorize_decls(
258 &self,
259 decls: Vec<Decl<'a>>,
260 ) -> (
261 &'a [&'a Located<Value<'a>>],
262 &'a [&'a Located<Union<'a>>],
263 &'a [&'a Located<Alias<'a>>],
264 ) {
265 let mut values = Vec::new();
266 let mut unions = Vec::new();
267 let mut aliases = Vec::new();
268
269 for decl in decls {
270 match decl {
271 Decl::Value(_doc, value) => values.push(value),
272 Decl::Union(_doc, union) => unions.push(union),
273 Decl::Alias(_doc, alias) => aliases.push(alias),
274 }
275 }
276
277 (
278 self.alloc_slice_copy(&values),
279 self.alloc_slice_copy(&unions),
280 self.alloc_slice_copy(&aliases),
281 )
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288 use bumpalo::Bump;
289 use indoc::indoc;
290
291 macro_rules! assert_module_header_snapshot {
292 ($input:expr) => {{
293 let input = indoc!($input);
294 let bump = Bump::new();
295 let src = bump.alloc_str(input);
296 let mut parser = Parser::new(&bump, src.as_bytes());
297 let result = parser.module_header();
298 match result {
299 Ok((name, exposing)) => {
300 insta::with_settings!({
301 description => format!("Code:\n\n{}", input),
302 omit_expression => true,
303 }, {
304 insta::assert_debug_snapshot!((name, exposing));
305 });
306 }
307 Err(e) => {
308 panic!("Expected successful parse, got error: {:?}", e);
309 }
310 }
311 }};
312 }
313
314 #[test]
315 fn module_header_simple_open() {
316 assert_module_header_snapshot!("module Foo exposing (..)");
317 }
318
319 #[test]
320 fn module_header_dotted() {
321 assert_module_header_snapshot!("module Foo.Bar exposing (baz)");
322 }
323
324 #[test]
325 fn module_header_mixed_exposing() {
326 assert_module_header_snapshot!("module Main exposing (main, Msg(..))");
327 }
328
329 #[test]
330 fn module_header_deeply_nested() {
331 assert_module_header_snapshot!("module Platform.Cmd.Extra exposing (batch, none)");
332 }
333
334 macro_rules! assert_module_snapshot {
339 ($input:expr) => {{
340 let input = indoc!($input);
341 let bump = Bump::new();
342 let src = bump.alloc_str(input);
343 let mut parser = Parser::new(&bump, src.as_bytes());
344 let result = parser.module();
345 match result {
346 Ok(module) => {
347 insta::with_settings!({
348 description => format!("Code:\n\n{}", input),
349 omit_expression => true,
350 }, {
351 insta::assert_debug_snapshot!(module);
352 });
353 }
354 Err(e) => {
355 panic!("Expected successful parse, got error: {:?}", e);
356 }
357 }
358 }};
359 }
360
361 #[test]
362 fn module_header_only() {
363 assert_module_snapshot!("module Main exposing (..)\n");
364 }
365
366 #[test]
367 fn module_with_imports() {
368 assert_module_snapshot!(
369 r#"
370 module Main exposing (..)
371
372 import List
373 import Maybe exposing (Maybe(..))
374 "#
375 );
376 }
377
378 #[test]
379 fn module_with_value() {
380 assert_module_snapshot!(
381 r#"
382 module Main exposing (..)
383
384 main = 42
385 "#
386 );
387 }
388
389 #[test]
390 fn module_with_type() {
391 assert_module_snapshot!(
392 r#"
393 module Main exposing (..)
394
395 type Maybe a
396 = Just a
397 | Nothing
398 "#
399 );
400 }
401
402 #[test]
403 fn module_with_alias() {
404 assert_module_snapshot!(
405 r#"
406 module Main exposing (..)
407
408 type alias Point = { x : Int, y : Int }
409 "#
410 );
411 }
412
413 #[test]
414 fn module_full() {
415 assert_module_snapshot!(
416 r#"
417 module Main exposing (main, Model, Msg(..))
418
419 import Html exposing (div)
420 import Platform.Cmd as Cmd
421
422 type alias Model = { count : Int }
423
424 type Msg
425 = Increment
426 | Decrement
427
428 main = 0
429 "#
430 );
431 }
432
433 #[test]
434 fn module_no_header() {
435 assert_module_snapshot!("x = 1\n");
437 }
438
439 #[test]
440 fn module_with_infix() {
441 assert_module_snapshot!(
442 r#"
443 module Main exposing (..)
444
445 infix left 6 (|>) = apR
446
447 apR f x = f x
448 "#
449 );
450 }
451}