From 70c4cc6f189cd46907b47b1b53a7832aaeaafc28 Mon Sep 17 00:00:00 2001 From: Soispha Date: Fri, 22 Dec 2023 20:32:43 +0100 Subject: [PATCH] feat(trixy-lang_parser): Add support for doc comments Parsing right now works by simply comparing the input string: ``` "/" -> -> "/" -> |-> "//" -> ``` A better method to do this though would be to turn "//" and "///" into keywords and simply leave the parsing to the parser module not the tokenizer. --- trixy/trixy-lang_parser/docs/grammar.ebnf | 26 ++- trixy/trixy-lang_parser/docs/grammar.pdf | Bin 49162 -> 67698 bytes trixy/trixy-lang_parser/example/comments.tri | 12 ++ .../example/failing_comments.tri | 13 ++ trixy/trixy-lang_parser/generate_docs | 4 +- .../src/command_spec/checked.rs | 36 ++++- .../src/command_spec/unchecked.rs | 33 +++- trixy/trixy-lang_parser/src/error.rs | 2 +- trixy/trixy-lang_parser/src/lexing/error.rs | 5 +- trixy/trixy-lang_parser/src/lexing/mod.rs | 46 +++++- .../trixy-lang_parser/src/lexing/tokenizer.rs | 58 +++++-- .../src/parsing/checked/mod.rs | 49 ++++-- .../src/parsing/checked/test.rs | 93 ++++++++++- .../src/parsing/unchecked/error.rs | 26 ++- .../src/parsing/unchecked/mod.rs | 149 ++++++++++++++++-- .../src/parsing/unchecked/test.rs | 5 +- 16 files changed, 487 insertions(+), 70 deletions(-) create mode 100644 trixy/trixy-lang_parser/example/comments.tri create mode 100644 trixy/trixy-lang_parser/example/failing_comments.tri diff --git a/trixy/trixy-lang_parser/docs/grammar.ebnf b/trixy/trixy-lang_parser/docs/grammar.ebnf index abe1be8..d495fc3 100644 --- a/trixy/trixy-lang_parser/docs/grammar.ebnf +++ b/trixy/trixy-lang_parser/docs/grammar.ebnf @@ -6,15 +6,27 @@ # - Block comments (`/* */`). # *) -CommandSpec = { Function | Namespace | Enumeration | Structure } ; -Function = "fn" Identifier "(" [NamedType {"," NamedType }] ")" [ "->" Type ] ";" ; -Namespace = "nasp" Identifier "{" {Function | Namespace | Enumeration | Structure} "}" ; -Structure = "struct" Identifier "{" [NamedType {"," NamedType } [","]] "}" ";"; -Enumeration = "enum" Identifier "{" [Identifier {"," Identifier} [","]] "}" ";"; -Identifier = (CHARACTER | "_") { NUMBER | CHARACTER | "_" } ; -NamedType = Identifier ":" Type; +CommandSpec = {Function | Namespace | Enumeration | Structure } ; + +Function = {DocComment} "fn" Identifier "(" [NamedType {"," NamedType }] ")" [ "->" Type ] ";" ; +Namespace = {DocComment} "nasp" Identifier "{" {Function | Namespace | Enumeration | Structure} "}" ; +Structure = {DocComment} "struct" Identifier "{" [DocNamedType {"," DocNamedType } [","]] "}" ";"; +Enumeration = {DocComment} "enum" Identifier "{" [DocIdentifier {"," DocIdentifier} [","]] "}" ";"; + Type = Identifier ["<" Type {"," Type} ">"]; +Identifier = (CHARACTER | "_") { NUMBER | CHARACTER | "_" } ; +DocIdentifier = {DocComment} (CHARACTER | "_") { NUMBER | CHARACTER | "_" } ; + +NamedType = Identifier ":" Type; +DocNamedType = {DocComment} Identifier ":" Type; + + +DocComment = "///" {ANYTHING} LineEnding; + +Comment = "//" [ NOT ("/" {ANYTHING} LineEnding) | "//"] {ANYTHING} LineEnding; +LineEnding = "\\n" | "\\r" | "\\r\\n"; + # (* # vim: ft=ebnf # *) diff --git a/trixy/trixy-lang_parser/docs/grammar.pdf b/trixy/trixy-lang_parser/docs/grammar.pdf index 97ec4e93d5743ec1bc11a37659b8ab94bc120a87..716a39f4ca7afe33f06daa98ec15512bc7f3ab59 100644 GIT binary patch delta 34634 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 E0N@{rga7~l delta 17264 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 X8OxVS2mu@nG&nahGYTaoMNdWwGzkL6 diff --git a/trixy/trixy-lang_parser/example/comments.tri b/trixy/trixy-lang_parser/example/comments.tri new file mode 100644 index 0000000..597996a --- /dev/null +++ b/trixy/trixy-lang_parser/example/comments.tri @@ -0,0 +1,12 @@ +fn print(message: String); + +/// First doc comment +// Some more text +nasp trinitrix { + /// Second doc comment + fn hi(name: String) -> String; +} + + +// That's a flat out lie, but it results in a rather nice syntax highlight compared to nothing: +// vim: syntax=rust diff --git a/trixy/trixy-lang_parser/example/failing_comments.tri b/trixy/trixy-lang_parser/example/failing_comments.tri new file mode 100644 index 0000000..7aa985b --- /dev/null +++ b/trixy/trixy-lang_parser/example/failing_comments.tri @@ -0,0 +1,13 @@ +fn print(message: CommandTransferValue); + +/// Some doc comment +// Some more text +nasp trinitrix { + fn hi(name: String) -> String; +} + +/// Trailing doc comment (I will fail) + + +// That's a flat out lie, but it results in a rather nice syntax highlight compared to nothing: +// vim: syntax=rust diff --git a/trixy/trixy-lang_parser/generate_docs b/trixy/trixy-lang_parser/generate_docs index e48d336..f84a636 100755 --- a/trixy/trixy-lang_parser/generate_docs +++ b/trixy/trixy-lang_parser/generate_docs @@ -2,8 +2,8 @@ -ebnf2pdf "./docs/grammar.ebnf" -mv out.pdf ./docs/grammar.pdf +ebnf2pdf make "./docs/grammar.ebnf" +mv grammar.ebnf.pdf ./docs/grammar.pdf # vim: ft=sh diff --git a/trixy/trixy-lang_parser/src/command_spec/checked.rs b/trixy/trixy-lang_parser/src/command_spec/checked.rs index 3da9a5c..30d0eda 100644 --- a/trixy/trixy-lang_parser/src/command_spec/checked.rs +++ b/trixy/trixy-lang_parser/src/command_spec/checked.rs @@ -4,6 +4,8 @@ use std::fmt::Display; use crate::lexing::TokenKind; +use super::unchecked; + /// These are the "primitive" types used in trixy, you can use any of them to create new structures pub const BASE_TYPES: [ConstIdentifier; 8] = [ Identifier::from("Integer"), @@ -24,6 +26,7 @@ pub struct Namespace { pub structures: Vec, pub enumerations: Vec, pub namespaces: Vec, + pub attributes: Vec, } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -48,13 +51,15 @@ impl From for CommandSpec { #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Structure { pub identifier: Identifier, - pub contents: Vec, + pub contents: Vec, + pub attributes: Vec, } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Enumeration { pub identifier: Identifier, - pub states: Vec, + pub states: Vec, + pub attributes: Vec, } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -62,6 +67,7 @@ pub struct Function { pub identifier: Identifier, pub inputs: Vec, pub output: Option, + pub attributes: Vec, } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -76,6 +82,13 @@ pub struct NamedType { pub r#type: Type, } +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct DocNamedType { + pub name: Identifier, + pub r#type: Type, + pub attributes: Vec, +} + impl From for Identifier { fn from(value: TokenKind) -> Self { match value { @@ -92,6 +105,19 @@ impl From for Identifier { } } +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +pub enum Attribute { + #[allow(non_camel_case_types)] + doc(String), +} +impl From for Attribute { + fn from(value: unchecked::Attribute) -> Self { + match value { + unchecked::Attribute::doc { content: name, .. } => Self::doc(name), + } + } +} + /// An Identifier /// These include /// - Variable names @@ -103,6 +129,12 @@ pub struct Identifier { pub name: String, } +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +pub struct DocIdentifier { + pub name: String, + pub attributes: Vec, +} + /// A const version of [Identifier] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct ConstIdentifier { diff --git a/trixy/trixy-lang_parser/src/command_spec/unchecked.rs b/trixy/trixy-lang_parser/src/command_spec/unchecked.rs index 7619f96..ef88fb7 100644 --- a/trixy/trixy-lang_parser/src/command_spec/unchecked.rs +++ b/trixy/trixy-lang_parser/src/command_spec/unchecked.rs @@ -4,7 +4,7 @@ use std::fmt::{Display, Write}; -use crate::lexing::Token; +use crate::lexing::{Token, TokenSpan}; #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)] pub struct CommandSpec { @@ -22,6 +22,7 @@ impl From for Namespace { structures: value.structures, enumerations: value.enumerations, namespaces: value.namespaces, + attributes: vec![], } } } @@ -34,6 +35,8 @@ pub struct Namespace { pub structures: Vec, pub enumerations: Vec, pub namespaces: Vec, + + pub attributes: Vec, } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -44,23 +47,45 @@ pub enum Declaration { Namespace(Namespace), } +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +pub enum Attribute { + #[allow(non_camel_case_types)] + doc{content: String, span: TokenSpan}, +} + #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct Function { pub identifier: Token, // Will later become an Identifier pub inputs: Vec, pub output: Option, + pub attributes: Vec, } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct Structure { pub identifier: Token, // Will later become an Identifier - pub contents: Vec, + pub contents: Vec, + pub attributes: Vec, } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct Enumeration { - pub identifier: Token, // Will later become an Identifier - pub states: Vec, // Will later become an Identifier + pub identifier: Token, // Will later become an Identifier + pub states: Vec, // Will later become an Identifier + pub attributes: Vec, +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +pub struct DocToken { + pub token: Token, + pub attributes: Vec, +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +pub struct DocNamedType { + pub name: Token, // Will later become an Identifier + pub r#type: Type, + pub attributes: Vec, } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] diff --git a/trixy/trixy-lang_parser/src/error.rs b/trixy/trixy-lang_parser/src/error.rs index ccbc4fc..c6fd486 100644 --- a/trixy/trixy-lang_parser/src/error.rs +++ b/trixy/trixy-lang_parser/src/error.rs @@ -55,7 +55,7 @@ impl ErrorContext { }; let line_above; - if line_number == 0 { + if line_number == 1 { // We only have one line, so no line above line_above = "".to_owned(); } else { diff --git a/trixy/trixy-lang_parser/src/lexing/error.rs b/trixy/trixy-lang_parser/src/lexing/error.rs index 98f3699..fed9d09 100644 --- a/trixy/trixy-lang_parser/src/lexing/error.rs +++ b/trixy/trixy-lang_parser/src/lexing/error.rs @@ -13,10 +13,12 @@ pub enum LexingError { UnknownCharacter(char), #[error("The Arrow token must be of the form: ->")] ExpectedArrow, + #[error("The Comment token must start with two slashes")] + ExpectedComment, } impl AdditionalHelp for LexingError { - fn additional_help(& self) -> String { + fn additional_help(&self) -> String { let out = match self { LexingError::NoMatchesTaken => "This token does not produce a possible match".to_owned(), LexingError::UnexpectedEOF => "This eof was completely unexpected".to_owned(), @@ -24,6 +26,7 @@ impl AdditionalHelp for LexingError { LexingError::UnknownCharacter(char) => { format!("This char: `{char}`; is not a valid token") }, + LexingError::ExpectedComment => "The '/' started comment parsing, but I could not find a matching '/'".to_owned(), }; out } diff --git a/trixy/trixy-lang_parser/src/lexing/mod.rs b/trixy/trixy-lang_parser/src/lexing/mod.rs index 58f77d6..cbaec89 100644 --- a/trixy/trixy-lang_parser/src/lexing/mod.rs +++ b/trixy/trixy-lang_parser/src/lexing/mod.rs @@ -28,6 +28,18 @@ impl TokenStream { tokens.push(tok); } + // filter out comments + let tokens = tokens + .into_iter() + .filter(|token| { + if let TokenKind::Comment(_) = token.kind { + false + } else { + true + } + }) + .collect(); + Ok(Self { tokens, original_file: src.to_owned(), @@ -40,8 +52,8 @@ impl TokenStream { } /// Get a reference to the uppermost token, without modifying the token list - pub fn peek(&self) -> &Token { - self.tokens.last().expect("This should not be emtpy") + pub fn peek(&self) -> Option<&Token> { + self.tokens.last() } /// Remove to the uppermost token @@ -80,6 +92,15 @@ pub struct TokenSpan { pub end: usize, } +impl TokenSpan { + pub fn from_range(start: TokenSpan, end: TokenSpan) -> Self { + Self { + start: start.start, + end: end.end, + } + } +} + /// A Token #[derive(Debug, Default, PartialEq, PartialOrd, Ord, Eq, Clone)] pub struct Token { @@ -123,6 +144,10 @@ pub enum TokenKind { ParenClose, SquareOpen, SquareClose, + + DocComment(String), + Comment(String), + /// This is not a real TokenKind, but only used for error handling #[default] Dummy, @@ -135,6 +160,16 @@ impl TokenKind { return true; } } + if let TokenKind::Comment(_) = self { + if let TokenKind::Comment(_) = other { + return true; + } + } + if let TokenKind::DocComment(_) = self { + if let TokenKind::DocComment(_) = other { + return true; + } + } self == other } } @@ -161,6 +196,8 @@ impl Display for TokenKind { TokenKind::Dummy => f.write_str("DUMMY"), TokenKind::SquareOpen => f.write_str("SQUAREOPEN"), TokenKind::SquareClose => f.write_str("SQUARECLOSE"), + TokenKind::DocComment(text) => write!(f, "DOCCOMMENT({})", text), + TokenKind::Comment(text) => write!(f, "COMMENT({})", text), } } } @@ -234,10 +271,13 @@ macro_rules! token { [struct] => { $crate::lexing::TokenKind::Keyword($crate::lexing::Keyword::r#struct) }; [enum] => { $crate::lexing::TokenKind::Keyword($crate::lexing::Keyword::r#enum) }; - // This is only works for checking for a identifier + // This is only works for checking for a identifier or comment // see the `same_kind` method on TokenKind [Ident] => { $crate::lexing::TokenKind::Identifier("".to_owned()) }; [Identifier] => { $crate::lexing::TokenKind::Identifier("".to_owned()) }; + [DocComment] => { $crate::lexing::TokenKind::DocComment("".to_owned()) }; + [DocCommentMatch] => { $crate::lexing::TokenKind::DocComment(_doc_comment) }; + [Comment] => { $crate::lexing::TokenKind::Comment("".to_owned()) }; } #[cfg(test)] diff --git a/trixy/trixy-lang_parser/src/lexing/tokenizer.rs b/trixy/trixy-lang_parser/src/lexing/tokenizer.rs index a4eb885..c7a9882 100644 --- a/trixy/trixy-lang_parser/src/lexing/tokenizer.rs +++ b/trixy/trixy-lang_parser/src/lexing/tokenizer.rs @@ -62,7 +62,9 @@ impl<'a> Tokenizer<'a> { ',' => (TokenKind::Comma, 1), '<' => (TokenKind::SquareOpen, 1), '>' => (TokenKind::SquareClose, 1), + '-' => tokenize_arrow(self.remaining_text)?, + '/' => tokenize_comment(self.remaining_text)?, // can't use a OR (`|`) here, as the guard takes precedence c if c.is_alphabetic() => tokenize_ident(self.remaining_text)?, @@ -74,17 +76,17 @@ impl<'a> Tokenizer<'a> { Ok((tok, length)) } - /// Skip past any whitespace characters or comments. fn skip_ignored_tokens(&mut self) { loop { let ws = self.skip_whitespace(); - let comments = self.skip_comments(); - + let comments = self.skip_block_comment(); if ws + comments == 0 { return; } } } + + /// Skip past any whitespace characters fn skip_whitespace(&mut self) -> usize { let mut remaining = self.remaining_text; @@ -102,21 +104,21 @@ impl<'a> Tokenizer<'a> { self.chomp(skip); skip } + fn skip_block_comment(&mut self) -> usize { + let pairs = [("/*", "*/")]; - fn skip_comments(&mut self) -> usize { - let remaining = self.remaining_text; - let pairs = [("//", "\n"), ("/*", "*/")]; + let src = self.remaining_text; - let mut skip = 0; for &(pattern, matcher) in &pairs { - if remaining.starts_with(pattern) { - let leftovers = skip_until(remaining, matcher); - skip = remaining.len() - leftovers.len(); - break; + if src.starts_with(pattern) { + let leftovers = skip_until(src, matcher); + let skip = src.len() - leftovers.len(); + self.chomp(skip); + return skip; } } - self.chomp(skip); - skip + + 0 } fn chomp(&mut self, chars_to_chomp: usize) { @@ -125,6 +127,36 @@ impl<'a> Tokenizer<'a> { } } +fn tokenize_comment(text: &str) -> Result<(TokenKind, usize), LexingError> { + // every token starts with two slashes + let slashes: &str = &text[..2]; + if slashes != "//" { + Err(LexingError::ExpectedComment) + } else { + let text: &str = &text[2..]; + if let Some('/') = text.chars().next() { + let text = &text[1..]; + let (doc_comment, chars_read) = take_while(text, |ch| ch != '\n' && ch != '\r')?; + + // trim whitespace + let doc_comment = doc_comment.trim_start(); + let doc_comment = doc_comment.trim_end(); + + return Ok(( + TokenKind::DocComment(doc_comment.to_owned()), + chars_read + 3, + )); + } + let (comment, chars_read) = take_while(text, |ch| ch != '\n' && ch != '\r')?; + + // trim whitespace + let comment = comment.trim_start(); + let comment = comment.trim_end(); + + return Ok((TokenKind::Comment(comment.to_owned()), chars_read + 2)); + } +} + fn tokenize_ident(text: &str) -> Result<(TokenKind, usize), LexingError> { let (got, chars_read) = take_while(text, |ch| ch == '_' || ch.is_alphanumeric())?; diff --git a/trixy/trixy-lang_parser/src/parsing/checked/mod.rs b/trixy/trixy-lang_parser/src/parsing/checked/mod.rs index 65b1e91..1e44d4f 100644 --- a/trixy/trixy-lang_parser/src/parsing/checked/mod.rs +++ b/trixy/trixy-lang_parser/src/parsing/checked/mod.rs @@ -3,14 +3,14 @@ use std::mem; use crate::{ command_spec::{ checked::{ - CommandSpec, Enumeration, Function, Identifier, NamedType, Namespace, Structure, Type, - BASE_TYPES, + CommandSpec, DocIdentifier, DocNamedType, Enumeration, Function, Identifier, NamedType, + Namespace, Structure, Type, BASE_TYPES, }, unchecked::{ - CommandSpec as UncheckedCommandSpec, Enumeration as UncheckedEnumeration, - Function as UncheckedFunction, NamedType as UncheckedNamedType, - Namespace as UncheckedNamespace, Structure as UncheckedStructure, - Type as UncheckedType, + CommandSpec as UncheckedCommandSpec, DocNamedType as UncheckedDocNamedType, + Enumeration as UncheckedEnumeration, Function as UncheckedFunction, + NamedType as UncheckedNamedType, Namespace as UncheckedNamespace, + Structure as UncheckedStructure, Type as UncheckedType, }, }, error::ErrorContext, @@ -66,6 +66,12 @@ impl UncheckedCommandSpec { } } +macro_rules! pass_attrs_along { + ($name:ident) => { + $name.attributes.into_iter().map(|a| a.into()).collect() + }; +} + impl Parser { fn parse(mut self) -> Result { let namespace: UncheckedNamespace = @@ -129,6 +135,7 @@ impl Parser { structures, enumerations, namespaces, + attributes: pass_attrs_along!(namespace), }) } @@ -151,6 +158,7 @@ impl Parser { identifier, inputs, output, + attributes: pass_attrs_along!(function), }) } @@ -164,10 +172,20 @@ impl Parser { let mut states = vec![]; for mut state in enumeration.states { - states.push(mem::take(&mut state.kind).into()) + states.push({ + let ident: Identifier = mem::take(&mut state.token.kind).into(); + DocIdentifier { + name: ident.name, + attributes: pass_attrs_along!(state), + } + }) } - Ok(Enumeration { identifier, states }) + Ok(Enumeration { + identifier, + states, + attributes: pass_attrs_along!(enumeration), + }) } fn process_structure( @@ -179,12 +197,13 @@ impl Parser { let identifier: Identifier = mem::take(&mut structure.identifier.kind).into(); let mut contents = vec![]; for named_type in structure.contents { - contents.push(self.process_named_type(named_type)?); + contents.push(self.process_doc_named_type(named_type)?); } Ok(Structure { identifier, contents, + attributes: pass_attrs_along!(structure), }) } @@ -196,6 +215,18 @@ impl Parser { let r#type: Type = self.process_type(named_type.r#type)?; Ok(NamedType { name, r#type }) } + fn process_doc_named_type( + &mut self, + mut doc_named_type: UncheckedDocNamedType, + ) -> Result { + let name: Identifier = mem::take(&mut doc_named_type.name.kind).into(); + let r#type: Type = self.process_type(doc_named_type.r#type)?; + Ok(DocNamedType { + name, + r#type, + attributes: pass_attrs_along!(doc_named_type), + }) + } fn process_type(&mut self, mut r#type: UncheckedType) -> Result { let identifier: Identifier = mem::take(&mut r#type.identifier.kind).into(); diff --git a/trixy/trixy-lang_parser/src/parsing/checked/test.rs b/trixy/trixy-lang_parser/src/parsing/checked/test.rs index 2326b11..53e27a9 100644 --- a/trixy/trixy-lang_parser/src/parsing/checked/test.rs +++ b/trixy/trixy-lang_parser/src/parsing/checked/test.rs @@ -1,8 +1,11 @@ use crate::command_spec::checked::{ - CommandSpec, Enumeration, Function, Identifier, NamedType, Namespace, Structure, Type, + Attribute, CommandSpec, DocIdentifier, DocNamedType, Enumeration, Function, Identifier, + NamedType, Namespace, Structure, Type, }; use crate::lexing::TokenStream; +use pretty_assertions::assert_eq; + #[test] fn test_full() { let input = "nasp trinitrix { @@ -57,13 +60,14 @@ fn test_full() { }, ], output: None, + attributes: vec![], }], structures: vec![Structure { identifier: Identifier { name: "Callback".to_owned(), }, contents: vec![ - NamedType { + DocNamedType { name: Identifier { name: "func".to_owned(), }, @@ -73,8 +77,9 @@ fn test_full() { }, generic_args: vec![], }, + attributes: vec![], }, - NamedType { + DocNamedType { name: Identifier { name: "timeout".to_owned(), }, @@ -84,26 +89,33 @@ fn test_full() { }, generic_args: vec![], }, + attributes: vec![], }, ], + attributes: vec![], }], enumerations: vec![Enumeration { identifier: Identifier { name: "CallbackPriority".to_owned(), }, states: vec![ - Identifier { + DocIdentifier { name: "High".to_owned(), + attributes: vec![], }, - Identifier { + DocIdentifier { name: "Medium".to_owned(), + attributes: vec![], }, - Identifier { + DocIdentifier { name: "Low".to_owned(), + attributes: vec![], }, ], + attributes: vec![], }], namespaces: vec![], + attributes: vec![], }], }; assert_eq!(output, expected); @@ -132,3 +144,72 @@ fn execute_callback(callback: Name); _ => panic!("Wrong error in test!"), }; } + +#[test] +fn test_comments() { + let input = "fn print(message: String); + +/// First doc comment +// Some more text +nasp trinitrix { + /// Second doc comment + fn hi(name: String) -> String; +} +"; + let output = TokenStream::lex(&input).unwrap().parse().unwrap(); + let expected = CommandSpec { + structures: vec![], + enumerations: vec![], + functions: vec![Function { + identifier: Identifier { + name: "print".to_owned(), + }, + inputs: vec![NamedType { + name: Identifier { + name: "message".to_owned(), + }, + r#type: Type { + identifier: Identifier { + name: "String".to_owned(), + }, + generic_args: vec![], + }, + }], + output: None, + attributes: vec![], + }], + namespaces: vec![Namespace { + name: Identifier { + name: "trinitrix".to_owned(), + }, + functions: vec![Function { + identifier: Identifier { + name: "hi".to_owned(), + }, + inputs: vec![NamedType { + name: Identifier { + name: "name".to_owned(), + }, + r#type: Type { + identifier: Identifier { + name: "String".to_owned(), + }, + generic_args: vec![], + }, + }], + output: Some(Type { + identifier: Identifier { + name: "String".to_owned(), + }, + generic_args: vec![], + }), + attributes: vec![Attribute::doc("Second doc comment".to_owned())], + }], + structures: vec![], + enumerations: vec![], + namespaces: vec![], + attributes: vec![Attribute::doc("First doc comment".to_owned())], + }], + }; + assert_eq!(output, expected); +} diff --git a/trixy/trixy-lang_parser/src/parsing/unchecked/error.rs b/trixy/trixy-lang_parser/src/parsing/unchecked/error.rs index d697087..5d5270c 100644 --- a/trixy/trixy-lang_parser/src/parsing/unchecked/error.rs +++ b/trixy/trixy-lang_parser/src/parsing/unchecked/error.rs @@ -2,37 +2,47 @@ use std::{error::Error, fmt::Display}; use thiserror::Error; use crate::{ + command_spec::unchecked::Attribute, error::{AdditionalHelp, ErrorContext, ErrorContextDisplay}, lexing::{TokenKind, TokenSpan}, }; #[derive(Error, Debug, Clone)] pub enum ParsingError { - #[error("Expected '{expected}' but received '{actual}'")] + #[error("Expected '{expected}', but received: '{actual}'")] ExpectedDifferentToken { expected: TokenKind, actual: TokenKind, span: TokenSpan, }, + #[error("Expected '{expected}', but the token stream stopped")] + UnexpectedEOF { + expected: TokenKind, + span: TokenSpan, + }, + #[error("Expected a Keyword to start a new declaration, but found: '{actual}'")] ExpectedKeyword { actual: TokenKind, span: TokenSpan }, + + #[error("DocComment does not have target")] + TrailingDocComment { + comments: Vec, + span: TokenSpan, + }, } impl ParsingError { pub fn span(&self) -> &TokenSpan { match self { ParsingError::ExpectedDifferentToken { span, .. } => span, ParsingError::ExpectedKeyword { span, .. } => span, + ParsingError::TrailingDocComment { span, .. } => span, + ParsingError::UnexpectedEOF { span, .. } => span, } } -} -impl ParsingError { pub fn get_span(&self) -> TokenSpan { - match self { - ParsingError::ExpectedDifferentToken { span, .. } => *span, - ParsingError::ExpectedKeyword { span, .. } => *span, - } + *self.span() } } @@ -50,6 +60,8 @@ impl AdditionalHelp for ParsingError { ParsingError::ExpectedKeyword { actual, .. } => format!( "I expected a keyword (that is something like 'fn' or 'nasp') but you put a '{}' there!", actual), + ParsingError::TrailingDocComment { .. } => "I expected some target (a function, namespace, enum, or something like this) which this doc comment annotates, but you put nothing there".to_owned(), + ParsingError::UnexpectedEOF { expected, .. } => format!("Put the expected token ('{expected}') here."), } } } diff --git a/trixy/trixy-lang_parser/src/parsing/unchecked/mod.rs b/trixy/trixy-lang_parser/src/parsing/unchecked/mod.rs index 622f870..c6db5a2 100644 --- a/trixy/trixy-lang_parser/src/parsing/unchecked/mod.rs +++ b/trixy/trixy-lang_parser/src/parsing/unchecked/mod.rs @@ -1,9 +1,12 @@ +use std::mem; + use crate::{ command_spec::unchecked::{ - CommandSpec, Declaration, Enumeration, Function, NamedType, Namespace, Structure, Type, + Attribute, CommandSpec, Declaration, DocNamedType, DocToken, Enumeration, Function, + NamedType, Namespace, Structure, Type, }, error::ErrorContext, - lexing::{Token, TokenKind, TokenStream}, + lexing::{Token, TokenKind, TokenSpan, TokenStream}, token, }; @@ -22,12 +25,18 @@ impl TokenStream { pub(super) struct Parser { token_stream: TokenStream, + active_doc_comments: Vec, + last_span: TokenSpan, } impl Parser { fn new(mut token_stream: TokenStream) -> Self { token_stream.reverse(); - Self { token_stream } + Self { + token_stream, + active_doc_comments: vec![], + last_span: TokenSpan::default(), + } } fn parse(&mut self) -> Result { @@ -52,15 +61,55 @@ impl Parser { } fn parse_next(&mut self) -> Result { - match self.peek().kind() { + // Use of [peek_raw] here is fine, as we know that the function is only called, when + // something should still be contained in the token stream + match self.peek_raw().kind() { token![nasp] => Ok(Declaration::Namespace(self.parse_namespace()?)), token![fn] => Ok(Declaration::Function(self.parse_function()?)), token![struct] => Ok(Declaration::Structure(self.parse_structure()?)), token![enum] => Ok(Declaration::Enumeration(self.parse_enumeration()?)), + token![DocCommentMatch] => { + while self.expect_peek(token![DocComment]) { + let comment_to_push = { + let doc_comment = self.expect(token![DocComment])?; + let span = *doc_comment.span(); + let name = if let TokenKind::DocComment(content) = doc_comment.kind { + content + } else { + unreachable!("The expect should have accounted for that case"); + }; + + Attribute::doc { + content: name, + span, + } + }; + self.active_doc_comments.push(comment_to_push); + } + + if self.token_stream.is_empty() { + fn get_span(attr: Option<&Attribute>) -> TokenSpan { + match attr.expect("Something should be here") { + Attribute::doc { span, .. } => *span, + } + } + + let span = TokenSpan::from_range( + get_span(self.active_doc_comments.first()), + get_span(self.active_doc_comments.last()), + ); + Err(ParsingError::TrailingDocComment { + comments: mem::take(&mut self.active_doc_comments), + span, + }) + } else { + self.parse_next() + } + } _ => { let err = ParsingError::ExpectedKeyword { - span: *self.peek().span(), - actual: self.peek().kind().clone(), + span: *self.peek_raw().span(), + actual: self.peek_raw().kind().clone(), }; return Err(err); @@ -88,11 +137,34 @@ impl Parser { }) } + fn parse_doc_comments(&mut self) -> Result, ParsingError> { + let mut attrs = mem::take(&mut self.active_doc_comments); + + while self.expect_peek(token![DocComment]) { + attrs.push({ + let doc_comment = self.expect(token![DocComment])?; + let span = *doc_comment.span(); + let name = if let TokenKind::DocComment(content) = doc_comment.kind { + content + } else { + unreachable!("The expect should have accounted for that case"); + }; + Attribute::doc { + content: name, + span, + } + }); + } + Ok(attrs) + } + fn parse_namespace(&mut self) -> Result { + let attributes = self.parse_doc_comments()?; self.expect(token![nasp])?; let mut namespace = Namespace::default(); namespace.name = self.expect(token![Ident])?; + namespace.attributes = attributes; self.expect(token![BraceOpen])?; @@ -113,40 +185,54 @@ impl Parser { } fn parse_enumeration(&mut self) -> Result { + let attributes = self.parse_doc_comments()?; self.expect(token![enum])?; let identifier = self.expect(token![Ident])?; self.expect(token![BraceOpen])?; let mut states = vec![]; if self.expect_peek(token![Ident]) { - states.push(self.expect(token![Ident])?); + let attributes = self.parse_doc_comments()?; + states.push(DocToken { + token: self.expect(token![Ident])?, + attributes, + }); } while self.expect_peek(token![Comma]) { self.expect(token![Comma])?; if self.expect_peek(token![Ident]) { - states.push(self.expect(token![Ident])?); + let attributes = self.parse_doc_comments()?; + states.push(DocToken { + token: self.expect(token![Ident])?, + attributes, + }); } else { break; } } self.expect(token![BraceClose])?; self.expect(token![;])?; - Ok(Enumeration { identifier, states }) + Ok(Enumeration { + identifier, + states, + attributes, + }) } fn parse_structure(&mut self) -> Result { + let attributes = self.parse_doc_comments()?; self.expect(token![struct])?; let name = self.expect(token![Ident])?; self.expect(token![BraceOpen])?; let mut contents = vec![]; if self.expect_peek(token![Ident]) { - contents.push(self.parse_named_type()?); + contents.push(self.parse_doc_named_type()?); } while self.expect_peek(token![Comma]) { self.expect(token![Comma])?; if self.expect_peek(token![Ident]) { - contents.push(self.parse_named_type()?); + contents.push(self.parse_doc_named_type()?); } else { break; } @@ -157,6 +243,7 @@ impl Parser { Ok(Structure { identifier: name, contents, + attributes, }) } @@ -167,7 +254,20 @@ impl Parser { Ok(NamedType { name, r#type }) } + fn parse_doc_named_type(&mut self) -> Result { + let attributes = self.parse_doc_comments()?; + let name = self.expect(token![Ident])?; + self.expect(token![Colon])?; + let r#type = self.parse_type()?; + Ok(DocNamedType { + name, + r#type, + attributes, + }) + } + fn parse_function(&mut self) -> Result { + let attributes = self.parse_doc_comments()?; self.expect(token![fn])?; let name = self.expect(token![Ident])?; self.expect(token![ParenOpen])?; @@ -192,6 +292,7 @@ impl Parser { identifier: name, inputs, output: output_type, + attributes, }) } @@ -216,7 +317,14 @@ impl Parser { /// ``` /// pub(super) fn expect(&mut self, token: TokenKind) -> Result { - let actual_token = self.peek(); + let actual_token = if let Some(token) = self.peek() { + token + } else { + return Err(ParsingError::UnexpectedEOF { + expected: token, + span: self.last_span, + }); + }; if actual_token.kind().same_kind(&token) { Ok(self.pop()) } else { @@ -233,7 +341,10 @@ impl Parser { /// Check if the next token is of the specified TokenKind. /// Does not alter the token_stream fn expect_peek(&self, token: TokenKind) -> bool { - let actual_token = self.peek(); + let actual_token = match self.peek() { + Some(ok) => ok, + None => return false, + }; if actual_token.kind().same_kind(&token) { true } else { @@ -242,12 +353,22 @@ impl Parser { } /// Looks at the next token without removing it - fn peek(&self) -> &Token { + fn peek(&self) -> Option<&Token> { self.token_stream.peek() } + /// Looks at the next token without removing it. + /// Unwraps the option returned from [peek], only use it, if you know that a token must exist + fn peek_raw(&self) -> &Token { + self.token_stream.peek().expect("The token should exist") + } + /// Removes the next token fn pop(&mut self) -> Token { + self.last_span = *self + .peek() + .expect("Calling pop should mean, that a token was first peeked for") + .span(); self.token_stream.pop() } } diff --git a/trixy/trixy-lang_parser/src/parsing/unchecked/test.rs b/trixy/trixy-lang_parser/src/parsing/unchecked/test.rs index a6627bf..ccf9b69 100644 --- a/trixy/trixy-lang_parser/src/parsing/unchecked/test.rs +++ b/trixy/trixy-lang_parser/src/parsing/unchecked/test.rs @@ -20,8 +20,8 @@ nasp trinitrix { {} let parsed = TokenStream::lex(input).unwrap().parse_unchecked(); let err = parsed.unwrap_err().source; match err { - ParsingError::ExpectedDifferentToken { .. } => panic!("Wrong error"), ParsingError::ExpectedKeyword { .. } => {} + _ => panic!("Wrong error"), } } @@ -56,6 +56,7 @@ nasp trinitrix { }, }], output: None, + attributes: vec![], }], namespaces: vec![Namespace { name: Token { @@ -87,10 +88,12 @@ nasp trinitrix { }, generic_args: vec![], }), + attributes: vec![], }], structures: vec![], enumerations: vec![], namespaces: vec![], + attributes: vec![], }], };