This commit is contained in:
kimmy441 2025-12-19 20:47:49 +00:00 committed by GitHub
commit 1382f81bc7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 987 additions and 0 deletions

30
README.md Normal file
View File

@ -0,0 +1,30 @@
# sequence
A stackless bytecode interpreter implemented in OCaml using CPS.
All control flow is expressed through explicit continuations rather than the OCaml call to ensure stack safety through a trampoline
## Continuation Representation
Continuations are represented as a recursive type:
```ocaml
type continuation =
| Halt
| Return_to of continuation
| Apply_to of { func : value; cont : continuation }
| Eval_to of { expr : instruction; cont : continuation }
| Pop_to of { n : int; cont : continuation }
| Exception_to of { handler : continuation; cont : continuation }
```
Each continuation represents "what to do next" after the current computation completes
Exception continuations enable structured error handling
## Building/Running
```bash
dune build
dune exec sequence
```

204
bin/main.ml Normal file
View File

@ -0,0 +1,204 @@
open Sequence
let example_program =
[|
Instr.PUSH (Instr.Int 5);
Instr.PUSH (Instr.Int 3);
Instr.ADD;
Instr.DUP;
Instr.PUSH (Instr.Int 2);
Instr.MUL;
Instr.ADD;
Instr.HALT;
|]
let arithmetic_program =
[|
Instr.PUSH (Instr.Int 10);
Instr.PUSH (Instr.Int 7);
Instr.SUB;
Instr.PUSH (Instr.Int 2);
Instr.MUL;
Instr.HALT;
|]
let conditional_program =
[|
Instr.PUSH (Instr.Int 5);
Instr.PUSH (Instr.Int 3);
Instr.LT;
Instr.JMP_IF 2;
Instr.PUSH (Instr.Int 100);
Instr.JMP 1;
Instr.PUSH (Instr.Int 200);
Instr.HALT;
|]
let comparison_program =
[|
Instr.PUSH (Instr.Int 10);
Instr.PUSH (Instr.Int 5);
Instr.GT;
Instr.PUSH (Instr.Int 5);
Instr.PUSH (Instr.Int 10);
Instr.LE;
Instr.AND;
Instr.HALT;
|]
let division_program =
[|
Instr.PUSH (Instr.Int 20);
Instr.PUSH (Instr.Int 4);
Instr.DIV;
Instr.PUSH (Instr.Int 3);
Instr.MOD;
Instr.HALT;
|]
let stack_ops_program =
[|
Instr.PUSH (Instr.Int 1);
Instr.PUSH (Instr.Int 2);
Instr.PUSH (Instr.Int 3);
Instr.ROT;
Instr.SWAP;
Instr.OVER;
Instr.ADD;
Instr.HALT;
|]
let logical_program =
[|
Instr.PUSH (Instr.Bool true);
Instr.PUSH (Instr.Bool false);
Instr.OR;
Instr.PUSH (Instr.Bool true);
Instr.AND;
Instr.NOT;
Instr.HALT;
|]
let negation_program =
[|
Instr.PUSH (Instr.Int 42);
Instr.NEG;
Instr.PUSH (Instr.Int 10);
Instr.ADD;
Instr.HALT;
|]
let closure_program =
[|
Instr.PUSH (Instr.Int 100);
Instr.MK_CLOSURE 10;
Instr.CALL;
Instr.HALT;
Instr.PUSH (Instr.Int 200);
Instr.RET;
|]
let exception_program =
[|
Instr.TRY 3;
Instr.PUSH (Instr.Int 42);
Instr.PUSH (Instr.Int 0);
Instr.DIV;
Instr.HALT;
Instr.POP;
Instr.PUSH (Instr.Int 999);
Instr.HALT;
|]
let complex_program =
[|
Instr.PUSH (Instr.Int 15);
Instr.PUSH (Instr.Int 3);
Instr.DIV;
Instr.DUP;
Instr.PUSH (Instr.Int 2);
Instr.MUL;
Instr.SWAP;
Instr.ADD;
Instr.PUSH (Instr.Int 10);
Instr.GT;
Instr.JMP_IF 2;
Instr.PUSH (Instr.Int 0);
Instr.JMP 1;
Instr.PUSH (Instr.Int 1);
Instr.HALT;
|]
let run_test name program expected =
Printf.printf "Test: %s\n" name;
match Vm.execute_program program with
| Vm.Success (Some (Instr.Int result)) ->
if result = expected then
Printf.printf " ✓ Pass: %d (expected %d)\n\n" result expected
else
Printf.printf " ✗ Fail: got %d, expected %d\n\n" result expected
| Vm.Success (Some (Instr.Bool result)) ->
Printf.printf " Result: %b\n\n" result
| Vm.Success None ->
Printf.printf " ✗ Fail: No result\n\n"
| Vm.Failure e ->
Printf.printf " ✗ Error: %s\n\n" (State.show_error e)
let run_trace_test name program =
Printf.printf "Trace test: %s\n" name;
let result, trace = Vm.execute_with_trace program in
(match result with
| Vm.Success (Some (Instr.Int n)) ->
Printf.printf " Result: %d\n" n
| Vm.Success (Some (Instr.Bool b)) ->
Printf.printf " Result: %b\n" b
| Vm.Success None ->
Printf.printf " Result: None\n"
| Vm.Failure e ->
Printf.printf " Error: %s\n" (State.show_error e));
Printf.printf " Trace entries: %d\n" (List.length trace);
if List.length trace > 0 then
(let last = List.hd (List.rev trace) in
Printf.printf " Final: pc=%d, stack_len=%d, cont_depth=%d\n"
last.Trampoline.pc last.Trampoline.stack_len last.Trampoline.cont_depth);
Printf.printf "\n"
let () =
run_test "Basic arithmetic" example_program 24;
run_test "Subtraction and multiplication" arithmetic_program 6;
run_test "Conditional branching" conditional_program 200;
run_test "Comparison operations" comparison_program 1;
run_test "Division and modulo" division_program 2;
run_test "Stack operations" stack_ops_program 5;
run_test "Logical operations" logical_program 0;
run_test "Negation" negation_program (-32);
run_test "Complex expression" complex_program 1;
let invalid_jump = [|
Instr.PUSH (Instr.Int 1);
Instr.JMP 1000;
Instr.HALT;
|] in
let errors = Vm.validate invalid_jump in
Printf.printf "Invalid jump validation: %d errors found\n" (List.length errors);
List.iter (fun (pc, err) ->
Printf.printf " PC %d: %s\n" pc (State.show_error err))
errors;
Printf.printf "\n";
let disasm = Vm.disassemble example_program in
List.iter (fun (pc, instr) ->
Printf.printf " %d: %s\n" pc instr)
disasm;
Printf.printf "\n";
run_trace_test "Traced execution" arithmetic_program;
match Vm.execute_program exception_program with
| Vm.Success (Some (Instr.Int n)) ->
Printf.printf "Exception caught, result: %d\n" n
| Vm.Success None ->
Printf.printf "Exception caught, no result\n"
| Vm.Failure e ->
Printf.printf "Exception not caught: %s\n" (State.show_error e);
Printf.printf "\n"

