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:
+14
-154
@@ -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> {
|
||||
|
||||
Reference in New Issue
Block a user