Compare commits
4 Commits
f3b3d5d3b6
...
469b8d3875
Author | SHA1 | Date |
---|---|---|
|
469b8d3875 | |
|
ab76b1d43e | |
|
f808fef3f1 | |
|
32d453ebef |
|
@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- Change the syntax to set the type of a tag from `tag "[name]" of "[type]"` to `tag<"[type]"> "[name]"`
|
||||||
|
- Remove the keyword `of`
|
||||||
- Option to deduplicate source files during serialization when using `SerdeWrapper`
|
- Option to deduplicate source files during serialization when using `SerdeWrapper`
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
|
@ -48,11 +48,11 @@ pub enum KeywordKind {
|
||||||
From,
|
From,
|
||||||
Import,
|
Import,
|
||||||
Tag,
|
Tag,
|
||||||
Of,
|
|
||||||
Replace,
|
Replace,
|
||||||
Int,
|
Int,
|
||||||
Bool,
|
Bool,
|
||||||
Macro,
|
Macro,
|
||||||
|
Val,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for KeywordKind {
|
impl Display for KeywordKind {
|
||||||
|
@ -114,11 +114,11 @@ impl KeywordKind {
|
||||||
Self::From => "from",
|
Self::From => "from",
|
||||||
Self::Import => "import",
|
Self::Import => "import",
|
||||||
Self::Tag => "tag",
|
Self::Tag => "tag",
|
||||||
Self::Of => "of",
|
|
||||||
Self::Replace => "replace",
|
Self::Replace => "replace",
|
||||||
Self::Int => "int",
|
Self::Int => "int",
|
||||||
Self::Bool => "bool",
|
Self::Bool => "bool",
|
||||||
Self::Macro => "macro",
|
Self::Macro => "macro",
|
||||||
|
Self::Val => "val",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,7 @@
|
||||||
|
|
||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
use std::{collections::HashSet, fmt::Display};
|
use std::fmt::Display;
|
||||||
|
|
||||||
use getset::Getters;
|
|
||||||
use itertools::Itertools as _;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{
|
base::{
|
||||||
|
@ -14,6 +11,10 @@ use crate::{
|
||||||
},
|
},
|
||||||
lexical::token::StringLiteral,
|
lexical::token::StringLiteral,
|
||||||
syntax::syntax_tree::expression::Expression,
|
syntax::syntax_tree::expression::Expression,
|
||||||
|
transpile::error::{
|
||||||
|
AssignmentError, IllegalIndexing, MismatchedTypes, MissingFunctionDeclaration,
|
||||||
|
UnknownIdentifier,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)]
|
||||||
|
@ -31,77 +32,20 @@ pub enum Error {
|
||||||
UnresolvedMacroUsage(#[from] UnresolvedMacroUsage),
|
UnresolvedMacroUsage(#[from] UnresolvedMacroUsage),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
IncompatibleFunctionAnnotation(#[from] IncompatibleFunctionAnnotation),
|
IncompatibleFunctionAnnotation(#[from] IncompatibleFunctionAnnotation),
|
||||||
|
#[error(transparent)]
|
||||||
|
IllegalIndexing(#[from] IllegalIndexing),
|
||||||
|
#[error(transparent)]
|
||||||
|
MismatchedTypes(#[from] MismatchedTypes),
|
||||||
|
#[error(transparent)]
|
||||||
|
UnknownIdentifier(#[from] UnknownIdentifier),
|
||||||
|
#[error(transparent)]
|
||||||
|
AssignmentError(#[from] AssignmentError),
|
||||||
|
#[error("Lua is disabled, but a Lua function was used.")]
|
||||||
|
LuaDisabled,
|
||||||
|
#[error("Other: {0}")]
|
||||||
|
Other(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove duplicate error (also in transpile)
|
|
||||||
/// An error that occurs when a function declaration is missing.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Getters)]
|
|
||||||
pub struct MissingFunctionDeclaration {
|
|
||||||
#[get = "pub"]
|
|
||||||
span: Span,
|
|
||||||
#[get = "pub"]
|
|
||||||
alternatives: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MissingFunctionDeclaration {
|
|
||||||
#[expect(dead_code)]
|
|
||||||
pub(super) fn from_context(identifier_span: Span, functions: &HashSet<String>) -> Self {
|
|
||||||
let own_name = identifier_span.str();
|
|
||||||
let alternatives = functions
|
|
||||||
.iter()
|
|
||||||
.filter_map(|function_name| {
|
|
||||||
let normalized_distance =
|
|
||||||
strsim::normalized_damerau_levenshtein(own_name, function_name);
|
|
||||||
(normalized_distance > 0.8
|
|
||||||
|| strsim::damerau_levenshtein(own_name, function_name) < 3)
|
|
||||||
.then_some((normalized_distance, function_name))
|
|
||||||
})
|
|
||||||
.sorted_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal))
|
|
||||||
.map(|(_, data)| data)
|
|
||||||
.take(8)
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
alternatives,
|
|
||||||
span: identifier_span,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for MissingFunctionDeclaration {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
use std::fmt::Write;
|
|
||||||
|
|
||||||
let message = format!(
|
|
||||||
"no matching function declaration found for invocation of function `{}`",
|
|
||||||
self.span.str()
|
|
||||||
);
|
|
||||||
write!(f, "{}", Message::new(Severity::Error, message))?;
|
|
||||||
|
|
||||||
let help_message = if self.alternatives.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let mut message = String::from("did you mean ");
|
|
||||||
for (i, alternative) in self.alternatives.iter().enumerate() {
|
|
||||||
if i > 0 {
|
|
||||||
message.push_str(", ");
|
|
||||||
}
|
|
||||||
write!(message, "`{alternative}`")?;
|
|
||||||
}
|
|
||||||
Some(message + "?")
|
|
||||||
};
|
|
||||||
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"\n{}",
|
|
||||||
SourceCodeDisplay::new(&self.span, help_message.as_ref())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for MissingFunctionDeclaration {}
|
|
||||||
|
|
||||||
/// 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)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct UnexpectedExpression(pub Expression);
|
pub struct UnexpectedExpression(pub Expression);
|
||||||
|
|
1072
src/semantic/mod.rs
1072
src/semantic/mod.rs
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,97 @@
|
||||||
|
use std::{collections::HashMap, sync::RwLock};
|
||||||
|
|
||||||
|
/// Type of variable
|
||||||
|
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
|
||||||
|
pub enum VariableType {
|
||||||
|
/// A function.
|
||||||
|
Function,
|
||||||
|
/// A macro function parameter.
|
||||||
|
MacroParameter,
|
||||||
|
/// A scoreboard.
|
||||||
|
Scoreboard,
|
||||||
|
/// A scoreboard value.
|
||||||
|
ScoreboardValue,
|
||||||
|
/// Multiple values stored in scoreboard.
|
||||||
|
ScoreboardArray,
|
||||||
|
/// A tag applied to entities.
|
||||||
|
Tag,
|
||||||
|
/// A boolean stored in a data storage.
|
||||||
|
BooleanStorage,
|
||||||
|
/// Multiple booleans stored in a data storage array.
|
||||||
|
BooleanStorageArray,
|
||||||
|
/// Compiler internal function.
|
||||||
|
InternalFunction,
|
||||||
|
/// Compiler internal value.
|
||||||
|
ComptimeValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A scope that stores variables.
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct SemanticScope<'a> {
|
||||||
|
/// Parent scope where variables are inherited from.
|
||||||
|
parent: Option<&'a Self>,
|
||||||
|
/// Variables stored in the scope.
|
||||||
|
variables: RwLock<HashMap<String, VariableType>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SemanticScope<'a> {
|
||||||
|
/// Creates a new scope.
|
||||||
|
#[must_use]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let scope = Self::default();
|
||||||
|
|
||||||
|
scope.set_variable("print", VariableType::InternalFunction);
|
||||||
|
|
||||||
|
scope
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new scope with a parent.
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_parent(parent: &'a Self) -> Self {
|
||||||
|
Self {
|
||||||
|
parent: Some(parent),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a variable from the scope.
|
||||||
|
pub fn get_variable(&self, name: &str) -> Option<VariableType> {
|
||||||
|
let var = self.variables.read().unwrap().get(name).copied();
|
||||||
|
if var.is_some() {
|
||||||
|
var
|
||||||
|
} else {
|
||||||
|
self.parent
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|parent| parent.get_variable(name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a variable in the scope.
|
||||||
|
pub fn set_variable(&self, name: &str, var: VariableType) {
|
||||||
|
self.variables
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.insert(name.to_string(), var);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the variables stored in the current scope.
|
||||||
|
pub fn get_local_variables(&self) -> &RwLock<HashMap<String, VariableType>> {
|
||||||
|
&self.variables
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets all variables stored in the scope.
|
||||||
|
///
|
||||||
|
/// This function does not return a reference to the variables, but clones them.
|
||||||
|
pub fn get_all_variables(&self) -> HashMap<String, VariableType> {
|
||||||
|
let mut variables = self.variables.read().unwrap().clone();
|
||||||
|
if let Some(parent) = self.parent.as_ref() {
|
||||||
|
variables.extend(parent.get_all_variables());
|
||||||
|
}
|
||||||
|
variables
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the parent scope.
|
||||||
|
pub fn get_parent(&self) -> Option<&Self> {
|
||||||
|
self.parent
|
||||||
|
}
|
||||||
|
}
|
|
@ -253,7 +253,7 @@ impl SourceElement for Import {
|
||||||
///
|
///
|
||||||
/// ``` ebnf
|
/// ``` ebnf
|
||||||
/// TagDeclaration:
|
/// TagDeclaration:
|
||||||
/// 'tag' StringLiteral ('of' StringLiteral)? 'replace'? '[' (StringLiteral (',' StringLiteral)*)? ']'
|
/// 'tag' ('<' StringLiteral '>')? StringLiteral 'replace'? '[' (StringLiteral (',' StringLiteral)*)? ']'
|
||||||
/// ;
|
/// ;
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
@ -262,9 +262,9 @@ pub struct Tag {
|
||||||
#[get = "pub"]
|
#[get = "pub"]
|
||||||
tag_keyword: Keyword,
|
tag_keyword: Keyword,
|
||||||
#[get = "pub"]
|
#[get = "pub"]
|
||||||
name: StringLiteral,
|
of_type: Option<(Punctuation, StringLiteral, Punctuation)>,
|
||||||
#[get = "pub"]
|
#[get = "pub"]
|
||||||
of_type: Option<(Keyword, StringLiteral)>,
|
name: StringLiteral,
|
||||||
#[get = "pub"]
|
#[get = "pub"]
|
||||||
replace: Option<Keyword>,
|
replace: Option<Keyword>,
|
||||||
#[get = "pub"]
|
#[get = "pub"]
|
||||||
|
@ -278,15 +278,15 @@ impl Tag {
|
||||||
self,
|
self,
|
||||||
) -> (
|
) -> (
|
||||||
Keyword,
|
Keyword,
|
||||||
|
Option<(Punctuation, StringLiteral, Punctuation)>,
|
||||||
StringLiteral,
|
StringLiteral,
|
||||||
Option<(Keyword, StringLiteral)>,
|
|
||||||
Option<Keyword>,
|
Option<Keyword>,
|
||||||
DelimitedList<StringLiteral>,
|
DelimitedList<StringLiteral>,
|
||||||
) {
|
) {
|
||||||
(
|
(
|
||||||
self.tag_keyword,
|
self.tag_keyword,
|
||||||
self.name,
|
|
||||||
self.of_type,
|
self.of_type,
|
||||||
|
self.name,
|
||||||
self.replace,
|
self.replace,
|
||||||
self.entries,
|
self.entries,
|
||||||
)
|
)
|
||||||
|
@ -299,7 +299,7 @@ impl Tag {
|
||||||
|
|
||||||
self.of_type
|
self.of_type
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(TagType::Function, |(_, tag_type)| {
|
.map_or(TagType::Function, |(_, tag_type, _)| {
|
||||||
match tag_type.str_content().as_ref() {
|
match tag_type.str_content().as_ref() {
|
||||||
"function" => TagType::Function,
|
"function" => TagType::Function,
|
||||||
"block" => TagType::Block,
|
"block" => TagType::Block,
|
||||||
|
@ -419,18 +419,24 @@ impl Parser<'_> {
|
||||||
// eat the tag keyword
|
// eat the tag keyword
|
||||||
self.forward();
|
self.forward();
|
||||||
|
|
||||||
|
let of_type = match self.stop_at_significant() {
|
||||||
|
Reading::Atomic(Token::Punctuation(punc)) if punc.punctuation == '<' => {
|
||||||
|
// eat the open bracket
|
||||||
|
self.forward();
|
||||||
|
|
||||||
|
let of_type = self.parse_string_literal(handler)?;
|
||||||
|
|
||||||
|
// eat the close bracket
|
||||||
|
let closing = self.parse_punctuation('>', true, handler)?;
|
||||||
|
|
||||||
|
Some((punc, of_type, closing))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
// parse the name
|
// parse the name
|
||||||
let name = self.parse_string_literal(handler)?;
|
let name = self.parse_string_literal(handler)?;
|
||||||
|
|
||||||
let of_type = self
|
|
||||||
.try_parse(|parser| {
|
|
||||||
let of_keyword = parser.parse_keyword(KeywordKind::Of, &VoidHandler)?;
|
|
||||||
let of_type = parser.parse_string_literal(handler)?;
|
|
||||||
|
|
||||||
Ok((of_keyword, of_type))
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
let replace = self
|
let replace = self
|
||||||
.try_parse(|parser| parser.parse_keyword(KeywordKind::Replace, &VoidHandler))
|
.try_parse(|parser| parser.parse_keyword(KeywordKind::Replace, &VoidHandler))
|
||||||
.ok();
|
.ok();
|
||||||
|
@ -444,8 +450,8 @@ impl Parser<'_> {
|
||||||
|
|
||||||
Ok(Declaration::Tag(Tag {
|
Ok(Declaration::Tag(Tag {
|
||||||
tag_keyword,
|
tag_keyword,
|
||||||
name,
|
|
||||||
of_type,
|
of_type,
|
||||||
|
name,
|
||||||
replace,
|
replace,
|
||||||
entries,
|
entries,
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -247,6 +247,14 @@ impl SourceElement for Parenthesized {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for Parenthesized {
|
||||||
|
type Target = Expression;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.expression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents a indexed expression in the syntax tree.
|
/// Represents a indexed expression in the syntax tree.
|
||||||
///
|
///
|
||||||
/// Syntax Synopsis:
|
/// Syntax Synopsis:
|
||||||
|
|
|
@ -325,6 +325,7 @@ pub enum VariableDeclaration {
|
||||||
Array(ArrayVariableDeclaration),
|
Array(ArrayVariableDeclaration),
|
||||||
Score(ScoreVariableDeclaration),
|
Score(ScoreVariableDeclaration),
|
||||||
Tag(TagVariableDeclaration),
|
Tag(TagVariableDeclaration),
|
||||||
|
ComptimeValue(ComptimeValueDeclaration),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceElement for VariableDeclaration {
|
impl SourceElement for VariableDeclaration {
|
||||||
|
@ -334,6 +335,7 @@ impl SourceElement for VariableDeclaration {
|
||||||
Self::Array(declaration) => declaration.span(),
|
Self::Array(declaration) => declaration.span(),
|
||||||
Self::Score(declaration) => declaration.span(),
|
Self::Score(declaration) => declaration.span(),
|
||||||
Self::Tag(declaration) => declaration.span(),
|
Self::Tag(declaration) => declaration.span(),
|
||||||
|
Self::ComptimeValue(declaration) => declaration.span(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -347,6 +349,7 @@ impl VariableDeclaration {
|
||||||
Self::Array(declaration) => &declaration.identifier,
|
Self::Array(declaration) => &declaration.identifier,
|
||||||
Self::Score(declaration) => &declaration.identifier,
|
Self::Score(declaration) => &declaration.identifier,
|
||||||
Self::Tag(declaration) => &declaration.identifier,
|
Self::Tag(declaration) => &declaration.identifier,
|
||||||
|
Self::ComptimeValue(declaration) => &declaration.identifier,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,6 +361,7 @@ impl VariableDeclaration {
|
||||||
Self::Array(declaration) => &declaration.variable_type,
|
Self::Array(declaration) => &declaration.variable_type,
|
||||||
Self::Score(declaration) => &declaration.int_keyword,
|
Self::Score(declaration) => &declaration.int_keyword,
|
||||||
Self::Tag(declaration) => &declaration.bool_keyword,
|
Self::Tag(declaration) => &declaration.bool_keyword,
|
||||||
|
Self::ComptimeValue(declaration) => &declaration.val_keyword,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,6 +387,14 @@ impl VariableDeclaration {
|
||||||
declaration.annotations.push_front(annotation);
|
declaration.annotations.push_front(annotation);
|
||||||
Ok(Self::Tag(declaration))
|
Ok(Self::Tag(declaration))
|
||||||
}
|
}
|
||||||
|
Self::ComptimeValue(_) => {
|
||||||
|
let err = Error::InvalidAnnotation(InvalidAnnotation {
|
||||||
|
annotation: annotation.assignment.identifier.span,
|
||||||
|
target: "comptime values".to_string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -681,6 +693,53 @@ impl TagVariableDeclaration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a compile time value declaration in the syntax tree.
|
||||||
|
///
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ```ebnf
|
||||||
|
/// ComptimeValueDeclaration:
|
||||||
|
/// 'val' identifier VariableDeclarationAssignment?
|
||||||
|
/// ```
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||||
|
pub struct ComptimeValueDeclaration {
|
||||||
|
/// The type of the variable.
|
||||||
|
#[get = "pub"]
|
||||||
|
val_keyword: Keyword,
|
||||||
|
/// The identifier of the variable.
|
||||||
|
#[get = "pub"]
|
||||||
|
identifier: Identifier,
|
||||||
|
/// The optional assignment of the variable.
|
||||||
|
#[get = "pub"]
|
||||||
|
assignment: Option<VariableDeclarationAssignment>,
|
||||||
|
/// The annotations of the variable declaration.
|
||||||
|
#[get = "pub"]
|
||||||
|
annotations: VecDeque<Annotation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceElement for ComptimeValueDeclaration {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
self.val_keyword
|
||||||
|
.span()
|
||||||
|
.join(
|
||||||
|
&self
|
||||||
|
.assignment
|
||||||
|
.as_ref()
|
||||||
|
.map_or_else(|| self.identifier.span(), SourceElement::span),
|
||||||
|
)
|
||||||
|
.expect("The span of the single variable declaration is invalid.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComptimeValueDeclaration {
|
||||||
|
/// Dissolves the [`ComptimeValueDeclaration`] into its components.
|
||||||
|
#[must_use]
|
||||||
|
pub fn dissolve(self) -> (Keyword, Identifier, Option<VariableDeclarationAssignment>) {
|
||||||
|
(self.val_keyword, self.identifier, self.assignment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents an assignment in the syntax tree.
|
/// Represents an assignment in the syntax tree.
|
||||||
///
|
///
|
||||||
/// Syntax Synopsis:
|
/// Syntax Synopsis:
|
||||||
|
@ -883,7 +942,10 @@ impl Parser<'_> {
|
||||||
) -> ParseResult<Semicolon> {
|
) -> ParseResult<Semicolon> {
|
||||||
let statement = match self.stop_at_significant() {
|
let statement = match self.stop_at_significant() {
|
||||||
Reading::Atomic(Token::Keyword(keyword))
|
Reading::Atomic(Token::Keyword(keyword))
|
||||||
if matches!(keyword.keyword, KeywordKind::Int | KeywordKind::Bool) =>
|
if matches!(
|
||||||
|
keyword.keyword,
|
||||||
|
KeywordKind::Int | KeywordKind::Bool | KeywordKind::Val
|
||||||
|
) =>
|
||||||
{
|
{
|
||||||
self.parse_variable_declaration(handler)
|
self.parse_variable_declaration(handler)
|
||||||
.map(SemicolonStatement::VariableDeclaration)
|
.map(SemicolonStatement::VariableDeclaration)
|
||||||
|
@ -895,18 +957,20 @@ impl Parser<'_> {
|
||||||
let destination = {
|
let destination = {
|
||||||
let identifier = p.parse_identifier(&VoidHandler)?;
|
let identifier = p.parse_identifier(&VoidHandler)?;
|
||||||
|
|
||||||
if let Ok(tree) = p.step_into(
|
match p.stop_at_significant() {
|
||||||
|
Reading::IntoDelimited(punc) if punc.punctuation == '[' => {
|
||||||
|
let tree = p.step_into(
|
||||||
Delimiter::Bracket,
|
Delimiter::Bracket,
|
||||||
|pp| pp.parse_expression(&VoidHandler),
|
|pp| pp.parse_expression(&VoidHandler),
|
||||||
&VoidHandler,
|
&VoidHandler,
|
||||||
) {
|
)?;
|
||||||
let open = tree.open;
|
let open = tree.open;
|
||||||
let close = tree.close;
|
let close = tree.close;
|
||||||
let expression = tree.tree?;
|
let expression = tree.tree?;
|
||||||
|
|
||||||
AssignmentDestination::Indexed(identifier, open, expression, close)
|
AssignmentDestination::Indexed(identifier, open, expression, close)
|
||||||
} else {
|
}
|
||||||
AssignmentDestination::Identifier(identifier)
|
_ => AssignmentDestination::Identifier(identifier),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let equals = p.parse_punctuation('=', true, &VoidHandler)?;
|
let equals = p.parse_punctuation('=', true, &VoidHandler)?;
|
||||||
|
@ -948,7 +1012,10 @@ impl Parser<'_> {
|
||||||
|
|
||||||
let variable_type = match self.stop_at_significant() {
|
let variable_type = match self.stop_at_significant() {
|
||||||
Reading::Atomic(Token::Keyword(keyword))
|
Reading::Atomic(Token::Keyword(keyword))
|
||||||
if matches!(keyword.keyword, KeywordKind::Int | KeywordKind::Bool) =>
|
if matches!(
|
||||||
|
keyword.keyword,
|
||||||
|
KeywordKind::Int | KeywordKind::Bool | KeywordKind::Val
|
||||||
|
) =>
|
||||||
{
|
{
|
||||||
self.forward();
|
self.forward();
|
||||||
keyword
|
keyword
|
||||||
|
@ -983,7 +1050,10 @@ impl Parser<'_> {
|
||||||
let identifier = self.parse_identifier(handler)?;
|
let identifier = self.parse_identifier(handler)?;
|
||||||
|
|
||||||
match self.stop_at_significant() {
|
match self.stop_at_significant() {
|
||||||
Reading::IntoDelimited(punc) if punc.punctuation == '[' => {
|
Reading::IntoDelimited(punc)
|
||||||
|
if punc.punctuation == '['
|
||||||
|
&& matches!(variable_type.keyword, KeywordKind::Int | KeywordKind::Bool) =>
|
||||||
|
{
|
||||||
let tree = self.step_into(
|
let tree = self.step_into(
|
||||||
Delimiter::Bracket,
|
Delimiter::Bracket,
|
||||||
|p| {
|
|p| {
|
||||||
|
@ -1071,6 +1141,16 @@ impl Parser<'_> {
|
||||||
let expression = self.parse_expression(handler)?;
|
let expression = self.parse_expression(handler)?;
|
||||||
let assignment = VariableDeclarationAssignment { equals, expression };
|
let assignment = VariableDeclarationAssignment { equals, expression };
|
||||||
|
|
||||||
|
if variable_type.keyword == KeywordKind::Val {
|
||||||
|
Ok(VariableDeclaration::ComptimeValue(
|
||||||
|
ComptimeValueDeclaration {
|
||||||
|
val_keyword: variable_type,
|
||||||
|
identifier,
|
||||||
|
assignment: Some(assignment),
|
||||||
|
annotations: VecDeque::new(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
} else {
|
||||||
Ok(VariableDeclaration::Single(SingleVariableDeclaration {
|
Ok(VariableDeclaration::Single(SingleVariableDeclaration {
|
||||||
variable_type,
|
variable_type,
|
||||||
identifier,
|
identifier,
|
||||||
|
@ -1078,6 +1158,7 @@ impl Parser<'_> {
|
||||||
annotations: VecDeque::new(),
|
annotations: VecDeque::new(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// SingleVariableDeclaration without Assignment
|
// SingleVariableDeclaration without Assignment
|
||||||
_ => Ok(VariableDeclaration::Single(SingleVariableDeclaration {
|
_ => Ok(VariableDeclaration::Single(SingleVariableDeclaration {
|
||||||
variable_type,
|
variable_type,
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
//! Execute block statement syntax tree.
|
//! Execute block statement syntax tree.
|
||||||
|
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use derive_more::From;
|
use derive_more::From;
|
||||||
use enum_as_inner::EnumAsInner;
|
use enum_as_inner::EnumAsInner;
|
||||||
use getset::Getters;
|
use getset::Getters;
|
||||||
|
@ -998,10 +996,10 @@ pub trait ExecuteBlockHeadItem {
|
||||||
#[expect(clippy::missing_errors_doc)]
|
#[expect(clippy::missing_errors_doc)]
|
||||||
fn analyze_semantics(
|
fn analyze_semantics(
|
||||||
&self,
|
&self,
|
||||||
macro_names: &HashSet<String>,
|
scope: &crate::semantic::SemanticScope,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Result<(), crate::semantic::error::Error> {
|
) -> Result<(), crate::semantic::error::Error> {
|
||||||
self.selector().analyze_semantics(macro_names, handler)
|
self.selector().analyze_semantics(scope, handler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,10 @@ pub enum TranspileError {
|
||||||
MissingValue(#[from] MissingValue),
|
MissingValue(#[from] MissingValue),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
IllegalIndexing(#[from] IllegalIndexing),
|
IllegalIndexing(#[from] IllegalIndexing),
|
||||||
|
#[error(transparent)]
|
||||||
|
InvalidArgument(#[from] InvalidArgument),
|
||||||
|
#[error(transparent)]
|
||||||
|
NotComptime(#[from] NotComptime),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of a transpilation operation.
|
/// The result of a transpilation operation.
|
||||||
|
@ -52,8 +56,10 @@ pub type TranspileResult<T> = Result<T, TranspileError>;
|
||||||
/// 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, Getters)]
|
#[derive(Debug, Clone, PartialEq, Eq, Getters)]
|
||||||
pub struct MissingFunctionDeclaration {
|
pub struct MissingFunctionDeclaration {
|
||||||
|
/// The span of the identifier that is missing.
|
||||||
#[get = "pub"]
|
#[get = "pub"]
|
||||||
span: Span,
|
span: Span,
|
||||||
|
/// Possible alternatives for the missing function declaration.
|
||||||
#[get = "pub"]
|
#[get = "pub"]
|
||||||
alternatives: Vec<FunctionData>,
|
alternatives: Vec<FunctionData>,
|
||||||
}
|
}
|
||||||
|
@ -127,11 +133,26 @@ impl Display for MissingFunctionDeclaration {
|
||||||
|
|
||||||
impl std::error::Error for MissingFunctionDeclaration {}
|
impl std::error::Error for MissingFunctionDeclaration {}
|
||||||
|
|
||||||
|
impl std::hash::Hash for MissingFunctionDeclaration {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.span.hash(state);
|
||||||
|
for alternative in &self.alternatives {
|
||||||
|
alternative.identifier_span.hash(state);
|
||||||
|
alternative.namespace.hash(state);
|
||||||
|
alternative.parameters.hash(state);
|
||||||
|
alternative.public.hash(state);
|
||||||
|
alternative.statements.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An error that occurs when a function declaration is missing.
|
/// An error that occurs when a function declaration is missing.
|
||||||
#[allow(clippy::module_name_repetitions)]
|
#[allow(clippy::module_name_repetitions)]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct LuaRuntimeError {
|
pub struct LuaRuntimeError {
|
||||||
|
/// The span of the code block that caused the error.
|
||||||
pub code_block: Span,
|
pub code_block: Span,
|
||||||
|
/// The error message of the Lua runtime.
|
||||||
pub error_message: String,
|
pub error_message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,6 +176,8 @@ impl std::error::Error for LuaRuntimeError {}
|
||||||
|
|
||||||
#[cfg(feature = "lua")]
|
#[cfg(feature = "lua")]
|
||||||
impl LuaRuntimeError {
|
impl LuaRuntimeError {
|
||||||
|
/// Creates a new Lua runtime error from an mlua error.
|
||||||
|
#[must_use]
|
||||||
pub fn from_lua_err(err: &mlua::Error, span: Span) -> Self {
|
pub fn from_lua_err(err: &mlua::Error, span: Span) -> Self {
|
||||||
let err_string = err.to_string();
|
let err_string = err.to_string();
|
||||||
Self {
|
Self {
|
||||||
|
@ -168,9 +191,13 @@ impl LuaRuntimeError {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error that occurs when an annotation has an illegal content.
|
/// An error that occurs when an annotation has an illegal content.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Getters)]
|
||||||
pub struct IllegalAnnotationContent {
|
pub struct IllegalAnnotationContent {
|
||||||
|
/// The span of the annotation.
|
||||||
|
#[get = "pub"]
|
||||||
pub annotation: Span,
|
pub annotation: Span,
|
||||||
|
/// The error message.
|
||||||
|
#[get = "pub"]
|
||||||
pub message: String,
|
pub message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,9 +221,13 @@ impl Display for IllegalAnnotationContent {
|
||||||
impl std::error::Error for IllegalAnnotationContent {}
|
impl std::error::Error for IllegalAnnotationContent {}
|
||||||
|
|
||||||
/// An error that occurs when an expression can not evaluate to the wanted type.
|
/// An error that occurs when an expression can not evaluate to the wanted type.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Getters)]
|
||||||
pub struct MismatchedTypes {
|
pub struct MismatchedTypes {
|
||||||
|
/// The expression that can not evaluate to the wanted type.
|
||||||
|
#[get = "pub"]
|
||||||
pub expression: Span,
|
pub expression: Span,
|
||||||
|
/// The expected type.
|
||||||
|
#[get = "pub"]
|
||||||
pub expected_type: ExpectedType,
|
pub expected_type: ExpectedType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,9 +247,13 @@ impl Display for MismatchedTypes {
|
||||||
impl std::error::Error for MismatchedTypes {}
|
impl std::error::Error for MismatchedTypes {}
|
||||||
|
|
||||||
/// An error that occurs when an expression can not evaluate to the wanted type.
|
/// An error that occurs when an expression can not evaluate to the wanted type.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Getters)]
|
||||||
pub struct FunctionArgumentsNotAllowed {
|
pub struct FunctionArgumentsNotAllowed {
|
||||||
|
/// The arguments that are not allowed.
|
||||||
|
#[get = "pub"]
|
||||||
pub arguments: Span,
|
pub arguments: Span,
|
||||||
|
/// The error message.
|
||||||
|
#[get = "pub"]
|
||||||
pub message: String,
|
pub message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,9 +272,13 @@ 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.
|
/// An error that occurs when an expression can not evaluate to the wanted type.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Getters)]
|
||||||
pub struct AssignmentError {
|
pub struct AssignmentError {
|
||||||
|
/// The identifier that is assigned to.
|
||||||
|
#[get = "pub"]
|
||||||
pub identifier: Span,
|
pub identifier: Span,
|
||||||
|
/// The error message.
|
||||||
|
#[get = "pub"]
|
||||||
pub message: String,
|
pub message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,8 +297,10 @@ impl Display for AssignmentError {
|
||||||
impl std::error::Error for AssignmentError {}
|
impl std::error::Error for AssignmentError {}
|
||||||
|
|
||||||
/// An error that occurs when an unknown identifier is used.
|
/// An error that occurs when an unknown identifier is used.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Getters)]
|
||||||
pub struct UnknownIdentifier {
|
pub struct UnknownIdentifier {
|
||||||
|
/// The unknown identifier.
|
||||||
|
#[get = "pub"]
|
||||||
pub identifier: Span,
|
pub identifier: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,8 +326,10 @@ impl Display for UnknownIdentifier {
|
||||||
impl std::error::Error for UnknownIdentifier {}
|
impl std::error::Error for UnknownIdentifier {}
|
||||||
|
|
||||||
/// An error that occurs when there is a value expected but none provided.
|
/// An error that occurs when there is a value expected but none provided.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Getters)]
|
||||||
pub struct MissingValue {
|
pub struct MissingValue {
|
||||||
|
/// The expression that is missing a value.
|
||||||
|
#[get = "pub"]
|
||||||
pub expression: Span,
|
pub expression: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,9 +355,13 @@ impl Display for MissingValue {
|
||||||
impl std::error::Error for MissingValue {}
|
impl std::error::Error for MissingValue {}
|
||||||
|
|
||||||
/// An error that occurs when an indexing operation is not permitted.
|
/// An error that occurs when an indexing operation is not permitted.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Getters)]
|
||||||
pub struct IllegalIndexing {
|
pub struct IllegalIndexing {
|
||||||
|
/// The reason why the indexing operation is not permitted.
|
||||||
|
#[get = "pub"]
|
||||||
pub reason: IllegalIndexingReason,
|
pub reason: IllegalIndexingReason,
|
||||||
|
/// The expression that is the reason for the indexing being illegal.
|
||||||
|
#[get = "pub"]
|
||||||
pub expression: Span,
|
pub expression: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,11 +380,24 @@ impl Display for IllegalIndexing {
|
||||||
impl std::error::Error for IllegalIndexing {}
|
impl std::error::Error for IllegalIndexing {}
|
||||||
|
|
||||||
/// The reason why an indexing operation is not permitted.
|
/// The reason why an indexing operation is not permitted.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum IllegalIndexingReason {
|
pub enum IllegalIndexingReason {
|
||||||
|
/// The expression is not an identifier.
|
||||||
NotIdentifier,
|
NotIdentifier,
|
||||||
InvalidComptimeType { expected: ExpectedType },
|
/// The expression cannot be indexed.
|
||||||
IndexOutOfBounds { index: usize, length: usize },
|
NotIndexable,
|
||||||
|
/// The expression can only be indexed with a specific type that can be evaluated at compile time.
|
||||||
|
InvalidComptimeType {
|
||||||
|
/// The expected type.
|
||||||
|
expected: ExpectedType,
|
||||||
|
},
|
||||||
|
/// The index is out of bounds.
|
||||||
|
IndexOutOfBounds {
|
||||||
|
/// The index that is out of bounds.
|
||||||
|
index: usize,
|
||||||
|
/// The length indexed object.
|
||||||
|
length: usize,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for IllegalIndexingReason {
|
impl Display for IllegalIndexingReason {
|
||||||
|
@ -346,6 +406,9 @@ impl Display for IllegalIndexingReason {
|
||||||
Self::NotIdentifier => {
|
Self::NotIdentifier => {
|
||||||
write!(f, "The expression is not an identifier.")
|
write!(f, "The expression is not an identifier.")
|
||||||
}
|
}
|
||||||
|
Self::NotIndexable => {
|
||||||
|
write!(f, "The expression cannot be indexed.")
|
||||||
|
}
|
||||||
Self::InvalidComptimeType { expected } => {
|
Self::InvalidComptimeType { expected } => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
|
@ -361,3 +424,57 @@ impl Display for IllegalIndexingReason {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An error that occurs when an indexing operation is not permitted.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Getters)]
|
||||||
|
pub struct InvalidArgument {
|
||||||
|
/// The span of the argument.
|
||||||
|
#[get = "pub"]
|
||||||
|
pub span: Span,
|
||||||
|
/// The reason why the argument is invalid.
|
||||||
|
#[get = "pub"]
|
||||||
|
pub reason: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for InvalidArgument {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", Message::new(Severity::Error, &self.reason))?;
|
||||||
|
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"\n{}",
|
||||||
|
SourceCodeDisplay::new(&self.span, Option::<u8>::None)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for InvalidArgument {}
|
||||||
|
|
||||||
|
/// An error that occurs when an indexing operation is not permitted.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Getters)]
|
||||||
|
pub struct NotComptime {
|
||||||
|
/// The expression that cannot be evaluated at compile time.
|
||||||
|
#[get = "pub"]
|
||||||
|
pub expression: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for NotComptime {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
Message::new(
|
||||||
|
Severity::Error,
|
||||||
|
"The expression cannot be evaluated at compile time but is required to."
|
||||||
|
)
|
||||||
|
)?;
|
||||||
|
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"\n{}",
|
||||||
|
SourceCodeDisplay::new(&self.expression, Option::<u8>::None)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for NotComptime {}
|
||||||
|
|
|
@ -1,15 +1,8 @@
|
||||||
//! The expression transpiler.
|
//! The expression transpiler.
|
||||||
|
|
||||||
use std::{fmt::Display, string::ToString, sync::Arc};
|
use std::{fmt::Display, string::ToString};
|
||||||
|
|
||||||
use super::{util::MacroString, Scope, VariableData};
|
use super::util::MacroString;
|
||||||
use crate::{
|
|
||||||
base::{self, Handler, VoidHandler},
|
|
||||||
lexical::token::MacroStringLiteralPart,
|
|
||||||
syntax::syntax_tree::expression::{
|
|
||||||
Binary, BinaryOperator, Expression, PrefixOperator, Primary,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
use enum_as_inner::EnumAsInner;
|
use enum_as_inner::EnumAsInner;
|
||||||
|
@ -19,17 +12,25 @@ use shulkerbox::prelude::{Command, Condition, Execute};
|
||||||
|
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
use super::{
|
use super::{
|
||||||
error::{IllegalIndexing, IllegalIndexingReason, MismatchedTypes, UnknownIdentifier},
|
error::{
|
||||||
TranspileResult, Transpiler,
|
IllegalIndexing, IllegalIndexingReason, MismatchedTypes, NotComptime, UnknownIdentifier,
|
||||||
|
},
|
||||||
|
Scope, TranspileResult, Transpiler, VariableData,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
use crate::{
|
use crate::{
|
||||||
base::source_file::SourceElement,
|
base::{self, source_file::SourceElement, Handler, VoidHandler},
|
||||||
|
lexical::token::MacroStringLiteralPart,
|
||||||
|
syntax::syntax_tree::expression::{
|
||||||
|
Binary, BinaryOperator, Expression, PrefixOperator, Primary,
|
||||||
|
},
|
||||||
transpile::{
|
transpile::{
|
||||||
error::{FunctionArgumentsNotAllowed, MissingValue},
|
error::{FunctionArgumentsNotAllowed, MissingValue},
|
||||||
TranspileError,
|
TranspileError,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "shulkerbox")]
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// Compile-time evaluated value
|
/// Compile-time evaluated value
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
|
@ -85,7 +86,7 @@ impl Display for ValueType {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum ExpectedType {
|
pub enum ExpectedType {
|
||||||
Boolean,
|
Boolean,
|
||||||
Integer,
|
Integer,
|
||||||
|
@ -227,6 +228,7 @@ pub enum ExtendedCondition {
|
||||||
Comptime(bool),
|
Comptime(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "shulkerbox")]
|
||||||
impl Expression {
|
impl Expression {
|
||||||
/// Returns whether the expression can yield a certain type.
|
/// Returns whether the expression can yield a certain type.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -238,12 +240,14 @@ impl Expression {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate at compile-time.
|
/// Evaluate at compile-time.
|
||||||
#[must_use]
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// - If the expression is not compile-time evaluatable.
|
||||||
pub fn comptime_eval(
|
pub fn comptime_eval(
|
||||||
&self,
|
&self,
|
||||||
scope: &Arc<Scope>,
|
scope: &Arc<Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Option<ComptimeValue> {
|
) -> Result<ComptimeValue, NotComptime> {
|
||||||
match self {
|
match self {
|
||||||
Self::Primary(primary) => primary.comptime_eval(scope, handler),
|
Self::Primary(primary) => primary.comptime_eval(scope, handler),
|
||||||
Self::Binary(binary) => binary.comptime_eval(scope, handler),
|
Self::Binary(binary) => binary.comptime_eval(scope, handler),
|
||||||
|
@ -251,6 +255,7 @@ impl Expression {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "shulkerbox")]
|
||||||
impl Primary {
|
impl Primary {
|
||||||
/// Returns whether the primary can yield a certain type.
|
/// Returns whether the primary can yield a certain type.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -332,19 +337,40 @@ impl Primary {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate at compile-time.
|
/// Evaluate at compile-time.
|
||||||
#[must_use]
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// - If the expression is not compile-time evaluatable.
|
||||||
pub fn comptime_eval(
|
pub fn comptime_eval(
|
||||||
&self,
|
&self,
|
||||||
scope: &Arc<Scope>,
|
scope: &Arc<Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Option<ComptimeValue> {
|
) -> Result<ComptimeValue, NotComptime> {
|
||||||
match self {
|
match self {
|
||||||
Self::Boolean(boolean) => Some(ComptimeValue::Boolean(boolean.value())),
|
Self::Boolean(boolean) => Ok(ComptimeValue::Boolean(boolean.value())),
|
||||||
Self::Integer(int) => Some(ComptimeValue::Integer(int.as_i64())),
|
Self::Integer(int) => Ok(ComptimeValue::Integer(int.as_i64())),
|
||||||
Self::StringLiteral(string_literal) => Some(ComptimeValue::String(
|
Self::StringLiteral(string_literal) => Ok(ComptimeValue::String(
|
||||||
string_literal.str_content().to_string(),
|
string_literal.str_content().to_string(),
|
||||||
)),
|
)),
|
||||||
Self::Identifier(_) | Self::FunctionCall(_) | Self::Indexed(_) => None,
|
Self::Identifier(ident) => scope.get_variable(ident.span.str()).map_or_else(
|
||||||
|
|| {
|
||||||
|
Err(NotComptime {
|
||||||
|
expression: self.span(),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|var| match var.as_ref() {
|
||||||
|
VariableData::ComptimeValue { value } => {
|
||||||
|
value.read().unwrap().clone().ok_or_else(|| NotComptime {
|
||||||
|
expression: ident.span.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Err(NotComptime {
|
||||||
|
expression: self.span(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Self::FunctionCall(_) | Self::Indexed(_) => Err(NotComptime {
|
||||||
|
expression: self.span(),
|
||||||
|
}),
|
||||||
Self::Parenthesized(parenthesized) => {
|
Self::Parenthesized(parenthesized) => {
|
||||||
parenthesized.expression().comptime_eval(scope, handler)
|
parenthesized.expression().comptime_eval(scope, handler)
|
||||||
}
|
}
|
||||||
|
@ -354,12 +380,14 @@ impl Primary {
|
||||||
.comptime_eval(scope, handler)
|
.comptime_eval(scope, handler)
|
||||||
.and_then(|val| match (prefix.operator(), val) {
|
.and_then(|val| match (prefix.operator(), val) {
|
||||||
(PrefixOperator::LogicalNot(_), ComptimeValue::Boolean(boolean)) => {
|
(PrefixOperator::LogicalNot(_), ComptimeValue::Boolean(boolean)) => {
|
||||||
Some(ComptimeValue::Boolean(!boolean))
|
Ok(ComptimeValue::Boolean(!boolean))
|
||||||
}
|
}
|
||||||
(PrefixOperator::Negate(_), ComptimeValue::Integer(int)) => {
|
(PrefixOperator::Negate(_), ComptimeValue::Integer(int)) => {
|
||||||
Some(ComptimeValue::Integer(-int))
|
Ok(ComptimeValue::Integer(-int))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => Err(NotComptime {
|
||||||
|
expression: prefix.span(),
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Self::Lua(lua) => lua
|
Self::Lua(lua) => lua
|
||||||
|
@ -367,25 +395,28 @@ impl Primary {
|
||||||
.inspect_err(|err| {
|
.inspect_err(|err| {
|
||||||
handler.receive(err.clone());
|
handler.receive(err.clone());
|
||||||
})
|
})
|
||||||
.ok()
|
.map_err(|_| NotComptime {
|
||||||
.flatten(),
|
expression: lua.span(),
|
||||||
|
})
|
||||||
|
.and_then(|val| val),
|
||||||
Self::MacroStringLiteral(macro_string_literal) => {
|
Self::MacroStringLiteral(macro_string_literal) => {
|
||||||
if macro_string_literal
|
if macro_string_literal
|
||||||
.parts()
|
.parts()
|
||||||
.iter()
|
.iter()
|
||||||
.any(|part| matches!(part, MacroStringLiteralPart::MacroUsage { .. }))
|
.any(|part| matches!(part, MacroStringLiteralPart::MacroUsage { .. }))
|
||||||
{
|
{
|
||||||
Some(ComptimeValue::MacroString(
|
Ok(ComptimeValue::MacroString(
|
||||||
macro_string_literal.clone().into(),
|
macro_string_literal.clone().into(),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Some(ComptimeValue::String(macro_string_literal.str_content()))
|
Ok(ComptimeValue::String(macro_string_literal.str_content()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "shulkerbox")]
|
||||||
impl Binary {
|
impl Binary {
|
||||||
/// Returns whether the binary can yield a certain type.
|
/// Returns whether the binary can yield a certain type.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -428,12 +459,14 @@ impl Binary {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate at compile-time.
|
/// Evaluate at compile-time.
|
||||||
#[must_use]
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// - If the expression is not compile-time evaluatable.
|
||||||
pub fn comptime_eval(
|
pub fn comptime_eval(
|
||||||
&self,
|
&self,
|
||||||
scope: &Arc<Scope>,
|
scope: &Arc<Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Option<ComptimeValue> {
|
) -> Result<ComptimeValue, NotComptime> {
|
||||||
let left = self.left_operand().comptime_eval(scope, handler)?;
|
let left = self.left_operand().comptime_eval(scope, handler)?;
|
||||||
let right = self.right_operand().comptime_eval(scope, handler)?;
|
let right = self.right_operand().comptime_eval(scope, handler)?;
|
||||||
|
|
||||||
|
@ -447,7 +480,7 @@ impl Binary {
|
||||||
.right_operand()
|
.right_operand()
|
||||||
.can_yield_type(ValueType::Boolean, scope) =>
|
.can_yield_type(ValueType::Boolean, scope) =>
|
||||||
{
|
{
|
||||||
Some(ComptimeValue::Boolean(true))
|
Ok(ComptimeValue::Boolean(true))
|
||||||
}
|
}
|
||||||
(ComptimeValue::Boolean(false), _) | (_, ComptimeValue::Boolean(false))
|
(ComptimeValue::Boolean(false), _) | (_, ComptimeValue::Boolean(false))
|
||||||
if matches!(self.operator(), BinaryOperator::LogicalAnd(..))
|
if matches!(self.operator(), BinaryOperator::LogicalAnd(..))
|
||||||
|
@ -458,15 +491,17 @@ impl Binary {
|
||||||
.right_operand()
|
.right_operand()
|
||||||
.can_yield_type(ValueType::Boolean, scope) =>
|
.can_yield_type(ValueType::Boolean, scope) =>
|
||||||
{
|
{
|
||||||
Some(ComptimeValue::Boolean(false))
|
Ok(ComptimeValue::Boolean(false))
|
||||||
}
|
}
|
||||||
(ComptimeValue::Boolean(left), ComptimeValue::Boolean(right)) => {
|
(ComptimeValue::Boolean(left), ComptimeValue::Boolean(right)) => {
|
||||||
match self.operator() {
|
match self.operator() {
|
||||||
BinaryOperator::Equal(..) => Some(ComptimeValue::Boolean(left == right)),
|
BinaryOperator::Equal(..) => Ok(ComptimeValue::Boolean(left == right)),
|
||||||
BinaryOperator::NotEqual(..) => Some(ComptimeValue::Boolean(left != right)),
|
BinaryOperator::NotEqual(..) => Ok(ComptimeValue::Boolean(left != right)),
|
||||||
BinaryOperator::LogicalAnd(..) => Some(ComptimeValue::Boolean(left && right)),
|
BinaryOperator::LogicalAnd(..) => Ok(ComptimeValue::Boolean(left && right)),
|
||||||
BinaryOperator::LogicalOr(..) => Some(ComptimeValue::Boolean(left || right)),
|
BinaryOperator::LogicalOr(..) => Ok(ComptimeValue::Boolean(left || right)),
|
||||||
_ => None,
|
_ => Err(NotComptime {
|
||||||
|
expression: self.span(),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(ComptimeValue::Integer(left), ComptimeValue::Integer(right)) => {
|
(ComptimeValue::Integer(left), ComptimeValue::Integer(right)) => {
|
||||||
|
@ -496,14 +531,37 @@ impl Binary {
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
.ok_or_else(|| NotComptime {
|
||||||
|
expression: self.span(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
(ComptimeValue::String(left), ComptimeValue::String(right)) => match self.operator() {
|
(ComptimeValue::String(left), ComptimeValue::String(right)) => match self.operator() {
|
||||||
BinaryOperator::Add(..) => Some(ComptimeValue::String(left + &right)),
|
BinaryOperator::Add(..) => Ok(ComptimeValue::String(left + &right)),
|
||||||
BinaryOperator::Equal(..) => Some(ComptimeValue::Boolean(left == right)),
|
BinaryOperator::Equal(..) => Ok(ComptimeValue::Boolean(left == right)),
|
||||||
BinaryOperator::NotEqual(..) => Some(ComptimeValue::Boolean(left != right)),
|
BinaryOperator::NotEqual(..) => Ok(ComptimeValue::Boolean(left != right)),
|
||||||
_ => None,
|
_ => Err(NotComptime {
|
||||||
|
expression: self.span(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
_ => None,
|
// TODO: also allow macro strings
|
||||||
|
(
|
||||||
|
left @ ComptimeValue::String(_),
|
||||||
|
right @ (ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)),
|
||||||
|
)
|
||||||
|
| (
|
||||||
|
left @ (ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)),
|
||||||
|
right @ ComptimeValue::String(_),
|
||||||
|
) => match self.operator() {
|
||||||
|
BinaryOperator::Add(_) => Ok(ComptimeValue::String(
|
||||||
|
left.to_string_no_macro().unwrap() + &right.to_string_no_macro().unwrap(),
|
||||||
|
)),
|
||||||
|
_ => Err(NotComptime {
|
||||||
|
expression: self.span(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
_ => Err(NotComptime {
|
||||||
|
expression: self.span(),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -639,7 +697,7 @@ impl Transpiler {
|
||||||
Primary::Lua(lua) =>
|
Primary::Lua(lua) =>
|
||||||
{
|
{
|
||||||
#[expect(clippy::option_if_let_else)]
|
#[expect(clippy::option_if_let_else)]
|
||||||
if let Some(value) = lua.eval_comptime(scope, handler)? {
|
if let Ok(value) = lua.eval_comptime(scope, handler)? {
|
||||||
self.store_comptime_value(&value, target, lua, handler)
|
self.store_comptime_value(&value, target, lua, handler)
|
||||||
} else {
|
} else {
|
||||||
let err = TranspileError::MissingValue(MissingValue {
|
let err = TranspileError::MissingValue(MissingValue {
|
||||||
|
@ -801,7 +859,7 @@ impl Transpiler {
|
||||||
if let Some(variable) = variable.as_deref() {
|
if let Some(variable) = variable.as_deref() {
|
||||||
let from = match variable {
|
let from = match variable {
|
||||||
VariableData::Scoreboard { objective } => {
|
VariableData::Scoreboard { objective } => {
|
||||||
if let Some(ComptimeValue::String(target)) =
|
if let Ok(ComptimeValue::String(target)) =
|
||||||
indexed.index().comptime_eval(scope, handler)
|
indexed.index().comptime_eval(scope, handler)
|
||||||
{
|
{
|
||||||
Ok(DataLocation::ScoreboardValue {
|
Ok(DataLocation::ScoreboardValue {
|
||||||
|
@ -820,7 +878,7 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VariableData::ScoreboardArray { objective, targets } => {
|
VariableData::ScoreboardArray { objective, targets } => {
|
||||||
if let Some(ComptimeValue::Integer(index)) =
|
if let Ok(ComptimeValue::Integer(index)) =
|
||||||
indexed.index().comptime_eval(scope, handler)
|
indexed.index().comptime_eval(scope, handler)
|
||||||
{
|
{
|
||||||
if let Some(target) = usize::try_from(index)
|
if let Some(target) = usize::try_from(index)
|
||||||
|
@ -858,7 +916,7 @@ impl Transpiler {
|
||||||
storage_name,
|
storage_name,
|
||||||
paths,
|
paths,
|
||||||
} => {
|
} => {
|
||||||
if let Some(ComptimeValue::Integer(index)) =
|
if let Ok(ComptimeValue::Integer(index)) =
|
||||||
indexed.index().comptime_eval(scope, handler)
|
indexed.index().comptime_eval(scope, handler)
|
||||||
{
|
{
|
||||||
if let Some(path) = usize::try_from(index)
|
if let Some(path) = usize::try_from(index)
|
||||||
|
@ -915,14 +973,14 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transpile_binary_expression(
|
pub(super) fn transpile_binary_expression(
|
||||||
&mut self,
|
&mut self,
|
||||||
binary: &Binary,
|
binary: &Binary,
|
||||||
target: &DataLocation,
|
target: &DataLocation,
|
||||||
scope: &Arc<super::Scope>,
|
scope: &Arc<super::Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<Vec<Command>> {
|
) -> TranspileResult<Vec<Command>> {
|
||||||
if let Some(value) = binary.comptime_eval(scope, handler) {
|
if let Ok(value) = binary.comptime_eval(scope, handler) {
|
||||||
self.store_comptime_value(&value, target, binary, handler)
|
self.store_comptime_value(&value, target, binary, handler)
|
||||||
} else {
|
} else {
|
||||||
match binary.operator() {
|
match binary.operator() {
|
||||||
|
@ -1085,7 +1143,7 @@ impl Transpiler {
|
||||||
storage_name,
|
storage_name,
|
||||||
paths,
|
paths,
|
||||||
} => {
|
} => {
|
||||||
if let Some(ComptimeValue::Integer(index)) =
|
if let Ok(ComptimeValue::Integer(index)) =
|
||||||
indexed.index().comptime_eval(scope, handler)
|
indexed.index().comptime_eval(scope, handler)
|
||||||
{
|
{
|
||||||
if let Some(path) = usize::try_from(index)
|
if let Some(path) = usize::try_from(index)
|
||||||
|
@ -1166,15 +1224,15 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Primary::Lua(lua) => match lua.eval_comptime(scope, handler)? {
|
Primary::Lua(lua) => match lua.eval_comptime(scope, handler)? {
|
||||||
Some(ComptimeValue::String(value)) => Ok((
|
Ok(ComptimeValue::String(value)) => Ok((
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
ExtendedCondition::Runtime(Condition::Atom(value.into())),
|
ExtendedCondition::Runtime(Condition::Atom(value.into())),
|
||||||
)),
|
)),
|
||||||
Some(ComptimeValue::MacroString(value)) => Ok((
|
Ok(ComptimeValue::MacroString(value)) => Ok((
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
ExtendedCondition::Runtime(Condition::Atom(value.into())),
|
ExtendedCondition::Runtime(Condition::Atom(value.into())),
|
||||||
)),
|
)),
|
||||||
Some(ComptimeValue::Boolean(boolean)) => {
|
Ok(ComptimeValue::Boolean(boolean)) => {
|
||||||
Ok((Vec::new(), ExtendedCondition::Comptime(boolean)))
|
Ok((Vec::new(), ExtendedCondition::Comptime(boolean)))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
@ -102,6 +102,7 @@ impl Transpiler {
|
||||||
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()
|
||||||
.and_then(|val| val.to_string_no_macro())
|
.and_then(|val| val.to_string_no_macro())
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
let err = TranspileError::IllegalAnnotationContent(
|
let err = TranspileError::IllegalAnnotationContent(
|
||||||
|
@ -293,13 +294,10 @@ impl Transpiler {
|
||||||
let value = match expression {
|
let value = match expression {
|
||||||
Expression::Primary(Primary::Lua(lua)) => {
|
Expression::Primary(Primary::Lua(lua)) => {
|
||||||
lua.eval_comptime(scope, handler).and_then(|val| match val {
|
lua.eval_comptime(scope, handler).and_then(|val| match val {
|
||||||
Some(ComptimeValue::MacroString(s)) => Ok(Parameter::Static(s)),
|
Ok(ComptimeValue::MacroString(s)) => Ok(Parameter::Static(s)),
|
||||||
Some(val) => Ok(Parameter::Static(val.to_macro_string())),
|
Ok(val) => Ok(Parameter::Static(val.to_macro_string())),
|
||||||
None => {
|
Err(err) => {
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
let err = TranspileError::NotComptime(err);
|
||||||
expression: expression.span(),
|
|
||||||
expected_type: ExpectedType::String,
|
|
||||||
});
|
|
||||||
handler.receive(err.clone());
|
handler.receive(err.clone());
|
||||||
Err(err)
|
Err(err)
|
||||||
}
|
}
|
||||||
|
@ -361,7 +359,18 @@ impl Transpiler {
|
||||||
path: std::mem::take(&mut temp_path[0]),
|
path: std::mem::take(&mut temp_path[0]),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => todo!("other variable types"),
|
_ => {
|
||||||
|
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||||
|
expression: expression.span(),
|
||||||
|
expected_type: ExpectedType::AnyOf(vec![
|
||||||
|
ExpectedType::Integer,
|
||||||
|
ExpectedType::Boolean,
|
||||||
|
ExpectedType::String,
|
||||||
|
]),
|
||||||
|
});
|
||||||
|
handler.receive(err.clone());
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::Primary(
|
Expression::Primary(
|
||||||
|
|
|
@ -5,6 +5,7 @@ use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use shulkerbox::prelude::{Command, Execute};
|
use shulkerbox::prelude::{Command, Execute};
|
||||||
|
|
||||||
use serde_json::{json, Value as JsonValue};
|
use serde_json::{json, Value as JsonValue};
|
||||||
|
@ -15,8 +16,8 @@ use crate::{
|
||||||
semantic::error::{InvalidFunctionArguments, UnexpectedExpression},
|
semantic::error::{InvalidFunctionArguments, UnexpectedExpression},
|
||||||
syntax::syntax_tree::expression::{Expression, FunctionCall, Primary},
|
syntax::syntax_tree::expression::{Expression, FunctionCall, Primary},
|
||||||
transpile::{
|
transpile::{
|
||||||
error::{IllegalIndexing, IllegalIndexingReason, LuaRuntimeError, UnknownIdentifier},
|
error::{IllegalIndexing, IllegalIndexingReason, UnknownIdentifier},
|
||||||
expression::{ComptimeValue, DataLocation, StorageType},
|
expression::{ComptimeValue, DataLocation, ExpectedType, StorageType},
|
||||||
util::MacroString,
|
util::MacroString,
|
||||||
TranspileError,
|
TranspileError,
|
||||||
},
|
},
|
||||||
|
@ -182,17 +183,17 @@ fn print_function(
|
||||||
|
|
||||||
let args = get_args_assert_in_range(call, 1..=2)?;
|
let args = get_args_assert_in_range(call, 1..=2)?;
|
||||||
let first = args.first().expect("checked range");
|
let first = args.first().expect("checked range");
|
||||||
let (target, message_expression) = args.get(1).map_or_else(
|
let (target, message_expression) = if let Some(second) = args.get(1) {
|
||||||
|| ("@a".into(), first),
|
|
||||||
|second| {
|
|
||||||
(
|
(
|
||||||
first
|
first
|
||||||
.comptime_eval(scope, &VoidHandler)
|
.comptime_eval(scope, &VoidHandler)
|
||||||
.map_or_else(|| "@a".into(), |val| val.to_macro_string()),
|
.map(|val| val.to_macro_string())
|
||||||
|
.map_err(TranspileError::NotComptime)?,
|
||||||
second,
|
second,
|
||||||
)
|
)
|
||||||
},
|
} else {
|
||||||
);
|
("@a".into(), first)
|
||||||
|
};
|
||||||
|
|
||||||
let mut contains_macro = matches!(target, MacroString::MacroString(_));
|
let mut contains_macro = matches!(target, MacroString::MacroString(_));
|
||||||
|
|
||||||
|
@ -210,17 +211,24 @@ fn print_function(
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
vec![JsonValue::String(string.str_content().to_string())],
|
vec![JsonValue::String(string.str_content().to_string())],
|
||||||
)),
|
)),
|
||||||
|
#[cfg_attr(not(feature = "lua"), expect(unused_variables))]
|
||||||
Primary::Lua(lua) => {
|
Primary::Lua(lua) => {
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(feature = "lua")] {
|
||||||
let (ret, _lua) = lua.eval(scope, &VoidHandler)?;
|
let (ret, _lua) = lua.eval(scope, &VoidHandler)?;
|
||||||
Ok((
|
Ok((
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
vec![JsonValue::String(ret.to_string().map_err(|err| {
|
vec![JsonValue::String(ret.to_string().map_err(|err| {
|
||||||
TranspileError::LuaRuntimeError(LuaRuntimeError::from_lua_err(
|
TranspileError::LuaRuntimeError(super::error::LuaRuntimeError::from_lua_err(
|
||||||
&err,
|
&err,
|
||||||
lua.span(),
|
lua.span(),
|
||||||
))
|
))
|
||||||
})?)],
|
})?)],
|
||||||
))
|
))
|
||||||
|
} else {
|
||||||
|
Err(TranspileError::LuaDisabled)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Primary::Identifier(ident) => {
|
Primary::Identifier(ident) => {
|
||||||
let (cur_contains_macro, cmd, part) =
|
let (cur_contains_macro, cmd, part) =
|
||||||
|
@ -232,7 +240,7 @@ fn print_function(
|
||||||
Primary::Identifier(ident) => {
|
Primary::Identifier(ident) => {
|
||||||
match scope.get_variable(ident.span.str()).as_deref() {
|
match scope.get_variable(ident.span.str()).as_deref() {
|
||||||
Some(VariableData::Scoreboard { objective }) => {
|
Some(VariableData::Scoreboard { objective }) => {
|
||||||
if let Some(ComptimeValue::String(index)) =
|
if let Ok(ComptimeValue::String(index)) =
|
||||||
indexed.index().comptime_eval(scope, &VoidHandler)
|
indexed.index().comptime_eval(scope, &VoidHandler)
|
||||||
{
|
{
|
||||||
let (cmd, value) = get_data_location(
|
let (cmd, value) = get_data_location(
|
||||||
|
@ -244,11 +252,17 @@ fn print_function(
|
||||||
);
|
);
|
||||||
Ok((cmd.into_iter().collect(), vec![value]))
|
Ok((cmd.into_iter().collect(), vec![value]))
|
||||||
} else {
|
} else {
|
||||||
todo!("allow macro string, but throw error when index is not constant string")
|
// TODO: allow macro string, but throw error when index is not constant string
|
||||||
|
Err(TranspileError::IllegalIndexing(IllegalIndexing {
|
||||||
|
reason: IllegalIndexingReason::InvalidComptimeType {
|
||||||
|
expected: ExpectedType::String,
|
||||||
|
},
|
||||||
|
expression: indexed.index().span(),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(VariableData::ScoreboardArray { objective, targets }) => {
|
Some(VariableData::ScoreboardArray { objective, targets }) => {
|
||||||
if let Some(ComptimeValue::Integer(index)) =
|
if let Ok(ComptimeValue::Integer(index)) =
|
||||||
indexed.index().comptime_eval(scope, &VoidHandler)
|
indexed.index().comptime_eval(scope, &VoidHandler)
|
||||||
{
|
{
|
||||||
#[expect(clippy::option_if_let_else)]
|
#[expect(clippy::option_if_let_else)]
|
||||||
|
@ -274,14 +288,19 @@ fn print_function(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
todo!("throw error when index is not constant integer")
|
Err(TranspileError::IllegalIndexing(IllegalIndexing {
|
||||||
|
reason: IllegalIndexingReason::InvalidComptimeType {
|
||||||
|
expected: ExpectedType::Integer,
|
||||||
|
},
|
||||||
|
expression: indexed.index().span(),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(VariableData::BooleanStorageArray {
|
Some(VariableData::BooleanStorageArray {
|
||||||
storage_name,
|
storage_name,
|
||||||
paths,
|
paths,
|
||||||
}) => {
|
}) => {
|
||||||
if let Some(ComptimeValue::Integer(index)) =
|
if let Ok(ComptimeValue::Integer(index)) =
|
||||||
indexed.index().comptime_eval(scope, &VoidHandler)
|
indexed.index().comptime_eval(scope, &VoidHandler)
|
||||||
{
|
{
|
||||||
#[expect(clippy::option_if_let_else)]
|
#[expect(clippy::option_if_let_else)]
|
||||||
|
@ -308,10 +327,18 @@ fn print_function(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
todo!("throw error when index is not constant integer")
|
Err(TranspileError::IllegalIndexing(IllegalIndexing {
|
||||||
|
reason: IllegalIndexingReason::InvalidComptimeType {
|
||||||
|
expected: ExpectedType::Integer,
|
||||||
|
},
|
||||||
|
expression: indexed.index().span(),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => todo!("catch illegal indexing"),
|
_ => Err(TranspileError::IllegalIndexing(IllegalIndexing {
|
||||||
|
reason: IllegalIndexingReason::NotIndexable,
|
||||||
|
expression: indexed.object().span(),
|
||||||
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Err(TranspileError::IllegalIndexing(IllegalIndexing {
|
_ => Err(TranspileError::IllegalIndexing(IllegalIndexing {
|
||||||
|
@ -339,9 +366,43 @@ fn print_function(
|
||||||
Ok((cmds, parts))
|
Ok((cmds, parts))
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => todo!("print_function Primary"),
|
primary => {
|
||||||
|
let (storage_name, mut storage_paths) = transpiler.get_temp_storage_locations(1);
|
||||||
|
let location = DataLocation::Storage {
|
||||||
|
storage_name,
|
||||||
|
path: std::mem::take(&mut storage_paths[0]),
|
||||||
|
r#type: StorageType::Int,
|
||||||
|
};
|
||||||
|
let cmds = transpiler.transpile_primary_expression(
|
||||||
|
primary,
|
||||||
|
&location,
|
||||||
|
scope,
|
||||||
|
&VoidHandler,
|
||||||
|
)?;
|
||||||
|
let (cmd, part) = get_data_location(&location, transpiler);
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
cmds.into_iter().chain(cmd.into_iter()).collect(),
|
||||||
|
vec![part],
|
||||||
|
))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Expression::Binary(_) => todo!("print_function Binary"),
|
Expression::Binary(binary) => {
|
||||||
|
let (storage_name, mut storage_paths) = transpiler.get_temp_storage_locations(1);
|
||||||
|
let location = DataLocation::Storage {
|
||||||
|
storage_name,
|
||||||
|
path: std::mem::take(&mut storage_paths[0]),
|
||||||
|
r#type: StorageType::Int,
|
||||||
|
};
|
||||||
|
let cmds =
|
||||||
|
transpiler.transpile_binary_expression(binary, &location, scope, &VoidHandler)?;
|
||||||
|
let (cmd, part) = get_data_location(&location, transpiler);
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
cmds.into_iter().chain(cmd.into_iter()).collect(),
|
||||||
|
vec![part],
|
||||||
|
))
|
||||||
|
}
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
// TODO: prepend prefix with datapack name to parts and remove following
|
// TODO: prepend prefix with datapack name to parts and remove following
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Executes the Lua code and returns the resulting command.
|
//! Executes the Lua code and returns the resulting command.
|
||||||
|
|
||||||
#[cfg(feature = "lua")]
|
#[cfg(all(feature = "lua", feature = "shulkerbox"))]
|
||||||
mod enabled {
|
mod enabled {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@ mod enabled {
|
||||||
syntax::syntax_tree::expression::LuaCode,
|
syntax::syntax_tree::expression::LuaCode,
|
||||||
transpile::{
|
transpile::{
|
||||||
error::{
|
error::{
|
||||||
LuaRuntimeError, MismatchedTypes, TranspileError, TranspileResult,
|
InvalidArgument, LuaRuntimeError, MismatchedTypes, NotComptime, TranspileError,
|
||||||
UnknownIdentifier,
|
TranspileResult, UnknownIdentifier,
|
||||||
},
|
},
|
||||||
expression::{ComptimeValue, ExpectedType},
|
expression::{ComptimeValue, ExpectedType},
|
||||||
Scope, VariableData,
|
Scope, VariableData,
|
||||||
|
@ -86,11 +86,15 @@ mod enabled {
|
||||||
&self,
|
&self,
|
||||||
scope: &Arc<Scope>,
|
scope: &Arc<Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<Option<ComptimeValue>> {
|
) -> TranspileResult<Result<ComptimeValue, NotComptime>> {
|
||||||
// required to keep the lua instance alive
|
// required to keep the lua instance alive
|
||||||
let (lua_result, _lua) = self.eval(scope, handler)?;
|
let (lua_result, _lua) = self.eval(scope, handler)?;
|
||||||
|
|
||||||
self.handle_lua_result(lua_result, handler)
|
self.handle_lua_result(lua_result, handler).map(|res| {
|
||||||
|
res.ok_or_else(|| NotComptime {
|
||||||
|
expression: self.span(),
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_globals(&self, lua: &Lua, scope: &Arc<Scope>) -> TranspileResult<()> {
|
fn add_globals(&self, lua: &Lua, scope: &Arc<Scope>) -> TranspileResult<()> {
|
||||||
|
@ -262,8 +266,28 @@ 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::ComptimeValue { value }) => {
|
||||||
|
let value = value.read().unwrap();
|
||||||
|
match &*value {
|
||||||
|
Some(ComptimeValue::Boolean(b)) => Value::Boolean(*b),
|
||||||
|
Some(ComptimeValue::Integer(i)) => Value::Integer(*i),
|
||||||
|
Some(ComptimeValue::String(s)) => Value::String(
|
||||||
|
lua.create_string(s)
|
||||||
|
.map_err(|err| LuaRuntimeError::from_lua_err(&err, self.span()))?,
|
||||||
|
),
|
||||||
|
Some(ComptimeValue::MacroString(s)) => Value::String(
|
||||||
|
lua.create_string(s.to_string())
|
||||||
|
.map_err(|err| LuaRuntimeError::from_lua_err(&err, self.span()))?,
|
||||||
|
),
|
||||||
|
None => Value::Nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
Some(VariableData::Function { .. } | VariableData::InternalFunction { .. }) => {
|
Some(VariableData::Function { .. } | VariableData::InternalFunction { .. }) => {
|
||||||
todo!("(internal) functions are not supported yet");
|
// TODO: add support for functions
|
||||||
|
return Err(TranspileError::InvalidArgument(InvalidArgument {
|
||||||
|
reason: "functions cannot be passed to Lua".to_string(),
|
||||||
|
span: identifier.span(),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
return Err(TranspileError::UnknownIdentifier(UnknownIdentifier {
|
return Err(TranspileError::UnknownIdentifier(UnknownIdentifier {
|
||||||
|
@ -374,7 +398,7 @@ mod enabled {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "lua"))]
|
#[cfg(all(not(feature = "lua"), feature = "shulkerbox"))]
|
||||||
mod disabled {
|
mod disabled {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -397,7 +421,7 @@ mod disabled {
|
||||||
&self,
|
&self,
|
||||||
scope: &Arc<Scope>,
|
scope: &Arc<Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<()> {
|
) -> TranspileResult<((), ())> {
|
||||||
let _ = scope;
|
let _ = scope;
|
||||||
handler.receive(TranspileError::LuaDisabled);
|
handler.receive(TranspileError::LuaDisabled);
|
||||||
tracing::error!("Lua code evaluation is disabled");
|
tracing::error!("Lua code evaluation is disabled");
|
||||||
|
|
|
@ -16,7 +16,7 @@ use crate::{
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
pub mod conversions;
|
pub mod conversions;
|
||||||
mod error;
|
pub mod error;
|
||||||
|
|
||||||
pub mod expression;
|
pub mod expression;
|
||||||
|
|
||||||
|
@ -38,9 +38,12 @@ pub mod internal_functions;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
pub mod function;
|
pub mod function;
|
||||||
|
#[doc(inline)]
|
||||||
|
#[cfg(feature = "shulkerbox")]
|
||||||
pub use function::TranspiledFunctionArguments;
|
pub use function::TranspiledFunctionArguments;
|
||||||
|
|
||||||
mod variables;
|
mod variables;
|
||||||
|
#[cfg(feature = "shulkerbox")]
|
||||||
pub use variables::{Scope, VariableData};
|
pub use variables::{Scope, VariableData};
|
||||||
|
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
@ -58,7 +61,7 @@ pub struct FunctionData {
|
||||||
|
|
||||||
/// Possible values for an annotation.
|
/// Possible values for an annotation.
|
||||||
#[expect(clippy::module_name_repetitions)]
|
#[expect(clippy::module_name_repetitions)]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, EnumIs)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, EnumIs)]
|
||||||
pub enum TranspileAnnotationValue {
|
pub enum TranspileAnnotationValue {
|
||||||
/// No value.
|
/// No value.
|
||||||
None,
|
None,
|
||||||
|
|
|
@ -25,7 +25,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
error::{MismatchedTypes, TranspileError, TranspileResult},
|
error::{MismatchedTypes, MissingValue, TranspileError, TranspileResult, UnknownIdentifier},
|
||||||
expression::{ComptimeValue, ExpectedType, ExtendedCondition},
|
expression::{ComptimeValue, ExpectedType, ExtendedCondition},
|
||||||
variables::{Scope, TranspileAssignmentTarget, VariableData},
|
variables::{Scope, TranspileAssignmentTarget, VariableData},
|
||||||
FunctionData, TranspileAnnotationValue, TranspiledFunctionArguments,
|
FunctionData, TranspileAnnotationValue, TranspiledFunctionArguments,
|
||||||
|
@ -342,11 +342,46 @@ impl Transpiler {
|
||||||
Expression::Primary(Primary::FunctionCall(func)) => {
|
Expression::Primary(Primary::FunctionCall(func)) => {
|
||||||
self.transpile_function_call(func, scope, handler)
|
self.transpile_function_call(func, scope, handler)
|
||||||
}
|
}
|
||||||
|
Expression::Primary(Primary::Identifier(ident)) => {
|
||||||
|
match scope.get_variable(ident.span.str()).as_deref() {
|
||||||
|
Some(VariableData::ComptimeValue { value }) => {
|
||||||
|
value.read().unwrap().as_ref().map_or_else(
|
||||||
|
|| {
|
||||||
|
let error = TranspileError::MissingValue(MissingValue {
|
||||||
|
expression: ident.span.clone(),
|
||||||
|
});
|
||||||
|
handler.receive(error.clone());
|
||||||
|
Err(error)
|
||||||
|
},
|
||||||
|
|val| {
|
||||||
|
let cmd = val.to_string_no_macro().map_or_else(
|
||||||
|
|| Command::UsesMacro(val.to_macro_string().into()),
|
||||||
|
Command::Raw,
|
||||||
|
);
|
||||||
|
Ok(vec![cmd])
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Some(_) => {
|
||||||
|
let error = TranspileError::UnexpectedExpression(UnexpectedExpression(
|
||||||
|
expression.clone(),
|
||||||
|
));
|
||||||
|
handler.receive(error.clone());
|
||||||
|
Err(error)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let error = TranspileError::UnknownIdentifier(UnknownIdentifier {
|
||||||
|
identifier: ident.span.clone(),
|
||||||
|
});
|
||||||
|
handler.receive(error.clone());
|
||||||
|
Err(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
expression @ Expression::Primary(
|
expression @ Expression::Primary(
|
||||||
Primary::Integer(_)
|
Primary::Integer(_)
|
||||||
| Primary::Boolean(_)
|
| Primary::Boolean(_)
|
||||||
| Primary::Prefix(_)
|
| Primary::Prefix(_)
|
||||||
| Primary::Identifier(_)
|
|
||||||
| Primary::Indexed(_),
|
| Primary::Indexed(_),
|
||||||
) => {
|
) => {
|
||||||
let error =
|
let error =
|
||||||
|
@ -361,9 +396,9 @@ impl Transpiler {
|
||||||
Ok(vec![Command::UsesMacro(string.into())])
|
Ok(vec![Command::UsesMacro(string.into())])
|
||||||
}
|
}
|
||||||
Expression::Primary(Primary::Lua(code)) => match code.eval_comptime(scope, handler)? {
|
Expression::Primary(Primary::Lua(code)) => match code.eval_comptime(scope, handler)? {
|
||||||
Some(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
||||||
Some(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
|
Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
|
||||||
Some(ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)) => {
|
Ok(ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)) => {
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||||
expected_type: ExpectedType::String,
|
expected_type: ExpectedType::String,
|
||||||
expression: code.span(),
|
expression: code.span(),
|
||||||
|
@ -371,7 +406,13 @@ impl Transpiler {
|
||||||
handler.receive(err.clone());
|
handler.receive(err.clone());
|
||||||
Err(err)
|
Err(err)
|
||||||
}
|
}
|
||||||
None => Ok(Vec::new()),
|
Err(_) => {
|
||||||
|
let err = TranspileError::MissingValue(MissingValue {
|
||||||
|
expression: code.span(),
|
||||||
|
});
|
||||||
|
handler.receive(err.clone());
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Expression::Primary(Primary::Parenthesized(parenthesized)) => self
|
Expression::Primary(Primary::Parenthesized(parenthesized)) => self
|
||||||
|
@ -382,8 +423,8 @@ impl Transpiler {
|
||||||
handler,
|
handler,
|
||||||
),
|
),
|
||||||
Expression::Binary(bin) => match bin.comptime_eval(scope, handler) {
|
Expression::Binary(bin) => match bin.comptime_eval(scope, handler) {
|
||||||
Some(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
||||||
Some(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
|
Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
|
||||||
_ => {
|
_ => {
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||||
expression: bin.span(),
|
expression: bin.span(),
|
||||||
|
@ -621,7 +662,7 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(ComptimeValue::Boolean(value)) = cond_expression.comptime_eval(scope, handler) {
|
if let Ok(ComptimeValue::Boolean(value)) = cond_expression.comptime_eval(scope, handler) {
|
||||||
if value {
|
if value {
|
||||||
Ok(Some((Vec::new(), then)))
|
Ok(Some((Vec::new(), then)))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -30,17 +30,17 @@ use crate::{
|
||||||
use super::{
|
use super::{
|
||||||
error::{
|
error::{
|
||||||
AssignmentError, IllegalAnnotationContent, IllegalIndexing, IllegalIndexingReason,
|
AssignmentError, IllegalAnnotationContent, IllegalIndexing, IllegalIndexingReason,
|
||||||
MismatchedTypes,
|
MismatchedTypes, NotComptime,
|
||||||
},
|
},
|
||||||
expression::{ComptimeValue, DataLocation, ExpectedType, StorageType},
|
expression::{ComptimeValue, DataLocation, ExpectedType, StorageType},
|
||||||
internal_functions::InternalFunction,
|
|
||||||
FunctionData, TranspileAnnotationValue, TranspileError, TranspileResult,
|
FunctionData, TranspileAnnotationValue, TranspileError, TranspileResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
use super::Transpiler;
|
use super::{internal_functions::InternalFunction, Transpiler};
|
||||||
|
|
||||||
/// Stores the data required to access a variable.
|
/// Stores the data required to access a variable.
|
||||||
|
#[cfg(feature = "shulkerbox")]
|
||||||
#[derive(Debug, Clone, EnumAsInner)]
|
#[derive(Debug, Clone, EnumAsInner)]
|
||||||
pub enum VariableData {
|
pub enum VariableData {
|
||||||
/// A function.
|
/// A function.
|
||||||
|
@ -100,6 +100,11 @@ pub enum VariableData {
|
||||||
/// The implementation
|
/// The implementation
|
||||||
implementation: InternalFunction,
|
implementation: InternalFunction,
|
||||||
},
|
},
|
||||||
|
/// Compiler internal variable.
|
||||||
|
ComptimeValue {
|
||||||
|
/// The value.
|
||||||
|
value: Arc<RwLock<Option<ComptimeValue>>>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, EnumAsInner)]
|
#[derive(Debug, Clone, Copy, EnumAsInner)]
|
||||||
|
@ -122,6 +127,7 @@ impl<'a> From<&'a AssignmentDestination> for TranspileAssignmentTarget<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A scope that stores variables.
|
/// A scope that stores variables.
|
||||||
|
#[cfg(feature = "shulkerbox")]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Scope<'a> {
|
pub struct Scope<'a> {
|
||||||
/// Parent scope where variables are inherited from.
|
/// Parent scope where variables are inherited from.
|
||||||
|
@ -132,6 +138,7 @@ pub struct Scope<'a> {
|
||||||
shadowed: RwLock<HashMap<String, usize>>,
|
shadowed: RwLock<HashMap<String, usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "shulkerbox")]
|
||||||
impl<'a> Scope<'a> {
|
impl<'a> Scope<'a> {
|
||||||
/// Creates a new scope.
|
/// Creates a new scope.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
@ -226,6 +233,7 @@ impl<'a> Scope<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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>>>);
|
||||||
|
@ -279,6 +287,31 @@ impl Transpiler {
|
||||||
scope,
|
scope,
|
||||||
handler,
|
handler,
|
||||||
),
|
),
|
||||||
|
VariableDeclaration::ComptimeValue(declaration) => {
|
||||||
|
let value = if let Some(assignment) = declaration.assignment() {
|
||||||
|
Some(
|
||||||
|
assignment
|
||||||
|
.expression()
|
||||||
|
.comptime_eval(scope, handler)
|
||||||
|
.map_err(|err| {
|
||||||
|
let err = TranspileError::NotComptime(err);
|
||||||
|
handler.receive(err.clone());
|
||||||
|
err
|
||||||
|
})?,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.set_variable(
|
||||||
|
declaration.identifier().span.str(),
|
||||||
|
VariableData::ComptimeValue {
|
||||||
|
value: Arc::new(RwLock::new(value)),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Vec::new())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,7 +496,7 @@ impl Transpiler {
|
||||||
let (identifier, indexing_value) = match destination {
|
let (identifier, indexing_value) = match destination {
|
||||||
TranspileAssignmentTarget::Identifier(ident) => (ident, None),
|
TranspileAssignmentTarget::Identifier(ident) => (ident, None),
|
||||||
TranspileAssignmentTarget::Indexed(ident, expression) => {
|
TranspileAssignmentTarget::Indexed(ident, expression) => {
|
||||||
(ident, expression.comptime_eval(scope, handler))
|
(ident, expression.comptime_eval(scope, handler).ok())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Some(target) = scope.get_variable(identifier.span.str()) {
|
if let Some(target) = scope.get_variable(identifier.span.str()) {
|
||||||
|
@ -489,7 +522,15 @@ impl Transpiler {
|
||||||
target: s,
|
target: s,
|
||||||
}),
|
}),
|
||||||
Some(ComptimeValue::MacroString(s)) => {
|
Some(ComptimeValue::MacroString(s)) => {
|
||||||
todo!("indexing scoreboard with macro string: {s:?}")
|
// TODO: allow indexing with macro string
|
||||||
|
let err = TranspileError::IllegalIndexing(IllegalIndexing {
|
||||||
|
reason: IllegalIndexingReason::InvalidComptimeType {
|
||||||
|
expected: ExpectedType::String,
|
||||||
|
},
|
||||||
|
expression: expression.span(),
|
||||||
|
});
|
||||||
|
handler.receive(err.clone());
|
||||||
|
return Err(err);
|
||||||
}
|
}
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
let err = TranspileError::IllegalIndexing(IllegalIndexing {
|
let err = TranspileError::IllegalIndexing(IllegalIndexing {
|
||||||
|
@ -621,7 +662,15 @@ impl Transpiler {
|
||||||
entity: s,
|
entity: s,
|
||||||
}),
|
}),
|
||||||
Some(ComptimeValue::MacroString(s)) => {
|
Some(ComptimeValue::MacroString(s)) => {
|
||||||
todo!("indexing tag with macro string: {s:?}")
|
// TODO: allow indexing tag with macro string
|
||||||
|
let err = TranspileError::IllegalIndexing(IllegalIndexing {
|
||||||
|
expression: expression.span(),
|
||||||
|
reason: IllegalIndexingReason::InvalidComptimeType {
|
||||||
|
expected: ExpectedType::String,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
handler.receive(err.clone());
|
||||||
|
return Err(err);
|
||||||
}
|
}
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
let err = TranspileError::IllegalIndexing(IllegalIndexing {
|
let err = TranspileError::IllegalIndexing(IllegalIndexing {
|
||||||
|
@ -643,6 +692,16 @@ impl Transpiler {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
VariableData::ComptimeValue { value } => {
|
||||||
|
let comptime_value =
|
||||||
|
expression.comptime_eval(scope, handler).map_err(|err| {
|
||||||
|
let err = TranspileError::NotComptime(err);
|
||||||
|
handler.receive(err.clone());
|
||||||
|
err
|
||||||
|
})?;
|
||||||
|
*value.write().unwrap() = Some(comptime_value);
|
||||||
|
return Ok(Vec::new());
|
||||||
|
}
|
||||||
VariableData::Function { .. }
|
VariableData::Function { .. }
|
||||||
| VariableData::MacroParameter { .. }
|
| VariableData::MacroParameter { .. }
|
||||||
| VariableData::InternalFunction { .. } => {
|
| VariableData::InternalFunction { .. } => {
|
||||||
|
@ -704,6 +763,7 @@ impl Transpiler {
|
||||||
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()
|
||||||
.and_then(|val| val.to_string_no_macro())
|
.and_then(|val| val.to_string_no_macro())
|
||||||
{
|
{
|
||||||
// TODO: change invalid criteria if boolean
|
// TODO: change invalid criteria if boolean
|
||||||
|
@ -802,9 +862,11 @@ impl Transpiler {
|
||||||
if let (Some(name_eval), Some(target_eval)) = (
|
if let (Some(name_eval), Some(target_eval)) = (
|
||||||
objective
|
objective
|
||||||
.comptime_eval(scope, handler)
|
.comptime_eval(scope, handler)
|
||||||
|
.ok()
|
||||||
.and_then(|val| val.to_string_no_macro()),
|
.and_then(|val| val.to_string_no_macro()),
|
||||||
target
|
target
|
||||||
.comptime_eval(scope, handler)
|
.comptime_eval(scope, handler)
|
||||||
|
.ok()
|
||||||
.and_then(|val| val.to_string_no_macro()),
|
.and_then(|val| val.to_string_no_macro()),
|
||||||
) {
|
) {
|
||||||
// TODO: change invalid criteria if boolean
|
// TODO: change invalid criteria if boolean
|
||||||
|
@ -931,7 +993,15 @@ impl Transpiler {
|
||||||
Ok((name, targets))
|
Ok((name, targets))
|
||||||
}
|
}
|
||||||
TranspileAnnotationValue::Map(map) => {
|
TranspileAnnotationValue::Map(map) => {
|
||||||
todo!("allow map deobfuscate annotation for array variables")
|
// TODO: implement when map deobfuscate annotation is implemented
|
||||||
|
let error =
|
||||||
|
TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
||||||
|
annotation: deobfuscate_annotation.span(),
|
||||||
|
message: "Deobfuscate annotation value must be a string or none."
|
||||||
|
.to_string(),
|
||||||
|
});
|
||||||
|
handler.receive(error.clone());
|
||||||
|
Err(error)
|
||||||
}
|
}
|
||||||
TranspileAnnotationValue::Expression(_) => {
|
TranspileAnnotationValue::Expression(_) => {
|
||||||
let error =
|
let error =
|
||||||
|
|
|
@ -107,7 +107,7 @@ pub fn identifier_to_scoreboard_target(ident: &str) -> std::borrow::Cow<str> {
|
||||||
let new_ident = ident
|
let new_ident = ident
|
||||||
.chars()
|
.chars()
|
||||||
.map(|c| {
|
.map(|c| {
|
||||||
if *c != '_' && !c.is_ascii_alphanumeric() {
|
if c != '_' && !c.is_ascii_alphanumeric() {
|
||||||
'_'
|
'_'
|
||||||
} else {
|
} else {
|
||||||
c
|
c
|
||||||
|
|
Loading…
Reference in New Issue