12
dune Normal file
View File

@ -0,0 +1,12 @@
(executable
(public_name sequence)
(name main)
(libraries sequence)
(package sequence))
(library
(name sequence)
(public_name sequence)
(modules instr state cont step trampoline vm))

9
dune-project Normal file
View File

@ -0,0 +1,9 @@
(lang dune 3.0)
(name sequence)
(package
(name sequence)
(depends ocaml))

54
lib/cont.ml Normal file
View File

@ -0,0 +1,54 @@
open Instr
type t = continuation
let halt = Halt
let return_to cont = Return_to cont
let apply_to func cont = Apply_to { func; cont }
let eval_to expr cont = Eval_to { expr; cont }
let pop_to n cont = Pop_to { n; cont }
let exception_to handler cont = Exception_to { handler; cont }
let rec build_cont instructions cont =
match instructions with
| [] -> cont
| instr :: rest ->
eval_to instr (build_cont rest cont)
let apply_cont k v : (t * value option) option =
match k with
| Halt -> None
| Return_to k' -> Some (k', Some v)
| Apply_to { func; cont } ->
(match func with
| Closure { code; env } ->
Some (Eval_to { expr = Instr.LOAD code; cont }, Some v)
| _ -> None)
| Eval_to { expr; cont } ->
Some (cont, Some v)
| Pop_to { n; cont } ->
Some (cont, Some v)
| Exception_to { handler; cont } ->
Some (handler, Some v)
let rec depth = function
| Halt -> 0
| Return_to k -> 1 + depth k
| Apply_to { cont; _ } -> 1 + depth cont
| Eval_to { cont; _ } -> 1 + depth cont
| Pop_to { cont; _ } -> 1 + depth cont
| Exception_to { cont; _ } -> 1 + depth cont
let rec show_cont k =
match k with
| Halt -> "Halt"
| Return_to k' -> Printf.sprintf "Return_to(%s)" (show_cont k')
| Apply_to { func; cont } -> Printf.sprintf "Apply_to(%s, %s)" (show_value func) (show_cont cont)
| Eval_to { expr; cont } -> Printf.sprintf "Eval_to(%s, %s)" (show expr) (show_cont cont)
| Pop_to { n; cont } -> Printf.sprintf "Pop_to(%d, %s)" n (show_cont cont)
| Exception_to { handler; cont } -> Printf.sprintf "Exception_to(%s, %s)" (show_cont handler) (show_cont cont)

113
lib/instr.ml Normal file
View File

@ -0,0 +1,113 @@
type value =
| Int of int
| Closure of { code : int; env : value list }
| Continuation of continuation
| Bool of bool
and continuation =
| Halt
| Return_to of continuation
| Apply_to of { func : value; cont : continuation }
| Eval_to of { expr : t; cont : continuation }
| Pop_to of { n : int; cont : continuation }
| Exception_to of { handler : continuation; cont : continuation }
and t =
| PUSH of value
| POP
| DUP
| SWAP
| OVER
| ROT
| ADD
| SUB
| MUL
| DIV
| MOD
| EQ
| NE
| LT
| LE
| GT
| GE
| AND
| OR
| NOT
| NEG
| JMP of int
| JMP_IF of int
| JMP_IF_NOT of int
| CALL
| RET
| MK_CLOSURE of int
| LOAD of int
| STORE of int
| PUSH_ENV
| POP_ENV
| HALT
| TRY of int
| RAISE
| RERAISE
let show_value = function
| Int n -> Printf.sprintf "Int(%d)" n
| Bool b -> Printf.sprintf "Bool(%b)" b
| Closure { code; env } -> Printf.sprintf "Closure(code=%d, env_len=%d)" code (List.length env)
| Continuation _ -> "Continuation"
let show_continuation = function
| Halt -> "Halt"
| Return_to _ -> "Return_to"
| Apply_to _ -> "Apply_to"
| Eval_to _ -> "Eval_to"
| Pop_to _ -> "Pop_to"
| Exception_to _ -> "Exception_to"
let show = function
| PUSH v -> Printf.sprintf "PUSH %s" (show_value v)
| POP -> "POP"
| DUP -> "DUP"
| SWAP -> "SWAP"
| OVER -> "OVER"
| ROT -> "ROT"
| ADD -> "ADD"
| SUB -> "SUB"
| MUL -> "MUL"
| DIV -> "DIV"
| MOD -> "MOD"
| EQ -> "EQ"
| NE -> "NE"
| LT -> "LT"
| LE -> "LE"
| GT -> "GT"
| GE -> "GE"
| AND -> "AND"
| OR -> "OR"
| NOT -> "NOT"
| NEG -> "NEG"
| JMP offset -> Printf.sprintf "JMP %d" offset
| JMP_IF offset -> Printf.sprintf "JMP_IF %d" offset
| JMP_IF_NOT offset -> Printf.sprintf "JMP_IF_NOT %d" offset
| CALL -> "CALL"
| RET -> "RET"
| MK_CLOSURE code -> Printf.sprintf "MK_CLOSURE %d" code
| LOAD idx -> Printf.sprintf "LOAD %d" idx
| STORE idx -> Printf.sprintf "STORE %d" idx
| PUSH_ENV -> "PUSH_ENV"
| POP_ENV -> "POP_ENV"
| HALT -> "HALT"
| TRY offset -> Printf.sprintf "TRY %d" offset
| RAISE -> "RAISE"
| RERAISE -> "RERAISE"
let int_of_value = function
| Int n -> Some n
| Bool true -> Some 1
| Bool false -> Some 0
| _ -> None
let bool_of_value = function
| Bool b -> Some b
| Int 0 -> Some false
| Int n -> Some (n <> 0)
| _ -> None

