implement first version of compiler variables
This commit is contained in:
parent
ab76b1d43e
commit
469b8d3875
|
@ -52,6 +52,7 @@ pub enum KeywordKind {
|
||||||
Int,
|
Int,
|
||||||
Bool,
|
Bool,
|
||||||
Macro,
|
Macro,
|
||||||
|
Val,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for KeywordKind {
|
impl Display for KeywordKind {
|
||||||
|
@ -117,6 +118,7 @@ impl KeywordKind {
|
||||||
Self::Int => "int",
|
Self::Int => "int",
|
||||||
Self::Bool => "bool",
|
Self::Bool => "bool",
|
||||||
Self::Macro => "macro",
|
Self::Macro => "macro",
|
||||||
|
Self::Val => "val",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -396,6 +396,10 @@ impl Assignment {
|
||||||
let expected = match variable_type {
|
let expected = match variable_type {
|
||||||
VariableType::BooleanStorage | VariableType::Tag => ValueType::Boolean,
|
VariableType::BooleanStorage | VariableType::Tag => ValueType::Boolean,
|
||||||
VariableType::ScoreboardValue => ValueType::Integer,
|
VariableType::ScoreboardValue => ValueType::Integer,
|
||||||
|
VariableType::ComptimeValue => {
|
||||||
|
// TODO: check if the expression is a constant expression
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let err = error::Error::AssignmentError(AssignmentError {
|
let err = error::Error::AssignmentError(AssignmentError {
|
||||||
identifier: self.destination().span(),
|
identifier: self.destination().span(),
|
||||||
|
@ -445,6 +449,12 @@ impl VariableDeclaration {
|
||||||
KeywordKind::Int => (VariableType::ScoreboardValue, ExpectedType::Integer),
|
KeywordKind::Int => (VariableType::ScoreboardValue, ExpectedType::Integer),
|
||||||
_ => unreachable!("variable type is not a valid type"),
|
_ => unreachable!("variable type is not a valid type"),
|
||||||
},
|
},
|
||||||
|
Self::ComptimeValue(decl) => {
|
||||||
|
if let Some(assignment) = decl.assignment() {
|
||||||
|
assignment.expression().analyze_semantics(scope, handler)?;
|
||||||
|
}
|
||||||
|
(VariableType::ComptimeValue, ExpectedType::Any)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
scope.set_variable(name, var);
|
scope.set_variable(name, var);
|
||||||
let assignment = match self {
|
let assignment = match self {
|
||||||
|
@ -459,7 +469,12 @@ impl VariableDeclaration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Single(single) => single.assignment().as_ref(),
|
Self::Single(single) => single.assignment().as_ref(),
|
||||||
|
Self::ComptimeValue(decl) => decl.assignment().as_ref(),
|
||||||
};
|
};
|
||||||
|
if var == VariableType::ComptimeValue {
|
||||||
|
// TODO: check if the expression is a constant expression
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
if let Some(assignment) = assignment {
|
if let Some(assignment) = assignment {
|
||||||
let expected = match var {
|
let expected = match var {
|
||||||
VariableType::BooleanStorage | VariableType::Tag => ValueType::Boolean,
|
VariableType::BooleanStorage | VariableType::Tag => ValueType::Boolean,
|
||||||
|
@ -689,8 +704,35 @@ impl Binary {
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Result<(), error::Error> {
|
) -> Result<(), error::Error> {
|
||||||
match self.operator() {
|
match self.operator() {
|
||||||
BinaryOperator::Add(_)
|
BinaryOperator::Add(_) => {
|
||||||
| BinaryOperator::Subtract(_)
|
if (self
|
||||||
|
.left_operand()
|
||||||
|
.can_yield_type_semantics(ValueType::Integer, scope)
|
||||||
|
&& self
|
||||||
|
.right_operand()
|
||||||
|
.can_yield_type_semantics(ValueType::Integer, scope))
|
||||||
|
|| self
|
||||||
|
.left_operand()
|
||||||
|
.can_yield_type_semantics(ValueType::String, scope)
|
||||||
|
|| self
|
||||||
|
.right_operand()
|
||||||
|
.can_yield_type_semantics(ValueType::String, scope)
|
||||||
|
{
|
||||||
|
self.left_operand().analyze_semantics(scope, handler)?;
|
||||||
|
self.right_operand().analyze_semantics(scope, handler)
|
||||||
|
} else {
|
||||||
|
let err = error::Error::MismatchedTypes(MismatchedTypes {
|
||||||
|
expected_type: ExpectedType::AnyOf(vec![
|
||||||
|
ExpectedType::Integer,
|
||||||
|
ExpectedType::String,
|
||||||
|
]),
|
||||||
|
expression: self.span(),
|
||||||
|
});
|
||||||
|
handler.receive(err.clone());
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BinaryOperator::Subtract(_)
|
||||||
| BinaryOperator::Multiply(_)
|
| BinaryOperator::Multiply(_)
|
||||||
| BinaryOperator::Divide(_)
|
| BinaryOperator::Divide(_)
|
||||||
| BinaryOperator::Modulo(_) => {
|
| BinaryOperator::Modulo(_) => {
|
||||||
|
@ -779,8 +821,8 @@ impl Binary {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LuaCode {
|
impl LuaCode {
|
||||||
#[expect(clippy::unused_self, clippy::unnecessary_wraps)]
|
#[expect(clippy::unused_self)]
|
||||||
#[cfg_attr(feature = "lua", expect(unused_variables))]
|
#[cfg_attr(feature = "lua", expect(unused_variables, clippy::unnecessary_wraps))]
|
||||||
fn analyze_semantics(
|
fn analyze_semantics(
|
||||||
&self,
|
&self,
|
||||||
_scope: &SemanticScope,
|
_scope: &SemanticScope,
|
||||||
|
|
|
@ -21,6 +21,8 @@ pub enum VariableType {
|
||||||
BooleanStorageArray,
|
BooleanStorageArray,
|
||||||
/// Compiler internal function.
|
/// Compiler internal function.
|
||||||
InternalFunction,
|
InternalFunction,
|
||||||
|
/// Compiler internal value.
|
||||||
|
ComptimeValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A scope that stores variables.
|
/// A scope that stores variables.
|
||||||
|
|
|
@ -325,6 +325,7 @@ pub enum VariableDeclaration {
|
||||||
Array(ArrayVariableDeclaration),
|
Array(ArrayVariableDeclaration),
|
||||||
Score(ScoreVariableDeclaration),
|
Score(ScoreVariableDeclaration),
|
||||||
Tag(TagVariableDeclaration),
|
Tag(TagVariableDeclaration),
|
||||||
|
ComptimeValue(ComptimeValueDeclaration),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceElement for VariableDeclaration {
|
impl SourceElement for VariableDeclaration {
|
||||||
|
@ -334,6 +335,7 @@ impl SourceElement for VariableDeclaration {
|
||||||
Self::Array(declaration) => declaration.span(),
|
Self::Array(declaration) => declaration.span(),
|
||||||
Self::Score(declaration) => declaration.span(),
|
Self::Score(declaration) => declaration.span(),
|
||||||
Self::Tag(declaration) => declaration.span(),
|
Self::Tag(declaration) => declaration.span(),
|
||||||
|
Self::ComptimeValue(declaration) => declaration.span(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -347,6 +349,7 @@ impl VariableDeclaration {
|
||||||
Self::Array(declaration) => &declaration.identifier,
|
Self::Array(declaration) => &declaration.identifier,
|
||||||
Self::Score(declaration) => &declaration.identifier,
|
Self::Score(declaration) => &declaration.identifier,
|
||||||
Self::Tag(declaration) => &declaration.identifier,
|
Self::Tag(declaration) => &declaration.identifier,
|
||||||
|
Self::ComptimeValue(declaration) => &declaration.identifier,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,6 +361,7 @@ impl VariableDeclaration {
|
||||||
Self::Array(declaration) => &declaration.variable_type,
|
Self::Array(declaration) => &declaration.variable_type,
|
||||||
Self::Score(declaration) => &declaration.int_keyword,
|
Self::Score(declaration) => &declaration.int_keyword,
|
||||||
Self::Tag(declaration) => &declaration.bool_keyword,
|
Self::Tag(declaration) => &declaration.bool_keyword,
|
||||||
|
Self::ComptimeValue(declaration) => &declaration.val_keyword,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,6 +387,14 @@ impl VariableDeclaration {
|
||||||
declaration.annotations.push_front(annotation);
|
declaration.annotations.push_front(annotation);
|
||||||
Ok(Self::Tag(declaration))
|
Ok(Self::Tag(declaration))
|
||||||
}
|
}
|
||||||
|
Self::ComptimeValue(_) => {
|
||||||
|
let err = Error::InvalidAnnotation(InvalidAnnotation {
|
||||||
|
annotation: annotation.assignment.identifier.span,
|
||||||
|
target: "comptime values".to_string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -681,6 +693,53 @@ impl TagVariableDeclaration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a compile time value declaration in the syntax tree.
|
||||||
|
///
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ```ebnf
|
||||||
|
/// ComptimeValueDeclaration:
|
||||||
|
/// 'val' identifier VariableDeclarationAssignment?
|
||||||
|
/// ```
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||||
|
pub struct ComptimeValueDeclaration {
|
||||||
|
/// The type of the variable.
|
||||||
|
#[get = "pub"]
|
||||||
|
val_keyword: Keyword,
|
||||||
|
/// The identifier of the variable.
|
||||||
|
#[get = "pub"]
|
||||||
|
identifier: Identifier,
|
||||||
|
/// The optional assignment of the variable.
|
||||||
|
#[get = "pub"]
|
||||||
|
assignment: Option<VariableDeclarationAssignment>,
|
||||||
|
/// The annotations of the variable declaration.
|
||||||
|
#[get = "pub"]
|
||||||
|
annotations: VecDeque<Annotation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceElement for ComptimeValueDeclaration {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
self.val_keyword
|
||||||
|
.span()
|
||||||
|
.join(
|
||||||
|
&self
|
||||||
|
.assignment
|
||||||
|
.as_ref()
|
||||||
|
.map_or_else(|| self.identifier.span(), SourceElement::span),
|
||||||
|
)
|
||||||
|
.expect("The span of the single variable declaration is invalid.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComptimeValueDeclaration {
|
||||||
|
/// Dissolves the [`ComptimeValueDeclaration`] into its components.
|
||||||
|
#[must_use]
|
||||||
|
pub fn dissolve(self) -> (Keyword, Identifier, Option<VariableDeclarationAssignment>) {
|
||||||
|
(self.val_keyword, self.identifier, self.assignment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents an assignment in the syntax tree.
|
/// Represents an assignment in the syntax tree.
|
||||||
///
|
///
|
||||||
/// Syntax Synopsis:
|
/// Syntax Synopsis:
|
||||||
|
@ -883,7 +942,10 @@ impl Parser<'_> {
|
||||||
) -> ParseResult<Semicolon> {
|
) -> ParseResult<Semicolon> {
|
||||||
let statement = match self.stop_at_significant() {
|
let statement = match self.stop_at_significant() {
|
||||||
Reading::Atomic(Token::Keyword(keyword))
|
Reading::Atomic(Token::Keyword(keyword))
|
||||||
if matches!(keyword.keyword, KeywordKind::Int | KeywordKind::Bool) =>
|
if matches!(
|
||||||
|
keyword.keyword,
|
||||||
|
KeywordKind::Int | KeywordKind::Bool | KeywordKind::Val
|
||||||
|
) =>
|
||||||
{
|
{
|
||||||
self.parse_variable_declaration(handler)
|
self.parse_variable_declaration(handler)
|
||||||
.map(SemicolonStatement::VariableDeclaration)
|
.map(SemicolonStatement::VariableDeclaration)
|
||||||
|
@ -895,18 +957,20 @@ impl Parser<'_> {
|
||||||
let destination = {
|
let destination = {
|
||||||
let identifier = p.parse_identifier(&VoidHandler)?;
|
let identifier = p.parse_identifier(&VoidHandler)?;
|
||||||
|
|
||||||
if let Ok(tree) = p.step_into(
|
match p.stop_at_significant() {
|
||||||
|
Reading::IntoDelimited(punc) if punc.punctuation == '[' => {
|
||||||
|
let tree = p.step_into(
|
||||||
Delimiter::Bracket,
|
Delimiter::Bracket,
|
||||||
|pp| pp.parse_expression(&VoidHandler),
|
|pp| pp.parse_expression(&VoidHandler),
|
||||||
&VoidHandler,
|
&VoidHandler,
|
||||||
) {
|
)?;
|
||||||
let open = tree.open;
|
let open = tree.open;
|
||||||
let close = tree.close;
|
let close = tree.close;
|
||||||
let expression = tree.tree?;
|
let expression = tree.tree?;
|
||||||
|
|
||||||
AssignmentDestination::Indexed(identifier, open, expression, close)
|
AssignmentDestination::Indexed(identifier, open, expression, close)
|
||||||
} else {
|
}
|
||||||
AssignmentDestination::Identifier(identifier)
|
_ => AssignmentDestination::Identifier(identifier),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let equals = p.parse_punctuation('=', true, &VoidHandler)?;
|
let equals = p.parse_punctuation('=', true, &VoidHandler)?;
|
||||||
|
@ -948,7 +1012,10 @@ impl Parser<'_> {
|
||||||
|
|
||||||
let variable_type = match self.stop_at_significant() {
|
let variable_type = match self.stop_at_significant() {
|
||||||
Reading::Atomic(Token::Keyword(keyword))
|
Reading::Atomic(Token::Keyword(keyword))
|
||||||
if matches!(keyword.keyword, KeywordKind::Int | KeywordKind::Bool) =>
|
if matches!(
|
||||||
|
keyword.keyword,
|
||||||
|
KeywordKind::Int | KeywordKind::Bool | KeywordKind::Val
|
||||||
|
) =>
|
||||||
{
|
{
|
||||||
self.forward();
|
self.forward();
|
||||||
keyword
|
keyword
|
||||||
|
@ -983,7 +1050,10 @@ impl Parser<'_> {
|
||||||
let identifier = self.parse_identifier(handler)?;
|
let identifier = self.parse_identifier(handler)?;
|
||||||
|
|
||||||
match self.stop_at_significant() {
|
match self.stop_at_significant() {
|
||||||
Reading::IntoDelimited(punc) if punc.punctuation == '[' => {
|
Reading::IntoDelimited(punc)
|
||||||
|
if punc.punctuation == '['
|
||||||
|
&& matches!(variable_type.keyword, KeywordKind::Int | KeywordKind::Bool) =>
|
||||||
|
{
|
||||||
let tree = self.step_into(
|
let tree = self.step_into(
|
||||||
Delimiter::Bracket,
|
Delimiter::Bracket,
|
||||||
|p| {
|
|p| {
|
||||||
|
@ -1071,6 +1141,16 @@ impl Parser<'_> {
|
||||||
let expression = self.parse_expression(handler)?;
|
let expression = self.parse_expression(handler)?;
|
||||||
let assignment = VariableDeclarationAssignment { equals, expression };
|
let assignment = VariableDeclarationAssignment { equals, expression };
|
||||||
|
|
||||||
|
if variable_type.keyword == KeywordKind::Val {
|
||||||
|
Ok(VariableDeclaration::ComptimeValue(
|
||||||
|
ComptimeValueDeclaration {
|
||||||
|
val_keyword: variable_type,
|
||||||
|
identifier,
|
||||||
|
assignment: Some(assignment),
|
||||||
|
annotations: VecDeque::new(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
} else {
|
||||||
Ok(VariableDeclaration::Single(SingleVariableDeclaration {
|
Ok(VariableDeclaration::Single(SingleVariableDeclaration {
|
||||||
variable_type,
|
variable_type,
|
||||||
identifier,
|
identifier,
|
||||||
|
@ -1078,6 +1158,7 @@ impl Parser<'_> {
|
||||||
annotations: VecDeque::new(),
|
annotations: VecDeque::new(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// SingleVariableDeclaration without Assignment
|
// SingleVariableDeclaration without Assignment
|
||||||
_ => Ok(VariableDeclaration::Single(SingleVariableDeclaration {
|
_ => Ok(VariableDeclaration::Single(SingleVariableDeclaration {
|
||||||
variable_type,
|
variable_type,
|
||||||
|
|
|
@ -46,6 +46,8 @@ pub enum TranspileError {
|
||||||
IllegalIndexing(#[from] IllegalIndexing),
|
IllegalIndexing(#[from] IllegalIndexing),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
InvalidArgument(#[from] InvalidArgument),
|
InvalidArgument(#[from] InvalidArgument),
|
||||||
|
#[error(transparent)]
|
||||||
|
NotComptime(#[from] NotComptime),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of a transpilation operation.
|
/// The result of a transpilation operation.
|
||||||
|
@ -447,3 +449,32 @@ impl Display for InvalidArgument {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for InvalidArgument {}
|
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 {}
|
||||||
|
|
|
@ -12,7 +12,9 @@ use shulkerbox::prelude::{Command, Condition, Execute};
|
||||||
|
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
use super::{
|
use super::{
|
||||||
error::{IllegalIndexing, IllegalIndexingReason, MismatchedTypes, UnknownIdentifier},
|
error::{
|
||||||
|
IllegalIndexing, IllegalIndexingReason, MismatchedTypes, NotComptime, UnknownIdentifier,
|
||||||
|
},
|
||||||
Scope, TranspileResult, Transpiler, VariableData,
|
Scope, TranspileResult, Transpiler, VariableData,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
|
@ -238,12 +240,14 @@ impl Expression {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate at compile-time.
|
/// Evaluate at compile-time.
|
||||||
#[must_use]
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// - If the expression is not compile-time evaluatable.
|
||||||
pub fn comptime_eval(
|
pub fn comptime_eval(
|
||||||
&self,
|
&self,
|
||||||
scope: &Arc<Scope>,
|
scope: &Arc<Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Option<ComptimeValue> {
|
) -> Result<ComptimeValue, NotComptime> {
|
||||||
match self {
|
match self {
|
||||||
Self::Primary(primary) => primary.comptime_eval(scope, handler),
|
Self::Primary(primary) => primary.comptime_eval(scope, handler),
|
||||||
Self::Binary(binary) => binary.comptime_eval(scope, handler),
|
Self::Binary(binary) => binary.comptime_eval(scope, handler),
|
||||||
|
@ -333,19 +337,40 @@ impl Primary {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate at compile-time.
|
/// Evaluate at compile-time.
|
||||||
#[must_use]
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// - If the expression is not compile-time evaluatable.
|
||||||
pub fn comptime_eval(
|
pub fn comptime_eval(
|
||||||
&self,
|
&self,
|
||||||
scope: &Arc<Scope>,
|
scope: &Arc<Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Option<ComptimeValue> {
|
) -> Result<ComptimeValue, NotComptime> {
|
||||||
match self {
|
match self {
|
||||||
Self::Boolean(boolean) => Some(ComptimeValue::Boolean(boolean.value())),
|
Self::Boolean(boolean) => Ok(ComptimeValue::Boolean(boolean.value())),
|
||||||
Self::Integer(int) => Some(ComptimeValue::Integer(int.as_i64())),
|
Self::Integer(int) => Ok(ComptimeValue::Integer(int.as_i64())),
|
||||||
Self::StringLiteral(string_literal) => Some(ComptimeValue::String(
|
Self::StringLiteral(string_literal) => Ok(ComptimeValue::String(
|
||||||
string_literal.str_content().to_string(),
|
string_literal.str_content().to_string(),
|
||||||
)),
|
)),
|
||||||
Self::Identifier(_) | Self::FunctionCall(_) | Self::Indexed(_) => None,
|
Self::Identifier(ident) => scope.get_variable(ident.span.str()).map_or_else(
|
||||||
|
|| {
|
||||||
|
Err(NotComptime {
|
||||||
|
expression: self.span(),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|var| match var.as_ref() {
|
||||||
|
VariableData::ComptimeValue { value } => {
|
||||||
|
value.read().unwrap().clone().ok_or_else(|| NotComptime {
|
||||||
|
expression: ident.span.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Err(NotComptime {
|
||||||
|
expression: self.span(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Self::FunctionCall(_) | Self::Indexed(_) => Err(NotComptime {
|
||||||
|
expression: self.span(),
|
||||||
|
}),
|
||||||
Self::Parenthesized(parenthesized) => {
|
Self::Parenthesized(parenthesized) => {
|
||||||
parenthesized.expression().comptime_eval(scope, handler)
|
parenthesized.expression().comptime_eval(scope, handler)
|
||||||
}
|
}
|
||||||
|
@ -355,12 +380,14 @@ impl Primary {
|
||||||
.comptime_eval(scope, handler)
|
.comptime_eval(scope, handler)
|
||||||
.and_then(|val| match (prefix.operator(), val) {
|
.and_then(|val| match (prefix.operator(), val) {
|
||||||
(PrefixOperator::LogicalNot(_), ComptimeValue::Boolean(boolean)) => {
|
(PrefixOperator::LogicalNot(_), ComptimeValue::Boolean(boolean)) => {
|
||||||
Some(ComptimeValue::Boolean(!boolean))
|
Ok(ComptimeValue::Boolean(!boolean))
|
||||||
}
|
}
|
||||||
(PrefixOperator::Negate(_), ComptimeValue::Integer(int)) => {
|
(PrefixOperator::Negate(_), ComptimeValue::Integer(int)) => {
|
||||||
Some(ComptimeValue::Integer(-int))
|
Ok(ComptimeValue::Integer(-int))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => Err(NotComptime {
|
||||||
|
expression: prefix.span(),
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Self::Lua(lua) => lua
|
Self::Lua(lua) => lua
|
||||||
|
@ -368,19 +395,21 @@ impl Primary {
|
||||||
.inspect_err(|err| {
|
.inspect_err(|err| {
|
||||||
handler.receive(err.clone());
|
handler.receive(err.clone());
|
||||||
})
|
})
|
||||||
.ok()
|
.map_err(|_| NotComptime {
|
||||||
.flatten(),
|
expression: lua.span(),
|
||||||
|
})
|
||||||
|
.and_then(|val| val),
|
||||||
Self::MacroStringLiteral(macro_string_literal) => {
|
Self::MacroStringLiteral(macro_string_literal) => {
|
||||||
if macro_string_literal
|
if macro_string_literal
|
||||||
.parts()
|
.parts()
|
||||||
.iter()
|
.iter()
|
||||||
.any(|part| matches!(part, MacroStringLiteralPart::MacroUsage { .. }))
|
.any(|part| matches!(part, MacroStringLiteralPart::MacroUsage { .. }))
|
||||||
{
|
{
|
||||||
Some(ComptimeValue::MacroString(
|
Ok(ComptimeValue::MacroString(
|
||||||
macro_string_literal.clone().into(),
|
macro_string_literal.clone().into(),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Some(ComptimeValue::String(macro_string_literal.str_content()))
|
Ok(ComptimeValue::String(macro_string_literal.str_content()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -430,12 +459,14 @@ impl Binary {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate at compile-time.
|
/// Evaluate at compile-time.
|
||||||
#[must_use]
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// - If the expression is not compile-time evaluatable.
|
||||||
pub fn comptime_eval(
|
pub fn comptime_eval(
|
||||||
&self,
|
&self,
|
||||||
scope: &Arc<Scope>,
|
scope: &Arc<Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Option<ComptimeValue> {
|
) -> Result<ComptimeValue, NotComptime> {
|
||||||
let left = self.left_operand().comptime_eval(scope, handler)?;
|
let left = self.left_operand().comptime_eval(scope, handler)?;
|
||||||
let right = self.right_operand().comptime_eval(scope, handler)?;
|
let right = self.right_operand().comptime_eval(scope, handler)?;
|
||||||
|
|
||||||
|
@ -449,7 +480,7 @@ impl Binary {
|
||||||
.right_operand()
|
.right_operand()
|
||||||
.can_yield_type(ValueType::Boolean, scope) =>
|
.can_yield_type(ValueType::Boolean, scope) =>
|
||||||
{
|
{
|
||||||
Some(ComptimeValue::Boolean(true))
|
Ok(ComptimeValue::Boolean(true))
|
||||||
}
|
}
|
||||||
(ComptimeValue::Boolean(false), _) | (_, ComptimeValue::Boolean(false))
|
(ComptimeValue::Boolean(false), _) | (_, ComptimeValue::Boolean(false))
|
||||||
if matches!(self.operator(), BinaryOperator::LogicalAnd(..))
|
if matches!(self.operator(), BinaryOperator::LogicalAnd(..))
|
||||||
|
@ -460,15 +491,17 @@ impl Binary {
|
||||||
.right_operand()
|
.right_operand()
|
||||||
.can_yield_type(ValueType::Boolean, scope) =>
|
.can_yield_type(ValueType::Boolean, scope) =>
|
||||||
{
|
{
|
||||||
Some(ComptimeValue::Boolean(false))
|
Ok(ComptimeValue::Boolean(false))
|
||||||
}
|
}
|
||||||
(ComptimeValue::Boolean(left), ComptimeValue::Boolean(right)) => {
|
(ComptimeValue::Boolean(left), ComptimeValue::Boolean(right)) => {
|
||||||
match self.operator() {
|
match self.operator() {
|
||||||
BinaryOperator::Equal(..) => Some(ComptimeValue::Boolean(left == right)),
|
BinaryOperator::Equal(..) => Ok(ComptimeValue::Boolean(left == right)),
|
||||||
BinaryOperator::NotEqual(..) => Some(ComptimeValue::Boolean(left != right)),
|
BinaryOperator::NotEqual(..) => Ok(ComptimeValue::Boolean(left != right)),
|
||||||
BinaryOperator::LogicalAnd(..) => Some(ComptimeValue::Boolean(left && right)),
|
BinaryOperator::LogicalAnd(..) => Ok(ComptimeValue::Boolean(left && right)),
|
||||||
BinaryOperator::LogicalOr(..) => Some(ComptimeValue::Boolean(left || right)),
|
BinaryOperator::LogicalOr(..) => Ok(ComptimeValue::Boolean(left || right)),
|
||||||
_ => None,
|
_ => Err(NotComptime {
|
||||||
|
expression: self.span(),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(ComptimeValue::Integer(left), ComptimeValue::Integer(right)) => {
|
(ComptimeValue::Integer(left), ComptimeValue::Integer(right)) => {
|
||||||
|
@ -498,14 +531,37 @@ impl Binary {
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
.ok_or_else(|| NotComptime {
|
||||||
|
expression: self.span(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
(ComptimeValue::String(left), ComptimeValue::String(right)) => match self.operator() {
|
(ComptimeValue::String(left), ComptimeValue::String(right)) => match self.operator() {
|
||||||
BinaryOperator::Add(..) => Some(ComptimeValue::String(left + &right)),
|
BinaryOperator::Add(..) => Ok(ComptimeValue::String(left + &right)),
|
||||||
BinaryOperator::Equal(..) => Some(ComptimeValue::Boolean(left == right)),
|
BinaryOperator::Equal(..) => Ok(ComptimeValue::Boolean(left == right)),
|
||||||
BinaryOperator::NotEqual(..) => Some(ComptimeValue::Boolean(left != right)),
|
BinaryOperator::NotEqual(..) => Ok(ComptimeValue::Boolean(left != right)),
|
||||||
_ => None,
|
_ => Err(NotComptime {
|
||||||
|
expression: self.span(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
_ => None,
|
// TODO: also allow macro strings
|
||||||
|
(
|
||||||
|
left @ ComptimeValue::String(_),
|
||||||
|
right @ (ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)),
|
||||||
|
)
|
||||||
|
| (
|
||||||
|
left @ (ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)),
|
||||||
|
right @ ComptimeValue::String(_),
|
||||||
|
) => match self.operator() {
|
||||||
|
BinaryOperator::Add(_) => Ok(ComptimeValue::String(
|
||||||
|
left.to_string_no_macro().unwrap() + &right.to_string_no_macro().unwrap(),
|
||||||
|
)),
|
||||||
|
_ => Err(NotComptime {
|
||||||
|
expression: self.span(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
_ => Err(NotComptime {
|
||||||
|
expression: self.span(),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -641,7 +697,7 @@ impl Transpiler {
|
||||||
Primary::Lua(lua) =>
|
Primary::Lua(lua) =>
|
||||||
{
|
{
|
||||||
#[expect(clippy::option_if_let_else)]
|
#[expect(clippy::option_if_let_else)]
|
||||||
if let Some(value) = lua.eval_comptime(scope, handler)? {
|
if let Ok(value) = lua.eval_comptime(scope, handler)? {
|
||||||
self.store_comptime_value(&value, target, lua, handler)
|
self.store_comptime_value(&value, target, lua, handler)
|
||||||
} else {
|
} else {
|
||||||
let err = TranspileError::MissingValue(MissingValue {
|
let err = TranspileError::MissingValue(MissingValue {
|
||||||
|
@ -803,7 +859,7 @@ impl Transpiler {
|
||||||
if let Some(variable) = variable.as_deref() {
|
if let Some(variable) = variable.as_deref() {
|
||||||
let from = match variable {
|
let from = match variable {
|
||||||
VariableData::Scoreboard { objective } => {
|
VariableData::Scoreboard { objective } => {
|
||||||
if let Some(ComptimeValue::String(target)) =
|
if let Ok(ComptimeValue::String(target)) =
|
||||||
indexed.index().comptime_eval(scope, handler)
|
indexed.index().comptime_eval(scope, handler)
|
||||||
{
|
{
|
||||||
Ok(DataLocation::ScoreboardValue {
|
Ok(DataLocation::ScoreboardValue {
|
||||||
|
@ -822,7 +878,7 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VariableData::ScoreboardArray { objective, targets } => {
|
VariableData::ScoreboardArray { objective, targets } => {
|
||||||
if let Some(ComptimeValue::Integer(index)) =
|
if let Ok(ComptimeValue::Integer(index)) =
|
||||||
indexed.index().comptime_eval(scope, handler)
|
indexed.index().comptime_eval(scope, handler)
|
||||||
{
|
{
|
||||||
if let Some(target) = usize::try_from(index)
|
if let Some(target) = usize::try_from(index)
|
||||||
|
@ -860,7 +916,7 @@ impl Transpiler {
|
||||||
storage_name,
|
storage_name,
|
||||||
paths,
|
paths,
|
||||||
} => {
|
} => {
|
||||||
if let Some(ComptimeValue::Integer(index)) =
|
if let Ok(ComptimeValue::Integer(index)) =
|
||||||
indexed.index().comptime_eval(scope, handler)
|
indexed.index().comptime_eval(scope, handler)
|
||||||
{
|
{
|
||||||
if let Some(path) = usize::try_from(index)
|
if let Some(path) = usize::try_from(index)
|
||||||
|
@ -924,7 +980,7 @@ impl Transpiler {
|
||||||
scope: &Arc<super::Scope>,
|
scope: &Arc<super::Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<Vec<Command>> {
|
) -> TranspileResult<Vec<Command>> {
|
||||||
if let Some(value) = binary.comptime_eval(scope, handler) {
|
if let Ok(value) = binary.comptime_eval(scope, handler) {
|
||||||
self.store_comptime_value(&value, target, binary, handler)
|
self.store_comptime_value(&value, target, binary, handler)
|
||||||
} else {
|
} else {
|
||||||
match binary.operator() {
|
match binary.operator() {
|
||||||
|
@ -1087,7 +1143,7 @@ impl Transpiler {
|
||||||
storage_name,
|
storage_name,
|
||||||
paths,
|
paths,
|
||||||
} => {
|
} => {
|
||||||
if let Some(ComptimeValue::Integer(index)) =
|
if let Ok(ComptimeValue::Integer(index)) =
|
||||||
indexed.index().comptime_eval(scope, handler)
|
indexed.index().comptime_eval(scope, handler)
|
||||||
{
|
{
|
||||||
if let Some(path) = usize::try_from(index)
|
if let Some(path) = usize::try_from(index)
|
||||||
|
@ -1168,15 +1224,15 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Primary::Lua(lua) => match lua.eval_comptime(scope, handler)? {
|
Primary::Lua(lua) => match lua.eval_comptime(scope, handler)? {
|
||||||
Some(ComptimeValue::String(value)) => Ok((
|
Ok(ComptimeValue::String(value)) => Ok((
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
ExtendedCondition::Runtime(Condition::Atom(value.into())),
|
ExtendedCondition::Runtime(Condition::Atom(value.into())),
|
||||||
)),
|
)),
|
||||||
Some(ComptimeValue::MacroString(value)) => Ok((
|
Ok(ComptimeValue::MacroString(value)) => Ok((
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
ExtendedCondition::Runtime(Condition::Atom(value.into())),
|
ExtendedCondition::Runtime(Condition::Atom(value.into())),
|
||||||
)),
|
)),
|
||||||
Some(ComptimeValue::Boolean(boolean)) => {
|
Ok(ComptimeValue::Boolean(boolean)) => {
|
||||||
Ok((Vec::new(), ExtendedCondition::Comptime(boolean)))
|
Ok((Vec::new(), ExtendedCondition::Comptime(boolean)))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
@ -102,6 +102,7 @@ impl Transpiler {
|
||||||
TranspileAnnotationValue::None => Ok(identifier_span.str().to_string()),
|
TranspileAnnotationValue::None => Ok(identifier_span.str().to_string()),
|
||||||
TranspileAnnotationValue::Expression(expr) => expr
|
TranspileAnnotationValue::Expression(expr) => expr
|
||||||
.comptime_eval(scope, handler)
|
.comptime_eval(scope, handler)
|
||||||
|
.ok()
|
||||||
.and_then(|val| val.to_string_no_macro())
|
.and_then(|val| val.to_string_no_macro())
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
let err = TranspileError::IllegalAnnotationContent(
|
let err = TranspileError::IllegalAnnotationContent(
|
||||||
|
@ -293,13 +294,10 @@ impl Transpiler {
|
||||||
let value = match expression {
|
let value = match expression {
|
||||||
Expression::Primary(Primary::Lua(lua)) => {
|
Expression::Primary(Primary::Lua(lua)) => {
|
||||||
lua.eval_comptime(scope, handler).and_then(|val| match val {
|
lua.eval_comptime(scope, handler).and_then(|val| match val {
|
||||||
Some(ComptimeValue::MacroString(s)) => Ok(Parameter::Static(s)),
|
Ok(ComptimeValue::MacroString(s)) => Ok(Parameter::Static(s)),
|
||||||
Some(val) => Ok(Parameter::Static(val.to_macro_string())),
|
Ok(val) => Ok(Parameter::Static(val.to_macro_string())),
|
||||||
None => {
|
Err(err) => {
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
let err = TranspileError::NotComptime(err);
|
||||||
expression: expression.span(),
|
|
||||||
expected_type: ExpectedType::String,
|
|
||||||
});
|
|
||||||
handler.receive(err.clone());
|
handler.receive(err.clone());
|
||||||
Err(err)
|
Err(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,17 +183,17 @@ fn print_function(
|
||||||
|
|
||||||
let args = get_args_assert_in_range(call, 1..=2)?;
|
let args = get_args_assert_in_range(call, 1..=2)?;
|
||||||
let first = args.first().expect("checked range");
|
let first = args.first().expect("checked range");
|
||||||
let (target, message_expression) = args.get(1).map_or_else(
|
let (target, message_expression) = if let Some(second) = args.get(1) {
|
||||||
|| ("@a".into(), first),
|
|
||||||
|second| {
|
|
||||||
(
|
(
|
||||||
first
|
first
|
||||||
.comptime_eval(scope, &VoidHandler)
|
.comptime_eval(scope, &VoidHandler)
|
||||||
.map_or_else(|| "@a".into(), |val| val.to_macro_string()),
|
.map(|val| val.to_macro_string())
|
||||||
|
.map_err(TranspileError::NotComptime)?,
|
||||||
second,
|
second,
|
||||||
)
|
)
|
||||||
},
|
} else {
|
||||||
);
|
("@a".into(), first)
|
||||||
|
};
|
||||||
|
|
||||||
let mut contains_macro = matches!(target, MacroString::MacroString(_));
|
let mut contains_macro = matches!(target, MacroString::MacroString(_));
|
||||||
|
|
||||||
|
@ -240,7 +240,7 @@ fn print_function(
|
||||||
Primary::Identifier(ident) => {
|
Primary::Identifier(ident) => {
|
||||||
match scope.get_variable(ident.span.str()).as_deref() {
|
match scope.get_variable(ident.span.str()).as_deref() {
|
||||||
Some(VariableData::Scoreboard { objective }) => {
|
Some(VariableData::Scoreboard { objective }) => {
|
||||||
if let Some(ComptimeValue::String(index)) =
|
if let Ok(ComptimeValue::String(index)) =
|
||||||
indexed.index().comptime_eval(scope, &VoidHandler)
|
indexed.index().comptime_eval(scope, &VoidHandler)
|
||||||
{
|
{
|
||||||
let (cmd, value) = get_data_location(
|
let (cmd, value) = get_data_location(
|
||||||
|
@ -262,7 +262,7 @@ fn print_function(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(VariableData::ScoreboardArray { objective, targets }) => {
|
Some(VariableData::ScoreboardArray { objective, targets }) => {
|
||||||
if let Some(ComptimeValue::Integer(index)) =
|
if let Ok(ComptimeValue::Integer(index)) =
|
||||||
indexed.index().comptime_eval(scope, &VoidHandler)
|
indexed.index().comptime_eval(scope, &VoidHandler)
|
||||||
{
|
{
|
||||||
#[expect(clippy::option_if_let_else)]
|
#[expect(clippy::option_if_let_else)]
|
||||||
|
@ -300,7 +300,7 @@ fn print_function(
|
||||||
storage_name,
|
storage_name,
|
||||||
paths,
|
paths,
|
||||||
}) => {
|
}) => {
|
||||||
if let Some(ComptimeValue::Integer(index)) =
|
if let Ok(ComptimeValue::Integer(index)) =
|
||||||
indexed.index().comptime_eval(scope, &VoidHandler)
|
indexed.index().comptime_eval(scope, &VoidHandler)
|
||||||
{
|
{
|
||||||
#[expect(clippy::option_if_let_else)]
|
#[expect(clippy::option_if_let_else)]
|
||||||
|
|
|
@ -12,8 +12,8 @@ mod enabled {
|
||||||
syntax::syntax_tree::expression::LuaCode,
|
syntax::syntax_tree::expression::LuaCode,
|
||||||
transpile::{
|
transpile::{
|
||||||
error::{
|
error::{
|
||||||
InvalidArgument, LuaRuntimeError, MismatchedTypes, TranspileError, TranspileResult,
|
InvalidArgument, LuaRuntimeError, MismatchedTypes, NotComptime, TranspileError,
|
||||||
UnknownIdentifier,
|
TranspileResult, UnknownIdentifier,
|
||||||
},
|
},
|
||||||
expression::{ComptimeValue, ExpectedType},
|
expression::{ComptimeValue, ExpectedType},
|
||||||
Scope, VariableData,
|
Scope, VariableData,
|
||||||
|
@ -86,11 +86,15 @@ mod enabled {
|
||||||
&self,
|
&self,
|
||||||
scope: &Arc<Scope>,
|
scope: &Arc<Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<Option<ComptimeValue>> {
|
) -> TranspileResult<Result<ComptimeValue, NotComptime>> {
|
||||||
// required to keep the lua instance alive
|
// required to keep the lua instance alive
|
||||||
let (lua_result, _lua) = self.eval(scope, handler)?;
|
let (lua_result, _lua) = self.eval(scope, handler)?;
|
||||||
|
|
||||||
self.handle_lua_result(lua_result, handler)
|
self.handle_lua_result(lua_result, handler).map(|res| {
|
||||||
|
res.ok_or_else(|| NotComptime {
|
||||||
|
expression: self.span(),
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_globals(&self, lua: &Lua, scope: &Arc<Scope>) -> TranspileResult<()> {
|
fn add_globals(&self, lua: &Lua, scope: &Arc<Scope>) -> TranspileResult<()> {
|
||||||
|
@ -262,6 +266,22 @@ mod enabled {
|
||||||
.map_err(|err| LuaRuntimeError::from_lua_err(&err, self.span()))?;
|
.map_err(|err| LuaRuntimeError::from_lua_err(&err, self.span()))?;
|
||||||
Value::Table(table)
|
Value::Table(table)
|
||||||
}
|
}
|
||||||
|
Some(VariableData::ComptimeValue { value }) => {
|
||||||
|
let value = value.read().unwrap();
|
||||||
|
match &*value {
|
||||||
|
Some(ComptimeValue::Boolean(b)) => Value::Boolean(*b),
|
||||||
|
Some(ComptimeValue::Integer(i)) => Value::Integer(*i),
|
||||||
|
Some(ComptimeValue::String(s)) => Value::String(
|
||||||
|
lua.create_string(s)
|
||||||
|
.map_err(|err| LuaRuntimeError::from_lua_err(&err, self.span()))?,
|
||||||
|
),
|
||||||
|
Some(ComptimeValue::MacroString(s)) => Value::String(
|
||||||
|
lua.create_string(s.to_string())
|
||||||
|
.map_err(|err| LuaRuntimeError::from_lua_err(&err, self.span()))?,
|
||||||
|
),
|
||||||
|
None => Value::Nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
Some(VariableData::Function { .. } | VariableData::InternalFunction { .. }) => {
|
Some(VariableData::Function { .. } | VariableData::InternalFunction { .. }) => {
|
||||||
// TODO: add support for functions
|
// TODO: add support for functions
|
||||||
return Err(TranspileError::InvalidArgument(InvalidArgument {
|
return Err(TranspileError::InvalidArgument(InvalidArgument {
|
||||||
|
|
|
@ -25,7 +25,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
error::{MismatchedTypes, TranspileError, TranspileResult},
|
error::{MismatchedTypes, MissingValue, TranspileError, TranspileResult, UnknownIdentifier},
|
||||||
expression::{ComptimeValue, ExpectedType, ExtendedCondition},
|
expression::{ComptimeValue, ExpectedType, ExtendedCondition},
|
||||||
variables::{Scope, TranspileAssignmentTarget, VariableData},
|
variables::{Scope, TranspileAssignmentTarget, VariableData},
|
||||||
FunctionData, TranspileAnnotationValue, TranspiledFunctionArguments,
|
FunctionData, TranspileAnnotationValue, TranspiledFunctionArguments,
|
||||||
|
@ -342,11 +342,46 @@ impl Transpiler {
|
||||||
Expression::Primary(Primary::FunctionCall(func)) => {
|
Expression::Primary(Primary::FunctionCall(func)) => {
|
||||||
self.transpile_function_call(func, scope, handler)
|
self.transpile_function_call(func, scope, handler)
|
||||||
}
|
}
|
||||||
|
Expression::Primary(Primary::Identifier(ident)) => {
|
||||||
|
match scope.get_variable(ident.span.str()).as_deref() {
|
||||||
|
Some(VariableData::ComptimeValue { value }) => {
|
||||||
|
value.read().unwrap().as_ref().map_or_else(
|
||||||
|
|| {
|
||||||
|
let error = TranspileError::MissingValue(MissingValue {
|
||||||
|
expression: ident.span.clone(),
|
||||||
|
});
|
||||||
|
handler.receive(error.clone());
|
||||||
|
Err(error)
|
||||||
|
},
|
||||||
|
|val| {
|
||||||
|
let cmd = val.to_string_no_macro().map_or_else(
|
||||||
|
|| Command::UsesMacro(val.to_macro_string().into()),
|
||||||
|
Command::Raw,
|
||||||
|
);
|
||||||
|
Ok(vec![cmd])
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Some(_) => {
|
||||||
|
let error = TranspileError::UnexpectedExpression(UnexpectedExpression(
|
||||||
|
expression.clone(),
|
||||||
|
));
|
||||||
|
handler.receive(error.clone());
|
||||||
|
Err(error)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let error = TranspileError::UnknownIdentifier(UnknownIdentifier {
|
||||||
|
identifier: ident.span.clone(),
|
||||||
|
});
|
||||||
|
handler.receive(error.clone());
|
||||||
|
Err(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
expression @ Expression::Primary(
|
expression @ Expression::Primary(
|
||||||
Primary::Integer(_)
|
Primary::Integer(_)
|
||||||
| Primary::Boolean(_)
|
| Primary::Boolean(_)
|
||||||
| Primary::Prefix(_)
|
| Primary::Prefix(_)
|
||||||
| Primary::Identifier(_)
|
|
||||||
| Primary::Indexed(_),
|
| Primary::Indexed(_),
|
||||||
) => {
|
) => {
|
||||||
let error =
|
let error =
|
||||||
|
@ -361,9 +396,9 @@ impl Transpiler {
|
||||||
Ok(vec![Command::UsesMacro(string.into())])
|
Ok(vec![Command::UsesMacro(string.into())])
|
||||||
}
|
}
|
||||||
Expression::Primary(Primary::Lua(code)) => match code.eval_comptime(scope, handler)? {
|
Expression::Primary(Primary::Lua(code)) => match code.eval_comptime(scope, handler)? {
|
||||||
Some(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
||||||
Some(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
|
Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
|
||||||
Some(ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)) => {
|
Ok(ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)) => {
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||||
expected_type: ExpectedType::String,
|
expected_type: ExpectedType::String,
|
||||||
expression: code.span(),
|
expression: code.span(),
|
||||||
|
@ -371,7 +406,13 @@ impl Transpiler {
|
||||||
handler.receive(err.clone());
|
handler.receive(err.clone());
|
||||||
Err(err)
|
Err(err)
|
||||||
}
|
}
|
||||||
None => Ok(Vec::new()),
|
Err(_) => {
|
||||||
|
let err = TranspileError::MissingValue(MissingValue {
|
||||||
|
expression: code.span(),
|
||||||
|
});
|
||||||
|
handler.receive(err.clone());
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Expression::Primary(Primary::Parenthesized(parenthesized)) => self
|
Expression::Primary(Primary::Parenthesized(parenthesized)) => self
|
||||||
|
@ -382,8 +423,8 @@ impl Transpiler {
|
||||||
handler,
|
handler,
|
||||||
),
|
),
|
||||||
Expression::Binary(bin) => match bin.comptime_eval(scope, handler) {
|
Expression::Binary(bin) => match bin.comptime_eval(scope, handler) {
|
||||||
Some(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
||||||
Some(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
|
Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
|
||||||
_ => {
|
_ => {
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||||
expression: bin.span(),
|
expression: bin.span(),
|
||||||
|
@ -621,7 +662,7 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(ComptimeValue::Boolean(value)) = cond_expression.comptime_eval(scope, handler) {
|
if let Ok(ComptimeValue::Boolean(value)) = cond_expression.comptime_eval(scope, handler) {
|
||||||
if value {
|
if value {
|
||||||
Ok(Some((Vec::new(), then)))
|
Ok(Some((Vec::new(), then)))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -30,7 +30,7 @@ use crate::{
|
||||||
use super::{
|
use super::{
|
||||||
error::{
|
error::{
|
||||||
AssignmentError, IllegalAnnotationContent, IllegalIndexing, IllegalIndexingReason,
|
AssignmentError, IllegalAnnotationContent, IllegalIndexing, IllegalIndexingReason,
|
||||||
MismatchedTypes,
|
MismatchedTypes, NotComptime,
|
||||||
},
|
},
|
||||||
expression::{ComptimeValue, DataLocation, ExpectedType, StorageType},
|
expression::{ComptimeValue, DataLocation, ExpectedType, StorageType},
|
||||||
FunctionData, TranspileAnnotationValue, TranspileError, TranspileResult,
|
FunctionData, TranspileAnnotationValue, TranspileError, TranspileResult,
|
||||||
|
@ -100,6 +100,11 @@ pub enum VariableData {
|
||||||
/// The implementation
|
/// The implementation
|
||||||
implementation: InternalFunction,
|
implementation: InternalFunction,
|
||||||
},
|
},
|
||||||
|
/// Compiler internal variable.
|
||||||
|
ComptimeValue {
|
||||||
|
/// The value.
|
||||||
|
value: Arc<RwLock<Option<ComptimeValue>>>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, EnumAsInner)]
|
#[derive(Debug, Clone, Copy, EnumAsInner)]
|
||||||
|
@ -282,6 +287,31 @@ impl Transpiler {
|
||||||
scope,
|
scope,
|
||||||
handler,
|
handler,
|
||||||
),
|
),
|
||||||
|
VariableDeclaration::ComptimeValue(declaration) => {
|
||||||
|
let value = if let Some(assignment) = declaration.assignment() {
|
||||||
|
Some(
|
||||||
|
assignment
|
||||||
|
.expression()
|
||||||
|
.comptime_eval(scope, handler)
|
||||||
|
.map_err(|err| {
|
||||||
|
let err = TranspileError::NotComptime(err);
|
||||||
|
handler.receive(err.clone());
|
||||||
|
err
|
||||||
|
})?,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.set_variable(
|
||||||
|
declaration.identifier().span.str(),
|
||||||
|
VariableData::ComptimeValue {
|
||||||
|
value: Arc::new(RwLock::new(value)),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Vec::new())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,7 +496,7 @@ impl Transpiler {
|
||||||
let (identifier, indexing_value) = match destination {
|
let (identifier, indexing_value) = match destination {
|
||||||
TranspileAssignmentTarget::Identifier(ident) => (ident, None),
|
TranspileAssignmentTarget::Identifier(ident) => (ident, None),
|
||||||
TranspileAssignmentTarget::Indexed(ident, expression) => {
|
TranspileAssignmentTarget::Indexed(ident, expression) => {
|
||||||
(ident, expression.comptime_eval(scope, handler))
|
(ident, expression.comptime_eval(scope, handler).ok())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Some(target) = scope.get_variable(identifier.span.str()) {
|
if let Some(target) = scope.get_variable(identifier.span.str()) {
|
||||||
|
@ -662,6 +692,16 @@ impl Transpiler {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
VariableData::ComptimeValue { value } => {
|
||||||
|
let comptime_value =
|
||||||
|
expression.comptime_eval(scope, handler).map_err(|err| {
|
||||||
|
let err = TranspileError::NotComptime(err);
|
||||||
|
handler.receive(err.clone());
|
||||||
|
err
|
||||||
|
})?;
|
||||||
|
*value.write().unwrap() = Some(comptime_value);
|
||||||
|
return Ok(Vec::new());
|
||||||
|
}
|
||||||
VariableData::Function { .. }
|
VariableData::Function { .. }
|
||||||
| VariableData::MacroParameter { .. }
|
| VariableData::MacroParameter { .. }
|
||||||
| VariableData::InternalFunction { .. } => {
|
| VariableData::InternalFunction { .. } => {
|
||||||
|
@ -723,6 +763,7 @@ impl Transpiler {
|
||||||
TranspileAnnotationValue::Expression(expr) => {
|
TranspileAnnotationValue::Expression(expr) => {
|
||||||
if let Some(name_eval) = expr
|
if let Some(name_eval) = expr
|
||||||
.comptime_eval(scope, handler)
|
.comptime_eval(scope, handler)
|
||||||
|
.ok()
|
||||||
.and_then(|val| val.to_string_no_macro())
|
.and_then(|val| val.to_string_no_macro())
|
||||||
{
|
{
|
||||||
// TODO: change invalid criteria if boolean
|
// TODO: change invalid criteria if boolean
|
||||||
|
@ -821,9 +862,11 @@ impl Transpiler {
|
||||||
if let (Some(name_eval), Some(target_eval)) = (
|
if let (Some(name_eval), Some(target_eval)) = (
|
||||||
objective
|
objective
|
||||||
.comptime_eval(scope, handler)
|
.comptime_eval(scope, handler)
|
||||||
|
.ok()
|
||||||
.and_then(|val| val.to_string_no_macro()),
|
.and_then(|val| val.to_string_no_macro()),
|
||||||
target
|
target
|
||||||
.comptime_eval(scope, handler)
|
.comptime_eval(scope, handler)
|
||||||
|
.ok()
|
||||||
.and_then(|val| val.to_string_no_macro()),
|
.and_then(|val| val.to_string_no_macro()),
|
||||||
) {
|
) {
|
||||||
// TODO: change invalid criteria if boolean
|
// TODO: change invalid criteria if boolean
|
||||||
|
|
Loading…
Reference in New Issue