improve compiler-internal function print

This commit is contained in:
Moritz Hölting 2025-03-28 13:54:25 +01:00
parent 0fd9dc432e
commit ca0edfc5bc
20 changed files with 143 additions and 75 deletions

View File

@ -11,6 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Macro strings
- Function parameters/arguments
- Variables:
- Integer (scoreboard)
- Boolean (data storage)
- Integer and boolean arrays (scoreboard and data storage)
- Integer map (scoreboard)
- Boolean map (tag)
- Example: barebones compiler
### Changed

View File

@ -118,7 +118,7 @@ impl Error {
/// return [`Some`], otherwise it will return [`None`].
#[must_use]
pub fn get_ref(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> {
return self.error.as_deref();
self.error.as_deref()
}
/// Consumes the [`Error`], returning its inner error (if any).

View File

@ -73,7 +73,7 @@ impl<'a, T> SourceCodeDisplay<'a, T> {
}
}
impl<'a, T: Display> Display for SourceCodeDisplay<'a, T> {
impl<T: Display> Display for SourceCodeDisplay<'_, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let start_location = self.span.start_location();
let end_location = self.span.end_location();

View File

@ -342,13 +342,13 @@ pub struct SourceIterator<'a> {
#[get_copy = "pub"]
prev: Option<(usize, char)>,
}
impl<'a> SourceIterator<'a> {
impl SourceIterator<'_> {
/// Peek at the next character in the source file.
pub fn peek(&mut self) -> Option<(usize, char)> {
self.iterator.peek().copied()
}
}
impl<'a> Iterator for SourceIterator<'a> {
impl Iterator for SourceIterator<'_> {
type Item = (usize, char);
fn next(&mut self) -> Option<Self::Item> {

View File

@ -730,7 +730,7 @@ impl Token {
}
}
// When there is no second slash and at the start of a line
else if prev_token.map_or(true, |token| token.span().str().contains('\n')) {
else if prev_token.is_none_or(|token| token.span().str().contains('\n')) {
Ok(Self::handle_command_literal(iter, start))
}
// Just a single slash punctuation

View File

@ -25,7 +25,6 @@ thread_local! {
}
/// Wrapper to remove duplicate source file data during (de-)serialization
#[expect(clippy::module_name_repetitions)]
#[derive(Debug)]
pub struct SerdeWrapper<T>(pub T);

View File

@ -211,7 +211,7 @@ pub struct Frame<'a> {
current_index: usize,
}
impl<'a> Frame<'a> {
impl Frame<'_> {
/// Checks if the current [`Frame`] doesn't have any more significant [`TokenTree`]s to
/// parse.
#[must_use]

View File

@ -286,7 +286,7 @@ impl SourceElement for Tag {
}
}
impl<'a> Parser<'a> {
impl Parser<'_> {
#[tracing::instrument(level = "trace", skip_all)]
pub fn parse_declaration(
&mut self,

View File

@ -432,7 +432,7 @@ impl LuaCode {
}
}
impl<'a> Parser<'a> {
impl Parser<'_> {
/// Parses an [`Expression`]
///
/// # Errors

View File

@ -228,7 +228,7 @@ impl SourceElement for AnnotationAssignment {
}
}
impl<'a> Parser<'a> {
impl Parser<'_> {
/// Parses a list of elements enclosed by a pair of delimiters, separated by a separator.
///
/// The parser position must be at the delimited list of the given delimiter. It will

View File

@ -92,7 +92,7 @@ impl Namespace {
}
}
impl<'a> Parser<'a> {
impl Parser<'_> {
/// Parses a [`ProgramFile`].
#[tracing::instrument(level = "debug", skip_all)]
pub fn parse_program(

View File

@ -749,7 +749,7 @@ impl SourceElement for AssignmentDestination {
}
}
impl<'a> Parser<'a> {
impl Parser<'_> {
/// Parses a [`Block`].
///
/// # Errors

View File

@ -756,7 +756,7 @@ impl Summon {
}
}
impl<'a> Parser<'a> {
impl Parser<'_> {
/// Parses an [`ExecuteBlock`].
///
/// # Errors

View File

@ -237,7 +237,6 @@ impl Display 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,

View File

@ -262,7 +262,7 @@ impl Primary {
Self::Identifier(ident) => {
scope
.get_variable(ident.span.str())
.map_or(false, |variable| match r#type {
.is_some_and(|variable| match r#type {
ValueType::Boolean => {
matches!(variable.as_ref(), VariableData::BooleanStorage { .. })
}
@ -289,7 +289,7 @@ impl Primary {
if let Self::Identifier(ident) = indexed.object().as_ref() {
scope
.get_variable(ident.span.str())
.map_or(false, |variable| match r#type {
.is_some_and(|variable| match r#type {
ValueType::Boolean => {
matches!(
variable.as_ref(),
@ -314,7 +314,7 @@ impl Primary {
Self::Lua(lua) => {
cfg_if::cfg_if! {
if #[cfg(feature = "lua")] {
lua.eval(scope, &VoidHandler).map_or(false, |(value, _)| match value {
lua.eval(scope, &VoidHandler).is_ok_and(|(value, _)| match value {
mlua::Value::Boolean(_) => matches!(r#type, ValueType::Boolean),
mlua::Value::Integer(_) => matches!(r#type, ValueType::Integer),
mlua::Value::String(_) => matches!(r#type, ValueType::String),

View File

@ -5,14 +5,14 @@ use std::{
sync::Arc,
};
use shulkerbox::prelude::Command;
use shulkerbox::prelude::{Command, Execute};
use serde_json::{json, Value as JsonValue};
use crate::{
base::{source_file::SourceElement as _, VoidHandler},
lexical::token::{Identifier, MacroStringLiteralPart},
semantic::error::InvalidFunctionArguments,
semantic::error::{InvalidFunctionArguments, UnexpectedExpression},
syntax::syntax_tree::expression::{Expression, FunctionCall, Primary},
transpile::{
error::{IllegalIndexing, IllegalIndexingReason, LuaRuntimeError, UnknownIdentifier},
@ -97,34 +97,42 @@ fn print_function(
#[expect(clippy::option_if_let_else)]
fn get_identifier_part(
ident: &Identifier,
_transpiler: &mut Transpiler,
transpiler: &mut Transpiler,
scope: &Arc<Scope>,
) -> TranspileResult<(bool, Vec<Command>, JsonValue)> {
) -> TranspileResult<(bool, Option<Command>, JsonValue)> {
if let Some(var) = scope.get_variable(ident.span.str()).as_deref() {
match var {
VariableData::MacroParameter { macro_name, .. } => Ok((
true,
Vec::new(),
None,
json!({"text": format!("$({macro_name})"), "color": PARAM_COLOR}),
)),
VariableData::ScoreboardValue { objective, target } => Ok((
false,
Vec::new(),
get_data_location(&DataLocation::ScoreboardValue {
objective: objective.to_string(),
target: target.to_string(),
}),
)),
VariableData::BooleanStorage { storage_name, path } => Ok((
false,
Vec::new(),
get_data_location(&DataLocation::Storage {
storage_name: storage_name.to_string(),
path: path.to_string(),
r#type: StorageType::Boolean,
}),
)),
_ => todo!("get_identifier_part"),
VariableData::ScoreboardValue { objective, target } => {
let (cmd, value) = get_data_location(
&DataLocation::ScoreboardValue {
objective: objective.to_string(),
target: target.to_string(),
},
transpiler,
);
Ok((false, cmd, value))
}
VariableData::BooleanStorage { storage_name, path } => {
let (cmd, value) = get_data_location(
&DataLocation::Storage {
storage_name: storage_name.to_string(),
path: path.to_string(),
r#type: StorageType::Boolean,
},
transpiler,
);
Ok((false, cmd, value))
}
_ => Err(TranspileError::UnexpectedExpression(UnexpectedExpression(
Expression::Primary(Primary::Identifier(ident.to_owned())),
))),
}
} else {
Err(TranspileError::UnknownIdentifier(UnknownIdentifier {
@ -133,15 +141,42 @@ fn print_function(
}
}
fn get_data_location(location: &DataLocation) -> JsonValue {
fn get_data_location(
location: &DataLocation,
transpiler: &mut Transpiler,
) -> (Option<Command>, JsonValue) {
match location {
DataLocation::ScoreboardValue { objective, target } => {
json!({"score": {"name": target, "objective": objective}, "color": PARAM_COLOR})
}
DataLocation::ScoreboardValue { objective, target } => (
None,
json!({"score": {"name": target, "objective": objective}, "color": PARAM_COLOR}),
),
DataLocation::Storage {
storage_name, path, ..
} => json!({"nbt": path, "storage": storage_name, "color": PARAM_COLOR}),
DataLocation::Tag { .. } => todo!("implement tag"),
} => (
None,
json!({"nbt": path, "storage": storage_name, "color": PARAM_COLOR}),
),
DataLocation::Tag { tag_name, entity } => {
let (temp_storage_name, temp_storage_paths) =
transpiler.get_temp_storage_locations(1);
let selector =
super::util::add_to_entity_selector(entity, &format!("tag={tag_name}"));
let cmd = Command::Execute(Execute::Store(
format!(
"success storage {temp_storage_name} {path} byte 1.0",
path = temp_storage_paths[0]
)
.into(),
Box::new(Execute::Run(Box::new(Command::Raw(format!(
"execute if entity {selector}"
))))),
));
(
Some(cmd),
json!({"nbt": temp_storage_paths[0], "storage": temp_storage_name, "color": PARAM_COLOR}),
)
}
}
}
@ -188,11 +223,10 @@ fn print_function(
))
}
Primary::Identifier(ident) => {
// TODO: get_identifier_part
let (cur_contains_macro, cmds, part) =
get_identifier_part(ident, transpiler, scope).expect("failed");
let (cur_contains_macro, cmd, part) =
get_identifier_part(ident, transpiler, scope)?;
contains_macro |= cur_contains_macro;
Ok((cmds, vec![part]))
Ok((cmd.into_iter().collect(), vec![part]))
}
Primary::Indexed(indexed) => match indexed.object().as_ref() {
Primary::Identifier(ident) => {
@ -201,13 +235,14 @@ fn print_function(
if let Some(ComptimeValue::String(index)) =
indexed.index().comptime_eval(scope, &VoidHandler)
{
Ok((
Vec::new(),
vec![get_data_location(&DataLocation::ScoreboardValue {
let (cmd, value) = get_data_location(
&DataLocation::ScoreboardValue {
objective: objective.to_string(),
target: index,
})],
))
},
transpiler,
);
Ok((cmd.into_iter().collect(), vec![value]))
} else {
todo!("allow macro string, but throw error when index is not constant string")
}
@ -221,15 +256,22 @@ fn print_function(
.ok()
.and_then(|index| targets.get(index))
{
Ok((
Vec::new(),
vec![get_data_location(&DataLocation::ScoreboardValue {
let (cmd, value) = get_data_location(
&DataLocation::ScoreboardValue {
objective: objective.to_string(),
target: target.to_string(),
})],
))
},
transpiler,
);
Ok((cmd.into_iter().collect(), vec![value]))
} else {
todo!("throw error when index is out of bounds")
Err(TranspileError::IllegalIndexing(IllegalIndexing {
reason: IllegalIndexingReason::IndexOutOfBounds {
index: usize::try_from(index).unwrap_or(usize::MAX),
length: targets.len(),
},
expression: indexed.index().span(),
}))
}
} else {
todo!("throw error when index is not constant integer")
@ -247,16 +289,23 @@ fn print_function(
.ok()
.and_then(|index| paths.get(index))
{
Ok((
Vec::new(),
vec![get_data_location(&DataLocation::Storage {
let (cmd, value) = get_data_location(
&DataLocation::Storage {
storage_name: storage_name.to_string(),
path: path.to_string(),
r#type: StorageType::Boolean,
})],
))
},
transpiler,
);
Ok((cmd.into_iter().collect(), vec![value]))
} else {
todo!("throw error when index is out of bounds")
Err(TranspileError::IllegalIndexing(IllegalIndexing {
reason: IllegalIndexingReason::IndexOutOfBounds {
index: usize::try_from(index).unwrap_or(usize::MAX),
length: paths.len(),
},
expression: indexed.index().span(),
}))
}
} else {
todo!("throw error when index is not constant integer")

View File

@ -98,7 +98,7 @@ impl Debug for FunctionData {
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
struct AnnotationValueWrapper<'a>(&'a TranspileAnnotationValue);
impl<'a> Debug for AnnotationValueWrapper<'a> {
impl Debug for AnnotationValueWrapper<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
TranspileAnnotationValue::None => None::<u8>.fmt(f),

View File

@ -574,7 +574,7 @@ impl Transpiler {
}
Parameter::Dynamic { prepare_cmds, storage_name, path } => {
acc_setup.extend(prepare_cmds);
acc_move.push(Command::Raw(format!(r#"data modify storage shulkerscript:function_arguments {arg_name} set from storage {storage_name} {path}"#)));
acc_move.push(Command::Raw(format!(r"data modify storage shulkerscript:function_arguments {arg_name} set from storage {storage_name} {path}")));
}
}
(acc_setup, acc_move, statics)},
@ -603,7 +603,7 @@ impl Transpiler {
));
let statics_cmd = match joined_statics {
MacroString::String(s) => Command::Raw(format!(
r#"data merge storage shulkerscript:function_arguments {{{s}}}"#
r"data merge storage shulkerscript:function_arguments {{{s}}}"
)),
MacroString::MacroString(_) => Command::UsesMacro(
super::util::join_macro_strings([

View File

@ -13,7 +13,7 @@ use crate::{
pub enum MacroString {
/// A normal string
String(String),
/// A string containing macros
/// A string containing expressions
MacroString(Vec<MacroStringPart>),
}
@ -114,6 +114,21 @@ where
})
}
/// Add additional information to an entity selector
#[must_use]
pub fn add_to_entity_selector(selector: impl Into<String>, additional: &str) -> String {
let selector: String = selector.into();
if selector.starts_with('@') {
if selector.ends_with(']') {
selector[..selector.len() - 1].to_string() + "," + additional + "]"
} else {
selector + "[" + additional + "]"
}
} else {
format!("@a[name={selector},{additional}]")
}
}
impl FromStr for MacroString {
type Err = ();

View File

@ -226,10 +226,10 @@ impl<'a> Scope<'a> {
}
}
impl<'a> Debug for Scope<'a> {
impl Debug for Scope<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
struct VariableWrapper<'a>(&'a RwLock<HashMap<String, Arc<VariableData>>>);
impl<'a> Debug for VariableWrapper<'a> {
impl Debug for VariableWrapper<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = self.0.read().unwrap();
s.deref().fmt(f)
@ -489,7 +489,7 @@ impl Transpiler {
target: s,
}),
Some(ComptimeValue::MacroString(s)) => {
todo!("indexing scoreboard with macro string: {s}")
todo!("indexing scoreboard with macro string: {s:?}")
}
Some(_) => {
let err = TranspileError::IllegalIndexing(IllegalIndexing {
@ -621,7 +621,7 @@ impl Transpiler {
entity: s,
}),
Some(ComptimeValue::MacroString(s)) => {
todo!("indexing tag with macro string: {s}")
todo!("indexing tag with macro string: {s:?}")
}
Some(_) => {
let err = TranspileError::IllegalIndexing(IllegalIndexing {