Skip to content
Snippets Groups Projects
simple.rs 5.17 KiB
Newer Older
Louis's avatar
Louis committed
	BinaryOp, DeclareFunction, ExpressionList, Print, Program, UnaryOp, ValueExpression,
	VoidExpression,
};
use crate::runtime::executor::Visitor;
use crate::runtime::value::{ForgeValue, UnsupportedOperation};
use std::collections::HashMap;
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};

#[derive(Clone, Default)]
pub struct SimpleExecutor {
	data: HashMap<String, ForgeValue>,
	vtable: HashMap<String, DeclareFunction>,
}

#[derive(Clone, Debug, PartialEq)]
pub enum RuntimeError {
	Unsupported(&'static str),
	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),
		}
	}
}

impl Error for RuntimeError {}

impl SimpleExecutor {
	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(_) => Err(RuntimeError::Unsupported("Block")),
			ValueExpression::Literal(lit) => Ok(ForgeValue::from(lit.clone())),
			ValueExpression::DeclareIdentifier(_) => {
				Err(RuntimeError::Unsupported("DeclareIdentifier"))
			}
			ValueExpression::Assignment(_) => Err(RuntimeError::Unsupported("Assignment")),
			ValueExpression::ConditionalBlock(_) => {
				Err(RuntimeError::Unsupported("ConditionalBlock"))
			}
			ValueExpression::Identifier(_) => Err(RuntimeError::Unsupported("Identifier")),
			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> {
Louis's avatar
Louis committed
		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)));
	}
Louis's avatar
Louis committed

	#[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));
	}