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
|
||||
|
||||
- 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`
|
||||
|
||||
### Removed
|
||||
|
|
|
@ -48,11 +48,11 @@ pub enum KeywordKind {
|
|||
From,
|
||||
Import,
|
||||
Tag,
|
||||
Of,
|
||||
Replace,
|
||||
Int,
|
||||
Bool,
|
||||
Macro,
|
||||
Val,
|
||||
}
|
||||
|
||||
impl Display for KeywordKind {
|
||||
|
@ -114,11 +114,11 @@ impl KeywordKind {
|
|||
Self::From => "from",
|
||||
Self::Import => "import",
|
||||
Self::Tag => "tag",
|
||||
Self::Of => "of",
|
||||
Self::Replace => "replace",
|
||||
Self::Int => "int",
|
||||
Self::Bool => "bool",
|
||||
Self::Macro => "macro",
|
||||
Self::Val => "val",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,10 +2,7 @@
|
|||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use std::{collections::HashSet, fmt::Display};
|
||||
|
||||
use getset::Getters;
|
||||
use itertools::Itertools as _;
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::{
|
||||
base::{
|
||||
|
@ -14,6 +11,10 @@ use crate::{
|
|||
},
|
||||
lexical::token::StringLiteral,
|
||||
syntax::syntax_tree::expression::Expression,
|
||||
transpile::error::{
|
||||
AssignmentError, IllegalIndexing, MismatchedTypes, MissingFunctionDeclaration,
|
||||
UnknownIdentifier,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)]
|
||||
|
@ -31,77 +32,20 @@ pub enum Error {
|
|||
UnresolvedMacroUsage(#[from] UnresolvedMacroUsage),
|
||||
#[error(transparent)]
|
||||
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.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct UnexpectedExpression(pub Expression);
|
||||
|
|
1058
src/semantic/mod.rs
1058
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
|
||||
/// TagDeclaration:
|
||||
/// 'tag' StringLiteral ('of' StringLiteral)? 'replace'? '[' (StringLiteral (',' StringLiteral)*)? ']'
|
||||
/// 'tag' ('<' StringLiteral '>')? StringLiteral 'replace'? '[' (StringLiteral (',' StringLiteral)*)? ']'
|
||||
/// ;
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
|
@ -262,9 +262,9 @@ pub struct Tag {
|
|||
#[get = "pub"]
|
||||
tag_keyword: Keyword,
|
||||
#[get = "pub"]
|
||||
name: StringLiteral,
|
||||
of_type: Option<(Punctuation, StringLiteral, Punctuation)>,
|
||||
#[get = "pub"]
|
||||
of_type: Option<(Keyword, StringLiteral)>,
|
||||
name: StringLiteral,
|
||||
#[get = "pub"]
|
||||
replace: Option<Keyword>,
|
||||
#[get = "pub"]
|
||||
|
@ -278,15 +278,15 @@ impl Tag {
|
|||
self,
|
||||
) -> (
|
||||
Keyword,
|
||||
Option<(Punctuation, StringLiteral, Punctuation)>,
|
||||
StringLiteral,
|
||||
Option<(Keyword, StringLiteral)>,
|
||||
Option<Keyword>,
|
||||
DelimitedList<StringLiteral>,
|
||||
) {
|
||||
(
|
||||
self.tag_keyword,
|
||||
self.name,
|
||||
self.of_type,
|
||||
self.name,
|
||||
self.replace,
|
||||
self.entries,
|
||||
)
|
||||
|
@ -299,7 +299,7 @@ impl Tag {
|
|||
|
||||
self.of_type
|
||||
.as_ref()
|
||||
.map_or(TagType::Function, |(_, tag_type)| {
|
||||
.map_or(TagType::Function, |(_, tag_type, _)| {
|
||||
match tag_type.str_content().as_ref() {
|
||||
"function" => TagType::Function,
|
||||
"block" => TagType::Block,
|
||||
|
@ -419,18 +419,24 @@ impl Parser<'_> {
|
|||
// eat the tag keyword
|
||||
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
|
||||
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
|
||||
.try_parse(|parser| parser.parse_keyword(KeywordKind::Replace, &VoidHandler))
|
||||
.ok();
|
||||
|
@ -444,8 +450,8 @@ impl Parser<'_> {
|
|||
|
||||
Ok(Declaration::Tag(Tag {
|
||||
tag_keyword,
|
||||
name,
|
||||
of_type,
|
||||
name,
|
||||
replace,
|
||||
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.
|
||||
///
|
||||
/// Syntax Synopsis:
|
||||
|
|
|
@ -325,6 +325,7 @@ pub enum VariableDeclaration {
|
|||
Array(ArrayVariableDeclaration),
|
||||
Score(ScoreVariableDeclaration),
|
||||
Tag(TagVariableDeclaration),
|
||||
ComptimeValue(ComptimeValueDeclaration),
|
||||
}
|
||||
|
||||
impl SourceElement for VariableDeclaration {
|
||||
|
@ -334,6 +335,7 @@ impl SourceElement for VariableDeclaration {
|
|||
Self::Array(declaration) => declaration.span(),
|
||||
Self::Score(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::Score(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::Score(declaration) => &declaration.int_keyword,
|
||||
Self::Tag(declaration) => &declaration.bool_keyword,
|
||||
Self::ComptimeValue(declaration) => &declaration.val_keyword,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -383,6 +387,14 @@ impl VariableDeclaration {
|
|||
declaration.annotations.push_front(annotation);
|
||||
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.
|
||||
///
|
||||
/// Syntax Synopsis:
|
||||
|
@ -883,7 +942,10 @@ impl Parser<'_> {
|
|||
) -> ParseResult<Semicolon> {
|
||||
let statement = match self.stop_at_significant() {
|
||||
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)
|
||||
.map(SemicolonStatement::VariableDeclaration)
|
||||
|
@ -895,18 +957,20 @@ impl Parser<'_> {
|
|||
let destination = {
|
||||
let identifier = p.parse_identifier(&VoidHandler)?;
|
||||
|
||||
if let Ok(tree) = p.step_into(
|
||||
Delimiter::Bracket,
|
||||
|pp| pp.parse_expression(&VoidHandler),
|
||||
&VoidHandler,
|
||||
) {
|
||||
let open = tree.open;
|
||||
let close = tree.close;
|
||||
let expression = tree.tree?;
|
||||
match p.stop_at_significant() {
|
||||
Reading::IntoDelimited(punc) if punc.punctuation == '[' => {
|
||||
let tree = p.step_into(
|
||||
Delimiter::Bracket,
|
||||
|pp| pp.parse_expression(&VoidHandler),
|
||||
&VoidHandler,
|
||||
)?;
|
||||
let open = tree.open;
|
||||
let close = tree.close;
|
||||
let expression = tree.tree?;
|
||||
|
||||
AssignmentDestination::Indexed(identifier, open, expression, close)
|
||||
} else {
|
||||
AssignmentDestination::Identifier(identifier)
|
||||
AssignmentDestination::Indexed(identifier, open, expression, close)
|
||||
}
|
||||
_ => AssignmentDestination::Identifier(identifier),
|
||||
}
|
||||
};
|
||||
let equals = p.parse_punctuation('=', true, &VoidHandler)?;
|
||||
|
@ -948,7 +1012,10 @@ impl Parser<'_> {
|
|||
|
||||
let variable_type = match self.stop_at_significant() {
|
||||
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();
|
||||
keyword
|
||||
|
@ -983,7 +1050,10 @@ impl Parser<'_> {
|
|||
let identifier = self.parse_identifier(handler)?;
|
||||
|
||||
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(
|
||||
Delimiter::Bracket,
|
||||
|p| {
|
||||
|
@ -1071,12 +1141,23 @@ impl Parser<'_> {
|
|||
let expression = self.parse_expression(handler)?;
|
||||
let assignment = VariableDeclarationAssignment { equals, expression };
|
||||
|
||||
Ok(VariableDeclaration::Single(SingleVariableDeclaration {
|
||||
variable_type,
|
||||
identifier,
|
||||
assignment: Some(assignment),
|
||||
annotations: VecDeque::new(),
|
||||
}))
|
||||
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 {
|
||||
variable_type,
|
||||
identifier,
|
||||
assignment: Some(assignment),
|
||||
annotations: VecDeque::new(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
// SingleVariableDeclaration without Assignment
|
||||
_ => Ok(VariableDeclaration::Single(SingleVariableDeclaration {
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
//! Execute block statement syntax tree.
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use derive_more::From;
|
||||
use enum_as_inner::EnumAsInner;
|
||||
use getset::Getters;
|
||||
|
@ -998,10 +996,10 @@ pub trait ExecuteBlockHeadItem {
|
|||
#[expect(clippy::missing_errors_doc)]
|
||||
fn analyze_semantics(
|
||||
&self,
|
||||
macro_names: &HashSet<String>,
|
||||
scope: &crate::semantic::SemanticScope,
|
||||
handler: &impl Handler<base::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),
|
||||
#[error(transparent)]
|
||||
IllegalIndexing(#[from] IllegalIndexing),
|
||||
#[error(transparent)]
|
||||
InvalidArgument(#[from] InvalidArgument),
|
||||
#[error(transparent)]
|
||||
NotComptime(#[from] NotComptime),
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Getters)]
|
||||
pub struct MissingFunctionDeclaration {
|
||||
/// The span of the identifier that is missing.
|
||||
#[get = "pub"]
|
||||
span: Span,
|
||||
/// Possible alternatives for the missing function declaration.
|
||||
#[get = "pub"]
|
||||
alternatives: Vec<FunctionData>,
|
||||
}
|
||||
|
@ -127,11 +133,26 @@ impl Display 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.
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct LuaRuntimeError {
|
||||
/// The span of the code block that caused the error.
|
||||
pub code_block: Span,
|
||||
/// The error message of the Lua runtime.
|
||||
pub error_message: String,
|
||||
}
|
||||
|
||||
|
@ -155,6 +176,8 @@ impl std::error::Error for LuaRuntimeError {}
|
|||
|
||||
#[cfg(feature = "lua")]
|
||||
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 {
|
||||
let err_string = err.to_string();
|
||||
Self {
|
||||
|
@ -168,9 +191,13 @@ impl LuaRuntimeError {
|
|||
}
|
||||
|
||||
/// 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 {
|
||||
/// The span of the annotation.
|
||||
#[get = "pub"]
|
||||
pub annotation: Span,
|
||||
/// The error message.
|
||||
#[get = "pub"]
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
|
@ -194,9 +221,13 @@ impl Display for IllegalAnnotationContent {
|
|||
impl std::error::Error for IllegalAnnotationContent {}
|
||||
|
||||
/// 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 {
|
||||
/// The expression that can not evaluate to the wanted type.
|
||||
#[get = "pub"]
|
||||
pub expression: Span,
|
||||
/// The expected type.
|
||||
#[get = "pub"]
|
||||
pub expected_type: ExpectedType,
|
||||
}
|
||||
|
||||
|
@ -216,9 +247,13 @@ impl Display for MismatchedTypes {
|
|||
impl std::error::Error for MismatchedTypes {}
|
||||
|
||||
/// 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 {
|
||||
/// The arguments that are not allowed.
|
||||
#[get = "pub"]
|
||||
pub arguments: Span,
|
||||
/// The error message.
|
||||
#[get = "pub"]
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
|
@ -237,9 +272,13 @@ impl Display for FunctionArgumentsNotAllowed {
|
|||
impl std::error::Error for FunctionArgumentsNotAllowed {}
|
||||
|
||||
/// 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 {
|
||||
/// The identifier that is assigned to.
|
||||
#[get = "pub"]
|
||||
pub identifier: Span,
|
||||
/// The error message.
|
||||
#[get = "pub"]
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
|
@ -258,8 +297,10 @@ impl Display for AssignmentError {
|
|||
impl std::error::Error for AssignmentError {}
|
||||
|
||||
/// 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 {
|
||||
/// The unknown identifier.
|
||||
#[get = "pub"]
|
||||
pub identifier: Span,
|
||||
}
|
||||
|
||||
|
@ -285,8 +326,10 @@ impl Display for UnknownIdentifier {
|
|||
impl std::error::Error for UnknownIdentifier {}
|
||||
|
||||
/// 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 {
|
||||
/// The expression that is missing a value.
|
||||
#[get = "pub"]
|
||||
pub expression: Span,
|
||||
}
|
||||
|
||||
|
@ -312,9 +355,13 @@ impl Display for MissingValue {
|
|||
impl std::error::Error for MissingValue {}
|
||||
|
||||
/// 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 {
|
||||
/// The reason why the indexing operation is not permitted.
|
||||
#[get = "pub"]
|
||||
pub reason: IllegalIndexingReason,
|
||||
/// The expression that is the reason for the indexing being illegal.
|
||||
#[get = "pub"]
|
||||
pub expression: Span,
|
||||
}
|
||||
|
||||
|
@ -333,11 +380,24 @@ impl Display for IllegalIndexing {
|
|||
impl std::error::Error for IllegalIndexing {}
|
||||
|
||||
/// The reason why an indexing operation is not permitted.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum IllegalIndexingReason {
|
||||
/// The expression is not an identifier.
|
||||
NotIdentifier,
|
||||
InvalidComptimeType { expected: ExpectedType },
|
||||
IndexOutOfBounds { index: usize, length: usize },
|
||||
/// The expression cannot be indexed.
|
||||
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 {
|
||||
|
@ -346,6 +406,9 @@ impl Display for IllegalIndexingReason {
|
|||
Self::NotIdentifier => {
|
||||
write!(f, "The expression is not an identifier.")
|
||||
}
|
||||
Self::NotIndexable => {
|
||||
write!(f, "The expression cannot be indexed.")
|
||||
}
|
||||
Self::InvalidComptimeType { expected } => {
|
||||
write!(
|
||||
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.
|
||||
|
||||
use std::{fmt::Display, string::ToString, sync::Arc};
|
||||
use std::{fmt::Display, string::ToString};
|
||||
|
||||
use super::{util::MacroString, Scope, VariableData};
|
||||
use crate::{
|
||||
base::{self, Handler, VoidHandler},
|
||||
lexical::token::MacroStringLiteralPart,
|
||||
syntax::syntax_tree::expression::{
|
||||
Binary, BinaryOperator, Expression, PrefixOperator, Primary,
|
||||
},
|
||||
};
|
||||
use super::util::MacroString;
|
||||
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
use enum_as_inner::EnumAsInner;
|
||||
|
@ -19,17 +12,25 @@ use shulkerbox::prelude::{Command, Condition, Execute};
|
|||
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
use super::{
|
||||
error::{IllegalIndexing, IllegalIndexingReason, MismatchedTypes, UnknownIdentifier},
|
||||
TranspileResult, Transpiler,
|
||||
error::{
|
||||
IllegalIndexing, IllegalIndexingReason, MismatchedTypes, NotComptime, UnknownIdentifier,
|
||||
},
|
||||
Scope, TranspileResult, Transpiler, VariableData,
|
||||
};
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
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::{
|
||||
error::{FunctionArgumentsNotAllowed, MissingValue},
|
||||
TranspileError,
|
||||
},
|
||||
};
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Compile-time evaluated value
|
||||
#[allow(missing_docs)]
|
||||
|
@ -85,7 +86,7 @@ impl Display for ValueType {
|
|||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum ExpectedType {
|
||||
Boolean,
|
||||
Integer,
|
||||
|
@ -227,6 +228,7 @@ pub enum ExtendedCondition {
|
|||
Comptime(bool),
|
||||
}
|
||||
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
impl Expression {
|
||||
/// Returns whether the expression can yield a certain type.
|
||||
#[must_use]
|
||||
|
@ -238,12 +240,14 @@ impl Expression {
|
|||
}
|
||||
|
||||
/// Evaluate at compile-time.
|
||||
#[must_use]
|
||||
///
|
||||
/// # Errors
|
||||
/// - If the expression is not compile-time evaluatable.
|
||||
pub fn comptime_eval(
|
||||
&self,
|
||||
scope: &Arc<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> Option<ComptimeValue> {
|
||||
) -> Result<ComptimeValue, NotComptime> {
|
||||
match self {
|
||||
Self::Primary(primary) => primary.comptime_eval(scope, handler),
|
||||
Self::Binary(binary) => binary.comptime_eval(scope, handler),
|
||||
|
@ -251,6 +255,7 @@ impl Expression {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
impl Primary {
|
||||
/// Returns whether the primary can yield a certain type.
|
||||
#[must_use]
|
||||
|
@ -332,19 +337,40 @@ impl Primary {
|
|||
}
|
||||
|
||||
/// Evaluate at compile-time.
|
||||
#[must_use]
|
||||
///
|
||||
/// # Errors
|
||||
/// - If the expression is not compile-time evaluatable.
|
||||
pub fn comptime_eval(
|
||||
&self,
|
||||
scope: &Arc<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> Option<ComptimeValue> {
|
||||
) -> Result<ComptimeValue, NotComptime> {
|
||||
match self {
|
||||
Self::Boolean(boolean) => Some(ComptimeValue::Boolean(boolean.value())),
|
||||
Self::Integer(int) => Some(ComptimeValue::Integer(int.as_i64())),
|
||||
Self::StringLiteral(string_literal) => Some(ComptimeValue::String(
|
||||
Self::Boolean(boolean) => Ok(ComptimeValue::Boolean(boolean.value())),
|
||||
Self::Integer(int) => Ok(ComptimeValue::Integer(int.as_i64())),
|
||||
Self::StringLiteral(string_literal) => Ok(ComptimeValue::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) => {
|
||||
parenthesized.expression().comptime_eval(scope, handler)
|
||||
}
|
||||
|
@ -354,12 +380,14 @@ impl Primary {
|
|||
.comptime_eval(scope, handler)
|
||||
.and_then(|val| match (prefix.operator(), val) {
|
||||
(PrefixOperator::LogicalNot(_), ComptimeValue::Boolean(boolean)) => {
|
||||
Some(ComptimeValue::Boolean(!boolean))
|
||||
Ok(ComptimeValue::Boolean(!boolean))
|
||||
}
|
||||
(PrefixOperator::Negate(_), ComptimeValue::Integer(int)) => {
|
||||
Some(ComptimeValue::Integer(-int))
|
||||
Ok(ComptimeValue::Integer(-int))
|
||||
}
|
||||
_ => None,
|
||||
_ => Err(NotComptime {
|
||||
expression: prefix.span(),
|
||||
}),
|
||||
})
|
||||
}
|
||||
Self::Lua(lua) => lua
|
||||
|
@ -367,25 +395,28 @@ impl Primary {
|
|||
.inspect_err(|err| {
|
||||
handler.receive(err.clone());
|
||||
})
|
||||
.ok()
|
||||
.flatten(),
|
||||
.map_err(|_| NotComptime {
|
||||
expression: lua.span(),
|
||||
})
|
||||
.and_then(|val| val),
|
||||
Self::MacroStringLiteral(macro_string_literal) => {
|
||||
if macro_string_literal
|
||||
.parts()
|
||||
.iter()
|
||||
.any(|part| matches!(part, MacroStringLiteralPart::MacroUsage { .. }))
|
||||
{
|
||||
Some(ComptimeValue::MacroString(
|
||||
Ok(ComptimeValue::MacroString(
|
||||
macro_string_literal.clone().into(),
|
||||
))
|
||||
} else {
|
||||
Some(ComptimeValue::String(macro_string_literal.str_content()))
|
||||
Ok(ComptimeValue::String(macro_string_literal.str_content()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
impl Binary {
|
||||
/// Returns whether the binary can yield a certain type.
|
||||
#[must_use]
|
||||
|
@ -428,12 +459,14 @@ impl Binary {
|
|||
}
|
||||
|
||||
/// Evaluate at compile-time.
|
||||
#[must_use]
|
||||
///
|
||||
/// # Errors
|
||||
/// - If the expression is not compile-time evaluatable.
|
||||
pub fn comptime_eval(
|
||||
&self,
|
||||
scope: &Arc<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> Option<ComptimeValue> {
|
||||
) -> Result<ComptimeValue, NotComptime> {
|
||||
let left = self.left_operand().comptime_eval(scope, handler)?;
|
||||
let right = self.right_operand().comptime_eval(scope, handler)?;
|
||||
|
||||
|
@ -447,7 +480,7 @@ impl Binary {
|
|||
.right_operand()
|
||||
.can_yield_type(ValueType::Boolean, scope) =>
|
||||
{
|
||||
Some(ComptimeValue::Boolean(true))
|
||||
Ok(ComptimeValue::Boolean(true))
|
||||
}
|
||||
(ComptimeValue::Boolean(false), _) | (_, ComptimeValue::Boolean(false))
|
||||
if matches!(self.operator(), BinaryOperator::LogicalAnd(..))
|
||||
|
@ -458,15 +491,17 @@ impl Binary {
|
|||
.right_operand()
|
||||
.can_yield_type(ValueType::Boolean, scope) =>
|
||||
{
|
||||
Some(ComptimeValue::Boolean(false))
|
||||
Ok(ComptimeValue::Boolean(false))
|
||||
}
|
||||
(ComptimeValue::Boolean(left), ComptimeValue::Boolean(right)) => {
|
||||
match self.operator() {
|
||||
BinaryOperator::Equal(..) => Some(ComptimeValue::Boolean(left == right)),
|
||||
BinaryOperator::NotEqual(..) => Some(ComptimeValue::Boolean(left != right)),
|
||||
BinaryOperator::LogicalAnd(..) => Some(ComptimeValue::Boolean(left && right)),
|
||||
BinaryOperator::LogicalOr(..) => Some(ComptimeValue::Boolean(left || right)),
|
||||
_ => None,
|
||||
BinaryOperator::Equal(..) => Ok(ComptimeValue::Boolean(left == right)),
|
||||
BinaryOperator::NotEqual(..) => Ok(ComptimeValue::Boolean(left != right)),
|
||||
BinaryOperator::LogicalAnd(..) => Ok(ComptimeValue::Boolean(left && right)),
|
||||
BinaryOperator::LogicalOr(..) => Ok(ComptimeValue::Boolean(left || right)),
|
||||
_ => Err(NotComptime {
|
||||
expression: self.span(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
(ComptimeValue::Integer(left), ComptimeValue::Integer(right)) => {
|
||||
|
@ -496,14 +531,37 @@ impl Binary {
|
|||
}
|
||||
_ => None,
|
||||
}
|
||||
.ok_or_else(|| NotComptime {
|
||||
expression: self.span(),
|
||||
})
|
||||
}
|
||||
(ComptimeValue::String(left), ComptimeValue::String(right)) => match self.operator() {
|
||||
BinaryOperator::Add(..) => Some(ComptimeValue::String(left + &right)),
|
||||
BinaryOperator::Equal(..) => Some(ComptimeValue::Boolean(left == right)),
|
||||
BinaryOperator::NotEqual(..) => Some(ComptimeValue::Boolean(left != right)),
|
||||
_ => None,
|
||||
BinaryOperator::Add(..) => Ok(ComptimeValue::String(left + &right)),
|
||||
BinaryOperator::Equal(..) => Ok(ComptimeValue::Boolean(left == right)),
|
||||
BinaryOperator::NotEqual(..) => Ok(ComptimeValue::Boolean(left != right)),
|
||||
_ => 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) =>
|
||||
{
|
||||
#[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)
|
||||
} else {
|
||||
let err = TranspileError::MissingValue(MissingValue {
|
||||
|
@ -801,7 +859,7 @@ impl Transpiler {
|
|||
if let Some(variable) = variable.as_deref() {
|
||||
let from = match variable {
|
||||
VariableData::Scoreboard { objective } => {
|
||||
if let Some(ComptimeValue::String(target)) =
|
||||
if let Ok(ComptimeValue::String(target)) =
|
||||
indexed.index().comptime_eval(scope, handler)
|
||||
{
|
||||
Ok(DataLocation::ScoreboardValue {
|
||||
|
@ -820,7 +878,7 @@ impl Transpiler {
|
|||
}
|
||||
}
|
||||
VariableData::ScoreboardArray { objective, targets } => {
|
||||
if let Some(ComptimeValue::Integer(index)) =
|
||||
if let Ok(ComptimeValue::Integer(index)) =
|
||||
indexed.index().comptime_eval(scope, handler)
|
||||
{
|
||||
if let Some(target) = usize::try_from(index)
|
||||
|
@ -858,7 +916,7 @@ impl Transpiler {
|
|||
storage_name,
|
||||
paths,
|
||||
} => {
|
||||
if let Some(ComptimeValue::Integer(index)) =
|
||||
if let Ok(ComptimeValue::Integer(index)) =
|
||||
indexed.index().comptime_eval(scope, handler)
|
||||
{
|
||||
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,
|
||||
binary: &Binary,
|
||||
target: &DataLocation,
|
||||
scope: &Arc<super::Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> 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)
|
||||
} else {
|
||||
match binary.operator() {
|
||||
|
@ -1085,7 +1143,7 @@ impl Transpiler {
|
|||
storage_name,
|
||||
paths,
|
||||
} => {
|
||||
if let Some(ComptimeValue::Integer(index)) =
|
||||
if let Ok(ComptimeValue::Integer(index)) =
|
||||
indexed.index().comptime_eval(scope, handler)
|
||||
{
|
||||
if let Some(path) = usize::try_from(index)
|
||||
|
@ -1166,15 +1224,15 @@ impl Transpiler {
|
|||
}
|
||||
},
|
||||
Primary::Lua(lua) => match lua.eval_comptime(scope, handler)? {
|
||||
Some(ComptimeValue::String(value)) => Ok((
|
||||
Ok(ComptimeValue::String(value)) => Ok((
|
||||
Vec::new(),
|
||||
ExtendedCondition::Runtime(Condition::Atom(value.into())),
|
||||
)),
|
||||
Some(ComptimeValue::MacroString(value)) => Ok((
|
||||
Ok(ComptimeValue::MacroString(value)) => Ok((
|
||||
Vec::new(),
|
||||
ExtendedCondition::Runtime(Condition::Atom(value.into())),
|
||||
)),
|
||||
Some(ComptimeValue::Boolean(boolean)) => {
|
||||
Ok(ComptimeValue::Boolean(boolean)) => {
|
||||
Ok((Vec::new(), ExtendedCondition::Comptime(boolean)))
|
||||
}
|
||||
_ => {
|
||||
|
|
|
@ -102,6 +102,7 @@ impl Transpiler {
|
|||
TranspileAnnotationValue::None => Ok(identifier_span.str().to_string()),
|
||||
TranspileAnnotationValue::Expression(expr) => expr
|
||||
.comptime_eval(scope, handler)
|
||||
.ok()
|
||||
.and_then(|val| val.to_string_no_macro())
|
||||
.ok_or_else(|| {
|
||||
let err = TranspileError::IllegalAnnotationContent(
|
||||
|
@ -293,13 +294,10 @@ impl Transpiler {
|
|||
let value = match expression {
|
||||
Expression::Primary(Primary::Lua(lua)) => {
|
||||
lua.eval_comptime(scope, handler).and_then(|val| match val {
|
||||
Some(ComptimeValue::MacroString(s)) => Ok(Parameter::Static(s)),
|
||||
Some(val) => Ok(Parameter::Static(val.to_macro_string())),
|
||||
None => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expression: expression.span(),
|
||||
expected_type: ExpectedType::String,
|
||||
});
|
||||
Ok(ComptimeValue::MacroString(s)) => Ok(Parameter::Static(s)),
|
||||
Ok(val) => Ok(Parameter::Static(val.to_macro_string())),
|
||||
Err(err) => {
|
||||
let err = TranspileError::NotComptime(err);
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
}
|
||||
|
@ -361,7 +359,18 @@ impl Transpiler {
|
|||
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(
|
||||
|
|
|
@ -5,6 +5,7 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
|
||||
use cfg_if::cfg_if;
|
||||
use shulkerbox::prelude::{Command, Execute};
|
||||
|
||||
use serde_json::{json, Value as JsonValue};
|
||||
|
@ -15,8 +16,8 @@ use crate::{
|
|||
semantic::error::{InvalidFunctionArguments, UnexpectedExpression},
|
||||
syntax::syntax_tree::expression::{Expression, FunctionCall, Primary},
|
||||
transpile::{
|
||||
error::{IllegalIndexing, IllegalIndexingReason, LuaRuntimeError, UnknownIdentifier},
|
||||
expression::{ComptimeValue, DataLocation, StorageType},
|
||||
error::{IllegalIndexing, IllegalIndexingReason, UnknownIdentifier},
|
||||
expression::{ComptimeValue, DataLocation, ExpectedType, StorageType},
|
||||
util::MacroString,
|
||||
TranspileError,
|
||||
},
|
||||
|
@ -182,17 +183,17 @@ fn print_function(
|
|||
|
||||
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 (target, message_expression) = if let Some(second) = args.get(1) {
|
||||
(
|
||||
first
|
||||
.comptime_eval(scope, &VoidHandler)
|
||||
.map(|val| val.to_macro_string())
|
||||
.map_err(TranspileError::NotComptime)?,
|
||||
second,
|
||||
)
|
||||
} else {
|
||||
("@a".into(), first)
|
||||
};
|
||||
|
||||
let mut contains_macro = matches!(target, MacroString::MacroString(_));
|
||||
|
||||
|
@ -210,17 +211,24 @@ fn print_function(
|
|||
Vec::new(),
|
||||
vec![JsonValue::String(string.str_content().to_string())],
|
||||
)),
|
||||
#[cfg_attr(not(feature = "lua"), expect(unused_variables))]
|
||||
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(),
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "lua")] {
|
||||
let (ret, _lua) = lua.eval(scope, &VoidHandler)?;
|
||||
Ok((
|
||||
Vec::new(),
|
||||
vec![JsonValue::String(ret.to_string().map_err(|err| {
|
||||
TranspileError::LuaRuntimeError(super::error::LuaRuntimeError::from_lua_err(
|
||||
&err,
|
||||
lua.span(),
|
||||
))
|
||||
})?)],
|
||||
))
|
||||
})?)],
|
||||
))
|
||||
} else {
|
||||
Err(TranspileError::LuaDisabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
Primary::Identifier(ident) => {
|
||||
let (cur_contains_macro, cmd, part) =
|
||||
|
@ -232,7 +240,7 @@ fn print_function(
|
|||
Primary::Identifier(ident) => {
|
||||
match scope.get_variable(ident.span.str()).as_deref() {
|
||||
Some(VariableData::Scoreboard { objective }) => {
|
||||
if let Some(ComptimeValue::String(index)) =
|
||||
if let Ok(ComptimeValue::String(index)) =
|
||||
indexed.index().comptime_eval(scope, &VoidHandler)
|
||||
{
|
||||
let (cmd, value) = get_data_location(
|
||||
|
@ -244,11 +252,17 @@ fn print_function(
|
|||
);
|
||||
Ok((cmd.into_iter().collect(), vec![value]))
|
||||
} 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 }) => {
|
||||
if let Some(ComptimeValue::Integer(index)) =
|
||||
if let Ok(ComptimeValue::Integer(index)) =
|
||||
indexed.index().comptime_eval(scope, &VoidHandler)
|
||||
{
|
||||
#[expect(clippy::option_if_let_else)]
|
||||
|
@ -274,14 +288,19 @@ fn print_function(
|
|||
}))
|
||||
}
|
||||
} 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 {
|
||||
storage_name,
|
||||
paths,
|
||||
}) => {
|
||||
if let Some(ComptimeValue::Integer(index)) =
|
||||
if let Ok(ComptimeValue::Integer(index)) =
|
||||
indexed.index().comptime_eval(scope, &VoidHandler)
|
||||
{
|
||||
#[expect(clippy::option_if_let_else)]
|
||||
|
@ -308,10 +327,18 @@ fn print_function(
|
|||
}))
|
||||
}
|
||||
} 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 {
|
||||
|
@ -339,9 +366,43 @@ fn print_function(
|
|||
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
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Executes the Lua code and returns the resulting command.
|
||||
|
||||
#[cfg(feature = "lua")]
|
||||
#[cfg(all(feature = "lua", feature = "shulkerbox"))]
|
||||
mod enabled {
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -12,8 +12,8 @@ mod enabled {
|
|||
syntax::syntax_tree::expression::LuaCode,
|
||||
transpile::{
|
||||
error::{
|
||||
LuaRuntimeError, MismatchedTypes, TranspileError, TranspileResult,
|
||||
UnknownIdentifier,
|
||||
InvalidArgument, LuaRuntimeError, MismatchedTypes, NotComptime, TranspileError,
|
||||
TranspileResult, UnknownIdentifier,
|
||||
},
|
||||
expression::{ComptimeValue, ExpectedType},
|
||||
Scope, VariableData,
|
||||
|
@ -86,11 +86,15 @@ mod enabled {
|
|||
&self,
|
||||
scope: &Arc<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<Option<ComptimeValue>> {
|
||||
) -> TranspileResult<Result<ComptimeValue, NotComptime>> {
|
||||
// required to keep the lua instance alive
|
||||
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<()> {
|
||||
|
@ -262,8 +266,28 @@ mod enabled {
|
|||
.map_err(|err| LuaRuntimeError::from_lua_err(&err, self.span()))?;
|
||||
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 { .. }) => {
|
||||
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 => {
|
||||
return Err(TranspileError::UnknownIdentifier(UnknownIdentifier {
|
||||
|
@ -374,7 +398,7 @@ mod enabled {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "lua"))]
|
||||
#[cfg(all(not(feature = "lua"), feature = "shulkerbox"))]
|
||||
mod disabled {
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -397,7 +421,7 @@ mod disabled {
|
|||
&self,
|
||||
scope: &Arc<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<()> {
|
||||
) -> TranspileResult<((), ())> {
|
||||
let _ = scope;
|
||||
handler.receive(TranspileError::LuaDisabled);
|
||||
tracing::error!("Lua code evaluation is disabled");
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::{
|
|||
#[doc(hidden)]
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
pub mod conversions;
|
||||
mod error;
|
||||
pub mod error;
|
||||
|
||||
pub mod expression;
|
||||
|
||||
|
@ -38,9 +38,12 @@ pub mod internal_functions;
|
|||
#[doc(hidden)]
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
pub mod function;
|
||||
#[doc(inline)]
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
pub use function::TranspiledFunctionArguments;
|
||||
|
||||
mod variables;
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
pub use variables::{Scope, VariableData};
|
||||
|
||||
pub mod util;
|
||||
|
@ -58,7 +61,7 @@ pub struct FunctionData {
|
|||
|
||||
/// Possible values for an annotation.
|
||||
#[expect(clippy::module_name_repetitions)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, EnumIs)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, EnumIs)]
|
||||
pub enum TranspileAnnotationValue {
|
||||
/// No value.
|
||||
None,
|
||||
|
|
|
@ -25,7 +25,7 @@ use crate::{
|
|||
};
|
||||
|
||||
use super::{
|
||||
error::{MismatchedTypes, TranspileError, TranspileResult},
|
||||
error::{MismatchedTypes, MissingValue, TranspileError, TranspileResult, UnknownIdentifier},
|
||||
expression::{ComptimeValue, ExpectedType, ExtendedCondition},
|
||||
variables::{Scope, TranspileAssignmentTarget, VariableData},
|
||||
FunctionData, TranspileAnnotationValue, TranspiledFunctionArguments,
|
||||
|
@ -342,11 +342,46 @@ impl Transpiler {
|
|||
Expression::Primary(Primary::FunctionCall(func)) => {
|
||||
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(
|
||||
Primary::Integer(_)
|
||||
| Primary::Boolean(_)
|
||||
| Primary::Prefix(_)
|
||||
| Primary::Identifier(_)
|
||||
| Primary::Indexed(_),
|
||||
) => {
|
||||
let error =
|
||||
|
@ -361,9 +396,9 @@ impl Transpiler {
|
|||
Ok(vec![Command::UsesMacro(string.into())])
|
||||
}
|
||||
Expression::Primary(Primary::Lua(code)) => match code.eval_comptime(scope, handler)? {
|
||||
Some(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
||||
Some(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
|
||||
Some(ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)) => {
|
||||
Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
||||
Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
|
||||
Ok(ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)) => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expected_type: ExpectedType::String,
|
||||
expression: code.span(),
|
||||
|
@ -371,7 +406,13 @@ impl Transpiler {
|
|||
handler.receive(err.clone());
|
||||
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
|
||||
|
@ -382,8 +423,8 @@ impl Transpiler {
|
|||
handler,
|
||||
),
|
||||
Expression::Binary(bin) => match bin.comptime_eval(scope, handler) {
|
||||
Some(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
||||
Some(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
|
||||
Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
||||
Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
|
||||
_ => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
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 {
|
||||
Ok(Some((Vec::new(), then)))
|
||||
} else {
|
||||
|
|
|
@ -30,17 +30,17 @@ use crate::{
|
|||
use super::{
|
||||
error::{
|
||||
AssignmentError, IllegalAnnotationContent, IllegalIndexing, IllegalIndexingReason,
|
||||
MismatchedTypes,
|
||||
MismatchedTypes, NotComptime,
|
||||
},
|
||||
expression::{ComptimeValue, DataLocation, ExpectedType, StorageType},
|
||||
internal_functions::InternalFunction,
|
||||
FunctionData, TranspileAnnotationValue, TranspileError, TranspileResult,
|
||||
};
|
||||
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
use super::Transpiler;
|
||||
use super::{internal_functions::InternalFunction, Transpiler};
|
||||
|
||||
/// Stores the data required to access a variable.
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
#[derive(Debug, Clone, EnumAsInner)]
|
||||
pub enum VariableData {
|
||||
/// A function.
|
||||
|
@ -100,6 +100,11 @@ pub enum VariableData {
|
|||
/// The implementation
|
||||
implementation: InternalFunction,
|
||||
},
|
||||
/// Compiler internal variable.
|
||||
ComptimeValue {
|
||||
/// The value.
|
||||
value: Arc<RwLock<Option<ComptimeValue>>>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, EnumAsInner)]
|
||||
|
@ -122,6 +127,7 @@ impl<'a> From<&'a AssignmentDestination> for TranspileAssignmentTarget<'a> {
|
|||
}
|
||||
|
||||
/// A scope that stores variables.
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
#[derive(Default)]
|
||||
pub struct Scope<'a> {
|
||||
/// Parent scope where variables are inherited from.
|
||||
|
@ -132,6 +138,7 @@ pub struct Scope<'a> {
|
|||
shadowed: RwLock<HashMap<String, usize>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
impl<'a> Scope<'a> {
|
||||
/// Creates a new scope.
|
||||
#[must_use]
|
||||
|
@ -226,6 +233,7 @@ impl<'a> Scope<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
impl Debug for Scope<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
struct VariableWrapper<'a>(&'a RwLock<HashMap<String, Arc<VariableData>>>);
|
||||
|
@ -279,6 +287,31 @@ impl Transpiler {
|
|||
scope,
|
||||
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 {
|
||||
TranspileAssignmentTarget::Identifier(ident) => (ident, None),
|
||||
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()) {
|
||||
|
@ -489,7 +522,15 @@ impl Transpiler {
|
|||
target: 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(_) => {
|
||||
let err = TranspileError::IllegalIndexing(IllegalIndexing {
|
||||
|
@ -621,7 +662,15 @@ impl Transpiler {
|
|||
entity: 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(_) => {
|
||||
let err = TranspileError::IllegalIndexing(IllegalIndexing {
|
||||
|
@ -643,6 +692,16 @@ impl Transpiler {
|
|||
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::MacroParameter { .. }
|
||||
| VariableData::InternalFunction { .. } => {
|
||||
|
@ -704,6 +763,7 @@ impl Transpiler {
|
|||
TranspileAnnotationValue::Expression(expr) => {
|
||||
if let Some(name_eval) = expr
|
||||
.comptime_eval(scope, handler)
|
||||
.ok()
|
||||
.and_then(|val| val.to_string_no_macro())
|
||||
{
|
||||
// TODO: change invalid criteria if boolean
|
||||
|
@ -802,9 +862,11 @@ impl Transpiler {
|
|||
if let (Some(name_eval), Some(target_eval)) = (
|
||||
objective
|
||||
.comptime_eval(scope, handler)
|
||||
.ok()
|
||||
.and_then(|val| val.to_string_no_macro()),
|
||||
target
|
||||
.comptime_eval(scope, handler)
|
||||
.ok()
|
||||
.and_then(|val| val.to_string_no_macro()),
|
||||
) {
|
||||
// TODO: change invalid criteria if boolean
|
||||
|
@ -931,7 +993,15 @@ impl Transpiler {
|
|||
Ok((name, targets))
|
||||
}
|
||||
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(_) => {
|
||||
let error =
|
||||
|
|
|
@ -107,7 +107,7 @@ pub fn identifier_to_scoreboard_target(ident: &str) -> std::borrow::Cow<str> {
|
|||
let new_ident = ident
|
||||
.chars()
|
||||
.map(|c| {
|
||||
if *c != '_' && !c.is_ascii_alphanumeric() {
|
||||
if c != '_' && !c.is_ascii_alphanumeric() {
|
||||
'_'
|
||||
} else {
|
||||
c
|
||||
|
|
Loading…
Reference in New Issue