run as expression, implement return statement
This commit is contained in:
parent
68149d9ddf
commit
dd97937feb
|
@ -35,7 +35,7 @@ pathdiff = "0.2.3"
|
|||
serde = { version = "1.0.217", features = ["derive"], optional = true }
|
||||
serde_json = { version = "1.0.138", optional = true }
|
||||
# shulkerbox = { version = "0.1.0", default-features = false, optional = true }
|
||||
shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "7392887c127b1aae0d78d573a02332c2fa2591c8", default-features = false, optional = true }
|
||||
shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "5c4d340162fc16065add448ed387a1ce481c27d6", default-features = false, optional = true }
|
||||
strsim = "0.11.1"
|
||||
strum = { version = "0.27.0", features = ["derive"] }
|
||||
thiserror = "2.0.11"
|
||||
|
|
|
@ -53,6 +53,7 @@ pub enum KeywordKind {
|
|||
Bool,
|
||||
Macro,
|
||||
Val,
|
||||
Return,
|
||||
}
|
||||
|
||||
impl Display for KeywordKind {
|
||||
|
@ -119,6 +120,7 @@ impl KeywordKind {
|
|||
Self::Bool => "bool",
|
||||
Self::Macro => "macro",
|
||||
Self::Val => "val",
|
||||
Self::Return => "return",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
|||
Conditional, Else, ExecuteBlock, ExecuteBlockHead, ExecuteBlockHeadItem as _,
|
||||
ExecuteBlockTail,
|
||||
},
|
||||
Assignment, AssignmentDestination, Block, Grouping, Run, Semicolon, SemicolonStatement,
|
||||
Assignment, AssignmentDestination, Block, Grouping, Semicolon, SemicolonStatement,
|
||||
Statement, VariableDeclaration,
|
||||
},
|
||||
AnyStringLiteral,
|
||||
|
@ -228,7 +228,6 @@ impl Statement {
|
|||
let child_scope = SemanticScope::with_parent(scope);
|
||||
group.analyze_semantics(&child_scope, handler)
|
||||
}
|
||||
Self::Run(run) => run.analyze_semantics(scope, handler),
|
||||
Self::Semicolon(sem) => sem.analyze_semantics(scope, handler),
|
||||
}
|
||||
}
|
||||
|
@ -342,16 +341,6 @@ impl Grouping {
|
|||
}
|
||||
}
|
||||
|
||||
impl Run {
|
||||
fn analyze_semantics(
|
||||
&self,
|
||||
scope: &SemanticScope,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> Result<(), error::Error> {
|
||||
self.expression().analyze_semantics(scope, handler)
|
||||
}
|
||||
}
|
||||
|
||||
impl Semicolon {
|
||||
fn analyze_semantics(
|
||||
&self,
|
||||
|
@ -364,6 +353,7 @@ impl Semicolon {
|
|||
}
|
||||
SemicolonStatement::Expression(expr) => expr.analyze_semantics(scope, handler),
|
||||
SemicolonStatement::VariableDeclaration(decl) => decl.analyze_semantics(scope, handler),
|
||||
SemicolonStatement::Return(ret) => ret.expression().analyze_semantics(scope, handler),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -665,6 +655,21 @@ impl Primary {
|
|||
Err(err)
|
||||
}
|
||||
}
|
||||
PrefixOperator::Run(_) => {
|
||||
if prefixed
|
||||
.operand()
|
||||
.can_yield_type_semantics(ValueType::String, scope)
|
||||
{
|
||||
prefixed.operand().analyze_semantics(scope, handler)
|
||||
} else {
|
||||
let err = error::Error::MismatchedTypes(MismatchedTypes {
|
||||
expected_type: ExpectedType::String,
|
||||
expression: prefixed.operand().span(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
},
|
||||
Self::Lua(lua) => lua.analyze_semantics(scope, handler),
|
||||
}
|
||||
|
@ -701,12 +706,24 @@ impl Primary {
|
|||
_ => false,
|
||||
},
|
||||
Self::Prefix(prefixed) => match prefixed.operator() {
|
||||
PrefixOperator::LogicalNot(_) => prefixed
|
||||
PrefixOperator::LogicalNot(_) => {
|
||||
expected == ValueType::Boolean
|
||||
&& prefixed
|
||||
.operand()
|
||||
.can_yield_type_semantics(ValueType::Boolean, scope),
|
||||
PrefixOperator::Negate(_) => prefixed
|
||||
.can_yield_type_semantics(ValueType::Boolean, scope)
|
||||
}
|
||||
PrefixOperator::Negate(_) => {
|
||||
expected == ValueType::Integer
|
||||
&& prefixed
|
||||
.operand()
|
||||
.can_yield_type_semantics(ValueType::Integer, scope),
|
||||
.can_yield_type_semantics(ValueType::Integer, scope)
|
||||
}
|
||||
PrefixOperator::Run(_) => {
|
||||
expected == ValueType::String
|
||||
&& prefixed
|
||||
.operand()
|
||||
.can_yield_type_semantics(ValueType::String, scope)
|
||||
}
|
||||
},
|
||||
Self::Parenthesized(parenthesized) => {
|
||||
parenthesized.can_yield_type_semantics(expected, scope)
|
||||
|
@ -807,8 +824,18 @@ impl Binary {
|
|||
#[must_use]
|
||||
fn can_yield_type_semantics(&self, expected: ValueType, scope: &SemanticScope) -> bool {
|
||||
match self.operator() {
|
||||
BinaryOperator::Add(_)
|
||||
| BinaryOperator::Subtract(_)
|
||||
BinaryOperator::Add(_) => {
|
||||
if expected == ValueType::Integer {
|
||||
self.left_operand()
|
||||
.can_yield_type_semantics(ValueType::Integer, scope)
|
||||
&& self
|
||||
.right_operand()
|
||||
.can_yield_type_semantics(ValueType::Integer, scope)
|
||||
} else {
|
||||
expected == ValueType::String
|
||||
}
|
||||
}
|
||||
BinaryOperator::Subtract(_)
|
||||
| BinaryOperator::Multiply(_)
|
||||
| BinaryOperator::Divide(_)
|
||||
| BinaryOperator::Modulo(_) => {
|
||||
|
|
|
@ -56,7 +56,7 @@ impl SyntaxKind {
|
|||
} else if variants.len() == 1 {
|
||||
variants[0].expected_binding_str()
|
||||
} else {
|
||||
let comma_range = ..variants.len() - 2;
|
||||
let comma_range = ..variants.len() - 1;
|
||||
let comma_elements = variants[comma_range]
|
||||
.iter()
|
||||
.map(Self::expected_binding_str)
|
||||
|
|
|
@ -357,11 +357,16 @@ impl Parser<'_> {
|
|||
Reading::Atomic(Token::Keyword(pub_keyword))
|
||||
if pub_keyword.keyword == KeywordKind::Pub =>
|
||||
{
|
||||
if let Ok(function) = self.try_parse(|parser| parser.parse_function(&VoidHandler)) {
|
||||
match self.peek_offset(2) {
|
||||
Some(Reading::Atomic(Token::Keyword(function_keyword)))
|
||||
if function_keyword.keyword == KeywordKind::Function =>
|
||||
{
|
||||
let function = self.parse_function(handler)?;
|
||||
tracing::trace!("Parsed function '{:?}'", function.identifier.span.str());
|
||||
|
||||
Ok(Declaration::Function(function))
|
||||
} else {
|
||||
}
|
||||
_ => {
|
||||
// eat the pub keyword
|
||||
self.forward();
|
||||
|
||||
|
@ -371,6 +376,7 @@ impl Parser<'_> {
|
|||
Ok(Declaration::GlobalVariable((Some(pub_keyword), var, semi)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parse annotations
|
||||
Reading::Atomic(Token::Punctuation(punctuation)) if punctuation.punctuation == '#' => {
|
||||
|
|
|
@ -314,12 +314,15 @@ pub enum PrefixOperator {
|
|||
LogicalNot(Punctuation),
|
||||
/// The negate operator '-'.
|
||||
Negate(Punctuation),
|
||||
/// The run keyword 'run'.
|
||||
Run(Keyword),
|
||||
}
|
||||
|
||||
impl SourceElement for PrefixOperator {
|
||||
fn span(&self) -> Span {
|
||||
match self {
|
||||
Self::LogicalNot(token) | Self::Negate(token) => token.span.clone(),
|
||||
Self::Run(token) => token.span.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -507,7 +510,7 @@ impl Parser<'_> {
|
|||
#[expect(clippy::too_many_lines)]
|
||||
pub fn parse_primary(&mut self, handler: &impl Handler<base::Error>) -> ParseResult<Primary> {
|
||||
match self.stop_at_significant() {
|
||||
// prefixed expression
|
||||
// prefixed expression with '!' or '-'
|
||||
Reading::Atomic(Token::Punctuation(punc)) if matches!(punc.punctuation, '!' | '-') => {
|
||||
// eat the prefix
|
||||
self.forward();
|
||||
|
@ -526,6 +529,23 @@ impl Parser<'_> {
|
|||
}))
|
||||
}
|
||||
|
||||
// prefixed expression with 'run'
|
||||
Reading::Atomic(Token::Keyword(run_keyword))
|
||||
if run_keyword.keyword == KeywordKind::Run =>
|
||||
{
|
||||
// eat the run keyword
|
||||
self.forward();
|
||||
|
||||
let expression = self.parse_primary(handler)?;
|
||||
|
||||
tracing::trace!("Parsed run expression: {:?}", expression);
|
||||
|
||||
Ok(Primary::Prefix(Prefix {
|
||||
operator: PrefixOperator::Run(run_keyword),
|
||||
operand: Box::new(expression),
|
||||
}))
|
||||
}
|
||||
|
||||
// parenthesized expression
|
||||
Reading::IntoDelimited(left_parenthesis) if left_parenthesis.punctuation == '(' => self
|
||||
.parse_parenthesized(handler)
|
||||
|
|
|
@ -43,7 +43,6 @@ use super::{expression::Expression, Annotation, AnyStringLiteral};
|
|||
/// | Grouping
|
||||
/// | DocComment
|
||||
/// | Semicolon
|
||||
/// | Run
|
||||
/// ;
|
||||
/// ```
|
||||
#[allow(missing_docs)]
|
||||
|
@ -56,7 +55,6 @@ pub enum Statement {
|
|||
Grouping(Grouping),
|
||||
DocComment(DocComment),
|
||||
Semicolon(Semicolon),
|
||||
Run(Run),
|
||||
}
|
||||
|
||||
impl SourceElement for Statement {
|
||||
|
@ -68,7 +66,6 @@ impl SourceElement for Statement {
|
|||
Self::Grouping(grouping) => grouping.span(),
|
||||
Self::DocComment(doc_comment) => doc_comment.span(),
|
||||
Self::Semicolon(semi) => semi.span(),
|
||||
Self::Run(run) => run.span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,6 +104,14 @@ impl Statement {
|
|||
target: "expressions".to_string(),
|
||||
});
|
||||
|
||||
Err(err)
|
||||
}
|
||||
SemicolonStatement::Return(_) => {
|
||||
let err = Error::InvalidAnnotation(InvalidAnnotation {
|
||||
annotation: annotation.assignment.identifier.span,
|
||||
target: "return statements".to_string(),
|
||||
});
|
||||
|
||||
Err(err)
|
||||
}
|
||||
},
|
||||
|
@ -162,46 +167,6 @@ impl SourceElement for Block {
|
|||
}
|
||||
}
|
||||
|
||||
/// Represents a run statement in the syntax tree.
|
||||
///
|
||||
/// Syntax Synopsis:
|
||||
///
|
||||
/// ``` ebnf
|
||||
/// Run:
|
||||
/// 'run' Expression ';'
|
||||
/// ;
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||
pub struct Run {
|
||||
/// The `run` keyword.
|
||||
#[get = "pub"]
|
||||
run_keyword: Keyword,
|
||||
/// The expression of the run statement.
|
||||
#[get = "pub"]
|
||||
expression: Expression,
|
||||
/// The semicolon of the run statement.
|
||||
#[get = "pub"]
|
||||
semicolon: Punctuation,
|
||||
}
|
||||
|
||||
impl SourceElement for Run {
|
||||
fn span(&self) -> Span {
|
||||
self.run_keyword
|
||||
.span()
|
||||
.join(&self.semicolon.span())
|
||||
.expect("The span of the run statement is invalid.")
|
||||
}
|
||||
}
|
||||
|
||||
impl Run {
|
||||
/// Dissolves the [`Run`] into its components.
|
||||
#[must_use]
|
||||
pub fn dissolve(self) -> (Keyword, Expression, Punctuation) {
|
||||
(self.run_keyword, self.expression, self.semicolon)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a grouping statement in the syntax tree.
|
||||
///
|
||||
/// Syntax Synopsis:
|
||||
|
@ -293,6 +258,8 @@ pub enum SemicolonStatement {
|
|||
VariableDeclaration(VariableDeclaration),
|
||||
/// An assignment.
|
||||
Assignment(Assignment),
|
||||
/// A return statement.
|
||||
Return(ReturnStatement),
|
||||
}
|
||||
|
||||
impl SourceElement for SemicolonStatement {
|
||||
|
@ -301,10 +268,46 @@ impl SourceElement for SemicolonStatement {
|
|||
Self::Expression(expression) => expression.span(),
|
||||
Self::VariableDeclaration(declaration) => declaration.span(),
|
||||
Self::Assignment(assignment) => assignment.span(),
|
||||
Self::Return(ret) => ret.span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a return statement in the syntax tree.
|
||||
///
|
||||
/// Syntax Synopsis:
|
||||
/// ```ebnf
|
||||
/// ReturnStatement:
|
||||
/// `return` Expression ;
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||
pub struct ReturnStatement {
|
||||
/// The `return` keyword.
|
||||
#[get = "pub"]
|
||||
return_keyword: Keyword,
|
||||
/// The expression of the return statement.
|
||||
#[get = "pub"]
|
||||
expression: Expression,
|
||||
}
|
||||
|
||||
impl SourceElement for ReturnStatement {
|
||||
fn span(&self) -> Span {
|
||||
self.return_keyword
|
||||
.span()
|
||||
.join(&self.expression.span())
|
||||
.expect("The span of the return statement is invalid.")
|
||||
}
|
||||
}
|
||||
|
||||
impl ReturnStatement {
|
||||
/// Dissolves the [`ReturnStatement`] into its components.
|
||||
#[must_use]
|
||||
pub fn dissolve(self) -> (Keyword, Expression) {
|
||||
(self.return_keyword, self.expression)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a variable declaration in the syntax tree.
|
||||
///
|
||||
/// Syntax Synopsis:
|
||||
|
@ -910,25 +913,6 @@ impl Parser<'_> {
|
|||
}))
|
||||
}
|
||||
|
||||
// run statement
|
||||
Reading::Atomic(Token::Keyword(run_keyword))
|
||||
if run_keyword.keyword == KeywordKind::Run =>
|
||||
{
|
||||
// eat the run keyword
|
||||
self.forward();
|
||||
|
||||
let expression = self.parse_expression(handler)?;
|
||||
let semicolon = self.parse_punctuation(';', true, handler)?;
|
||||
|
||||
tracing::trace!("Parsed run statement: {:?}", expression);
|
||||
|
||||
Ok(Statement::Run(Run {
|
||||
run_keyword,
|
||||
expression,
|
||||
semicolon,
|
||||
}))
|
||||
}
|
||||
|
||||
// semicolon statement
|
||||
_ => self.parse_semicolon(handler).map(Statement::Semicolon),
|
||||
}
|
||||
|
@ -941,6 +925,17 @@ impl Parser<'_> {
|
|||
handler: &impl Handler<base::Error>,
|
||||
) -> ParseResult<Semicolon> {
|
||||
let statement = match self.stop_at_significant() {
|
||||
Reading::Atomic(Token::Keyword(keyword)) if keyword.keyword == KeywordKind::Return => {
|
||||
// eat the return keyword
|
||||
self.forward();
|
||||
|
||||
let expression = self.parse_expression(handler)?;
|
||||
|
||||
Ok(SemicolonStatement::Return(ReturnStatement {
|
||||
return_keyword: keyword,
|
||||
expression,
|
||||
}))
|
||||
}
|
||||
Reading::Atomic(Token::Keyword(keyword))
|
||||
if matches!(
|
||||
keyword.keyword,
|
||||
|
@ -1025,6 +1020,7 @@ impl Parser<'_> {
|
|||
expected: SyntaxKind::Either(&[
|
||||
SyntaxKind::Keyword(KeywordKind::Int),
|
||||
SyntaxKind::Keyword(KeywordKind::Bool),
|
||||
SyntaxKind::Keyword(KeywordKind::Val),
|
||||
]),
|
||||
found: unexpected.into_token(),
|
||||
});
|
||||
|
|
|
@ -289,6 +289,10 @@ impl Primary {
|
|||
matches!(r#type, ValueType::Integer)
|
||||
&& prefix.operand().can_yield_type(r#type, scope)
|
||||
}
|
||||
PrefixOperator::Run(_) => {
|
||||
matches!(r#type, ValueType::Integer | ValueType::Boolean)
|
||||
&& prefix.operand().can_yield_type(ValueType::String, scope)
|
||||
}
|
||||
},
|
||||
Self::Indexed(indexed) => {
|
||||
if let Self::Identifier(ident) = indexed.object().as_ref() {
|
||||
|
@ -601,7 +605,7 @@ impl Transpiler {
|
|||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
#[expect(clippy::too_many_lines, clippy::cognitive_complexity)]
|
||||
pub(super) fn transpile_primary_expression(
|
||||
&mut self,
|
||||
primary: &Primary,
|
||||
|
@ -760,8 +764,8 @@ impl Transpiler {
|
|||
StorageType::Byte | StorageType::Int | StorageType::Long
|
||||
) =>
|
||||
{
|
||||
let (target_objective, mut targets) = self.get_temp_scoreboard_locations(1);
|
||||
let target_ident = targets.pop().expect("at least size 1");
|
||||
let (target_objective, [target_ident]) =
|
||||
self.get_temp_scoreboard_locations_array();
|
||||
|
||||
let score_to_storage_cmd = Command::Execute(Execute::Store(
|
||||
format!(
|
||||
|
@ -807,6 +811,65 @@ impl Transpiler {
|
|||
|
||||
Ok(cmds)
|
||||
}
|
||||
PrefixOperator::Run(_) => {
|
||||
let run_cmds =
|
||||
self.transpile_run_expression(prefix.operand(), scope, handler)?;
|
||||
let run_cmd = if run_cmds.len() == 1 {
|
||||
run_cmds.into_iter().next().expect("length is 1")
|
||||
} else {
|
||||
Command::Group(run_cmds)
|
||||
};
|
||||
match target {
|
||||
DataLocation::ScoreboardValue { objective, target } => {
|
||||
let store = format!("result score {target} {objective}");
|
||||
let exec = Command::Execute(Execute::Store(
|
||||
store.into(),
|
||||
Box::new(Execute::Run(Box::new(run_cmd))),
|
||||
));
|
||||
Ok(vec![exec])
|
||||
}
|
||||
DataLocation::Storage {
|
||||
storage_name,
|
||||
path,
|
||||
r#type,
|
||||
} => {
|
||||
let store = format!(
|
||||
"{result_success} storage {storage_name} {path} {t} 1.0",
|
||||
t = r#type.as_str(),
|
||||
result_success = if matches!(r#type, StorageType::Boolean) {
|
||||
"success"
|
||||
} else {
|
||||
"result"
|
||||
}
|
||||
);
|
||||
let exec = Command::Execute(Execute::Store(
|
||||
store.into(),
|
||||
Box::new(Execute::Run(Box::new(run_cmd))),
|
||||
));
|
||||
Ok(vec![exec])
|
||||
}
|
||||
DataLocation::Tag { tag_name, entity } => {
|
||||
let prepare_cmd =
|
||||
Command::Raw(format!("tag {entity} remove {tag_name}"));
|
||||
let success_cmd = Command::Raw(format!("tag {entity} add {tag_name}"));
|
||||
let (temp_storage_name, [temp_storage_path]) =
|
||||
self.get_temp_storage_locations_array();
|
||||
|
||||
let store_cmd = Command::Execute(Execute::Store(
|
||||
format!("success storage {temp_storage_name} {temp_storage_path} boolean 1.0").into(),
|
||||
Box::new(Execute::Run(Box::new(run_cmd)))
|
||||
));
|
||||
|
||||
let if_cmd = Command::Execute(Execute::If(
|
||||
Condition::Atom(format!("data storage {temp_storage_name} {{{temp_storage_name}:1b}}").into()),
|
||||
Box::new(Execute::Run(Box::new(success_cmd))),
|
||||
None,
|
||||
));
|
||||
|
||||
Ok(vec![store_cmd, prepare_cmd, if_cmd])
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Primary::Identifier(ident) => {
|
||||
let variable = scope.get_variable(ident.span.str());
|
||||
|
@ -1215,6 +1278,25 @@ impl Transpiler {
|
|||
},
|
||||
))
|
||||
}
|
||||
PrefixOperator::Run(_) => {
|
||||
let (temp_storage_name, [temp_storage_path]) =
|
||||
self.get_temp_storage_locations_array();
|
||||
let cond = ExtendedCondition::Runtime(Condition::Atom(
|
||||
format!("data storage {temp_storage_name} {{{temp_storage_path}:1b}}")
|
||||
.into(),
|
||||
));
|
||||
let store_cmds = self.transpile_primary_expression(
|
||||
primary,
|
||||
&DataLocation::Storage {
|
||||
storage_name: temp_storage_name,
|
||||
path: temp_storage_path,
|
||||
r#type: StorageType::Boolean,
|
||||
},
|
||||
scope,
|
||||
handler,
|
||||
)?;
|
||||
Ok((store_cmds, cond))
|
||||
}
|
||||
PrefixOperator::Negate(_) => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expected_type: ExpectedType::Boolean,
|
||||
|
@ -1289,11 +1371,12 @@ impl Transpiler {
|
|||
let right = binary.right_operand();
|
||||
let operator = binary.operator();
|
||||
|
||||
let (temp_objective, temp_locations) = self.get_temp_scoreboard_locations(2);
|
||||
let (temp_objective, [temp_location_a, temp_location_b]) =
|
||||
self.get_temp_scoreboard_locations_array();
|
||||
|
||||
let score_target_location = match target {
|
||||
DataLocation::ScoreboardValue { objective, target } => (objective, target),
|
||||
_ => (&temp_objective, &temp_locations[0]),
|
||||
_ => (&temp_objective, &temp_location_a),
|
||||
};
|
||||
|
||||
let left_cmds = self.transpile_expression(
|
||||
|
@ -1318,7 +1401,7 @@ impl Transpiler {
|
|||
right,
|
||||
&DataLocation::ScoreboardValue {
|
||||
objective: temp_objective.clone(),
|
||||
target: temp_locations[1].clone(),
|
||||
target: temp_location_b.clone(),
|
||||
},
|
||||
scope,
|
||||
handler,
|
||||
|
@ -1328,7 +1411,7 @@ impl Transpiler {
|
|||
right_cmds,
|
||||
(
|
||||
temp_objective.as_str(),
|
||||
std::borrow::Cow::Borrowed(&temp_locations[1]),
|
||||
std::borrow::Cow::Borrowed(&temp_location_b),
|
||||
),
|
||||
)
|
||||
};
|
||||
|
@ -1419,13 +1502,12 @@ impl Transpiler {
|
|||
_ => unreachable!("This function should only be called for comparison operators."),
|
||||
};
|
||||
|
||||
let (temp_objective, mut temp_locations) = self.get_temp_scoreboard_locations(2);
|
||||
let (temp_objective, [temp_location_a, temp_location_b]) =
|
||||
self.get_temp_scoreboard_locations_array();
|
||||
|
||||
let condition = Condition::Atom(
|
||||
format!(
|
||||
"score {target} {temp_objective} {operator} {source} {temp_objective}",
|
||||
target = temp_locations[0],
|
||||
source = temp_locations[1]
|
||||
"score {temp_location_a} {temp_objective} {operator} {temp_location_b} {temp_objective}"
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
|
@ -1434,7 +1516,7 @@ impl Transpiler {
|
|||
binary.left_operand(),
|
||||
&DataLocation::ScoreboardValue {
|
||||
objective: temp_objective.clone(),
|
||||
target: std::mem::take(&mut temp_locations[0]),
|
||||
target: temp_location_a,
|
||||
},
|
||||
scope,
|
||||
handler,
|
||||
|
@ -1443,7 +1525,7 @@ impl Transpiler {
|
|||
binary.right_operand(),
|
||||
&DataLocation::ScoreboardValue {
|
||||
objective: temp_objective,
|
||||
target: std::mem::take(&mut temp_locations[1]),
|
||||
target: temp_location_b,
|
||||
},
|
||||
scope,
|
||||
handler,
|
||||
|
@ -1707,6 +1789,17 @@ impl Transpiler {
|
|||
(objective, targets)
|
||||
}
|
||||
|
||||
/// Get temporary scoreboard locations.
|
||||
pub(super) fn get_temp_scoreboard_locations_array<const N: usize>(
|
||||
&mut self,
|
||||
) -> (String, [String; N]) {
|
||||
let (objective, targets) = self.get_temp_scoreboard_locations(N);
|
||||
|
||||
let targets = targets.try_into().expect("build from range of type");
|
||||
|
||||
(objective, targets)
|
||||
}
|
||||
|
||||
/// Get temporary storage locations.
|
||||
pub(super) fn get_temp_storage_locations(&mut self, amount: usize) -> (String, Vec<String>) {
|
||||
let storage_name = "shulkerscript:temp_".to_string()
|
||||
|
@ -1734,4 +1827,15 @@ impl Transpiler {
|
|||
|
||||
(storage_name, paths)
|
||||
}
|
||||
|
||||
/// Get temporary storage locations.
|
||||
pub(super) fn get_temp_storage_locations_array<const N: usize>(
|
||||
&mut self,
|
||||
) -> (String, [String; N]) {
|
||||
let (storage_name, paths) = self.get_temp_storage_locations(N);
|
||||
|
||||
let paths = paths.try_into().expect("build from range of type");
|
||||
|
||||
(storage_name, paths)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -316,13 +316,13 @@ impl Transpiler {
|
|||
|
||||
VariableData::BooleanStorage { .. }
|
||||
| VariableData::ScoreboardValue { .. } => {
|
||||
let (temp_storage, mut temp_path) =
|
||||
self.get_temp_storage_locations(1);
|
||||
let (temp_storage, [temp_path]) =
|
||||
self.get_temp_storage_locations_array();
|
||||
let prepare_cmds = self.transpile_primary_expression(
|
||||
primary,
|
||||
&super::expression::DataLocation::Storage {
|
||||
storage_name: temp_storage.clone(),
|
||||
path: temp_path[0].clone(),
|
||||
path: temp_path.clone(),
|
||||
r#type: match var.as_ref() {
|
||||
VariableData::BooleanStorage { .. } => {
|
||||
StorageType::Boolean
|
||||
|
@ -340,7 +340,7 @@ impl Transpiler {
|
|||
Ok(Parameter::Storage {
|
||||
prepare_cmds,
|
||||
storage_name: temp_storage,
|
||||
path: std::mem::take(&mut temp_path[0]),
|
||||
path: temp_path,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
|
@ -364,12 +364,13 @@ impl Transpiler {
|
|||
| Primary::FunctionCall(_),
|
||||
)
|
||||
| Expression::Binary(_) => {
|
||||
let (temp_storage, mut temp_path) = self.get_temp_storage_locations(1);
|
||||
let (temp_storage, [temp_path]) =
|
||||
self.get_temp_storage_locations_array();
|
||||
let prepare_cmds = self.transpile_expression(
|
||||
expression,
|
||||
&super::expression::DataLocation::Storage {
|
||||
storage_name: temp_storage.clone(),
|
||||
path: temp_path[0].clone(),
|
||||
path: temp_path.clone(),
|
||||
r#type: StorageType::Int,
|
||||
},
|
||||
scope,
|
||||
|
@ -379,7 +380,7 @@ impl Transpiler {
|
|||
Ok(Parameter::Storage {
|
||||
prepare_cmds,
|
||||
storage_name: temp_storage,
|
||||
path: std::mem::take(&mut temp_path[0]),
|
||||
path: temp_path,
|
||||
})
|
||||
}
|
||||
};
|
||||
|
|
|
@ -158,15 +158,12 @@ fn print_function(
|
|||
json!({"nbt": path, "storage": storage_name, "color": PARAM_COLOR}),
|
||||
),
|
||||
DataLocation::Tag { tag_name, entity } => {
|
||||
let (temp_storage_name, temp_storage_paths) =
|
||||
transpiler.get_temp_storage_locations(1);
|
||||
let (temp_storage_name, [temp_storage_path]) =
|
||||
transpiler.get_temp_storage_locations_array();
|
||||
let selector =
|
||||
super::util::add_to_entity_selector(entity, &format!("tag={tag_name}"));
|
||||
let cmd = Command::Execute(Execute::Store(
|
||||
format!(
|
||||
"success storage {temp_storage_name} {path} byte 1.0",
|
||||
path = temp_storage_paths[0]
|
||||
)
|
||||
format!("success storage {temp_storage_name} {temp_storage_path} byte 1.0")
|
||||
.into(),
|
||||
Box::new(Execute::Run(Box::new(Command::Raw(format!(
|
||||
"execute if entity {selector}"
|
||||
|
@ -175,7 +172,7 @@ fn print_function(
|
|||
|
||||
(
|
||||
Some(cmd),
|
||||
json!({"nbt": temp_storage_paths[0], "storage": temp_storage_name, "color": PARAM_COLOR}),
|
||||
json!({"nbt": temp_storage_path, "storage": temp_storage_name, "color": PARAM_COLOR}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -367,10 +364,10 @@ fn print_function(
|
|||
}
|
||||
|
||||
primary => {
|
||||
let (storage_name, mut storage_paths) = transpiler.get_temp_storage_locations(1);
|
||||
let (storage_name, [storage_path]) = transpiler.get_temp_storage_locations_array();
|
||||
let location = DataLocation::Storage {
|
||||
storage_name,
|
||||
path: std::mem::take(&mut storage_paths[0]),
|
||||
path: storage_path,
|
||||
r#type: StorageType::Int,
|
||||
};
|
||||
let cmds = transpiler.transpile_primary_expression(
|
||||
|
@ -388,10 +385,10 @@ fn print_function(
|
|||
}
|
||||
},
|
||||
Expression::Binary(binary) => {
|
||||
let (storage_name, mut storage_paths) = transpiler.get_temp_storage_locations(1);
|
||||
let (storage_name, [storage_path]) = transpiler.get_temp_storage_locations_array();
|
||||
let location = DataLocation::Storage {
|
||||
storage_name,
|
||||
path: std::mem::take(&mut storage_paths[0]),
|
||||
path: storage_path,
|
||||
r#type: StorageType::Int,
|
||||
};
|
||||
let cmds =
|
||||
|
|
|
@ -14,11 +14,11 @@ use crate::{
|
|||
semantic::error::UnexpectedExpression,
|
||||
syntax::syntax_tree::{
|
||||
declaration::{Declaration, ImportItems},
|
||||
expression::{Expression, FunctionCall, Primary},
|
||||
expression::{Expression, FunctionCall, PrefixOperator, Primary},
|
||||
program::{Namespace, ProgramFile},
|
||||
statement::{
|
||||
execute_block::{Conditional, Else, ExecuteBlock, ExecuteBlockHead, ExecuteBlockTail},
|
||||
SemicolonStatement, Statement,
|
||||
ReturnStatement, SemicolonStatement, Statement,
|
||||
},
|
||||
AnnotationAssignment,
|
||||
},
|
||||
|
@ -359,9 +359,6 @@ impl Transpiler {
|
|||
Statement::LiteralCommand(literal_command) => {
|
||||
Ok(vec![literal_command.clean_command().into()])
|
||||
}
|
||||
Statement::Run(run) => {
|
||||
self.transpile_run_expression(run.expression(), program_identifier, scope, handler)
|
||||
}
|
||||
Statement::Block(_) => {
|
||||
unreachable!("Only literal commands are allowed in functions at this time.")
|
||||
}
|
||||
|
@ -411,6 +408,11 @@ impl Transpiler {
|
|||
Expression::Primary(Primary::FunctionCall(func)) => {
|
||||
self.transpile_function_call(func, scope, handler)
|
||||
}
|
||||
Expression::Primary(Primary::Prefix(prefix))
|
||||
if matches!(prefix.operator(), PrefixOperator::Run(_)) =>
|
||||
{
|
||||
self.transpile_run_expression(prefix.operand(), scope, handler)
|
||||
}
|
||||
unexpected => {
|
||||
let error = TranspileError::UnexpectedExpression(UnexpectedExpression(
|
||||
unexpected.clone(),
|
||||
|
@ -433,24 +435,155 @@ impl Transpiler {
|
|||
scope,
|
||||
handler,
|
||||
),
|
||||
SemicolonStatement::Return(ret) => {
|
||||
self.transpile_return_statement(ret, program_identifier, scope, handler)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::only_used_in_recursion)]
|
||||
fn transpile_run_expression(
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn transpile_return_statement(
|
||||
&mut self,
|
||||
expression: &Expression,
|
||||
program_identifier: &str,
|
||||
ret: &ReturnStatement,
|
||||
_program_identifier: &str,
|
||||
scope: &Arc<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<Vec<Command>> {
|
||||
let comptime_val = ret
|
||||
.expression()
|
||||
.comptime_eval(scope, handler)
|
||||
.map(|val| val.to_macro_string());
|
||||
|
||||
let (prepare_cmds, ret_cmd) = if let Ok(val) = comptime_val {
|
||||
(Vec::new(), datapack::ReturnCommand::Value(val.into()))
|
||||
} else {
|
||||
match ret.expression() {
|
||||
Expression::Primary(Primary::Prefix(prefix))
|
||||
if matches!(prefix.operator(), PrefixOperator::Run(_)) =>
|
||||
{
|
||||
let ret_cmds =
|
||||
self.transpile_run_expression(prefix.operand(), scope, handler)?;
|
||||
let cmd = if ret_cmds.len() == 1 {
|
||||
ret_cmds.into_iter().next().unwrap()
|
||||
} else {
|
||||
Command::Group(ret_cmds)
|
||||
};
|
||||
(Vec::new(), datapack::ReturnCommand::Command(Box::new(cmd)))
|
||||
}
|
||||
Expression::Primary(Primary::FunctionCall(func)) => {
|
||||
let ret_cmds = self.transpile_function_call(func, scope, handler)?;
|
||||
let cmd = if ret_cmds.len() == 1 {
|
||||
ret_cmds.into_iter().next().unwrap()
|
||||
} else {
|
||||
Command::Group(ret_cmds)
|
||||
};
|
||||
(Vec::new(), datapack::ReturnCommand::Command(Box::new(cmd)))
|
||||
}
|
||||
Expression::Primary(Primary::Identifier(ident)) => {
|
||||
if let Some(var) = scope.get_variable(ident.span.str()) {
|
||||
match var.as_ref() {
|
||||
VariableData::BooleanStorage { storage_name, path } => (
|
||||
Vec::new(),
|
||||
datapack::ReturnCommand::Command(Box::new(Command::Raw(format!(
|
||||
"data get storage {storage_name} {path}"
|
||||
)))),
|
||||
),
|
||||
VariableData::ComptimeValue {
|
||||
value,
|
||||
read_only: _,
|
||||
} => 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::new(),
|
||||
datapack::ReturnCommand::Command(Box::new(cmd)),
|
||||
))
|
||||
},
|
||||
)?,
|
||||
VariableData::MacroParameter {
|
||||
index: _,
|
||||
macro_name,
|
||||
} => (
|
||||
Vec::new(),
|
||||
datapack::ReturnCommand::Command(Box::new(Command::UsesMacro(
|
||||
shulkerbox::util::MacroString::MacroString(vec![
|
||||
shulkerbox::util::MacroStringPart::MacroUsage(
|
||||
macro_name.clone(),
|
||||
),
|
||||
]),
|
||||
))),
|
||||
),
|
||||
VariableData::ScoreboardValue { objective, target } => (
|
||||
Vec::new(),
|
||||
datapack::ReturnCommand::Command(Box::new(Command::Raw(format!(
|
||||
"scoreboard players get {target} {objective}"
|
||||
)))),
|
||||
),
|
||||
_ => {
|
||||
let error =
|
||||
TranspileError::UnexpectedExpression(UnexpectedExpression(
|
||||
Expression::Primary(Primary::Identifier(ident.clone())),
|
||||
));
|
||||
handler.receive(error.clone());
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let error = TranspileError::UnknownIdentifier(UnknownIdentifier {
|
||||
identifier: ident.span.clone(),
|
||||
});
|
||||
handler.receive(error.clone());
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let (temp_objective, [temp_target]) =
|
||||
self.get_temp_scoreboard_locations_array();
|
||||
let ret_cmd = datapack::ReturnCommand::Command(Box::new(Command::Raw(
|
||||
format!("scoreboard players get {temp_target} {temp_objective}"),
|
||||
)));
|
||||
let cmds = self.transpile_expression(
|
||||
ret.expression(),
|
||||
&super::expression::DataLocation::ScoreboardValue {
|
||||
objective: temp_objective,
|
||||
target: temp_target,
|
||||
},
|
||||
scope,
|
||||
handler,
|
||||
)?;
|
||||
(cmds, ret_cmd)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let cmds = prepare_cmds
|
||||
.into_iter()
|
||||
.chain(std::iter::once(Command::Return(ret_cmd)))
|
||||
.collect();
|
||||
|
||||
Ok(cmds)
|
||||
}
|
||||
|
||||
pub(super) fn transpile_run_expression(
|
||||
&mut self,
|
||||
expression: &Primary,
|
||||
scope: &Arc<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<Vec<Command>> {
|
||||
match expression {
|
||||
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() {
|
||||
Primary::FunctionCall(func) => self.transpile_function_call(func, scope, handler),
|
||||
Primary::Identifier(ident) => match scope.get_variable(ident.span.str()).as_deref() {
|
||||
Some(VariableData::ComptimeValue {
|
||||
value,
|
||||
read_only: _,
|
||||
|
@ -472,7 +605,7 @@ impl Transpiler {
|
|||
),
|
||||
Some(_) => {
|
||||
let error = TranspileError::UnexpectedExpression(UnexpectedExpression(
|
||||
expression.clone(),
|
||||
Expression::Primary(expression.clone()),
|
||||
));
|
||||
handler.receive(error.clone());
|
||||
Err(error)
|
||||
|
@ -484,26 +617,23 @@ impl Transpiler {
|
|||
handler.receive(error.clone());
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
expression @ Expression::Primary(
|
||||
},
|
||||
|
||||
Primary::Integer(_)
|
||||
| Primary::Boolean(_)
|
||||
| Primary::Prefix(_)
|
||||
| Primary::Indexed(_),
|
||||
) => {
|
||||
let error =
|
||||
TranspileError::UnexpectedExpression(UnexpectedExpression(expression.clone()));
|
||||
| Primary::Indexed(_) => {
|
||||
let error = TranspileError::UnexpectedExpression(UnexpectedExpression(
|
||||
Expression::Primary(expression.clone()),
|
||||
));
|
||||
handler.receive(error.clone());
|
||||
Err(error)
|
||||
}
|
||||
Expression::Primary(Primary::StringLiteral(string)) => {
|
||||
Primary::StringLiteral(string) => {
|
||||
Ok(vec![Command::Raw(string.str_content().to_string())])
|
||||
}
|
||||
Expression::Primary(Primary::MacroStringLiteral(string)) => {
|
||||
Ok(vec![Command::UsesMacro(string.into())])
|
||||
}
|
||||
Expression::Primary(Primary::Lua(code)) => match code.eval_comptime(scope, handler)? {
|
||||
Primary::MacroStringLiteral(string) => Ok(vec![Command::UsesMacro(string.into())]),
|
||||
Primary::Lua(code) => match code.eval_comptime(scope, handler)? {
|
||||
Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
||||
Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
|
||||
Ok(ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)) => {
|
||||
|
@ -523,13 +653,10 @@ impl Transpiler {
|
|||
}
|
||||
},
|
||||
|
||||
Expression::Primary(Primary::Parenthesized(parenthesized)) => self
|
||||
.transpile_run_expression(
|
||||
parenthesized.expression(),
|
||||
program_identifier,
|
||||
scope,
|
||||
handler,
|
||||
),
|
||||
Primary::Parenthesized(parenthesized) => match parenthesized.expression().as_ref() {
|
||||
Expression::Primary(expression) => {
|
||||
self.transpile_run_expression(expression, scope, handler)
|
||||
}
|
||||
Expression::Binary(bin) => match bin.comptime_eval(scope, handler) {
|
||||
Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
||||
Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
|
||||
|
@ -542,6 +669,7 @@ impl Transpiler {
|
|||
Err(err)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -381,8 +381,7 @@ impl Transpiler {
|
|||
handler,
|
||||
)?;
|
||||
if is_global {
|
||||
let (temp_objective, temp_targets) = self.get_temp_scoreboard_locations(1);
|
||||
let temp_target = &temp_targets[0];
|
||||
let (temp_objective, [temp_target]) = self.get_temp_scoreboard_locations_array();
|
||||
let test_cmd = match declaration.variable_type().keyword {
|
||||
KeywordKind::Int => {
|
||||
Command::Raw(format!("scoreboard players get {name} {target}"))
|
||||
|
|
Loading…
Reference in New Issue