Introduce namespace configuration syntax
This commit is contained in:
parent
01040964af
commit
b105a45154
|
@ -1,4 +1,4 @@
|
|||
# Grammar of the shulkerscript language
|
||||
# Grammar of the ShulkerScript language
|
||||
|
||||
## Table of contents
|
||||
|
||||
|
@ -126,14 +126,14 @@ Expression:
|
|||
```
|
||||
|
||||
### Primary
|
||||
``` ebnf
|
||||
```ebnf
|
||||
Primary:
|
||||
FunctionCall
|
||||
;
|
||||
```
|
||||
|
||||
### FunctionCall
|
||||
``` ebnf
|
||||
```ebnf
|
||||
FunctionCall:
|
||||
Identifier '(' (Expression (',' Expression)*)? ')'
|
||||
;
|
||||
|
|
|
@ -24,6 +24,7 @@ pub enum KeywordKind {
|
|||
Group,
|
||||
Run,
|
||||
Lua,
|
||||
Namespace,
|
||||
}
|
||||
|
||||
impl ToString for KeywordKind {
|
||||
|
@ -68,6 +69,7 @@ impl KeywordKind {
|
|||
Self::Group => "group",
|
||||
Self::Run => "run",
|
||||
Self::Lua => "lua",
|
||||
Self::Namespace => "namespace",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ pub mod transpile;
|
|||
use std::{cell::Cell, fmt::Display, path::Path};
|
||||
|
||||
use base::{source_file::SourceFile, Handler, Result};
|
||||
use syntax::syntax_tree::program::Program;
|
||||
use syntax::syntax_tree::program::ProgramFile;
|
||||
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
use transpile::transpiler::Transpiler;
|
||||
|
@ -49,7 +49,7 @@ pub fn tokenize(path: &Path) -> Result<TokenStream> {
|
|||
/// # 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> {
|
||||
pub fn parse(path: &Path) -> Result<ProgramFile> {
|
||||
let source_file = SourceFile::load(path)?;
|
||||
|
||||
let printer = Printer::new();
|
||||
|
|
|
@ -21,6 +21,13 @@ use crate::{
|
|||
|
||||
use super::{statement::Block, ConnectedList};
|
||||
|
||||
/// Syntax Synopsis:
|
||||
///
|
||||
/// ``` ebnf
|
||||
/// Declaration:
|
||||
/// Function
|
||||
/// ;
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Declaration {
|
||||
|
|
|
@ -3,27 +3,128 @@
|
|||
use getset::Getters;
|
||||
|
||||
use crate::{
|
||||
base::Handler,
|
||||
base::{
|
||||
source_file::{SourceElement, Span},
|
||||
Handler,
|
||||
},
|
||||
lexical::token::{Keyword, KeywordKind, Punctuation, StringLiteral, Token},
|
||||
syntax::{
|
||||
error::Error,
|
||||
error::{Error, SyntaxKind, UnexpectedSyntax},
|
||||
parser::{Parser, Reading},
|
||||
},
|
||||
};
|
||||
|
||||
use super::declaration::Declaration;
|
||||
|
||||
/// Program is a collection of declarations.
|
||||
/// Program is a collection of declarations preceeded by a namespace selector.
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||
pub struct Program {
|
||||
pub struct ProgramFile {
|
||||
/// The namespace selector.
|
||||
#[get = "pub"]
|
||||
namespace: Namespace,
|
||||
/// The declarations within the program.
|
||||
#[get = "pub"]
|
||||
declarations: Vec<Declaration>,
|
||||
}
|
||||
|
||||
/// Namespace is a namespace selector.
|
||||
///
|
||||
/// Syntax Synopsis:
|
||||
///
|
||||
/// ```ebnf
|
||||
/// Namespace:
|
||||
/// 'namespace' StringLiteral ';' ;
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Namespace {
|
||||
/// The `namespace` keyword.
|
||||
#[get = "pub"]
|
||||
namespace_keyword: Keyword,
|
||||
/// The name of the namespace.
|
||||
#[get = "pub"]
|
||||
namespace_name: StringLiteral,
|
||||
/// The semicolon.
|
||||
#[get = "pub"]
|
||||
semicolon: Punctuation,
|
||||
}
|
||||
|
||||
impl SourceElement for Namespace {
|
||||
fn span(&self) -> Span {
|
||||
self.namespace_keyword
|
||||
.span()
|
||||
.join(&self.semicolon.span())
|
||||
.expect("Invalid span")
|
||||
}
|
||||
}
|
||||
|
||||
impl Namespace {
|
||||
/// Dissolves the namespace into its components.
|
||||
#[must_use]
|
||||
pub fn dissolve(self) -> (Keyword, StringLiteral, Punctuation) {
|
||||
(self.namespace_keyword, self.namespace_name, self.semicolon)
|
||||
}
|
||||
|
||||
/// Validates a namespace string.
|
||||
#[must_use]
|
||||
pub fn validate_str(namespace: &str) -> bool {
|
||||
const VALID_CHARS: &str = "0123456789abcdefghijklmnopqrstuvwxyz_-.";
|
||||
|
||||
namespace.chars().all(|c| VALID_CHARS.contains(c))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
/// Parses a [`Program`].
|
||||
pub fn parse_program(&mut self, handler: &impl Handler<Error>) -> Option<Program> {
|
||||
/// Parses a [`ProgramFile`].
|
||||
pub fn parse_program(&mut self, handler: &impl Handler<Error>) -> Option<ProgramFile> {
|
||||
let namespace = match self.stop_at_significant() {
|
||||
Reading::Atomic(Token::Keyword(namespace_keyword))
|
||||
if namespace_keyword.keyword == KeywordKind::Namespace =>
|
||||
{
|
||||
// eat the keyword
|
||||
self.forward();
|
||||
|
||||
let namespace_name = match self.stop_at_significant() {
|
||||
Reading::Atomic(Token::StringLiteral(name)) => {
|
||||
self.forward();
|
||||
Some(name)
|
||||
}
|
||||
unexpected => {
|
||||
self.forward();
|
||||
handler.receive(
|
||||
UnexpectedSyntax {
|
||||
expected: SyntaxKind::StringLiteral,
|
||||
found: unexpected.into_token(),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
.and_then(|name| Namespace::validate_str(name.str_content()).then_some(name))?;
|
||||
|
||||
let semicolon = self.parse_punctuation(';', true, handler)?;
|
||||
|
||||
Some(Namespace {
|
||||
namespace_keyword,
|
||||
namespace_name,
|
||||
semicolon,
|
||||
})
|
||||
}
|
||||
unexpected => {
|
||||
handler.receive(
|
||||
UnexpectedSyntax {
|
||||
expected: SyntaxKind::Keyword(KeywordKind::Namespace),
|
||||
found: unexpected.into_token(),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
None
|
||||
}
|
||||
}?;
|
||||
|
||||
let mut declarations = Vec::new();
|
||||
|
||||
while !self.is_exhausted() {
|
||||
|
@ -44,6 +145,9 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
Some(Program { declarations })
|
||||
Some(ProgramFile {
|
||||
namespace,
|
||||
declarations,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
|||
syntax::syntax_tree::{
|
||||
declaration::Declaration,
|
||||
expression::{Expression, FunctionCall, Primary},
|
||||
program::Program,
|
||||
program::{Namespace, ProgramFile},
|
||||
statement::{Conditional, Statement},
|
||||
},
|
||||
};
|
||||
|
@ -55,11 +55,13 @@ impl Transpiler {
|
|||
/// - [`TranspileError::MissingFunctionDeclaration`] If a called function is missing
|
||||
pub fn transpile(
|
||||
&mut self,
|
||||
program: &Program,
|
||||
program: &ProgramFile,
|
||||
handler: &impl Handler<TranspileError>,
|
||||
) -> Result<(), TranspileError> {
|
||||
let namespace = program.namespace();
|
||||
|
||||
for declaration in program.declarations() {
|
||||
self.transpile_declaration(declaration, handler);
|
||||
self.transpile_declaration(declaration, namespace, handler);
|
||||
}
|
||||
|
||||
let mut always_transpile_functions = Vec::new();
|
||||
|
@ -88,6 +90,7 @@ impl Transpiler {
|
|||
fn transpile_declaration(
|
||||
&mut self,
|
||||
declaration: &Declaration,
|
||||
namespace: &Namespace,
|
||||
_handler: &impl Handler<TranspileError>,
|
||||
) {
|
||||
match declaration {
|
||||
|
@ -109,7 +112,7 @@ impl Transpiler {
|
|||
self.functions.write().unwrap().insert(
|
||||
name,
|
||||
FunctionData {
|
||||
namespace: "shulkerscript".to_string(),
|
||||
namespace: namespace.namespace_name().str_content().to_string(),
|
||||
statements,
|
||||
annotations,
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue