use super::*; use crate::{Parser, Spanned}; use source_span::Span; use trilogy_scanner::TokenType::*; /// A complete Trilogy document. /// /// Similar to a module, but without a module declaration. #[derive(Clone, Debug)] pub struct Document { /// The inner documentation of this document, intended to document the whole file. pub documentation: Option, /// The definitions contained within this file. pub definitions: Vec, pub span: Span, } impl Spanned for Document { fn span(&self) -> Span { self.span } } impl Document { fn synchronize(parser: &mut Parser) { parser.synchronize([ DocOuter, KwType, KwFunc, KwProc, KwRule, KwSlot, KwExport, KwImport, EndOfFile, ]); } pub(crate) fn parse(parser: &mut Parser) -> Self { let documentation = Documentation::parse_inner(parser); let mut definitions = vec![]; loop { match Definition::parse_in_document(parser) { Ok(Some(definition)) => definitions.push(definition), Ok(None) => break, Err(..) => Document::synchronize(parser), } } if !parser.is_line_start { #[cfg(feature = "lax")] parser.warn(SyntaxError::new_spanless( "the document does not end with a new-line character", )); #[cfg(not(feature = "lax"))] parser.error(SyntaxError::new_spanless( "no new line found at end of file", )); } let span = match (&documentation, definitions.last()) { (Some(doc), Some(def)) => doc.span().union(def.span()), (Some(doc), None) => doc.span(), (None, Some(def)) => definitions.first().unwrap().span().union(def.span()), (None, None) => Span::default(), }; Self { span, documentation, definitions, } } } #[cfg(test)] mod test { use super::*; test_parse!(document_empty: "" |parser| Document::parse(&mut parser) => Document { .. }); test_parse!(document_empty_newline: "\n" |parser| Document::parse(&mut parser) => Document { .. }); test_parse!(document_documented: "#! hello\n#! world" |parser| Document::parse(&mut parser) => Document { documentation: Some(_), .. }); 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: [_], .. }); test_parse_error!(document_no_final_newline: "func f x = x" |parser| Document::parse(&mut parser) => "no new line found at end of file"); 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: [_, _, _,], .. }); 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"); test_parse!(document_module_empty: "type A {}\n" |parser| Document::parse(&mut parser) => Document { definitions: [_], .. }); test_parse!(document_module_nested: "type A {\n type B { }\n}\n" |parser| Document::parse(&mut parser) => Document { definitions: [_], .. }); 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"); 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"); #[test] #[rustfmt::skip] fn document_error_recovery() { use crate::Parser; use trilogy_scanner::Scanner; let scanner = Scanner::new("func f = y\nfunc f x = x\n"); let mut parser = Parser::new(scanner); let Amble { content, .. } = Amble::parse(&mut parser); assert_eq!(content.definitions.len(), 1, "expected one definition to succeed"); assert_eq!(parser.errors.len(), 1, "expected one definition to fail"); } }