use std::{collections::HashMap, ops, rc::Rc};
use crate::{error::RuntimeError, parser::expression::{BinaryOp, Expression::{self, *}, SpannedExpr, SpannedStatement, Statement::{self, *}}};
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum Value {
Number(f64),
String(String),
Boolean(bool),
None,
}
pub struct Interpreter {
pub variables: Vec<HashMap<String, Value>>,
pub functions: Vec<HashMap<String, Function>>,
program: Vec<Rc<SpannedStatement>>,
pos: usize,
}
#[derive(Debug, Clone)]
pub struct Function {
params: Rc<[String]>,
body: Rc<[SpannedStatement]>,
}
enum ControlFlow {
None,
Break,
Return(Value),
}
impl Interpreter {
pub fn new(program: Vec<SpannedStatement>) -> Interpreter {
let mut interpreter = Interpreter {
variables: Vec::new(),
functions: Vec::new(),
program: program.into_iter().map(Rc::new).collect(),
pos: 0,
};
interpreter.push_scope();
interpreter
}
pub fn run(&mut self) -> Result<(), RuntimeError> {
while let Some(s) = self.current() {
self.eval_statement(&s)?;
self.advance();
}
Ok(())
}
fn eval_statement(&mut self, statement: &SpannedStatement) -> Result<ControlFlow, RuntimeError> {
match &statement.statement {
VarDef { name, value } => {
let evaluated = self.eval_expression(&value)?;
self.insert_variable(name, evaluated);
},
Print(value) => {
println!("{}", self.eval_expression(&value)?.to_string())
},
Statement::BinaryOp { operation, variable, value } => {
self.eval_binary_op(operation, variable, &value)?;
},
FnDef {name, params, body} => {
self.define_function(name, ¶ms, &body);
},
Return(e) => {
return Ok(ControlFlow::Return(self.eval_expression(e)?));
},
If { condition, body, else_ } => {
self.eval_if(condition, body, else_)?;
},
While { condition, body } => {
self.eval_while(condition, body)?;
},
Break => return Ok(ControlFlow::Break),
Expr(e) => { self.eval_expression(e)?; }
}
Ok(ControlFlow::None)
}
fn eval_expression(&mut self, expr: &SpannedExpr) -> Result<Value, RuntimeError> {
match &expr.expr {
FnCall {name, args} => {
self.eval_fncall(name, args, &expr.span)
},
Expression::Number(n) => Ok(Value::Number(*n)),
Expression::Str(s) => Ok(Value::String(s.clone())),
Expression::Boolean(b) => Ok(Value::Boolean(*b)),
Expression::Identifier(s) => {
let value = self.get_variable(s.clone());
match value {
Some(v) => Ok(v.clone()),
None => panic!("unknown variable"),
}
},
Expression::Weigh {left, right} => {
let left_value = self.eval_expression(left)?;
let right_value = self.eval_expression(right)?;
return Ok(Value::Boolean(left_value >= right_value));
},
}
}
fn eval_while(&mut self, condition: &Box<SpannedExpr>, body: &Vec<SpannedStatement>) -> Result<(), RuntimeError> {
'outer: while matches!(self.eval_expression(condition)?, Value::Boolean(true)) {
self.push_scope();
for e in body {
if matches!(self.eval_statement(e)?, ControlFlow::Break) {
break 'outer;
}
}
self.pop_scope();
}
Ok(())
}
fn eval_if(&mut self, condition: &Box<SpannedExpr>, body: &Vec<SpannedStatement>, else_: &Option<Vec<SpannedStatement>>) -> Result<(), RuntimeError> {
let cond_val = self.eval_expression(condition)?;
if matches!(cond_val, Value::Boolean(true)) {
self.push_scope();
for stat in body {
self.eval_statement(stat)?;
}
self.pop_scope();
} else {
if let Some(v) = else_ {
self.push_scope();
for stat in v {
self.eval_statement(stat)?;
}
self.pop_scope();
}
}
Ok(())
}
fn eval_fncall(&mut self, name: &String, args: &Vec<SpannedExpr>, span: &ops::Range<usize>) -> Result<Value, RuntimeError> {
let function = self.get_function(name.to_string()).cloned();
if let Some(func) = function {
self.push_scope();
for (param, value) in func.params.iter().zip(args.iter()) {
let evaluated = self.eval_expression(value)?;
self.insert_variable(param, evaluated);
}
let mut return_value = Value::None;
for stat in func.body.iter() {
if let ControlFlow::Return(v) = self.eval_statement(stat)? {
return_value = v;
break;
}
}
self.pop_scope();
Ok(return_value)
} else {
throw("", span)
}
}
fn push_scope(&mut self) {
self.variables.push(HashMap::new());
self.functions.push(HashMap::new());
}
fn pop_scope(&mut self) {
self.variables.pop();
self.functions.pop();
}
fn define_function(&mut self, name: &String, params: &Vec<String>, body: &Vec<SpannedStatement>) {
let function = Function {
params: Rc::from(params.as_slice()),
body: Rc::from(body.as_slice()),
};
self.insert_function(name, function);
}
fn get_variable(&self, name: String) -> Option<&Value> {
for hm in self.variables.iter().rev() {
if let Some(v) = hm.get(&name) {
return Some(v);
}
}
None
}
fn get_variable_mut(&mut self, name: &String) -> Option<&mut Value> {
for hm in self.variables.iter_mut().rev() {
if let Some(v) = hm.get_mut(name) {
return Some(v);
}
}
None
}
fn get_function(&mut self, name: String) -> Option<&Function> {
for hm in self.functions.iter().rev() {
if let Some(v) = hm.get(&name) {
return Some(v)
}
}
None
}
fn insert_variable(&mut self, name: &String, value: Value) {
match self.variables.last_mut() {
Some(h) => h,
None => unreachable!(),
}.insert(name.to_string(), value);
}
fn insert_function(&mut self, name: &String, function: Function) {
self.functions.last_mut().unwrap().insert(name.to_string(), function);
}
fn eval_binary_op(&mut self, operation: &BinaryOp, variable: &String, value: &SpannedExpr) -> Result<(), RuntimeError> {
let evaluated = self.eval_expression(value)?;
let var = self.get_variable_mut(variable).unwrap();
match operation {
BinaryOp::Add => {
match var {
Value::Number(n1) => match evaluated {
Value::Number(n2) => *n1 += n2,
Value::String(s) => *n1 += s.parse::<f64>().expect("failed to add string to number"),
_ => panic!("incompatible types"),
},
Value::String(s1) => match evaluated {
Value::Number(n) => s1.push_str(n.to_string().as_str()),
Value::String(s2) => s1.push_str(s2.as_str()),
_ => panic!("incompatible types"),
},
_ => panic!("incompatible types"),
}
},
BinaryOp::Sub => {
match var {
Value::Number(n1) => match evaluated {
Value::Number(n2) => *n1 -= n2,
Value::String(s) => *n1 -= s.parse::<f64>().expect("failed to add string to number"),
_ => panic!("incompatible types"),
},
_ => panic!("incompatible types"),
}
},
BinaryOp::Mul => {
match var {
Value::Number(n1) => match evaluated {
Value::Number(n2) => *n1 *= n2,
Value::String(s) => *n1 *= s.parse::<f64>().expect("failed to add string to number"),
_ => panic!("incompatible types"),
},
Value::String(s) => match evaluated {
Value::Number(n) => *s = s.repeat(n as usize),
_ => panic!("incompatible types"),
},
_ => panic!("incompatible types"),
}
},
BinaryOp::Div => {
match var {
Value::Number(n1) => match evaluated {
Value::Number(n2) => *n1 /= n2,
Value::String(s) => *n1 /= s.parse::<f64>().expect("failed to add string to number"),
_ => panic!("incompatible types"),
},
_ => panic!("incompatible types"),
}
}
}
Ok(())
}
fn current(&self) -> Option<Rc<SpannedStatement>> {
self.program.get(self.pos).cloned()
}
fn advance(&mut self) {
self.pos += 1;
}
}
fn throw<T>(msg: &str, span: &ops::Range<usize>) -> Result<T, RuntimeError> {
Err(RuntimeError {
desc: msg.to_string(),
span: span.clone(),
})
}
impl ToString for Value {
fn to_string(&self) -> String {
match self {
Value::Number(n) => n.to_string(),
Value::String(s) => s.clone(),
Value::Boolean(b) => match b {
true => String::from("big"),
false => String::from("small"),
},
Value::None => "nil".to_string()
}
}
}