first version of print internal function for easier displaying variable values
This commit is contained in:
parent
237207a447
commit
0fd9dc432e
|
@ -18,8 +18,8 @@ license = "MIT OR Apache-2.0"
|
||||||
default = ["fs_access", "lua", "shulkerbox", "zip"]
|
default = ["fs_access", "lua", "shulkerbox", "zip"]
|
||||||
fs_access = ["shulkerbox?/fs_access"]
|
fs_access = ["shulkerbox?/fs_access"]
|
||||||
lua = ["dep:mlua"]
|
lua = ["dep:mlua"]
|
||||||
serde = ["dep:serde", "dep:flexbuffers", "shulkerbox?/serde"]
|
serde = ["dep:serde", "dep:serde_json", "shulkerbox?/serde"]
|
||||||
shulkerbox = ["dep:shulkerbox", "dep:chksum-md5"]
|
shulkerbox = ["dep:shulkerbox", "dep:chksum-md5", "dep:serde_json"]
|
||||||
zip = ["shulkerbox?/zip"]
|
zip = ["shulkerbox?/zip"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -28,12 +28,12 @@ chksum-md5 = { version = "0.1.0", optional = true }
|
||||||
colored = "3.0.0"
|
colored = "3.0.0"
|
||||||
derive_more = { version = "2.0.1", default-features = false, features = ["deref", "deref_mut", "from"] }
|
derive_more = { version = "2.0.1", default-features = false, features = ["deref", "deref_mut", "from"] }
|
||||||
enum-as-inner = "0.6.0"
|
enum-as-inner = "0.6.0"
|
||||||
flexbuffers = { version = "25.2.10", optional = true }
|
|
||||||
getset = "0.1.2"
|
getset = "0.1.2"
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
mlua = { version = "0.10.2", features = ["lua54", "vendored"], optional = true }
|
mlua = { version = "0.10.2", features = ["lua54", "vendored"], optional = true }
|
||||||
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 }
|
||||||
|
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 = "e9f2b9b91d72322ec2e063ce7b83415071306468", default-features = false, optional = true }
|
||||||
strsim = "0.11.1"
|
strsim = "0.11.1"
|
||||||
|
|
|
@ -33,6 +33,7 @@ pub enum Error {
|
||||||
IncompatibleFunctionAnnotation(#[from] IncompatibleFunctionAnnotation),
|
IncompatibleFunctionAnnotation(#[from] IncompatibleFunctionAnnotation),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: remove duplicate error (also in transpile)
|
||||||
/// An error that occurs when a function declaration is missing.
|
/// An error that occurs when a function declaration is missing.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Getters)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Getters)]
|
||||||
pub struct MissingFunctionDeclaration {
|
pub struct MissingFunctionDeclaration {
|
||||||
|
@ -43,6 +44,7 @@ pub struct MissingFunctionDeclaration {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MissingFunctionDeclaration {
|
impl MissingFunctionDeclaration {
|
||||||
|
#[expect(dead_code)]
|
||||||
pub(super) fn from_context(identifier_span: Span, functions: &HashSet<String>) -> Self {
|
pub(super) fn from_context(identifier_span: Span, functions: &HashSet<String>) -> Self {
|
||||||
let own_name = identifier_span.str();
|
let own_name = identifier_span.str();
|
||||||
let alternatives = functions
|
let alternatives = functions
|
||||||
|
|
|
@ -4,14 +4,11 @@
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use error::{
|
use error::{IncompatibleFunctionAnnotation, InvalidNamespaceName};
|
||||||
IncompatibleFunctionAnnotation, InvalidNamespaceName, MissingFunctionDeclaration,
|
|
||||||
UnresolvedMacroUsage,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{self, source_file::SourceElement as _, Handler},
|
base::{self, source_file::SourceElement as _, Handler},
|
||||||
lexical::token::{MacroStringLiteral, MacroStringLiteralPart},
|
lexical::token::MacroStringLiteral,
|
||||||
syntax::syntax_tree::{
|
syntax::syntax_tree::{
|
||||||
declaration::{Declaration, Function, ImportItems},
|
declaration::{Declaration, Function, ImportItems},
|
||||||
expression::{Expression, FunctionCall, Parenthesized, Primary},
|
expression::{Expression, FunctionCall, Parenthesized, Primary},
|
||||||
|
@ -387,28 +384,29 @@ impl MacroStringLiteral {
|
||||||
/// Analyzes the semantics of the macro string literal.
|
/// Analyzes the semantics of the macro string literal.
|
||||||
pub fn analyze_semantics(
|
pub fn analyze_semantics(
|
||||||
&self,
|
&self,
|
||||||
macro_names: &HashSet<String>,
|
_macro_names: &HashSet<String>,
|
||||||
handler: &impl Handler<base::Error>,
|
_handler: &impl Handler<base::Error>,
|
||||||
) -> Result<(), error::Error> {
|
) -> Result<(), error::Error> {
|
||||||
let mut errors = Vec::new();
|
// let mut errors = Vec::new();
|
||||||
for part in self.parts() {
|
// TODO: allow macro string literals to also contain other variables
|
||||||
if let MacroStringLiteralPart::MacroUsage { identifier, .. } = part {
|
// for part in self.parts() {
|
||||||
if !macro_names.contains(identifier.span.str()) {
|
// if let MacroStringLiteralPart::MacroUsage { identifier, .. } = part {
|
||||||
let err = error::Error::UnresolvedMacroUsage(UnresolvedMacroUsage {
|
// if !macro_names.contains(identifier.span.str()) {
|
||||||
span: identifier.span(),
|
// let err = error::Error::UnresolvedMacroUsage(UnresolvedMacroUsage {
|
||||||
});
|
// span: identifier.span(),
|
||||||
handler.receive(err.clone());
|
// });
|
||||||
errors.push(err);
|
// handler.receive(err.clone());
|
||||||
}
|
// errors.push(err);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
#[expect(clippy::option_if_let_else)]
|
// #[expect(clippy::option_if_let_else)]
|
||||||
if let Some(err) = errors.first() {
|
// if let Some(err) = errors.first() {
|
||||||
Err(err.clone())
|
// Err(err.clone())
|
||||||
} else {
|
// } else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,13 +460,14 @@ impl FunctionCall {
|
||||||
) -> Result<(), error::Error> {
|
) -> Result<(), error::Error> {
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
if !function_names.contains(self.identifier().span.str()) {
|
// TODO: also check for internal functions
|
||||||
let err = error::Error::MissingFunctionDeclaration(
|
// if !function_names.contains(self.identifier().span.str()) {
|
||||||
MissingFunctionDeclaration::from_context(self.identifier().span(), function_names),
|
// let err = error::Error::MissingFunctionDeclaration(
|
||||||
);
|
// MissingFunctionDeclaration::from_context(self.identifier().span(), function_names),
|
||||||
handler.receive(err.clone());
|
// );
|
||||||
errors.push(err);
|
// handler.receive(err.clone());
|
||||||
}
|
// errors.push(err);
|
||||||
|
// }
|
||||||
|
|
||||||
for expression in self
|
for expression in self
|
||||||
.arguments()
|
.arguments()
|
||||||
|
|
|
@ -42,7 +42,7 @@ where
|
||||||
// hold guard so no other can serialize at the same time in same thread
|
// hold guard so no other can serialize at the same time in same thread
|
||||||
let s = DEDUPLICATE_SOURCE_FILES.with(|d| {
|
let s = DEDUPLICATE_SOURCE_FILES.with(|d| {
|
||||||
let guard = d.read().unwrap();
|
let guard = d.read().unwrap();
|
||||||
let mut serialized_data = flexbuffers::FlexbufferSerializer::new();
|
let mut serialized_data = serde_json::Serializer::new(Vec::new());
|
||||||
self.0
|
self.0
|
||||||
.serialize(&mut serialized_data)
|
.serialize(&mut serialized_data)
|
||||||
.map_err(|_| serde::ser::Error::custom("could not buffer serialization"))?;
|
.map_err(|_| serde::ser::Error::custom("could not buffer serialization"))?;
|
||||||
|
|
|
@ -545,6 +545,20 @@ impl<'a> Parser<'a> {
|
||||||
arguments: token_tree.list,
|
arguments: token_tree.list,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
Reading::IntoDelimited(punc) if punc.punctuation == '[' => {
|
||||||
|
let token_tree = self.step_into(
|
||||||
|
Delimiter::Bracket,
|
||||||
|
|p| p.parse_expression(handler),
|
||||||
|
handler,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Primary::Indexed(Indexed {
|
||||||
|
object: Box::new(Primary::Identifier(identifier)),
|
||||||
|
left_bracket: token_tree.open,
|
||||||
|
index: Box::new(token_tree.tree?),
|
||||||
|
right_bracket: token_tree.close,
|
||||||
|
}))
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// regular identifier
|
// regular identifier
|
||||||
Ok(Primary::Identifier(identifier))
|
Ok(Primary::Identifier(identifier))
|
||||||
|
|
|
@ -0,0 +1,317 @@
|
||||||
|
//! Functions provided by the language itself.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
ops::{Bound, Deref, RangeBounds},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use shulkerbox::prelude::Command;
|
||||||
|
|
||||||
|
use serde_json::{json, Value as JsonValue};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
base::{source_file::SourceElement as _, VoidHandler},
|
||||||
|
lexical::token::{Identifier, MacroStringLiteralPart},
|
||||||
|
semantic::error::InvalidFunctionArguments,
|
||||||
|
syntax::syntax_tree::expression::{Expression, FunctionCall, Primary},
|
||||||
|
transpile::{
|
||||||
|
error::{IllegalIndexing, IllegalIndexingReason, LuaRuntimeError, UnknownIdentifier},
|
||||||
|
expression::{ComptimeValue, DataLocation, StorageType},
|
||||||
|
util::MacroString,
|
||||||
|
TranspileError,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{Scope, TranspileResult, Transpiler, VariableData};
|
||||||
|
|
||||||
|
/// A function that can be called from the language.
|
||||||
|
pub type InternalFunction =
|
||||||
|
fn(&mut Transpiler, &Arc<Scope>, &FunctionCall) -> TranspileResult<Vec<Command>>;
|
||||||
|
|
||||||
|
/// Adds all internal functions to the scope.
|
||||||
|
pub fn add_all_to_scope(scope: &Arc<Scope>) {
|
||||||
|
scope.set_variable(
|
||||||
|
"print",
|
||||||
|
VariableData::InternalFunction {
|
||||||
|
implementation: print_function,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_args_assert_in_range(
|
||||||
|
call: &FunctionCall,
|
||||||
|
range: impl RangeBounds<usize>,
|
||||||
|
) -> TranspileResult<Vec<&Expression>> {
|
||||||
|
let args = call
|
||||||
|
.arguments()
|
||||||
|
.as_ref()
|
||||||
|
.map(|args| args.elements().map(Deref::deref).collect::<Vec<_>>())
|
||||||
|
.unwrap_or_default();
|
||||||
|
if range.contains(&args.len()) {
|
||||||
|
Ok(args)
|
||||||
|
} else {
|
||||||
|
let span = args
|
||||||
|
.first()
|
||||||
|
.and_then(|first| {
|
||||||
|
args.last()
|
||||||
|
.map(|last| first.span().join(&last.span()).expect("invalid span"))
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
call.left_parenthesis()
|
||||||
|
.span()
|
||||||
|
.join(&call.right_parenthesis().span())
|
||||||
|
.expect("invalid span")
|
||||||
|
});
|
||||||
|
|
||||||
|
let actual = args.len();
|
||||||
|
let expected = match range.start_bound() {
|
||||||
|
Bound::Excluded(excluded) => (excluded + 1 > actual).then_some(excluded + 1),
|
||||||
|
Bound::Included(&included) => (included > actual).then_some(included),
|
||||||
|
Bound::Unbounded => None,
|
||||||
|
}
|
||||||
|
.or_else(|| match range.end_bound() {
|
||||||
|
Bound::Excluded(&excluded) => (excluded <= actual).then_some(excluded.wrapping_sub(1)),
|
||||||
|
Bound::Included(&included) => (included < actual).then_some(included),
|
||||||
|
Bound::Unbounded => None,
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
Err(TranspileError::InvalidFunctionArguments(
|
||||||
|
InvalidFunctionArguments {
|
||||||
|
expected,
|
||||||
|
actual: args.len(),
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::too_many_lines)]
|
||||||
|
fn print_function(
|
||||||
|
transpiler: &mut Transpiler,
|
||||||
|
scope: &Arc<Scope>,
|
||||||
|
call: &FunctionCall,
|
||||||
|
) -> TranspileResult<Vec<Command>> {
|
||||||
|
const PARAM_COLOR: &str = "gray";
|
||||||
|
|
||||||
|
#[expect(clippy::option_if_let_else)]
|
||||||
|
fn get_identifier_part(
|
||||||
|
ident: &Identifier,
|
||||||
|
_transpiler: &mut Transpiler,
|
||||||
|
scope: &Arc<Scope>,
|
||||||
|
) -> TranspileResult<(bool, Vec<Command>, JsonValue)> {
|
||||||
|
if let Some(var) = scope.get_variable(ident.span.str()).as_deref() {
|
||||||
|
match var {
|
||||||
|
VariableData::MacroParameter { macro_name, .. } => Ok((
|
||||||
|
true,
|
||||||
|
Vec::new(),
|
||||||
|
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"),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(TranspileError::UnknownIdentifier(UnknownIdentifier {
|
||||||
|
identifier: ident.span(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_data_location(location: &DataLocation) -> JsonValue {
|
||||||
|
match location {
|
||||||
|
DataLocation::ScoreboardValue { objective, target } => {
|
||||||
|
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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = get_args_assert_in_range(call, 1..=2)?;
|
||||||
|
let first = args.first().expect("checked range");
|
||||||
|
let (target, message_expression) = args.get(1).map_or_else(
|
||||||
|
|| ("@a".into(), first),
|
||||||
|
|second| {
|
||||||
|
(
|
||||||
|
first
|
||||||
|
.comptime_eval(scope, &VoidHandler)
|
||||||
|
.map_or_else(|| "@a".into(), |val| val.to_macro_string()),
|
||||||
|
second,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut contains_macro = matches!(target, MacroString::MacroString(_));
|
||||||
|
|
||||||
|
let (mut cmds, parts) = match message_expression {
|
||||||
|
Expression::Primary(primary) => match primary {
|
||||||
|
Primary::Boolean(boolean) => Ok((
|
||||||
|
Vec::new(),
|
||||||
|
vec![JsonValue::String(boolean.value().to_string())],
|
||||||
|
)),
|
||||||
|
Primary::Integer(integer) => Ok((
|
||||||
|
Vec::new(),
|
||||||
|
vec![JsonValue::String(integer.as_i64().to_string())],
|
||||||
|
)),
|
||||||
|
Primary::StringLiteral(string) => Ok((
|
||||||
|
Vec::new(),
|
||||||
|
vec![JsonValue::String(string.str_content().to_string())],
|
||||||
|
)),
|
||||||
|
Primary::Lua(lua) => {
|
||||||
|
let (ret, _lua) = lua.eval(scope, &VoidHandler)?;
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
vec![JsonValue::String(ret.to_string().map_err(|err| {
|
||||||
|
TranspileError::LuaRuntimeError(LuaRuntimeError::from_lua_err(
|
||||||
|
&err,
|
||||||
|
lua.span(),
|
||||||
|
))
|
||||||
|
})?)],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Primary::Identifier(ident) => {
|
||||||
|
// TODO: get_identifier_part
|
||||||
|
let (cur_contains_macro, cmds, part) =
|
||||||
|
get_identifier_part(ident, transpiler, scope).expect("failed");
|
||||||
|
contains_macro |= cur_contains_macro;
|
||||||
|
Ok((cmds, vec![part]))
|
||||||
|
}
|
||||||
|
Primary::Indexed(indexed) => match indexed.object().as_ref() {
|
||||||
|
Primary::Identifier(ident) => {
|
||||||
|
match scope.get_variable(ident.span.str()).as_deref() {
|
||||||
|
Some(VariableData::Scoreboard { objective }) => {
|
||||||
|
if let Some(ComptimeValue::String(index)) =
|
||||||
|
indexed.index().comptime_eval(scope, &VoidHandler)
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
vec![get_data_location(&DataLocation::ScoreboardValue {
|
||||||
|
objective: objective.to_string(),
|
||||||
|
target: index,
|
||||||
|
})],
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
todo!("allow macro string, but throw error when index is not constant string")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(VariableData::ScoreboardArray { objective, targets }) => {
|
||||||
|
if let Some(ComptimeValue::Integer(index)) =
|
||||||
|
indexed.index().comptime_eval(scope, &VoidHandler)
|
||||||
|
{
|
||||||
|
#[expect(clippy::option_if_let_else)]
|
||||||
|
if let Some(target) = usize::try_from(index)
|
||||||
|
.ok()
|
||||||
|
.and_then(|index| targets.get(index))
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
vec![get_data_location(&DataLocation::ScoreboardValue {
|
||||||
|
objective: objective.to_string(),
|
||||||
|
target: target.to_string(),
|
||||||
|
})],
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
todo!("throw error when index is out of bounds")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
todo!("throw error when index is not constant integer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(VariableData::BooleanStorageArray {
|
||||||
|
storage_name,
|
||||||
|
paths,
|
||||||
|
}) => {
|
||||||
|
if let Some(ComptimeValue::Integer(index)) =
|
||||||
|
indexed.index().comptime_eval(scope, &VoidHandler)
|
||||||
|
{
|
||||||
|
#[expect(clippy::option_if_let_else)]
|
||||||
|
if let Some(path) = usize::try_from(index)
|
||||||
|
.ok()
|
||||||
|
.and_then(|index| paths.get(index))
|
||||||
|
{
|
||||||
|
Ok((
|
||||||
|
Vec::new(),
|
||||||
|
vec![get_data_location(&DataLocation::Storage {
|
||||||
|
storage_name: storage_name.to_string(),
|
||||||
|
path: path.to_string(),
|
||||||
|
r#type: StorageType::Boolean,
|
||||||
|
})],
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
todo!("throw error when index is out of bounds")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
todo!("throw error when index is not constant integer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(TranspileError::IllegalIndexing(IllegalIndexing {
|
||||||
|
expression: indexed.object().span(),
|
||||||
|
reason: IllegalIndexingReason::NotIdentifier,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
Primary::MacroStringLiteral(macro_string) => {
|
||||||
|
let mut cmds = Vec::new();
|
||||||
|
let mut parts = Vec::new();
|
||||||
|
for part in macro_string.parts() {
|
||||||
|
match part {
|
||||||
|
MacroStringLiteralPart::Text(text) => {
|
||||||
|
parts.push(JsonValue::String(text.str().to_string()));
|
||||||
|
}
|
||||||
|
MacroStringLiteralPart::MacroUsage { identifier, .. } => {
|
||||||
|
let (cur_contains_macro, cur_cmds, part) =
|
||||||
|
get_identifier_part(identifier, transpiler, scope)?;
|
||||||
|
contains_macro |= cur_contains_macro;
|
||||||
|
cmds.extend(cur_cmds);
|
||||||
|
parts.push(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok((cmds, parts))
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => todo!("print_function Primary"),
|
||||||
|
},
|
||||||
|
Expression::Binary(_) => todo!("print_function Binary"),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
// TODO: prepend prefix with datapack name to parts and remove following
|
||||||
|
let print_args = if parts.len() == 1 {
|
||||||
|
serde_json::to_string(&parts[0]).expect("json serialization failed")
|
||||||
|
} else {
|
||||||
|
serde_json::to_string(&parts).expect("json serialization failed")
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: throw correct error
|
||||||
|
let cmd = format!("tellraw {target} {print_args}");
|
||||||
|
|
||||||
|
let cmd = if contains_macro {
|
||||||
|
Command::UsesMacro(cmd.parse::<MacroString>().expect("cannot fail").into())
|
||||||
|
} else {
|
||||||
|
Command::Raw(cmd)
|
||||||
|
};
|
||||||
|
|
||||||
|
cmds.push(cmd);
|
||||||
|
|
||||||
|
Ok(cmds)
|
||||||
|
}
|
|
@ -262,8 +262,8 @@ mod enabled {
|
||||||
.map_err(|err| LuaRuntimeError::from_lua_err(&err, self.span()))?;
|
.map_err(|err| LuaRuntimeError::from_lua_err(&err, self.span()))?;
|
||||||
Value::Table(table)
|
Value::Table(table)
|
||||||
}
|
}
|
||||||
Some(VariableData::Function { .. }) => {
|
Some(VariableData::Function { .. } | VariableData::InternalFunction { .. }) => {
|
||||||
todo!("functions are not supported yet");
|
todo!("(internal) functions are not supported yet");
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
return Err(TranspileError::UnknownIdentifier(UnknownIdentifier {
|
return Err(TranspileError::UnknownIdentifier(UnknownIdentifier {
|
||||||
|
|
|
@ -29,6 +29,9 @@ use strum::EnumIs;
|
||||||
#[cfg_attr(feature = "shulkerbox", doc(inline))]
|
#[cfg_attr(feature = "shulkerbox", doc(inline))]
|
||||||
pub use transpiler::Transpiler;
|
pub use transpiler::Transpiler;
|
||||||
|
|
||||||
|
#[cfg(feature = "shulkerbox")]
|
||||||
|
pub mod internal_functions;
|
||||||
|
|
||||||
mod variables;
|
mod variables;
|
||||||
pub use variables::{Scope, VariableData};
|
pub use variables::{Scope, VariableData};
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ impl Transpiler {
|
||||||
let scope = self
|
let scope = self
|
||||||
.scopes
|
.scopes
|
||||||
.entry(program_identifier)
|
.entry(program_identifier)
|
||||||
.or_default()
|
.or_insert_with(Scope::with_internal_functions)
|
||||||
.to_owned();
|
.to_owned();
|
||||||
self.transpile_program_declarations(program, &scope, handler);
|
self.transpile_program_declarations(program, &scope, handler);
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ impl Transpiler {
|
||||||
let scope = self
|
let scope = self
|
||||||
.scopes
|
.scopes
|
||||||
.entry(identifier_span.source_file().identifier().to_owned())
|
.entry(identifier_span.source_file().identifier().to_owned())
|
||||||
.or_default()
|
.or_insert_with(Scope::with_internal_functions)
|
||||||
.to_owned();
|
.to_owned();
|
||||||
self.get_or_transpile_function(&identifier_span, None, &scope, handler)?;
|
self.get_or_transpile_function(&identifier_span, None, &scope, handler)?;
|
||||||
}
|
}
|
||||||
|
@ -831,60 +831,68 @@ impl Transpiler {
|
||||||
.arguments()
|
.arguments()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|l| l.elements().map(Deref::deref).collect::<Vec<_>>());
|
.map(|l| l.elements().map(Deref::deref).collect::<Vec<_>>());
|
||||||
let (location, arguments) = self.get_or_transpile_function(
|
if let Some(VariableData::InternalFunction { implementation }) =
|
||||||
&func.identifier().span,
|
scope.get_variable(func.identifier().span.str()).as_deref()
|
||||||
arguments.as_deref(),
|
{
|
||||||
scope,
|
implementation(self, scope, func).inspect_err(|err| {
|
||||||
handler,
|
handler.receive(err.clone());
|
||||||
)?;
|
})
|
||||||
let mut function_call = format!("function {location}");
|
} else {
|
||||||
match arguments {
|
let (location, arguments) = self.get_or_transpile_function(
|
||||||
TranspiledFunctionArguments::Static(arguments) => {
|
&func.identifier().span,
|
||||||
use std::fmt::Write;
|
arguments.as_deref(),
|
||||||
let arguments_iter = arguments.iter().map(|(ident, v)| match v {
|
scope,
|
||||||
MacroString::String(s) => MacroString::String(format!(
|
handler,
|
||||||
r#"{macro_name}:"{escaped}""#,
|
)?;
|
||||||
macro_name = crate::util::identifier_to_macro(ident),
|
let mut function_call = format!("function {location}");
|
||||||
escaped = crate::util::escape_str(s)
|
match arguments {
|
||||||
)),
|
TranspiledFunctionArguments::Static(arguments) => {
|
||||||
MacroString::MacroString(parts) => MacroString::MacroString(
|
use std::fmt::Write;
|
||||||
std::iter::once(MacroStringPart::String(format!(
|
let arguments_iter = arguments.iter().map(|(ident, v)| match v {
|
||||||
r#"{macro_name}:""#,
|
MacroString::String(s) => MacroString::String(format!(
|
||||||
macro_name = crate::util::identifier_to_macro(ident)
|
r#"{macro_name}:"{escaped}""#,
|
||||||
)))
|
macro_name = crate::util::identifier_to_macro(ident),
|
||||||
.chain(parts.clone().into_iter().map(|part| match part {
|
escaped = crate::util::escape_str(s)
|
||||||
MacroStringPart::String(s) => {
|
)),
|
||||||
MacroStringPart::String(crate::util::escape_str(&s).to_string())
|
MacroString::MacroString(parts) => MacroString::MacroString(
|
||||||
}
|
std::iter::once(MacroStringPart::String(format!(
|
||||||
macro_usage @ MacroStringPart::MacroUsage(_) => macro_usage,
|
r#"{macro_name}:""#,
|
||||||
}))
|
macro_name = crate::util::identifier_to_macro(ident)
|
||||||
.chain(std::iter::once(MacroStringPart::String('"'.to_string())))
|
)))
|
||||||
.collect(),
|
.chain(parts.clone().into_iter().map(|part| match part {
|
||||||
),
|
MacroStringPart::String(s) => {
|
||||||
});
|
MacroStringPart::String(crate::util::escape_str(&s).to_string())
|
||||||
let arguments = super::util::join_macro_strings(arguments_iter);
|
}
|
||||||
|
macro_usage @ MacroStringPart::MacroUsage(_) => macro_usage,
|
||||||
|
}))
|
||||||
|
.chain(std::iter::once(MacroStringPart::String('"'.to_string())))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
let arguments = super::util::join_macro_strings(arguments_iter);
|
||||||
|
|
||||||
let cmd = match arguments {
|
let cmd = match arguments {
|
||||||
MacroString::String(arguments) => {
|
MacroString::String(arguments) => {
|
||||||
write!(function_call, " {{{arguments}}}").unwrap();
|
write!(function_call, " {{{arguments}}}").unwrap();
|
||||||
Command::Raw(function_call)
|
Command::Raw(function_call)
|
||||||
}
|
}
|
||||||
MacroString::MacroString(mut parts) => {
|
MacroString::MacroString(mut parts) => {
|
||||||
function_call.push_str(" {");
|
function_call.push_str(" {");
|
||||||
parts.insert(0, MacroStringPart::String(function_call));
|
parts.insert(0, MacroStringPart::String(function_call));
|
||||||
parts.push(MacroStringPart::String('}'.to_string()));
|
parts.push(MacroStringPart::String('}'.to_string()));
|
||||||
Command::UsesMacro(MacroString::MacroString(parts).into())
|
Command::UsesMacro(MacroString::MacroString(parts).into())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(vec![cmd])
|
Ok(vec![cmd])
|
||||||
|
}
|
||||||
|
TranspiledFunctionArguments::Dynamic(mut cmds) => {
|
||||||
|
function_call.push_str(" with storage shulkerscript:function_arguments");
|
||||||
|
cmds.push(Command::Raw(function_call));
|
||||||
|
Ok(cmds)
|
||||||
|
}
|
||||||
|
TranspiledFunctionArguments::None => Ok(vec![Command::Raw(function_call)]),
|
||||||
}
|
}
|
||||||
TranspiledFunctionArguments::Dynamic(mut cmds) => {
|
|
||||||
function_call.push_str(" with storage shulkerscript:function_arguments");
|
|
||||||
cmds.push(Command::Raw(function_call));
|
|
||||||
Ok(cmds)
|
|
||||||
}
|
|
||||||
TranspiledFunctionArguments::None => Ok(vec![Command::Raw(function_call)]),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ use super::{
|
||||||
MismatchedTypes,
|
MismatchedTypes,
|
||||||
},
|
},
|
||||||
expression::{ComptimeValue, DataLocation, ExpectedType, StorageType},
|
expression::{ComptimeValue, DataLocation, ExpectedType, StorageType},
|
||||||
|
internal_functions::InternalFunction,
|
||||||
FunctionData, TranspileAnnotationValue, TranspileError, TranspileResult,
|
FunctionData, TranspileAnnotationValue, TranspileError, TranspileResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -94,6 +95,11 @@ pub enum VariableData {
|
||||||
/// The paths to the booleans.
|
/// The paths to the booleans.
|
||||||
paths: Vec<String>,
|
paths: Vec<String>,
|
||||||
},
|
},
|
||||||
|
/// Compiler internal function.
|
||||||
|
InternalFunction {
|
||||||
|
/// The implementation
|
||||||
|
implementation: InternalFunction,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, EnumAsInner)]
|
#[derive(Debug, Clone, Copy, EnumAsInner)]
|
||||||
|
@ -133,6 +139,19 @@ impl<'a> Scope<'a> {
|
||||||
Arc::new(Self::default())
|
Arc::new(Self::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new scope with internal functions.
|
||||||
|
#[cfg(feature = "shulkerbox")]
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_internal_functions() -> Arc<Self> {
|
||||||
|
use super::internal_functions;
|
||||||
|
|
||||||
|
let scope = Self::new();
|
||||||
|
|
||||||
|
internal_functions::add_all_to_scope(&scope);
|
||||||
|
|
||||||
|
scope
|
||||||
|
}
|
||||||
|
|
||||||
/// 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: &'a Arc<Self>) -> Arc<Self> {
|
||||||
|
@ -624,15 +643,18 @@ impl Transpiler {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
VariableData::Function { .. } | VariableData::MacroParameter { .. } => {
|
VariableData::Function { .. }
|
||||||
|
| VariableData::MacroParameter { .. }
|
||||||
|
| VariableData::InternalFunction { .. } => {
|
||||||
let err = TranspileError::AssignmentError(AssignmentError {
|
let err = TranspileError::AssignmentError(AssignmentError {
|
||||||
identifier: identifier.span(),
|
identifier: identifier.span(),
|
||||||
message: format!(
|
message: format!(
|
||||||
"Cannot assign to a {}.",
|
"Cannot assign to a {}.",
|
||||||
if matches!(target.as_ref(), VariableData::Function { .. }) {
|
match target.as_ref() {
|
||||||
"function"
|
VariableData::Function { .. } => "function",
|
||||||
} else {
|
VariableData::MacroParameter { .. } => "macro parameter",
|
||||||
"function argument"
|
VariableData::InternalFunction { .. } => "internal function",
|
||||||
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue