use crate::parse::ast::{ BinaryOp, DeclareFunction, DeclareIdent, ExpressionList, Print, Program, UnaryOp, ValueExpression, VoidExpression, }; use crate::parser::ast::Assignment; use crate::runtime::executor::Visitor; use crate::runtime::value::{ForgeValue, UnsupportedOperation}; use std::collections::hash_map::Entry; use std::collections::HashMap; use std::error::Error; use std::fmt::{Debug, Display, Formatter}; #[derive(Clone, Default)] pub struct SimpleScope { data: HashMap<String, ForgeValue>, vtable: HashMap<String, DeclareFunction>, } #[derive(Clone, Default)] pub struct SimpleExecutor { scope: SimpleScope, } #[derive(Clone, Debug, PartialEq)] pub enum RuntimeError { Unsupported(&'static str), UndefinedVariable(String), VariableAlreadyDefined(String), BadOperands(UnsupportedOperation), } impl From<UnsupportedOperation> for RuntimeError { fn from(value: UnsupportedOperation) -> Self { Self::BadOperands(value) } } impl Display for RuntimeError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { RuntimeError::Unsupported(expr) => { write!( f, "[Runtime] Encountered an unsupported expression: {}", expr ) } RuntimeError::BadOperands(unsupported) => write!(f, "[Runtime] {}", unsupported), RuntimeError::UndefinedVariable(var) => { write!(f, "[Runtime] Attempt to access undefined variable {}", var) } RuntimeError::VariableAlreadyDefined(var) => write!( f, "[Runtime] Attempt to redefine already defined variable {}", var ), } } } impl Error for RuntimeError {} pub type RuntimeResult<T> = Result<T, RuntimeError>; impl SimpleExecutor { pub fn get_variable(&self, identifier: impl ToString) -> Option<&ForgeValue> { self.scope.data.get(&identifier.to_string()) } pub fn set_variable( &mut self, identifier: impl ToString, value: ForgeValue, ) -> RuntimeResult<()> { let entry = self.scope.data.entry(identifier.to_string()); match entry { Entry::Occupied(mut e) => { e.insert(value); Ok(()) } Entry::Vacant(_) => Err(RuntimeError::UndefinedVariable(identifier.to_string())), } } pub fn define_variable( &mut self, identifier: impl ToString, value: ForgeValue, ) -> RuntimeResult<()> { let entry = self.scope.data.entry(identifier.to_string()); match entry { Entry::Vacant(e) => { e.insert(value); Ok(()) } Entry::Occupied(_) => Err(RuntimeError::VariableAlreadyDefined(identifier.to_string())), } } fn process_assignment(&mut self, assignment: &Assignment) -> RuntimeResult<ForgeValue> { let value = self.evaluate_value_expression(assignment.value.as_ref())?; self.set_variable(&assignment.ident, value.clone())?; Ok(value) } fn evaluate_expression_list( &mut self, list: &ExpressionList, ) -> Result<ForgeValue, RuntimeError> { let mut last_val = Ok(ForgeValue::Null); for expr in &list.expressions { last_val = Ok(self.evaluate_expression(expr)?); } if list.is_void { Ok(ForgeValue::Null) } else { last_val } } } impl Visitor for SimpleExecutor { type Output = ForgeValue; type Error = RuntimeError; fn evaluate_value_expression( &mut self, expression: &ValueExpression, ) -> Result<Self::Output, Self::Error> { match expression { ValueExpression::Unary { operator, operand } => { let value = self.evaluate_value_expression(operand.as_ref())?; match operator { UnaryOp::Negate => Ok(value.invert()), UnaryOp::Not => Ok(!value), } } ValueExpression::Binary { operator, lhs, rhs } => { let lhs = self.evaluate_value_expression(lhs.as_ref())?; let rhs = self.evaluate_value_expression(rhs.as_ref())?; match operator { BinaryOp::Add => Ok(lhs + rhs), BinaryOp::Subtract => Ok((lhs - rhs)?), BinaryOp::Divide => Ok((lhs / rhs)?), BinaryOp::Multiply => Ok((lhs * rhs)?), BinaryOp::Modulo => Ok((lhs % rhs)?), BinaryOp::Equals => Ok((lhs == rhs).into()), } } ValueExpression::Grouped(group) => self.evaluate_value_expression(group.inner.as_ref()), ValueExpression::Block(block) => self.evaluate_expression_list(block), ValueExpression::Literal(lit) => Ok(ForgeValue::from(lit.clone())), ValueExpression::DeclareIdentifier(decl) => match decl { DeclareIdent::WithValue(assignment) => { self.define_variable(&assignment.ident, ForgeValue::Null)?; self.process_assignment(assignment) } DeclareIdent::WithoutValue(identifier) => { self.define_variable(identifier, ForgeValue::Null)?; Ok(ForgeValue::Null) } }, ValueExpression::Assignment(assign) => self.process_assignment(assign), ValueExpression::ConditionalBlock(condition) => { let mut has_found = false; let mut last_value = ForgeValue::Null; for block in condition.blocks.iter() { let guard_val = self.evaluate_value_expression(block.guard.as_ref())?; if guard_val.as_bool() { has_found = true; last_value = self.evaluate_expression_list(&block.block)?; break; } } if !has_found { if let Some(value) = &condition.fallback { last_value = self.evaluate_expression_list(value)?; } } Ok(last_value) } ValueExpression::Identifier(ident) => self .get_variable(ident) .cloned() .ok_or_else(|| RuntimeError::UndefinedVariable(ident.to_string())), ValueExpression::FunctionCall(_) => Err(RuntimeError::Unsupported("FunctionCall")), ValueExpression::DeclareFunction(_) => { Err(RuntimeError::Unsupported("DeclareFunction")) } } } fn evaluate_void_expression( &mut self, expression: &VoidExpression, ) -> Result<Self::Output, Self::Error> { match expression { VoidExpression::ConditionLoop(_) => { return Err(RuntimeError::Unsupported("ConditionLoop")) } VoidExpression::Import(_) => return Err(RuntimeError::Unsupported("Import")), VoidExpression::Export(_) => return Err(RuntimeError::Unsupported("Export")), VoidExpression::Print(Print { expr }) => { let value = self.evaluate_value_expression(expr.as_ref())?; println!("{}", value); } } Ok(ForgeValue::Null) } fn evaluate_program(&mut self, program: &Program) -> Result<Self::Output, Self::Error> { self.evaluate_expression_list(&program.0) } } #[cfg(test)] mod interpreter_test { use crate::parse::parse_program; use crate::runtime::executor::simple::{RuntimeError, SimpleExecutor}; use crate::runtime::executor::Visitor; use crate::runtime::value::ForgeValue; #[test] fn the_basics() { let add_numbers = parse_program("1 + 1").expect("Failed to parse"); let add_strings = parse_program("\"foo\" + \" \" + \"bar\"").expect("Failed to parse"); let concat_string_num = parse_program("\"#\" + 1").expect("Failed to parse"); let bad_mult = parse_program("false * 123").expect("Failed to parse"); let mut vm = SimpleExecutor::default(); assert_eq!(vm.evaluate_program(&add_numbers), Ok(ForgeValue::from(2))); assert_eq!( vm.evaluate_program(&add_strings), Ok(ForgeValue::from("foo bar")) ); assert_eq!( vm.evaluate_program(&concat_string_num), Ok(ForgeValue::from("#1")) ); assert!(matches!( vm.evaluate_program(&bad_mult), Err(RuntimeError::BadOperands(_)) )) } #[test] fn combos() { let add_numbers = parse_program("1 + -1").expect("Failed to parse"); let mut vm = SimpleExecutor::default(); assert_eq!(vm.evaluate_program(&add_numbers), Ok(ForgeValue::from(0))); } #[test] fn print() { let print_4 = parse_program("print 2 + 2").expect("Failed to parse"); let mut vm = SimpleExecutor::default(); assert_eq!(vm.evaluate_program(&print_4), Ok(ForgeValue::Null)); } #[test] fn define_variables() { let simple_define = parse_program("let foo").expect("Failed to parse"); let assignment = parse_program("let bar = 123").expect("Failed to parse"); let redecl_err = parse_program("let foo = 123").expect("Failed to parse"); let missing_err = parse_program("print missing").expect("Failed to parse"); let use_vars = parse_program("let splop = bar * 2").expect("Failed to parse"); let mut vm = SimpleExecutor::default(); assert_eq!(vm.evaluate_program(&simple_define), Ok(ForgeValue::Null)); assert_eq!(vm.evaluate_program(&assignment), Ok(ForgeValue::from(123))); assert_eq!( vm.evaluate_program(&redecl_err), Err(RuntimeError::VariableAlreadyDefined(String::from("foo"))) ); assert_eq!( vm.evaluate_program(&missing_err), Err(RuntimeError::UndefinedVariable(String::from("missing"))) ); assert_eq!(vm.evaluate_program(&use_vars), Ok(ForgeValue::from(246))); assert_eq!(vm.get_variable("splop"), Some(&ForgeValue::from(246))); } }