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:
+61
-220
@@ -1,201 +1,37 @@
|
||||
use crate::{
|
||||
backend::environment::EnvironmentStack,
|
||||
frontend::{
|
||||
common::{
|
||||
ast::{AstNode, AstNodeKind, Expr, Stmt},
|
||||
source_registry::SourceSlice,
|
||||
tokens::{LiteralValue, NativeFunction, TokenType},
|
||||
base_value::{BaseValue, NativeFunction, Number, Truthy},
|
||||
lox_result::{runtime_error, LoxError, LoxResult},
|
||||
},
|
||||
logging::display_ast::PrettyPrintExt,
|
||||
result::{runtime_error, LoxError, LoxResult},
|
||||
frontend::{source_registry::SourceSlice, tokens::TokenType},
|
||||
};
|
||||
use std::{
|
||||
fmt::{Debug, Display},
|
||||
ops::{Add, Div, Mul, Neg, Not, Rem, Sub},
|
||||
};
|
||||
|
||||
fn error(message: String) -> LoxError {
|
||||
LoxError::RuntimeError {
|
||||
source_slice: SourceSlice::default(), // todo change this with the actual source slice
|
||||
message: message,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_at(source_slice: SourceSlice, message: String) -> LoxError {
|
||||
LoxError::RuntimeError {
|
||||
source_slice,
|
||||
message,
|
||||
}
|
||||
}
|
||||
|
||||
impl Not for LiteralValue {
|
||||
type Output = LiteralValue;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
match self {
|
||||
LiteralValue::Boolean(b) => LiteralValue::Boolean(!b),
|
||||
LiteralValue::Number(n) => LiteralValue::Boolean(n == 0.0),
|
||||
_ => LiteralValue::Boolean(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Truthy {
|
||||
fn is_truthy(&self) -> bool;
|
||||
}
|
||||
|
||||
impl Truthy for LiteralValue {
|
||||
fn is_truthy(&self) -> bool {
|
||||
match self {
|
||||
LiteralValue::Boolean(b) => *b,
|
||||
LiteralValue::Number(n) => *n != 0.0,
|
||||
LiteralValue::String(s) => !s.is_empty(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for LiteralValue {
|
||||
type Output = LoxResult<LiteralValue>;
|
||||
|
||||
fn neg(self) -> Self::Output {
|
||||
Ok(LiteralValue::Boolean(!self.is_truthy()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for LiteralValue {
|
||||
type Output = LoxResult<LiteralValue>;
|
||||
|
||||
fn add(self, other: LiteralValue) -> Self::Output {
|
||||
match (self, other) {
|
||||
(LiteralValue::Number(a), LiteralValue::Number(b)) => Ok(LiteralValue::Number(a + b)),
|
||||
(LiteralValue::String(a), LiteralValue::String(b)) => {
|
||||
Ok(LiteralValue::String(format!("{}{}", a, b)))
|
||||
}
|
||||
_ => Err(error("Cannot add non-numeric values".to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LiteralValue {
|
||||
pub fn add_with_source(
|
||||
self,
|
||||
other: LiteralValue,
|
||||
source_slice: SourceSlice,
|
||||
) -> LoxResult<LiteralValue> {
|
||||
match (self, other) {
|
||||
(LiteralValue::Number(a), LiteralValue::Number(b)) => Ok(LiteralValue::Number(a + b)),
|
||||
(LiteralValue::String(a), LiteralValue::String(b)) => {
|
||||
Ok(LiteralValue::String(format!("{}{}", a, b)))
|
||||
}
|
||||
_ => Err(error_at(
|
||||
source_slice,
|
||||
"Cannot add non-numeric values".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for LiteralValue {
|
||||
type Output = LoxResult<LiteralValue>;
|
||||
|
||||
fn sub(self, other: LiteralValue) -> Self::Output {
|
||||
match (self, other) {
|
||||
(LiteralValue::Number(a), LiteralValue::Number(b)) => Ok(LiteralValue::Number(a - b)),
|
||||
_ => Err(error("Cannot subtract non-numeric values".to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Div for LiteralValue {
|
||||
type Output = LoxResult<LiteralValue>;
|
||||
|
||||
fn div(self, other: LiteralValue) -> Self::Output {
|
||||
match (self, other) {
|
||||
(LiteralValue::Number(a), LiteralValue::Number(b)) => {
|
||||
if b == 0.0 {
|
||||
Err(error("Division by zero".to_string()))
|
||||
} else {
|
||||
Ok(LiteralValue::Number(a / b))
|
||||
}
|
||||
}
|
||||
_ => Err(error("Cannot divide non-numeric values".to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for LiteralValue {
|
||||
type Output = LoxResult<LiteralValue>;
|
||||
|
||||
fn mul(self, other: LiteralValue) -> Self::Output {
|
||||
match (self, other) {
|
||||
(LiteralValue::Number(a), LiteralValue::Number(b)) => Ok(LiteralValue::Number(a * b)),
|
||||
_ => Err(error("Cannot multiply non-numeric values".to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Rem for LiteralValue {
|
||||
type Output = LoxResult<LiteralValue>;
|
||||
|
||||
fn rem(self, other: LiteralValue) -> Self::Output {
|
||||
match (self, other) {
|
||||
(LiteralValue::Number(a), LiteralValue::Number(b)) => {
|
||||
if b == 0.0 {
|
||||
Err(error("Division by zero".to_string()))
|
||||
} else {
|
||||
Ok(LiteralValue::Number(a % b))
|
||||
}
|
||||
}
|
||||
_ => Err(error("Cannot divide non-numeric values".to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for LiteralValue {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
match (self, other) {
|
||||
(LiteralValue::Number(a), LiteralValue::Number(b)) => a.partial_cmp(b),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LiteralValue> for bool {
|
||||
fn from(value: LiteralValue) -> Self {
|
||||
match value {
|
||||
LiteralValue::Boolean(b) => b,
|
||||
LiteralValue::Number(n) => n != 0.0,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
pub struct Interpreter {
|
||||
enviorment: EnvironmentStack,
|
||||
}
|
||||
|
||||
pub trait EvaluateInterpreter<T> {
|
||||
fn evaluate(&mut self, stmt: T) -> LoxResult<LiteralValue>;
|
||||
fn evaluate(&mut self, stmt: T) -> LoxResult<BaseValue>;
|
||||
}
|
||||
|
||||
impl<'a, R: AstNodeKind + Clone + Debug + Display> EvaluateInterpreter<AstNode<R>> for Interpreter
|
||||
where
|
||||
Interpreter: EvaluateInterpreter<R>,
|
||||
{
|
||||
fn evaluate(&mut self, stmt: AstNode<R>) -> LoxResult<LiteralValue> {
|
||||
fn evaluate(&mut self, stmt: AstNode<R>) -> LoxResult<BaseValue> {
|
||||
match self.evaluate(stmt.node.clone()) {
|
||||
Ok(value) => Ok(value),
|
||||
Err(err) => Err(LoxError::RuntimeError {
|
||||
source_slice: stmt.source_slice,
|
||||
message: err.get_message(),
|
||||
}),
|
||||
Err(err) => runtime_error(stmt.source_slice, err.get_message()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Direct Expr evaluation to avoid infinite recursion
|
||||
impl EvaluateInterpreter<Expr> for Interpreter {
|
||||
fn evaluate(&mut self, expr: Expr) -> LoxResult<LiteralValue> {
|
||||
fn evaluate(&mut self, expr: Expr) -> LoxResult<BaseValue> {
|
||||
match expr {
|
||||
Expr::Literal { value } => Ok(value),
|
||||
Expr::Identifier { name } => self.enviorment.get(&name),
|
||||
@@ -219,7 +55,7 @@ impl EvaluateInterpreter<Expr> for Interpreter {
|
||||
}
|
||||
|
||||
impl EvaluateInterpreter<AstNode<Stmt>> for Interpreter {
|
||||
fn evaluate(&mut self, node: AstNode<Stmt>) -> LoxResult<LiteralValue> {
|
||||
fn evaluate(&mut self, node: AstNode<Stmt>) -> LoxResult<BaseValue> {
|
||||
let stmt = node.node;
|
||||
let result = self.interpret_stmt_inner(stmt);
|
||||
match result {
|
||||
@@ -240,13 +76,13 @@ impl Interpreter {
|
||||
let mut env = EnvironmentStack::new();
|
||||
let _ = env.declare(
|
||||
"clock".to_string(),
|
||||
LiteralValue::NativeFunction(NativeFunction::new(0, |_args| {
|
||||
LiteralValue::Number(
|
||||
BaseValue::NativeFunction(NativeFunction::new(Vec::default(), |_args| {
|
||||
BaseValue::Number(Number::U128(
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_millis() as f64,
|
||||
)
|
||||
.as_millis(),
|
||||
))
|
||||
})),
|
||||
);
|
||||
Self { enviorment: env }
|
||||
@@ -255,7 +91,7 @@ impl Interpreter {
|
||||
&mut self,
|
||||
callee: Box<AstNode<Expr>>,
|
||||
arguments: Vec<AstNode<Expr>>,
|
||||
) -> LoxResult<LiteralValue> {
|
||||
) -> LoxResult<BaseValue> {
|
||||
let source_slice = callee.source_slice.clone();
|
||||
|
||||
// Estrai il nome della variabile se il callee è un identificatore
|
||||
@@ -279,11 +115,11 @@ impl Interpreter {
|
||||
let evaluated_arguments = arguments
|
||||
.iter()
|
||||
.map(|arg| self.evaluate(arg.clone()))
|
||||
.collect::<Result<Vec<LiteralValue>, LoxError>>()?;
|
||||
.collect::<Result<Vec<BaseValue>, LoxError>>()?;
|
||||
|
||||
match function {
|
||||
LiteralValue::NativeFunction(func) => Ok((func.function)(&evaluated_arguments)),
|
||||
LiteralValue::Function(func) => {
|
||||
BaseValue::NativeFunction(func) => Ok((func.function)(&evaluated_arguments)),
|
||||
BaseValue::Function(func) => {
|
||||
func.parameters
|
||||
.iter()
|
||||
.enumerate()
|
||||
@@ -291,7 +127,7 @@ impl Interpreter {
|
||||
let value = evaluated_arguments.get(index).unwrap();
|
||||
self.enviorment.declare(par.0.clone(), value.clone())
|
||||
})
|
||||
.collect::<LoxResult<Vec<LiteralValue>>>()?;
|
||||
.collect::<LoxResult<Vec<BaseValue>>>()?;
|
||||
self.evaluate(func.body)
|
||||
}
|
||||
_ => runtime_error(
|
||||
@@ -303,49 +139,54 @@ impl Interpreter {
|
||||
|
||||
fn evaluate_binary(
|
||||
&mut self,
|
||||
left: LiteralValue,
|
||||
left: BaseValue,
|
||||
operator: TokenType,
|
||||
right: LiteralValue,
|
||||
) -> LoxResult<LiteralValue> {
|
||||
right: BaseValue,
|
||||
) -> LoxResult<BaseValue> {
|
||||
match operator {
|
||||
TokenType::Plus => left + right,
|
||||
TokenType::Minus => left - right,
|
||||
TokenType::Star => left * right,
|
||||
TokenType::Slash => left / right,
|
||||
TokenType::Greater => Ok(LiteralValue::Boolean(left > right)),
|
||||
TokenType::GreaterEqual => Ok(LiteralValue::Boolean(left >= right)),
|
||||
TokenType::Less => Ok(LiteralValue::Boolean(left < right)),
|
||||
TokenType::LessEqual => Ok(LiteralValue::Boolean(left <= right)),
|
||||
TokenType::EqualEqual => Ok(LiteralValue::Boolean(left == right)),
|
||||
TokenType::BangEqual => Ok(LiteralValue::Boolean(left != right)),
|
||||
TokenType::And => Ok(LiteralValue::Boolean(left.is_truthy() && right.is_truthy())),
|
||||
TokenType::Or => Ok(LiteralValue::Boolean(left.is_truthy() || right.is_truthy())),
|
||||
_ => Err(error(format!(
|
||||
"Unsupported binary operator: {:?}",
|
||||
operator
|
||||
))),
|
||||
TokenType::Greater => Ok(BaseValue::Boolean(left > right)),
|
||||
TokenType::GreaterEqual => Ok(BaseValue::Boolean(left >= right)),
|
||||
TokenType::Less => Ok(BaseValue::Boolean(left < right)),
|
||||
TokenType::LessEqual => Ok(BaseValue::Boolean(left <= right)),
|
||||
TokenType::EqualEqual => Ok(BaseValue::Boolean(left == right)),
|
||||
TokenType::BangEqual => Ok(BaseValue::Boolean(left != right)),
|
||||
TokenType::And => Ok(BaseValue::Boolean(left.is_truthy() && right.is_truthy())),
|
||||
TokenType::Or => Ok(BaseValue::Boolean(left.is_truthy() || right.is_truthy())),
|
||||
_ => Err(LoxError::RuntimeError {
|
||||
source_slice: SourceSlice::default(),
|
||||
message: format!("Unsupported binary operator: {:?}", operator),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn evaluate_unary(
|
||||
&mut self,
|
||||
operator: TokenType,
|
||||
operand: LiteralValue,
|
||||
) -> LoxResult<LiteralValue> {
|
||||
fn evaluate_unary(&mut self, operator: TokenType, operand: BaseValue) -> LoxResult<BaseValue> {
|
||||
match operator {
|
||||
TokenType::Minus => -operand,
|
||||
TokenType::Minus => match operand {
|
||||
BaseValue::Number(n) => Ok(BaseValue::Number(n.neg())),
|
||||
_ => Err(LoxError::RuntimeError {
|
||||
source_slice: SourceSlice::default(),
|
||||
message: "Cannot negate non-numeric value".to_string(),
|
||||
}),
|
||||
},
|
||||
TokenType::Bang => Ok(!operand),
|
||||
_ => Err(error(format!("Unsupported unary operator: {:?}", operator))),
|
||||
_ => Err(LoxError::RuntimeError {
|
||||
source_slice: SourceSlice::default(),
|
||||
message: format!("Unsupported unary operator: {:?}", operator),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn interpret_stmt_inner(&mut self, stmt: Stmt) -> LoxResult<LiteralValue> {
|
||||
fn interpret_stmt_inner(&mut self, stmt: Stmt) -> LoxResult<BaseValue> {
|
||||
match stmt {
|
||||
Stmt::Expression { expression } => self.evaluate(*expression),
|
||||
Stmt::Print { expression } => {
|
||||
let value = self.evaluate(*expression)?;
|
||||
println!("print interpreter: \t{}", value);
|
||||
Ok(LiteralValue::Nil)
|
||||
println!("{}", value);
|
||||
Ok(BaseValue::Nil)
|
||||
}
|
||||
Stmt::Block { statements } => self.evaluate_block(*statements),
|
||||
Stmt::Stmt { expression } => self.evaluate(*expression.clone()),
|
||||
@@ -362,7 +203,7 @@ impl Interpreter {
|
||||
}
|
||||
_ => self.evaluate(*expr_node)?,
|
||||
},
|
||||
None => LiteralValue::Nil,
|
||||
None => BaseValue::Nil,
|
||||
};
|
||||
self.enviorment.declare(name.clone(), value)
|
||||
}
|
||||
@@ -381,7 +222,7 @@ impl Interpreter {
|
||||
while self.evaluate(*condition.clone())?.is_truthy() {
|
||||
self.evaluate(*body.clone())?;
|
||||
}
|
||||
Ok(LiteralValue::Nil)
|
||||
Ok(BaseValue::Nil)
|
||||
}
|
||||
Stmt::For {
|
||||
variable,
|
||||
@@ -392,10 +233,10 @@ impl Interpreter {
|
||||
self.enviorment.push_new_scope();
|
||||
let source_slice = variable.source_slice.clone();
|
||||
let val = self.evaluate(*variable)?;
|
||||
if !matches!(val, LiteralValue::Number(..)) {
|
||||
if !matches!(val, BaseValue::Number(..)) {
|
||||
return runtime_error(source_slice, "Expected number literal");
|
||||
}
|
||||
let mut ret = LiteralValue::Nil;
|
||||
let mut ret = BaseValue::Nil;
|
||||
while self.evaluate(*condition.clone())?.is_truthy() {
|
||||
ret = self.evaluate(*body.clone())?;
|
||||
self.evaluate(*increment.clone())?;
|
||||
@@ -411,18 +252,18 @@ impl Interpreter {
|
||||
then_branch: Box<AstNode<Stmt>>,
|
||||
elif_branch: Vec<(Box<AstNode<Expr>>, Box<AstNode<Stmt>>)>,
|
||||
else_branch: Option<Box<AstNode<Stmt>>>,
|
||||
) -> LoxResult<LiteralValue> {
|
||||
) -> LoxResult<BaseValue> {
|
||||
let condition = self.evaluate(*condition)?;
|
||||
match condition {
|
||||
LiteralValue::Boolean(true) => self.evaluate(*then_branch),
|
||||
LiteralValue::Boolean(false) => {
|
||||
BaseValue::Boolean(true) => self.evaluate(*then_branch),
|
||||
BaseValue::Boolean(false) => {
|
||||
for (elif_condition, elif_then_branch) in elif_branch {
|
||||
let condition = self.evaluate(*elif_condition)?;
|
||||
match condition {
|
||||
LiteralValue::Boolean(true) => {
|
||||
BaseValue::Boolean(true) => {
|
||||
return self.evaluate(*elif_then_branch);
|
||||
}
|
||||
LiteralValue::Boolean(false) => continue,
|
||||
BaseValue::Boolean(false) => continue,
|
||||
_ => {
|
||||
return Err(LoxError::TypeMismatch {
|
||||
source_slice: SourceSlice::default(), // todo change this to the actual source slice
|
||||
@@ -435,7 +276,7 @@ impl Interpreter {
|
||||
if let Some(else_block) = else_branch {
|
||||
self.evaluate(*else_block)
|
||||
} else {
|
||||
Ok(LiteralValue::Nil)
|
||||
Ok(BaseValue::Nil)
|
||||
}
|
||||
}
|
||||
_ => Err(LoxError::TypeMismatch {
|
||||
@@ -446,7 +287,7 @@ impl Interpreter {
|
||||
}
|
||||
}
|
||||
|
||||
fn evaluate_block(&mut self, statements: Vec<AstNode<Stmt>>) -> LoxResult<LiteralValue> {
|
||||
fn evaluate_block(&mut self, statements: Vec<AstNode<Stmt>>) -> LoxResult<BaseValue> {
|
||||
self.enviorment.push_new_scope();
|
||||
let (elements, final_expr) = match statements.split_last() {
|
||||
Some((stmt, body)) => match &stmt.node {
|
||||
@@ -474,7 +315,7 @@ impl Interpreter {
|
||||
}
|
||||
None => {
|
||||
self.enviorment.pop_scope();
|
||||
Ok(LiteralValue::Nil)
|
||||
Ok(BaseValue::Nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user