79
lib/state.ml Normal file
View File

@ -0,0 +1,79 @@
type error =
| StackUnderflow
| InvalidIndex of int
| TypeMismatch of string
| DivisionByZero
| InvalidJump of int
| InvalidClosure
| NoExceptionHandler
type 'a result = Ok of 'a | Error of error
type t = {
pc : int;
stack : Instr.value list;
code : Instr.t array;
env : Instr.value list;
env_stack : Instr.value list list;
exception_value : Instr.value option;
}
let create code = {
pc = 0;
stack = [];
code;
env = [];
env_stack = [];
exception_value = None;
}
let with_pc state pc = { state with pc }
let with_stack state stack = { state with stack }
let with_env state env = { state with env }
let with_exception state exn = { state with exception_value = Some exn }
let clear_exception state = { state with exception_value = None }
let push state v = { state with stack = v :: state.stack }
let pop state = match state.stack with
| [] -> Error StackUnderflow
| v :: rest -> Ok (v, { state with stack = rest })
let pop_n state n =
if n < 0 then Error (InvalidIndex n)
else if n = 0 then Ok ([], state)
else
let rec aux acc n = function
| [] when n > 0 -> Error StackUnderflow
| rest when n = 0 -> Ok (List.rev acc, { state with stack = rest })
| v :: rest -> aux (v :: acc) (n - 1) rest
in
aux [] n state.stack
let peek state = match state.stack with
| [] -> Error StackUnderflow
| v :: _ -> Ok v
let push_env state =
{ state with env_stack = state.env :: state.env_stack }
let pop_env state = match state.env_stack with
| [] -> Error StackUnderflow
| env :: rest -> Ok ({ state with env = env; env_stack = rest })
let get_env state = state.env
let set_env state env = { state with env }
let show state =
Printf.sprintf "State(pc=%d, stack_len=%d, env_len=%d, env_stack_len=%d)"
state.pc (List.length state.stack) (List.length state.env) (List.length state.env_stack)
let show_error = function
| StackUnderflow -> "StackUnderflow"
| InvalidIndex i -> Printf.sprintf "InvalidIndex(%d)" i
| TypeMismatch s -> Printf.sprintf "TypeMismatch(%s)" s
| DivisionByZero -> "DivisionByZero"
| InvalidJump i -> Printf.sprintf "InvalidJump(%d)" i
| InvalidClosure -> "InvalidClosure"
| NoExceptionHandler -> "NoExceptionHandler"

361
lib/step.ml Normal file
View File

