caml-urm
A OCaml module for manipulating unlimited register machines
urm.ml (3780B)
1 open Array 2 open Genlex 3 4 type instruction = 5 | T of int * int 6 | Z of int 7 | S of int 8 | P of int 9 | J of int * int * int 10 11 type t = 12 { instruction_pointer : int; 13 registers : int -> int; 14 } 15 16 type machine = t 17 18 let of_registers f = 19 { instruction_pointer = 0; 20 registers = f 21 } 22 23 let of_list xs = 24 let f acc (reg_n, reg_val) = fun n -> if n = reg_n then reg_val else acc n 25 in xs |> List.fold_left f (fun _ -> 0) |> of_registers 26 27 let zeros = of_registers (fun _ -> 0) 28 29 let register m = m.registers 30 31 (** Executes the next instruction. Returns [Some _] is the instruction pointer 32 points to a valid adress and [None] otherwise. *) 33 let step (program : instruction array) (m : machine) : machine option = 34 if m.instruction_pointer >= length program then None 35 else 36 let updated = 37 match program.(m.instruction_pointer) with 38 | T (r1, r2) -> 39 { registers = 40 (fun n -> if n == r2 then m.registers r1 else m.registers n); 41 instruction_pointer = m.instruction_pointer + 1; 42 } 43 | Z r -> 44 { registers = (fun n -> if n == r then 0 else m.registers n); 45 instruction_pointer = m.instruction_pointer + 1; 46 } 47 | S r -> 48 { registers = 49 (fun n -> if n == r then 1 + (m.registers n) else m.registers n); 50 instruction_pointer = m.instruction_pointer + 1; 51 } 52 | P r -> 53 { registers = 54 (fun n -> if n == r then (m.registers n) - 1 else m.registers n); 55 instruction_pointer = m.instruction_pointer + 1; 56 } 57 | J (r1, r2, i) when m.registers r1 == m.registers r2 -> 58 (* [i - 1] is used so that the instruction count starts at 1 instead of 59 0 *) 60 if i > 0 then { m with instruction_pointer = i - 1 } 61 else 62 let e = Printf.sprintf "invalid jump address: J(%d, %d, %d)" r1 r2 i 63 in raise (Invalid_argument e) 64 | J _ -> 65 { m with instruction_pointer = m.instruction_pointer + 1 } 66 in Some updated 67 68 let rec exec program m = 69 match step program m with 70 | None -> { m with instruction_pointer = 0 } 71 | Some n -> exec program n 72 73 let nexec max program m = 74 if max <= 0 75 then raise (Invalid_argument "the number of clock-cycles must be positive ") 76 else 77 let rec exec_safe_tail m clock_cycles = 78 if clock_cycles <= max 79 then 80 match step program m with 81 | None -> Some { m with instruction_pointer = 0 } 82 | Some n -> exec_safe_tail n (clock_cycles + 1) 83 else None 84 in exec_safe_tail m 0 85 86 87 (********************************** Parsing **********************************) 88 89 exception Syntax_error of string 90 91 (** A simple lexer for instructions *) 92 let lex = make_lexer ["T"; "Z"; "S"; "P"; "J"; "("; ")"; ","] 93 94 (** Parse a single instruction *) 95 let parse_instruction (i : token list) : instruction = 96 match i with 97 | [ Kwd "T"; Kwd "("; Int r1; Kwd ","; Int r2; Kwd ")" ] -> 98 T (r1, r2) 99 | [ Kwd "Z"; Kwd "("; Int r; Kwd ")" ] -> 100 Z r 101 | [ Kwd "S"; Kwd "("; Int r; Kwd ")" ] -> 102 S r 103 | [ Kwd "P"; Kwd "("; Int r; Kwd ")" ] -> 104 P r 105 | [ Kwd "J"; Kwd "("; Int r1; Kwd ","; Int r2; Kwd ","; Int i; Kwd ")" ] 106 when i > 0 -> 107 J (r1, r2, i) 108 | [ Kwd "J"; Kwd "("; Int _; Kwd ","; Int _; Kwd ","; Int i; Kwd ")" ] -> 109 raise (Syntax_error (Printf.sprintf "invalid jump address: %d" i)) 110 | _ -> 111 raise (Syntax_error "invalid syntax") 112 113 let parse (s : string) : instruction array = 114 let f s = 115 let s = String.trim s in 116 if s = "" then None 117 else 118 s |> Stream.of_string 119 |> lex 120 |> Stream.npeek 8 121 |> parse_instruction 122 |> Option.some 123 in 124 s |> String.split_on_char '\n' 125 |> List.filter_map f 126 |> Array.of_list 127