0107264026
This commit introduces purity inference to the compiler's optimizer. This allows for more aggressive constant folding and dead code elimination by tracking which functions and global variables are free of side effects. Key changes include: - Added `global_purity` field to `Optimizer` and `Environment`. - Modified `Optimizer::is_pure` to recursively determine if an AST node represents a pure computation. - Introduced `Optimizer::try_fold_pure` to replace the old `try_fold_intrinsic`, enabling folding of pure function calls with constant arguments. - Updated `Environment::register_native` and `Environment::register_constant` to optionally record purity. - Added purity flags to several built-in functions in `core.rs` and `datetime.rs`. - A new example `optimizer_purity.myc` demonstrates the new feature.
335 lines
15 KiB
Rust
335 lines
15 KiB
Rust
use std::rc::Rc;
|
|
use crate::ast::types::{Value, StaticType, Signature};
|
|
use crate::ast::environment::Environment;
|
|
|
|
|
|
pub fn register(env: &Environment) {
|
|
register_constants(env);
|
|
register_arithmetic(env);
|
|
register_comparison(env);
|
|
register_logic(env);
|
|
}
|
|
|
|
fn register_constants(env: &Environment) {
|
|
// True/False are keywords or literals in parser, but could be exposed as constants too if needed.
|
|
// In Delphi RTL: CFalse, CTrue, CNaN
|
|
|
|
// We register NaN as a value, not a function.
|
|
env.register_constant("NaN", StaticType::Float, Value::Float(f64::NAN));
|
|
env.register_constant("true", StaticType::Bool, Value::Bool(true));
|
|
env.register_constant("false", StaticType::Bool, Value::Bool(false));
|
|
}
|
|
|
|
fn register_arithmetic(env: &Environment) {
|
|
// --- Add (+) ---
|
|
let add_ty = StaticType::FunctionOverloads(vec![
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Int, StaticType::Int]), ret: StaticType::Int },
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Float, StaticType::Float]), ret: StaticType::Float },
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Text, StaticType::Text]), ret: StaticType::Text },
|
|
Signature { params: StaticType::Tuple(vec![StaticType::DateTime, StaticType::Int]), ret: StaticType::DateTime },
|
|
]);
|
|
env.register_native("+", add_ty, true, |args| {
|
|
if args.len() == 2 {
|
|
match (&args[0], &args[1]) {
|
|
(Value::Int(a), Value::Int(b)) => Value::Int(a + b),
|
|
(Value::Float(a), Value::Float(b)) => Value::Float(a + b),
|
|
(Value::Int(a), Value::Float(b)) => Value::Float(*a as f64 + b),
|
|
(Value::Float(a), Value::Int(b)) => Value::Float(a + *b as f64),
|
|
(Value::Text(a), Value::Text(b)) => {
|
|
let mut res = a.to_string();
|
|
res.push_str(b);
|
|
Value::Text(Rc::from(res))
|
|
},
|
|
(Value::DateTime(ts), Value::Int(ms)) => Value::DateTime(ts + ms),
|
|
_ => Value::Void,
|
|
}
|
|
} else {
|
|
// Variadic sum
|
|
let mut acc = 0.0;
|
|
for arg in args {
|
|
if let Value::Int(i) = arg { acc += i as f64; }
|
|
else if let Value::Float(f) = arg { acc += f; }
|
|
}
|
|
if acc.fract() == 0.0 { Value::Int(acc as i64) } else { Value::Float(acc) }
|
|
}
|
|
});
|
|
|
|
// --- Subtract (-) ---
|
|
let sub_ty = StaticType::FunctionOverloads(vec![
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Int, StaticType::Int]), ret: StaticType::Int },
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Float, StaticType::Float]), ret: StaticType::Float },
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Int]), ret: StaticType::Int }, // Negation
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Float]), ret: StaticType::Float },
|
|
Signature { params: StaticType::Tuple(vec![StaticType::DateTime, StaticType::DateTime]), ret: StaticType::Int },
|
|
Signature { params: StaticType::Tuple(vec![StaticType::DateTime, StaticType::Int]), ret: StaticType::DateTime },
|
|
]);
|
|
env.register_native("-", sub_ty, true, |args| {
|
|
if args.is_empty() { return Value::Void; }
|
|
if args.len() == 1 {
|
|
return match args[0] {
|
|
Value::Int(i) => Value::Int(-i),
|
|
Value::Float(f) => Value::Float(-f),
|
|
_ => Value::Void,
|
|
};
|
|
}
|
|
if args.len() == 2 {
|
|
return match (&args[0], &args[1]) {
|
|
(Value::Int(a), Value::Int(b)) => Value::Int(a - b),
|
|
(Value::Float(a), Value::Float(b)) => Value::Float(a - b),
|
|
(Value::Int(a), Value::Float(b)) => Value::Float(*a as f64 - b),
|
|
(Value::Float(a), Value::Int(b)) => Value::Float(a - *b as f64),
|
|
(Value::DateTime(a), Value::DateTime(b)) => Value::Int(a - b),
|
|
(Value::DateTime(a), Value::Int(b)) => Value::DateTime(a - b),
|
|
_ => Value::Void,
|
|
};
|
|
}
|
|
// Variadic sub
|
|
let mut acc = match args[0] {
|
|
Value::Int(i) => i as f64,
|
|
Value::Float(f) => f,
|
|
_ => return Value::Void,
|
|
};
|
|
for arg in &args[1..] {
|
|
if let Value::Int(i) = arg { acc -= *i as f64; }
|
|
else if let Value::Float(f) = arg { acc -= f; }
|
|
}
|
|
if acc.fract() == 0.0 { Value::Int(acc as i64) } else { Value::Float(acc) }
|
|
});
|
|
|
|
// --- Multiply (*) ---
|
|
let mul_ty = StaticType::FunctionOverloads(vec![
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Int, StaticType::Int]), ret: StaticType::Int },
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Float, StaticType::Float]), ret: StaticType::Float },
|
|
]);
|
|
env.register_native("*", mul_ty, true, |args| {
|
|
if args.len() == 2 {
|
|
match (&args[0], &args[1]) {
|
|
(Value::Int(a), Value::Int(b)) => Value::Int(a * b),
|
|
(Value::Float(a), Value::Float(b)) => Value::Float(a * b),
|
|
(Value::Int(a), Value::Float(b)) => Value::Float(*a as f64 * b),
|
|
(Value::Float(a), Value::Int(b)) => Value::Float(a * *b as f64),
|
|
_ => Value::Void,
|
|
}
|
|
} else {
|
|
let mut acc = 1.0;
|
|
for arg in args {
|
|
if let Value::Int(i) = arg { acc *= i as f64; }
|
|
else if let Value::Float(f) = arg { acc *= f; }
|
|
}
|
|
if acc.fract() == 0.0 { Value::Int(acc as i64) } else { Value::Float(acc) }
|
|
}
|
|
});
|
|
|
|
// --- Divide (/) ---
|
|
let div_ty = StaticType::FunctionOverloads(vec![
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Int, StaticType::Int]), ret: StaticType::Float },
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Float, StaticType::Float]), ret: StaticType::Float },
|
|
]);
|
|
env.register_native("/", div_ty, true, |args| {
|
|
if args.len() != 2 { return Value::Void; }
|
|
let a = match args[0] { Value::Int(i) => i as f64, Value::Float(f) => f, _ => return Value::Void };
|
|
let b = match args[1] { Value::Int(i) => i as f64, Value::Float(f) => f, _ => return Value::Void };
|
|
if b == 0.0 { Value::Float(f64::NAN) } else { Value::Float(a / b) }
|
|
});
|
|
|
|
// --- Integer Divide (//) ---
|
|
let int_div_ty = StaticType::Function(Box::new(
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Int, StaticType::Int]), ret: StaticType::Int }
|
|
));
|
|
env.register_native("//", int_div_ty, true, |args| {
|
|
if args.len() != 2 { return Value::Void; }
|
|
match (&args[0], &args[1]) {
|
|
(Value::Int(a), Value::Int(b)) => {
|
|
if *b == 0 { Value::Void } else { Value::Int(a / b) }
|
|
},
|
|
// Also allow float truncation? Delphi RTL says IntDivide takes DataValue and returns DataValue.
|
|
// Usually div is for integers. Let's stick to Int.
|
|
_ => Value::Void,
|
|
}
|
|
});
|
|
|
|
// --- Modulus (%) ---
|
|
let mod_ty = StaticType::Function(Box::new(
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Int, StaticType::Int]), ret: StaticType::Int }
|
|
));
|
|
env.register_native("%", mod_ty, true, |args| {
|
|
if args.len() != 2 { return Value::Void; }
|
|
match (&args[0], &args[1]) {
|
|
(Value::Int(a), Value::Int(b)) => {
|
|
if *b == 0 { Value::Void } else { Value::Int(a % b) }
|
|
},
|
|
_ => Value::Void,
|
|
}
|
|
});
|
|
}
|
|
|
|
fn register_comparison(env: &Environment) {
|
|
let cmp_ty = StaticType::FunctionOverloads(vec![
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Int, StaticType::Int]), ret: StaticType::Bool },
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Float, StaticType::Float]), ret: StaticType::Bool },
|
|
Signature { params: StaticType::Tuple(vec![StaticType::DateTime, StaticType::DateTime]), ret: StaticType::Bool },
|
|
]);
|
|
|
|
// --- Greater Than (>) ---
|
|
env.register_native(">", cmp_ty.clone(), true, |args| {
|
|
if args.len() != 2 { return Value::Void; }
|
|
match (&args[0], &args[1]) {
|
|
(Value::Int(a), Value::Int(b)) => Value::Bool(a > b),
|
|
(Value::Int(a), Value::Float(b)) => Value::Bool((*a as f64) > *b),
|
|
(Value::Float(a), Value::Int(b)) => Value::Bool(*a > (*b as f64)),
|
|
(Value::Float(a), Value::Float(b)) => Value::Bool(a > b),
|
|
(Value::DateTime(a), Value::DateTime(b)) => Value::Bool(a > b),
|
|
_ => Value::Bool(false),
|
|
}
|
|
});
|
|
|
|
// --- Less Than (<) ---
|
|
env.register_native("<", cmp_ty.clone(), true, |args| {
|
|
if args.len() != 2 { return Value::Void; }
|
|
match (&args[0], &args[1]) {
|
|
(Value::Int(a), Value::Int(b)) => Value::Bool(a < b),
|
|
(Value::Int(a), Value::Float(b)) => Value::Bool((*a as f64) < *b),
|
|
(Value::Float(a), Value::Int(b)) => Value::Bool(*a < (*b as f64)),
|
|
(Value::Float(a), Value::Float(b)) => Value::Bool(a < b),
|
|
(Value::DateTime(a), Value::DateTime(b)) => Value::Bool(a < b),
|
|
_ => Value::Bool(false),
|
|
}
|
|
});
|
|
|
|
// --- Greater Or Equal (>=) ---
|
|
env.register_native(">=", cmp_ty.clone(), true, |args| {
|
|
if args.len() != 2 { return Value::Void; }
|
|
match (&args[0], &args[1]) {
|
|
(Value::Int(a), Value::Int(b)) => Value::Bool(a >= b),
|
|
(Value::Int(a), Value::Float(b)) => Value::Bool((*a as f64) >= *b),
|
|
(Value::Float(a), Value::Int(b)) => Value::Bool(*a >= (*b as f64)),
|
|
(Value::Float(a), Value::Float(b)) => Value::Bool(a >= b),
|
|
(Value::DateTime(a), Value::DateTime(b)) => Value::Bool(a >= b),
|
|
_ => Value::Bool(false),
|
|
}
|
|
});
|
|
|
|
// --- Less Or Equal (<=) ---
|
|
env.register_native("<=", cmp_ty.clone(), true, |args| {
|
|
if args.len() != 2 { return Value::Void; }
|
|
match (&args[0], &args[1]) {
|
|
(Value::Int(a), Value::Int(b)) => Value::Bool(a <= b),
|
|
(Value::Int(a), Value::Float(b)) => Value::Bool((*a as f64) <= *b),
|
|
(Value::Float(a), Value::Int(b)) => Value::Bool(*a <= (*b as f64)),
|
|
(Value::Float(a), Value::Float(b)) => Value::Bool(a <= b),
|
|
(Value::DateTime(a), Value::DateTime(b)) => Value::Bool(a <= b),
|
|
_ => Value::Bool(false),
|
|
}
|
|
});
|
|
|
|
// --- Equal (=) ---
|
|
let eq_ty = StaticType::Function(Box::new(
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Any, StaticType::Any]), ret: StaticType::Bool }
|
|
));
|
|
env.register_native("=", eq_ty.clone(), true, |args| {
|
|
if args.len() != 2 { return Value::Void; }
|
|
// Simple equality check.
|
|
// Note: Floating point equality is tricky, but we follow standard behavior for now.
|
|
match (&args[0], &args[1]) {
|
|
(Value::Int(a), Value::Int(b)) => Value::Bool(a == b),
|
|
(Value::Float(a), Value::Float(b)) => Value::Bool((a - b).abs() < f64::EPSILON),
|
|
(Value::Int(a), Value::Float(b)) => Value::Bool((*a as f64 - b).abs() < f64::EPSILON),
|
|
(Value::Float(a), Value::Int(b)) => Value::Bool((a - *b as f64).abs() < f64::EPSILON),
|
|
(Value::Text(a), Value::Text(b)) => Value::Bool(a == b),
|
|
(Value::Bool(a), Value::Bool(b)) => Value::Bool(a == b),
|
|
(Value::Keyword(a), Value::Keyword(b)) => Value::Bool(a == b),
|
|
(Value::DateTime(a), Value::DateTime(b)) => Value::Bool(a == b),
|
|
(Value::Void, Value::Void) => Value::Bool(true),
|
|
_ => Value::Bool(false),
|
|
}
|
|
});
|
|
|
|
// --- Not Equal (<>) ---
|
|
env.register_native("<>", eq_ty, true, |args| {
|
|
if args.len() != 2 { return Value::Void; }
|
|
match (&args[0], &args[1]) {
|
|
(Value::Int(a), Value::Int(b)) => Value::Bool(a != b),
|
|
(Value::Float(a), Value::Float(b)) => Value::Bool((a - b).abs() >= f64::EPSILON),
|
|
(Value::Int(a), Value::Float(b)) => Value::Bool((*a as f64 - b).abs() >= f64::EPSILON),
|
|
(Value::Float(a), Value::Int(b)) => Value::Bool((a - *b as f64).abs() >= f64::EPSILON),
|
|
(Value::Text(a), Value::Text(b)) => Value::Bool(a != b),
|
|
(Value::Bool(a), Value::Bool(b)) => Value::Bool(a != b),
|
|
(Value::Keyword(a), Value::Keyword(b)) => Value::Bool(a != b),
|
|
(Value::DateTime(a), Value::DateTime(b)) => Value::Bool(a != b),
|
|
(Value::Void, Value::Void) => Value::Bool(false),
|
|
_ => Value::Bool(true),
|
|
}
|
|
});
|
|
}
|
|
|
|
fn register_logic(env: &Environment) {
|
|
// --- Not (not) ---
|
|
let not_ty = StaticType::FunctionOverloads(vec![
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Bool]), ret: StaticType::Bool },
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Int]), ret: StaticType::Int },
|
|
]);
|
|
env.register_native("not", not_ty, true, |args| {
|
|
if args.len() != 1 { return Value::Void; }
|
|
match &args[0] {
|
|
Value::Bool(b) => Value::Bool(!b),
|
|
Value::Int(i) => Value::Int(!i), // Bitwise NOT
|
|
_ => Value::Void,
|
|
}
|
|
});
|
|
|
|
// --- And (and) ---
|
|
let logic_op_ty = StaticType::FunctionOverloads(vec![
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Bool, StaticType::Bool]), ret: StaticType::Bool },
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Int, StaticType::Int]), ret: StaticType::Int },
|
|
]);
|
|
env.register_native("and", logic_op_ty.clone(), true, |args| {
|
|
if args.len() != 2 { return Value::Void; }
|
|
match (&args[0], &args[1]) {
|
|
(Value::Bool(a), Value::Bool(b)) => Value::Bool(*a && *b),
|
|
(Value::Int(a), Value::Int(b)) => Value::Int(a & b),
|
|
_ => Value::Void,
|
|
}
|
|
});
|
|
|
|
// --- Or (or) ---
|
|
env.register_native("or", logic_op_ty.clone(), true, |args| {
|
|
if args.len() != 2 { return Value::Void; }
|
|
match (&args[0], &args[1]) {
|
|
(Value::Bool(a), Value::Bool(b)) => Value::Bool(*a || *b),
|
|
(Value::Int(a), Value::Int(b)) => Value::Int(a | b),
|
|
_ => Value::Void,
|
|
}
|
|
});
|
|
|
|
// --- Xor (xor) ---
|
|
env.register_native("xor", logic_op_ty.clone(), true, |args| {
|
|
if args.len() != 2 { return Value::Void; }
|
|
match (&args[0], &args[1]) {
|
|
(Value::Bool(a), Value::Bool(b)) => Value::Bool(*a ^ *b),
|
|
(Value::Int(a), Value::Int(b)) => Value::Int(a ^ b),
|
|
_ => Value::Void,
|
|
}
|
|
});
|
|
|
|
// --- Shift Left (<<) ---
|
|
let shift_ty = StaticType::Function(Box::new(
|
|
Signature { params: StaticType::Tuple(vec![StaticType::Int, StaticType::Int]), ret: StaticType::Int }
|
|
));
|
|
env.register_native("<<", shift_ty.clone(), true, |args| {
|
|
if args.len() != 2 { return Value::Void; }
|
|
match (&args[0], &args[1]) {
|
|
(Value::Int(a), Value::Int(b)) => Value::Int(a << b),
|
|
_ => Value::Void,
|
|
}
|
|
});
|
|
|
|
// --- Shift Right (>>) ---
|
|
env.register_native(">>", shift_ty, true, |args| {
|
|
if args.len() != 2 { return Value::Void; }
|
|
match (&args[0], &args[1]) {
|
|
(Value::Int(a), Value::Int(b)) => Value::Int(a >> b),
|
|
_ => Value::Void,
|
|
}
|
|
});
|
|
}
|