|
| 1 | +use std::collections::HashMap; |
| 2 | +use crate::common::Span; |
| 3 | +use crate::syntax::*; |
| 4 | + |
| 5 | +pub struct Env<'a> { |
| 6 | + parent: Option<&'a Env<'a>>, |
| 7 | + vars: HashMap<&'a str, Value>, |
| 8 | +} |
| 9 | + |
| 10 | +#[derive(Debug, Clone, Copy)] |
| 11 | +pub enum Value { |
| 12 | + Number(f64), |
| 13 | + Bool(bool), |
| 14 | + Unit, |
| 15 | +} |
| 16 | + |
| 17 | +#[derive(Debug)] |
| 18 | +pub enum EvalError { |
| 19 | + InvalidStatement(Span), |
| 20 | + InvalidExpression(Span), |
| 21 | + UnknownVariable(String, Span), |
| 22 | + InvalidType(Value, Value, Span), |
| 23 | +} |
| 24 | + |
| 25 | +pub fn interpret_program<'a>(ast: &'a ParsedAst) -> (HashMap<&'a str, Value>, Vec<EvalError>) { |
| 26 | + let mut env = Env { parent: None, vars: HashMap::new() }; |
| 27 | + let mut errors = Vec::new(); |
| 28 | + |
| 29 | + for stmt in &ast.stmts { |
| 30 | + match eval_stmt(stmt, &mut env) { |
| 31 | + Err(e) => errors.push(e), |
| 32 | + Ok(_) => {}, |
| 33 | + } |
| 34 | + } |
| 35 | + |
| 36 | + (env.vars, errors) |
| 37 | +} |
| 38 | + |
| 39 | +fn eval_stmt<'a>(stmt: &Stmt<'a>, env: &mut Env<'a>) -> Result<(), EvalError> { |
| 40 | + match &stmt.kind { |
| 41 | + StmtKind::Let { name, value } => { |
| 42 | + let v = eval_expr(value, env)?; |
| 43 | + env.vars.insert(*name, v); |
| 44 | + Ok(()) |
| 45 | + } |
| 46 | + StmtKind::Expr(expr) => { |
| 47 | + eval_expr(expr, env)?; |
| 48 | + Ok(()) |
| 49 | + } |
| 50 | + StmtKind::Empty => Ok(()), |
| 51 | + StmtKind::Error(_) => Err(EvalError::InvalidStatement(stmt.span)), |
| 52 | + } |
| 53 | +} |
| 54 | + |
| 55 | +fn eval_expr<'a>(expr: &Expr<'a>, env: &Env<'a>) -> Result<Value, EvalError> { |
| 56 | + match &expr.kind { |
| 57 | + ExprKind::Literal(Literal::Number { value, .. }) => Ok(Value::Number(*value)), |
| 58 | + ExprKind::Literal(Literal::Bool(b)) => Ok(Value::Bool(*b)), |
| 59 | + |
| 60 | + ExprKind::Identifier { name } => { |
| 61 | + let mut current = env; |
| 62 | + loop { |
| 63 | + let found = current.vars.get(name).copied(); |
| 64 | + |
| 65 | + if let Some(val) = found { |
| 66 | + return Ok(val) |
| 67 | + } |
| 68 | + |
| 69 | + match current.parent { |
| 70 | + Some(p) => current = p, |
| 71 | + None => return Err(EvalError::UnknownVariable(name.to_string(), expr.span)), |
| 72 | + } |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + ExprKind::UnaryOp { op, expr: inner_expr } => { |
| 77 | + let v = eval_expr(inner_expr, env)?; |
| 78 | + |
| 79 | + match (op, v) { |
| 80 | + (UnaryOp::Neg, Value::Number(v)) => Ok(Value::Number(-v)), |
| 81 | + _ => return Err(EvalError::InvalidType(v, Value::Number(0.0), expr.span)) |
| 82 | + } |
| 83 | + } |
| 84 | + |
| 85 | + ExprKind::BinaryOp { op, left, right } => { |
| 86 | + let l = eval_expr(left, env)?; |
| 87 | + let r = eval_expr(right, env)?; |
| 88 | + |
| 89 | + match (op, l, r) { |
| 90 | + (BinaryOp::Add, Value::Number(l), Value::Number(r)) => Ok(Value::Number(l + r)), |
| 91 | + (BinaryOp::Sub, Value::Number(l), Value::Number(r)) => Ok(Value::Number(l - r)), |
| 92 | + (BinaryOp::Mul, Value::Number(l), Value::Number(r)) => Ok(Value::Number(l * r)), |
| 93 | + (BinaryOp::Div, Value::Number(l), Value::Number(r)) => Ok(Value::Number(l / r)), |
| 94 | + |
| 95 | + (BinaryOp::Gt, Value::Number(l), Value::Number(r)) => Ok(Value::Bool(l > r)), |
| 96 | + (BinaryOp::Ge, Value::Number(l), Value::Number(r)) => Ok(Value::Bool(l >= r)), |
| 97 | + (BinaryOp::Lt, Value::Number(l), Value::Number(r)) => Ok(Value::Bool(l < r)), |
| 98 | + (BinaryOp::Le, Value::Number(l), Value::Number(r)) => Ok(Value::Bool(l <= r)), |
| 99 | + |
| 100 | + (BinaryOp::Eq, Value::Number(l), Value::Number(r)) => Ok(Value::Bool(l == r)), |
| 101 | + (BinaryOp::Ne, Value::Number(l), Value::Number(r)) => Ok(Value::Bool(l != r)), |
| 102 | + (BinaryOp::Eq, Value::Bool(l), Value::Bool(r)) => Ok(Value::Bool(l != r)), |
| 103 | + (BinaryOp::Ne, Value::Bool(l), Value::Bool(r)) => Ok(Value::Bool(l != r)), |
| 104 | + _ => return Err(EvalError::InvalidType(l, r, expr.span)), |
| 105 | + } |
| 106 | + } |
| 107 | + |
| 108 | + ExprKind::Block { stmts, tail_expr } => { |
| 109 | + let mut inner_env = Env { parent: Some(env), vars: HashMap::new() }; |
| 110 | + |
| 111 | + for stmt in stmts { |
| 112 | + eval_stmt(stmt, &mut inner_env)?; |
| 113 | + } |
| 114 | + |
| 115 | + tail_expr.as_ref().map(|expr| eval_expr(expr, &inner_env)).unwrap_or(Ok(Value::Unit)) |
| 116 | + } |
| 117 | + |
| 118 | + ExprKind::Error(_) => Err(EvalError::InvalidExpression(expr.span)), |
| 119 | + } |
| 120 | +} |
0 commit comments