@ -0,0 +1,361 @@
open Instr
type result =
| Continue of { state : State.t; cont : Cont.t }
| Halted of value option
| Error of State.error
let step state cont : result =
if state.pc >= Array.length state.code then
Halted None
else
let instr = state.code.(state.pc) in
let next_pc = state.pc + 1 in
let next_state = State.with_pc state next_pc in
match instr with
| PUSH v ->
let new_state = State.push next_state v in
Continue { state = new_state; cont }
| POP ->
(match State.pop next_state with
| State.Error e -> Error e
| State.Ok (_, new_state) ->
Continue { state = new_state; cont })
| DUP ->
(match State.peek next_state with
| State.Error e -> Error e
| State.Ok v ->
let new_state = State.push next_state v in
Continue { state = new_state; cont })
| SWAP ->
(match State.pop_n next_state 2 with
| State.Error e -> Error e
| State.Ok ([a; b], new_state) ->
let new_state = State.push (State.push new_state a) b in
Continue { state = new_state; cont }
| State.Ok _ -> Error (State.TypeMismatch "SWAP"))
| OVER ->
(match State.pop_n next_state 2 with
| State.Error e -> Error e
| State.Ok ([a; b], new_state) ->
let new_state = State.push (State.push new_state b) a in
let new_state = State.push new_state b in
Continue { state = new_state; cont }
| State.Ok _ -> Error (State.TypeMismatch "OVER"))
| ROT ->
(match State.pop_n next_state 3 with
| State.Error e -> Error e
| State.Ok ([a; b; c], new_state) ->
let new_state = State.push (State.push (State.push new_state a) c) b in
Continue { state = new_state; cont }
| State.Ok _ -> Error (State.TypeMismatch "ROT"))
| ADD ->
(match State.pop_n next_state 2 with
| State.Error e -> Error e
| State.Ok ([Int a; Int b], new_state) ->
let new_state = State.push new_state (Int (a + b)) in
Continue { state = new_state; cont }
| State.Ok _ -> Error (State.TypeMismatch "ADD"))
| SUB ->
(match State.pop_n next_state 2 with
| State.Error e -> Error e
| State.Ok ([Int a; Int b], new_state) ->
let new_state = State.push new_state (Int (a - b)) in
Continue { state = new_state; cont }
| State.Ok _ -> Error (State.TypeMismatch "SUB"))
| MUL ->
(match State.pop_n next_state 2 with
| State.Error e -> Error e
| State.Ok ([Int a; Int b], new_state) ->
let new_state = State.push new_state (Int (a * b)) in
Continue { state = new_state; cont }
| State.Ok _ -> Error (State.TypeMismatch "MUL"))
| DIV ->
(match State.pop_n next_state 2 with
| State.Error e -> Error e
| State.Ok ([Int a; Int 0], _) -> Error State.DivisionByZero
| State.Ok ([Int a; Int b], new_state) ->
let new_state = State.push new_state (Int (a / b)) in
Continue { state = new_state; cont }
| State.Ok _ -> Error (State.TypeMismatch "DIV"))
| MOD ->
(match State.pop_n next_state 2 with
| State.Error e -> Error e
| State.Ok ([Int a; Int 0], _) -> Error State.DivisionByZero
| State.Ok ([Int a; Int b], new_state) ->
let new_state = State.push new_state (Int (a mod b)) in
Continue { state = new_state; cont }
| State.Ok _ -> Error (State.TypeMismatch "MOD"))
| EQ ->
(match State.pop_n next_state 2 with
| State.Error e -> Error e
| State.Ok ([Int a; Int b], new_state) ->
let new_state = State.push new_state (Int (if a = b then 1 else 0)) in
Continue { state = new_state; cont }
| State.Ok ([Bool a; Bool b], new_state) ->
let new_state = State.push new_state (Bool (a = b)) in
Continue { state = new_state; cont }
| State.Ok _ -> Error (State.TypeMismatch "EQ"))
| NE ->
(match State.pop_n next_state 2 with
| State.Error e -> Error e
| State.Ok ([Int a; Int b], new_state) ->
let new_state = State.push new_state (Int (if a <> b then 1 else 0)) in
Continue { state = new_state; cont }
| State.Ok ([Bool a; Bool b], new_state) ->
let new_state = State.push new_state (Bool (a <> b)) in
Continue { state = new_state; cont }
| State.Ok _ -> Error (State.TypeMismatch "NE"))
| LT ->
(match State.pop_n next_state 2 with
| State.Error e -> Error e
| State.Ok ([Int a; Int b], new_state) ->
let new_state = State.push new_state (Int (if a < b then 1 else 0)) in
Continue { state = new_state; cont }
| State.Ok _ -> Error (State.TypeMismatch "LT"))
| LE ->
(match State.pop_n next_state 2 with
| State.Error e -> Error e
| State.Ok ([Int a; Int b], new_state) ->
let new_state = State.push new_state (Int (if a <= b then 1 else 0)) in
Continue { state = new_state; cont }
| State.Ok _ -> Error (State.TypeMismatch "LE"))
| GT ->
(match State.pop_n next_state 2 with
| State.Error e -> Error e
| State.Ok ([Int a; Int b], new_state) ->
let new_state = State.push new_state (Int (if a > b then 1 else 0)) in
Continue { state = new_state; cont }
| State.Ok _ -> Error (State.TypeMismatch "GT"))
| GE ->
(match State.pop_n next_state 2 with
| State.Error e -> Error e
| State.Ok ([Int a; Int b], new_state) ->
let new_state = State.push new_state (Int (if a >= b then 1 else 0)) in
Continue { state = new_state; cont }
| State.Ok _ -> Error (State.TypeMismatch "GE"))
| AND ->
(match State.pop_n next_state 2 with
| State.Error e -> Error e
| State.Ok ([a; b], new_state) ->
(match (bool_of_value a, bool_of_value b) with
| (Some a', Some b') ->
let new_state = State.push new_state (Bool (a' && b')) in
Continue { state = new_state; cont }
| _ -> Error (State.TypeMismatch "AND")))
| OR ->
(match State.pop_n next_state 2 with
| State.Error e -> Error e
| State.Ok ([a; b], new_state) ->
(match (bool_of_value a, bool_of_value b) with
| (Some a', Some b') ->
let new_state = State.push new_state (Bool (a' || b')) in
Continue { state = new_state; cont }
| _ -> Error (State.TypeMismatch "OR")))
| NOT ->
(match State.pop next_state with
| State.Error e -> Error e
| State.Ok (v, new_state) ->
(match bool_of_value v with
| Some b ->
let new_state = State.push new_state (Bool (not b)) in
Continue { state = new_state; cont }
| None -> Error (State.TypeMismatch "NOT")))
| NEG ->
(match State.pop next_state with
| State.Error e -> Error e
| State.Ok (Int n, new_state) ->
let new_state = State.push new_state (Int (-n)) in
Continue { state = new_state; cont }
| State.Ok _ -> Error (State.TypeMismatch "NEG"))
| JMP offset ->
let new_pc = next_state.pc + offset - 1 in
if new_pc < 0 || new_pc >= Array.length state.code then
Error (State.InvalidJump new_pc)
else
let new_state = State.with_pc next_state new_pc in
Continue { state = new_state; cont }
| JMP_IF offset ->
(match State.pop next_state with
| State.Error e -> Error e
| State.Ok (v, new_state) ->
(match bool_of_value v with
| Some true ->
let new_pc = new_state.pc + offset - 1 in
if new_pc < 0 || new_pc >= Array.length state.code then
Error (State.InvalidJump new_pc)
else
let new_state = State.with_pc new_state new_pc in
Continue { state = new_state; cont }
| Some false ->
Continue { state = new_state; cont }
| None -> Error (State.TypeMismatch "JMP_IF")))
| JMP_IF_NOT offset ->
(match State.pop next_state with
| State.Error e -> Error e
| State.Ok (v, new_state) ->
(match bool_of_value v with
| Some false ->
let new_pc = new_state.pc + offset - 1 in
if new_pc < 0 || new_pc >= Array.length state.code then
Error (State.InvalidJump new_pc)
else
let new_state = State.with_pc new_state new_pc in
Continue { state = new_state; cont }
| Some true ->
Continue { state = new_state; cont }
| None -> Error (State.TypeMismatch "JMP_IF_NOT")))
| CALL ->
(match State.pop next_state with
| State.Error e -> Error e
| State.Ok (func, new_state) ->
(match func with
| Closure { code; env } ->
let new_state = State.push_env new_state in
let new_state = State.set_env new_state env in
let new_pc = code in
if new_pc < 0 || new_pc >= Array.length state.code then
Error (State.InvalidJump new_pc)
else
let new_state = State.with_pc new_state new_pc in
let new_cont = Cont.return_to cont in
Continue { state = new_state; cont = new_cont }
| _ -> Error State.InvalidClosure))
| RET ->
(match State.pop_env next_state with
| State.Error _ ->
(match cont with
| Return_to k ->
Continue { state = next_state; cont = k }
| Halt ->
Halted (match State.peek next_state with State.Ok v -> Some v | _ -> None)
| _ ->
Halted (match State.peek next_state with State.Ok v -> Some v | _ -> None))
| State.Ok (new_state, _) ->
(match cont with
| Return_to k ->
Continue { state = new_state; cont = k }
| Halt ->
Halted (match State.peek new_state with State.Ok v -> Some v | _ -> None)
| _ ->
Halted (match State.peek new_state with State.Ok v -> Some v | _ -> None)))
| MK_CLOSURE code ->
(match State.pop next_state with
| State.Error e -> Error e
| State.Ok (env_val, new_state) ->
let env = match env_val with
| Closure { env; _ } -> env
| _ -> State.get_env new_state
in
let closure = Closure { code; env } in
let new_state = State.push new_state closure in
Continue { state = new_state; cont })
| LOAD idx ->
if idx < 0 || idx >= List.length next_state.env then
Error (State.InvalidIndex idx)
else
let v = List.nth next_state.env idx in
let new_state = State.push next_state v in
Continue { state = new_state; cont }
| STORE idx ->
(match State.pop next_state with
| State.Error e -> Error e
| State.Ok (v, new_state) ->
let env = new_state.env in
let env' = if idx < List.length env then
let rec update i = function
| [] -> []
| x :: xs -> if i = idx then v :: xs else x :: update (i + 1) xs
in update 0 env
else
let padding = List.init (idx - List.length env) (fun _ -> Int 0) in
env @ padding @ [v]
in
let new_state = State.set_env new_state env' in
Continue { state = new_state; cont })
| PUSH_ENV ->
let new_state = State.push_env next_state in
Continue { state = new_state; cont }
| POP_ENV ->
(match State.pop_env next_state with
| State.Error e -> Error e
| State.Ok (new_state, _) ->
Continue { state = new_state; cont })
| TRY offset ->
let handler_pc = next_state.pc + offset - 1 in
if handler_pc < 0 || handler_pc >= Array.length state.code then
Error (State.InvalidJump handler_pc)
else
let handler_cont = Cont.eval_to (state.code.(handler_pc)) Cont.halt in
let new_cont = Cont.exception_to handler_cont cont in
Continue { state = next_state; cont = new_cont }
| RAISE ->
(match State.pop next_state with
| State.Error e -> Error e
| State.Ok (exn_val, new_state) ->
let new_state = State.with_exception new_state exn_val in
let rec find_handler = function
| Exception_to { handler; cont } ->
let new_state = State.clear_exception new_state in
let new_state = State.push new_state exn_val in
Continue { state = new_state; cont = handler }
| Return_to k -> find_handler k
| Apply_to { cont; _ } -> find_handler cont
| Eval_to { cont; _ } -> find_handler cont
| Pop_to { cont; _ } -> find_handler cont
| Halt -> Error State.NoExceptionHandler
in
find_handler cont)
| RERAISE ->
(match next_state.exception_value with
| None -> Error State.NoExceptionHandler
| Some exn_val ->
let rec find_handler = function
| Exception_to { handler; cont } ->
let new_state = State.clear_exception new_state in
let new_state = State.push new_state exn_val in
Continue { state = new_state; cont = handler }
| Return_to k -> find_handler k
| Apply_to { cont; _ } -> find_handler cont
| Eval_to { cont; _ } -> find_handler cont
| Pop_to { cont; _ } -> find_handler cont
| Halt -> Error State.NoExceptionHandler
in
find_handler cont)
| HALT ->
Halted (match State.peek next_state with State.Ok v -> Some v | _ -> None)

