implement uninstall annotation
This commit is contained in:
parent
1e82d2321f
commit
68149d9ddf
|
@ -35,7 +35,7 @@ pathdiff = "0.2.3"
|
||||||
serde = { version = "1.0.217", features = ["derive"], optional = true }
|
serde = { version = "1.0.217", features = ["derive"], optional = true }
|
||||||
serde_json = { version = "1.0.138", optional = true }
|
serde_json = { version = "1.0.138", 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 = "e9f2b9b91d72322ec2e063ce7b83415071306468", default-features = false, optional = true }
|
shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "7392887c127b1aae0d78d573a02332c2fa2591c8", 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"
|
||||||
|
|
|
@ -162,16 +162,14 @@ impl Function {
|
||||||
let child_scope = SemanticScope::with_parent(scope);
|
let child_scope = SemanticScope::with_parent(scope);
|
||||||
|
|
||||||
if let Some(parameters) = self.parameters().as_ref().map(ConnectedList::elements) {
|
if let Some(parameters) = self.parameters().as_ref().map(ConnectedList::elements) {
|
||||||
if let Some(incompatible) = self
|
if let Some(incompatible) = self.annotations().iter().find(|a| {
|
||||||
.annotations()
|
["tick", "load", "uninstall"].contains(&a.assignment().identifier.span.str())
|
||||||
.iter()
|
}) {
|
||||||
.find(|a| ["tick", "load"].contains(&a.assignment().identifier.span.str()))
|
|
||||||
{
|
|
||||||
let err =
|
let err =
|
||||||
error::Error::IncompatibleFunctionAnnotation(IncompatibleFunctionAnnotation {
|
error::Error::IncompatibleFunctionAnnotation(IncompatibleFunctionAnnotation {
|
||||||
span: incompatible.assignment().identifier.span.clone(),
|
span: incompatible.assignment().identifier.span.clone(),
|
||||||
reason:
|
reason:
|
||||||
"functions with the `tick` or `load` annotation cannot have parameters"
|
"functions with the `tick`, `load` or `uninstall` annotation cannot have parameters"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
});
|
});
|
||||||
handler.receive(err.clone());
|
handler.receive(err.clone());
|
||||||
|
|
|
@ -1722,10 +1722,16 @@ impl Transpiler {
|
||||||
.to_hex_lowercase()
|
.to_hex_lowercase()
|
||||||
.split_off(16)
|
.split_off(16)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
self.temp_counter = self.temp_counter.wrapping_add(amount);
|
self.temp_counter = self.temp_counter.wrapping_add(amount);
|
||||||
|
|
||||||
|
self.temp_data_storage_locations.extend(
|
||||||
|
paths
|
||||||
|
.iter()
|
||||||
|
.map(|path| (storage_name.clone(), path.clone())),
|
||||||
|
);
|
||||||
|
|
||||||
(storage_name, paths)
|
(storage_name, paths)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,8 +85,8 @@ impl Transpiler {
|
||||||
Ok("shu/".to_string() + &md5::hash(hash_data).to_hex_lowercase())
|
Ok("shu/".to_string() + &md5::hash(hash_data).to_hex_lowercase())
|
||||||
},
|
},
|
||||||
|val| match val {
|
|val| match val {
|
||||||
TranspileAnnotationValue::None => Ok(identifier_span.str().to_string()),
|
TranspileAnnotationValue::None(_) => Ok(identifier_span.str().to_string()),
|
||||||
TranspileAnnotationValue::Expression(expr) => expr
|
TranspileAnnotationValue::Expression(expr, _) => expr
|
||||||
.comptime_eval(scope, handler)
|
.comptime_eval(scope, handler)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|val| val.to_string_no_macro())
|
.and_then(|val| val.to_string_no_macro())
|
||||||
|
@ -101,10 +101,10 @@ impl Transpiler {
|
||||||
handler.receive(err.clone());
|
handler.receive(err.clone());
|
||||||
err
|
err
|
||||||
}),
|
}),
|
||||||
TranspileAnnotationValue::Map(_) => {
|
TranspileAnnotationValue::Map(_, span) => {
|
||||||
let err =
|
let err =
|
||||||
TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
||||||
annotation: identifier_span.clone(),
|
annotation: span.clone(),
|
||||||
message: "Deobfuscate annotation cannot be a map.".to_string(),
|
message: "Deobfuscate annotation cannot be a map.".to_string(),
|
||||||
});
|
});
|
||||||
handler.receive(err.clone());
|
handler.receive(err.clone());
|
||||||
|
|
|
@ -64,28 +64,34 @@ pub struct FunctionData {
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, EnumIs)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, EnumIs)]
|
||||||
pub enum TranspileAnnotationValue {
|
pub enum TranspileAnnotationValue {
|
||||||
/// No value.
|
/// No value.
|
||||||
None,
|
None(Span),
|
||||||
/// A single expression.
|
/// A single expression.
|
||||||
Expression(Expression),
|
Expression(Expression, Span),
|
||||||
/// A map of key-value pairs.
|
/// A map of key-value pairs.
|
||||||
Map(BTreeMap<String, TranspileAnnotationValue>),
|
Map(BTreeMap<String, TranspileAnnotationValue>, Span),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Option<AnnotationValue>> for TranspileAnnotationValue {
|
impl TranspileAnnotationValue {
|
||||||
fn from(value: Option<AnnotationValue>) -> Self {
|
/// Creates a new `TranspileAnnotationValue` from an [`AnnotationValue`] and [`Span`] of the key.
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_annotation_value(value: Option<AnnotationValue>, key_span: &Span) -> Self {
|
||||||
match value {
|
match value {
|
||||||
None => Self::None,
|
None => Self::None(key_span.clone()),
|
||||||
Some(AnnotationValue::Single { value, .. }) => Self::Expression(value),
|
Some(AnnotationValue::Single { value, .. }) => {
|
||||||
|
let span = value.span();
|
||||||
|
Self::Expression(value, span)
|
||||||
|
}
|
||||||
Some(AnnotationValue::Multiple { list, .. }) => {
|
Some(AnnotationValue::Multiple { list, .. }) => {
|
||||||
|
let span = list.span();
|
||||||
let map = list
|
let map = list
|
||||||
.into_elements()
|
.into_elements()
|
||||||
.map(|elem| {
|
.map(|elem| {
|
||||||
let key = elem.identifier.span.str().to_string();
|
let key = elem.identifier.span.str().to_string();
|
||||||
let value = Self::from(elem.value);
|
let value = Self::from_annotation_value(elem.value, &elem.identifier.span);
|
||||||
(key, value)
|
(key, value)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Self::Map(map)
|
Self::Map(map, span)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,9 +118,11 @@ impl Debug for FunctionData {
|
||||||
impl Debug for AnnotationValueWrapper<'_> {
|
impl Debug for AnnotationValueWrapper<'_> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
TranspileAnnotationValue::None => None::<u8>.fmt(f),
|
TranspileAnnotationValue::None(_) => None::<u8>.fmt(f),
|
||||||
TranspileAnnotationValue::Expression(expr) => expr.span().str().fmt(f),
|
TranspileAnnotationValue::Expression(expr, _) => {
|
||||||
TranspileAnnotationValue::Map(map) => AnnotationsWrapper(map).fmt(f),
|
expr.span().str().fmt(f)
|
||||||
|
}
|
||||||
|
TranspileAnnotationValue::Map(map, _) => AnnotationsWrapper(map).fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
//! Transpiler for `Shulkerscript`
|
//! Transpiler for `Shulkerscript`
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashSet},
|
collections::{BTreeMap, BTreeSet, HashSet},
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
sync::{Arc, OnceLock},
|
sync::{Arc, OnceLock},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
use shulkerbox::datapack::{self, Command, Datapack, Execute};
|
use shulkerbox::datapack::{self, Command, Datapack, Execute};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -21,7 +22,10 @@ use crate::{
|
||||||
},
|
},
|
||||||
AnnotationAssignment,
|
AnnotationAssignment,
|
||||||
},
|
},
|
||||||
transpile::util::{MacroString, MacroStringPart},
|
transpile::{
|
||||||
|
error::IllegalAnnotationContent,
|
||||||
|
util::{MacroString, MacroStringPart},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
@ -38,6 +42,7 @@ pub struct Transpiler {
|
||||||
pub(super) datapack: shulkerbox::datapack::Datapack,
|
pub(super) datapack: shulkerbox::datapack::Datapack,
|
||||||
pub(super) setup_cmds: Vec<Command>,
|
pub(super) setup_cmds: Vec<Command>,
|
||||||
pub(super) initialized_constant_scores: HashSet<i64>,
|
pub(super) initialized_constant_scores: HashSet<i64>,
|
||||||
|
pub(super) temp_data_storage_locations: BTreeSet<(String, String)>,
|
||||||
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>>,
|
pub(super) scopes: BTreeMap<String, Arc<Scope>>,
|
||||||
|
@ -53,6 +58,7 @@ impl Transpiler {
|
||||||
datapack: shulkerbox::datapack::Datapack::new(main_namespace_name, pack_format),
|
datapack: shulkerbox::datapack::Datapack::new(main_namespace_name, pack_format),
|
||||||
setup_cmds: Vec::new(),
|
setup_cmds: Vec::new(),
|
||||||
initialized_constant_scores: HashSet::new(),
|
initialized_constant_scores: HashSet::new(),
|
||||||
|
temp_data_storage_locations: BTreeSet::new(),
|
||||||
temp_counter: 0,
|
temp_counter: 0,
|
||||||
scopes: BTreeMap::new(),
|
scopes: BTreeMap::new(),
|
||||||
}
|
}
|
||||||
|
@ -110,10 +116,10 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut always_transpile_functions = Vec::new();
|
let functions = self
|
||||||
|
.scopes
|
||||||
{
|
.iter()
|
||||||
let functions = self.scopes.iter().flat_map(|(_, scope)| {
|
.flat_map(|(_, scope)| {
|
||||||
scope
|
scope
|
||||||
.get_local_variables()
|
.get_local_variables()
|
||||||
.read()
|
.read()
|
||||||
|
@ -122,16 +128,24 @@ impl Transpiler {
|
||||||
.filter_map(|data| data.as_function().map(|(data, _, _)| data))
|
.filter_map(|data| data.as_function().map(|(data, _, _)| data))
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
});
|
})
|
||||||
for data in functions {
|
.unique_by(|data| (data.namespace.clone(), data.identifier_span.clone()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let always_transpile_functions = functions
|
||||||
|
.iter()
|
||||||
|
.filter_map(|data| {
|
||||||
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")
|
||||||
|
|| data.annotations.contains_key("uninstall");
|
||||||
if always_transpile_function {
|
if always_transpile_function {
|
||||||
always_transpile_functions.push(data.identifier_span.clone());
|
Some(data.identifier_span.clone())
|
||||||
};
|
} else {
|
||||||
}
|
None
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
tracing::trace!(
|
tracing::trace!(
|
||||||
"Transpiling functions requested by user: {:?}",
|
"Transpiling functions requested by user: {:?}",
|
||||||
|
@ -163,6 +177,48 @@ impl Transpiler {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let uninstall_function_cmds = functions
|
||||||
|
.iter()
|
||||||
|
.filter_map(|data| data.annotations.get("uninstall").map(|val| (data, val)))
|
||||||
|
.map(|(function, val)| match val {
|
||||||
|
TranspileAnnotationValue::None(_) => {
|
||||||
|
let identifier_span = &function.identifier_span;
|
||||||
|
let scope = self
|
||||||
|
.scopes
|
||||||
|
.entry(identifier_span.source_file().identifier().to_owned())
|
||||||
|
.or_insert_with(Scope::with_internal_functions)
|
||||||
|
.to_owned();
|
||||||
|
let (function_path, _) =
|
||||||
|
self.get_or_transpile_function(identifier_span, None, &scope, handler)?;
|
||||||
|
let uninstall_cmd = Command::Raw(format!("function {function_path}"));
|
||||||
|
Ok(uninstall_cmd)
|
||||||
|
}
|
||||||
|
TranspileAnnotationValue::Expression(_, span)
|
||||||
|
| TranspileAnnotationValue::Map(_, span) => {
|
||||||
|
let error =
|
||||||
|
TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
||||||
|
annotation: span.clone(),
|
||||||
|
message: "uninstall annotation must not have a value".to_string(),
|
||||||
|
});
|
||||||
|
handler.receive(error.clone());
|
||||||
|
Err(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<TranspileResult<Vec<_>>>()?;
|
||||||
|
|
||||||
|
self.datapack
|
||||||
|
.add_uninstall_commands(uninstall_function_cmds);
|
||||||
|
|
||||||
|
let temp_data_uninstall_cmds = self
|
||||||
|
.temp_data_storage_locations
|
||||||
|
.iter()
|
||||||
|
.map(|(storage_name, path)| {
|
||||||
|
Command::Raw(format!("data remove storage {storage_name} {path}"))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
self.datapack
|
||||||
|
.add_uninstall_commands(temp_data_uninstall_cmds);
|
||||||
|
|
||||||
Ok(self.datapack)
|
Ok(self.datapack)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +264,10 @@ impl Transpiler {
|
||||||
} = annotation.assignment();
|
} = annotation.assignment();
|
||||||
(
|
(
|
||||||
key.span().str().to_string(),
|
key.span().str().to_string(),
|
||||||
TranspileAnnotationValue::from(value.clone()),
|
TranspileAnnotationValue::from_annotation_value(
|
||||||
|
value.clone(),
|
||||||
|
&key.span,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -229,7 +288,7 @@ impl Transpiler {
|
||||||
VariableData::Function {
|
VariableData::Function {
|
||||||
function_data,
|
function_data,
|
||||||
path: OnceLock::new(),
|
path: OnceLock::new(),
|
||||||
function_scope: scope.clone(),
|
function_scope: Scope::with_parent(scope.clone()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -805,11 +805,13 @@ impl Transpiler {
|
||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
if let Some(deobfuscate_annotation) = deobfuscate_annotation {
|
if let Some(deobfuscate_annotation) = deobfuscate_annotation {
|
||||||
let deobfuscate_annotation_value =
|
let deobfuscate_annotation_value = TranspileAnnotationValue::from_annotation_value(
|
||||||
TranspileAnnotationValue::from(deobfuscate_annotation.assignment().value.clone());
|
deobfuscate_annotation.assignment().value.clone(),
|
||||||
|
&deobfuscate_annotation.assignment().identifier.span,
|
||||||
|
);
|
||||||
|
|
||||||
match deobfuscate_annotation_value {
|
match deobfuscate_annotation_value {
|
||||||
TranspileAnnotationValue::Expression(expr) => {
|
TranspileAnnotationValue::Expression(expr, _) => {
|
||||||
if let Some(name_eval) = expr
|
if let Some(name_eval) = expr
|
||||||
.comptime_eval(scope, handler)
|
.comptime_eval(scope, handler)
|
||||||
.ok()
|
.ok()
|
||||||
|
@ -834,8 +836,8 @@ impl Transpiler {
|
||||||
Err(error)
|
Err(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TranspileAnnotationValue::None => Ok(identifier.span.str().to_string()),
|
TranspileAnnotationValue::None(_) => Ok(identifier.span.str().to_string()),
|
||||||
TranspileAnnotationValue::Map(_) => {
|
TranspileAnnotationValue::Map(_, _) => {
|
||||||
let error =
|
let error =
|
||||||
TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
||||||
annotation: deobfuscate_annotation.span(),
|
annotation: deobfuscate_annotation.span(),
|
||||||
|
@ -888,10 +890,12 @@ impl Transpiler {
|
||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
if let Some(deobfuscate_annotation) = deobfuscate_annotation {
|
if let Some(deobfuscate_annotation) = deobfuscate_annotation {
|
||||||
let deobfuscate_annotation_value =
|
let deobfuscate_annotation_value = TranspileAnnotationValue::from_annotation_value(
|
||||||
TranspileAnnotationValue::from(deobfuscate_annotation.assignment().value.clone());
|
deobfuscate_annotation.assignment().value.clone(),
|
||||||
|
&deobfuscate_annotation.assignment().identifier.span,
|
||||||
|
);
|
||||||
|
|
||||||
if let TranspileAnnotationValue::Map(map) = deobfuscate_annotation_value {
|
if let TranspileAnnotationValue::Map(map, _) = deobfuscate_annotation_value {
|
||||||
if map.len() > 2 {
|
if map.len() > 2 {
|
||||||
let error =
|
let error =
|
||||||
TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
||||||
|
@ -904,8 +908,8 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
if let (Some(name), Some(target)) = (map.get("name"), map.get("target")) {
|
if let (Some(name), Some(target)) = (map.get("name"), map.get("target")) {
|
||||||
if let (
|
if let (
|
||||||
TranspileAnnotationValue::Expression(objective),
|
TranspileAnnotationValue::Expression(objective, _),
|
||||||
TranspileAnnotationValue::Expression(target),
|
TranspileAnnotationValue::Expression(target, _),
|
||||||
) = (name, target)
|
) = (name, target)
|
||||||
{
|
{
|
||||||
if let (Some(name_eval), Some(target_eval)) = (
|
if let (Some(name_eval), Some(target_eval)) = (
|
||||||
|
@ -1022,11 +1026,13 @@ impl Transpiler {
|
||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
if let Some(deobfuscate_annotation) = deobfuscate_annotation {
|
if let Some(deobfuscate_annotation) = deobfuscate_annotation {
|
||||||
let deobfuscate_annotation_value =
|
let deobfuscate_annotation_value = TranspileAnnotationValue::from_annotation_value(
|
||||||
TranspileAnnotationValue::from(deobfuscate_annotation.assignment().value.clone());
|
deobfuscate_annotation.assignment().value.clone(),
|
||||||
|
&deobfuscate_annotation.assignment().identifier.span,
|
||||||
|
);
|
||||||
|
|
||||||
match deobfuscate_annotation_value {
|
match deobfuscate_annotation_value {
|
||||||
TranspileAnnotationValue::None => {
|
TranspileAnnotationValue::None(_) => {
|
||||||
let ident_str = declaration.identifier().span.str();
|
let ident_str = declaration.identifier().span.str();
|
||||||
let name = if matches!(variable_type, KeywordKind::Int) {
|
let name = if matches!(variable_type, KeywordKind::Int) {
|
||||||
ident_str.to_string()
|
ident_str.to_string()
|
||||||
|
@ -1041,7 +1047,7 @@ impl Transpiler {
|
||||||
let targets = (0..len).map(|i| i.to_string()).collect();
|
let targets = (0..len).map(|i| i.to_string()).collect();
|
||||||
Ok((name, targets))
|
Ok((name, targets))
|
||||||
}
|
}
|
||||||
TranspileAnnotationValue::Map(map) => {
|
TranspileAnnotationValue::Map(map, _) => {
|
||||||
// TODO: implement when map deobfuscate annotation is implemented
|
// TODO: implement when map deobfuscate annotation is implemented
|
||||||
let error =
|
let error =
|
||||||
TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
||||||
|
@ -1052,7 +1058,7 @@ impl Transpiler {
|
||||||
handler.receive(error.clone());
|
handler.receive(error.clone());
|
||||||
Err(error)
|
Err(error)
|
||||||
}
|
}
|
||||||
TranspileAnnotationValue::Expression(_) => {
|
TranspileAnnotationValue::Expression(_, _) => {
|
||||||
let error =
|
let error =
|
||||||
TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
||||||
annotation: deobfuscate_annotation.span(),
|
annotation: deobfuscate_annotation.span(),
|
||||||
|
|
Loading…
Reference in New Issue