Selaa lähdekoodia

Changed the way errors are represented + context analysis now prints multiple errors before exiting

Taddeus Kroes 12 vuotta sitten
vanhempi
sitoutus
885fa4bc95
19 muutettua tiedostoa jossa 106 lisäystä ja 85 poistoa
  1. 5 24
      main.ml
  2. 11 9
      phases/assemble.ml
  3. 1 1
      phases/boolop.ml
  4. 1 1
      phases/constprop.ml
  5. 14 11
      phases/context.ml
  6. 2 2
      phases/desug.ml
  7. 1 1
      phases/dimreduce.ml
  8. 1 1
      phases/extern.ml
  9. 1 1
      phases/index.ml
  10. 1 1
      phases/load.ml
  11. 1 1
      phases/output.ml
  12. 4 4
      phases/parse.ml
  13. 1 1
      phases/peephole.ml
  14. 1 1
      phases/print.ml
  15. 14 12
      phases/typecheck.ml
  16. 1 1
      phases/unroll.ml
  17. 16 13
      types.mli
  18. 23 0
      util.ml
  19. 7 0
      util.mli

+ 5 - 24
main.ml

@@ -105,30 +105,11 @@ let compile () =
  * Command-line arguments are stored in lobals.args *)
 let main () =
   try
-    try
-      parse_args ();
-      compile ();
-      0
-    with
-    (*| InvalidNode ->
-      raise (CompileError "invalid node")*)
-    | InvalidInput name ->
-      raise (CompileError ("invalid input for phase \"" ^ name ^ "\""))
-    | NodeError (node, msg) ->
-      (* If no location is given, just stringify the node to at least give
-       * some information *)
-      let msg = if locof node = noloc then
-        msg ^ "\nnode: " ^ Stringify.node2str node
-      else msg in
-      raise (LocError (locof node, msg))
-  with
-  | CompileError msg ->
-    eprintf "Error: %s\n" msg;
-    1
-  | LocError (loc, msg) ->
-    prerr_loc_msg loc ("Error: " ^ msg);
-    1
-  | EmptyError ->
+    parse_args ();
+    compile ();
+    0
+  with FatalError err ->
+    print_error err;
     1
 
 let _ = exit (main ())

+ 11 - 9
phases/assemble.ml

@@ -165,14 +165,16 @@ let assemble program =
 
     | TypeCast (ctype, value, _) ->
       let vtype = typeof value in
-      (match (ctype, vtype) with
-      | (Float, Int) | (Int, Float) -> ()
-      | _ ->
-        let msg = sprintf
-          "invalid typecast: %s -> %s"
-          (type2str vtype) (type2str ctype)
-        in raise (NodeError (node, msg))
-      );
+      begin
+        match (ctype, vtype) with
+        | (Float, Int) | (Int, Float) -> ()
+        | _ ->
+          let msg = sprintf
+            "invalid typecast: %s -> %s"
+            (type2str vtype) (type2str ctype)
+          in
+          raise (FatalError (NodeMsg (node, msg)))
+      end;
       trav value @ [Convert (vtype, ctype)]
 
     (* Function calls *)
@@ -267,4 +269,4 @@ let assemble program =
 
 let phase = function
   | Ast node -> Assembly (assemble node)
-  | _ -> raise (InvalidInput "assembly")
+  | _ -> raise InvalidInput

+ 1 - 1
phases/boolop.ml

@@ -64,4 +64,4 @@ and bool_op = function
 
 let phase = function
   | Ast node -> Ast (bool_op node)
-  | _ -> raise (InvalidInput "bool operations")
+  | _ -> raise InvalidInput

+ 1 - 1
phases/constprop.ml

@@ -188,4 +188,4 @@ let propagate_consts node =
 
 let phase = function
   | Ast node -> Ast (propagate_consts node)
-  | _ -> raise (InvalidInput "constant propagation")
+  | _ -> raise InvalidInput

+ 14 - 11
phases/context.ml

