Switch interpreter to use new base value types

The commit changes the interpreter and related code to use the new
`BaeValue` and `Number` types, moving away from direct floats. The main
changes include:

- Replace LiteralValue with BaeValue across the codebase - Add Number
enum for type-safe numeric values - Move AST definitions to common
module - Update operators to handle new numeric types - Add 'is' token
type and parser support Switch to common base_value library for value
types

This commit represents a significant refactoring to switch the
interpreter to use a new shared base value type system. The main changes
include:

1. Move value types to a new common/base_value.rs module 2. Add richer
numeric type support with promotion rules 3. Update interpreter to use
new BaseValue enum 4. Move AST types to common module for sharing 5.
Consolidate value operations like Add, Sub etc 6. Add proper test
coverage for numeric operations

The commit improves type safety and adds more robust numeric handling
while keeping the interpreter's core logic clean.
This commit is contained in:
Giulio Agostini
2025-10-07 14:28:24 +02:00
parent fa2e9178fb
commit c1ecd7d1f7
17 changed files with 1074 additions and 398 deletions
+187
View File
@@ -0,0 +1,187 @@
use rlox::common::base_value::{BaseValue, Number};
#[test]
fn test_number_addition_with_type_promotion() {
// Test float + int -> both become F64
let float_num = Number::F64(3.5);
let int_num = Number::I32(2);
let result = float_num.add(int_num);
match result {
Number::F64(val) => assert_eq!(val, 5.5),
_ => panic!("Expected F64 result from float + int"),
}
// Test int + unsigned -> both become I64
let int_num = Number::I32(-5);
let unsigned_num = Number::U32(10);
let result = int_num.add(unsigned_num);
match result {
Number::I64(val) => assert_eq!(val, 5),
_ => panic!("Expected I64 result from int + unsigned"),
}
// Test float + unsigned -> both become F64
let float_num = Number::F32(2.5);
let unsigned_num = Number::U64(3);
let result = float_num.add(unsigned_num);
match result {
Number::F64(val) => assert_eq!(val, 5.5),
_ => panic!("Expected F64 result from float + unsigned"),
}
// Test unsigned + unsigned -> both stay U128
let unsigned1 = Number::U32(100);
let unsigned2 = Number::U64(200);
let result = unsigned1.add(unsigned2);
match result {
Number::U128(val) => assert_eq!(val, 300),
_ => panic!("Expected U128 result from unsigned + unsigned"),
}
}
#[test]
fn test_number_subtraction() {
let a = Number::F64(10.5);
let b = Number::I32(5);
let result = a.sub(b);
match result {
Number::F64(val) => assert_eq!(val, 5.5),
_ => panic!("Expected F64 result"),
}
let a = Number::I32(10);
let b = Number::U32(3);
let result = a.sub(b);
match result {
Number::I64(val) => assert_eq!(val, 7),
_ => panic!("Expected I64 result"),
}
}
#[test]
fn test_number_multiplication() {
let a = Number::F64(2.5);
let b = Number::I32(4);
let result = a.mul(b);
match result {
Number::F64(val) => assert_eq!(val, 10.0),
_ => panic!("Expected F64 result"),
}
let a = Number::U32(5);
let b = Number::U32(6);
let result = a.mul(b);
match result {
Number::U128(val) => assert_eq!(val, 30),
_ => panic!("Expected U128 result"),
}
}
#[test]
fn test_number_division() {
let a = Number::F64(10.0);
let b = Number::I32(4);
let result = a.div(b).unwrap();
match result {
Number::F64(val) => assert_eq!(val, 2.5),
_ => panic!("Expected F64 result"),
}
// Test division by zero
let a = Number::I32(10);
let b = Number::I32(0);
assert!(a.div(b).is_none());
let a = Number::F64(10.0);
let b = Number::F64(0.0);
assert!(a.div(b).is_none());
}
#[test]
fn test_number_remainder() {
let a = Number::I32(10);
let b = Number::I32(3);
let result = a.rem(b).unwrap();
match result {
Number::I64(val) => assert_eq!(val, 1),
_ => panic!("Expected I64 result"),
}
let a = Number::F64(10.5);
let b = Number::F64(3.0);
let result = a.rem(b).unwrap();
match result {
Number::F64(val) => assert_eq!(val, 1.5),
_ => panic!("Expected F64 result"),
}
}
#[test]
fn test_number_negation() {
let num = Number::I32(5);
let result = num.neg();
match result {
Number::I32(val) => assert_eq!(val, -5),
_ => panic!("Expected I32 result"),
}
let num = Number::U32(5);
let result = num.neg();
match result {
Number::I64(val) => assert_eq!(val, -5),
_ => panic!("Expected I64 result for negated unsigned"),
}
let num = Number::F64(3.5);
let result = num.neg();
match result {
Number::F64(val) => assert_eq!(val, -3.5),
_ => panic!("Expected F64 result"),
}
}
#[test]
fn test_number_comparison() {
let a = Number::I32(5);
let b = Number::F64(5.0);
assert_eq!(a.partial_cmp(&b), Some(std::cmp::Ordering::Equal));
let a = Number::I32(5);
let b = Number::F64(6.0);
assert_eq!(a.partial_cmp(&b), Some(std::cmp::Ordering::Less));
let a = Number::U32(10);
let b = Number::I32(5);
assert_eq!(a.partial_cmp(&b), Some(std::cmp::Ordering::Greater));
}
#[test]
fn test_baevalue_operations() {
// Test BaseValue addition
let a = BaseValue::Number(Number::I32(5));
let b = BaseValue::Number(Number::F64(3.5));
let result = (a + b).unwrap();
match result {
BaseValue::Number(Number::F64(val)) => assert_eq!(val, 8.5),
_ => panic!("Expected Number(F64) result"),
}
// Test string concatenation
let a = BaseValue::String("Hello ".to_string());
let b = BaseValue::String("World".to_string());
let result = (a + b).unwrap();
match result {
BaseValue::String(s) => assert_eq!(s, "Hello World"),
_ => panic!("Expected String result"),
}
}
#[test]
fn test_is_zero() {
assert!(Number::I32(0).is_zero());
assert!(Number::F64(0.0).is_zero());
assert!(Number::U32(0).is_zero());
assert!(!Number::I32(1).is_zero());
assert!(!Number::F64(0.1).is_zero());
assert!(!Number::U32(1).is_zero());
}