156 lines
4.2 KiB
Rust
156 lines
4.2 KiB
Rust
use crate::lex::{ TeaValue, Operator };
|
|
use crate::parse::{ ASTNode, Value };
|
|
|
|
use std::collections::HashMap;
|
|
|
|
#[derive(Debug)]
|
|
enum VarType {
|
|
Const(TeaValue),
|
|
//Mut,
|
|
}
|
|
|
|
enum BlockType {
|
|
Block,
|
|
Parenthetical,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct InterpreterError {
|
|
description: String,
|
|
}
|
|
|
|
impl InterpreterError {
|
|
pub fn new(e: &str) -> Self {
|
|
Self {
|
|
description: e.to_owned(),
|
|
}
|
|
}
|
|
}
|
|
|
|
macro_rules! some_or_error {
|
|
($matchee:expr, $ret:expr) => {
|
|
match $matchee {
|
|
Some(s) => s,
|
|
_ => return Err($ret),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_value(v: Value, scopes: &mut [Scope]) -> Result<TeaValue, InterpreterError> {
|
|
match v {
|
|
Value::Lit(t) => Ok(t),
|
|
Value::Id(s) => {
|
|
match scopes.iter().rev().find(|scope| scope.contains_key(&s)) {
|
|
None => Err(InterpreterError::new("undefined variable")),
|
|
Some(scope) => match scope.get(&s).unwrap() {
|
|
VarType::Const(t) => Ok(t.clone()),
|
|
},
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
macro_rules! apply_op {
|
|
($a:ident, $b:ident, $op:tt) => {
|
|
match ($a, $b) {
|
|
(TeaValue::Float(aa), TeaValue::Float(bb)) => {
|
|
TeaValue::Float(aa $op bb)
|
|
},
|
|
(TeaValue::Int(aa), TeaValue::Int(bb)) => {
|
|
TeaValue::Int(aa $op bb)
|
|
},
|
|
//(TeaValue::Uint(aa), TeaValue::Uint(bb)) => {
|
|
// TeaValue::Uint(aa $op bb)
|
|
//},
|
|
_ => panic!("Invalid types"),
|
|
}
|
|
}
|
|
}
|
|
|
|
type Scope = HashMap<String, VarType>;
|
|
|
|
fn interpret_block(ast: Vec<ASTNode>, scopes: &mut Vec<Scope>, block_type: BlockType) -> Result<Vec<Value>, InterpreterError> {
|
|
let mut ast = ast.clone();
|
|
ast.reverse();
|
|
let mut stack = vec![];
|
|
//let mut scope = Scope::new();
|
|
loop {
|
|
match ast.pop() {
|
|
None => return Ok(stack),
|
|
Some(ASTNode::Block(tokens)) => {
|
|
scopes.push(HashMap::new());
|
|
let mut result = interpret_block(tokens, scopes, BlockType::Block)?;
|
|
scopes.pop();
|
|
stack.append(&mut result);
|
|
},
|
|
Some(ASTNode::Parenthetical(tokens)) => {
|
|
let mut result = interpret_block(tokens, scopes, BlockType::Parenthetical)?;
|
|
stack.append(&mut result);
|
|
},
|
|
Some(ASTNode::Val(v)) => stack.push(v),
|
|
Some(ASTNode::BinOp(op)) => {
|
|
let b = some_or_error!(stack.pop(), InterpreterError::new("right value needed"));
|
|
let a = some_or_error!(stack.pop(), InterpreterError::new("left value needed"));
|
|
let b: TeaValue = get_value(b, scopes)?;
|
|
let a: TeaValue = get_value(a, scopes)?;
|
|
stack.push(Value::Lit(match op {
|
|
Operator::Plus => {
|
|
apply_op!(a, b, +)
|
|
},
|
|
Operator::Minus => {
|
|
apply_op!(a, b, -)
|
|
},
|
|
Operator::Star => {
|
|
apply_op!(a, b, *)
|
|
},
|
|
Operator::Slash => {
|
|
apply_op!(a, b, /)
|
|
},
|
|
}));
|
|
},
|
|
Some(ASTNode::UnaryMinus) => {
|
|
let x = some_or_error!(stack.pop(), InterpreterError::new("value needed"));
|
|
let x: TeaValue = get_value(x, scopes)?;
|
|
stack.push(Value::Lit(match x {
|
|
TeaValue::Float(f) => TeaValue::Float(-f),
|
|
TeaValue::Int(i) => TeaValue::Int(-i),
|
|
TeaValue::Uint(u) => TeaValue::Int(-(u as i32)),
|
|
TeaValue::String(_) => return Err(InterpreterError::new("cannot invert string")),
|
|
}));
|
|
},
|
|
Some(ASTNode::ConstDecl) => {
|
|
if let BlockType::Parenthetical = block_type {
|
|
return Err(InterpreterError::new("let bindings not allowed in parenthetical"));
|
|
}
|
|
let rhs = some_or_error!(stack.pop(), InterpreterError::new("rhs value needed"));
|
|
let lhs = some_or_error!(stack.pop(), InterpreterError::new("lhs value needed"));
|
|
let rhs: TeaValue = get_value(rhs, scopes)?;
|
|
//let current_scope = .unwrap();
|
|
match lhs {
|
|
Value::Lit(_) => return Err(InterpreterError::new("invalid left hand side")),
|
|
Value::Id(s) => {
|
|
if scopes.last().unwrap().contains_key(&s) {
|
|
return Err(InterpreterError::new("already defined constant"));
|
|
} else {
|
|
scopes.last_mut().unwrap().insert(s, VarType::Const(rhs));
|
|
}
|
|
}
|
|
}
|
|
},
|
|
Some(ASTNode::StatementEnd) => {
|
|
if let BlockType::Parenthetical = block_type {
|
|
return Err(InterpreterError::new("statements not allowed in parenthetical"));
|
|
}
|
|
if !stack.is_empty() {
|
|
return Err(InterpreterError::new("stack not empty"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn interpret(ast: Vec<ASTNode>) -> Result<Vec<Value>, InterpreterError> {
|
|
let mut scopes = vec![HashMap::new()];
|
|
interpret_block(ast, &mut scopes, BlockType::Block)
|
|
}
|