@@ -9,7 +9,7 @@ let type2str = function Funcname _ -> "function" | Varname _ -> "variable"
 let mapfind name tbl =
   if Hashtbl.mem tbl name then Some (Hashtbl.find tbl name) else None
 
-let check_in_scope name errnode scope =
+let check_in_scope name errnode scope err =
   let (vars, funs) = scope in
   let (name, tbl, other_map, desired_type) = match name with
   | Varname  name -> (name, vars, funs, "variable")
@@ -23,7 +23,8 @@ let check_in_scope name errnode scope =
     | Some _ -> sprintf "\"%s\" is not a %s" name desired_type
     | None   -> sprintf "undefined %s \"%s\"" desired_type name
     in
-    raise (NodeError (errnode, msg))
+    err := !err @ [NodeMsg (errnode, msg)];
+    (DummyNode, -1)
 
 let add_to_scope name dec depth (vars, funs) =
   let (name, tbl, name_type) = match name with
@@ -43,13 +44,13 @@ let add_to_scope name dec depth (vars, funs) =
       let msg = sprintf "Error: cannot redeclare %s \"%s\"" name_type name in
       prerr_loc_msg (locof dec) msg;
       prerr_loc_msg (locof orig) "Previously declared here:";
-      raise EmptyError
+      raise (FatalError NoMsg)
   | Some _ ->
     Hashtbl.replace tbl name (dec, depth, name_type)
   | None ->
     Hashtbl.add tbl name (dec, depth, name_type)
 
-let rec analyse scope depth node =
+let rec analyse scope depth node err =
   let rec collect node = match node with
     (* For extern array declarations, add the dimension names as well *)
     | GlobalDec (ArrayDims (ctype, dims), name, ann) ->
@@ -86,21 +87,21 @@ let rec analyse scope depth node =
     (* For a variable or function call, look for its declaration in the
      * current scope and save a its type/depth information  *)
     | Var (name, dims, ann) ->
-      let (dec, dec_depth) = check_in_scope (Varname name) node scope in
+      let (dec, dec_depth) = check_in_scope (Varname name) node scope err in
       VarUse (dec, optmap collect dims, Depth depth :: ann)
 
     | FunCall (name, args, ann) ->
-      let (dec, dec_depth) = check_in_scope (Funcname name) node scope in
+      let (dec, dec_depth) = check_in_scope (Funcname name) node scope err in
       FunUse (dec, List.map collect args, Depth depth :: ann)
 
     (* Assign statements are replaced with VarLet nodes, which stores the
      * declaration of the assigned variable *)
     | Assign (name, dims, value, ann) ->
-      let (dec, dec_depth) = check_in_scope (Varname name) node scope in
+      let (dec, dec_depth) = check_in_scope (Varname name) node scope err in
       VarLet (dec, optmap collect dims, collect value, Depth depth :: ann)
 
     | Allocate (dec, dims, ann) ->
-      let (dec, dec_depth) = check_in_scope (Varname (nameof dec)) node scope in
+      let (dec, dec_depth) = check_in_scope (Varname (nameof dec)) node scope err in
       Allocate (dec, List.map collect dims, Depth depth :: ann)
 
     | _ -> traverse_unit collect node
@@ -113,7 +114,7 @@ let rec analyse scope depth node =
       let (vars, funs) = scope in
       let local_scope = (Hashtbl.copy vars, Hashtbl.copy funs) in
       let params = List.map (traverse local_scope (depth + 1)) params in
-      let body = analyse local_scope (depth + 1) body in
+      let body = analyse local_scope (depth + 1) body err in
       FunDef (export, ret_type, name, params, body, ann)
 
     | Param (ArrayDims (ctype, dims), name, ann) ->
@@ -159,8 +160,10 @@ let rec analyse scope depth node =
 
 let analyse_context program =
   let scope = (Hashtbl.create 20, Hashtbl.create 20) in
-  analyse scope 0 program
+  let err = ref [] in
+  let node = analyse scope 0 program err in
+  quit_on_error node !err
 
 let phase = function
   | Ast node -> Ast (analyse_context node)
-  | _ -> raise (InvalidInput "context analysis")
+  | _ -> raise InvalidInput

+ 2 - 2
phases/desug.ml

@@ -269,7 +269,7 @@ let rec array_init = function
           "dimension mismatch: expected %d nesting levels, got %d"
           ndims depth
         in
-        raise (NodeError (node, msg))
+        raise (FatalError (NodeMsg (node, msg)))
     in
     Block (List.rev (traverse 0 [] value))
 
@@ -311,4 +311,4 @@ let phase = function
      * transform all for-loops to while-loops afterwards *)
     Ast (for_to_while (array_init (node)))
 
-  | _ -> raise (InvalidInput "desugar")
+  | _ -> raise InvalidInput

+ 1 - 1
phases/dimreduce.ml

@@ -128,4 +128,4 @@ and dim_reduce depth = function
 
 let phase = function
   | Ast node -> Ast (dim_reduce 0 (expand_dims node))
-  | _ -> raise (InvalidInput "dimension reduction")
+  | _ -> raise InvalidInput

+ 1 - 1
phases/extern.ml

@@ -143,4 +143,4 @@ let phase = function
     let globals = Hashtbl.create 20 in
     let node = create_funcs globals node in
     Ast (replace_vars globals 0 node)
-  | _ -> raise (InvalidInput "extern vars")
+  | _ -> raise InvalidInput

+ 1 - 1
phases/index.ml

@@ -80,4 +80,4 @@ let phase = function
   | Ast node ->
     let tagged = tag_index (strip_context node) in
     Ast (Context.analyse_context tagged)
-  | _ -> raise (InvalidInput "index analysis")
+  | _ -> raise InvalidInput

+ 1 - 1
phases/load.ml

@@ -63,4 +63,4 @@ let phase = function
       in
       FileContent (display_name, content)
 
-  | _ -> raise (InvalidInput "load")
+  | _ -> raise InvalidInput

+ 1 - 1
phases/output.ml

@@ -13,4 +13,4 @@ let phase = function
       Print.print_assembly stdout instrs
     end;
     Empty
-  | _ -> raise (InvalidInput "output")
+  | _ -> raise InvalidInput

+ 4 - 4
phases/parse.ml

@@ -13,9 +13,9 @@ let shift_back lexbuf = shift_loc (get_loc lexbuf) 0 (-1)
 let parse_with_error lexbuf =
   try Some (Parser.program Lexer.token lexbuf) with
   | Lexer.SyntaxError msg ->
-    raise (LocError ((shift_back lexbuf), msg))
+    raise (FatalError (LocMsg (shift_back lexbuf, msg)))
   | Parser.Error ->
-    raise (LocError ((shift_back lexbuf), "syntax error"))
+    raise (FatalError (LocMsg (shift_back lexbuf, "syntax error")))
 
 let phase = function
   | FileContent (display_name, content) ->
@@ -23,7 +23,7 @@ let phase = function
     lexbuf.lex_curr_p <- { lexbuf.lex_curr_p with pos_fname = display_name };
     let ast = parse_with_error lexbuf in
     begin match ast with
-      | None -> raise (CompileError "no syntax tree was constructed")
+      | None -> raise (FatalError (Msg "no syntax tree was constructed"))
       | Some node -> Ast node
     end
-  | _ -> raise (InvalidInput "parse")
+  | _ -> raise InvalidInput

+ 1 - 1
phases/peephole.ml

@@ -92,4 +92,4 @@ let phase = function
       oldcount newcount (oldcount - newcount)
     );
     Assembly instrs
-  | _ -> raise (InvalidInput "peephole")
+  | _ -> raise InvalidInput

+ 1 - 1
phases/print.ml

@@ -21,7 +21,7 @@ let op2str = function
   | Le  -> "le"
   | Gt  -> "gt"
   | Ge  -> "ge"
-  | _ -> raise (CompileError ("operator unsupported by VM"))
+  | _ -> raise (FatalError (Msg "operator unsupported by VM"))
 
 let prefix = function
   | Bool _  -> "b"

+ 14 - 12
phases/typecheck.ml

@@ -19,6 +19,8 @@ open Types
 open Util
 open Stringify
 
+let node_error (node, msg) = FatalError (NodeMsg (node, msg))
+
 (* Stringify a list of types for use in error messages.
  * ctype list -> string *)
 let rec types2str = function
@@ -48,7 +50,7 @@ let check_type ?(msg="") expected node =
             (type2str_error expected) (type2str_error got)
     | _ -> msg
     in
-    raise (NodeError (node, msg))
+    raise (node_error (node, msg))
   end
 
 let op_types = function
@@ -69,7 +71,7 @@ let check_type_op allowed_types desc node =
       "%s cannot be applied to type %s, only to %s"
       desc (type2str got) (types2str allowed_types)
     in
-    raise (NodeError (node, msg))
+    raise (node_error (node, msg))
   ); ()
 
 let check_dims_match dims dec_type errnode =
@@ -78,7 +80,7 @@ let check_dims_match dims dec_type errnode =
     let msg = sprintf
       "dimension mismatch: expected %d indices, got %d" expected got
     in
-    raise (NodeError (errnode, msg))
+    raise (node_error (errnode, msg))
   | _ -> ()
 
 let rec typecheck node =
@@ -97,7 +99,7 @@ let rec typecheck node =
           "function \"%s\" expects %d arguments, got %d"
           name nparams nargs
         in
-        raise (NodeError (node, msg))
+        raise (node_error (node, msg))
       | _ ->
         let args = List.map typecheck args in
         let check_arg_type arg param =
@@ -173,21 +175,21 @@ let rec typecheck node =
     begin
       match (ret_type, find_return (block_body body)) with
       | (Void, Some (ret, _)) ->
-        raise (NodeError (ret, "void function should not have a return value"))
+        raise (node_error (ret, "void function should not have a return value"))
 
       | ((Bool | Int | Float), None) ->
         let msg = sprintf
           "expected return value of type %s for function \"%s\""
           (type2str ret_type) name
         in
-        raise (NodeError (node, msg))
+        raise (node_error (node, msg))
 
       | ((Bool | Int | Float), Some (ret, t)) when t != ret_type ->
         let msg = sprintf
           "function \"%s\" has return type %s, got %s"
           name (type2str ret_type) (type2str t)
         in
-        raise (NodeError (ret, msg))
+        raise (node_error (ret, msg))
 
       | _ -> FunDef (export, ret_type, name, params, body, ann)
     end
@@ -212,7 +214,7 @@ let rec typecheck node =
     let min = Nativeint.of_int32 Int32.min_int in
     let max = Nativeint.of_int32 Int32.max_int in
     if cmpval < min || cmpval > max then (
-      raise (NodeError (node, "integer value out of range (signed 32-bit)"))
+      raise (node_error (node, "integer value out of range (signed 32-bit)"))
     );
     Const (IntVal value, Type Int :: ann)
   | Const (FloatVal value, ann) ->
@@ -221,7 +223,7 @@ let rec typecheck node =
   (* Extern arrays variables are transformed to imported functions, so the
    * pointer cannot be passed *)
   | VarUse (GlobalDec (ArrayDims _, _, _), None, _) ->
-    raise (NodeError (node, "imported array pointers may only be \
+    raise (node_error (node, "imported array pointers may only be \
                              dereferenced, not used directly"))
 
   (* Variables inherit the type of their declaration *)
@@ -238,7 +240,7 @@ let rec typecheck node =
   (* Array pointers cannot be re-assigned, because array dimension reduction
    * makes assumptions about dimensions of an array *)
   | VarLet (dec, None, _, _) when is_array dec ->
-    raise (NodeError (node, "cannot assign value to array pointer after \
+    raise (node_error (node, "cannot assign value to array pointer after \
                              initialisation"))
 
   (* Assigned values must match variable declaration *)
@@ -262,11 +264,11 @@ let rec typecheck node =
   (* ArrayConst initialisations are transformed during desugaring, so any
    * occurrences that are left are illegal *)
   | ArrayConst _ ->
-    raise (NodeError (node, "array constants can only be used in array \
+    raise (node_error (node, "array constants can only be used in array \
                              initialisation"))
 
   | _ -> traverse_unit typecheck node
 
 let phase = function
   | Ast node -> Ast (typecheck node)
-  | _ -> raise (InvalidInput "typecheck")
+  | _ -> raise InvalidInput

+ 1 - 1
phases/unroll.ml

@@ -89,4 +89,4 @@ let phase = function
     let node = unroll counters node in
     let node = prune_vardecs counters node in
     Ast (Constprop.propagate_consts node)
-  | _ -> raise (InvalidInput "loop unrolling")
+  | _ -> raise InvalidInput

+ 16 - 13
types.mli

@@ -229,24 +229,27 @@ type args_record = {
 
 (** {2 Exceptions} *)
 
-(** Error occurred at a certain location in an input file. Used in combination
-    with {!Util.prerr_loc} and {!Util.prerr_loc_msg}. *)
-exception LocError of location * string
+type error_msg =
+  (** General compilation error message (caught by main function). *)
+  | Msg of string
 
-(** Error occurred at a certain AST node, to be transformed to {!LocError} using
-    {!Util.locof}. *)
-exception NodeError of node * string
+  (** Error occurred at a certain location in an input file. Used in combination
+      with {!Util.prerr_loc} and {!Util.prerr_loc_msg}. *)
+  | LocMsg of location * string
 
-(** General compilation error message (caught by main function). *)
-exception CompileError of string
+  (** Error occurred at a certain AST node, use {!Util.locof} to get node
+      location. *)
+  | NodeMsg of node * string
 
-(** Error without message, just makes the compiler fail with non-zero return
-    value. *)
-exception EmptyError
+  (** No error message, just quit *)
+  | NoMsg
+
+(** Raised error message. Makes the compiler print the message and quit with
+    status code 1. *)
+exception FatalError of error_msg
 
 (** Catch-all error for traversals that accept a limit set of node types. *)
 exception InvalidNode
 
 (** Error raised when a phase receives an unsupported {!intermediate} type. *)
-exception InvalidInput of string
-
+exception InvalidInput

+ 23 - 0
util.ml

@@ -520,3 +520,26 @@ let node_error node msg =
 
 let node_warning node msg =
   prerr_loc_msg (locof node) ("Warning: " ^ msg)
+
+let print_error = function
+  | Msg msg ->
+    eprintf "Error: %s\n" msg;
+
+  | LocMsg (loc, msg) ->
+    prerr_loc_msg loc ("Error: " ^ msg)
+
+  | NodeMsg (node, msg) ->
+    (* If no location is given, just stringify the node to at least give
+      * some information *)
+    let msg = if locof node = noloc then
+      msg ^ "\nnode: " ^ Stringify.node2str node
+    else msg in
+    node_error node msg
+
+  | NoMsg -> ()
+
+let quit_on_error node = function
+  | [] -> node
+  | errors ->
+    List.iter print_error errors;
+    raise (FatalError NoMsg)

+ 7 - 0
util.mli

@@ -170,6 +170,13 @@ val node_error : node -> string -> unit
 (** Print a warning message for a node. Calls {!prerr_loc_msg}. *)
 val node_warning : node -> string -> unit
 
+(** Print an error message of type {!Types.error_msg}. *)
+val print_error : error_msg -> unit
+
+(** Raise a {!Types.FatalError} if the given error list is not empty, and
+    print the errors before quitting. *)
+val quit_on_error : node -> error_msg list -> node
+
 (** {2 String utilities} *)
 
 (** [repeat s n] returns a new string of [n] times [s]. *)