// Mini Interpreter - Stress tests complex ADTs, recursion, and pattern matching // // This demonstrates: // - Abstract syntax trees // - Recursive evaluation // - Pattern matching on complex types // - Error handling // Value types in our mini language type Value = | VInt(Int) | VBool(Bool) | VString(String) | VUnit // Expression AST type Expr = | EInt(Int) | EBool(Bool) | EString(String) | EVar(String) | EAdd(Expr, Expr) | ESub(Expr, Expr) | EMul(Expr, Expr) | EDiv(Expr, Expr) | EEq(Expr, Expr) | ELt(Expr, Expr) | EGt(Expr, Expr) | EIf(Expr, Expr, Expr) | ELet(String, Expr, Expr) // Environment (variable bindings) type Env = | EmptyEnv | ExtendEnv(String, Value, Env) // Environment operations fn envLookup(env: Env, name: String): Option = match env { EmptyEnv => None, ExtendEnv(n, v, rest) => if n == name then Some(v) else envLookup(rest, name) } fn envExtend(env: Env, name: String, value: Value): Env = ExtendEnv(name, value, env) // Value pretty printing fn valueToString(v: Value): String = match v { VInt(n) => toString(n), VBool(b) => if b then "true" else "false", VString(s) => "\"" + s + "\"", VUnit => "()" } // Main evaluator fn eval(expr: Expr, env: Env): Result = match expr { EInt(n) => Ok(VInt(n)), EBool(b) => Ok(VBool(b)), EString(s) => Ok(VString(s)), EVar(name) => match envLookup(env, name) { None => Err("Unbound variable: " + name), Some(v) => Ok(v) }, EAdd(left, right) => evalBinOp(left, right, env, fn(a: Int, b: Int): Int => a + b), ESub(left, right) => evalBinOp(left, right, env, fn(a: Int, b: Int): Int => a - b), EMul(left, right) => evalBinOp(left, right, env, fn(a: Int, b: Int): Int => a * b), EDiv(left, right) => evalDiv(left, right, env), EEq(left, right) => evalCompare(left, right, env, fn(a: Int, b: Int): Bool => a == b), ELt(left, right) => evalCompare(left, right, env, fn(a: Int, b: Int): Bool => a < b), EGt(left, right) => evalCompare(left, right, env, fn(a: Int, b: Int): Bool => a > b), EIf(cond, thenBranch, elseBranch) => evalIf(cond, thenBranch, elseBranch, env), ELet(name, valueExpr, bodyExpr) => evalLet(name, valueExpr, bodyExpr, env) } fn evalBinOp(left: Expr, right: Expr, env: Env, op: fn(Int, Int): Int): Result = match eval(left, env) { Err(e) => Err(e), Ok(leftVal) => match eval(right, env) { Err(e) => Err(e), Ok(rightVal) => match (leftVal, rightVal) { (VInt(a), VInt(b)) => Ok(VInt(op(a, b))), (_, _) => Err("Type error: expected Int") } } } fn evalDiv(left: Expr, right: Expr, env: Env): Result = match eval(left, env) { Err(e) => Err(e), Ok(leftVal) => match eval(right, env) { Err(e) => Err(e), Ok(rightVal) => match (leftVal, rightVal) { (VInt(a), VInt(b)) => if b == 0 then Err("Division by zero") else Ok(VInt(a / b)), (_, _) => Err("Type error: expected Int") } } } fn evalCompare(left: Expr, right: Expr, env: Env, op: fn(Int, Int): Bool): Result = match eval(left, env) { Err(e) => Err(e), Ok(leftVal) => match eval(right, env) { Err(e) => Err(e), Ok(rightVal) => match (leftVal, rightVal) { (VInt(a), VInt(b)) => Ok(VBool(op(a, b))), (_, _) => Err("Type error: expected Int") } } } fn evalIf(cond: Expr, thenBranch: Expr, elseBranch: Expr, env: Env): Result = match eval(cond, env) { Err(e) => Err(e), Ok(condVal) => match condVal { VBool(true) => eval(thenBranch, env), VBool(false) => eval(elseBranch, env), _ => Err("Condition must be boolean") } } fn evalLet(name: String, valueExpr: Expr, bodyExpr: Expr, env: Env): Result = match eval(valueExpr, env) { Err(e) => Err(e), Ok(value) => eval(bodyExpr, envExtend(env, name, value)) } // Run a program and print result fn runProgram(expr: Expr): Unit with {Console} = match eval(expr, EmptyEnv) { Ok(value) => Console.print("=> " + valueToString(value)), Err(e) => Console.print("Error: " + e) } // Test programs fn program1(): Expr = ELet("x", EInt(10), ELet("y", EInt(20), EAdd(EVar("x"), EVar("y")))) fn program2(): Expr = ELet("a", EInt(5), EMul(EVar("a"), EAdd(EVar("a"), EInt(1)))) fn program3(): Expr = EIf(EGt(EInt(10), EInt(5)), EString("yes"), EString("no")) fn program4(): Expr = ELet("n", EInt(5), ELet("a", EMul(EVar("n"), ESub(EVar("n"), EInt(1))), ELet("b", EMul(EVar("a"), ESub(EVar("n"), EInt(2))), ELet("c", EMul(EVar("b"), ESub(EVar("n"), EInt(3))), EMul(EVar("c"), ESub(EVar("n"), EInt(4))))))) fn program5(): Expr = EDiv(EInt(10), EInt(0)) fn program6(): Expr = EVar("undefined") // Main fn main(): Unit with {Console} = { Console.print("========================================") Console.print(" MINI INTERPRETER DEMO") Console.print("========================================") Console.print("") Console.print("Program 1: let x = 10 in let y = 20 in x + y") runProgram(program1()) Console.print("") Console.print("Program 2: let a = 5 in a * (a + 1)") runProgram(program2()) Console.print("") Console.print("Program 3: if 10 > 5 then \"yes\" else \"no\"") runProgram(program3()) Console.print("") Console.print("Program 4: 5! computed iteratively") runProgram(program4()) Console.print("") Console.print("Program 5: Division by zero error") runProgram(program5()) Console.print("") Console.print("Program 6: Unbound variable error") runProgram(program6()) } let output = run main() with {}