63
lib/trampoline.ml Normal file
View File

@ -0,0 +1,63 @@
open Step
type thunk = unit -> result
type trace_entry = {
pc : int;
stack_len : int;
cont_depth : int;
}
type config = {
max_steps : int option;
trace : bool;
trace_entries : trace_entry list ref;
}
let default_config = {
max_steps = None;
trace = false;
trace_entries = ref [];
}
let rec run ?(config = default_config) thunk =
match config.max_steps with
| Some max when max <= 0 -> None
| Some max -> run_with_limit ~config max thunk
| None -> run_unlimited ~config thunk
and run_unlimited ~config thunk =
match thunk () with
| Continue { state; cont } ->
(if config.trace then
config.trace_entries := {
pc = state.pc;
stack_len = List.length state.stack;
cont_depth = Cont.depth cont;
} :: !(config.trace_entries));
run_unlimited ~config (fun () -> step state cont)
| Halted v -> Some v
| Error e -> None
and run_with_limit ~config max_steps thunk =
let rec aux steps thunk =
if steps >= max_steps then
None
else
match thunk () with
| Continue { state; cont } ->
(if config.trace then
config.trace_entries := {
pc = state.pc;
stack_len = List.length state.stack;
cont_depth = Cont.depth cont;
} :: !(config.trace_entries));
aux (steps + 1) (fun () -> step state cont)
| Halted v -> Some v
| Error e -> None
in
aux 0 thunk
let get_trace config = List.rev !(config.trace_entries)
let clear_trace config = config.trace_entries := []

