diff --git a/src/interpreter/interpretable.rs b/src/interpreter/interpretable.rs index 131f5c5..7f6c5c8 100644 --- a/src/interpreter/interpretable.rs +++ b/src/interpreter/interpretable.rs @@ -2,28 +2,16 @@ use std::{cell::RefCell, rc::Rc}; use crate::{ ast, - errors::{ErrorHandler, SloxError}, - interpreter::{Environment, EnvironmentRef, Value}, + errors::{ErrorKind, SloxError, SloxResult}, + interpreter::{functions::Function, Environment, EnvironmentRef, Value}, resolver::ResolvedVariables, tokens::{Token, TokenType}, }; -use super::functions::Function; - /// Evaluate an interpretable, returning its value. -pub fn evaluate( - err_hdl: &mut ErrorHandler, - ast: &dyn Interpretable, - vars: ResolvedVariables, -) -> Result { +pub fn evaluate(ast: &dyn Interpretable, vars: ResolvedVariables) -> SloxResult { let env = Rc::new(RefCell::new(Environment::default())); - match ast.interpret(&env) { - Ok(v) => Ok(v.result()), - Err(e) => { - err_hdl.report(e); - Err(e) - } - } + ast.interpret(&env).map(|v| v.result()) } /* ------- * @@ -43,7 +31,7 @@ pub enum InterpreterFlowControl { impl InterpreterFlowControl { /// Return the result's value. If the flow control value does not represent /// a result, panic. - pub(crate) fn result(self) -> Value { + pub fn result(self) -> Value { match self { Self::Result(v) => v, other => panic!("Result expected, {:?} found instead", other), @@ -52,7 +40,7 @@ impl InterpreterFlowControl { /// Check whether a flow control value contains actual flow control /// information. - pub(crate) fn is_flow_control(&self) -> bool { + pub fn is_flow_control(&self) -> bool { matches!(self, Self::Break(_) | Self::Continue(_) | Self::Return(_)) } } @@ -70,13 +58,22 @@ impl From for InterpreterFlowControl { } /// A result returned by some part of the interpreter. -pub type InterpreterResult = Result; +pub type InterpreterResult = SloxResult; /// An Interpretable can be evaluated and will return a value. pub trait Interpretable { fn interpret(&self, environment: &EnvironmentRef) -> InterpreterResult; } +/// Generate an error with a static message. +fn error(token: &Token, message: &str) -> SloxResult { + Err(SloxError::with_token( + ErrorKind::Runtime, + token, + message.to_owned(), + )) +} + /* ----------------------------- * * INTERPRETER FOR PROGRAM NODES * * ----------------------------- */ @@ -331,12 +328,12 @@ impl ast::ExprNode { TokenType::Plus => match (left_value, right_value) { (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b).into()), (Value::String(a), Value::String(b)) => Ok(Value::String(a + &b).into()), - _ => Err(SloxError::new(operator, "type error")), + _ => error(operator, "type error"), }, TokenType::Minus => match (left_value, right_value) { (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a - b).into()), - _ => Err(SloxError::new(operator, "type error")), + _ => error(operator, "type error"), }, TokenType::Star => match (left_value, right_value) { @@ -344,38 +341,38 @@ impl ast::ExprNode { (Value::String(a), Value::Number(b)) => { Ok(Value::String(a.repeat(b as usize)).into()) } - _ => Err(SloxError::new(operator, "type error")), + _ => error(operator, "type error"), }, TokenType::Slash => match (left_value, right_value) { (Value::Number(a), Value::Number(b)) => { if b == 0. { - Err(SloxError::new(operator, "division by zero")) + error(operator, "division by zero") } else { Ok(Value::Number(a / b).into()) } } - _ => Err(SloxError::new(operator, "type error")), + _ => error(operator, "type error"), }, TokenType::Greater => match (left_value, right_value) { (Value::Number(a), Value::Number(b)) => Ok(Value::Boolean(a > b).into()), - _ => Err(SloxError::new(operator, "type error")), + _ => error(operator, "type error"), }, TokenType::GreaterEqual => match (left_value, right_value) { (Value::Number(a), Value::Number(b)) => Ok(Value::Boolean(a >= b).into()), - _ => Err(SloxError::new(operator, "type error")), + _ => error(operator, "type error"), }, TokenType::Less => match (left_value, right_value) { (Value::Number(a), Value::Number(b)) => Ok(Value::Boolean(a < b).into()), - _ => Err(SloxError::new(operator, "type error")), + _ => error(operator, "type error"), }, TokenType::LessEqual => match (left_value, right_value) { (Value::Number(a), Value::Number(b)) => Ok(Value::Boolean(a <= b).into()), - _ => Err(SloxError::new(operator, "type error")), + _ => error(operator, "type error"), }, TokenType::EqualEqual => Ok(Value::Boolean(left_value == right_value).into()), @@ -401,7 +398,7 @@ impl ast::ExprNode { if let Value::Number(n) = right_value { Ok(Value::Number(-n).into()) } else { - Err(SloxError::new(operator, "number expected")) + error(operator, "number expected") } } @@ -446,9 +443,10 @@ impl ast::ExprNode { if let Value::Callable(callable_ref) = &callee { let callable = callable_ref.borrow(); if callable.arity() != arg_values.len() { - Err(SloxError::new( + Err(SloxError::with_token( + ErrorKind::Runtime, right_paren, - &format!( + format!( "expected {} arguments, found {}", arg_values.len(), callable.arity() @@ -458,10 +456,7 @@ impl ast::ExprNode { Ok(callable.call(environment, arg_values)?.into()) } } else { - Err(SloxError::new( - right_paren, - "can only call functions and classes", - )) + error(right_paren, "can only call functions and classes") } } } diff --git a/src/interpreter/value.rs b/src/interpreter/value.rs index f337910..f27198b 100644 --- a/src/interpreter/value.rs +++ b/src/interpreter/value.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, rc::Rc}; +use std::{cell::RefCell, fmt::Display, rc::Rc}; use super::Callable; @@ -24,6 +24,18 @@ impl PartialEq for Value { } } +impl Display for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Value::Nil => f.write_str("nil"), + Value::Boolean(b) => b.fmt(f), + Value::String(s) => s.fmt(f), + Value::Number(n) => n.fmt(f), + Value::Callable(c) => f.write_str(&c.borrow().to_string()), + } + } +} + impl Value { /// Check whether a value is truthy or not. pub fn is_truthy(&self) -> bool { diff --git a/src/main.rs b/src/main.rs index 9cb5f4b..e624fdd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,7 @@ use std::{ #[cfg(feature = "dump_ast")] use ast::AstDumper; use errors::{ErrorHandler, SloxResult}; -use interpreter::evaluate; +use interpreter::{evaluate, Value}; use parser::Parser; use resolver::resolve_variables; use scanner::Scanner; @@ -36,9 +36,12 @@ fn run(source: String) -> SloxResult<()> { #[cfg(feature = "dump_ast")] println!("AST generated ! {}", ast.dump()); - let resolved_vars = error_handler.final_error(resolve_variables(&ast))?; - error_handler.final_error(evaluate(&ast, resolved_vars)); - + let resolved_vars = error_handler.report_or_continue(resolve_variables(&ast))?; + let value = error_handler.report_or_continue(evaluate(&ast, resolved_vars))?; + match value { + Value::Nil => (), + _ => println!("{value}"), + } Ok(()) }