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, PrettyPrintSExpr)]
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}
16
17impl Spanned for Document {
18 fn span(&self) -> Span {
19 match (&self.documentation, self.definitions.last()) {
20 (Some(doc), Some(def)) => doc.span().union(def.span()),
21 (Some(doc), None) => doc.span(),
22 (None, Some(def)) => self.definitions.first().unwrap().span().union(def.span()),
23 (None, None) => Span::default(),
24 }
25 }
26}
27
28impl Document {
29 fn synchronize(parser: &mut Parser) {
30 parser.synchronize([
31 DocOuter, KwType, KwFunc, KwProc, KwRule, KwConst, KwExport, KwImport, EndOfFile,
32 ]);
33 }
34
35 pub(crate) fn parse(parser: &mut Parser) -> Self {
36 let documentation = Documentation::parse_inner(parser);
37
38 let mut definitions = vec![];
39 loop {
40 match Definition::parse_in_document(parser) {
41 Ok(Some(definition)) => definitions.push(definition),
42 Ok(None) => break,
43 Err(..) => Document::synchronize(parser),
44 }
45 }
46
47 if !parser.is_line_start {
48 #[cfg(feature = "lax")]
49 parser.warn(SyntaxError::new_spanless(
50 "the document does not end with a new-line character",
51 ));
52
53 #[cfg(not(feature = "lax"))]
54 parser.error(SyntaxError::new_spanless(
55 "no new line found at end of file",
56 ));
57 }
58
59 Self {
60 documentation,
61 definitions,
62 }
63 }
64}
65
66#[cfg(test)]
67mod test {
68 use super::*;
69
70 test_parse!(document_empty: "" |parser| Document::parse(&mut parser) => "(Document () [])");
71 test_parse!(document_empty_newline: "\n" |parser| Document::parse(&mut parser) => "(Document () [])");
72
73 test_parse!(document_documented: "#! hello\n#! world" |parser| Document::parse(&mut parser) => "(Document (Documentation _) [])");
74 test_parse!(document_documented_with_def: "#! hello\n#! world\n\n## Hello\nfunc f x = x\n" |parser| Document::parse(&mut parser) => "(Document (Documentation _) [(Definition (Documentation _) _)])");
75
76 test_parse_error!(document_no_final_newline: "func f x = x" |parser| Document::parse(&mut parser) => "no new line found at end of file");
77
78 test_parse!(document_multiple_defs: "func f x = x\nfunc f y = y\nfunc g x = x\n" |parser| Document::parse(&mut parser) => "(Document () [(Definition () _) (Definition () _) (Definition () _)])");
79 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");
80
81 test_parse!(document_module_empty: "type A {}\n" |parser| Document::parse(&mut parser) => "(Document () [(Definition () _)])");
82 test_parse!(document_module_nested: "type A {\n type B { }\n}\n" |parser| Document::parse(&mut parser) => "(Document () [(Definition () _)])");
83
84 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");
85 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");
86
87 #[test]
88 #[rustfmt::skip]
89 fn document_error_recovery() {
90 use crate::Parser;
91 use trilogy_scanner::Scanner;
92 let scanner = Scanner::new("func f = y\nfunc f x = x\n");
93 let mut parser = Parser::new(scanner);
94 let Amble { content, .. } = Amble::parse(&mut parser);
95 assert_eq!(content.definitions.len(), 1, "expected one definition to succeed");
96 assert_eq!(parser.errors.len(), 1, "expected one definition to fail");
97 }
98}