2024-08-26 01:58:03 +02:00
|
|
|
open Environment
|
2024-08-17 13:32:59 +02:00
|
|
|
open Error
|
2024-08-26 01:58:03 +02:00
|
|
|
open Expr
|
2024-08-25 02:12:51 +02:00
|
|
|
open Stmt
|
2024-08-17 13:32:59 +02:00
|
|
|
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
|
|
|
|
|
|
2024-08-28 22:55:15 +02:00
|
|
|
type 'a interpreter_result =
|
|
|
|
|
| Ok of 'a
|
|
|
|
|
| Break
|
|
|
|
|
| Continue
|
|
|
|
|
| Return of lox_value
|
|
|
|
|
| Error of runtime_error
|
|
|
|
|
|
|
|
|
|
module InterpreterResult = struct
|
|
|
|
|
type 'a t = 'a interpreter_result
|
|
|
|
|
|
|
|
|
|
let ok value = Ok value
|
|
|
|
|
let break () = Break
|
|
|
|
|
let continue () = Continue
|
|
|
|
|
let return (value : lox_value) = Return value
|
|
|
|
|
let error e = Error e
|
|
|
|
|
|
|
|
|
|
let map f result =
|
|
|
|
|
match result with
|
|
|
|
|
| Ok x -> f x |> ok
|
|
|
|
|
| Break -> Break
|
|
|
|
|
| Continue -> Continue
|
|
|
|
|
| Return _ as r -> r
|
|
|
|
|
| Error _ as e -> e
|
|
|
|
|
|
|
|
|
|
let map_error f result =
|
|
|
|
|
match result with
|
|
|
|
|
| Error e -> f e |> error
|
|
|
|
|
| Ok _ as x -> x
|
|
|
|
|
| Break -> Break
|
|
|
|
|
| Continue -> Continue
|
|
|
|
|
| Return _ as r -> r
|
|
|
|
|
|
|
|
|
|
let bind result f =
|
|
|
|
|
match result with
|
|
|
|
|
| Ok value -> f value
|
|
|
|
|
| Break -> Break
|
|
|
|
|
| Continue -> Continue
|
|
|
|
|
| Return _ as r -> r
|
|
|
|
|
| Error _ as e -> e
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
open InterpreterResult
|
|
|
|
|
|
|
|
|
|
let ( let* ) = InterpreterResult.bind
|
|
|
|
|
|
|
|
|
|
type expr_result = lox_value interpreter_result
|
|
|
|
|
type stmt_result = unit interpreter_result
|
|
|
|
|
|
|
|
|
|
let rec interpret_expr (env : environment) (expr_node : expr_node) : lox_value interpreter_result =
|
2024-08-27 01:57:47 +02:00
|
|
|
let { pos; expr } = expr_node in
|
2024-08-25 02:12:51 +02:00
|
|
|
match expr with
|
2024-08-28 22:55:15 +02:00
|
|
|
| Literal literal -> value_of_literal literal |> ok
|
2024-08-26 01:58:03 +02:00
|
|
|
| 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
|
2024-08-28 22:55:15 +02:00
|
|
|
RuntimeError.make pos msg |> error)
|
2024-08-26 01:58:03 +02:00
|
|
|
| Assignment { name; expr } ->
|
|
|
|
|
if not (Env.is_defined env name) then
|
|
|
|
|
let msg = Printf.sprintf "tried to assign to undefined variable %s" name in
|
2024-08-28 22:55:15 +02:00
|
|
|
RuntimeError.make pos msg |> error
|
2024-08-26 01:58:03 +02:00
|
|
|
else
|
|
|
|
|
let* value = interpret_expr env expr in
|
|
|
|
|
Env.update env name value;
|
|
|
|
|
Ok value
|
2024-08-26 17:26:59 +02:00
|
|
|
| Unary { op; expr } -> (
|
|
|
|
|
let* expr = interpret_expr env expr in
|
|
|
|
|
match (op, expr) with
|
2024-08-28 22:55:15 +02:00
|
|
|
| Neg, Number x -> Number (-.x) |> ok
|
|
|
|
|
| Not, value -> Bool (lox_value_to_bool value |> not) |> ok
|
2024-08-26 17:26:59 +02:00
|
|
|
| _, _ ->
|
|
|
|
|
let msg =
|
|
|
|
|
Printf.sprintf "Invalid operant of type %s to operator %s"
|
|
|
|
|
(type_string_of_lox_value expr) (show_unary_op op)
|
|
|
|
|
in
|
2024-08-28 22:55:15 +02:00
|
|
|
RuntimeError.make pos msg |> error)
|
2024-08-26 17:26:59 +02:00
|
|
|
| Binary { op; left; right } -> (
|
2024-08-26 01:58:03 +02:00
|
|
|
let* left = interpret_expr env left in
|
|
|
|
|
let* right = interpret_expr env right in
|
2024-08-17 13:32:59 +02:00
|
|
|
match (left, op, right) with
|
2024-08-28 22:55:15 +02:00
|
|
|
| String a, Plus, String b -> String (a ^ b) |> ok
|
|
|
|
|
| Number x, Plus, Number y -> Number (x +. y) |> ok
|
|
|
|
|
| Number x, Minus, Number y -> Number (x -. y) |> ok
|
|
|
|
|
| Number x, Mul, Number y -> Number (x *. y) |> ok
|
2024-08-25 02:12:51 +02:00
|
|
|
| Number x, Div, Number y ->
|
2024-08-28 22:55:15 +02:00
|
|
|
if y <> 0. then Number (x /. y) |> ok
|
2024-08-25 02:12:51 +02:00
|
|
|
else
|
|
|
|
|
let msg = "Division by 0" in
|
2024-08-28 22:55:15 +02:00
|
|
|
RuntimeError.make pos msg |> error
|
|
|
|
|
| Bool b, And, Bool c -> Bool (b && c) |> ok
|
|
|
|
|
| Bool b, Or, Bool c -> Bool (b || c) |> ok
|
2024-08-25 02:12:51 +02:00
|
|
|
| _, Equal, _ -> Ok (Bool (left = right))
|
2024-08-28 22:55:15 +02:00
|
|
|
| Number x, Greater, Number y -> Bool (x > y) |> ok
|
|
|
|
|
| Number x, GreaterEqual, Number y -> Bool (x >= y) |> ok
|
|
|
|
|
| Number x, Less, Number y -> Bool (x < y) |> ok
|
|
|
|
|
| Number x, LessEqual, Number y -> Bool (x <= y) |> ok
|
|
|
|
|
| String a, Greater, String b -> Bool (a > b) |> ok
|
|
|
|
|
| String a, GreaterEqual, String b -> Bool (a >= b) |> ok
|
|
|
|
|
| String a, Less, String b -> Bool (a < b) |> ok
|
|
|
|
|
| String a, LessEqual, String b -> Bool (a <= b) |> ok
|
2024-08-17 13:32:59 +02:00
|
|
|
| _, _, _ ->
|
|
|
|
|
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
|
2024-08-28 22:55:15 +02:00
|
|
|
RuntimeError.make pos msg |> error)
|
2024-08-26 17:26:59 +02:00
|
|
|
| Logical { op; left; right } -> (
|
|
|
|
|
let* left = interpret_expr env left in
|
|
|
|
|
match (op, lox_value_to_bool left) with
|
|
|
|
|
| And, false | Or, true -> Ok left (* short circuit *)
|
|
|
|
|
| _ -> interpret_expr env right)
|
2024-08-27 20:32:05 +02:00
|
|
|
| Call { callee; args } -> (
|
|
|
|
|
let* callee = interpret_expr env callee in
|
2024-08-28 22:55:15 +02:00
|
|
|
let f (acc : lox_value list interpreter_result) arg =
|
|
|
|
|
let* acc = acc in
|
|
|
|
|
let* arg = interpret_expr env arg in
|
|
|
|
|
Ok (arg :: acc)
|
2024-08-27 20:32:05 +02:00
|
|
|
in
|
|
|
|
|
let* args = List.fold_left f (Ok []) args in
|
|
|
|
|
let args = List.rev args in
|
|
|
|
|
match callee with
|
2024-08-28 22:55:15 +02:00
|
|
|
| NativeFunction { name; arity; fn } -> (
|
2024-08-28 17:29:36 +02:00
|
|
|
let args_len = List.length args in
|
|
|
|
|
if args_len <> arity then
|
|
|
|
|
let msg =
|
|
|
|
|
Printf.sprintf "Native Function %s has arity %d, but was called with %d args" name
|
|
|
|
|
args_len arity
|
|
|
|
|
in
|
2024-08-28 22:55:15 +02:00
|
|
|
RuntimeError.make pos msg |> error
|
|
|
|
|
else
|
|
|
|
|
match fn args with Ok value -> Ok value | Error s -> RuntimeError.make pos s |> error)
|
|
|
|
|
| Function { name; arity; arg_names; body } -> (
|
2024-08-28 17:29:36 +02:00
|
|
|
let args_len = List.length args in
|
|
|
|
|
if args_len <> arity then
|
|
|
|
|
let msg =
|
|
|
|
|
Printf.sprintf "Function %s has arity %d, but was called with %d args" name args_len
|
|
|
|
|
arity
|
|
|
|
|
in
|
2024-08-28 22:55:15 +02:00
|
|
|
RuntimeError.make pos msg |> error
|
2024-08-28 17:29:36 +02:00
|
|
|
else
|
|
|
|
|
let env = Env.push_frame env in
|
|
|
|
|
let () =
|
|
|
|
|
List.iter2 (fun name value -> assert (Env.define env name value)) arg_names args
|
|
|
|
|
in
|
2024-08-28 22:55:15 +02:00
|
|
|
let result = interpret_stmt env body in
|
|
|
|
|
match result with
|
|
|
|
|
| Ok () -> Ok Nil
|
|
|
|
|
| Return value -> Ok value
|
|
|
|
|
| Error _ as e -> e
|
|
|
|
|
| _ -> assert false)
|
2024-08-27 20:32:05 +02:00
|
|
|
| _ ->
|
2024-08-28 00:12:58 +02:00
|
|
|
ignore args;
|
2024-08-27 20:32:05 +02:00
|
|
|
let msg = Printf.sprintf "%s object is not callable" (type_string_of_lox_value callee) in
|
2024-08-28 22:55:15 +02:00
|
|
|
RuntimeError.make pos msg |> error)
|
2024-08-25 02:12:51 +02:00
|
|
|
|
2024-08-28 22:55:15 +02:00
|
|
|
and interpret_stmt (env : environment) (stmt_node : stmt_node) : unit interpreter_result =
|
|
|
|
|
let { stmt; pos } = stmt_node in
|
2024-08-25 02:12:51 +02:00
|
|
|
match stmt with
|
2024-08-26 01:58:03 +02:00
|
|
|
| Expr expr ->
|
2024-08-27 20:32:05 +02:00
|
|
|
let* value = interpret_expr env expr in
|
|
|
|
|
ignore value;
|
2024-08-26 01:58:03 +02:00
|
|
|
Ok ()
|
2024-08-28 22:55:15 +02:00
|
|
|
| Break -> break ()
|
|
|
|
|
| Continue -> continue ()
|
|
|
|
|
| Return expr ->
|
|
|
|
|
let* value = interpret_expr env expr in
|
|
|
|
|
Return value
|
2024-08-25 02:12:51 +02:00
|
|
|
| Print expr ->
|
2024-08-26 01:58:03 +02:00
|
|
|
let* value = interpret_expr env expr in
|
2024-08-25 02:12:51 +02:00
|
|
|
print_endline (Value.string_of_lox_value value);
|
|
|
|
|
Ok ()
|
2024-08-26 01:58:03 +02:00
|
|
|
| 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
|
2024-08-28 17:29:36 +02:00
|
|
|
let msg =
|
|
|
|
|
Printf.sprintf "Tried to define %s, but a variable of that name was already defined" name
|
|
|
|
|
in
|
2024-08-28 22:55:15 +02:00
|
|
|
RuntimeError.make pos msg |> error
|
2024-08-28 17:29:36 +02:00
|
|
|
| FunDecl { name; arg_names; body } ->
|
|
|
|
|
let fn = make_lox_function name arg_names body in
|
|
|
|
|
let success = Env.define env name fn in
|
|
|
|
|
if success then Ok ()
|
|
|
|
|
else
|
|
|
|
|
let msg =
|
|
|
|
|
Printf.sprintf
|
|
|
|
|
"Tried to define function %s, but a variable of that name was already defined" name
|
|
|
|
|
in
|
2024-08-28 22:55:15 +02:00
|
|
|
RuntimeError.make pos msg |> error
|
2024-08-26 01:58:03 +02:00
|
|
|
| Block stmts ->
|
|
|
|
|
let env = Env.enter env in
|
|
|
|
|
let rec _interpret stmts =
|
|
|
|
|
match stmts with
|
|
|
|
|
| stmt :: tail ->
|
2024-08-27 20:32:05 +02:00
|
|
|
let* () = interpret_stmt env stmt in
|
2024-08-26 01:58:03 +02:00
|
|
|
_interpret tail
|
|
|
|
|
| [] -> Ok ()
|
|
|
|
|
in
|
|
|
|
|
_interpret stmts
|
2024-08-26 17:26:59 +02:00
|
|
|
| If { cond; then_; else_ } ->
|
|
|
|
|
let* cond = interpret_expr env cond in
|
|
|
|
|
let cond = lox_value_to_bool cond in
|
|
|
|
|
if cond then interpret_stmt env then_
|
|
|
|
|
else Option.map (interpret_stmt env) else_ |> Option.value ~default:(Ok ())
|
2024-08-27 01:57:47 +02:00
|
|
|
| While { cond; body } ->
|
|
|
|
|
let* cond = interpret_expr env cond in
|
|
|
|
|
let cond = lox_value_to_bool cond in
|
|
|
|
|
if cond then
|
2024-08-27 02:46:17 +02:00
|
|
|
let result = interpret_stmt env body in
|
|
|
|
|
match result with
|
2024-08-28 22:55:15 +02:00
|
|
|
| Ok () | Continue -> interpret_stmt env stmt_node
|
|
|
|
|
| Break -> Ok ()
|
|
|
|
|
| other -> other
|
2024-08-27 01:57:47 +02:00
|
|
|
else Ok ()
|
2024-08-27 17:15:35 +02:00
|
|
|
| For { init; cond; update; body } ->
|
2024-08-27 18:31:23 +02:00
|
|
|
let env = Env.enter env in
|
2024-08-27 20:32:05 +02:00
|
|
|
let* () = init |> Option.map (interpret_stmt env) |> Option.value ~default:(Ok ()) in
|
2024-08-27 17:15:35 +02:00
|
|
|
let eval_cond () =
|
|
|
|
|
cond
|
|
|
|
|
|> Option.map (interpret_expr env)
|
|
|
|
|
|> Option.value ~default:(Ok (Value.Bool true))
|
2024-08-28 22:55:15 +02:00
|
|
|
|> map Value.lox_value_to_bool
|
2024-08-27 17:15:35 +02:00
|
|
|
in
|
|
|
|
|
let do_update () =
|
|
|
|
|
update |> Option.map (interpret_expr env) |> Option.value ~default:(Ok Value.Nil)
|
|
|
|
|
in
|
|
|
|
|
let rec loop () =
|
|
|
|
|
let* cond = eval_cond () in
|
|
|
|
|
if cond then
|
|
|
|
|
let result = interpret_stmt env body in
|
|
|
|
|
match result with
|
2024-08-28 22:55:15 +02:00
|
|
|
| Ok () | Continue ->
|
2024-08-27 20:32:05 +02:00
|
|
|
let* value = do_update () in
|
|
|
|
|
ignore value;
|
2024-08-27 17:15:35 +02:00
|
|
|
loop ()
|
2024-08-28 22:55:15 +02:00
|
|
|
| Break -> Ok ()
|
|
|
|
|
| other -> other
|
2024-08-27 17:15:35 +02:00
|
|
|
else Ok ()
|
|
|
|
|
in
|
|
|
|
|
loop ()
|