Compare commits
No commits in common. "e772c4b2c205cd1b9831226826ca5a598ae26447" and "8ae065f5822a0bffb7bdd34379c34726f7adaf2b" have entirely different histories.
e772c4b2c2
...
8ae065f582
|
@ -38,7 +38,7 @@ path-absolutize = "3.1.1"
|
||||||
pathdiff = "0.2.3"
|
pathdiff = "0.2.3"
|
||||||
serde = { version = "1.0.217", features = ["derive"], optional = true }
|
serde = { version = "1.0.217", features = ["derive"], optional = true }
|
||||||
# shulkerbox = { version = "0.1.0", default-features = false, optional = true }
|
# shulkerbox = { version = "0.1.0", default-features = false, optional = true }
|
||||||
shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "cf8c922704a38980def3c6267cee2eeba304394c", default-features = false, optional = true }
|
shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "811d71508208f8415d881f7c4d73429f1a73f36a", default-features = false, optional = true }
|
||||||
strsim = "0.11.1"
|
strsim = "0.11.1"
|
||||||
strum = { version = "0.27.0", features = ["derive"] }
|
strum = { version = "0.27.0", features = ["derive"] }
|
||||||
thiserror = "2.0.11"
|
thiserror = "2.0.11"
|
||||||
|
|
|
@ -307,10 +307,6 @@ impl Semicolon {
|
||||||
SemicolonStatement::VariableDeclaration(decl) => {
|
SemicolonStatement::VariableDeclaration(decl) => {
|
||||||
decl.analyze_semantics(function_names, macro_names, handler)
|
decl.analyze_semantics(function_names, macro_names, handler)
|
||||||
}
|
}
|
||||||
SemicolonStatement::Assignment(_assignment) => {
|
|
||||||
// TODO: correctly analyze the semantics of the assignment
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,14 +93,6 @@ impl Statement {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
SemicolonStatement::Assignment(_) => {
|
|
||||||
let err = Error::InvalidAnnotation(InvalidAnnotation {
|
|
||||||
annotation: annotation.assignment.identifier.span,
|
|
||||||
target: "assignments".to_string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
Err(err)
|
|
||||||
}
|
|
||||||
SemicolonStatement::Expression(_) => {
|
SemicolonStatement::Expression(_) => {
|
||||||
let err = Error::InvalidAnnotation(InvalidAnnotation {
|
let err = Error::InvalidAnnotation(InvalidAnnotation {
|
||||||
annotation: annotation.assignment.identifier.span,
|
annotation: annotation.assignment.identifier.span,
|
||||||
|
@ -280,7 +272,7 @@ impl Semicolon {
|
||||||
/// Syntax Synopsis:
|
/// Syntax Synopsis:
|
||||||
/// ``` ebnf
|
/// ``` ebnf
|
||||||
/// SemicolonStatement:
|
/// SemicolonStatement:
|
||||||
/// (Expression | VariableDeclaration | Assignment)
|
/// (Expression | VariableDeclaration)
|
||||||
/// ';'
|
/// ';'
|
||||||
/// ;
|
/// ;
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -291,8 +283,6 @@ pub enum SemicolonStatement {
|
||||||
Expression(Expression),
|
Expression(Expression),
|
||||||
/// A variable declaration.
|
/// A variable declaration.
|
||||||
VariableDeclaration(VariableDeclaration),
|
VariableDeclaration(VariableDeclaration),
|
||||||
/// An assignment.
|
|
||||||
Assignment(Assignment),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceElement for SemicolonStatement {
|
impl SourceElement for SemicolonStatement {
|
||||||
|
@ -300,7 +290,6 @@ impl SourceElement for SemicolonStatement {
|
||||||
match self {
|
match self {
|
||||||
Self::Expression(expression) => expression.span(),
|
Self::Expression(expression) => expression.span(),
|
||||||
Self::VariableDeclaration(declaration) => declaration.span(),
|
Self::VariableDeclaration(declaration) => declaration.span(),
|
||||||
Self::Assignment(assignment) => assignment.span(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -680,44 +669,6 @@ impl TagVariableDeclaration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents an assignment in the syntax tree.
|
|
||||||
///
|
|
||||||
/// Syntax Synopsis:
|
|
||||||
/// ```ebnf
|
|
||||||
/// Assignment:
|
|
||||||
/// Identifier '=' Expression
|
|
||||||
/// ```
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
|
||||||
pub struct Assignment {
|
|
||||||
/// The identifier of the assignment.
|
|
||||||
#[get = "pub"]
|
|
||||||
identifier: Identifier,
|
|
||||||
/// The equals sign of the assignment.
|
|
||||||
#[get = "pub"]
|
|
||||||
equals: Punctuation,
|
|
||||||
/// The expression of the assignment.
|
|
||||||
#[get = "pub"]
|
|
||||||
expression: Expression,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SourceElement for Assignment {
|
|
||||||
fn span(&self) -> Span {
|
|
||||||
self.identifier
|
|
||||||
.span()
|
|
||||||
.join(&self.expression.span())
|
|
||||||
.expect("The span of the assignment is invalid.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Assignment {
|
|
||||||
/// Dissolves the [`Assignment`] into its components.
|
|
||||||
#[must_use]
|
|
||||||
pub fn dissolve(self) -> (Identifier, Punctuation, Expression) {
|
|
||||||
(self.identifier, self.equals, self.expression)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
/// Parses a [`Block`].
|
/// Parses a [`Block`].
|
||||||
///
|
///
|
||||||
|
@ -857,27 +808,9 @@ impl<'a> Parser<'a> {
|
||||||
self.parse_variable_declaration(handler)
|
self.parse_variable_declaration(handler)
|
||||||
.map(SemicolonStatement::VariableDeclaration)
|
.map(SemicolonStatement::VariableDeclaration)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => self
|
||||||
// try to parse assignment
|
.parse_expression(handler)
|
||||||
// TODO: improve
|
.map(SemicolonStatement::Expression),
|
||||||
#[expect(clippy::option_if_let_else)]
|
|
||||||
if let Ok(assignment) = self.try_parse(|p| {
|
|
||||||
let identifier = p.parse_identifier(&VoidHandler)?;
|
|
||||||
let equals = p.parse_punctuation('=', true, &VoidHandler)?;
|
|
||||||
let expression = p.parse_expression(&VoidHandler)?;
|
|
||||||
|
|
||||||
Ok(SemicolonStatement::Assignment(Assignment {
|
|
||||||
identifier,
|
|
||||||
equals,
|
|
||||||
expression,
|
|
||||||
}))
|
|
||||||
}) {
|
|
||||||
Ok(assignment)
|
|
||||||
} else {
|
|
||||||
self.parse_expression(handler)
|
|
||||||
.map(SemicolonStatement::Expression)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
let semicolon = self.parse_punctuation(';', true, handler)?;
|
let semicolon = self.parse_punctuation(';', true, handler)?;
|
||||||
|
|
|
@ -36,8 +36,6 @@ pub enum TranspileError {
|
||||||
MismatchedTypes(#[from] MismatchedTypes),
|
MismatchedTypes(#[from] MismatchedTypes),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
FunctionArgumentsNotAllowed(#[from] FunctionArgumentsNotAllowed),
|
FunctionArgumentsNotAllowed(#[from] FunctionArgumentsNotAllowed),
|
||||||
#[error(transparent)]
|
|
||||||
AssignmentError(#[from] AssignmentError),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of a transpilation operation.
|
/// The result of a transpilation operation.
|
||||||
|
@ -229,25 +227,3 @@ impl Display for FunctionArgumentsNotAllowed {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for FunctionArgumentsNotAllowed {}
|
impl std::error::Error for FunctionArgumentsNotAllowed {}
|
||||||
|
|
||||||
/// An error that occurs when an expression can not evaluate to the wanted type.
|
|
||||||
#[expect(clippy::module_name_repetitions)]
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub struct AssignmentError {
|
|
||||||
pub identifier: Span,
|
|
||||||
pub message: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for AssignmentError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}", Message::new(Severity::Error, &self.message))?;
|
|
||||||
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"\n{}",
|
|
||||||
SourceCodeDisplay::new(&self.identifier, Option::<u8>::None)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for AssignmentError {}
|
|
||||||
|
|
|
@ -38,9 +38,7 @@ use super::{
|
||||||
/// A transpiler for `Shulkerscript`.
|
/// A transpiler for `Shulkerscript`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Transpiler {
|
pub struct Transpiler {
|
||||||
pub(super) main_namespace_name: String,
|
|
||||||
pub(super) datapack: shulkerbox::datapack::Datapack,
|
pub(super) datapack: shulkerbox::datapack::Datapack,
|
||||||
pub(super) setup_cmds: Vec<Command>,
|
|
||||||
/// Top-level [`Scope`] for each program identifier
|
/// Top-level [`Scope`] for each program identifier
|
||||||
scopes: BTreeMap<String, Arc<Scope<'static>>>,
|
scopes: BTreeMap<String, Arc<Scope<'static>>>,
|
||||||
/// Key: (program identifier, function name)
|
/// Key: (program identifier, function name)
|
||||||
|
@ -53,11 +51,8 @@ impl Transpiler {
|
||||||
/// Creates a new transpiler.
|
/// Creates a new transpiler.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(main_namespace_name: impl Into<String>, pack_format: u8) -> Self {
|
pub fn new(main_namespace_name: impl Into<String>, pack_format: u8) -> Self {
|
||||||
let main_namespace_name = main_namespace_name.into();
|
|
||||||
Self {
|
Self {
|
||||||
main_namespace_name: main_namespace_name.clone(),
|
|
||||||
datapack: shulkerbox::datapack::Datapack::new(main_namespace_name, pack_format),
|
datapack: shulkerbox::datapack::Datapack::new(main_namespace_name, pack_format),
|
||||||
setup_cmds: Vec::new(),
|
|
||||||
scopes: BTreeMap::new(),
|
scopes: BTreeMap::new(),
|
||||||
functions: BTreeMap::new(),
|
functions: BTreeMap::new(),
|
||||||
aliases: HashMap::new(),
|
aliases: HashMap::new(),
|
||||||
|
@ -124,14 +119,6 @@ impl Transpiler {
|
||||||
self.get_or_transpile_function(&identifier_span, None, &scope, handler)?;
|
self.get_or_transpile_function(&identifier_span, None, &scope, handler)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.setup_cmds.is_empty() {
|
|
||||||
let main_namespace = self.datapack.namespace_mut(&self.main_namespace_name);
|
|
||||||
let setup_fn = main_namespace.function_mut("shu/setup");
|
|
||||||
setup_fn.get_commands_mut().extend(self.setup_cmds.clone());
|
|
||||||
self.datapack
|
|
||||||
.add_load(format!("{}:shu/setup", self.main_namespace_name));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,8 +218,8 @@ impl Transpiler {
|
||||||
Declaration::Tag(tag) => {
|
Declaration::Tag(tag) => {
|
||||||
let namespace = self
|
let namespace = self
|
||||||
.datapack
|
.datapack
|
||||||
.namespace_mut(namespace.namespace_name().str_content());
|
.namespace_mut(&namespace.namespace_name().str_content());
|
||||||
let sb_tag = namespace.tag_mut(tag.name().str_content(), tag.tag_type());
|
let sb_tag = namespace.tag_mut(&tag.name().str_content(), tag.tag_type());
|
||||||
|
|
||||||
if let Some(list) = &tag.entries().list {
|
if let Some(list) = &tag.entries().list {
|
||||||
for value in list.elements() {
|
for value in list.elements() {
|
||||||
|
@ -566,12 +553,6 @@ impl Transpiler {
|
||||||
SemicolonStatement::VariableDeclaration(decl) => {
|
SemicolonStatement::VariableDeclaration(decl) => {
|
||||||
self.transpile_variable_declaration(decl, program_identifier, scope, handler)
|
self.transpile_variable_declaration(decl, program_identifier, scope, handler)
|
||||||
}
|
}
|
||||||
SemicolonStatement::Assignment(assignment) => self.transpile_assignment(
|
|
||||||
assignment.identifier(),
|
|
||||||
assignment.expression(),
|
|
||||||
scope,
|
|
||||||
handler,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,12 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use chksum_md5 as md5;
|
use chksum_md5 as md5;
|
||||||
use shulkerbox::prelude::{Command, Condition, Execute};
|
use shulkerbox::prelude::Command;
|
||||||
use strum::EnumIs;
|
use strum::EnumIs;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{self, source_file::SourceElement as _, Handler},
|
base::{self, source_file::SourceElement as _, Handler},
|
||||||
lexical::token::{Identifier, KeywordKind},
|
lexical::token::KeywordKind,
|
||||||
syntax::syntax_tree::{
|
syntax::syntax_tree::{
|
||||||
expression::{Expression, Primary},
|
expression::{Expression, Primary},
|
||||||
statement::{SingleVariableDeclaration, VariableDeclaration},
|
statement::{SingleVariableDeclaration, VariableDeclaration},
|
||||||
|
@ -21,9 +21,8 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
error::{AssignmentError, IllegalAnnotationContent},
|
error::IllegalAnnotationContent, expression::DataLocation, FunctionData,
|
||||||
expression::DataLocation,
|
TranspileAnnotationValue, TranspileError, TranspileResult, Transpiler,
|
||||||
FunctionData, TranspileAnnotationValue, TranspileError, TranspileResult, Transpiler,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Stores the data required to access a variable.
|
/// Stores the data required to access a variable.
|
||||||
|
@ -88,8 +87,6 @@ pub struct Scope<'a> {
|
||||||
parent: Option<&'a Arc<Self>>,
|
parent: Option<&'a Arc<Self>>,
|
||||||
/// Variables stored in the scope.
|
/// Variables stored in the scope.
|
||||||
variables: RwLock<HashMap<String, Arc<VariableData>>>,
|
variables: RwLock<HashMap<String, Arc<VariableData>>>,
|
||||||
/// How many times the variable has been shadowed in the current scope.
|
|
||||||
shadowed: RwLock<HashMap<String, usize>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Scope<'a> {
|
impl<'a> Scope<'a> {
|
||||||
|
@ -120,35 +117,12 @@ impl<'a> Scope<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the number of times a variable has been shadowed.
|
|
||||||
pub fn get_variable_shadow_count(&self, name: &str) -> usize {
|
|
||||||
let count = self
|
|
||||||
.shadowed
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.get(name)
|
|
||||||
.copied()
|
|
||||||
.unwrap_or(0);
|
|
||||||
self.parent.as_ref().map_or(count, |parent| {
|
|
||||||
count
|
|
||||||
+ parent.get_variable_shadow_count(name)
|
|
||||||
+ usize::from(parent.get_variable(name).is_some())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets a variable in the scope.
|
/// Sets a variable in the scope.
|
||||||
pub fn set_variable(&self, name: &str, var: VariableData) {
|
pub fn set_variable(&self, name: &str, var: VariableData) {
|
||||||
let prev = self
|
self.variables
|
||||||
.variables
|
|
||||||
.write()
|
.write()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.insert(name.to_string(), Arc::new(var));
|
.insert(name.to_string(), Arc::new(var));
|
||||||
*self
|
|
||||||
.shadowed
|
|
||||||
.write()
|
|
||||||
.unwrap()
|
|
||||||
.entry(name.to_string())
|
|
||||||
.or_default() += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the variables stored in the current scope.
|
/// Gets the variables stored in the current scope.
|
||||||
|
@ -187,7 +161,6 @@ impl<'a> Debug for Scope<'a> {
|
||||||
s.field("parent", &self.parent);
|
s.field("parent", &self.parent);
|
||||||
|
|
||||||
s.field("variables", &VariableWrapper(&self.variables));
|
s.field("variables", &VariableWrapper(&self.variables));
|
||||||
s.field("shadowed", &self.shadowed);
|
|
||||||
s.finish()
|
s.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,6 +184,7 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::too_many_lines)]
|
||||||
fn transpile_single_variable_declaration(
|
fn transpile_single_variable_declaration(
|
||||||
&mut self,
|
&mut self,
|
||||||
single: &SingleVariableDeclaration,
|
single: &SingleVariableDeclaration,
|
||||||
|
@ -235,14 +209,97 @@ impl Transpiler {
|
||||||
handler.receive(error.clone());
|
handler.receive(error.clone());
|
||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
let (name, target) =
|
let (name, target) = if let Some(deobfuscate_annotation) = deobfuscate_annotation {
|
||||||
get_single_data_location_identifiers(single, program_identifier, scope, handler)?;
|
let deobfuscate_annotation_value =
|
||||||
|
TranspileAnnotationValue::from(deobfuscate_annotation.assignment().value.clone());
|
||||||
|
|
||||||
|
if let TranspileAnnotationValue::Map(map) = deobfuscate_annotation_value {
|
||||||
|
if map.len() > 2 {
|
||||||
|
let error =
|
||||||
|
TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
||||||
|
annotation: deobfuscate_annotation.span(),
|
||||||
|
message: "Deobfuscate annotation must have at most 2 key-value pairs."
|
||||||
|
.to_string(),
|
||||||
|
});
|
||||||
|
handler.receive(error.clone());
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
if let (Some(name), Some(target)) = (map.get("name"), map.get("target")) {
|
||||||
|
if let (
|
||||||
|
TranspileAnnotationValue::Expression(objective),
|
||||||
|
TranspileAnnotationValue::Expression(target),
|
||||||
|
) = (name, target)
|
||||||
|
{
|
||||||
|
if let (Some(name_eval), Some(target_eval)) =
|
||||||
|
(objective.comptime_eval(), target.comptime_eval())
|
||||||
|
{
|
||||||
|
// TODO: change invalid criteria if boolean
|
||||||
|
if !crate::util::is_valid_scoreboard_name(&name_eval) {
|
||||||
|
let error = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
||||||
|
annotation: deobfuscate_annotation.span(),
|
||||||
|
message: "Deobfuscate annotation 'name' must be a valid scoreboard name.".to_string()
|
||||||
|
});
|
||||||
|
handler.receive(error.clone());
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
if !crate::util::is_valid_player_name(&target_eval) {
|
||||||
|
let error = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
||||||
|
annotation: deobfuscate_annotation.span(),
|
||||||
|
message: "Deobfuscate annotation 'target' must be a valid player name.".to_string()
|
||||||
|
});
|
||||||
|
handler.receive(error.clone());
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
(name_eval, target_eval)
|
||||||
|
} else {
|
||||||
|
let error = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
||||||
|
annotation: deobfuscate_annotation.span(),
|
||||||
|
message: "Deobfuscate annotation 'name' or 'target' could not have been evaluated at compile time.".to_string()
|
||||||
|
});
|
||||||
|
handler.receive(error.clone());
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let error = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
||||||
|
annotation: deobfuscate_annotation.span(),
|
||||||
|
message: "Deobfuscate annotation 'name' and 'target' must be compile time expressions.".to_string()
|
||||||
|
});
|
||||||
|
handler.receive(error.clone());
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let error =
|
||||||
|
TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
||||||
|
annotation: deobfuscate_annotation.span(),
|
||||||
|
message:
|
||||||
|
"Deobfuscate annotation must have both 'name' and 'target' keys."
|
||||||
|
.to_string(),
|
||||||
|
});
|
||||||
|
handler.receive(error.clone());
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let error = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
||||||
|
annotation: deobfuscate_annotation.span(),
|
||||||
|
message: "Deobfuscate annotation must be a map.".to_string(),
|
||||||
|
});
|
||||||
|
handler.receive(error.clone());
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let name =
|
||||||
|
"shu_values_".to_string() + &md5::hash(program_identifier).to_hex_lowercase();
|
||||||
|
let target = md5::hash((Arc::as_ptr(scope) as usize).to_le_bytes())
|
||||||
|
.to_hex_lowercase()
|
||||||
|
.split_off(16);
|
||||||
|
|
||||||
|
(name, target)
|
||||||
|
};
|
||||||
|
|
||||||
match variable_type {
|
match variable_type {
|
||||||
KeywordKind::Int => {
|
KeywordKind::Int => {
|
||||||
if !self.datapack.scoreboards().contains_key(&name) {
|
if !self.datapack.scoreboards().contains_key(&name) {
|
||||||
self.datapack
|
self.datapack.register_scoreboard(&name, None, None);
|
||||||
.register_scoreboard(&name, None::<&str>, None::<&str>);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.set_variable(
|
scope.set_variable(
|
||||||
|
@ -253,15 +310,6 @@ impl Transpiler {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
KeywordKind::Bool => {
|
|
||||||
scope.set_variable(
|
|
||||||
single.identifier().span.str(),
|
|
||||||
VariableData::BooleanStorage {
|
|
||||||
storage_name: name,
|
|
||||||
path: target,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => todo!("implement other variable types"),
|
_ => todo!("implement other variable types"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,7 +317,7 @@ impl Transpiler {
|
||||||
|| Ok(Vec::new()),
|
|| Ok(Vec::new()),
|
||||||
|assignment| {
|
|assignment| {
|
||||||
self.transpile_assignment(
|
self.transpile_assignment(
|
||||||
single.identifier(),
|
single.identifier().span.str(),
|
||||||
assignment.expression(),
|
assignment.expression(),
|
||||||
scope,
|
scope,
|
||||||
handler,
|
handler,
|
||||||
|
@ -278,166 +326,27 @@ impl Transpiler {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn transpile_assignment(
|
fn transpile_assignment(
|
||||||
&mut self,
|
&mut self,
|
||||||
identifier: &Identifier,
|
name: &str,
|
||||||
expression: &crate::syntax::syntax_tree::expression::Expression,
|
expression: &crate::syntax::syntax_tree::expression::Expression,
|
||||||
scope: &Arc<Scope>,
|
scope: &Arc<Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<Vec<Command>> {
|
) -> TranspileResult<Vec<Command>> {
|
||||||
if let Some(target) = scope.get_variable(identifier.span.str()) {
|
let target = scope.get_variable(name).unwrap();
|
||||||
let data_location = match target.as_ref() {
|
let data_location = match target.as_ref() {
|
||||||
VariableData::BooleanStorage { storage_name, path } => Ok(DataLocation::Storage {
|
VariableData::BooleanStorage { storage_name, path } => DataLocation::Storage {
|
||||||
storage_name: storage_name.to_owned(),
|
storage_name: storage_name.to_owned(),
|
||||||
path: path.to_owned(),
|
path: path.to_owned(),
|
||||||
r#type: super::expression::StorageType::Boolean,
|
r#type: super::expression::StorageType::Boolean,
|
||||||
}),
|
},
|
||||||
VariableData::ScoreboardValue { objective, target } => {
|
VariableData::ScoreboardValue { objective, target } => DataLocation::ScoreboardValue {
|
||||||
Ok(DataLocation::ScoreboardValue {
|
objective: objective.to_owned(),
|
||||||
objective: objective.to_owned(),
|
target: target.to_owned(),
|
||||||
target: target.to_owned(),
|
},
|
||||||
})
|
_ => todo!("implement other variable types"),
|
||||||
}
|
};
|
||||||
VariableData::Function { .. } | VariableData::FunctionArgument { .. } => {
|
self.transpile_expression(expression, &data_location, scope, handler)
|
||||||
Err(TranspileError::AssignmentError(AssignmentError {
|
|
||||||
identifier: identifier.span(),
|
|
||||||
message: format!(
|
|
||||||
"Cannot assign to a {}.",
|
|
||||||
if matches!(target.as_ref(), VariableData::Function { .. }) {
|
|
||||||
"function"
|
|
||||||
} else {
|
|
||||||
"function argument"
|
|
||||||
}
|
|
||||||
),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
_ => todo!("implement other variable types"),
|
|
||||||
}?;
|
|
||||||
self.transpile_expression(expression, &data_location, scope, handler)
|
|
||||||
} else {
|
|
||||||
Err(TranspileError::AssignmentError(AssignmentError {
|
|
||||||
identifier: identifier.span(),
|
|
||||||
message: "Variable does not exist.".to_string(),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[expect(clippy::too_many_lines)]
|
|
||||||
fn get_single_data_location_identifiers(
|
|
||||||
single: &SingleVariableDeclaration,
|
|
||||||
program_identifier: &str,
|
|
||||||
scope: &Arc<Scope>,
|
|
||||||
handler: &impl Handler<base::Error>,
|
|
||||||
) -> TranspileResult<(String, String)> {
|
|
||||||
let mut deobfuscate_annotations = single
|
|
||||||
.annotations()
|
|
||||||
.iter()
|
|
||||||
.filter(|a| a.has_identifier("deobfuscate"));
|
|
||||||
|
|
||||||
let variable_type = single.variable_type().keyword;
|
|
||||||
|
|
||||||
let deobfuscate_annotation = deobfuscate_annotations.next();
|
|
||||||
|
|
||||||
if let Some(duplicate) = deobfuscate_annotations.next() {
|
|
||||||
let error = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
|
||||||
annotation: duplicate.span(),
|
|
||||||
message: "Multiple deobfuscate annotations are not allowed.".to_string(),
|
|
||||||
});
|
|
||||||
handler.receive(error.clone());
|
|
||||||
return Err(error);
|
|
||||||
}
|
|
||||||
if let Some(deobfuscate_annotation) = deobfuscate_annotation {
|
|
||||||
let deobfuscate_annotation_value =
|
|
||||||
TranspileAnnotationValue::from(deobfuscate_annotation.assignment().value.clone());
|
|
||||||
|
|
||||||
if let TranspileAnnotationValue::Map(map) = deobfuscate_annotation_value {
|
|
||||||
if map.len() > 2 {
|
|
||||||
let error = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
|
||||||
annotation: deobfuscate_annotation.span(),
|
|
||||||
message: "Deobfuscate annotation must have at most 2 key-value pairs."
|
|
||||||
.to_string(),
|
|
||||||
});
|
|
||||||
handler.receive(error.clone());
|
|
||||||
return Err(error);
|
|
||||||
}
|
|
||||||
if let (Some(name), Some(target)) = (map.get("name"), map.get("target")) {
|
|
||||||
if let (
|
|
||||||
TranspileAnnotationValue::Expression(objective),
|
|
||||||
TranspileAnnotationValue::Expression(target),
|
|
||||||
) = (name, target)
|
|
||||||
{
|
|
||||||
if let (Some(name_eval), Some(target_eval)) =
|
|
||||||
(objective.comptime_eval(), target.comptime_eval())
|
|
||||||
{
|
|
||||||
// TODO: change invalid criteria if boolean
|
|
||||||
if !crate::util::is_valid_scoreboard_name(&name_eval) {
|
|
||||||
let error = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
|
||||||
annotation: deobfuscate_annotation.span(),
|
|
||||||
message: "Deobfuscate annotation 'name' must be a valid scoreboard name.".to_string()
|
|
||||||
});
|
|
||||||
handler.receive(error.clone());
|
|
||||||
return Err(error);
|
|
||||||
}
|
|
||||||
if !crate::util::is_valid_player_name(&target_eval) {
|
|
||||||
let error = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
|
||||||
annotation: deobfuscate_annotation.span(),
|
|
||||||
message: "Deobfuscate annotation 'target' must be a valid player name.".to_string()
|
|
||||||
});
|
|
||||||
handler.receive(error.clone());
|
|
||||||
return Err(error);
|
|
||||||
}
|
|
||||||
Ok((name_eval, target_eval))
|
|
||||||
} else {
|
|
||||||
let error = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
|
||||||
annotation: deobfuscate_annotation.span(),
|
|
||||||
message: "Deobfuscate annotation 'name' or 'target' could not have been evaluated at compile time.".to_string()
|
|
||||||
});
|
|
||||||
handler.receive(error.clone());
|
|
||||||
Err(error)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let error = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
|
||||||
annotation: deobfuscate_annotation.span(),
|
|
||||||
message: "Deobfuscate annotation 'name' and 'target' must be compile time expressions.".to_string()
|
|
||||||
});
|
|
||||||
handler.receive(error.clone());
|
|
||||||
Err(error)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let error = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
|
||||||
annotation: deobfuscate_annotation.span(),
|
|
||||||
message: "Deobfuscate annotation must have both 'name' and 'target' keys."
|
|
||||||
.to_string(),
|
|
||||||
});
|
|
||||||
handler.receive(error.clone());
|
|
||||||
Err(error)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let error = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
|
||||||
annotation: deobfuscate_annotation.span(),
|
|
||||||
message: "Deobfuscate annotation must be a map.".to_string(),
|
|
||||||
});
|
|
||||||
handler.receive(error.clone());
|
|
||||||
Err(error)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let hashed = md5::hash(program_identifier).to_hex_lowercase();
|
|
||||||
let name = "shu_values_".to_string() + &hashed;
|
|
||||||
let identifier_name = single.identifier().span.str();
|
|
||||||
// TODO: generate same name each time (not dependent on pointer)
|
|
||||||
let mut target = md5::hash(format!(
|
|
||||||
"{scope}\0{identifier_name}\0{shadowed}",
|
|
||||||
scope = Arc::as_ptr(scope) as usize,
|
|
||||||
shadowed = scope.get_variable_shadow_count(identifier_name)
|
|
||||||
))
|
|
||||||
.to_hex_lowercase();
|
|
||||||
|
|
||||||
if matches!(variable_type, KeywordKind::Int) {
|
|
||||||
target.split_off(16);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((name, target))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -483,23 +392,4 @@ mod tests {
|
||||||
panic!("Variable missing")
|
panic!("Variable missing")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_shadowed_count() {
|
|
||||||
let scope = Scope::new();
|
|
||||||
scope.set_variable(
|
|
||||||
"test",
|
|
||||||
VariableData::Scoreboard {
|
|
||||||
objective: "test1".to_string(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let child = Scope::with_parent(&scope);
|
|
||||||
child.set_variable(
|
|
||||||
"test",
|
|
||||||
VariableData::Scoreboard {
|
|
||||||
objective: "test2".to_string(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
assert_eq!(child.get_variable_shadow_count("test"), 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue