implement first version of compiler variables

This commit is contained in:
Moritz Hölting 2025-04-01 01:00:36 +02:00
parent ab76b1d43e
commit 469b8d3875
11 changed files with 415 additions and 99 deletions

View File

@ -52,6 +52,7 @@ pub enum KeywordKind {
Int,
Bool,
Macro,
Val,
}
impl Display for KeywordKind {
@ -117,6 +118,7 @@ impl KeywordKind {
Self::Int => "int",
Self::Bool => "bool",
Self::Macro => "macro",
Self::Val => "val",
}
}

View File

@ -396,6 +396,10 @@ impl Assignment {
let expected = match variable_type {
VariableType::BooleanStorage | VariableType::Tag => ValueType::Boolean,
VariableType::ScoreboardValue => ValueType::Integer,
VariableType::ComptimeValue => {
// TODO: check if the expression is a constant expression
return Ok(());
}
_ => {
let err = error::Error::AssignmentError(AssignmentError {
identifier: self.destination().span(),
@ -445,6 +449,12 @@ impl VariableDeclaration {
KeywordKind::Int => (VariableType::ScoreboardValue, ExpectedType::Integer),
_ => 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);
let assignment = match self {
@ -459,7 +469,12 @@ impl VariableDeclaration {
}
}
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 {
let expected = match var {
VariableType::BooleanStorage | VariableType::Tag => ValueType::Boolean,
@ -689,8 +704,35 @@ impl Binary {
handler: &impl Handler<base::Error>,
) -> Result<(), error::Error> {
match self.operator() {
BinaryOperator::Add(_)
| BinaryOperator::Subtract(_)
BinaryOperator::Add(_) => {
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::Divide(_)
| BinaryOperator::Modulo(_) => {
@ -779,8 +821,8 @@ impl Binary {
}
impl LuaCode {
#[expect(clippy::unused_self, clippy::unnecessary_wraps)]
#[cfg_attr(feature = "lua", expect(unused_variables))]
#[expect(clippy::unused_self)]
#[cfg_attr(feature = "lua", expect(unused_variables, clippy::unnecessary_wraps))]
fn analyze_semantics(
&self,
_scope: &SemanticScope,

View File

@ -21,6 +21,8 @@ pub enum VariableType {
BooleanStorageArray,
/// Compiler internal function.
InternalFunction,
/// Compiler internal value.
ComptimeValue,
}
/// A scope that stores variables.

View File

@ -325,6 +325,7 @@ pub enum VariableDeclaration {
Array(ArrayVariableDeclaration),
Score(ScoreVariableDeclaration),
Tag(TagVariableDeclaration),
ComptimeValue(ComptimeValueDeclaration),
}
impl SourceElement for VariableDeclaration {
@ -334,6 +335,7 @@ impl SourceElement for VariableDeclaration {
Self::Array(declaration) => declaration.span(),
Self::Score(declaration) => declaration.span(),
Self::Tag(declaration) => declaration.span(),
Self::ComptimeValue(declaration) => declaration.span(),
}
}
}
@ -347,6 +349,7 @@ impl VariableDeclaration {
Self::Array(declaration) => &declaration.identifier,
Self::Score(declaration) => &declaration.identifier,
Self::Tag(declaration) => &declaration.identifier,
Self::ComptimeValue(declaration) => &declaration.identifier,
}
}
@ -358,6 +361,7 @@ impl VariableDeclaration {
Self::Array(declaration) => &declaration.variable_type,
Self::Score(declaration) => &declaration.int_keyword,
Self::Tag(declaration) => &declaration.bool_keyword,
Self::ComptimeValue(declaration) => &declaration.val_keyword,
}
}
@ -383,6 +387,14 @@ impl VariableDeclaration {
declaration.annotations.push_front(annotation);
Ok(Self::Tag(declaration))
}
Self::ComptimeValue(_) => {
let err = Error::InvalidAnnotation(InvalidAnnotation {
annotation: annotation.assignment.identifier.span,
target: "comptime values".to_string(),
});
Err(err)
}
}
}
}
@ -681,6 +693,53 @@ impl TagVariableDeclaration {
}
}
/// Represents a compile time value declaration in the syntax tree.
///
/// Syntax Synopsis:
///
/// ```ebnf
/// ComptimeValueDeclaration:
/// 'val' identifier VariableDeclarationAssignment?
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
pub struct ComptimeValueDeclaration {
/// The type of the variable.
#[get = "pub"]
val_keyword: Keyword,
/// The identifier of the variable.
#[get = "pub"]
identifier: Identifier,
/// The optional assignment of the variable.
#[get = "pub"]
assignment: Option<VariableDeclarationAssignment>,
/// The annotations of the variable declaration.
#[get = "pub"]
annotations: VecDeque<Annotation>,
}
impl SourceElement for ComptimeValueDeclaration {
fn span(&self) -> Span {
self.val_keyword
.span()
.join(
&self
.assignment
.as_ref()
.map_or_else(|| self.identifier.span(), SourceElement::span),
)
.expect("The span of the single variable declaration is invalid.")
}
}
impl ComptimeValueDeclaration {
/// Dissolves the [`ComptimeValueDeclaration`] into its components.
#[must_use]
pub fn dissolve(self) -> (Keyword, Identifier, Option<VariableDeclarationAssignment>) {
(self.val_keyword, self.identifier, self.assignment)
}
}
/// Represents an assignment in the syntax tree.
///
/// Syntax Synopsis:
@ -883,7 +942,10 @@ impl Parser<'_> {
) -> ParseResult<Semicolon> {
let statement = match self.stop_at_significant() {
Reading::Atomic(Token::Keyword(keyword))
if matches!(keyword.keyword, KeywordKind::Int | KeywordKind::Bool) =>
if matches!(
keyword.keyword,
KeywordKind::Int | KeywordKind::Bool | KeywordKind::Val
) =>
{
self.parse_variable_declaration(handler)
.map(SemicolonStatement::VariableDeclaration)
@ -895,18 +957,20 @@ impl Parser<'_> {
let destination = {
let identifier = p.parse_identifier(&VoidHandler)?;
if let Ok(tree) = p.step_into(
Delimiter::Bracket,
|pp| pp.parse_expression(&VoidHandler),
&VoidHandler,
) {
let open = tree.open;
let close = tree.close;
let expression = tree.tree?;
match p.stop_at_significant() {
Reading::IntoDelimited(punc) if punc.punctuation == '[' => {
let tree = p.step_into(
Delimiter::Bracket,
|pp| pp.parse_expression(&VoidHandler),
&VoidHandler,
)?;
let open = tree.open;
let close = tree.close;
let expression = tree.tree?;
AssignmentDestination::Indexed(identifier, open, expression, close)
} else {
AssignmentDestination::Identifier(identifier)
AssignmentDestination::Indexed(identifier, open, expression, close)
}
_ => AssignmentDestination::Identifier(identifier),
}
};
let equals = p.parse_punctuation('=', true, &VoidHandler)?;
@ -948,7 +1012,10 @@ impl Parser<'_> {
let variable_type = match self.stop_at_significant() {
Reading::Atomic(Token::Keyword(keyword))
if matches!(keyword.keyword, KeywordKind::Int | KeywordKind::Bool) =>
if matches!(
keyword.keyword,
KeywordKind::Int | KeywordKind::Bool | KeywordKind::Val
) =>
{
self.forward();
keyword
@ -983,7 +1050,10 @@ impl Parser<'_> {
let identifier = self.parse_identifier(handler)?;
match self.stop_at_significant() {
Reading::IntoDelimited(punc) if punc.punctuation == '[' => {
Reading::IntoDelimited(punc)
if punc.punctuation == '['
&& matches!(variable_type.keyword, KeywordKind::Int | KeywordKind::Bool) =>
{
let tree = self.step_into(
Delimiter::Bracket,
|p| {
@ -1071,12 +1141,23 @@ impl Parser<'_> {
let expression = self.parse_expression(handler)?;
let assignment = VariableDeclarationAssignment { equals, expression };
Ok(VariableDeclaration::Single(SingleVariableDeclaration {
variable_type,
identifier,
assignment: Some(assignment),
annotations: VecDeque::new(),
}))
if variable_type.keyword == KeywordKind::Val {
Ok(VariableDeclaration::ComptimeValue(
ComptimeValueDeclaration {
val_keyword: variable_type,
identifier,
assignment: Some(assignment),
annotations: VecDeque::new(),
},
))
} else {
Ok(VariableDeclaration::Single(SingleVariableDeclaration {
variable_type,
identifier,
assignment: Some(assignment),
annotations: VecDeque::new(),
}))
}
}
// SingleVariableDeclaration without Assignment
_ => Ok(VariableDeclaration::Single(SingleVariableDeclaration {

View File

@ -46,6 +46,8 @@ pub enum TranspileError {
IllegalIndexing(#[from] IllegalIndexing),
#[error(transparent)]
InvalidArgument(#[from] InvalidArgument),
#[error(transparent)]
NotComptime(#[from] NotComptime),
}
/// The result of a transpilation operation.
@ -447,3 +449,32 @@ impl Display 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 {}

View File

@ -12,7 +12,9 @@ use shulkerbox::prelude::{Command, Condition, Execute};
#[cfg(feature = "shulkerbox")]
use super::{
error::{IllegalIndexing, IllegalIndexingReason, MismatchedTypes, UnknownIdentifier},
error::{
IllegalIndexing, IllegalIndexingReason, MismatchedTypes, NotComptime, UnknownIdentifier,
},
Scope, TranspileResult, Transpiler, VariableData,
};
#[cfg(feature = "shulkerbox")]
@ -238,12 +240,14 @@ impl Expression {
}
/// Evaluate at compile-time.
#[must_use]
///
/// # Errors
/// - If the expression is not compile-time evaluatable.
pub fn comptime_eval(
&self,
scope: &Arc<Scope>,
handler: &impl Handler<base::Error>,
) -> Option<ComptimeValue> {
) -> Result<ComptimeValue, NotComptime> {
match self {
Self::Primary(primary) => primary.comptime_eval(scope, handler),
Self::Binary(binary) => binary.comptime_eval(scope, handler),
@ -333,19 +337,40 @@ impl Primary {
}
/// Evaluate at compile-time.
#[must_use]
///
/// # Errors
/// - If the expression is not compile-time evaluatable.
pub fn comptime_eval(
&self,
scope: &Arc<Scope>,
handler: &impl Handler<base::Error>,
) -> Option<ComptimeValue> {
) -> Result<ComptimeValue, NotComptime> {
match self {
Self::Boolean(boolean) => Some(ComptimeValue::Boolean(boolean.value())),
Self::Integer(int) => Some(ComptimeValue::Integer(int.as_i64())),
Self::StringLiteral(string_literal) => Some(ComptimeValue::String(
Self::Boolean(boolean) => Ok(ComptimeValue::Boolean(boolean.value())),
Self::Integer(int) => Ok(ComptimeValue::Integer(int.as_i64())),
Self::StringLiteral(string_literal) => Ok(ComptimeValue::String(
string_literal.str_content().to_string(),
)),
Self::Identifier(_) | Self::FunctionCall(_) | Self::Indexed(_) => None,
Self::Identifier(ident) => scope.get_variable(ident.span.str()).map_or_else(
|| {
Err(NotComptime {
expression: self.span(),
})
},
|var| match var.as_ref() {
VariableData::ComptimeValue { value } => {
value.read().unwrap().clone().ok_or_else(|| NotComptime {
expression: ident.span.clone(),
})
}
_ => Err(NotComptime {
expression: self.span(),
}),
},
),
Self::FunctionCall(_) | Self::Indexed(_) => Err(NotComptime {
expression: self.span(),
}),
Self::Parenthesized(parenthesized) => {
parenthesized.expression().comptime_eval(scope, handler)
}
@ -355,12 +380,14 @@ impl Primary {
.comptime_eval(scope, handler)
.and_then(|val| match (prefix.operator(), val) {
(PrefixOperator::LogicalNot(_), ComptimeValue::Boolean(boolean)) => {
Some(ComptimeValue::Boolean(!boolean))
Ok(ComptimeValue::Boolean(!boolean))
}
(PrefixOperator::Negate(_), ComptimeValue::Integer(int)) => {
Some(ComptimeValue::Integer(-int))
Ok(ComptimeValue::Integer(-int))
}
_ => None,
_ => Err(NotComptime {
expression: prefix.span(),
}),
})
}
Self::Lua(lua) => lua
@ -368,19 +395,21 @@ impl Primary {
.inspect_err(|err| {
handler.receive(err.clone());
})
.ok()
.flatten(),
.map_err(|_| NotComptime {
expression: lua.span(),
})
.and_then(|val| val),
Self::MacroStringLiteral(macro_string_literal) => {
if macro_string_literal
.parts()
.iter()
.any(|part| matches!(part, MacroStringLiteralPart::MacroUsage { .. }))
{
Some(ComptimeValue::MacroString(
Ok(ComptimeValue::MacroString(
macro_string_literal.clone().into(),
))
} else {
Some(ComptimeValue::String(macro_string_literal.str_content()))
Ok(ComptimeValue::String(macro_string_literal.str_content()))
}
}
}
@ -430,12 +459,14 @@ impl Binary {
}
/// Evaluate at compile-time.
#[must_use]
///
/// # Errors
/// - If the expression is not compile-time evaluatable.
pub fn comptime_eval(
&self,
scope: &Arc<Scope>,
handler: &impl Handler<base::Error>,
) -> Option<ComptimeValue> {
) -> Result<ComptimeValue, NotComptime> {
let left = self.left_operand().comptime_eval(scope, handler)?;
let right = self.right_operand().comptime_eval(scope, handler)?;
@ -449,7 +480,7 @@ impl Binary {
.right_operand()
.can_yield_type(ValueType::Boolean, scope) =>
{
Some(ComptimeValue::Boolean(true))
Ok(ComptimeValue::Boolean(true))
}
(ComptimeValue::Boolean(false), _) | (_, ComptimeValue::Boolean(false))
if matches!(self.operator(), BinaryOperator::LogicalAnd(..))
@ -460,15 +491,17 @@ impl Binary {
.right_operand()
.can_yield_type(ValueType::Boolean, scope) =>
{
Some(ComptimeValue::Boolean(false))
Ok(ComptimeValue::Boolean(false))
}
(ComptimeValue::Boolean(left), ComptimeValue::Boolean(right)) => {
match self.operator() {
BinaryOperator::Equal(..) => Some(ComptimeValue::Boolean(left == right)),
BinaryOperator::NotEqual(..) => Some(ComptimeValue::Boolean(left != right)),
BinaryOperator::LogicalAnd(..) => Some(ComptimeValue::Boolean(left && right)),
BinaryOperator::LogicalOr(..) => Some(ComptimeValue::Boolean(left || right)),
_ => None,
BinaryOperator::Equal(..) => Ok(ComptimeValue::Boolean(left == right)),
BinaryOperator::NotEqual(..) => Ok(ComptimeValue::Boolean(left != right)),
BinaryOperator::LogicalAnd(..) => Ok(ComptimeValue::Boolean(left && right)),
BinaryOperator::LogicalOr(..) => Ok(ComptimeValue::Boolean(left || right)),
_ => Err(NotComptime {
expression: self.span(),
}),
}
}
(ComptimeValue::Integer(left), ComptimeValue::Integer(right)) => {
@ -498,14 +531,37 @@ impl Binary {
}
_ => None,
}
.ok_or_else(|| NotComptime {
expression: self.span(),
})
}
(ComptimeValue::String(left), ComptimeValue::String(right)) => match self.operator() {
BinaryOperator::Add(..) => Some(ComptimeValue::String(left + &right)),
BinaryOperator::Equal(..) => Some(ComptimeValue::Boolean(left == right)),
BinaryOperator::NotEqual(..) => Some(ComptimeValue::Boolean(left != right)),
_ => None,
BinaryOperator::Add(..) => Ok(ComptimeValue::String(left + &right)),
BinaryOperator::Equal(..) => Ok(ComptimeValue::Boolean(left == right)),
BinaryOperator::NotEqual(..) => Ok(ComptimeValue::Boolean(left != right)),
_ => Err(NotComptime {
expression: self.span(),
}),
},
_ => None,
// TODO: also allow macro strings
(
left @ ComptimeValue::String(_),
right @ (ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)),
)
| (
left @ (ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)),
right @ ComptimeValue::String(_),
) => match self.operator() {
BinaryOperator::Add(_) => Ok(ComptimeValue::String(
left.to_string_no_macro().unwrap() + &right.to_string_no_macro().unwrap(),
)),
_ => Err(NotComptime {
expression: self.span(),
}),
},
_ => Err(NotComptime {
expression: self.span(),
}),
}
}
}
@ -641,7 +697,7 @@ impl Transpiler {
Primary::Lua(lua) =>
{
#[expect(clippy::option_if_let_else)]
if let Some(value) = lua.eval_comptime(scope, handler)? {
if let Ok(value) = lua.eval_comptime(scope, handler)? {
self.store_comptime_value(&value, target, lua, handler)
} else {
let err = TranspileError::MissingValue(MissingValue {
@ -803,7 +859,7 @@ impl Transpiler {
if let Some(variable) = variable.as_deref() {
let from = match variable {
VariableData::Scoreboard { objective } => {
if let Some(ComptimeValue::String(target)) =
if let Ok(ComptimeValue::String(target)) =
indexed.index().comptime_eval(scope, handler)
{
Ok(DataLocation::ScoreboardValue {
@ -822,7 +878,7 @@ impl Transpiler {
}
}
VariableData::ScoreboardArray { objective, targets } => {
if let Some(ComptimeValue::Integer(index)) =
if let Ok(ComptimeValue::Integer(index)) =
indexed.index().comptime_eval(scope, handler)
{
if let Some(target) = usize::try_from(index)
@ -860,7 +916,7 @@ impl Transpiler {
storage_name,
paths,
} => {
if let Some(ComptimeValue::Integer(index)) =
if let Ok(ComptimeValue::Integer(index)) =
indexed.index().comptime_eval(scope, handler)
{
if let Some(path) = usize::try_from(index)
@ -924,7 +980,7 @@ impl Transpiler {
scope: &Arc<super::Scope>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<Vec<Command>> {
if let Some(value) = binary.comptime_eval(scope, handler) {
if let Ok(value) = binary.comptime_eval(scope, handler) {
self.store_comptime_value(&value, target, binary, handler)
} else {
match binary.operator() {
@ -1087,7 +1143,7 @@ impl Transpiler {
storage_name,
paths,
} => {
if let Some(ComptimeValue::Integer(index)) =
if let Ok(ComptimeValue::Integer(index)) =
indexed.index().comptime_eval(scope, handler)
{
if let Some(path) = usize::try_from(index)
@ -1168,15 +1224,15 @@ impl Transpiler {
}
},
Primary::Lua(lua) => match lua.eval_comptime(scope, handler)? {
Some(ComptimeValue::String(value)) => Ok((
Ok(ComptimeValue::String(value)) => Ok((
Vec::new(),
ExtendedCondition::Runtime(Condition::Atom(value.into())),
)),
Some(ComptimeValue::MacroString(value)) => Ok((
Ok(ComptimeValue::MacroString(value)) => Ok((
Vec::new(),
ExtendedCondition::Runtime(Condition::Atom(value.into())),
)),
Some(ComptimeValue::Boolean(boolean)) => {
Ok(ComptimeValue::Boolean(boolean)) => {
Ok((Vec::new(), ExtendedCondition::Comptime(boolean)))
}
_ => {

View File

@ -102,6 +102,7 @@ impl Transpiler {
TranspileAnnotationValue::None => Ok(identifier_span.str().to_string()),
TranspileAnnotationValue::Expression(expr) => expr
.comptime_eval(scope, handler)
.ok()
.and_then(|val| val.to_string_no_macro())
.ok_or_else(|| {
let err = TranspileError::IllegalAnnotationContent(
@ -293,13 +294,10 @@ impl Transpiler {
let value = match expression {
Expression::Primary(Primary::Lua(lua)) => {
lua.eval_comptime(scope, handler).and_then(|val| match val {
Some(ComptimeValue::MacroString(s)) => Ok(Parameter::Static(s)),
Some(val) => Ok(Parameter::Static(val.to_macro_string())),
None => {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
expression: expression.span(),
expected_type: ExpectedType::String,
});
Ok(ComptimeValue::MacroString(s)) => Ok(Parameter::Static(s)),
Ok(val) => Ok(Parameter::Static(val.to_macro_string())),
Err(err) => {
let err = TranspileError::NotComptime(err);
handler.receive(err.clone());
Err(err)
}

View File

@ -183,17 +183,17 @@ fn print_function(
let args = get_args_assert_in_range(call, 1..=2)?;
let first = args.first().expect("checked range");
let (target, message_expression) = args.get(1).map_or_else(
|| ("@a".into(), first),
|second| {
(
first
.comptime_eval(scope, &VoidHandler)
.map_or_else(|| "@a".into(), |val| val.to_macro_string()),
second,
)
},
);
let (target, message_expression) = if let Some(second) = args.get(1) {
(
first
.comptime_eval(scope, &VoidHandler)
.map(|val| val.to_macro_string())
.map_err(TranspileError::NotComptime)?,
second,
)
} else {
("@a".into(), first)
};
let mut contains_macro = matches!(target, MacroString::MacroString(_));
@ -240,7 +240,7 @@ fn print_function(
Primary::Identifier(ident) => {
match scope.get_variable(ident.span.str()).as_deref() {
Some(VariableData::Scoreboard { objective }) => {
if let Some(ComptimeValue::String(index)) =
if let Ok(ComptimeValue::String(index)) =
indexed.index().comptime_eval(scope, &VoidHandler)
{
let (cmd, value) = get_data_location(
@ -262,7 +262,7 @@ fn print_function(
}
}
Some(VariableData::ScoreboardArray { objective, targets }) => {
if let Some(ComptimeValue::Integer(index)) =
if let Ok(ComptimeValue::Integer(index)) =
indexed.index().comptime_eval(scope, &VoidHandler)
{
#[expect(clippy::option_if_let_else)]
@ -300,7 +300,7 @@ fn print_function(
storage_name,
paths,
}) => {
if let Some(ComptimeValue::Integer(index)) =
if let Ok(ComptimeValue::Integer(index)) =
indexed.index().comptime_eval(scope, &VoidHandler)
{
#[expect(clippy::option_if_let_else)]

View File

@ -12,8 +12,8 @@ mod enabled {
syntax::syntax_tree::expression::LuaCode,
transpile::{
error::{
InvalidArgument, LuaRuntimeError, MismatchedTypes, TranspileError, TranspileResult,
UnknownIdentifier,
InvalidArgument, LuaRuntimeError, MismatchedTypes, NotComptime, TranspileError,
TranspileResult, UnknownIdentifier,
},
expression::{ComptimeValue, ExpectedType},
Scope, VariableData,
@ -86,11 +86,15 @@ mod enabled {
&self,
scope: &Arc<Scope>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<Option<ComptimeValue>> {
) -> TranspileResult<Result<ComptimeValue, NotComptime>> {
// required to keep the lua instance alive
let (lua_result, _lua) = self.eval(scope, handler)?;
self.handle_lua_result(lua_result, handler)
self.handle_lua_result(lua_result, handler).map(|res| {
res.ok_or_else(|| NotComptime {
expression: self.span(),
})
})
}
fn add_globals(&self, lua: &Lua, scope: &Arc<Scope>) -> TranspileResult<()> {
@ -262,6 +266,22 @@ mod enabled {
.map_err(|err| LuaRuntimeError::from_lua_err(&err, self.span()))?;
Value::Table(table)
}
Some(VariableData::ComptimeValue { value }) => {
let value = value.read().unwrap();
match &*value {
Some(ComptimeValue::Boolean(b)) => Value::Boolean(*b),
Some(ComptimeValue::Integer(i)) => Value::Integer(*i),
Some(ComptimeValue::String(s)) => Value::String(
lua.create_string(s)
.map_err(|err| LuaRuntimeError::from_lua_err(&err, self.span()))?,
),
Some(ComptimeValue::MacroString(s)) => Value::String(
lua.create_string(s.to_string())
.map_err(|err| LuaRuntimeError::from_lua_err(&err, self.span()))?,
),
None => Value::Nil,
}
}
Some(VariableData::Function { .. } | VariableData::InternalFunction { .. }) => {
// TODO: add support for functions
return Err(TranspileError::InvalidArgument(InvalidArgument {

View File

@ -25,7 +25,7 @@ use crate::{
};
use super::{
error::{MismatchedTypes, TranspileError, TranspileResult},
error::{MismatchedTypes, MissingValue, TranspileError, TranspileResult, UnknownIdentifier},
expression::{ComptimeValue, ExpectedType, ExtendedCondition},
variables::{Scope, TranspileAssignmentTarget, VariableData},
FunctionData, TranspileAnnotationValue, TranspiledFunctionArguments,
@ -342,11 +342,46 @@ impl Transpiler {
Expression::Primary(Primary::FunctionCall(func)) => {
self.transpile_function_call(func, scope, handler)
}
Expression::Primary(Primary::Identifier(ident)) => {
match scope.get_variable(ident.span.str()).as_deref() {
Some(VariableData::ComptimeValue { value }) => {
value.read().unwrap().as_ref().map_or_else(
|| {
let error = TranspileError::MissingValue(MissingValue {
expression: ident.span.clone(),
});
handler.receive(error.clone());
Err(error)
},
|val| {
let cmd = val.to_string_no_macro().map_or_else(
|| Command::UsesMacro(val.to_macro_string().into()),
Command::Raw,
);
Ok(vec![cmd])
},
)
}
Some(_) => {
let error = TranspileError::UnexpectedExpression(UnexpectedExpression(
expression.clone(),
));
handler.receive(error.clone());
Err(error)
}
None => {
let error = TranspileError::UnknownIdentifier(UnknownIdentifier {
identifier: ident.span.clone(),
});
handler.receive(error.clone());
Err(error)
}
}
}
expression @ Expression::Primary(
Primary::Integer(_)
| Primary::Boolean(_)
| Primary::Prefix(_)
| Primary::Identifier(_)
| Primary::Indexed(_),
) => {
let error =
@ -361,9 +396,9 @@ impl Transpiler {
Ok(vec![Command::UsesMacro(string.into())])
}
Expression::Primary(Primary::Lua(code)) => match code.eval_comptime(scope, handler)? {
Some(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
Some(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
Some(ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)) => {
Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
Ok(ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)) => {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
expected_type: ExpectedType::String,
expression: code.span(),
@ -371,7 +406,13 @@ impl Transpiler {
handler.receive(err.clone());
Err(err)
}
None => Ok(Vec::new()),
Err(_) => {
let err = TranspileError::MissingValue(MissingValue {
expression: code.span(),
});
handler.receive(err.clone());
Err(err)
}
},
Expression::Primary(Primary::Parenthesized(parenthesized)) => self
@ -382,8 +423,8 @@ impl Transpiler {
handler,
),
Expression::Binary(bin) => match bin.comptime_eval(scope, handler) {
Some(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
Some(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
_ => {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
expression: bin.span(),
@ -621,7 +662,7 @@ impl Transpiler {
}
});
if let Some(ComptimeValue::Boolean(value)) = cond_expression.comptime_eval(scope, handler) {
if let Ok(ComptimeValue::Boolean(value)) = cond_expression.comptime_eval(scope, handler) {
if value {
Ok(Some((Vec::new(), then)))
} else {

View File

@ -30,7 +30,7 @@ use crate::{
use super::{
error::{
AssignmentError, IllegalAnnotationContent, IllegalIndexing, IllegalIndexingReason,
MismatchedTypes,
MismatchedTypes, NotComptime,
},
expression::{ComptimeValue, DataLocation, ExpectedType, StorageType},
FunctionData, TranspileAnnotationValue, TranspileError, TranspileResult,
@ -100,6 +100,11 @@ pub enum VariableData {
/// The implementation
implementation: InternalFunction,
},
/// Compiler internal variable.
ComptimeValue {
/// The value.
value: Arc<RwLock<Option<ComptimeValue>>>,
},
}
#[derive(Debug, Clone, Copy, EnumAsInner)]
@ -282,6 +287,31 @@ impl Transpiler {
scope,
handler,
),
VariableDeclaration::ComptimeValue(declaration) => {
let value = if let Some(assignment) = declaration.assignment() {
Some(
assignment
.expression()
.comptime_eval(scope, handler)
.map_err(|err| {
let err = TranspileError::NotComptime(err);
handler.receive(err.clone());
err
})?,
)
} else {
None
};
scope.set_variable(
declaration.identifier().span.str(),
VariableData::ComptimeValue {
value: Arc::new(RwLock::new(value)),
},
);
Ok(Vec::new())
}
}
}
@ -466,7 +496,7 @@ impl Transpiler {
let (identifier, indexing_value) = match destination {
TranspileAssignmentTarget::Identifier(ident) => (ident, None),
TranspileAssignmentTarget::Indexed(ident, expression) => {
(ident, expression.comptime_eval(scope, handler))
(ident, expression.comptime_eval(scope, handler).ok())
}
};
if let Some(target) = scope.get_variable(identifier.span.str()) {
@ -662,6 +692,16 @@ impl Transpiler {
return Err(err);
}
},
VariableData::ComptimeValue { value } => {
let comptime_value =
expression.comptime_eval(scope, handler).map_err(|err| {
let err = TranspileError::NotComptime(err);
handler.receive(err.clone());
err
})?;
*value.write().unwrap() = Some(comptime_value);
return Ok(Vec::new());
}
VariableData::Function { .. }
| VariableData::MacroParameter { .. }
| VariableData::InternalFunction { .. } => {
@ -723,6 +763,7 @@ impl Transpiler {
TranspileAnnotationValue::Expression(expr) => {
if let Some(name_eval) = expr
.comptime_eval(scope, handler)
.ok()
.and_then(|val| val.to_string_no_macro())
{
// TODO: change invalid criteria if boolean
@ -821,9 +862,11 @@ impl Transpiler {
if let (Some(name_eval), Some(target_eval)) = (
objective
.comptime_eval(scope, handler)
.ok()
.and_then(|val| val.to_string_no_macro()),
target
.comptime_eval(scope, handler)
.ok()
.and_then(|val| val.to_string_no_macro()),
) {
// TODO: change invalid criteria if boolean