diff --git a/hosts/ocaml/bin/run_tests.ml b/hosts/ocaml/bin/run_tests.ml index 537c076a..2e784767 100644 --- a/hosts/ocaml/bin/run_tests.ml +++ b/hosts/ocaml/bin/run_tests.ml @@ -1746,16 +1746,42 @@ let run_foundation_tests () = | v when nm v = "false" -> incr pass_count; Printf.printf " PASS: OP_BIF_IS_LIST tuple -> false\n" | v -> incr fail_count; - Printf.printf " FAIL: IS_LIST tuple: got %s\n" (Sx_types.inspect v))); + Printf.printf " FAIL: IS_LIST tuple: got %s\n" (Sx_types.inspect v)); + (* ELEMENT: element(2, {1,2,3}) -> 2. Calling convention: push + Index then Tuple; opcode pops Tuple (TOS) then Index. *) + check "OP_BIF_ELEMENT element(2,{1,2,3}) -> 2" (Sx_types.Integer 2) + (run [| Sx_types.Integer 2; tup3 |] [| 1;0;0; 1;1;0; 233; 50 |]); + check "OP_BIF_ELEMENT element(1,{1,2,3}) -> 1" (Sx_types.Integer 1) + (run [| Sx_types.Integer 1; tup3 |] [| 1;0;0; 1;1;0; 233; 50 |]); + (* ELEMENT out of range raises *) + (let raised = + (try ignore (run [| Sx_types.Integer 9; tup3 |] + [| 1;0;0; 1;1;0; 233; 50 |]); false + with Sx_types.Eval_error _ -> true) in + if raised then begin + incr pass_count; + Printf.printf " PASS: OP_BIF_ELEMENT out-of-range raises\n" + end else begin + incr fail_count; + Printf.printf " FAIL: OP_BIF_ELEMENT out-of-range should raise\n" + end); + (* LISTS_REVERSE [7,8,9] -> [9,8,7]; verify HD = 9 then HD of TL = 8 *) + check "OP_BIF_LISTS_REVERSE then HD -> 9" (Sx_types.Integer 9) + (run [| lst3 |] [| 1;0;0; 235; 231; 50 |]); + check "OP_BIF_LISTS_REVERSE then TL,HD -> 8" (Sx_types.Integer 8) + (run [| lst3 |] [| 1;0;0; 235; 232; 231; 50 |]); + (* reverse preserves length *) + check "OP_BIF_LISTS_REVERSE then LENGTH -> 3" (Sx_types.Integer 3) + (run [| lst3 |] [| 1;0;0; 235; 230; 50 |])); - (* A still-stubbed opcode (233 = erlang.OP_BIF_ELEMENT) raises the + (* A still-stubbed opcode (222 = erlang.OP_PATTERN_TUPLE) raises the not-wired Eval_error — confirms the honest-failure path remains for opcodes whose real handlers haven't landed. *) (let globals = Hashtbl.create 1 in try - ignore (Sx_vm.execute_module (make_bc_seq [| 233; 50 |]) globals); + ignore (Sx_vm.execute_module (make_bc_seq [| 222; 50 |]) globals); incr fail_count; - Printf.printf " FAIL: erlang.OP_BIF_ELEMENT dispatch should have raised\n" + Printf.printf " FAIL: erlang.OP_PATTERN_TUPLE dispatch should have raised\n" with | Sx_types.Eval_error msg when (let needle = "not yet wired" in diff --git a/hosts/ocaml/lib/extensions/erlang_ext.ml b/hosts/ocaml/lib/extensions/erlang_ext.ml index 22f78f13..64ed1701 100644 --- a/hosts/ocaml/lib/extensions/erlang_ext.ml +++ b/hosts/ocaml/lib/extensions/erlang_ext.ml @@ -177,6 +177,72 @@ module M : Sx_vm_extension.EXTENSION = struct let v = Sx_vm.pop vm in Sx_vm.push vm (er_bool (is_tag v "tuple")))) in + (* element/2 and lists:reverse/1 — pure stack transforms (no + bytecode operands). Calling convention: args pushed left→right, + so element/2 stack is [.. Index Tuple] (Tuple on top). Erlang + element/2 is 1-indexed. *) + let op_bif_element = + (233, "erlang.OP_BIF_ELEMENT", + (fun (vm : Sx_vm.vm) _f -> + bump (); + let tup = Sx_vm.pop vm in + let idx = Sx_vm.pop vm in + match tup, idx with + | Dict d, Integer i when er_tag d = "tuple" -> + let es = match Hashtbl.find_opt d "elements" with + | Some (List es) -> es + | Some (ListRef r) -> !r + | _ -> raise (Eval_error + "erlang.OP_BIF_ELEMENT: tuple without :elements") + in + let n = List.length es in + if i < 1 || i > n then + raise (Eval_error + (Printf.sprintf + "erlang.OP_BIF_ELEMENT: index %d out of range 1..%d" i n)) + else + Sx_vm.push vm (List.nth es (i - 1)) + | _, Integer _ -> + raise (Eval_error "erlang.OP_BIF_ELEMENT: 2nd arg not a tuple") + | _ -> + raise (Eval_error "erlang.OP_BIF_ELEMENT: 1st arg not an integer"))) + in + let op_bif_lists_reverse = + (235, "erlang.OP_BIF_LISTS_REVERSE", + (fun (vm : Sx_vm.vm) _f -> + bump (); + let v = Sx_vm.pop vm in + let mk_nil () = + let h = Hashtbl.create 1 in + Hashtbl.replace h "tag" (String "nil"); Dict h in + let mk_cons hd tl = + let h = Hashtbl.create 3 in + Hashtbl.replace h "tag" (String "cons"); + Hashtbl.replace h "head" hd; + Hashtbl.replace h "tail" tl; + Dict h in + let rec rev acc node = + match node with + | Dict d -> + (match er_tag d with + | "nil" -> acc + | "cons" -> + let hd = match Hashtbl.find_opt d "head" with + | Some x -> x + | None -> raise (Eval_error + "erlang.OP_BIF_LISTS_REVERSE: cons without :head") in + let tl = match Hashtbl.find_opt d "tail" with + | Some x -> x + | None -> raise (Eval_error + "erlang.OP_BIF_LISTS_REVERSE: cons without :tail") in + rev (mk_cons hd acc) tl + | _ -> raise (Eval_error + "erlang.OP_BIF_LISTS_REVERSE: not a proper list")) + | _ -> raise (Eval_error + "erlang.OP_BIF_LISTS_REVERSE: not a proper list") + in + Sx_vm.push vm (rev (mk_nil ()) v))) + in [ op 222 "erlang.OP_PATTERN_TUPLE"; op 223 "erlang.OP_PATTERN_LIST"; @@ -189,9 +255,9 @@ module M : Sx_vm_extension.EXTENSION = struct op_bif_length; op_bif_hd; op_bif_tl; - op 233 "erlang.OP_BIF_ELEMENT"; + op_bif_element; op_bif_tuple_size; - op 235 "erlang.OP_BIF_LISTS_REVERSE"; + op_bif_lists_reverse; op_bif_is_integer; op_bif_is_atom; op_bif_is_list;