Introduce namespace configuration syntax

This commit is contained in:
Moritz Hölting 2024-04-06 21:49:25 +02:00
parent 01040964af
commit b105a45154
6 changed files with 132 additions and 16 deletions

View File

@ -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)*)? ')'
;

View File

@ -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",
}
}
}

View File

@ -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();

View File

@ -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 {

View File

@ -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,
})
}
}

View File

@ -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,
},