Actually just three programming languages in a trenchcoat
1use super::*;
2use crate::{Parser, Spanned};
3use source_span::Span;
4use trilogy_scanner::TokenType::*;
5
6/// A complete Trilogy document.
7///
8/// Similar to a module, but without a module declaration.
9#[derive(Clone, Debug)]
10pub struct Document {
11 /// The inner documentation of this document, intended to document the whole file.
12 pub documentation: Option<Documentation>,
13 /// The definitions contained within this file.
14 pub definitions: Vec<Definition>,
15 pub span: Span,
16}
17
18impl Spanned for Document {
19 fn span(&self) -> Span {
20 self.span
21 }
22}
23
24impl Document {
25 fn synchronize(parser: &mut Parser) {
26 parser.synchronize([
27 DocOuter, KwType, KwFunc, KwProc, KwRule, KwSlot, KwExport, KwImport, EndOfFile,
28 ]);
29 }
30
31 pub(crate) fn parse(parser: &mut Parser) -> Self {
32 let documentation = Documentation::parse_inner(parser);
33
34 let mut definitions = vec![];
35 loop {
36 match Definition::parse_in_document(parser) {
37 Ok(Some(definition)) => definitions.push(definition),
38 Ok(None) => break,
39 Err(..) => Document::synchronize(parser),
40 }
41 }
42
43 if !parser.is_line_start {
44 #[cfg(feature = "lax")]
45 parser.warn(SyntaxError::new_spanless(
46 "the document does not end with a new-line character",
47 ));
48
49 #[cfg(not(feature = "lax"))]
50 parser.error(SyntaxError::new_spanless(
51 "no new line found at end of file",
52 ));
53 }
54
55 let span = match (&documentation, definitions.last()) {
56 (Some(doc), Some(def)) => doc.span().union(def.span()),
57 (Some(doc), None) => doc.span(),
58 (None, Some(def)) => definitions.first().unwrap().span().union(def.span()),
59 (None, None) => Span::default(),
60 };
61 Self {
62 span,
63 documentation,
64 definitions,
65 }
66 }
67}
68
69#[cfg(test)]
70mod test {
71 use super::*;
72
73 test_parse!(document_empty: "" |parser| Document::parse(&mut parser) => Document { .. });
74 test_parse!(document_empty_newline: "\n" |parser| Document::parse(&mut parser) => Document { .. });
75
76 test_parse!(document_documented: "#! hello\n#! world" |parser| Document::parse(&mut parser) => Document { documentation: Some(_), .. });
77 test_parse!(document_documented_with_def: "#! hello\n#! world\n\n## Hello\nfunc f x = x\n" |parser| Document::parse(&mut parser) => Document { documentation: Some(_), definitions: [_], .. });
78
79 test_parse_error!(document_no_final_newline: "func f x = x" |parser| Document::parse(&mut parser) => "no new line found at end of file");
80
81 test_parse!(document_multiple_defs: "func f x = x\nfunc f y = y\nfunc g x = x\n" |parser| Document::parse(&mut parser) => Document { definitions: [_, _, _,], .. });
82 test_parse_error!(document_defs_no_newline: "func f x = x func f y = y\n" |parser| Document::parse(&mut parser) => "definitions must be separated by line breaks");
83
84 test_parse!(document_module_empty: "type A {}\n" |parser| Document::parse(&mut parser) => Document { definitions: [_], .. });
85 test_parse!(document_module_nested: "type A {\n type B { }\n}\n" |parser| Document::parse(&mut parser) => Document { definitions: [_], .. });
86
87 test_parse_error!(document_module_no_end_newline: "type A {\n type B { }}\n" |parser| Document::parse(&mut parser) => "definition in type must end with a line break");
88 test_parse_error!(document_module_no_start_newline: "type A {type B { }}\n" |parser| Document::parse(&mut parser) => "definitions must be separated by line breaks");
89
90 #[test]
91 #[rustfmt::skip]
92 fn document_error_recovery() {
93 use crate::Parser;
94 use trilogy_scanner::Scanner;
95 let scanner = Scanner::new("func f = y\nfunc f x = x\n");
96 let mut parser = Parser::new(scanner);
97 let Amble { content, .. } = Amble::parse(&mut parser);
98 assert_eq!(content.definitions.len(), 1, "expected one definition to succeed");
99 assert_eq!(parser.errors.len(), 1, "expected one definition to fail");
100 }
101}