62
lib/vm.ml Normal file
View File

@ -0,0 +1,62 @@
open Instr
open State
open Cont
open Step
open Trampoline
type t = {
code : Instr.t array;
initial_state : State.t;
}
type execution_result =
| Success of value option
| Failure of State.error
| Timeout
let create code =
let state = State.create code in
{ code; initial_state = state }
let execute ?(config = Trampoline.default_config) vm =
let state = vm.initial_state in
let cont = Cont.halt in
match Trampoline.run ~config (fun () -> step state cont) with
| Some v -> Success v
| None -> Failure NoExceptionHandler
let execute_with_limit vm max_steps =
let config = { Trampoline.default_config with max_steps = Some max_steps } in
execute ~config vm
let execute_program ?(config = Trampoline.default_config) code =
let vm = create code in
execute ~config vm
let execute_program_with_limit code max_steps =
let vm = create code in
execute_with_limit vm max_steps
let execute_with_trace code =
let config = { Trampoline.default_config with trace = true } in
let result = execute_program ~config code in
(result, Trampoline.get_trace config)
let disassemble code =
Array.mapi (fun i instr -> (i, Instr.show instr)) code
|> Array.to_list
let validate code =
let errors = ref [] in
Array.iteri (fun i instr ->
match instr with
| JMP offset | JMP_IF offset | JMP_IF_NOT offset | TRY offset ->
let target = i + 1 + offset - 1 in
if target < 0 || target >= Array.length code then
errors := (i, State.InvalidJump target) :: !errors
| MK_CLOSURE code_addr ->
if code_addr < 0 || code_addr >= Array.length code then
errors := (i, State.InvalidJump code_addr) :: !errors
| _ -> ())
code;
List.rev !errors