diff --git a/Cargo.toml b/Cargo.toml index 6f2c772..de25874 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,10 +7,13 @@ edition = "2021" [features] default = ["lua", "shulkerbox"] +serde = ["dep:serde", "shulkerbox?/serde"] shulkerbox = ["dep:shulkerbox"] -serde = ["dep:serde"] lua = ["dep:mlua"] +[target.'cfg(target_arch = "wasm32")'.dependencies] +path-absolutize = { version = "3.1.1", features = ["use_unix_paths_on_wasm"] } + [dependencies] chksum-md5 = "0.0.0" colored = "2.1.0" diff --git a/grammar.md b/grammar.md index f356aa6..469d952 100644 --- a/grammar.md +++ b/grammar.md @@ -4,7 +4,12 @@ ### Program ```ebnf -Program: Declaration*; +Program: Namespace Declaration*; +``` + +### Namespace +```ebnf +Namespace: 'namespace' StringLiteral; ``` ### Declaration @@ -15,7 +20,7 @@ Declaration: FunctionDeclaration; ### FunctionDeclaration ```ebnf Function: - Annotation* 'fn' Identifier '(' ParameterList? ')' Block + Annotation* 'pub'? 'fn' Identifier '(' ParameterList? ')' Block ; ParameterList: Identifier (',' Identifier)* ','? diff --git a/src/lexical/token.rs b/src/lexical/token.rs index 02bf545..61afef0 100644 --- a/src/lexical/token.rs +++ b/src/lexical/token.rs @@ -19,6 +19,7 @@ use super::{error::UnterminatedDelimitedComment, Error}; #[allow(missing_docs)] pub enum KeywordKind { Function, + Pub, If, Else, Align, @@ -76,6 +77,7 @@ impl KeywordKind { pub fn as_str(self) -> &'static str { match self { Self::Function => "fn", + Self::Pub => "pub", Self::If => "if", Self::Else => "else", Self::Align => "align", diff --git a/src/syntax/syntax_tree/declaration.rs b/src/syntax/syntax_tree/declaration.rs index d368ab6..86b67a0 100644 --- a/src/syntax/syntax_tree/declaration.rs +++ b/src/syntax/syntax_tree/declaration.rs @@ -97,7 +97,7 @@ impl SourceElement for Annotation { /// /// ``` ebnf /// Function: -/// Annotation* 'fn' Identifier '(' ParameterList? ')' Block +/// Annotation* 'pub'? 'fn' Identifier '(' ParameterList? ')' Block /// ; /// /// ParameterList: @@ -107,6 +107,8 @@ impl SourceElement for Annotation { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] pub struct Function { + #[get = "pub"] + public_keyword: Option, #[get = "pub"] annotations: Vec, #[get = "pub"] @@ -130,6 +132,7 @@ impl Function { pub fn dissolve( self, ) -> ( + Option, Vec, Keyword, Identifier, @@ -139,6 +142,7 @@ impl Function { Block, ) { ( + self.public_keyword, self.annotations, self.function_keyword, self.identifier, @@ -148,11 +152,21 @@ impl Function { self.block, ) } + + /// Returns `true` if the function is public. + #[must_use] + pub fn is_public(&self) -> bool { + self.public_keyword.is_some() + } } impl SourceElement for Function { fn span(&self) -> Span { - self.function_keyword.span.join(&self.block.span()).unwrap() + self.public_keyword + .as_ref() + .map_or_else(|| self.function_keyword.span(), SourceElement::span) + .join(&self.block.span()) + .unwrap() } } @@ -231,6 +245,7 @@ impl<'a> Parser<'a> { let block = self.parse_block(handler)?; Some(Declaration::Function(Function { + public_keyword: None, annotations: Vec::new(), function_keyword, identifier, @@ -241,6 +256,55 @@ impl<'a> Parser<'a> { })) } + Reading::Atomic(Token::Keyword(pub_keyword)) + if pub_keyword.keyword == KeywordKind::Pub => + { + // eat the public keyword + self.forward(); + + // parse the function keyword + let function_keyword_reading = self.next_significant_token(); + + match function_keyword_reading { + Reading::Atomic(Token::Keyword(function_keyword)) + if function_keyword.keyword == KeywordKind::Function => + { + // eat the function keyword + self.forward(); + + // parse the identifier + let identifier = self.parse_identifier(handler)?; + let delimited_tree = self.parse_enclosed_list( + Delimiter::Parenthesis, + ',', + |parser: &mut Parser<'_>| parser.parse_identifier(handler), + handler, + )?; + + // parse the block + let block = self.parse_block(handler)?; + + Some(Declaration::Function(Function { + public_keyword: Some(pub_keyword), + annotations: Vec::new(), + function_keyword, + identifier, + open_paren: delimited_tree.open, + parameters: delimited_tree.list, + close_paren: delimited_tree.close, + block, + })) + } + unexpected => { + handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax { + expected: SyntaxKind::Keyword(KeywordKind::Function), + found: unexpected.into_token(), + })); + None + } + } + } + // parse annotations Reading::Atomic(Token::Punctuation(punctuation)) if punctuation.punctuation == '#' => { // parse the annotation