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
|
## Table of contents
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ pub enum KeywordKind {
|
||||||
Group,
|
Group,
|
||||||
Run,
|
Run,
|
||||||
Lua,
|
Lua,
|
||||||
|
Namespace,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for KeywordKind {
|
impl ToString for KeywordKind {
|
||||||
|
@ -68,6 +69,7 @@ impl KeywordKind {
|
||||||
Self::Group => "group",
|
Self::Group => "group",
|
||||||
Self::Run => "run",
|
Self::Run => "run",
|
||||||
Self::Lua => "lua",
|
Self::Lua => "lua",
|
||||||
|
Self::Namespace => "namespace",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ pub mod transpile;
|
||||||
use std::{cell::Cell, fmt::Display, path::Path};
|
use std::{cell::Cell, fmt::Display, path::Path};
|
||||||
|
|
||||||
use base::{source_file::SourceFile, Handler, Result};
|
use base::{source_file::SourceFile, Handler, Result};
|
||||||
use syntax::syntax_tree::program::Program;
|
use syntax::syntax_tree::program::ProgramFile;
|
||||||
|
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
use transpile::transpiler::Transpiler;
|
use transpile::transpiler::Transpiler;
|
||||||
|
@ -49,7 +49,7 @@ pub fn tokenize(path: &Path) -> Result<TokenStream> {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - If an error occurs while reading the file.
|
/// - If an error occurs while reading the file.
|
||||||
/// - If an error occurs while parsing the source code.
|
/// - 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 source_file = SourceFile::load(path)?;
|
||||||
|
|
||||||
let printer = Printer::new();
|
let printer = Printer::new();
|
||||||
|
|
|
@ -21,6 +21,13 @@ use crate::{
|
||||||
|
|
||||||
use super::{statement::Block, ConnectedList};
|
use super::{statement::Block, ConnectedList};
|
||||||
|
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ``` ebnf
|
||||||
|
/// Declaration:
|
||||||
|
/// Function
|
||||||
|
/// ;
|
||||||
|
/// ```
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum Declaration {
|
pub enum Declaration {
|
||||||
|
|
|
@ -3,27 +3,128 @@
|
||||||
use getset::Getters;
|
use getset::Getters;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::Handler,
|
base::{
|
||||||
|
source_file::{SourceElement, Span},
|
||||||
|
Handler,
|
||||||
|
},
|
||||||
|
lexical::token::{Keyword, KeywordKind, Punctuation, StringLiteral, Token},
|
||||||
syntax::{
|
syntax::{
|
||||||
error::Error,
|
error::{Error, SyntaxKind, UnexpectedSyntax},
|
||||||
parser::{Parser, Reading},
|
parser::{Parser, Reading},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::declaration::Declaration;
|
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))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
#[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.
|
/// The declarations within the program.
|
||||||
#[get = "pub"]
|
#[get = "pub"]
|
||||||
declarations: Vec<Declaration>,
|
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> {
|
impl<'a> Parser<'a> {
|
||||||
/// Parses a [`Program`].
|
/// Parses a [`ProgramFile`].
|
||||||
pub fn parse_program(&mut self, handler: &impl Handler<Error>) -> Option<Program> {
|
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();
|
let mut declarations = Vec::new();
|
||||||
|
|
||||||
while !self.is_exhausted() {
|
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::{
|
syntax::syntax_tree::{
|
||||||
declaration::Declaration,
|
declaration::Declaration,
|
||||||
expression::{Expression, FunctionCall, Primary},
|
expression::{Expression, FunctionCall, Primary},
|
||||||
program::Program,
|
program::{Namespace, ProgramFile},
|
||||||
statement::{Conditional, Statement},
|
statement::{Conditional, Statement},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -55,11 +55,13 @@ impl Transpiler {
|
||||||
/// - [`TranspileError::MissingFunctionDeclaration`] If a called function is missing
|
/// - [`TranspileError::MissingFunctionDeclaration`] If a called function is missing
|
||||||
pub fn transpile(
|
pub fn transpile(
|
||||||
&mut self,
|
&mut self,
|
||||||
program: &Program,
|
program: &ProgramFile,
|
||||||
handler: &impl Handler<TranspileError>,
|
handler: &impl Handler<TranspileError>,
|
||||||
) -> Result<(), TranspileError> {
|
) -> Result<(), TranspileError> {
|
||||||
|
let namespace = program.namespace();
|
||||||
|
|
||||||
for declaration in program.declarations() {
|
for declaration in program.declarations() {
|
||||||
self.transpile_declaration(declaration, handler);
|
self.transpile_declaration(declaration, namespace, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut always_transpile_functions = Vec::new();
|
let mut always_transpile_functions = Vec::new();
|
||||||
|
@ -88,6 +90,7 @@ impl Transpiler {
|
||||||
fn transpile_declaration(
|
fn transpile_declaration(
|
||||||
&mut self,
|
&mut self,
|
||||||
declaration: &Declaration,
|
declaration: &Declaration,
|
||||||
|
namespace: &Namespace,
|
||||||
_handler: &impl Handler<TranspileError>,
|
_handler: &impl Handler<TranspileError>,
|
||||||
) {
|
) {
|
||||||
match declaration {
|
match declaration {
|
||||||
|
@ -109,7 +112,7 @@ impl Transpiler {
|
||||||
self.functions.write().unwrap().insert(
|
self.functions.write().unwrap().insert(
|
||||||
name,
|
name,
|
||||||
FunctionData {
|
FunctionData {
|
||||||
namespace: "shulkerscript".to_string(),
|
namespace: namespace.namespace_name().str_content().to_string(),
|
||||||
statements,
|
statements,
|
||||||
annotations,
|
annotations,
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue