+module List = ListLabels
+module String = StringLabels
+
+module Spl : sig
+ type id = string
+
+ type binop =
+ | Plus
+ | Minus
+ | Times
+ | Div
+
+ type stm =
+ | CompoundStm of stm * stm
+ | AssignStm of id * exp
+ | PrintStm of exp list
+ and exp =
+ | IdExp of id
+ | NumExp of int
+ | OpExp of exp * binop * exp
+ | EseqExp of stm * exp
+
+ exception Unknown_identifier of string
+
+ val maxargs : stm -> int option
+ (** Option because a program may not have any print statements at all. *)
+
+ val interp : stm -> unit
+ (** raises Unknown_identifier, if such is encountered *)
+end = struct
+ module Table : sig
+ type ('k, 'v) t
+ val empty : ('k, 'v) t
+ val set : ('k, 'v) t -> k:'k -> v:'v -> ('k, 'v) t
+ val get : ('k, 'v) t -> k:'k -> 'v option
+ end = struct
+ type ('k, 'v) t = ('k * 'v) list
+ let empty = []
+ let set t ~k ~v = (k, v) :: t
+ let get t ~k =
+ let rec search = function
+ | [] -> None
+ | (key, v) :: _ when key = k -> Some v
+ | (_, _) :: rest -> search rest
+ in
+ search t
+ end
+
+ type id = string
+
+ type binop =
+ | Plus
+ | Minus
+ | Times
+ | Div
+
+ type stm =
+ | CompoundStm of stm * stm
+ | AssignStm of id * exp
+ | PrintStm of exp list
+ and exp =
+ | IdExp of id
+ | NumExp of int
+ | OpExp of exp * binop * exp
+ | EseqExp of stm * exp
+
+ exception Unknown_identifier of string
+
+ let interp_binop op v1 v2 =
+ match op with
+ | Plus -> v1 + v2
+ | Minus -> v1 - v2
+ | Times -> v1 * v2
+ | Div -> v1 / v2
+
+ let rec interp_stm tbl_0 stm =
+ begin match stm with
+ | PrintStm exps ->
+ let (tbl_1, val_ints) =
+ List.fold_right exps
+ ~init:(tbl_0, [])
+ ~f:(fun e (tbl0, vs) ->
+ let (tbl1, v) = interp_exp tbl0 e in
+ (tbl1, v :: vs)
+ )
+ in
+ let val_strings = List.map val_ints ~f:string_of_int in
+ print_endline (String.concat val_strings ~sep:" ");
+ tbl_1
+ | AssignStm (id, e) ->
+ let (tbl_1, v) = interp_exp tbl_0 e in
+ Table.set tbl_1 ~k:id ~v
+ | CompoundStm (s1, s2) ->
+ let tbl_1 = interp_stm tbl_0 s1 in
+ interp_stm tbl_1 s2
+ end
+ and interp_exp tbl_0 exp =
+ ( match exp with
+ | IdExp id ->
+ ( match Table.get tbl_0 ~k:id with
+ | Some v -> (tbl_0, v)
+ | None -> raise (Unknown_identifier id)
+ )
+ | NumExp n -> (tbl_0, n)
+ | OpExp (e1, op, e2) ->
+ let (tbl_1, v1) = interp_exp tbl_0 e1 in
+ let (tbl_2, v2) = interp_exp tbl_1 e2 in
+ (tbl_2, interp_binop op v1 v2)
+ | EseqExp (s, e) ->
+ let tbl_1 = interp_stm tbl_0 s in
+ interp_exp tbl_1 e
+ )
+
+ let interp stm : unit =
+ ignore (interp_stm (Table.empty) stm)
+
+ (* 01.p.1: Write ML function (maxargs : stm -> int) that tells the
+ * maximum number of arguments of any print statement within any
+ * subexpression of a given statement. For example, maxargs(prog)
+ * is 2.
+ *)
+ let maxargs stm =
+ let opt_max_update opt n =
+ match opt with
+ | None -> Some n
+ | Some m -> Some (max m n)
+ in
+ let opt_max_merge a b =
+ match a, b with
+ | None , None -> None
+ | None , b -> b
+ | Some _, None -> a
+ | Some _, Some n -> opt_max_update a n
+ in
+ let rec check_stm max_opt stm =
+ match stm with
+ | PrintStm exps ->
+ List.fold_left exps
+ ~init:(opt_max_update max_opt (List.length exps))
+ ~f:check_exp
+ | AssignStm (_, e) ->
+ check_exp max_opt e
+ | CompoundStm (s1, s2) ->
+ opt_max_merge (check_stm max_opt s1) (check_stm max_opt s2)
+ and check_exp max_opt exp =
+ match exp with
+ | IdExp _ | NumExp _ -> max_opt
+ | OpExp (e1, _, e2) ->
+ opt_max_merge (check_exp max_opt e1) (check_exp max_opt e2)
+ | EseqExp (s, e) ->
+ opt_max_merge (check_stm max_opt s) (check_exp max_opt e)
+ in
+ check_stm None stm
+end
+
+let spl_prog_orig =
+ (* a := 5 + 3;
+ * b := (print(a, a - 1), 10 * a);
+ * print(b)
+ *)
+ Spl.CompoundStm
+ ( Spl.AssignStm ("a", Spl.OpExp (Spl.NumExp 5, Spl.Plus, Spl.NumExp 3))
+ , Spl.CompoundStm
+ ( Spl.AssignStm
+ ( "b"
+ , Spl.EseqExp
+ ( Spl.PrintStm
+ [ Spl.IdExp "a"
+ ; Spl.OpExp (Spl.IdExp "a", Spl.Minus, Spl.NumExp 1)
+ ]
+ , Spl.OpExp (Spl.NumExp 10, Spl.Times, Spl.IdExp "a")
+ )
+ )
+ , Spl.PrintStm [Spl.IdExp "b"]
+ )
+ )
+
+let spl_prog_noprint =
+ (* a := 5 + 3;
+ * b := 10 * a
+ *)
+ Spl.CompoundStm
+ ( Spl.AssignStm
+ ("a", Spl.OpExp (Spl.NumExp 5, Spl.Plus, Spl.NumExp 3))
+ , Spl.AssignStm
+ ("b", Spl.OpExp (Spl.NumExp 10, Spl.Times, Spl.IdExp "a"))
+ )
+
+let () =
+ let string_of_maxargs int_opt =
+ match int_opt with
+ | Some n -> string_of_int n
+ | None -> "N/A"
+ in
+ Printf.printf "maxargs : spl_prog_orig -> %s\n"
+ (string_of_maxargs (Spl.maxargs spl_prog_orig));
+ Printf.printf "maxargs : spl_prog_noprint -> %s\n"
+ (string_of_maxargs (Spl.maxargs spl_prog_noprint));
+ print_endline "BEGIN Spl.interp spl_prog_orig";
+ Spl.interp spl_prog_orig;
+ print_endline "END Spl.interp spl_prog_orig"