1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
use crate::ast::*;
use crate::transpiler::Transpiler;
impl Transpiler {
pub(in crate::transpiler) fn process_const_assignment(
&mut self,
const_assign: &ConstAssignment,
) -> Result<(), String> {
// Infer the type of the constant value
let value_type = self.infer_type(&const_assign.value);
// Check if this assignment is type-safe (in case const is redeclared)
self.check_type_assignment(&const_assign.target, &value_type)?;
// Record the variable's type
self.variable_types
.insert(const_assign.target.clone(), value_type);
// Evaluate the expression at compile time
let value = self.evaluate_const_expr(&const_assign.value)?;
// Store the constant value
self.compile_time_constants
.insert(const_assign.target.clone(), value);
// Also store as a regular variable so it can be used in expressions
self.variables
.insert(const_assign.target.clone(), Expression::Number(value));
// Constants are compile-time only, no runtime initialization needed
Ok(())
}
/// Evaluate an expression at compile time (must be constant)
fn evaluate_const_expr(&self, expr: &Expression) -> Result<f64, String> {
match expr {
Expression::Number(n) => Ok(*n),
Expression::Boolean(b) => Ok(if *b { 1.0 } else { 0.0 }),
Expression::Identifier(name) => {
// Look up in compile-time constants
self.compile_time_constants
.get(name)
.copied()
.ok_or_else(|| {
format!("Const expression references non-const variable: {}", name)
})
}
Expression::Binary(left, op, right) => {
let left_val = self.evaluate_const_expr(left)?;
let right_val = self.evaluate_const_expr(right)?;
match op {
BinaryOp::Add => Ok(left_val + right_val),
BinaryOp::Sub => Ok(left_val - right_val),
BinaryOp::Mul => Ok(left_val * right_val),
BinaryOp::Div => {
if right_val == 0.0 {
Err("Division by zero in const expression".to_string())
} else {
Ok(left_val / right_val)
}
}
BinaryOp::Mod => {
if right_val == 0.0 {
Err("Modulo by zero in const expression".to_string())
} else {
// Use integer modulo for consistency with runtime behavior
Ok(((left_val as i32) % (right_val as i32)) as f64)
}
}
BinaryOp::Pow => {
let base = left_val as i32;
let exp = right_val as i32;
if exp < 0 {
return Err("Power exponent must be non-negative in const expression"
.to_string());
}
match base.checked_pow(exp as u32) {
Some(result) => Ok(result as f64),
None => {
eprintln!(
"⚠️ Warning: Const power operation {}^{} overflows i32, clamping to i32::MAX",
base, exp
);
Ok(i32::MAX as f64)
}
}
}
_ => Err(format!(
"Operator {:?} not supported in const expressions",
op
)),
}
}
Expression::Unary(op, operand) => {
let val = self.evaluate_const_expr(operand)?;
match op {
UnaryOp::Neg => Ok(-val),
UnaryOp::Pos => Ok(val),
UnaryOp::Not => Ok(if val == 0.0 { 1.0 } else { 0.0 }),
}
}
Expression::String(_) | Expression::Array(_) | Expression::Map(_) => {
Err("Only numeric constants are supported at this time.".to_string())
}
_ => Err("Expression type cannot be evaluated at compile time".to_string()),
}
}
}