allow importing global variables, get rid of functions and aliases field of transpiler
This commit is contained in:
parent
0a8bf37e40
commit
1e82d2321f
13
src/lib.rs
13
src/lib.rs
|
@ -115,9 +115,6 @@ pub fn parse(
|
||||||
|
|
||||||
/// Transpiles the given source code into a shulkerbox [`Datapack`].
|
/// Transpiles the given source code into a shulkerbox [`Datapack`].
|
||||||
///
|
///
|
||||||
/// # Parameters:
|
|
||||||
/// - `script_paths`: A list of tuples containing the identifier and the path of each script file.
|
|
||||||
///
|
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - If an error occurs during [`parse()`]
|
/// - If an error occurs during [`parse()`]
|
||||||
/// - If an error occurs while transpiling the source code.
|
/// - If an error occurs while transpiling the source code.
|
||||||
|
@ -164,15 +161,7 @@ where
|
||||||
|
|
||||||
Ok(program)
|
Ok(program)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
if programs.iter().any(Result::is_err) {
|
|
||||||
return Err(programs.into_iter().find_map(Result::err).unwrap());
|
|
||||||
}
|
|
||||||
let programs = programs
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(Result::ok)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
tracing::info!("Transpiling the source code.");
|
tracing::info!("Transpiling the source code.");
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ fn get_global_scope(declarations: &[Declaration]) -> SemanticScope<'static> {
|
||||||
let scope = SemanticScope::new();
|
let scope = SemanticScope::new();
|
||||||
|
|
||||||
for declaration in declarations {
|
for declaration in declarations {
|
||||||
declaration.add_to_scope(&scope);
|
declaration.add_to_semantic_scope(&scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
scope
|
scope
|
||||||
|
@ -83,23 +83,13 @@ impl Namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Declaration {
|
impl Declaration {
|
||||||
fn add_to_scope(&self, scope: &SemanticScope) {
|
fn add_to_semantic_scope(&self, scope: &SemanticScope) {
|
||||||
match self {
|
match self {
|
||||||
Self::Function(func) => {
|
Self::Function(func) => {
|
||||||
let name = func.identifier();
|
let name = func.identifier();
|
||||||
scope.set_variable(name.span.str(), VariableType::Function);
|
scope.set_variable(name.span.str(), VariableType::Function);
|
||||||
}
|
}
|
||||||
Self::Import(imp) => match imp.items() {
|
Self::GlobalVariable((_, var, _)) => {
|
||||||
ImportItems::All(_) => {}
|
|
||||||
ImportItems::Named(items) => {
|
|
||||||
for item in items.elements() {
|
|
||||||
if scope.get_variable(item.span.str()) != Some(VariableType::Function) {
|
|
||||||
scope.set_variable(item.span.str(), VariableType::Function);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Self::GlobalVariable((var, _)) => {
|
|
||||||
let name = var.identifier();
|
let name = var.identifier();
|
||||||
let var_type = match var {
|
let var_type = match var {
|
||||||
VariableDeclaration::Array(arr) => match arr.variable_type().keyword {
|
VariableDeclaration::Array(arr) => match arr.variable_type().keyword {
|
||||||
|
@ -118,7 +108,7 @@ impl Declaration {
|
||||||
};
|
};
|
||||||
scope.set_variable(name.span.str(), var_type);
|
scope.set_variable(name.span.str(), var_type);
|
||||||
}
|
}
|
||||||
Self::Tag(_) => {}
|
Self::Import(_) | Self::Tag(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +146,7 @@ impl Declaration {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Self::GlobalVariable((var, _)) => var.analyze_semantics(scope, handler),
|
Self::GlobalVariable((_, var, _)) => var.analyze_semantics(scope, handler),
|
||||||
|
|
||||||
Self::Tag(_) => Ok(()),
|
Self::Tag(_) => Ok(()),
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ use super::{
|
||||||
/// Function
|
/// Function
|
||||||
/// | Import
|
/// | Import
|
||||||
/// | Tag
|
/// | Tag
|
||||||
/// | (VariableDeclaration ';')
|
/// | ('pub'? VariableDeclaration ';')
|
||||||
/// ;
|
/// ;
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
@ -46,7 +46,7 @@ pub enum Declaration {
|
||||||
Function(Function),
|
Function(Function),
|
||||||
Import(Import),
|
Import(Import),
|
||||||
Tag(Tag),
|
Tag(Tag),
|
||||||
GlobalVariable((VariableDeclaration, Punctuation)),
|
GlobalVariable((Option<Keyword>, VariableDeclaration, Punctuation)),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceElement for Declaration {
|
impl SourceElement for Declaration {
|
||||||
|
@ -55,8 +55,9 @@ impl SourceElement for Declaration {
|
||||||
Self::Function(function) => function.span(),
|
Self::Function(function) => function.span(),
|
||||||
Self::Import(import) => import.span(),
|
Self::Import(import) => import.span(),
|
||||||
Self::Tag(tag) => tag.span(),
|
Self::Tag(tag) => tag.span(),
|
||||||
Self::GlobalVariable((variable, semicolon)) => variable
|
Self::GlobalVariable((pub_kw, variable, semicolon)) => pub_kw
|
||||||
.span()
|
.as_ref()
|
||||||
|
.map_or_else(|| variable.span(), SourceElement::span)
|
||||||
.join(&semicolon.span)
|
.join(&semicolon.span)
|
||||||
.expect("invalid declaration span"),
|
.expect("invalid declaration span"),
|
||||||
}
|
}
|
||||||
|
@ -75,10 +76,10 @@ impl Declaration {
|
||||||
|
|
||||||
Ok(Self::Function(function))
|
Ok(Self::Function(function))
|
||||||
}
|
}
|
||||||
Self::GlobalVariable((var, semi)) => {
|
Self::GlobalVariable((pub_kw, var, semi)) => {
|
||||||
let var_with_annotation = var.with_annotation(annotation)?;
|
let var_with_annotation = var.with_annotation(annotation)?;
|
||||||
|
|
||||||
Ok(Self::GlobalVariable((var_with_annotation, semi)))
|
Ok(Self::GlobalVariable((pub_kw, var_with_annotation, semi)))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let err = Error::InvalidAnnotation(InvalidAnnotation {
|
let err = Error::InvalidAnnotation(InvalidAnnotation {
|
||||||
|
@ -356,11 +357,19 @@ impl Parser<'_> {
|
||||||
Reading::Atomic(Token::Keyword(pub_keyword))
|
Reading::Atomic(Token::Keyword(pub_keyword))
|
||||||
if pub_keyword.keyword == KeywordKind::Pub =>
|
if pub_keyword.keyword == KeywordKind::Pub =>
|
||||||
{
|
{
|
||||||
let function = self.parse_function(handler)?;
|
if let Ok(function) = self.try_parse(|parser| parser.parse_function(&VoidHandler)) {
|
||||||
|
tracing::trace!("Parsed function '{:?}'", function.identifier.span.str());
|
||||||
|
|
||||||
tracing::trace!("Parsed function '{:?}'", function.identifier.span.str());
|
Ok(Declaration::Function(function))
|
||||||
|
} else {
|
||||||
|
// eat the pub keyword
|
||||||
|
self.forward();
|
||||||
|
|
||||||
Ok(Declaration::Function(function))
|
let var = self.parse_variable_declaration(handler)?;
|
||||||
|
let semi = self.parse_punctuation(';', true, handler)?;
|
||||||
|
|
||||||
|
Ok(Declaration::GlobalVariable((Some(pub_keyword), var, semi)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse annotations
|
// parse annotations
|
||||||
|
@ -480,7 +489,7 @@ impl Parser<'_> {
|
||||||
let var = self.parse_variable_declaration(handler)?;
|
let var = self.parse_variable_declaration(handler)?;
|
||||||
let semi = self.parse_punctuation(';', true, handler)?;
|
let semi = self.parse_punctuation(';', true, handler)?;
|
||||||
|
|
||||||
Ok(Declaration::GlobalVariable((var, semi)))
|
Ok(Declaration::GlobalVariable((None, var, semi)))
|
||||||
}
|
}
|
||||||
|
|
||||||
unexpected => {
|
unexpected => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use chksum_md5 as md5;
|
use chksum_md5 as md5;
|
||||||
use enum_as_inner::EnumAsInner;
|
use enum_as_inner::EnumAsInner;
|
||||||
use std::{collections::BTreeMap, sync::Arc};
|
use std::{borrow::ToOwned, collections::BTreeMap, sync::Arc};
|
||||||
|
|
||||||
use shulkerbox::datapack::{Command, Execute};
|
use shulkerbox::datapack::{Command, Execute};
|
||||||
|
|
||||||
|
@ -48,41 +48,27 @@ impl Transpiler {
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<(String, TranspiledFunctionArguments)> {
|
) -> TranspileResult<(String, TranspiledFunctionArguments)> {
|
||||||
let program_identifier = identifier_span.source_file().identifier();
|
let program_identifier = identifier_span.source_file().identifier();
|
||||||
let program_query = (
|
let function = scope.get_variable(identifier_span.str());
|
||||||
program_identifier.to_string(),
|
let already_transpiled = function
|
||||||
identifier_span.str().to_string(),
|
.as_ref()
|
||||||
);
|
|
||||||
let alias_query = self.aliases.get(&program_query).cloned();
|
|
||||||
let already_transpiled = scope
|
|
||||||
.get_variable(identifier_span.str())
|
|
||||||
.expect("called function should be in scope")
|
.expect("called function should be in scope")
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.as_function()
|
.as_function()
|
||||||
.map(|(_, path)| path.get().is_some())
|
.map(|(_, path, _)| path.get().is_some())
|
||||||
.expect("called variable should be of type function");
|
.expect("called variable should be of type function");
|
||||||
|
|
||||||
let function_data = scope
|
let function_data = function.ok_or_else(|| {
|
||||||
.get_variable(identifier_span.str())
|
let error = TranspileError::MissingFunctionDeclaration(
|
||||||
.or_else(|| {
|
MissingFunctionDeclaration::from_scope(identifier_span.clone(), scope),
|
||||||
alias_query
|
);
|
||||||
.clone()
|
handler.receive(error.clone());
|
||||||
.and_then(|(alias_program_identifier, alias_function_name)| {
|
error
|
||||||
self.scopes
|
})?;
|
||||||
.get(&alias_program_identifier)
|
|
||||||
.and_then(|s| s.get_variable(&alias_function_name))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.ok_or_else(|| {
|
|
||||||
let error = TranspileError::MissingFunctionDeclaration(
|
|
||||||
MissingFunctionDeclaration::from_scope(identifier_span.clone(), scope),
|
|
||||||
);
|
|
||||||
handler.receive(error.clone());
|
|
||||||
error
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let VariableData::Function {
|
let VariableData::Function {
|
||||||
function_data,
|
function_data,
|
||||||
path: function_path,
|
path: function_path,
|
||||||
|
function_scope,
|
||||||
} = function_data.as_ref()
|
} = function_data.as_ref()
|
||||||
else {
|
else {
|
||||||
unreachable!("must be of correct type, otherwise errored out before");
|
unreachable!("must be of correct type, otherwise errored out before");
|
||||||
|
@ -134,8 +120,6 @@ impl Transpiler {
|
||||||
|
|
||||||
function_path.set(function_location.clone()).unwrap();
|
function_path.set(function_location.clone()).unwrap();
|
||||||
|
|
||||||
let function_scope = Scope::with_parent(scope);
|
|
||||||
|
|
||||||
for (i, param) in function_data.parameters.iter().enumerate() {
|
for (i, param) in function_data.parameters.iter().enumerate() {
|
||||||
let param_str = param.identifier().span.str();
|
let param_str = param.identifier().span.str();
|
||||||
match param.variable_type() {
|
match param.variable_type() {
|
||||||
|
@ -181,7 +165,7 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
let commands =
|
let commands =
|
||||||
self.transpile_function(&statements, program_identifier, &function_scope, handler)?;
|
self.transpile_function(&statements, program_identifier, function_scope, handler)?;
|
||||||
|
|
||||||
let namespace = self.datapack.namespace_mut(&function_data.namespace);
|
let namespace = self.datapack.namespace_mut(&function_data.namespace);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Transpiler for `Shulkerscript`
|
//! Transpiler for `Shulkerscript`
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashMap, HashSet},
|
collections::{BTreeMap, HashSet},
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
sync::{Arc, OnceLock},
|
sync::{Arc, OnceLock},
|
||||||
};
|
};
|
||||||
|
@ -40,11 +40,7 @@ pub struct Transpiler {
|
||||||
pub(super) initialized_constant_scores: HashSet<i64>,
|
pub(super) initialized_constant_scores: HashSet<i64>,
|
||||||
pub(super) temp_counter: usize,
|
pub(super) temp_counter: usize,
|
||||||
/// Top-level [`Scope`] for each program identifier
|
/// Top-level [`Scope`] for each program identifier
|
||||||
pub(super) scopes: BTreeMap<String, Arc<Scope<'static>>>,
|
pub(super) scopes: BTreeMap<String, Arc<Scope>>,
|
||||||
/// Key: (program identifier, function name)
|
|
||||||
pub(super) functions: BTreeMap<(String, String), FunctionData>,
|
|
||||||
/// Key: alias, Value: target
|
|
||||||
pub(super) aliases: HashMap<(String, String), (String, String)>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transpiler {
|
impl Transpiler {
|
||||||
|
@ -59,8 +55,6 @@ impl Transpiler {
|
||||||
initialized_constant_scores: HashSet::new(),
|
initialized_constant_scores: HashSet::new(),
|
||||||
temp_counter: 0,
|
temp_counter: 0,
|
||||||
scopes: BTreeMap::new(),
|
scopes: BTreeMap::new(),
|
||||||
functions: BTreeMap::new(),
|
|
||||||
aliases: HashMap::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +69,11 @@ impl Transpiler {
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Result<Datapack, TranspileError> {
|
) -> Result<Datapack, TranspileError> {
|
||||||
tracing::trace!("Transpiling program declarations");
|
tracing::trace!("Transpiling program declarations");
|
||||||
|
|
||||||
|
let mut aliases = Vec::new();
|
||||||
|
|
||||||
for program in programs {
|
for program in programs {
|
||||||
|
let mut program_aliases = BTreeMap::new();
|
||||||
let program_identifier = program
|
let program_identifier = program
|
||||||
.namespace()
|
.namespace()
|
||||||
.span()
|
.span()
|
||||||
|
@ -84,17 +82,48 @@ impl Transpiler {
|
||||||
.clone();
|
.clone();
|
||||||
let scope = self
|
let scope = self
|
||||||
.scopes
|
.scopes
|
||||||
.entry(program_identifier)
|
.entry(program_identifier.clone())
|
||||||
.or_insert_with(Scope::with_internal_functions)
|
.or_insert_with(Scope::with_internal_functions)
|
||||||
.to_owned();
|
.to_owned();
|
||||||
self.transpile_program_declarations(program, &scope, handler)?;
|
self.transpile_program_declarations(program, &mut program_aliases, &scope, handler)?;
|
||||||
|
aliases.push((scope.clone(), program_aliases));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (scope, program_aliases) in aliases {
|
||||||
|
for (alias_name, (alias_program_identifier, actual_name)) in program_aliases {
|
||||||
|
if let Some(alias_scope) = self.scopes.get(&alias_program_identifier) {
|
||||||
|
if let Some(var_data) = alias_scope.get_variable(&actual_name) {
|
||||||
|
scope.set_arc_variable(&alias_name, var_data);
|
||||||
|
} else {
|
||||||
|
tracing::error!(
|
||||||
|
"Importing a non-existent variable: {} from {}",
|
||||||
|
actual_name,
|
||||||
|
alias_program_identifier
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tracing::error!(
|
||||||
|
"Importing from a non-existent program: {}",
|
||||||
|
alias_program_identifier
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut always_transpile_functions = Vec::new();
|
let mut always_transpile_functions = Vec::new();
|
||||||
|
|
||||||
{
|
{
|
||||||
let functions = &mut self.functions;
|
let functions = self.scopes.iter().flat_map(|(_, scope)| {
|
||||||
for (_, data) in functions.iter() {
|
scope
|
||||||
|
.get_local_variables()
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.values()
|
||||||
|
.filter_map(|data| data.as_function().map(|(data, _, _)| data))
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
});
|
||||||
|
for data in functions {
|
||||||
let always_transpile_function = data.annotations.contains_key("tick")
|
let always_transpile_function = data.annotations.contains_key("tick")
|
||||||
|| data.annotations.contains_key("load")
|
|| data.annotations.contains_key("load")
|
||||||
|| data.annotations.contains_key("deobfuscate");
|
|| data.annotations.contains_key("deobfuscate");
|
||||||
|
@ -141,24 +170,25 @@ impl Transpiler {
|
||||||
fn transpile_program_declarations(
|
fn transpile_program_declarations(
|
||||||
&mut self,
|
&mut self,
|
||||||
program: &ProgramFile,
|
program: &ProgramFile,
|
||||||
|
program_aliases: &mut BTreeMap<String, (String, String)>,
|
||||||
scope: &Arc<Scope>,
|
scope: &Arc<Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<()> {
|
) -> TranspileResult<()> {
|
||||||
let namespace = program.namespace();
|
let namespace = program.namespace();
|
||||||
|
|
||||||
for declaration in program.declarations() {
|
for declaration in program.declarations() {
|
||||||
self.transpile_declaration(declaration, namespace, scope, handler)?;
|
self.transpile_declaration(declaration, namespace, program_aliases, scope, handler)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transpiles the given declaration.
|
/// Transpiles the given declaration.
|
||||||
#[allow(clippy::needless_pass_by_ref_mut)]
|
|
||||||
fn transpile_declaration(
|
fn transpile_declaration(
|
||||||
&mut self,
|
&mut self,
|
||||||
declaration: &Declaration,
|
declaration: &Declaration,
|
||||||
namespace: &Namespace,
|
namespace: &Namespace,
|
||||||
|
program_aliases: &mut BTreeMap<String, (String, String)>,
|
||||||
scope: &Arc<Scope>,
|
scope: &Arc<Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<()> {
|
) -> TranspileResult<()> {
|
||||||
|
@ -197,19 +227,18 @@ impl Transpiler {
|
||||||
scope.set_variable(
|
scope.set_variable(
|
||||||
&name,
|
&name,
|
||||||
VariableData::Function {
|
VariableData::Function {
|
||||||
function_data: function_data.clone(),
|
function_data,
|
||||||
path: OnceLock::new(),
|
path: OnceLock::new(),
|
||||||
|
function_scope: scope.clone(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.functions
|
|
||||||
.insert((program_identifier, name), function_data);
|
|
||||||
}
|
}
|
||||||
Declaration::Import(import) => {
|
Declaration::Import(import) => {
|
||||||
let path = import.module().str_content();
|
let path = import.module().str_content();
|
||||||
let import_identifier =
|
let import_identifier =
|
||||||
super::util::calculate_import_identifier(&program_identifier, path);
|
super::util::calculate_import_identifier(&program_identifier, path);
|
||||||
|
|
||||||
let aliases = &mut self.aliases;
|
// let aliases = &mut self.aliases;
|
||||||
|
|
||||||
match import.items() {
|
match import.items() {
|
||||||
ImportItems::All(_) => {
|
ImportItems::All(_) => {
|
||||||
|
@ -220,8 +249,8 @@ impl Transpiler {
|
||||||
ImportItems::Named(list) => {
|
ImportItems::Named(list) => {
|
||||||
for item in list.elements() {
|
for item in list.elements() {
|
||||||
let name = item.span.str();
|
let name = item.span.str();
|
||||||
aliases.insert(
|
program_aliases.insert(
|
||||||
(program_identifier.clone(), name.to_string()),
|
name.to_string(),
|
||||||
(import_identifier.clone(), name.to_string()),
|
(import_identifier.clone(), name.to_string()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -244,7 +273,7 @@ impl Transpiler {
|
||||||
sb_tag.set_replace(true);
|
sb_tag.set_replace(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Declaration::GlobalVariable((declaration, _)) => {
|
Declaration::GlobalVariable((_, declaration, _)) => {
|
||||||
let setup_variable_cmds = self.transpile_variable_declaration(
|
let setup_variable_cmds = self.transpile_variable_declaration(
|
||||||
declaration,
|
declaration,
|
||||||
true,
|
true,
|
||||||
|
@ -278,7 +307,7 @@ impl Transpiler {
|
||||||
unreachable!("Only literal commands are allowed in functions at this time.")
|
unreachable!("Only literal commands are allowed in functions at this time.")
|
||||||
}
|
}
|
||||||
Statement::ExecuteBlock(execute) => {
|
Statement::ExecuteBlock(execute) => {
|
||||||
let child_scope = Scope::with_parent(scope);
|
let child_scope = Scope::with_parent(scope.clone());
|
||||||
Ok(self.transpile_execute_block(
|
Ok(self.transpile_execute_block(
|
||||||
execute,
|
execute,
|
||||||
program_identifier,
|
program_identifier,
|
||||||
|
@ -291,7 +320,7 @@ impl Transpiler {
|
||||||
Ok(vec![Command::Comment(content.to_string())])
|
Ok(vec![Command::Comment(content.to_string())])
|
||||||
}
|
}
|
||||||
Statement::Grouping(group) => {
|
Statement::Grouping(group) => {
|
||||||
let child_scope = Scope::with_parent(scope);
|
let child_scope = Scope::with_parent(scope.clone());
|
||||||
let statements = group.block().statements();
|
let statements = group.block().statements();
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
let commands = statements
|
let commands = statements
|
||||||
|
|
|
@ -49,6 +49,8 @@ pub enum VariableData {
|
||||||
function_data: FunctionData,
|
function_data: FunctionData,
|
||||||
/// The path to the function once it is generated.
|
/// The path to the function once it is generated.
|
||||||
path: OnceLock<String>,
|
path: OnceLock<String>,
|
||||||
|
/// The scope of the function.
|
||||||
|
function_scope: Arc<Scope>,
|
||||||
},
|
},
|
||||||
/// A macro function parameter.
|
/// A macro function parameter.
|
||||||
MacroParameter {
|
MacroParameter {
|
||||||
|
@ -131,9 +133,9 @@ impl<'a> From<&'a AssignmentDestination> for TranspileAssignmentTarget<'a> {
|
||||||
/// A scope that stores variables.
|
/// A scope that stores variables.
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Scope<'a> {
|
pub struct Scope {
|
||||||
/// Parent scope where variables are inherited from.
|
/// Parent scope where variables are inherited from.
|
||||||
parent: Option<&'a Arc<Self>>,
|
parent: Option<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.
|
/// How many times the variable has been shadowed in the current scope.
|
||||||
|
@ -141,7 +143,7 @@ pub struct Scope<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
impl<'a> Scope<'a> {
|
impl Scope {
|
||||||
/// Creates a new scope.
|
/// Creates a new scope.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Arc<Self> {
|
pub fn new() -> Arc<Self> {
|
||||||
|
@ -163,7 +165,7 @@ impl<'a> Scope<'a> {
|
||||||
|
|
||||||
/// Creates a new scope with a parent.
|
/// Creates a new scope with a parent.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_parent(parent: &'a Arc<Self>) -> Arc<Self> {
|
pub fn with_parent(parent: Arc<Self>) -> Arc<Self> {
|
||||||
Arc::new(Self {
|
Arc::new(Self {
|
||||||
parent: Some(parent),
|
parent: Some(parent),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -200,11 +202,17 @@ impl<'a> Scope<'a> {
|
||||||
|
|
||||||
/// 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) {
|
||||||
|
self.set_arc_variable(name, Arc::new(var));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a variable in the scope.
|
||||||
|
/// This function is used to set a variable that is already wrapped in an `Arc`.
|
||||||
|
pub fn set_arc_variable(&self, name: &str, var: Arc<VariableData>) {
|
||||||
let prev = self
|
let prev = self
|
||||||
.variables
|
.variables
|
||||||
.write()
|
.write()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.insert(name.to_string(), Arc::new(var));
|
.insert(name.to_string(), var);
|
||||||
*self
|
*self
|
||||||
.shadowed
|
.shadowed
|
||||||
.write()
|
.write()
|
||||||
|
@ -231,12 +239,12 @@ impl<'a> Scope<'a> {
|
||||||
|
|
||||||
/// Gets the parent scope.
|
/// Gets the parent scope.
|
||||||
pub fn get_parent(&self) -> Option<Arc<Self>> {
|
pub fn get_parent(&self) -> Option<Arc<Self>> {
|
||||||
self.parent.cloned()
|
self.parent.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
impl Debug for Scope<'_> {
|
impl Debug for Scope {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
struct VariableWrapper<'a>(&'a RwLock<HashMap<String, Arc<VariableData>>>);
|
struct VariableWrapper<'a>(&'a RwLock<HashMap<String, Arc<VariableData>>>);
|
||||||
impl Debug for VariableWrapper<'_> {
|
impl Debug for VariableWrapper<'_> {
|
||||||
|
@ -1263,7 +1271,7 @@ mod tests {
|
||||||
objective: "test".to_string(),
|
objective: "test".to_string(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let child = Scope::with_parent(&scope);
|
let child = Scope::with_parent(scope);
|
||||||
if let Some(var) = child.get_variable("test") {
|
if let Some(var) = child.get_variable("test") {
|
||||||
match var.as_ref() {
|
match var.as_ref() {
|
||||||
VariableData::Scoreboard { objective } => assert_eq!(objective, "test"),
|
VariableData::Scoreboard { objective } => assert_eq!(objective, "test"),
|
||||||
|
@ -1283,7 +1291,7 @@ mod tests {
|
||||||
objective: "test1".to_string(),
|
objective: "test1".to_string(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let child = Scope::with_parent(&scope);
|
let child = Scope::with_parent(scope);
|
||||||
child.set_variable(
|
child.set_variable(
|
||||||
"test",
|
"test",
|
||||||
VariableData::Scoreboard {
|
VariableData::Scoreboard {
|
||||||
|
|
Loading…
Reference in New Issue