mirror of
https://github.com/MorizzG/MLox.git
synced 2025-12-06 04:22:41 +00:00
100 lines
3.7 KiB
OCaml
100 lines
3.7 KiB
OCaml
let ( let* ) = Result.bind
|
|
|
|
open Environment
|
|
open Error
|
|
open Expr
|
|
open Stmt
|
|
open Value
|
|
|
|
let value_of_literal (literal : literal) : Value.lox_value =
|
|
match literal with String s -> String s | Number x -> Number x | Bool b -> Bool b | Nil -> Nil
|
|
|
|
let rec interpret_expr (env : environment) (expr : expr_node) : (lox_value, runtime_error) result =
|
|
let { pos; expr } = expr in
|
|
match expr with
|
|
| Literal literal -> Ok (value_of_literal literal)
|
|
| Variable name -> (
|
|
let value_opt = Env.get env name in
|
|
match value_opt with
|
|
| Some x -> Ok x
|
|
| None ->
|
|
let msg = Printf.sprintf "name \"%s\" is not defined" name in
|
|
Error (RuntimeError.make pos msg))
|
|
| Assignment { name; expr } ->
|
|
if not (Env.is_defined env name) then
|
|
let msg = Printf.sprintf "tried to assign to undefined variable %s" name in
|
|
Error (RuntimeError.make pos msg)
|
|
else
|
|
let* value = interpret_expr env expr in
|
|
Env.update env name value;
|
|
Ok value
|
|
| BinaryExpr { op; left; right } -> (
|
|
let* left = interpret_expr env left in
|
|
let* right = interpret_expr env right in
|
|
match (left, op, right) with
|
|
| String a, Plus, String b -> Ok (String (a ^ b))
|
|
| Number x, Plus, Number y -> Ok (Number (x +. y))
|
|
| Number x, Minus, Number y -> Ok (Number (x -. y))
|
|
| Number x, Mul, Number y -> Ok (Number (x *. y))
|
|
| Number x, Div, Number y ->
|
|
if y <> 0. then Ok (Number (x /. y))
|
|
else
|
|
let msg = "Division by 0" in
|
|
Error { pos; msg }
|
|
| Bool b, And, Bool c -> Ok (Bool (b && c))
|
|
| Bool b, Or, Bool c -> Ok (Bool (b || c))
|
|
| _, Equal, _ -> Ok (Bool (left = right))
|
|
| Number x, Greater, Number y -> Ok (Bool (x > y))
|
|
| Number x, GreaterEqual, Number y -> Ok (Bool (x >= y))
|
|
| Number x, Less, Number y -> Ok (Bool (x < y))
|
|
| Number x, LessEqual, Number y -> Ok (Bool (x <= y))
|
|
| String a, Greater, String b -> Ok (Bool (a > b))
|
|
| String a, GreaterEqual, String b -> Ok (Bool (a >= b))
|
|
| String a, Less, String b -> Ok (Bool (a < b))
|
|
| String a, LessEqual, String b -> Ok (Bool (a <= b))
|
|
| _, _, _ ->
|
|
let msg =
|
|
Printf.sprintf "Invalid operands of type %s and %s to operator %s"
|
|
(type_string_of_lox_value left) (type_string_of_lox_value right) (show_binary_op op)
|
|
in
|
|
Error { pos; msg })
|
|
| UnaryExpr { op; expr } -> (
|
|
let* expr = interpret_expr env expr in
|
|
match (op, expr) with
|
|
| Neg, Number x -> Ok (Number (-.x))
|
|
| Not, Bool b -> Ok (Bool (not b))
|
|
| _, _ ->
|
|
let msg =
|
|
Printf.sprintf "Invalid operant of type %s to operator %s"
|
|
(type_string_of_lox_value expr) (show_unary_op op)
|
|
in
|
|
Error (RuntimeError.make pos msg))
|
|
|
|
let rec interpret_stmt (env : environment) (stmt : stmt_node) : (unit, runtime_error) result =
|
|
let { pos; stmt } = stmt in
|
|
ignore pos;
|
|
match stmt with
|
|
| Expr expr ->
|
|
let* _ = interpret_expr env expr in
|
|
Ok ()
|
|
| Print expr ->
|
|
let* value = interpret_expr env expr in
|
|
print_endline (Value.string_of_lox_value value);
|
|
Ok ()
|
|
| VarDecl { name; init } ->
|
|
let* init = Option.map (interpret_expr env) init |> Option.value ~default:(Ok Nil) in
|
|
let success = Env.define env name init in
|
|
if success then Ok ()
|
|
else
|
|
let msg = Printf.sprintf "Tried to define %s, but was already defined" name in
|
|
Error (RuntimeError.make pos msg)
|
|
| Block stmts ->
|
|
let env = Env.enter env in
|
|
let rec _interpret stmts =
|
|
match stmts with
|
|
| stmt :: tail ->
|
|
let* _ = interpret_stmt env stmt in
|
|
_interpret tail
|
|
| [] -> Ok ()
|
|
in
|
|
_interpret stmts
|