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 = { version = "1.0.217", features = ["derive"], optional = true }
|
||||||
serde_json = { version = "1.0.138", optional = true }
|
serde_json = { version = "1.0.138", optional = true }
|
||||||
# shulkerbox = { version = "0.1.0", default-features = false, optional = true }
|
# shulkerbox = { version = "0.1.0", default-features = false, optional = true }
|
||||||
shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "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"
|
strsim = "0.11.1"
|
||||||
strum = { version = "0.27.0", features = ["derive"] }
|
strum = { version = "0.27.0", features = ["derive"] }
|
||||||
thiserror = "2.0.11"
|
thiserror = "2.0.11"
|
||||||
|
|
|
@ -53,6 +53,7 @@ pub enum KeywordKind {
|
||||||
Bool,
|
Bool,
|
||||||
Macro,
|
Macro,
|
||||||
Val,
|
Val,
|
||||||
|
Return,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for KeywordKind {
|
impl Display for KeywordKind {
|
||||||
|
@ -119,6 +120,7 @@ impl KeywordKind {
|
||||||
Self::Bool => "bool",
|
Self::Bool => "bool",
|
||||||
Self::Macro => "macro",
|
Self::Macro => "macro",
|
||||||
Self::Val => "val",
|
Self::Val => "val",
|
||||||
|
Self::Return => "return",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
||||||
Conditional, Else, ExecuteBlock, ExecuteBlockHead, ExecuteBlockHeadItem as _,
|
Conditional, Else, ExecuteBlock, ExecuteBlockHead, ExecuteBlockHeadItem as _,
|
||||||
ExecuteBlockTail,
|
ExecuteBlockTail,
|
||||||
},
|
},
|
||||||
Assignment, AssignmentDestination, Block, Grouping, Run, Semicolon, SemicolonStatement,
|
Assignment, AssignmentDestination, Block, Grouping, Semicolon, SemicolonStatement,
|
||||||
Statement, VariableDeclaration,
|
Statement, VariableDeclaration,
|
||||||
},
|
},
|
||||||
AnyStringLiteral,
|
AnyStringLiteral,
|
||||||
|
@ -228,7 +228,6 @@ impl Statement {
|
||||||
let child_scope = SemanticScope::with_parent(scope);
|
let child_scope = SemanticScope::with_parent(scope);
|
||||||
group.analyze_semantics(&child_scope, handler)
|
group.analyze_semantics(&child_scope, handler)
|
||||||
}
|
}
|
||||||
Self::Run(run) => run.analyze_semantics(scope, handler),
|
|
||||||
Self::Semicolon(sem) => sem.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 {
|
impl Semicolon {
|
||||||
fn analyze_semantics(
|
fn analyze_semantics(
|
||||||
&self,
|
&self,
|
||||||
|
@ -364,6 +353,7 @@ impl Semicolon {
|
||||||
}
|
}
|
||||||
SemicolonStatement::Expression(expr) => expr.analyze_semantics(scope, handler),
|
SemicolonStatement::Expression(expr) => expr.analyze_semantics(scope, handler),
|
||||||
SemicolonStatement::VariableDeclaration(decl) => decl.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)
|
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),
|
Self::Lua(lua) => lua.analyze_semantics(scope, handler),
|
||||||
}
|
}
|
||||||
|
@ -701,12 +706,24 @@ impl Primary {
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
Self::Prefix(prefixed) => match prefixed.operator() {
|
Self::Prefix(prefixed) => match prefixed.operator() {
|
||||||
PrefixOperator::LogicalNot(_) => prefixed
|
PrefixOperator::LogicalNot(_) => {
|
||||||
|
expected == ValueType::Boolean
|
||||||
|
&& prefixed
|
||||||
.operand()
|
.operand()
|
||||||
.can_yield_type_semantics(ValueType::Boolean, scope),
|
.can_yield_type_semantics(ValueType::Boolean, scope)
|
||||||
PrefixOperator::Negate(_) => prefixed
|
}
|
||||||
|
PrefixOperator::Negate(_) => {
|
||||||
|
expected == ValueType::Integer
|
||||||
|
&& prefixed
|
||||||
.operand()
|
.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) => {
|
Self::Parenthesized(parenthesized) => {
|
||||||
parenthesized.can_yield_type_semantics(expected, scope)
|
parenthesized.can_yield_type_semantics(expected, scope)
|
||||||
|
@ -807,8 +824,18 @@ impl Binary {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn can_yield_type_semantics(&self, expected: ValueType, scope: &SemanticScope) -> bool {
|
fn can_yield_type_semantics(&self, expected: ValueType, scope: &SemanticScope) -> bool {
|
||||||
match self.operator() {
|
match self.operator() {
|
||||||
BinaryOperator::Add(_)
|
BinaryOperator::Add(_) => {
|
||||||
| BinaryOperator::Subtract(_)
|
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::Multiply(_)
|
||||||
| BinaryOperator::Divide(_)
|
| BinaryOperator::Divide(_)
|
||||||
| BinaryOperator::Modulo(_) => {
|
| BinaryOperator::Modulo(_) => {
|
||||||
|
|
|
@ -56,7 +56,7 @@ impl SyntaxKind {
|
||||||
} else if variants.len() == 1 {
|
} else if variants.len() == 1 {
|
||||||
variants[0].expected_binding_str()
|
variants[0].expected_binding_str()
|
||||||
} else {
|
} else {
|
||||||
let comma_range = ..variants.len() - 2;
|
let comma_range = ..variants.len() - 1;
|
||||||
let comma_elements = variants[comma_range]
|
let comma_elements = variants[comma_range]
|
||||||
.iter()
|
.iter()
|
||||||
.map(Self::expected_binding_str)
|
.map(Self::expected_binding_str)
|
||||||
|
|
|
@ -357,11 +357,16 @@ impl Parser<'_> {
|
||||||
Reading::Atomic(Token::Keyword(pub_keyword))
|
Reading::Atomic(Token::Keyword(pub_keyword))
|
||||||
if pub_keyword.keyword == KeywordKind::Pub =>
|
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());
|
tracing::trace!("Parsed function '{:?}'", function.identifier.span.str());
|
||||||
|
|
||||||
Ok(Declaration::Function(function))
|
Ok(Declaration::Function(function))
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
// eat the pub keyword
|
// eat the pub keyword
|
||||||
self.forward();
|
self.forward();
|
||||||
|
|
||||||
|
@ -371,6 +376,7 @@ impl Parser<'_> {
|
||||||
Ok(Declaration::GlobalVariable((Some(pub_keyword), var, semi)))
|
Ok(Declaration::GlobalVariable((Some(pub_keyword), var, semi)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// parse annotations
|
// parse annotations
|
||||||
Reading::Atomic(Token::Punctuation(punctuation)) if punctuation.punctuation == '#' => {
|
Reading::Atomic(Token::Punctuation(punctuation)) if punctuation.punctuation == '#' => {
|
||||||
|
|
|
@ -314,12 +314,15 @@ pub enum PrefixOperator {
|
||||||
LogicalNot(Punctuation),
|
LogicalNot(Punctuation),
|
||||||
/// The negate operator '-'.
|
/// The negate operator '-'.
|
||||||
Negate(Punctuation),
|
Negate(Punctuation),
|
||||||
|
/// The run keyword 'run'.
|
||||||
|
Run(Keyword),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceElement for PrefixOperator {
|
impl SourceElement for PrefixOperator {
|
||||||
fn span(&self) -> Span {
|
fn span(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
Self::LogicalNot(token) | Self::Negate(token) => token.span.clone(),
|
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)]
|
#[expect(clippy::too_many_lines)]
|
||||||
pub fn parse_primary(&mut self, handler: &impl Handler<base::Error>) -> ParseResult<Primary> {
|
pub fn parse_primary(&mut self, handler: &impl Handler<base::Error>) -> ParseResult<Primary> {
|
||||||
match self.stop_at_significant() {
|
match self.stop_at_significant() {
|
||||||
// prefixed expression
|
// prefixed expression with '!' or '-'
|
||||||
Reading::Atomic(Token::Punctuation(punc)) if matches!(punc.punctuation, '!' | '-') => {
|
Reading::Atomic(Token::Punctuation(punc)) if matches!(punc.punctuation, '!' | '-') => {
|
||||||
// eat the prefix
|
// eat the prefix
|
||||||
self.forward();
|
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
|
// parenthesized expression
|
||||||
Reading::IntoDelimited(left_parenthesis) if left_parenthesis.punctuation == '(' => self
|
Reading::IntoDelimited(left_parenthesis) if left_parenthesis.punctuation == '(' => self
|
||||||
.parse_parenthesized(handler)
|
.parse_parenthesized(handler)
|
||||||
|
|
|
@ -43,7 +43,6 @@ use super::{expression::Expression, Annotation, AnyStringLiteral};
|
||||||
/// | Grouping
|
/// | Grouping
|
||||||
/// | DocComment
|
/// | DocComment
|
||||||
/// | Semicolon
|
/// | Semicolon
|
||||||
/// | Run
|
|
||||||
/// ;
|
/// ;
|
||||||
/// ```
|
/// ```
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
|
@ -56,7 +55,6 @@ pub enum Statement {
|
||||||
Grouping(Grouping),
|
Grouping(Grouping),
|
||||||
DocComment(DocComment),
|
DocComment(DocComment),
|
||||||
Semicolon(Semicolon),
|
Semicolon(Semicolon),
|
||||||
Run(Run),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceElement for Statement {
|
impl SourceElement for Statement {
|
||||||
|
@ -68,7 +66,6 @@ impl SourceElement for Statement {
|
||||||
Self::Grouping(grouping) => grouping.span(),
|
Self::Grouping(grouping) => grouping.span(),
|
||||||
Self::DocComment(doc_comment) => doc_comment.span(),
|
Self::DocComment(doc_comment) => doc_comment.span(),
|
||||||
Self::Semicolon(semi) => semi.span(),
|
Self::Semicolon(semi) => semi.span(),
|
||||||
Self::Run(run) => run.span(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,6 +104,14 @@ impl Statement {
|
||||||
target: "expressions".to_string(),
|
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)
|
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.
|
/// Represents a grouping statement in the syntax tree.
|
||||||
///
|
///
|
||||||
/// Syntax Synopsis:
|
/// Syntax Synopsis:
|
||||||
|
@ -293,6 +258,8 @@ pub enum SemicolonStatement {
|
||||||
VariableDeclaration(VariableDeclaration),
|
VariableDeclaration(VariableDeclaration),
|
||||||
/// An assignment.
|
/// An assignment.
|
||||||
Assignment(Assignment),
|
Assignment(Assignment),
|
||||||
|
/// A return statement.
|
||||||
|
Return(ReturnStatement),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceElement for SemicolonStatement {
|
impl SourceElement for SemicolonStatement {
|
||||||
|
@ -301,10 +268,46 @@ impl SourceElement for SemicolonStatement {
|
||||||
Self::Expression(expression) => expression.span(),
|
Self::Expression(expression) => expression.span(),
|
||||||
Self::VariableDeclaration(declaration) => declaration.span(),
|
Self::VariableDeclaration(declaration) => declaration.span(),
|
||||||
Self::Assignment(assignment) => assignment.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.
|
/// Represents a variable declaration in the syntax tree.
|
||||||
///
|
///
|
||||||
/// Syntax Synopsis:
|
/// 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
|
// semicolon statement
|
||||||
_ => self.parse_semicolon(handler).map(Statement::Semicolon),
|
_ => self.parse_semicolon(handler).map(Statement::Semicolon),
|
||||||
}
|
}
|
||||||
|
@ -941,6 +925,17 @@ impl Parser<'_> {
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> ParseResult<Semicolon> {
|
) -> ParseResult<Semicolon> {
|
||||||
let statement = match self.stop_at_significant() {
|
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))
|
Reading::Atomic(Token::Keyword(keyword))
|
||||||
if matches!(
|
if matches!(
|
||||||
keyword.keyword,
|
keyword.keyword,
|
||||||
|
@ -1025,6 +1020,7 @@ impl Parser<'_> {
|
||||||
expected: SyntaxKind::Either(&[
|
expected: SyntaxKind::Either(&[
|
||||||
SyntaxKind::Keyword(KeywordKind::Int),
|
SyntaxKind::Keyword(KeywordKind::Int),
|
||||||
SyntaxKind::Keyword(KeywordKind::Bool),
|
SyntaxKind::Keyword(KeywordKind::Bool),
|
||||||
|
SyntaxKind::Keyword(KeywordKind::Val),
|
||||||
]),
|
]),
|
||||||
found: unexpected.into_token(),
|
found: unexpected.into_token(),
|
||||||
});
|
});
|
||||||
|
|
|
@ -289,6 +289,10 @@ impl Primary {
|
||||||
matches!(r#type, ValueType::Integer)
|
matches!(r#type, ValueType::Integer)
|
||||||
&& prefix.operand().can_yield_type(r#type, scope)
|
&& 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) => {
|
Self::Indexed(indexed) => {
|
||||||
if let Self::Identifier(ident) = indexed.object().as_ref() {
|
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(
|
pub(super) fn transpile_primary_expression(
|
||||||
&mut self,
|
&mut self,
|
||||||
primary: &Primary,
|
primary: &Primary,
|
||||||
|
@ -760,8 +764,8 @@ impl Transpiler {
|
||||||
StorageType::Byte | StorageType::Int | StorageType::Long
|
StorageType::Byte | StorageType::Int | StorageType::Long
|
||||||
) =>
|
) =>
|
||||||
{
|
{
|
||||||
let (target_objective, mut targets) = self.get_temp_scoreboard_locations(1);
|
let (target_objective, [target_ident]) =
|
||||||
let target_ident = targets.pop().expect("at least size 1");
|
self.get_temp_scoreboard_locations_array();
|
||||||
|
|
||||||
let score_to_storage_cmd = Command::Execute(Execute::Store(
|
let score_to_storage_cmd = Command::Execute(Execute::Store(
|
||||||
format!(
|
format!(
|
||||||
|
@ -807,6 +811,65 @@ impl Transpiler {
|
||||||
|
|
||||||
Ok(cmds)
|
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) => {
|
Primary::Identifier(ident) => {
|
||||||
let variable = scope.get_variable(ident.span.str());
|
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(_) => {
|
PrefixOperator::Negate(_) => {
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||||
expected_type: ExpectedType::Boolean,
|
expected_type: ExpectedType::Boolean,
|
||||||
|
@ -1289,11 +1371,12 @@ impl Transpiler {
|
||||||
let right = binary.right_operand();
|
let right = binary.right_operand();
|
||||||
let operator = binary.operator();
|
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 {
|
let score_target_location = match target {
|
||||||
DataLocation::ScoreboardValue { objective, target } => (objective, target),
|
DataLocation::ScoreboardValue { objective, target } => (objective, target),
|
||||||
_ => (&temp_objective, &temp_locations[0]),
|
_ => (&temp_objective, &temp_location_a),
|
||||||
};
|
};
|
||||||
|
|
||||||
let left_cmds = self.transpile_expression(
|
let left_cmds = self.transpile_expression(
|
||||||
|
@ -1318,7 +1401,7 @@ impl Transpiler {
|
||||||
right,
|
right,
|
||||||
&DataLocation::ScoreboardValue {
|
&DataLocation::ScoreboardValue {
|
||||||
objective: temp_objective.clone(),
|
objective: temp_objective.clone(),
|
||||||
target: temp_locations[1].clone(),
|
target: temp_location_b.clone(),
|
||||||
},
|
},
|
||||||
scope,
|
scope,
|
||||||
handler,
|
handler,
|
||||||
|
@ -1328,7 +1411,7 @@ impl Transpiler {
|
||||||
right_cmds,
|
right_cmds,
|
||||||
(
|
(
|
||||||
temp_objective.as_str(),
|
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."),
|
_ => 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(
|
let condition = Condition::Atom(
|
||||||
format!(
|
format!(
|
||||||
"score {target} {temp_objective} {operator} {source} {temp_objective}",
|
"score {temp_location_a} {temp_objective} {operator} {temp_location_b} {temp_objective}"
|
||||||
target = temp_locations[0],
|
|
||||||
source = temp_locations[1]
|
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
|
@ -1434,7 +1516,7 @@ impl Transpiler {
|
||||||
binary.left_operand(),
|
binary.left_operand(),
|
||||||
&DataLocation::ScoreboardValue {
|
&DataLocation::ScoreboardValue {
|
||||||
objective: temp_objective.clone(),
|
objective: temp_objective.clone(),
|
||||||
target: std::mem::take(&mut temp_locations[0]),
|
target: temp_location_a,
|
||||||
},
|
},
|
||||||
scope,
|
scope,
|
||||||
handler,
|
handler,
|
||||||
|
@ -1443,7 +1525,7 @@ impl Transpiler {
|
||||||
binary.right_operand(),
|
binary.right_operand(),
|
||||||
&DataLocation::ScoreboardValue {
|
&DataLocation::ScoreboardValue {
|
||||||
objective: temp_objective,
|
objective: temp_objective,
|
||||||
target: std::mem::take(&mut temp_locations[1]),
|
target: temp_location_b,
|
||||||
},
|
},
|
||||||
scope,
|
scope,
|
||||||
handler,
|
handler,
|
||||||
|
@ -1707,6 +1789,17 @@ impl Transpiler {
|
||||||
(objective, targets)
|
(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.
|
/// Get temporary storage locations.
|
||||||
pub(super) fn get_temp_storage_locations(&mut self, amount: usize) -> (String, Vec<String>) {
|
pub(super) fn get_temp_storage_locations(&mut self, amount: usize) -> (String, Vec<String>) {
|
||||||
let storage_name = "shulkerscript:temp_".to_string()
|
let storage_name = "shulkerscript:temp_".to_string()
|
||||||
|
@ -1734,4 +1827,15 @@ impl Transpiler {
|
||||||
|
|
||||||
(storage_name, paths)
|
(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::BooleanStorage { .. }
|
||||||
| VariableData::ScoreboardValue { .. } => {
|
| VariableData::ScoreboardValue { .. } => {
|
||||||
let (temp_storage, mut temp_path) =
|
let (temp_storage, [temp_path]) =
|
||||||
self.get_temp_storage_locations(1);
|
self.get_temp_storage_locations_array();
|
||||||
let prepare_cmds = self.transpile_primary_expression(
|
let prepare_cmds = self.transpile_primary_expression(
|
||||||
primary,
|
primary,
|
||||||
&super::expression::DataLocation::Storage {
|
&super::expression::DataLocation::Storage {
|
||||||
storage_name: temp_storage.clone(),
|
storage_name: temp_storage.clone(),
|
||||||
path: temp_path[0].clone(),
|
path: temp_path.clone(),
|
||||||
r#type: match var.as_ref() {
|
r#type: match var.as_ref() {
|
||||||
VariableData::BooleanStorage { .. } => {
|
VariableData::BooleanStorage { .. } => {
|
||||||
StorageType::Boolean
|
StorageType::Boolean
|
||||||
|
@ -340,7 +340,7 @@ impl Transpiler {
|
||||||
Ok(Parameter::Storage {
|
Ok(Parameter::Storage {
|
||||||
prepare_cmds,
|
prepare_cmds,
|
||||||
storage_name: temp_storage,
|
storage_name: temp_storage,
|
||||||
path: std::mem::take(&mut temp_path[0]),
|
path: temp_path,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -364,12 +364,13 @@ impl Transpiler {
|
||||||
| Primary::FunctionCall(_),
|
| Primary::FunctionCall(_),
|
||||||
)
|
)
|
||||||
| Expression::Binary(_) => {
|
| 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(
|
let prepare_cmds = self.transpile_expression(
|
||||||
expression,
|
expression,
|
||||||
&super::expression::DataLocation::Storage {
|
&super::expression::DataLocation::Storage {
|
||||||
storage_name: temp_storage.clone(),
|
storage_name: temp_storage.clone(),
|
||||||
path: temp_path[0].clone(),
|
path: temp_path.clone(),
|
||||||
r#type: StorageType::Int,
|
r#type: StorageType::Int,
|
||||||
},
|
},
|
||||||
scope,
|
scope,
|
||||||
|
@ -379,7 +380,7 @@ impl Transpiler {
|
||||||
Ok(Parameter::Storage {
|
Ok(Parameter::Storage {
|
||||||
prepare_cmds,
|
prepare_cmds,
|
||||||
storage_name: temp_storage,
|
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}),
|
json!({"nbt": path, "storage": storage_name, "color": PARAM_COLOR}),
|
||||||
),
|
),
|
||||||
DataLocation::Tag { tag_name, entity } => {
|
DataLocation::Tag { tag_name, entity } => {
|
||||||
let (temp_storage_name, temp_storage_paths) =
|
let (temp_storage_name, [temp_storage_path]) =
|
||||||
transpiler.get_temp_storage_locations(1);
|
transpiler.get_temp_storage_locations_array();
|
||||||
let selector =
|
let selector =
|
||||||
super::util::add_to_entity_selector(entity, &format!("tag={tag_name}"));
|
super::util::add_to_entity_selector(entity, &format!("tag={tag_name}"));
|
||||||
let cmd = Command::Execute(Execute::Store(
|
let cmd = Command::Execute(Execute::Store(
|
||||||
format!(
|
format!("success storage {temp_storage_name} {temp_storage_path} byte 1.0")
|
||||||
"success storage {temp_storage_name} {path} byte 1.0",
|
|
||||||
path = temp_storage_paths[0]
|
|
||||||
)
|
|
||||||
.into(),
|
.into(),
|
||||||
Box::new(Execute::Run(Box::new(Command::Raw(format!(
|
Box::new(Execute::Run(Box::new(Command::Raw(format!(
|
||||||
"execute if entity {selector}"
|
"execute if entity {selector}"
|
||||||
|
@ -175,7 +172,7 @@ fn print_function(
|
||||||
|
|
||||||
(
|
(
|
||||||
Some(cmd),
|
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 => {
|
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 {
|
let location = DataLocation::Storage {
|
||||||
storage_name,
|
storage_name,
|
||||||
path: std::mem::take(&mut storage_paths[0]),
|
path: storage_path,
|
||||||
r#type: StorageType::Int,
|
r#type: StorageType::Int,
|
||||||
};
|
};
|
||||||
let cmds = transpiler.transpile_primary_expression(
|
let cmds = transpiler.transpile_primary_expression(
|
||||||
|
@ -388,10 +385,10 @@ fn print_function(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Expression::Binary(binary) => {
|
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 {
|
let location = DataLocation::Storage {
|
||||||
storage_name,
|
storage_name,
|
||||||
path: std::mem::take(&mut storage_paths[0]),
|
path: storage_path,
|
||||||
r#type: StorageType::Int,
|
r#type: StorageType::Int,
|
||||||
};
|
};
|
||||||
let cmds =
|
let cmds =
|
||||||
|
|
|
@ -14,11 +14,11 @@ use crate::{
|
||||||
semantic::error::UnexpectedExpression,
|
semantic::error::UnexpectedExpression,
|
||||||
syntax::syntax_tree::{
|
syntax::syntax_tree::{
|
||||||
declaration::{Declaration, ImportItems},
|
declaration::{Declaration, ImportItems},
|
||||||
expression::{Expression, FunctionCall, Primary},
|
expression::{Expression, FunctionCall, PrefixOperator, Primary},
|
||||||
program::{Namespace, ProgramFile},
|
program::{Namespace, ProgramFile},
|
||||||
statement::{
|
statement::{
|
||||||
execute_block::{Conditional, Else, ExecuteBlock, ExecuteBlockHead, ExecuteBlockTail},
|
execute_block::{Conditional, Else, ExecuteBlock, ExecuteBlockHead, ExecuteBlockTail},
|
||||||
SemicolonStatement, Statement,
|
ReturnStatement, SemicolonStatement, Statement,
|
||||||
},
|
},
|
||||||
AnnotationAssignment,
|
AnnotationAssignment,
|
||||||
},
|
},
|
||||||
|
@ -359,9 +359,6 @@ impl Transpiler {
|
||||||
Statement::LiteralCommand(literal_command) => {
|
Statement::LiteralCommand(literal_command) => {
|
||||||
Ok(vec![literal_command.clean_command().into()])
|
Ok(vec![literal_command.clean_command().into()])
|
||||||
}
|
}
|
||||||
Statement::Run(run) => {
|
|
||||||
self.transpile_run_expression(run.expression(), program_identifier, scope, handler)
|
|
||||||
}
|
|
||||||
Statement::Block(_) => {
|
Statement::Block(_) => {
|
||||||
unreachable!("Only literal commands are allowed in functions at this time.")
|
unreachable!("Only literal commands are allowed in functions at this time.")
|
||||||
}
|
}
|
||||||
|
@ -411,6 +408,11 @@ impl Transpiler {
|
||||||
Expression::Primary(Primary::FunctionCall(func)) => {
|
Expression::Primary(Primary::FunctionCall(func)) => {
|
||||||
self.transpile_function_call(func, scope, handler)
|
self.transpile_function_call(func, scope, handler)
|
||||||
}
|
}
|
||||||
|
Expression::Primary(Primary::Prefix(prefix))
|
||||||
|
if matches!(prefix.operator(), PrefixOperator::Run(_)) =>
|
||||||
|
{
|
||||||
|
self.transpile_run_expression(prefix.operand(), scope, handler)
|
||||||
|
}
|
||||||
unexpected => {
|
unexpected => {
|
||||||
let error = TranspileError::UnexpectedExpression(UnexpectedExpression(
|
let error = TranspileError::UnexpectedExpression(UnexpectedExpression(
|
||||||
unexpected.clone(),
|
unexpected.clone(),
|
||||||
|
@ -433,24 +435,155 @@ impl Transpiler {
|
||||||
scope,
|
scope,
|
||||||
handler,
|
handler,
|
||||||
),
|
),
|
||||||
|
SemicolonStatement::Return(ret) => {
|
||||||
|
self.transpile_return_statement(ret, program_identifier, scope, handler)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::only_used_in_recursion)]
|
#[expect(clippy::too_many_lines)]
|
||||||
fn transpile_run_expression(
|
fn transpile_return_statement(
|
||||||
&mut self,
|
&mut self,
|
||||||
expression: &Expression,
|
ret: &ReturnStatement,
|
||||||
program_identifier: &str,
|
_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>,
|
scope: &Arc<Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<Vec<Command>> {
|
) -> TranspileResult<Vec<Command>> {
|
||||||
match expression {
|
match expression {
|
||||||
Expression::Primary(Primary::FunctionCall(func)) => {
|
Primary::FunctionCall(func) => self.transpile_function_call(func, scope, handler),
|
||||||
self.transpile_function_call(func, scope, handler)
|
Primary::Identifier(ident) => match scope.get_variable(ident.span.str()).as_deref() {
|
||||||
}
|
|
||||||
Expression::Primary(Primary::Identifier(ident)) => {
|
|
||||||
match scope.get_variable(ident.span.str()).as_deref() {
|
|
||||||
Some(VariableData::ComptimeValue {
|
Some(VariableData::ComptimeValue {
|
||||||
value,
|
value,
|
||||||
read_only: _,
|
read_only: _,
|
||||||
|
@ -472,7 +605,7 @@ impl Transpiler {
|
||||||
),
|
),
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
let error = TranspileError::UnexpectedExpression(UnexpectedExpression(
|
let error = TranspileError::UnexpectedExpression(UnexpectedExpression(
|
||||||
expression.clone(),
|
Expression::Primary(expression.clone()),
|
||||||
));
|
));
|
||||||
handler.receive(error.clone());
|
handler.receive(error.clone());
|
||||||
Err(error)
|
Err(error)
|
||||||
|
@ -484,26 +617,23 @@ impl Transpiler {
|
||||||
handler.receive(error.clone());
|
handler.receive(error.clone());
|
||||||
Err(error)
|
Err(error)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
expression @ Expression::Primary(
|
|
||||||
Primary::Integer(_)
|
Primary::Integer(_)
|
||||||
| Primary::Boolean(_)
|
| Primary::Boolean(_)
|
||||||
| Primary::Prefix(_)
|
| Primary::Prefix(_)
|
||||||
| Primary::Indexed(_),
|
| Primary::Indexed(_) => {
|
||||||
) => {
|
let error = TranspileError::UnexpectedExpression(UnexpectedExpression(
|
||||||
let error =
|
Expression::Primary(expression.clone()),
|
||||||
TranspileError::UnexpectedExpression(UnexpectedExpression(expression.clone()));
|
));
|
||||||
handler.receive(error.clone());
|
handler.receive(error.clone());
|
||||||
Err(error)
|
Err(error)
|
||||||
}
|
}
|
||||||
Expression::Primary(Primary::StringLiteral(string)) => {
|
Primary::StringLiteral(string) => {
|
||||||
Ok(vec![Command::Raw(string.str_content().to_string())])
|
Ok(vec![Command::Raw(string.str_content().to_string())])
|
||||||
}
|
}
|
||||||
Expression::Primary(Primary::MacroStringLiteral(string)) => {
|
Primary::MacroStringLiteral(string) => Ok(vec![Command::UsesMacro(string.into())]),
|
||||||
Ok(vec![Command::UsesMacro(string.into())])
|
Primary::Lua(code) => match code.eval_comptime(scope, handler)? {
|
||||||
}
|
|
||||||
Expression::Primary(Primary::Lua(code)) => match code.eval_comptime(scope, handler)? {
|
|
||||||
Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
||||||
Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
|
Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
|
||||||
Ok(ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)) => {
|
Ok(ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)) => {
|
||||||
|
@ -523,13 +653,10 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Expression::Primary(Primary::Parenthesized(parenthesized)) => self
|
Primary::Parenthesized(parenthesized) => match parenthesized.expression().as_ref() {
|
||||||
.transpile_run_expression(
|
Expression::Primary(expression) => {
|
||||||
parenthesized.expression(),
|
self.transpile_run_expression(expression, scope, handler)
|
||||||
program_identifier,
|
}
|
||||||
scope,
|
|
||||||
handler,
|
|
||||||
),
|
|
||||||
Expression::Binary(bin) => match bin.comptime_eval(scope, handler) {
|
Expression::Binary(bin) => match bin.comptime_eval(scope, handler) {
|
||||||
Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
||||||
Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
|
Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
|
||||||
|
@ -542,6 +669,7 @@ impl Transpiler {
|
||||||
Err(err)
|
Err(err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -381,8 +381,7 @@ impl Transpiler {
|
||||||
handler,
|
handler,
|
||||||
)?;
|
)?;
|
||||||
if is_global {
|
if is_global {
|
||||||
let (temp_objective, temp_targets) = self.get_temp_scoreboard_locations(1);
|
let (temp_objective, [temp_target]) = self.get_temp_scoreboard_locations_array();
|
||||||
let temp_target = &temp_targets[0];
|
|
||||||
let test_cmd = match declaration.variable_type().keyword {
|
let test_cmd = match declaration.variable_type().keyword {
|
||||||
KeywordKind::Int => {
|
KeywordKind::Int => {
|
||||||
Command::Raw(format!("scoreboard players get {name} {target}"))
|
Command::Raw(format!("scoreboard players get {name} {target}"))
|
||||||
|
|
Loading…
Reference in New Issue