caml-urm

A OCaml module for manipulating unlimited register machines

Commit
4da5a6d6d4b15412cba9732ea48bda6c1c75f1f0
Parent
47dc65a881e96aa5061037a9ce1ca84183b736dc
Author
Pablo <pablo-escobar@riseup.net>
Date

Changed the command line paramenters

Also introduced the Urm.of_list function to help parsing of the command line arguments

Diffstat

5 files changed, 97 insertions, 81 deletions

Status File Name N° Changes Insertions Deletions
Modified docs.pdf 0 0 0
Modified main.ml 141 69 72
Modified urm.1 27 19 8
Modified urm.ml 4 4 0
Modified urm.mli 6 5 1
diff --git a/docs.pdf b/docs.pdf
Binary files differ.
diff --git a/main.ml b/main.ml
@@ -1,89 +1,86 @@
 open List
 
 let usage () =
-  Printf.printf "USAGE: urm [-i ITERS] REGISTERS FILE\n";
+  Printf.printf "USAGE: urm FILE [-i ITERS] [-RN=V...] [-RN...] \n";
   Printf.printf "See urm.1 for additional information\n"
 
 let default_max_iters = 1000
 
-(** An equivalent to Haskell's [sequence] *)
-let sequence (xs : 'a option list) : 'a list option =
-  let f x acc = Option.bind x (fun x -> Option.map (List.cons x) acc)
-  in List.fold_right f xs (Some [])
-
 (** Execute the program from [file] with initial register values given by
     [regs] and the maximum number of allowed iterations set to [iters] *)
-let main iters regs file =
+let main iters init_m regs_prints file =
   let contents = really_input_string file (in_channel_length file) in
   try
-    let program = Urm.parse contents
-    and regs_fn i = if i < Array.length regs then regs.(i) else 0
-    in
-      match Urm.nexec iters program (Urm.of_registers regs_fn) with
-      | Some m ->
-        let regs_strs =
-          Urm.register m
-            |> List.init (Array.length regs)
-            |> List.map (Printf.sprintf "%d")
-        in Printf.printf "%s\n" (String.concat " " regs_strs)
-      | None ->
-        Printf.eprintf
-          "ERROR: the input program did not halt after %d iterations\n"
-          iters;
-        Printf.eprintf "The input program may not halt\n";
-        exit 1
-  with Urm.Syntax_error err -> Printf.eprintf "ERROR: %s\n" err
-
-
-(** Initial parsing of the command-line arguments: get the strings out *)
-let (iters, regs_str, filepath) : int * string * string =
-  let parse_iters iters_str =
-    match int_of_string_opt iters_str with
-    | Some n when n > 0 ->
-      n
-    | Some n ->
-      Printf.eprintf
-        "ERROR: negative number of maximum iterations provided: %d\n" n;
-      Printf.eprintf "Expected a positive integers\n";
-      usage ();
-      exit 1
+    let program = Urm.parse contents in
+    match Urm.nexec iters program init_m with
+    | Some m ->
+      List.iter (fun n -> Printf.printf "R%d = %d\n" n (Urm.register m n))
+                regs_prints
     | None ->
       Printf.eprintf
-        "ERROR: invalid number of maximum iterations provided: '%s'\n"
-        iters_str;
-      Printf.eprintf "Expected a positive integers\n";
-      usage ();
-      exit 1
-  in
-    match Sys.argv with
-    | [| _ ; "-i" ; iters_str ; regs ; filepath |] ->
-      (parse_iters iters_str, regs, filepath)
-    | [| _ ; regs ; filepath |] ->
-      (default_max_iters, regs, filepath)
-    | argv when Array.length argv < 3 ->
-      Printf.eprintf "ERROR: not enought arguments provided\n";
-      usage ();
-      exit 1
-    | _ ->
-      Printf.eprintf "ERROR: invalid arguments provided\n";
-      usage ();
+        "ERROR: the input program did not halt after %d iterations\n"
+        iters;
+      Printf.eprintf "The input program may not halt\n";
       exit 1
+  with Urm.Syntax_error err -> Printf.eprintf "ERROR: %s\n" err
 
-(** Parse the initial values of the registers as a list of ints separated by
-    spaces *)
-let regs =
-  let regs = List.filter ((<>) "") (String.split_on_char ' ' regs_str)
-  in
-    match sequence (List.map int_of_string_opt regs) with
-    | Some regs ->
-      regs
-    | None ->
-      Printf.eprintf
-        "ERROR: invalid initial values for the registers provided: '%s'\n"
-        regs_str;
-      Printf.eprintf "Expected a list of integers separated by spaces\n";
-      usage ();
-      exit 1
+(** Parses the command-line arguments concerning registers.
+    returns [(regs_vals, regs_prints)], where [regs_vals] is the list of pairs
+    encoding the initial values of the machine, and [regs_prints] is the list
+    of registers that should be printed at the end of execution. *)
+let parse_regs (regs : string list) : (int * int) list * int list =
+  let rec fail reg =
+    Printf.eprintf "ERROR: invalid arguments provided: %s\n" reg;
+    usage ();
+    exit 1
+  and f reg (acc_vals, acc_prints) =
+    if not (String.starts_with ~prefix:"-R" reg) then fail reg
+    else let reg' = String.sub reg 2 (String.length reg - 2) in
+    match String.split_on_char '=' reg' with
+    | [reg_n] ->
+      (match int_of_string_opt reg_n with
+       | Some reg_n -> (acc_vals, reg_n :: acc_prints)
+       | None -> fail reg)
+    | [ reg_n ; reg_val ] ->
+      (match (int_of_string_opt reg_n, int_of_string_opt reg_val) with
+       | (Some reg_n, Some reg_val) ->
+           ((reg_n, reg_val) :: acc_vals, acc_prints)
+       | _ -> fail reg)
+    | _ -> fail reg
+  in List.fold_right f regs ([], [])
+
+(** Parses the [-i] command line argument *)
+let parse_iters (iters_str : string) : int =
+  match int_of_string_opt iters_str with
+  | Some n when n > 0 ->
+    n
+  | Some n ->
+    Printf.eprintf
+      "ERROR: negative number of maximum iterations provided: %d\n" n;
+    Printf.eprintf "Expected a positive integers\n";
+    usage ();
+    exit 1
+  | None ->
+    Printf.eprintf
+      "ERROR: invalid number of maximum iterations provided: '%s'\n"
+      iters_str;
+    Printf.eprintf "Expected a positive integers\n";
+    usage ();
+    exit 1
+
+(** Parses all command line arguments *)
+let (iters, init_m, regs_prints, filepath) : int * Urm.t * int list * string =
+  match Array.to_list Sys.argv with
+  | _ :: file_path :: "-i" :: iters_str :: regs ->
+    let (reg_vals, reg_prints) = parse_regs regs in
+    (parse_iters iters_str, Urm.of_list reg_vals, reg_prints, file_path)
+  | _ :: file_path :: regs ->
+    let (reg_vals, reg_prints) = parse_regs regs in
+    (default_max_iters, Urm.of_list reg_vals, reg_prints, file_path)
+  | _ ->
+    Printf.eprintf "ERROR: not enought arguments provided\n";
+    usage ();
+    exit 1
 
 (** Try to open the file with the contents of the program *)
 let file =
@@ -93,5 +90,5 @@ let file =
     Printf.eprintf "ERROR: %s\n" err;
     exit 1
 
-let () = main iters (Array.of_list regs) file
+let () = main iters init_m regs_prints file
 
diff --git a/urm.1 b/urm.1
@@ -6,9 +6,9 @@
 .\"
 .\" Unlimited Register Machine simulator
 .\"
-.\" Copyright (C) 2021 Pablo
+.\" Copyright (C) 2022 Pablo
 .\" Free use of this software is granted under the terms of the GPL-3.0 License
-.TH "MAN" "1" "2021-12-11" "\ \&" "\ \&"
+.TH "MAN" "1" "2023-03-19" "\ \&" "\ \&"
 .ie \n(.g .ds Aq \(aq
 .el       .ds Aq '
 .ss \n[.ss] 0
@@ -33,14 +33,25 @@
 A simple Unlimited Register Machine (URM) simulator
 .SH "USAGE"
 .sp
-\fBurm\fP [-i ITERS] REGISTERS FILE
+\fBurm\fP \fIFILE\fP [-i \fIITERS\fP] [-R\fIN\fP=\fIV\fP...] [-R\fIN\fP...]
 .SH "OPTIONS"
 .sp
-\fB-i ITERS\fP
-.RS 4 Sets the maximum number of iterations allowed to \fBITERS\fP: an arbitrary
+\fB-i\fP \fIITERS\fP
+.RS 4
+Sets the maximum number of iterations allowed to \fIITERS\fP: an arbitrary
 program may not halt for a given input and this option configures how much
 instructions is the machine allowed to run before failing with an error.
-Defaults to \fB1000\fP
+Defaults to \fI1000\fP
+.RE
+.sp
+\fB-R\fP\fIN\fP=\fIV\fP
+.RS 4
+Sets the initial value of the \fIN\fP-th register to \fIV\fP
+.RE
+.sp
+\fB-R\fP\fIN\fP
+.RS 4
+Prints the value of the \fIN\fP-th register at the end of execution
 .RE
 .SH "AUTHORS"
 .sp
@@ -50,8 +61,8 @@ Defaults to \fB1000\fP
 .SH "RESOURCES"
 .sp
 \fBGit source repository:\fP \c
-.URL "https://git.pablopie.xyz/caml\-urm/README.html" "" ""
+.URL "https://git.pablopie.xyz/caml\-urm/" "" ""
 .SH "COPYING"
 .sp
-Copyright (C) 2021 Pablo.
+Copyright (C) 2023 Pablo.
 Free use of this software is granted under the terms of the GPL\-3.0 License.
diff --git a/urm.ml b/urm.ml
@@ -20,6 +20,10 @@ let of_registers f =
     registers = f
   }
 
+let of_list xs =
+  let f acc (reg_n, reg_val) = fun n -> if n = reg_n then reg_val else acc n
+  in xs |> List.fold_left f (fun _ -> 0) |> of_registers
+
 let zeros = of_registers (fun _ -> 0)
 
 let register m = m.registers
diff --git a/urm.mli b/urm.mli
@@ -37,11 +37,15 @@ type instruction =
 type t
 
 (** An alias for {! t } *)
-type machine
+type machine = t
 
 (** Returns a machine whose register values are given by a function. *)
 val of_registers : (int -> int) -> machine
 
+(** Returns a machine whose registers are given by the pairs in a list. All
+    other registers are zero initialized. *)
+val of_list : (int * int) list -> machine
+
 (** [register m i] returns the value of the [i]-th register of [m]. *)
 val register : machine -> int -> int