standalone exapunks vm in ocaml

Add LINK, and tests

+147 -25
+19 -18
bin/main.ml
··· 8 8 Printexc.record_backtrace true; 9 9 print_newline (); 10 10 let vm = Vm.init ~debug:true () in 11 - let host = Host.create "home" 9 in 11 + let home = Host.create "home" in 12 + let inbox = Host.create "inbox" in 13 + let outbox = Host.create "outbox" in 14 + Vm.add_host vm home; 15 + Vm.add_host vm inbox; 16 + Vm.add_host vm outbox; 17 + Vm.connect_hosts home [ (800, inbox, -1) ]; 18 + Vm.connect_hosts inbox [ (800, outbox, -1) ]; 12 19 let file = File.create "200" ~contents:[ Int 72; Int 52; Int 4; Int 60 ] in 13 - Vm.add_host vm host; 14 - Vm.place_file vm host file; 20 + Vm.place_file vm inbox file; 15 21 let code = 16 22 {| 17 - GRAB 200 18 - COPY F X 19 - ADDI X F X 20 - MULI X F X 21 - SUBI X F X 22 - COPY X F 23 - SEEK -9999 24 - VOID F 25 - SEEK -3 26 - FILE T 27 - |} 23 + LINK 800 24 + GRAB 200 25 + COPY F X 26 + ADDI X F X 27 + MULI X F X 28 + SUBI X F X 29 + COPY X F 30 + LINK 800 31 + |} 28 32 in 29 - (match Vm.create_exa vm host "A" code with 33 + (match Vm.create_exa vm home "A" code with 30 34 | Error err -> print_string err 31 35 | Ok _ -> 32 36 Result.iter_error 33 37 (fun (e : InstResult.error_t) -> print_string (InstResult.show (Error e))) 34 38 (Vm.run vm)); 35 39 print_newline (); 36 - 37 - let f2 = File.create "300" ~contents:[ Int 72; Int 52; Int 4; Int 60; Int 436 ] in 38 - Printf.printf "Results good? %b" (File.equal file f2); 39 40 exit 0
+2
lib/Common.ml
··· 35 35 | TEST_LT of r_n * r_n 36 36 | TEST_GT of r_n * r_n 37 37 (* Movement *) 38 + | LINK of r_n 38 39 | HOST of register 39 40 (* File Manipulation *) 40 41 | MAKE ··· 65 66 name : string; 66 67 grid_size : int; 67 68 mutable grid : obj StringMap.t; 69 + mutable links : host IntMap.t; 68 70 } 69 71 70 72 and value =
+1
lib/Exa.ml
··· 3 3 file, You can obtain one at https://mozilla.org/MPL/2.0/. *) 4 4 5 5 open Common 6 + 6 7 type t = Common.exa 7 8 8 9 let create ?(code = Dynarray.create ()) (name : string) (host : host) : t =
+1
lib/File.ml
··· 3 3 file, You can obtain one at https://mozilla.org/MPL/2.0/. *) 4 4 5 5 open Common 6 + 6 7 type t = Common.file 7 8 8 9 let file_gensym = ref 0
+24 -2
lib/Host.ml
··· 6 6 7 7 type t = Common.host 8 8 9 - let create (name : string) (size : int) : t = 10 - { name; grid = StringMap.create 0; grid_size = size } 9 + type links = (int * t * int) list 10 + 11 + let create ?(size = 9) ?(links = []) (name : string) : t = 12 + { 13 + name; 14 + grid = StringMap.create size; 15 + grid_size = size; 16 + links = links |> List.to_seq |> IntMap.of_seq; 17 + } 18 + 19 + let show (host : t) : string = 20 + Printf.sprintf "{| name = %s; links = [ %s ] |}" host.name 21 + (host.links |> IntMap.to_seq 22 + |> Seq.map (fun ((i : int), (h : t)) -> Printf.sprintf "(%i %s)" i h.name) 23 + |> List.of_seq |> String.concat "; ") 11 24 12 25 let add (host : t) (obj : Object.t) = 13 26 if StringMap.length host.grid >= host.grid_size then false ··· 26 39 | F _ -> None) 27 40 28 41 let remove (host : t) (name : string) = StringMap.remove host.grid name 42 + 43 + let add_links (host : t) (links : links) = 44 + List.iter 45 + (fun (there, dest, back) -> 46 + IntMap.replace host.links there dest; 47 + IntMap.replace dest.links back host) 48 + links 49 + 50 + let get_link (host : t) (link : int) = IntMap.find_opt host.links link
+3
lib/OpCode.ml
··· 45 45 | TEST_EQ (left, right) -> testInstruction "TEST" left "=" right 46 46 | TEST_LT (left, right) -> testInstruction "TEST" left "<" right 47 47 | TEST_GT (left, right) -> testInstruction "TEST" left ">" right 48 + | LINK dest -> rnInstruction "LINK" dest 48 49 | HOST dest -> regInstruction "HOST" dest 49 50 | MAKE -> "MAKE" 50 51 | GRAB file -> rnInstruction "GRAB" file ··· 102 103 | TEST_LT _, _ 103 104 | TEST_GT _, _ -> 104 105 false 106 + | LINK dest1, LINK dest2 -> Register.equal_r_n dest1 dest2 107 + | LINK _, _ -> false 105 108 | HOST r1, HOST r2 -> Register.equal r1 r2 106 109 | HOST _, _ -> false 107 110 | MAKE, MAKE -> true
+14
lib/Reader.ml
··· 80 80 | `TEST_GT -> TEST_GT (left, right))))) 81 81 | _ -> Error "TOO MANY ARGS" 82 82 83 + let parse_link args = 84 + match args with 85 + | [] -> Error "NOT ENOUGH ARGS" 86 + | [ rn ] -> Result.bind (parse_r_n rn) (fun rn -> Ok (LINK rn)) 87 + | _ -> Error "TOO MANY ARGS" 88 + 89 + let parse_host args = 90 + match args with 91 + | [] -> Error "NOT ENOUGH ARGS" 92 + | [ r ] -> Result.bind (parse_register r) (fun r -> Ok (HOST r)) 93 + | _ -> Error "TOO MANY ARGS" 94 + 83 95 let parse_grab args = 84 96 match args with 85 97 | [] -> Error "NOT ENOUGH ARGS" ··· 122 134 | "FJMP" -> parse_mark args 123 135 | "TEST" -> parse_test args 124 136 | "MAKE" -> Ok MAKE 137 + | "HOST" -> parse_host args 138 + | "LINK" -> parse_link args 125 139 | "GRAB" -> parse_grab args 126 140 | "FILE" -> parse_file args 127 141 | "SEEK" -> parse_seek args
+6
lib/Utils.ml
··· 8 8 seq |> List.map fn |> String.concat join 9 9 10 10 module StringMap = Hashtbl.Make (String) 11 + module IntMap = Hashtbl.Make (Int) 12 + 13 + let merge tab1 tab2 = 14 + Hashtbl.( 15 + tab2 |> to_seq |> replace_seq tab1; 16 + tab1)
+19 -3
lib/Vm.ml
··· 31 31 32 32 let add_host (vm : t) (host : Host.t) : unit = StringMap.add vm.hosts host.name host 33 33 34 + let connect_hosts (host : Host.t) links = Host.add_links host links 35 + 34 36 let add_file (vm : t) (file : File.t) : unit = StringMap.add vm.files file.name file 35 37 36 38 let place_exa (vm : t) (host : Host.t) (exa : Exa.t) : unit = ··· 259 261 let host (exa : Exa.t) (dest : register) : inst_result = 260 262 set_register exa dest (Value.key exa.host.name) 261 263 264 + let link (_vm : t) (exa : Exa.t) (dest : r_n) : inst_result = 265 + match value_of_r_n exa dest with 266 + | Error _ as e -> e 267 + | Ok (Key _) -> runtime_error "CANNOT LINK WITH KEYWORD" 268 + | Ok (Int i) -> ( 269 + match Host.get_link exa.host i with 270 + | None -> 271 + Printf.printf "exa.host: %s\n" (Host.show exa.host); 272 + runtime_error "NO MATCHING LINK" 273 + | Some dest -> 274 + if Host.add dest (E exa) then ( 275 + Host.remove exa.host exa.name; 276 + exa.host <- dest); 277 + pass ()) 278 + 262 279 let grab (exa : Exa.t) (src : r_n) : inst_result = 263 280 match value_of_r_n exa src with 264 281 | Error _ as e -> e ··· 311 328 match exa.f with 312 329 | None -> runtime_error "NO CURRENT FILE" 313 330 | Some file -> 314 - if 315 - Host.add exa.host (F file) 316 - then ( 331 + if Host.add exa.host (F file) then ( 317 332 exa.f <- None; 318 333 exa.f_pos <- 0; 319 334 pass ()) ··· 358 373 | TEST_EQ (left, right) -> test_fn exa left test_eq right 359 374 | TEST_LT (left, right) -> test_fn exa left test_lt right 360 375 | TEST_GT (left, right) -> test_fn exa left test_gt right 376 + | LINK dest -> link vm exa dest 361 377 | HOST dest -> host exa dest 362 378 | MAKE -> make vm exa 363 379 | GRAB id -> grab exa id
+43
test/End_to_end_test.ml
··· 1 + (* This Source Code Form is subject to the terms of the Mozilla Public 2 + License, v. 2.0. If a copy of the MPL was not distributed with this 3 + file, You can obtain one at https://mozilla.org/MPL/2.0/. *) 4 + 5 + open Exapunks 6 + open Helpers 7 + 8 + let () = set_context __FILE__ 9 + 10 + let () = 11 + specify "tutorial 2" (fun () -> 12 + let vm = Vm.init () in 13 + let home = Host.create "home" in 14 + let inbox = Host.create "inbox" in 15 + let outbox = Host.create "outbox" in 16 + Vm.add_host vm home; 17 + Vm.add_host vm inbox; 18 + Vm.add_host vm outbox; 19 + Vm.connect_hosts home [ (800, inbox, -1) ]; 20 + Vm.connect_hosts inbox [ (800, outbox, -1) ]; 21 + let file = File.create "200" ~contents:[ Int 72; Int 52; Int 4; Int 60 ] in 22 + Vm.place_file vm inbox file; 23 + let code = 24 + {| 25 + LINK 800 26 + GRAB 200 27 + COPY F X 28 + ADDI X F X 29 + MULI X F X 30 + SUBI X F X 31 + COPY X F 32 + LINK 800 33 + |} 34 + in 35 + (match Vm.create_exa vm home "A" code with 36 + | Error e -> Alcotest.(check string) "" e (string_of_int (Random.bits ())) 37 + | Ok _exa -> 38 + ignore (Vm.run vm); 39 + let f2 = 40 + File.create "200" ~contents:[ Int 72; Int 52; Int 4; Int 60; Int 436 ] 41 + in 42 + Alcotest.check t_file "file has been changed" f2 file); 43 + ())
+1
test/Exapunks_test.ml
··· 2 2 License, v. 2.0. If a copy of the MPL was not distributed with this 3 3 file, You can obtain one at https://mozilla.org/MPL/2.0/. *) 4 4 5 + open End_to_end_test 5 6 open File_test 6 7 open Reader_test 7 8 open Vm_test
+1 -1
test/Helpers.ml
··· 45 45 46 46 let setup ?(hostsize = 9) (hostname : string) = 47 47 let vm = Vm.init () in 48 - let host = Host.create hostname hostsize in 48 + let host = Host.create ~size:hostsize hostname in 49 49 Vm.add_host vm host; 50 50 (vm, host)
+12
test/Vm_test.ml
··· 49 49 ignore (Vm.host exa X); 50 50 Alcotest.check t_value "correct" exa.x (Value.key "home"); 51 51 ()) 52 + 53 + let () = 54 + specify "#link" (fun () -> 55 + let vm, host = setup "home" in 56 + let dest = Host.create "other" in 57 + Vm.add_host vm dest; 58 + Vm.connect_hosts host [ (800, dest, -1) ]; 59 + match Vm.create_exa vm host "A" "LINK 800" with 60 + | Error e -> Alcotest.(check string) "" e (string_of_int (Random.bits ())) 61 + | Ok exa -> 62 + ignore (Vm.tick vm); 63 + Alcotest.(check string) "" exa.host.name "other")
+1 -1
test/dune
··· 4 4 5 5 (tests 6 6 (names exapunks_test) 7 - (modules exapunks_test vm_test file_test reader_test helpers) 7 + (modules exapunks_test vm_test file_test reader_test end_to_end_test helpers) 8 8 (action 9 9 (run %{test} --expert)) 10 10 (libraries exapunks alcotest testo))