Compare commits
1 Commits
a07f16f283
...
4bfae0c960
Author | SHA1 | Date |
---|---|---|
|
4bfae0c960 |
|
@ -3,7 +3,7 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
- development
|
||||
- 'releases/**'
|
||||
pull_request:
|
||||
|
||||
|
|
|
@ -304,9 +304,7 @@ impl Semicolon {
|
|||
Err(error)
|
||||
}
|
||||
},
|
||||
SemicolonStatement::VariableDeclaration(decl) => {
|
||||
decl.analyze_semantics(function_names, macro_names, handler)
|
||||
}
|
||||
SemicolonStatement::VariableDeclaration(decl) => decl.analyze_semantics(handler),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -523,87 +521,61 @@ impl VariableDeclaration {
|
|||
/// Analyzes the semantics of a variable declaration.
|
||||
pub fn analyze_semantics(
|
||||
&self,
|
||||
function_names: &HashSet<String>,
|
||||
macro_names: &HashSet<String>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> Result<(), error::Error> {
|
||||
match self {
|
||||
Self::Array(array) => array.assignment().as_ref().map_or(Ok(()), |assignment| {
|
||||
assignment
|
||||
.expression()
|
||||
.analyze_semantics(function_names, macro_names, handler)
|
||||
}),
|
||||
Self::Single(single) => {
|
||||
if let Some(assignment) = single.assignment() {
|
||||
let err = match single.variable_type().keyword {
|
||||
KeywordKind::Int => !matches!(
|
||||
assignment.expression(),
|
||||
// TODO: also allow macro identifier but not macro string literal
|
||||
Expression::Primary(
|
||||
Primary::Integer(_) | Primary::Lua(_) | Primary::FunctionCall(_)
|
||||
)
|
||||
),
|
||||
KeywordKind::Bool => !matches!(
|
||||
assignment.expression(),
|
||||
Expression::Primary(Primary::Boolean(_) | Primary::Lua(_))
|
||||
),
|
||||
_ => false,
|
||||
};
|
||||
if err {
|
||||
match self.expression() {
|
||||
Expression::Primary(Primary::Integer(num)) => {
|
||||
if self.variable_type().keyword == KeywordKind::Bool {
|
||||
let err = error::Error::UnexpectedExpression(UnexpectedExpression(
|
||||
assignment.expression().clone(),
|
||||
Expression::Primary(Primary::Integer(num.clone())),
|
||||
));
|
||||
handler.receive(err.clone());
|
||||
return Err(err);
|
||||
}
|
||||
assignment
|
||||
.expression()
|
||||
.analyze_semantics(function_names, macro_names, handler)
|
||||
Err(err)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Self::Score(score) => {
|
||||
if let Some((_, assignment)) = score.target_assignment() {
|
||||
// TODO: also allow macro identifier but not macro string literal
|
||||
if !matches!(
|
||||
assignment.expression(),
|
||||
Expression::Primary(
|
||||
Primary::Integer(_) | Primary::Lua(_) | Primary::FunctionCall(_)
|
||||
)
|
||||
Expression::Primary(Primary::Boolean(bool)) => {
|
||||
if self.variable_type().keyword == KeywordKind::Int {
|
||||
let err = error::Error::UnexpectedExpression(UnexpectedExpression(
|
||||
Expression::Primary(Primary::Boolean(bool.clone())),
|
||||
));
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Expression::Primary(Primary::StringLiteral(str)) => {
|
||||
if matches!(
|
||||
self.variable_type().keyword,
|
||||
KeywordKind::Int | KeywordKind::Bool
|
||||
) {
|
||||
let err = error::Error::UnexpectedExpression(UnexpectedExpression(
|
||||
assignment.expression().clone(),
|
||||
Expression::Primary(Primary::StringLiteral(str.clone())),
|
||||
));
|
||||
handler.receive(err.clone());
|
||||
return Err(err);
|
||||
}
|
||||
assignment
|
||||
.expression()
|
||||
.analyze_semantics(function_names, macro_names, handler)
|
||||
Err(err)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Self::Tag(tag) => {
|
||||
if let Some((_, assignment)) = tag.target_assignment() {
|
||||
if !matches!(
|
||||
assignment.expression(),
|
||||
Expression::Primary(Primary::Boolean(_) | Primary::Lua(_))
|
||||
Expression::Primary(Primary::MacroStringLiteral(str)) => {
|
||||
if matches!(
|
||||
self.variable_type().keyword,
|
||||
KeywordKind::Int | KeywordKind::Bool
|
||||
) {
|
||||
let err = error::Error::UnexpectedExpression(UnexpectedExpression(
|
||||
assignment.expression().clone(),
|
||||
Expression::Primary(Primary::MacroStringLiteral(str.clone())),
|
||||
));
|
||||
handler.receive(err.clone());
|
||||
return Err(err);
|
||||
}
|
||||
assignment
|
||||
.expression()
|
||||
.analyze_semantics(function_names, macro_names, handler)
|
||||
Err(err)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Expression::Primary(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
58
src/serde.rs
58
src/serde.rs
|
@ -14,15 +14,13 @@ use serde::{
|
|||
|
||||
use crate::base::source_file::SourceFile;
|
||||
|
||||
thread_local! {
|
||||
static DEDUPLICATE_SOURCE_FILES: LazyLock<RwLock<bool>> = LazyLock::new(|| RwLock::new(false));
|
||||
static DEDUPLICATE_SOURCE_FILES: LazyLock<RwLock<bool>> = LazyLock::new(|| RwLock::new(false));
|
||||
|
||||
static SERIALIZE_DATA: LazyLock<Mutex<SerializeData>> =
|
||||
static SERIALIZE_DATA: LazyLock<Mutex<SerializeData>> =
|
||||
LazyLock::new(|| Mutex::new(SerializeData::default()));
|
||||
|
||||
static DESERIALIZE_DATA: LazyLock<RwLock<Option<DeserializeData>>> =
|
||||
static DESERIALIZE_DATA: LazyLock<RwLock<Option<DeserializeData>>> =
|
||||
LazyLock::new(|| RwLock::new(None));
|
||||
}
|
||||
|
||||
/// Wrapper to remove duplicate source file data during (de-)serialization
|
||||
#[expect(clippy::module_name_repetitions)]
|
||||
|
@ -37,27 +35,20 @@ where
|
|||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
DEDUPLICATE_SOURCE_FILES.with(|d| *d.write().unwrap() = true);
|
||||
SERIALIZE_DATA.with(|d| d.lock().unwrap().clear());
|
||||
// hold guard so no other can serialize at the same time in same thread
|
||||
let s = DEDUPLICATE_SOURCE_FILES.with(|d| {
|
||||
let guard = d.read().unwrap();
|
||||
*DEDUPLICATE_SOURCE_FILES.write().unwrap() = true;
|
||||
SERIALIZE_DATA.lock().unwrap().clear();
|
||||
let mut serialized_data = flexbuffers::FlexbufferSerializer::new();
|
||||
self.0
|
||||
.serialize(&mut serialized_data)
|
||||
.map_err(|_| serde::ser::Error::custom("could not buffer serialization"))?;
|
||||
drop(serialized_data);
|
||||
let mut s = serializer.serialize_struct("SerdeWrapper", 3)?;
|
||||
|
||||
SERIALIZE_DATA.with(|d| {
|
||||
s.serialize_field("source_files", &d.lock().unwrap().id_to_source_file)
|
||||
})?;
|
||||
s.serialize_field(
|
||||
"source_files",
|
||||
&SERIALIZE_DATA.lock().unwrap().id_to_source_file,
|
||||
)?;
|
||||
s.serialize_field("data", &self.0)?;
|
||||
drop(guard);
|
||||
Ok(s)
|
||||
})?;
|
||||
|
||||
DEDUPLICATE_SOURCE_FILES.with(|d| *d.write().unwrap() = false);
|
||||
*DEDUPLICATE_SOURCE_FILES.write().unwrap() = false;
|
||||
s.end()
|
||||
}
|
||||
}
|
||||
|
@ -96,13 +87,11 @@ where
|
|||
let source_files: BTreeMap<u64, SourceFile> = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
|
||||
DESERIALIZE_DATA.with(|d| {
|
||||
*d.write().unwrap() = Some(DeserializeData {
|
||||
*DESERIALIZE_DATA.write().unwrap() = Some(DeserializeData {
|
||||
id_to_source_file: source_files
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, Arc::new(v)))
|
||||
.collect(),
|
||||
})
|
||||
});
|
||||
let data = seq
|
||||
.next_element()?
|
||||
|
@ -124,14 +113,12 @@ where
|
|||
if data.is_some() {
|
||||
return Err(de::Error::duplicate_field("data"));
|
||||
}
|
||||
DESERIALIZE_DATA.with(|d| {
|
||||
*d.write().unwrap() =
|
||||
*DESERIALIZE_DATA.write().unwrap() =
|
||||
source_files.as_ref().map(|source_files| DeserializeData {
|
||||
id_to_source_file: source_files
|
||||
.iter()
|
||||
.map(|(&k, v)| (k, Arc::new(v.clone())))
|
||||
.collect(),
|
||||
})
|
||||
});
|
||||
data = Some(map.next_value()?);
|
||||
}
|
||||
|
@ -149,14 +136,14 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
DEDUPLICATE_SOURCE_FILES.with(|d| *d.write().unwrap() = true);
|
||||
DESERIALIZE_DATA.with(|d| *d.write().unwrap() = None);
|
||||
*DEDUPLICATE_SOURCE_FILES.write().unwrap() = true;
|
||||
*DESERIALIZE_DATA.write().unwrap() = None;
|
||||
let res = deserializer.deserialize_struct(
|
||||
"SerdeWrapper",
|
||||
&["source_files", "data"],
|
||||
WrapperVisitor(PhantomData::<T>),
|
||||
);
|
||||
DEDUPLICATE_SOURCE_FILES.with(|d| *d.write().unwrap() = false);
|
||||
*DEDUPLICATE_SOURCE_FILES.write().unwrap() = false;
|
||||
|
||||
res
|
||||
}
|
||||
|
@ -213,11 +200,9 @@ pub mod source_file {
|
|||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
if DEDUPLICATE_SOURCE_FILES.with(|d| *d.read().unwrap()) {
|
||||
SERIALIZE_DATA.with(|d| {
|
||||
let mut data = d.lock().unwrap();
|
||||
if *DEDUPLICATE_SOURCE_FILES.read().unwrap() {
|
||||
let mut data = SERIALIZE_DATA.lock().unwrap();
|
||||
serializer.serialize_u64(data.get_id_of(this))
|
||||
})
|
||||
} else {
|
||||
this.as_ref().serialize(serializer)
|
||||
}
|
||||
|
@ -227,18 +212,17 @@ pub mod source_file {
|
|||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
if DEDUPLICATE_SOURCE_FILES.with(|d| *d.read().unwrap()) {
|
||||
if *DEDUPLICATE_SOURCE_FILES.read().unwrap() {
|
||||
let id = u64::deserialize(deserializer)?;
|
||||
Ok(DESERIALIZE_DATA.with(|d| {
|
||||
d.read()
|
||||
Ok(DESERIALIZE_DATA
|
||||
.read()
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.ok_or_else(|| de::Error::custom("SourceFiles do not have been loaded yet"))?
|
||||
.id_to_source_file
|
||||
.get(&id)
|
||||
.map(Arc::clone)
|
||||
.ok_or_else(|| serde::de::Error::custom("invalid source_file id"))
|
||||
}))?
|
||||
.ok_or_else(|| serde::de::Error::custom("invalid source_file id"))?)
|
||||
} else {
|
||||
Ok(Arc::new(SourceFile::deserialize(deserializer)?))
|
||||
}
|
||||
|
|
|
@ -3,20 +3,16 @@
|
|||
pub mod execute_block;
|
||||
|
||||
use derive_more::From;
|
||||
use enum_as_inner::EnumAsInner;
|
||||
use getset::Getters;
|
||||
|
||||
use crate::{
|
||||
base::{
|
||||
self,
|
||||
source_file::{SourceElement, Span},
|
||||
Handler, VoidHandler,
|
||||
Handler,
|
||||
},
|
||||
lexical::{
|
||||
token::{
|
||||
CommandLiteral, DocComment, Identifier, Integer, Keyword, KeywordKind, Punctuation,
|
||||
Token,
|
||||
},
|
||||
token::{CommandLiteral, DocComment, Identifier, Keyword, KeywordKind, Punctuation, Token},
|
||||
token_stream::Delimiter,
|
||||
},
|
||||
syntax::{
|
||||
|
@ -27,7 +23,7 @@ use crate::{
|
|||
|
||||
use self::execute_block::ExecuteBlock;
|
||||
|
||||
use super::{expression::Expression, AnyStringLiteral};
|
||||
use super::expression::Expression;
|
||||
|
||||
/// Represents a statement in the syntax tree.
|
||||
///
|
||||
|
@ -234,7 +230,7 @@ impl Semicolon {
|
|||
/// ;
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, From, EnumAsInner)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum SemicolonStatement {
|
||||
/// An expression that ends with a semicolon.
|
||||
Expression(Expression),
|
||||
|
@ -256,69 +252,18 @@ impl SourceElement for SemicolonStatement {
|
|||
/// Syntax Synopsis:
|
||||
///
|
||||
/// ```ebnf
|
||||
/// VariableDeclaration:
|
||||
/// SingleVariableDeclaration
|
||||
/// | ArrayVariableDeclaration
|
||||
/// | ScoreVariableDeclaration
|
||||
/// | TagVariableDeclaration
|
||||
/// ;
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, From, EnumAsInner)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum VariableDeclaration {
|
||||
Single(SingleVariableDeclaration),
|
||||
Array(ArrayVariableDeclaration),
|
||||
Score(ScoreVariableDeclaration),
|
||||
Tag(TagVariableDeclaration),
|
||||
}
|
||||
|
||||
impl SourceElement for VariableDeclaration {
|
||||
fn span(&self) -> Span {
|
||||
match self {
|
||||
Self::Single(declaration) => declaration.span(),
|
||||
Self::Array(declaration) => declaration.span(),
|
||||
Self::Score(declaration) => declaration.span(),
|
||||
Self::Tag(declaration) => declaration.span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VariableDeclaration {
|
||||
/// Get the identifier of the variable declaration
|
||||
#[must_use]
|
||||
pub fn identifier(&self) -> &Identifier {
|
||||
match self {
|
||||
Self::Single(declaration) => &declaration.identifier,
|
||||
Self::Array(declaration) => &declaration.identifier,
|
||||
Self::Score(declaration) => &declaration.identifier,
|
||||
Self::Tag(declaration) => &declaration.identifier,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the type of the variable declaration
|
||||
#[must_use]
|
||||
pub fn variable_type(&self) -> &Keyword {
|
||||
match self {
|
||||
Self::Single(declaration) => &declaration.variable_type,
|
||||
Self::Array(declaration) => &declaration.variable_type,
|
||||
Self::Score(declaration) => &declaration.int_keyword,
|
||||
Self::Tag(declaration) => &declaration.bool_keyword,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a variable assignment.
|
||||
///
|
||||
/// Syntax Synopsis:
|
||||
///
|
||||
/// ```ebnf
|
||||
/// VariableDeclarationAssignment:
|
||||
/// '=' Expression
|
||||
/// LuaCode:
|
||||
/// ('int' | 'bool') identifier '=' Expression ';'
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||
pub struct VariableDeclarationAssignment {
|
||||
pub struct VariableDeclaration {
|
||||
/// The type of the variable.
|
||||
#[get = "pub"]
|
||||
variable_type: Keyword,
|
||||
/// The identifier of the variable.
|
||||
#[get = "pub"]
|
||||
identifier: Identifier,
|
||||
/// The equals sign of the variable declaration.
|
||||
#[get = "pub"]
|
||||
equals: Punctuation,
|
||||
|
@ -327,265 +272,12 @@ pub struct VariableDeclarationAssignment {
|
|||
expression: Expression,
|
||||
}
|
||||
|
||||
impl SourceElement for VariableDeclarationAssignment {
|
||||
impl SourceElement for VariableDeclaration {
|
||||
fn span(&self) -> Span {
|
||||
self.equals
|
||||
self.variable_type
|
||||
.span()
|
||||
.join(&self.expression.span())
|
||||
.expect("The span of the variable declaration assignment is invalid.")
|
||||
}
|
||||
}
|
||||
|
||||
impl VariableDeclarationAssignment {
|
||||
/// Dissolves the [`VariableDeclarationAssignment`] into its components.
|
||||
#[must_use]
|
||||
pub fn dissolve(self) -> (Punctuation, Expression) {
|
||||
(self.equals, self.expression)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a single variable declaration in the syntax tree.
|
||||
///
|
||||
/// Syntax Synopsis:
|
||||
///
|
||||
/// ```ebnf
|
||||
/// SingleVariableDeclaration:
|
||||
/// ('int' | 'bool') identifier VariableDeclarationAssignment?
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||
pub struct SingleVariableDeclaration {
|
||||
/// The type of the variable.
|
||||
#[get = "pub"]
|
||||
variable_type: Keyword,
|
||||
/// The identifier of the variable.
|
||||
#[get = "pub"]
|
||||
identifier: Identifier,
|
||||
/// The optional assignment of the variable.
|
||||
#[get = "pub"]
|
||||
assignment: Option<VariableDeclarationAssignment>,
|
||||
}
|
||||
|
||||
impl SourceElement for SingleVariableDeclaration {
|
||||
fn span(&self) -> Span {
|
||||
self.variable_type
|
||||
.span()
|
||||
.join(
|
||||
&self
|
||||
.assignment
|
||||
.as_ref()
|
||||
.map_or_else(|| self.identifier.span(), SourceElement::span),
|
||||
)
|
||||
.expect("The span of the single variable declaration is invalid.")
|
||||
}
|
||||
}
|
||||
|
||||
impl SingleVariableDeclaration {
|
||||
/// Dissolves the [`SingleVariableDeclaration`] into its components.
|
||||
#[must_use]
|
||||
pub fn dissolve(self) -> (Keyword, Identifier, Option<VariableDeclarationAssignment>) {
|
||||
(self.variable_type, self.identifier, self.assignment)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an array variable declaration in the syntax tree.
|
||||
///
|
||||
/// Syntax Synopsis:
|
||||
///
|
||||
/// ```ebnf
|
||||
/// ArrayVariableDeclaration:
|
||||
/// ('int' | 'bool') identifier '[' integer ']' VariableDeclarationAssignment?
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||
pub struct ArrayVariableDeclaration {
|
||||
/// The type of the variable.
|
||||
#[get = "pub"]
|
||||
variable_type: Keyword,
|
||||
/// The identifier of the variable.
|
||||
#[get = "pub"]
|
||||
identifier: Identifier,
|
||||
/// The opening bracket of the array.
|
||||
#[get = "pub"]
|
||||
open_bracket: Punctuation,
|
||||
/// The array size
|
||||
#[get = "pub"]
|
||||
size: Integer,
|
||||
/// The closing bracket of the array.
|
||||
#[get = "pub"]
|
||||
close_bracket: Punctuation,
|
||||
/// The optional assignment of the variable.
|
||||
#[get = "pub"]
|
||||
assignment: Option<VariableDeclarationAssignment>,
|
||||
}
|
||||
|
||||
impl SourceElement for ArrayVariableDeclaration {
|
||||
fn span(&self) -> Span {
|
||||
self.variable_type
|
||||
.span()
|
||||
.join(
|
||||
&self
|
||||
.assignment
|
||||
.as_ref()
|
||||
.map_or_else(|| self.close_bracket.span(), SourceElement::span),
|
||||
)
|
||||
.expect("The span of the array variable declaration is invalid.")
|
||||
}
|
||||
}
|
||||
|
||||
impl ArrayVariableDeclaration {
|
||||
/// Dissolves the [`ArrayVariableDeclaration`] into its components.
|
||||
#[must_use]
|
||||
pub fn dissolve(
|
||||
self,
|
||||
) -> (
|
||||
Keyword,
|
||||
Identifier,
|
||||
Punctuation,
|
||||
Integer,
|
||||
Punctuation,
|
||||
Option<VariableDeclarationAssignment>,
|
||||
) {
|
||||
(
|
||||
self.variable_type,
|
||||
self.identifier,
|
||||
self.open_bracket,
|
||||
self.size,
|
||||
self.close_bracket,
|
||||
self.assignment,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
type CriteriaSelection = (Punctuation, AnyStringLiteral, Punctuation);
|
||||
|
||||
/// Represents a scoreboard variable declaration in the syntax tree.
|
||||
///
|
||||
/// Syntax Synopsis:
|
||||
///
|
||||
/// ```ebnf
|
||||
/// ScoreVariableDeclaration:
|
||||
/// 'int' ('<' AnyStringLiteral '>')? identifier '[' AnyStringLiteral? ']' VariableDeclarationAssignment?
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||
pub struct ScoreVariableDeclaration {
|
||||
/// `int` keyword
|
||||
#[get = "pub"]
|
||||
int_keyword: Keyword,
|
||||
/// The scoreboard criteria
|
||||
#[get = "pub"]
|
||||
criteria: Option<CriteriaSelection>,
|
||||
/// The identifier of the variable.
|
||||
#[get = "pub"]
|
||||
identifier: Identifier,
|
||||
/// Opening bracket of the score variable
|
||||
#[get = "pub"]
|
||||
open_bracket: Punctuation,
|
||||
/// Closing bracket of the score variable
|
||||
#[get = "pub"]
|
||||
close_bracket: Punctuation,
|
||||
/// The optional assignment of the variable.
|
||||
#[get = "pub"]
|
||||
target_assignment: Option<(AnyStringLiteral, VariableDeclarationAssignment)>,
|
||||
}
|
||||
|
||||
impl SourceElement for ScoreVariableDeclaration {
|
||||
fn span(&self) -> Span {
|
||||
self.int_keyword
|
||||
.span()
|
||||
.join(&self.target_assignment.as_ref().map_or_else(
|
||||
|| self.close_bracket.span(),
|
||||
|(_, assignment)| assignment.span(),
|
||||
))
|
||||
.expect("The span of the score variable declaration is invalid.")
|
||||
}
|
||||
}
|
||||
|
||||
impl ScoreVariableDeclaration {
|
||||
/// Dissolves the [`ScoreVariableDeclaration`] into its components.
|
||||
#[expect(clippy::type_complexity)]
|
||||
#[must_use]
|
||||
pub fn dissolve(
|
||||
self,
|
||||
) -> (
|
||||
Keyword,
|
||||
Option<CriteriaSelection>,
|
||||
Identifier,
|
||||
Punctuation,
|
||||
Punctuation,
|
||||
Option<(AnyStringLiteral, VariableDeclarationAssignment)>,
|
||||
) {
|
||||
(
|
||||
self.int_keyword,
|
||||
self.criteria,
|
||||
self.identifier,
|
||||
self.open_bracket,
|
||||
self.close_bracket,
|
||||
self.target_assignment,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a tag variable declaration in the syntax tree.
|
||||
///
|
||||
/// Syntax Synopsis:
|
||||
///
|
||||
/// ```ebnf
|
||||
/// TagVariableDeclaration:
|
||||
/// 'bool' identifier '[' AnyStringLiteral? ']' VariableDeclarationAssignment?
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||
pub struct TagVariableDeclaration {
|
||||
/// `bool` keyword
|
||||
#[get = "pub"]
|
||||
bool_keyword: Keyword,
|
||||
/// The identifier of the variable.
|
||||
#[get = "pub"]
|
||||
identifier: Identifier,
|
||||
/// Opening bracket of the score variable
|
||||
#[get = "pub"]
|
||||
open_bracket: Punctuation,
|
||||
/// Closing bracket of the score variable
|
||||
#[get = "pub"]
|
||||
close_bracket: Punctuation,
|
||||
/// The optional assignment of the variable.
|
||||
#[get = "pub"]
|
||||
target_assignment: Option<(AnyStringLiteral, VariableDeclarationAssignment)>,
|
||||
}
|
||||
|
||||
impl SourceElement for TagVariableDeclaration {
|
||||
fn span(&self) -> Span {
|
||||
self.bool_keyword
|
||||
.span()
|
||||
.join(&self.target_assignment.as_ref().map_or_else(
|
||||
|| self.close_bracket.span(),
|
||||
|(_, assignment)| assignment.span(),
|
||||
))
|
||||
.expect("The span of the tag variable declaration is invalid.")
|
||||
}
|
||||
}
|
||||
|
||||
impl TagVariableDeclaration {
|
||||
/// Dissolves the [`TagVariableDeclaration`] into its components.
|
||||
#[must_use]
|
||||
pub fn dissolve(
|
||||
self,
|
||||
) -> (
|
||||
Keyword,
|
||||
Identifier,
|
||||
Punctuation,
|
||||
Punctuation,
|
||||
Option<(AnyStringLiteral, VariableDeclarationAssignment)>,
|
||||
) {
|
||||
(
|
||||
self.bool_keyword,
|
||||
self.identifier,
|
||||
self.open_bracket,
|
||||
self.close_bracket,
|
||||
self.target_assignment,
|
||||
)
|
||||
.expect("The span of the variable declaration is invalid.")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -738,12 +430,6 @@ impl<'a> Parser<'a> {
|
|||
&mut self,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> ParseResult<VariableDeclaration> {
|
||||
enum IndexingType {
|
||||
IntegerSize(Integer),
|
||||
AnyString(AnyStringLiteral),
|
||||
None,
|
||||
}
|
||||
|
||||
let variable_type = match self.stop_at_significant() {
|
||||
Reading::Atomic(Token::Keyword(keyword))
|
||||
if matches!(keyword.keyword, KeywordKind::Int | KeywordKind::Bool) =>
|
||||
|
@ -764,163 +450,21 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
};
|
||||
|
||||
let criteria_selection = if variable_type.keyword == KeywordKind::Int {
|
||||
self.try_parse(|p| {
|
||||
let open = p.parse_punctuation('<', true, &VoidHandler)?;
|
||||
let criteria = p.parse_any_string_literal(&VoidHandler)?;
|
||||
let close = p.parse_punctuation('>', true, &VoidHandler)?;
|
||||
Ok((open, criteria, close))
|
||||
})
|
||||
.ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// read identifier
|
||||
self.stop_at_significant();
|
||||
let identifier = self.parse_identifier(handler)?;
|
||||
|
||||
match self.stop_at_significant() {
|
||||
Reading::IntoDelimited(punc) if punc.punctuation == '[' => {
|
||||
let tree = self.step_into(
|
||||
Delimiter::Bracket,
|
||||
|p| {
|
||||
let res = match p.stop_at_significant() {
|
||||
Reading::Atomic(Token::Integer(int)) => {
|
||||
p.forward();
|
||||
IndexingType::IntegerSize(int)
|
||||
}
|
||||
|
||||
Reading::Atomic(Token::StringLiteral(s)) => {
|
||||
let selector = AnyStringLiteral::from(s);
|
||||
p.forward();
|
||||
IndexingType::AnyString(selector)
|
||||
}
|
||||
Reading::Atomic(Token::MacroStringLiteral(s)) => {
|
||||
let selector = AnyStringLiteral::from(s);
|
||||
p.forward();
|
||||
IndexingType::AnyString(selector)
|
||||
}
|
||||
|
||||
Reading::DelimitedEnd(punc) if punc.punctuation == ']' => {
|
||||
IndexingType::None
|
||||
}
|
||||
|
||||
unexpected => {
|
||||
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Either(&[
|
||||
SyntaxKind::Integer,
|
||||
SyntaxKind::AnyStringLiteral,
|
||||
]),
|
||||
found: unexpected.into_token(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
},
|
||||
handler,
|
||||
)?;
|
||||
|
||||
let open_bracket = tree.open;
|
||||
let close_bracket = tree.close;
|
||||
let inner = tree.tree?;
|
||||
|
||||
match inner {
|
||||
IndexingType::IntegerSize(size) => {
|
||||
let assignment = self
|
||||
.try_parse(|p| {
|
||||
// read equals sign
|
||||
let equals = p.parse_punctuation('=', true, handler)?;
|
||||
let equals = self.parse_punctuation('=', true, handler)?;
|
||||
|
||||
// read expression
|
||||
let expression = p.parse_expression(handler)?;
|
||||
let expression = self.parse_expression(handler)?;
|
||||
|
||||
Ok(VariableDeclarationAssignment { equals, expression })
|
||||
Ok(VariableDeclaration {
|
||||
variable_type,
|
||||
identifier,
|
||||
equals,
|
||||
expression,
|
||||
})
|
||||
.ok();
|
||||
|
||||
Ok(VariableDeclaration::Array(ArrayVariableDeclaration {
|
||||
variable_type,
|
||||
identifier,
|
||||
open_bracket,
|
||||
size,
|
||||
close_bracket,
|
||||
assignment,
|
||||
}))
|
||||
}
|
||||
IndexingType::AnyString(selector) => {
|
||||
let equals = self.parse_punctuation('=', true, handler)?;
|
||||
let expression = self.parse_expression(handler)?;
|
||||
|
||||
let assignment = VariableDeclarationAssignment { equals, expression };
|
||||
|
||||
match variable_type.keyword {
|
||||
KeywordKind::Int => {
|
||||
Ok(VariableDeclaration::Score(ScoreVariableDeclaration {
|
||||
int_keyword: variable_type,
|
||||
criteria: criteria_selection,
|
||||
identifier,
|
||||
open_bracket,
|
||||
close_bracket,
|
||||
target_assignment: Some((selector, assignment)),
|
||||
}))
|
||||
}
|
||||
KeywordKind::Bool => {
|
||||
Ok(VariableDeclaration::Tag(TagVariableDeclaration {
|
||||
bool_keyword: variable_type,
|
||||
identifier,
|
||||
open_bracket,
|
||||
close_bracket,
|
||||
target_assignment: Some((selector, assignment)),
|
||||
}))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
IndexingType::None => match variable_type.keyword {
|
||||
KeywordKind::Int => {
|
||||
Ok(VariableDeclaration::Score(ScoreVariableDeclaration {
|
||||
int_keyword: variable_type,
|
||||
criteria: criteria_selection,
|
||||
identifier,
|
||||
open_bracket,
|
||||
close_bracket,
|
||||
target_assignment: None,
|
||||
}))
|
||||
}
|
||||
KeywordKind::Bool => Ok(VariableDeclaration::Tag(TagVariableDeclaration {
|
||||
bool_keyword: variable_type,
|
||||
identifier,
|
||||
open_bracket,
|
||||
close_bracket,
|
||||
target_assignment: None,
|
||||
})),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
// SingleVariableDeclaration with Assignment
|
||||
Reading::Atomic(Token::Punctuation(punc)) if punc.punctuation == '=' => {
|
||||
self.forward();
|
||||
let equals = punc;
|
||||
let expression = self.parse_expression(handler)?;
|
||||
let assignment = VariableDeclarationAssignment { equals, expression };
|
||||
|
||||
Ok(VariableDeclaration::Single(SingleVariableDeclaration {
|
||||
variable_type,
|
||||
identifier,
|
||||
assignment: Some(assignment),
|
||||
}))
|
||||
}
|
||||
// SingleVariableDeclaration without Assignment
|
||||
_ => Ok(VariableDeclaration::Single(SingleVariableDeclaration {
|
||||
variable_type,
|
||||
identifier,
|
||||
assignment: None,
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,6 @@ mod transpiler;
|
|||
#[cfg(feature = "shulkerbox")]
|
||||
#[cfg_attr(feature = "shulkerbox", doc(inline))]
|
||||
pub use transpiler::Transpiler;
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
mod variables;
|
||||
|
||||
pub mod util;
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ use crate::{
|
|||
program::{Namespace, ProgramFile},
|
||||
statement::{
|
||||
execute_block::{Conditional, Else, ExecuteBlock, ExecuteBlockHead, ExecuteBlockTail},
|
||||
SemicolonStatement, Statement,
|
||||
Statement,
|
||||
},
|
||||
},
|
||||
transpile::error::MissingFunctionDeclaration,
|
||||
|
@ -29,7 +29,6 @@ use crate::{
|
|||
|
||||
use super::{
|
||||
error::{TranspileError, TranspileResult},
|
||||
variables::{Scope, VariableType},
|
||||
FunctionData,
|
||||
};
|
||||
|
||||
|
@ -75,10 +74,8 @@ impl Transpiler {
|
|||
) -> Result<(), TranspileError> {
|
||||
tracing::trace!("Transpiling program declarations");
|
||||
|
||||
let scope = Scope::new();
|
||||
|
||||
for program in programs {
|
||||
self.transpile_program_declarations(program, &scope, handler);
|
||||
self.transpile_program_declarations(program, handler);
|
||||
}
|
||||
|
||||
let mut always_transpile_functions = Vec::new();
|
||||
|
@ -101,7 +98,7 @@ impl Transpiler {
|
|||
);
|
||||
|
||||
for identifier_span in always_transpile_functions {
|
||||
self.get_or_transpile_function(&identifier_span, None, &scope, handler)?;
|
||||
self.get_or_transpile_function(&identifier_span, None, handler)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -111,13 +108,12 @@ impl Transpiler {
|
|||
fn transpile_program_declarations(
|
||||
&mut self,
|
||||
program: &ProgramFile,
|
||||
scope: &Scope,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) {
|
||||
let namespace = program.namespace();
|
||||
|
||||
for declaration in program.declarations() {
|
||||
self.transpile_declaration(declaration, namespace, scope, handler);
|
||||
self.transpile_declaration(declaration, namespace, handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,7 +123,6 @@ impl Transpiler {
|
|||
&mut self,
|
||||
declaration: &Declaration,
|
||||
namespace: &Namespace,
|
||||
_scope: &Scope,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) {
|
||||
let program_identifier = declaration.span().source_file().identifier().clone();
|
||||
|
@ -148,6 +143,7 @@ impl Transpiler {
|
|||
)
|
||||
})
|
||||
.collect();
|
||||
#[allow(clippy::significant_drop_tightening)]
|
||||
self.functions.insert(
|
||||
(program_identifier, name),
|
||||
FunctionData {
|
||||
|
@ -207,7 +203,6 @@ impl Transpiler {
|
|||
if tag.replace().is_some() {
|
||||
sb_tag.set_replace(true);
|
||||
}
|
||||
// TODO: handle global variables
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -220,7 +215,6 @@ impl Transpiler {
|
|||
&mut self,
|
||||
identifier_span: &Span,
|
||||
arguments: Option<&[&Expression]>,
|
||||
scope: &Scope,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<(String, Option<BTreeMap<String, String>>)> {
|
||||
let program_identifier = identifier_span.source_file().identifier();
|
||||
|
@ -243,8 +237,6 @@ impl Transpiler {
|
|||
if !already_transpiled {
|
||||
tracing::trace!("Function not transpiled yet, transpiling.");
|
||||
|
||||
let function_scope = scope.new_child();
|
||||
|
||||
let statements = {
|
||||
let functions = &self.functions;
|
||||
let function_data = functions
|
||||
|
@ -265,15 +257,9 @@ impl Transpiler {
|
|||
error
|
||||
})?;
|
||||
|
||||
for (i, param) in function_data.parameters.iter().enumerate() {
|
||||
function_scope.set_variable(param, VariableType::FunctionArgument { index: i });
|
||||
}
|
||||
|
||||
function_data.statements.clone()
|
||||
};
|
||||
|
||||
let commands =
|
||||
self.transpile_function(&statements, program_identifier, &function_scope, handler)?;
|
||||
let commands = self.transpile_function(&statements, program_identifier, handler)?;
|
||||
|
||||
let functions = &self.functions;
|
||||
let function_data = functions
|
||||
|
@ -383,22 +369,22 @@ impl Transpiler {
|
|||
})
|
||||
.map(|(s, _)| s.to_owned())?;
|
||||
|
||||
let arg_count = arguments.map(<[&Expression]>::len);
|
||||
if arg_count.is_some_and(|arg_count| arg_count != parameters.len()) {
|
||||
let arg_count = arguments.iter().flat_map(|x| x.iter()).count();
|
||||
if arg_count != parameters.len() {
|
||||
let err = TranspileError::InvalidFunctionArguments(InvalidFunctionArguments {
|
||||
expected: parameters.len(),
|
||||
actual: arg_count.expect("checked in if condition"),
|
||||
actual: arg_count,
|
||||
span: identifier_span.clone(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
} else if arg_count.is_some_and(|arg_count| arg_count > 0) {
|
||||
} else if arg_count > 0 {
|
||||
let mut compiled_args = Vec::new();
|
||||
let mut errs = Vec::new();
|
||||
for expression in arguments.iter().flat_map(|x| x.iter()) {
|
||||
let value = match expression {
|
||||
Expression::Primary(Primary::FunctionCall(func)) => self
|
||||
.transpile_function_call(func, scope, handler)
|
||||
.transpile_function_call(func, handler)
|
||||
.map(|cmd| match cmd {
|
||||
Command::Raw(s) => s,
|
||||
_ => unreachable!("Function call should always return a raw command"),
|
||||
|
@ -440,14 +426,13 @@ impl Transpiler {
|
|||
&mut self,
|
||||
statements: &[Statement],
|
||||
program_identifier: &str,
|
||||
scope: &Scope,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<Vec<Command>> {
|
||||
let mut errors = Vec::new();
|
||||
let commands = statements
|
||||
.iter()
|
||||
.filter_map(|statement| {
|
||||
self.transpile_statement(statement, program_identifier, scope, handler)
|
||||
self.transpile_statement(statement, program_identifier, handler)
|
||||
.unwrap_or_else(|err| {
|
||||
errors.push(err);
|
||||
None
|
||||
|
@ -466,7 +451,6 @@ impl Transpiler {
|
|||
&mut self,
|
||||
statement: &Statement,
|
||||
program_identifier: &str,
|
||||
scope: &Scope,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<Option<Command>> {
|
||||
match statement {
|
||||
|
@ -475,7 +459,7 @@ impl Transpiler {
|
|||
}
|
||||
Statement::Run(run) => match run.expression() {
|
||||
Expression::Primary(Primary::FunctionCall(func)) => {
|
||||
self.transpile_function_call(func, scope, handler).map(Some)
|
||||
self.transpile_function_call(func, handler).map(Some)
|
||||
}
|
||||
Expression::Primary(Primary::Integer(num)) => {
|
||||
let error = TranspileError::UnexpectedExpression(UnexpectedExpression(
|
||||
|
@ -505,26 +489,19 @@ impl Transpiler {
|
|||
unreachable!("Only literal commands are allowed in functions at this time.")
|
||||
}
|
||||
Statement::ExecuteBlock(execute) => {
|
||||
let child_scope = scope.new_child();
|
||||
self.transpile_execute_block(execute, program_identifier, &child_scope, handler)
|
||||
self.transpile_execute_block(execute, program_identifier, handler)
|
||||
}
|
||||
Statement::DocComment(doccomment) => {
|
||||
let content = doccomment.content();
|
||||
Ok(Some(Command::Comment(content.to_string())))
|
||||
}
|
||||
Statement::Grouping(group) => {
|
||||
let child_scope = scope.new_child();
|
||||
let statements = group.block().statements();
|
||||
let mut errors = Vec::new();
|
||||
let commands = statements
|
||||
.iter()
|
||||
.filter_map(|statement| {
|
||||
self.transpile_statement(
|
||||
statement,
|
||||
program_identifier,
|
||||
&child_scope,
|
||||
handler,
|
||||
)
|
||||
self.transpile_statement(statement, program_identifier, handler)
|
||||
.unwrap_or_else(|err| {
|
||||
errors.push(err);
|
||||
None
|
||||
|
@ -544,7 +521,7 @@ impl Transpiler {
|
|||
#[expect(clippy::match_wildcard_for_single_variants)]
|
||||
SemicolonStatement::Expression(expr) => match expr {
|
||||
Expression::Primary(Primary::FunctionCall(func)) => {
|
||||
self.transpile_function_call(func, scope, handler).map(Some)
|
||||
self.transpile_function_call(func, handler).map(Some)
|
||||
}
|
||||
unexpected => {
|
||||
let error = TranspileError::UnexpectedExpression(UnexpectedExpression(
|
||||
|
@ -554,24 +531,8 @@ impl Transpiler {
|
|||
Err(error)
|
||||
}
|
||||
},
|
||||
SemicolonStatement::VariableDeclaration(decl) => {
|
||||
// let value = match decl {
|
||||
// VariableDeclaration::Single(single) => {
|
||||
// match single.variable_type().keyword {
|
||||
// KeywordKind::Int => {
|
||||
// VariableType::ScoreboardValue { objective: (), name: () }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// TODO: only for demonstration
|
||||
// scope.set_variable(
|
||||
// decl.identifier().span.str(),
|
||||
// VariableType::Tag {
|
||||
// tag_name: "TODO".to_string(),
|
||||
// },
|
||||
// );
|
||||
todo!("Variable declarations are not yet supported: {decl:?}")
|
||||
SemicolonStatement::VariableDeclaration(_) => {
|
||||
todo!("Variable declarations are not yet supported.")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -580,19 +541,14 @@ impl Transpiler {
|
|||
fn transpile_function_call(
|
||||
&mut self,
|
||||
func: &FunctionCall,
|
||||
scope: &Scope,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<Command> {
|
||||
let arguments = func
|
||||
.arguments()
|
||||
.as_ref()
|
||||
.map(|l| l.elements().map(Deref::deref).collect::<Vec<_>>());
|
||||
let (location, arguments) = self.get_or_transpile_function(
|
||||
&func.identifier().span,
|
||||
arguments.as_deref(),
|
||||
scope,
|
||||
handler,
|
||||
)?;
|
||||
let (location, arguments) =
|
||||
self.get_or_transpile_function(&func.identifier().span, arguments.as_deref(), handler)?;
|
||||
let mut function_call = format!("function {location}");
|
||||
if let Some(arguments) = arguments {
|
||||
use std::fmt::Write;
|
||||
|
@ -616,10 +572,9 @@ impl Transpiler {
|
|||
&mut self,
|
||||
execute: &ExecuteBlock,
|
||||
program_identifier: &str,
|
||||
scope: &Scope,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<Option<Command>> {
|
||||
self.transpile_execute_block_internal(execute, program_identifier, scope, handler)
|
||||
self.transpile_execute_block_internal(execute, program_identifier, handler)
|
||||
.map(|ex| ex.map(Command::Execute))
|
||||
}
|
||||
|
||||
|
@ -627,7 +582,6 @@ impl Transpiler {
|
|||
&mut self,
|
||||
execute: &ExecuteBlock,
|
||||
program_identifier: &str,
|
||||
scope: &Scope,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<Option<Execute>> {
|
||||
match execute {
|
||||
|
@ -639,7 +593,7 @@ impl Transpiler {
|
|||
.statements()
|
||||
.iter()
|
||||
.filter_map(|s| {
|
||||
self.transpile_statement(s, program_identifier, scope, handler)
|
||||
self.transpile_statement(s, program_identifier, handler)
|
||||
.unwrap_or_else(|err| {
|
||||
errors.push(err);
|
||||
None
|
||||
|
@ -660,12 +614,11 @@ impl Transpiler {
|
|||
.transpile_execute_block_internal(
|
||||
execute_block,
|
||||
program_identifier,
|
||||
scope,
|
||||
handler,
|
||||
),
|
||||
}?;
|
||||
|
||||
self.combine_execute_head_tail(head, tail, program_identifier, scope, handler)
|
||||
self.combine_execute_head_tail(head, tail, program_identifier, handler)
|
||||
}
|
||||
ExecuteBlock::IfElse(cond, block, el) => {
|
||||
let statements = block.statements();
|
||||
|
@ -676,7 +629,7 @@ impl Transpiler {
|
|||
let commands = statements
|
||||
.iter()
|
||||
.filter_map(|statement| {
|
||||
self.transpile_statement(statement, program_identifier, scope, handler)
|
||||
self.transpile_statement(statement, program_identifier, handler)
|
||||
.unwrap_or_else(|err| {
|
||||
errors.push(err);
|
||||
None
|
||||
|
@ -688,7 +641,7 @@ impl Transpiler {
|
|||
}
|
||||
Some(Execute::Runs(commands))
|
||||
} else {
|
||||
self.transpile_statement(&statements[0], program_identifier, scope, handler)?
|
||||
self.transpile_statement(&statements[0], program_identifier, handler)?
|
||||
.map(|cmd| Execute::Run(Box::new(cmd)))
|
||||
};
|
||||
|
||||
|
@ -700,7 +653,6 @@ impl Transpiler {
|
|||
then,
|
||||
Some(el),
|
||||
program_identifier,
|
||||
scope,
|
||||
handler,
|
||||
)
|
||||
},
|
||||
|
@ -715,7 +667,6 @@ impl Transpiler {
|
|||
then: Execute,
|
||||
el: Option<&Else>,
|
||||
program_identifier: &str,
|
||||
scope: &Scope,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<Option<Execute>> {
|
||||
let (_, cond) = cond.clone().dissolve();
|
||||
|
@ -729,7 +680,7 @@ impl Transpiler {
|
|||
if statements.is_empty() {
|
||||
None
|
||||
} else if statements.len() == 1 {
|
||||
self.transpile_statement(&statements[0], program_identifier, scope, handler)
|
||||
self.transpile_statement(&statements[0], program_identifier, handler)
|
||||
.unwrap_or_else(|err| {
|
||||
errors.push(err);
|
||||
None
|
||||
|
@ -739,7 +690,7 @@ impl Transpiler {
|
|||
let commands = statements
|
||||
.iter()
|
||||
.filter_map(|statement| {
|
||||
self.transpile_statement(statement, program_identifier, scope, handler)
|
||||
self.transpile_statement(statement, program_identifier, handler)
|
||||
.unwrap_or_else(|err| {
|
||||
errors.push(err);
|
||||
None
|
||||
|
@ -767,20 +718,12 @@ impl Transpiler {
|
|||
head: &ExecuteBlockHead,
|
||||
tail: Option<Execute>,
|
||||
program_identifier: &str,
|
||||
scope: &Scope,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<Option<Execute>> {
|
||||
Ok(match head {
|
||||
ExecuteBlockHead::Conditional(cond) => {
|
||||
if let Some(tail) = tail {
|
||||
self.transpile_conditional(
|
||||
cond,
|
||||
tail,
|
||||
None,
|
||||
program_identifier,
|
||||
scope,
|
||||
handler,
|
||||
)?
|
||||
self.transpile_conditional(cond, tail, None, program_identifier, handler)?
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -47,11 +47,11 @@ pub fn identifier_to_macro(ident: &str) -> std::borrow::Cow<str> {
|
|||
if ident.contains("__")
|
||||
|| ident
|
||||
.chars()
|
||||
.any(|c| c == '_' || !c.is_ascii_alphanumeric())
|
||||
.any(|c| !(c == '_' && c.is_ascii_alphanumeric()))
|
||||
{
|
||||
let new_ident = ident
|
||||
.chars()
|
||||
.filter(|c| *c != '_' && c.is_ascii_alphanumeric())
|
||||
.filter(|c| *c == '_' || c.is_ascii_alphanumeric())
|
||||
.collect::<String>();
|
||||
|
||||
let chksum = md5::hash(ident).to_hex_lowercase();
|
||||
|
|
|
@ -1,117 +0,0 @@
|
|||
#![expect(unused)]
|
||||
|
||||
use std::{collections::HashMap, sync::RwLock};
|
||||
|
||||
use super::Transpiler;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum VariableType {
|
||||
FunctionArgument {
|
||||
index: usize,
|
||||
},
|
||||
Scoreboard {
|
||||
objective: String,
|
||||
},
|
||||
ScoreboardValue {
|
||||
objective: String,
|
||||
name: String,
|
||||
},
|
||||
ScoreboardArray {
|
||||
objective: String,
|
||||
names: Vec<String>,
|
||||
},
|
||||
Tag {
|
||||
tag_name: String,
|
||||
},
|
||||
BooleanStorage {
|
||||
storage_name: String,
|
||||
path: String,
|
||||
},
|
||||
BooleanStorageArray {
|
||||
storage_name: String,
|
||||
paths: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Scope<'a> {
|
||||
parent: Option<&'a Scope<'a>>,
|
||||
variables: RwLock<HashMap<String, VariableType>>,
|
||||
}
|
||||
|
||||
impl<'a> Scope<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn new_child(&'a self) -> Self {
|
||||
Self {
|
||||
parent: Some(self),
|
||||
variables: RwLock::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_variable(&self, name: &str) -> Option<VariableType> {
|
||||
let var = self.variables.read().unwrap().get(name).cloned();
|
||||
if var.is_some() {
|
||||
var
|
||||
} else {
|
||||
self.parent.and_then(|parent| parent.get_variable(name))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_variable(&self, name: &str, var: VariableType) {
|
||||
self.variables
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(name.to_string(), var);
|
||||
}
|
||||
|
||||
pub fn get_parent(&self) -> Option<&'a Self> {
|
||||
self.parent
|
||||
}
|
||||
}
|
||||
|
||||
impl Transpiler {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_scope() {
|
||||
let scope = Scope::new();
|
||||
{
|
||||
let mut variables = scope.variables.write().unwrap();
|
||||
variables.insert(
|
||||
"test".to_string(),
|
||||
VariableType::Scoreboard {
|
||||
objective: "test".to_string(),
|
||||
},
|
||||
);
|
||||
}
|
||||
match scope.get_variable("test") {
|
||||
Some(VariableType::Scoreboard { objective }) => assert_eq!(objective, "test"),
|
||||
_ => panic!("Incorrect Variable"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parent() {
|
||||
let scope = Scope::new();
|
||||
{
|
||||
let mut variables = scope.variables.write().unwrap();
|
||||
variables.insert(
|
||||
"test".to_string(),
|
||||
VariableType::Scoreboard {
|
||||
objective: "test".to_string(),
|
||||
},
|
||||
);
|
||||
}
|
||||
let child = scope.new_child();
|
||||
match child.get_variable("test") {
|
||||
Some(VariableType::Scoreboard { objective }) => assert_eq!(objective, "test"),
|
||||
_ => panic!("Incorrect Variable"),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue