Check scope of break statements
authorSiraaj Khandkar <siraaj@khandkar.net>
Tue, 18 Sep 2018 20:31:45 +0000 (16:31 -0400)
committerSiraaj Khandkar <siraaj@khandkar.net>
Tue, 18 Sep 2018 20:31:45 +0000 (16:31 -0400)
compiler/src/lib/tiger/tiger_env.ml
compiler/src/lib/tiger/tiger_env.mli
compiler/src/lib/tiger/tiger_error.ml
compiler/src/lib/tiger/tiger_error.mli
compiler/src/lib/tiger/tiger_semant.ml
compiler/src/lib/tiger/tiger_symbol.ml
compiler/src/lib/tiger/tiger_symbol.mli
compiler/src/lib/tiger/tiger_test_cases.ml
screenshots/tests-tail.jpg

index f698f89..1e4cd1d 100644 (file)
@@ -1,15 +1,18 @@
 module Map   = Tiger_map
+module Sym   = Tiger_symbol
 module Type  = Tiger_env_type
 module Value = Tiger_env_value
 
 type t =
   { typs : Type.env
   ; vals : Value.env
+  ; loop : Sym.t option
   }
 
 let base =
   { typs = Type.built_in
   ; vals = Value.built_in
+  ; loop = None
   }
 
 let get_typ {typs; _} k =
@@ -23,3 +26,20 @@ let set_typ t k v =
 
 let set_val t k v =
   {t with vals = Map.set t.vals ~k ~v}
+
+let loop_begin t =
+  let loop = Sym.new_of_string "loop" in
+  let t = {t with loop = Some loop} in
+  (loop, t)
+
+let loop_end t given =
+  match t.loop with
+  | None ->
+      assert false
+  | Some current when (not (Sym.is_equal current given)) ->
+      assert false
+  | Some _ ->
+      {t with loop = None}
+
+let loop_current {loop; _} =
+  loop
index 91747e5..3338949 100644 (file)
@@ -7,3 +7,7 @@ val get_val : t -> Tiger_symbol.t -> Tiger_env_value.t option
 
 val set_typ : t -> Tiger_symbol.t -> Tiger_env_type.t -> t
 val set_val : t -> Tiger_symbol.t -> Tiger_env_value.t -> t
+
+val loop_begin   : t -> (Tiger_symbol.t * t)
+val loop_end     : t ->  Tiger_symbol.t -> t
+val loop_current : t ->  Tiger_symbol.t option
index 11b4b95..f2b3ae1 100644 (file)
@@ -10,6 +10,7 @@ type t =
       ;   to_id  : Sym.t
       ;   to_pos : Pos.t
       }
+  | Break_outside_loop of Pos.t
   | Invalid_syntax    of Pos.t
   | Unknown_id        of {id    : Sym.t; pos : Pos.t}
   | Unknown_type      of {ty_id : Sym.t; pos : Pos.t}
@@ -80,6 +81,8 @@ let to_string =
   function
   | Invalid_syntax pos ->
       s "Invalid syntax in %s" (Pos.to_string pos)
+  | Break_outside_loop pos ->
+      s "Break statement is not within a loop. In %s" (Pos.to_string pos)
   | Cycle_in_type_decs  {from_id; from_pos; to_id; to_pos} ->
       s ( "Circular type declaration between %S and %S."
         ^^"Locations: %S (in %s), %S (in %s).")
@@ -167,6 +170,7 @@ let is_unknown_id t =
   match t with
   | Unknown_id _ ->
       true
+  | Break_outside_loop _
   | Cycle_in_type_decs _
   | Invalid_syntax _
   | Unknown_type _
@@ -190,6 +194,7 @@ let is_unknown_type t =
   match t with
   | Unknown_type _ ->
       true
+  | Break_outside_loop _
   | Cycle_in_type_decs _
   | Unknown_id _
   | Invalid_syntax _
@@ -213,6 +218,7 @@ let is_wrong_type t =
   match t with
   | Wrong_type _ ->
       true
+  | Break_outside_loop _
   | Cycle_in_type_decs _
   | Unknown_type _
   | Unknown_id _
@@ -236,6 +242,7 @@ let is_wrong_number_of_args t =
   match t with
   | Wrong_number_of_args _ ->
       true
+  | Break_outside_loop _
   | Cycle_in_type_decs _
   | Wrong_type _
   | Unknown_type _
@@ -259,6 +266,7 @@ let is_invalid_syntax t =
   match t with
   | Invalid_syntax _ ->
       true
+  | Break_outside_loop _
   | Cycle_in_type_decs _
   | Wrong_type _
   | Unknown_type _
@@ -282,6 +290,7 @@ let is_not_a_record t =
   match t with
   | Exp_not_a_record _ ->
       true
+  | Break_outside_loop _
   | Cycle_in_type_decs _
   | Invalid_syntax _
   | Wrong_type _
@@ -305,6 +314,7 @@ let is_not_an_array t =
   match t with
   | Exp_not_an_array _ ->
       true
+  | Break_outside_loop _
   | Cycle_in_type_decs _
   | Exp_not_a_record _
   | Invalid_syntax _
@@ -328,6 +338,7 @@ let is_no_such_field_in_record t =
   match t with
   | No_such_field_in_record _ ->
       true
+  | Break_outside_loop _
   | Cycle_in_type_decs _
   | Exp_not_an_array _
   | Exp_not_a_record _
@@ -351,6 +362,31 @@ let is_cycle_in_type_dec t =
   match t with
   | Cycle_in_type_decs _ ->
       true
+  | Break_outside_loop _
+  | No_such_field_in_record _
+  | Exp_not_an_array _
+  | Exp_not_a_record _
+  | Invalid_syntax _
+  | Wrong_type _
+  | Unknown_type _
+  | Unknown_id _
+  | Id_is_a_function _
+  | Id_not_a_function _
+  | Wrong_type_of_expression_in_var_dec _
+  | Wrong_type_used_as_array _
+  | Wrong_type_used_as_record _
+  | Wrong_type_of_field_value _
+  | Wrong_type_of_arg _
+  | Wrong_number_of_args _
+  | Invalid_operand_type _
+  | Different_operand_types _ ->
+      false
+
+let is_break_outside_loop t =
+  match t with
+  | Break_outside_loop _ ->
+      true
+  | Cycle_in_type_decs _
   | No_such_field_in_record _
   | Exp_not_an_array _
   | Exp_not_a_record _
index 73505b8..0344269 100644 (file)
@@ -10,6 +10,7 @@ type t =
       ;   to_id  : Sym.t
       ;   to_pos : Pos.t
       }
+  | Break_outside_loop of Pos.t
   | Invalid_syntax    of Pos.t
   | Unknown_id        of {id    : Sym.t; pos : Pos.t}
   | Unknown_type      of {ty_id : Sym.t; pos : Pos.t}
@@ -85,3 +86,4 @@ val is_not_a_record : t -> bool
 val is_not_an_array : t -> bool
 val is_no_such_field_in_record : t -> bool
 val is_cycle_in_type_dec : t -> bool
+val is_break_outside_loop : t -> bool
index 6bb45fd..19e8c8b 100644 (file)
@@ -192,16 +192,25 @@ end = struct
       | A.WhileExp {test; body; pos} ->
           (* test : must be int, because we have no bool *)
           check_int (trexp test) ~pos;
-          ignore (trexp body);  (* Only care if a type-error is raised *)
+          let (loop, env) = Env.loop_begin env in
+          (* Only care if an error is raised *)
+          ignore (transExp ~env body);
+          ignore (Env.loop_end env loop);
           return_unit
       | A.ForExp {var; lo; hi; body; pos; escape=_} ->
           check_int (trexp lo) ~pos;
           check_int (trexp hi) ~pos;
-          (* Only care if a type-error is raised *)
+          let (loop, env) = Env.loop_begin env in
           let env = Env.set_val env var (Value.Var {ty = Type.Int}) in
+          (* Only care if an error is raised *)
           ignore (transExp ~env body);
+          ignore (Env.loop_end env loop);
           return_unit
-      | A.BreakExp _ ->
+      | A.BreakExp pos ->
+          (match Env.loop_current env with
+          | Some _ -> ()
+          | None   -> E.raise (E.Break_outside_loop pos)
+          );
           return_unit
       | A.LetExp {decs; body; pos=_} ->
           (* (1) decs augment env *)
index f1dc51a..b5d895b 100644 (file)
@@ -5,22 +5,30 @@ type t =
   ; symbol : int
   }
 
-let nextsym = ref 0
+let counter = ref 0
 
 let symbols = H.create 16
 
+let next name =
+  incr counter;
+  let symbol = !counter in
+  {name; symbol}
+
+let new_of_string name =
+  let t = next name in
+  H.replace symbols ~key:t.name ~data:t.symbol;
+  t
+
 let of_string name =
   match H.find_opt symbols name with
-  | Some symbol ->
-      {name; symbol}
-  | None ->
-      incr nextsym;
-      let symbol = !nextsym in
-      H.replace symbols ~key:name ~data:symbol;
-      {name; symbol}
+  | Some s -> {name; symbol=s}
+  | None   -> new_of_string name
 
 let to_string {name; _} =
   name
 
 let is_equal {symbol=s1; _} {symbol=s2; _} =
   s1 = s2
+
+let show {name; symbol} =
+  Printf.sprintf "Symbol[%S, %d]" name symbol
index e5c560f..1dd5acc 100644 (file)
@@ -1,7 +1,13 @@
 type t
 
+val new_of_string : string -> t
+
 val of_string : string -> t
 
 val to_string : t -> string
+(* Reversable. Returns original. *)
+
+val show : t -> string
+(* Not-reversable. M-expression with name and symbol. *)
 
 val is_equal : t -> t -> bool
index 082e02f..1ead5f5 100644 (file)
@@ -120,6 +120,30 @@ let micro =
         "
         ~is_error_expected_semant:(Some Error.is_cycle_in_type_dec)
     )
+  ; ( Test.case
+        "Break outside loop"
+        ~code:
+          "break"
+        ~is_error_expected_semant:(Some Error.is_break_outside_loop)
+    )
+  ; ( Test.case
+        "Break within for loop"
+        ~code:"for i := 0 to 5 do (print(\"x\"); break)"
+    )
+  ; ( Test.case
+        "Break after for loop"
+        ~code:"(for i := 0 to 5 do (print(\"x\"); break); break)"
+        ~is_error_expected_semant:(Some Error.is_break_outside_loop)
+    )
+  ; ( Test.case
+        "Break within while loop"
+        ~code:"while 1 do (print(\"x\"); break)"
+    )
+  ; ( Test.case
+        "Break after while loop"
+        ~code:"(while 1 do (print(\"x\"); break); break)"
+        ~is_error_expected_semant:(Some Error.is_break_outside_loop)
+    )
   ]
 
 let book ~dir =
index b0c72d7..10e7dbe 100644 (file)
Binary files a/screenshots/tests-tail.jpg and b/screenshots/tests-tail.jpg differ
This page took 0.031767 seconds and 4 git commands to generate.