Implement annotations for functions
This commit is contained in:
parent
d4305b3629
commit
b9bc5438e5
|
@ -15,13 +15,18 @@ Declaration: FunctionDeclaration;
|
||||||
### FunctionDeclaration
|
### FunctionDeclaration
|
||||||
```ebnf
|
```ebnf
|
||||||
Function:
|
Function:
|
||||||
'fn' Identifier '(' ParameterList? ')' Block
|
Annotation* 'fn' Identifier '(' ParameterList? ')' Block
|
||||||
;
|
;
|
||||||
ParameterList:
|
ParameterList:
|
||||||
Identifier (',' Identifier)* ','?
|
Identifier (',' Identifier)* ','?
|
||||||
;
|
;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Annotation
|
||||||
|
```ebnf
|
||||||
|
Annotation: '#[' Identifier ('=' StringLiteral)? ']';
|
||||||
|
```
|
||||||
|
|
||||||
### Statement
|
### Statement
|
||||||
```ebnf
|
```ebnf
|
||||||
Statement:
|
Statement:
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::{
|
||||||
fs,
|
fs,
|
||||||
iter::{Iterator, Peekable},
|
iter::{Iterator, Peekable},
|
||||||
ops::Range,
|
ops::Range,
|
||||||
path::PathBuf,
|
path::{Path, PathBuf},
|
||||||
str::CharIndices,
|
str::CharIndices,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
@ -85,9 +85,9 @@ impl SourceFile {
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - [`Error::IoError`]: Error occurred when reading the file contents.
|
/// - [`Error::IoError`]: Error occurred when reading the file contents.
|
||||||
pub fn load(path: PathBuf) -> Result<Arc<Self>, Error> {
|
pub fn load(path: &Path) -> Result<Arc<Self>, Error> {
|
||||||
let source = fs::read_to_string(&path).map_err(Error::IoError)?;
|
let source = fs::read_to_string(path).map_err(Error::IoError)?;
|
||||||
Ok(Self::new(path, source))
|
Ok(Self::new(path.to_path_buf(), source))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the [`Location`] of a given byte index
|
/// Get the [`Location`] of a given byte index
|
||||||
|
|
|
@ -7,6 +7,7 @@ use crate::base::{
|
||||||
Handler,
|
Handler,
|
||||||
};
|
};
|
||||||
use derive_more::From;
|
use derive_more::From;
|
||||||
|
use enum_as_inner::EnumAsInner;
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
use strum_macros::EnumIter;
|
use strum_macros::EnumIter;
|
||||||
|
|
||||||
|
@ -64,8 +65,8 @@ impl KeywordKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is an enumeration containing all kinds of tokens in the Flux programming language.
|
/// Is an enumeration containing all kinds of tokens in the Shulkerscript programming language.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, From)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, From, EnumAsInner)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
WhiteSpaces(WhiteSpaces),
|
WhiteSpaces(WhiteSpaces),
|
||||||
|
@ -202,7 +203,7 @@ impl SourceElement for StringLiteral {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is an enumeration representing the two kinds of comments in the Flux programming language.
|
/// Is an enumeration representing the two kinds of comments in the Shulkerscript programming language.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum CommentKind {
|
pub enum CommentKind {
|
||||||
/// A comment that starts with `//` and ends at the end of the line.
|
/// A comment that starts with `//` and ends at the end of the line.
|
||||||
|
|
|
@ -173,8 +173,11 @@ pub enum TokenTree {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub enum Delimiter {
|
pub enum Delimiter {
|
||||||
|
/// ()
|
||||||
Parenthesis,
|
Parenthesis,
|
||||||
|
/// {}
|
||||||
Brace,
|
Brace,
|
||||||
|
/// []
|
||||||
Bracket,
|
Bracket,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
43
src/lib.rs
43
src/lib.rs
|
@ -17,19 +17,58 @@ pub mod compile;
|
||||||
pub mod lexical;
|
pub mod lexical;
|
||||||
pub mod syntax;
|
pub mod syntax;
|
||||||
|
|
||||||
use std::{cell::Cell, fmt::Display, path::PathBuf};
|
use std::{cell::Cell, fmt::Display, path::Path};
|
||||||
|
|
||||||
use base::{source_file::SourceFile, Handler, Result};
|
use base::{source_file::SourceFile, Handler, Result};
|
||||||
use compile::compiler::Compiler;
|
use compile::compiler::Compiler;
|
||||||
use shulkerbox::{util::compile::CompileOptions, virtual_fs::VFolder};
|
use shulkerbox::{util::compile::CompileOptions, virtual_fs::VFolder};
|
||||||
|
use syntax::syntax_tree::program::Program;
|
||||||
|
|
||||||
use crate::{base::Error, lexical::token_stream::TokenStream, syntax::parser::Parser};
|
use crate::{base::Error, lexical::token_stream::TokenStream, syntax::parser::Parser};
|
||||||
|
|
||||||
|
/// Converts the given source code to tokens.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// - If an error occurs while reading the file.
|
||||||
|
pub fn tokenize(path: &Path) -> Result<TokenStream> {
|
||||||
|
let source_file = SourceFile::load(path)?;
|
||||||
|
|
||||||
|
let printer = Printer::new();
|
||||||
|
|
||||||
|
Ok(TokenStream::tokenize(&source_file, &printer))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses the given source code.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// - If an error occurs while reading the file.
|
||||||
|
/// - If an error occurs while parsing the source code.
|
||||||
|
pub fn parse(path: &Path) -> Result<Program> {
|
||||||
|
let source_file = SourceFile::load(path)?;
|
||||||
|
|
||||||
|
let printer = Printer::new();
|
||||||
|
|
||||||
|
let tokens = TokenStream::tokenize(&source_file, &printer);
|
||||||
|
|
||||||
|
if printer.has_printed() {
|
||||||
|
return Err(Error::Other(
|
||||||
|
"An error occurred while tokenizing the source code.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut parser = Parser::new(&tokens);
|
||||||
|
let program = parser.parse_program(&printer).ok_or(Error::Other(
|
||||||
|
"An error occured while parsing the source code.",
|
||||||
|
))?;
|
||||||
|
|
||||||
|
Ok(program)
|
||||||
|
}
|
||||||
|
|
||||||
/// Compiles the given source code.
|
/// Compiles the given source code.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - If an error occurs while reading the file.
|
/// - If an error occurs while reading the file.
|
||||||
pub fn compile(path: PathBuf) -> Result<VFolder> {
|
pub fn compile(path: &Path) -> Result<VFolder> {
|
||||||
let source_file = SourceFile::load(path)?;
|
let source_file = SourceFile::load(path)?;
|
||||||
|
|
||||||
let printer = Printer::new();
|
let printer = Printer::new();
|
||||||
|
|
|
@ -142,6 +142,20 @@ impl<'a> Parser<'a> {
|
||||||
close: close_punctuation,
|
close: close_punctuation,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tries to parse the given function, and if it fails, resets the current index to the
|
||||||
|
/// `current_index` before the function call.
|
||||||
|
pub fn try_parse<T>(&mut self, f: impl FnOnce(&mut Self) -> Option<T>) -> Option<T> {
|
||||||
|
let current_index = self.current_frame.current_index;
|
||||||
|
|
||||||
|
let result = f(self);
|
||||||
|
|
||||||
|
if result.is_none() {
|
||||||
|
self.current_frame.current_index = current_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a result of [`Parser::step_into()`] function.
|
/// Represents a result of [`Parser::step_into()`] function.
|
||||||
|
@ -409,20 +423,6 @@ impl<'a> Frame<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to parse the given function, and if it fails, resets the current index to the
|
|
||||||
/// `current_index` before the function call.
|
|
||||||
pub fn try_parse<T>(&mut self, f: impl FnOnce(&mut Self) -> Option<T>) -> Option<T> {
|
|
||||||
let current_index = self.current_index;
|
|
||||||
|
|
||||||
let result = f(self);
|
|
||||||
|
|
||||||
if result.is_none() {
|
|
||||||
self.current_index = current_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the read value of the [`Frame`].
|
/// Represents the read value of the [`Frame`].
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
Handler,
|
Handler,
|
||||||
},
|
},
|
||||||
lexical::{
|
lexical::{
|
||||||
token::{Identifier, Keyword, KeywordKind, Punctuation, Token},
|
token::{Identifier, Keyword, KeywordKind, Punctuation, StringLiteral, Token},
|
||||||
token_stream::Delimiter,
|
token_stream::Delimiter,
|
||||||
},
|
},
|
||||||
syntax::{
|
syntax::{
|
||||||
|
@ -33,12 +33,62 @@ impl SourceElement for Declaration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ``` ebnf
|
||||||
|
/// Annotation:
|
||||||
|
/// '#[' Identifier ('=' StringLiteral)? ']'
|
||||||
|
/// ;
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||||
|
pub struct Annotation {
|
||||||
|
#[get = "pub"]
|
||||||
|
pound_sign: Punctuation,
|
||||||
|
#[get = "pub"]
|
||||||
|
open_bracket: Punctuation,
|
||||||
|
#[get = "pub"]
|
||||||
|
identifier: Identifier,
|
||||||
|
#[get = "pub"]
|
||||||
|
value: Option<(Punctuation, StringLiteral)>,
|
||||||
|
#[get = "pub"]
|
||||||
|
close_bracket: Punctuation,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Annotation {
|
||||||
|
/// Dissolves the [`Annotation`] into its components.
|
||||||
|
#[must_use]
|
||||||
|
pub fn dissolve(
|
||||||
|
self,
|
||||||
|
) -> (
|
||||||
|
Punctuation,
|
||||||
|
Punctuation,
|
||||||
|
Identifier,
|
||||||
|
Option<(Punctuation, StringLiteral)>,
|
||||||
|
Punctuation,
|
||||||
|
) {
|
||||||
|
(
|
||||||
|
self.pound_sign,
|
||||||
|
self.open_bracket,
|
||||||
|
self.identifier,
|
||||||
|
self.value,
|
||||||
|
self.close_bracket,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl SourceElement for Annotation {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
self.pound_sign
|
||||||
|
.span
|
||||||
|
.join(&self.close_bracket.span())
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Syntax Synopsis:
|
/// Syntax Synopsis:
|
||||||
///
|
///
|
||||||
/// ``` ebnf
|
/// ``` ebnf
|
||||||
/// Function:
|
/// Function:
|
||||||
/// 'fn' Identifier '(' ParameterList? ')' Block
|
/// Annotation* 'fn' Identifier '(' ParameterList? ')' Block
|
||||||
/// ;
|
/// ;
|
||||||
///
|
///
|
||||||
/// ParameterList:
|
/// ParameterList:
|
||||||
|
@ -47,6 +97,8 @@ impl SourceElement for Declaration {
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
|
#[get = "pub"]
|
||||||
|
annotations: Vec<Annotation>,
|
||||||
#[get = "pub"]
|
#[get = "pub"]
|
||||||
function_keyword: Keyword,
|
function_keyword: Keyword,
|
||||||
#[get = "pub"]
|
#[get = "pub"]
|
||||||
|
@ -64,9 +116,11 @@ pub struct Function {
|
||||||
impl Function {
|
impl Function {
|
||||||
/// Dissolves the [`Function`] into its components.
|
/// Dissolves the [`Function`] into its components.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
pub fn dissolve(
|
pub fn dissolve(
|
||||||
self,
|
self,
|
||||||
) -> (
|
) -> (
|
||||||
|
Vec<Annotation>,
|
||||||
Keyword,
|
Keyword,
|
||||||
Identifier,
|
Identifier,
|
||||||
Punctuation,
|
Punctuation,
|
||||||
|
@ -75,6 +129,7 @@ impl Function {
|
||||||
Block,
|
Block,
|
||||||
) {
|
) {
|
||||||
(
|
(
|
||||||
|
self.annotations,
|
||||||
self.function_keyword,
|
self.function_keyword,
|
||||||
self.identifier,
|
self.identifier,
|
||||||
self.open_paren,
|
self.open_paren,
|
||||||
|
@ -92,6 +147,59 @@ impl SourceElement for Function {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
|
pub fn parse_annotation(&mut self, handler: &impl Handler<Error>) -> Option<Annotation> {
|
||||||
|
match self.stop_at_significant() {
|
||||||
|
Reading::Atomic(Token::Punctuation(punctuation)) if punctuation.punctuation == '#' => {
|
||||||
|
// eat the pound sign
|
||||||
|
self.forward();
|
||||||
|
|
||||||
|
// step into the brackets
|
||||||
|
let content = self.step_into(
|
||||||
|
Delimiter::Bracket,
|
||||||
|
|parser| {
|
||||||
|
let identifier = parser.parse_identifier(handler)?;
|
||||||
|
|
||||||
|
let value = if let Reading::Atomic(Token::Punctuation(punctuation)) =
|
||||||
|
parser.stop_at_significant()
|
||||||
|
{
|
||||||
|
if punctuation.punctuation == '=' {
|
||||||
|
// eat the equals sign
|
||||||
|
parser.forward();
|
||||||
|
|
||||||
|
// parse the string literal
|
||||||
|
let string_literal = parser
|
||||||
|
.next_significant_token()
|
||||||
|
.into_token()?
|
||||||
|
.into_string_literal()
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
Some((punctuation, string_literal))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Some((identifier, value))
|
||||||
|
},
|
||||||
|
handler,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let (identifier, value) = content.tree?;
|
||||||
|
|
||||||
|
Some(Annotation {
|
||||||
|
pound_sign: punctuation,
|
||||||
|
open_bracket: content.open,
|
||||||
|
identifier,
|
||||||
|
value,
|
||||||
|
close_bracket: content.close,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_declaration(&mut self, handler: &impl Handler<Error>) -> Option<Declaration> {
|
pub fn parse_declaration(&mut self, handler: &impl Handler<Error>) -> Option<Declaration> {
|
||||||
match self.stop_at_significant() {
|
match self.stop_at_significant() {
|
||||||
Reading::Atomic(Token::Keyword(function_keyword))
|
Reading::Atomic(Token::Keyword(function_keyword))
|
||||||
|
@ -113,6 +221,7 @@ impl<'a> Parser<'a> {
|
||||||
let block = self.parse_block(handler)?;
|
let block = self.parse_block(handler)?;
|
||||||
|
|
||||||
Some(Declaration::Function(Function {
|
Some(Declaration::Function(Function {
|
||||||
|
annotations: Vec::new(),
|
||||||
function_keyword,
|
function_keyword,
|
||||||
identifier,
|
identifier,
|
||||||
open_paren: delimited_tree.open,
|
open_paren: delimited_tree.open,
|
||||||
|
@ -122,6 +231,27 @@ impl<'a> Parser<'a> {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse annotations
|
||||||
|
Reading::Atomic(Token::Punctuation(punctuation)) if punctuation.punctuation == '#' => {
|
||||||
|
// parse the annotation
|
||||||
|
let mut annotations = Vec::new();
|
||||||
|
|
||||||
|
while let Some(annotation) =
|
||||||
|
self.try_parse(|parser| parser.parse_annotation(handler))
|
||||||
|
{
|
||||||
|
annotations.push(annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the function
|
||||||
|
self.parse_declaration(handler)
|
||||||
|
.map(|declaration| match declaration {
|
||||||
|
Declaration::Function(mut function) => {
|
||||||
|
function.annotations.extend(annotations);
|
||||||
|
Declaration::Function(function)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
unexpected => {
|
unexpected => {
|
||||||
// make progress
|
// make progress
|
||||||
self.forward();
|
self.forward();
|
||||||
|
|
Loading…
Reference in New Issue