Add RTL functions for arithmetic and comparisons

This commit introduces a new module `ast::rtl` to house the standard
runtime library functions for the language. It includes implementations
for arithmetic operators (+, -, *, /, //, %), logical operators (and,
or, xor, not), bitwise shifts (<<, >>), and comparison operators (=, <>,
<, >, <=, >=).

Additionally, a `date` function for parsing datetime strings has been
added. This enhances the language's capability to handle basic
mathematical and logical operations.
Add RTL functions for arithmetic and comparisons

This commit introduces the runtime library (RTL) functions for basic
arithmetic operations, logical/bitwise operations, and comparisons. It
also includes a `date` function for parsing datetime strings.
Integration tests have been updated to cover these new functionalities.
This commit is contained in:
Michael Schimmel
2026-02-19 13:47:14 +01:00
parent a5957f524b
commit 07bf59eb47
7 changed files with 428 additions and 154 deletions
+14 -154
View File
@@ -11,6 +11,7 @@ use crate::ast::vm::{VM, TracingObserver};
use crate::ast::compiler::tco::TCO;
use crate::ast::compiler::dumper::Dumper;
use crate::ast::compiler::macros::{MacroExpander, MacroRegistry, MacroEvaluator};
use crate::ast::rtl;
pub struct Environment {
pub global_names: Rc<RefCell<HashMap<Symbol, u32>>>,
@@ -87,161 +88,20 @@ impl Environment {
values.push(Value::Function(Rc::new(func)));
}
pub fn register_constant(&self, name: &str, ty: StaticType, val: Value) {
let mut names = self.global_names.borrow_mut();
let mut types = self.global_types.borrow_mut();
let mut values = self.global_values.borrow_mut();
let idx = values.len() as u32;
names.insert(Symbol::from(name), idx);
types.insert(idx, ty);
values.push(val);
}
fn register_stdlib(&self) {
use crate::ast::types::Signature;
use chrono::{NaiveDate, NaiveDateTime};
self.register_native("date", StaticType::Function(Box::new(Signature {
params: vec![StaticType::Text],
ret: StaticType::DateTime
})), |args| {
if let Value::Text(s) = &args[0] {
// Try parse YYYY-MM-DD
if let Ok(dt) = NaiveDate::parse_from_str(s, "%Y-%m-%d") {
let ts = dt.and_hms_opt(0, 0, 0).unwrap().and_utc().timestamp_millis();
return Value::DateTime(ts);
}
// Try parse YYYY-MM-DD HH:MM:SS
if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S") {
return Value::DateTime(dt.and_utc().timestamp_millis());
}
}
Value::Void
});
let plus_ty = StaticType::FunctionOverloads(vec![
Signature { params: vec![StaticType::Int, StaticType::Int], ret: StaticType::Int },
Signature { params: vec![StaticType::Float, StaticType::Float], ret: StaticType::Float },
Signature { params: vec![StaticType::Text, StaticType::Text], ret: StaticType::Text },
Signature { params: vec![StaticType::DateTime, StaticType::Int], ret: StaticType::DateTime },
]);
self.register_native("+", plus_ty, |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 {
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) }
}
});
let minus_ty = StaticType::FunctionOverloads(vec![
Signature { params: vec![StaticType::Int, StaticType::Int], ret: StaticType::Int },
Signature { params: vec![StaticType::Float, StaticType::Float], ret: StaticType::Float },
Signature { params: vec![StaticType::Int], ret: StaticType::Int },
Signature { params: vec![StaticType::Float], ret: StaticType::Float },
Signature { params: vec![StaticType::DateTime, StaticType::DateTime], ret: StaticType::Int },
Signature { params: vec![StaticType::DateTime, StaticType::Int], ret: StaticType::DateTime },
]);
self.register_native("-", minus_ty, |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,
};
}
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) }
});
let mult_ty = StaticType::FunctionOverloads(vec![
Signature { params: vec![StaticType::Int, StaticType::Int], ret: StaticType::Int },
Signature { params: vec![StaticType::Float, StaticType::Float], ret: StaticType::Float },
]);
self.register_native("*", mult_ty, |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) }
}
});
let div_ty = StaticType::FunctionOverloads(vec![
Signature { params: vec![StaticType::Int, StaticType::Int], ret: StaticType::Float },
Signature { params: vec![StaticType::Float, StaticType::Float], ret: StaticType::Float },
]);
self.register_native("/", div_ty, |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 };
Value::Float(a / b)
});
let cmp_ty = StaticType::FunctionOverloads(vec![
Signature { params: vec![StaticType::Int, StaticType::Int], ret: StaticType::Bool },
Signature { params: vec![StaticType::Float, StaticType::Float], ret: StaticType::Bool },
Signature { params: vec![StaticType::DateTime, StaticType::DateTime], ret: StaticType::Bool },
]);
self.register_native(">", cmp_ty.clone(), |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),
}
});
self.register_native("<", cmp_ty, |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),
}
});
// Register all standard library functions via RTL module
rtl::register(self);
}
pub fn dump_ast(&self, source: &str) -> Result<String, String> {