Browse Source

Implemented most of typechecking, some bugfixes in other phases

Taddeus Kroes 12 năm trước cách đây
mục cha
commit
96ecd75efb
10 tập tin đã thay đổi với 217 bổ sung47 xóa
  1. 2 2
      Makefile
  2. 13 12
      ast.ml
  3. 1 1
      main.ml
  4. 13 7
      phases/context_analysis.ml
  5. 0 4
      phases/desug.ml
  6. 148 0
      phases/typecheck.ml
  7. 26 20
      stringify.ml
  8. 6 0
      stringify.mli
  9. 5 1
      util.ml
  10. 3 0
      util.mli

+ 2 - 2
Makefile

@@ -2,9 +2,9 @@ RESULT := civicc
 SOURCES := ast.ml util.mli util.ml lexer.mll parser.mly stringify.mli \
 	stringify.ml \
 	phases/load.ml phases/parse.ml phases/print.ml phases/desug.ml \
-	phases/context_analysis.ml phases/dim_reduce.ml \
+	phases/context_analysis.ml phases/typecheck.ml phases/dim_reduce.ml \
 	main.ml
-PRE_TARGETS := ast.cmi util.cmi util.o
+PRE_TARGETS := ast.cmi ast.o util.cmi util.o
 LIBS := str unix
 
 OCAMLFLAGS := -g

+ 13 - 12
ast.ml

@@ -1,10 +1,10 @@
 type loc = string * int * int * int * int
 let noloc = ("", 0, 0, 0, 0)
 
-type monop = Neg | Not
-type binop = Add | Sub | Mul | Div | Mod
-           | Eq | Ne | Lt | Le | Gt | Ge
-           | And | Or
+type operator = Neg | Not
+              | Add | Sub | Mul | Div | Mod
+              | Eq | Ne | Lt | Le | Gt | Ge
+              | And | Or
 type ctype = Void | Bool | Int | Float
            | ArrayDec of ctype * node list
            | ArrayDef of ctype * node list
@@ -39,25 +39,27 @@ and node =
     | ArrayScalar of node * loc
     | Var of string * loc
     | Deref of string * node list * loc
-    | Monop of monop * node * loc
-    | Binop of binop * node * node * loc
+    | Monop of operator * node * loc
+    | Binop of operator * node * node * loc
     | Cond of node * node * node * loc
     | TypeCast of ctype * node * loc
     | FunCall of string * node list * loc
+    | Arg of node
 
     (* additional types for convenience in traversals *)
+    | VarLet of node * node * ctype * int
     | VarUse of node * ctype * int
     | FunUse of node * ctype * int
-    | Type of ctype
     | DimDec of node
-    | Arg of node
+    | Type of node * ctype
+    | DummyNode
 
 (* container for command-line arguments *)
 type args = {
-    mutable infile : string option;
+    mutable infile  : string option;
     mutable outfile : string option;
     mutable verbose : int;
-    mutable cpp : bool;
+    mutable cpp     : bool;
 }
 
 (* intermediate representations between phases *)
@@ -99,6 +101,7 @@ exception InvalidInput of string
  *     | DoWhile (cond, body, loc) ->
  *     | For (counter, start, stop, step, body, loc) ->
  *     | Expr (value) ->
+ *     | Block (stats) ->
  *
  *     | BoolConst (value, loc) ->
  *     | IntConst (value, loc) ->
@@ -113,8 +116,6 @@ exception InvalidInput of string
  *     | TypeCast (ctype, value, loc) ->
  *     | FunCall (name, args, loc) ->
  *
- *     | Statements (stats) ->
- *
  *     | node -> transform visit node
  *
  *)

+ 1 - 1
main.ml

@@ -18,8 +18,8 @@ let compile args =
         Desug.phase;
         Print.phase;
         Context_analysis.phase;
-        (*
         Typecheck.phase;
+        (*
         Extern_vars.phase;
         Dim_reduce.phase;
         Print.phase;

+ 13 - 7
phases/context_analysis.ml

@@ -16,8 +16,8 @@ let check_in_scope name errnode scope =
         | Funcname name -> (name, funs, vars, "function")
     in
     match mapfind name tbl with
-    | Some (decl, decl_depth, _) ->
-        (decl, decl_depth)
+    | Some (decl, dec_depth, _) ->
+        (decl, dec_depth)
     | None ->
         let msg = match mapfind name other_map with
             | Some _ -> sprintf "\"%s\" is not a %s" name desired_type
@@ -72,13 +72,19 @@ let rec analyse scope depth args node =
         (* For a variable or function call, look for its declaration in the
          * current scope and save a its type/depth information  *)
         | Var (name, _) ->
-            let (decl, decl_depth) = check_in_scope (Varname name) node scope in
-            VarUse (node, ctypeof decl, depth - decl_depth)
+            let (decl, dec_depth) = check_in_scope (Varname name) node scope in
+            VarUse (node, ctypeof decl, depth - dec_depth)
 
         | FunCall (name, args, loc) ->
-            let (decl, decl_depth) = check_in_scope (Funcname name) node scope in
+            let (decl, dec_depth) = check_in_scope (Funcname name) node scope in
             let node = FunCall (name, transform_all collect args, loc) in
-            FunUse (node, ctypeof decl, depth - decl_depth)
+            FunUse (node, ctypeof decl, depth - dec_depth)
+
+        (* Assign statements are wrapped in VarLet nodes, which stores the type
+         * and depth of the assigned variable are *)
+        | Assign (name, value, loc) ->
+            let (decl, dec_depth) = check_in_scope (Varname name) node scope in
+            VarLet (node, collect value, ctypeof decl, depth - dec_depth)
 
         | _ -> transform_children collect node
     in
@@ -102,7 +108,7 @@ let rec analyse scope depth args node =
             let body = analyse scope depth args body in
             FunDef (export, ret_type, name, params, body, loc)
 
-        | Param (ArrayDec (_, dims) as atype, name, _) as node ->
+        | Param (ArrayDec (_, dims), name, _) as node ->
             let rec add_dims = function
                 | [] -> ()
                 | Dim (name, _) as dim :: tail ->

+ 0 - 4
phases/desug.ml

@@ -1,10 +1,6 @@
 open Ast
 open Util
 
-let block_body = function
-    | Block nodes -> nodes
-    | _ -> raise InvalidNode
-
 let rec replace_var var replacement = function
     | Var (name, loc) when name = var -> Var (replacement, loc)
     | node -> transform_children (replace_var var replacement) node

+ 148 - 0
phases/typecheck.ml

@@ -0,0 +1,148 @@
+open Printf
+open Ast
+open Util
+open Stringify
+
+(*
+ * Do a number of checks:
+ * x A void function must not return a value.
+ * x A non-void function must return a value of the correct type.
+ * - Array indices must be of type integer.
+ * - The number of array indices must match the number of array dimensions.
+ * x The type on the right-hand side of an assignment must match the type on
+ *   the left-hand side.
+ * - The number of arguments used for a function call must match the number of
+ *   parameters for that function.
+ * - The types of the function arguments must match the types of parameters.
+ * x The operands of a unary or binary operation must have valid types.
+ * x The predicate expression of an if, while, or do-while statement must be
+ *   a boolean.
+ * x Only values having a basic type can be type cast.
+ *)
+
+let check_type ?(msg="") expected = function
+    | Type (node, got) when got != expected ->
+        let msg = match msg with
+            | "" -> sprintf "expected type %s, got %s"
+                            (type2str expected) (type2str got)
+            | _ -> msg
+        in
+        raise (NodeError (node, msg))
+    | Type _ -> ()
+    | _ -> raise InvalidNode
+
+let op_types = function
+    | Not | And | Or                      -> [Bool]
+    | Mod                                 -> [Int]
+    | Neg | Sub | Div | Lt | Le | Gt | Ge -> [Int; Float]
+    | Add | Mul | Eq | Ne                 -> [Bool; Int; Float]
+
+let op_result_type operand_type = function
+    | Not | And | Or | Eq | Ne | Lt | Le | Gt | Ge -> Bool
+    | Neg | Add | Sub | Mul | Div | Mod            -> operand_type
+
+(* Check if the given operator can be applied to the given type *)
+let check_type_op allowed_types desc = function
+    | Type (node, ctype) when not (List.mem ctype allowed_types) ->
+        let msg =
+            sprintf "%s cannot be applied to type %s, only to %s"
+                    desc (type2str ctype) (types2str allowed_types)
+        in
+        raise (NodeError (node, msg))
+    | Type _ -> ()
+    | _ -> raise InvalidNode
+
+let rec typecheck node = match node with
+    | BoolConst (value, _)    -> Type (node, Bool)
+    | IntConst (value, _)     -> Type (node, Int)
+    | FloatConst (value, _)   -> Type (node, Float)
+    | VarUse (_, ctype, _)    -> Type (node, ctype)
+    | FunUse (_, ret_type, _) -> Type (node, ret_type)
+
+    | Monop (op, (Type (_, vtype) as value), _) ->
+        let desc = sprintf "unary operator \"%s\"" (op2str op) in
+        check_type_op (op_types op) desc value;
+        Type (node, op_result_type vtype op)
+    | Monop (op, value, loc) ->
+        typecheck (Monop (op, typecheck value, loc))
+
+    | Binop (op, (Type (_, ltype) as left), right, loc) ->
+        let desc = sprintf "binary operator \"%s\"" (op2str op) in
+        check_type_op (op_types op) desc left;
+        check_type ltype right;
+        Type (node, op_result_type ltype op)
+    | Binop (op, left, right, loc) ->
+        typecheck (Binop (op, typecheck left, typecheck right, loc))
+
+    | Cond (Type (cond, condtype), Type (texpr, ttype), fexpr, loc) ->
+        check_type ttype fexpr;
+        Type (node, ttype)
+
+    | VarLet (_, (Type (_, vtype) as value), dec_type, depth) ->
+        check_type dec_type value;
+        Type (node, vtype)
+    | VarLet (assign, value, dec_type, depth) ->
+        typecheck (VarLet (assign, typecheck value, dec_type, depth))
+
+    | TypeCast (ctype, (Type _ as value), loc) ->
+        check_type_op [Bool; Int; Float] "typecast" value;
+        Type (node, ctype)
+    | TypeCast (ctype, value, loc) ->
+        typecheck (TypeCast (ctype, typecheck value, loc))
+
+    | Return (Type _, _) ->
+        node
+    | Return (value, loc) ->
+        typecheck (Return (typecheck value, loc))
+
+    | FunDef (export, ret_type, name, params, body, loc) ->
+        let params = transform_all typecheck params in
+        let body = typecheck body in
+        let rec find_return = function
+            | []                                   -> None
+            | [Return (Type (_, rtype), _) as ret] -> Some (ret, rtype)
+            | hd :: tl                             -> find_return tl
+        in (
+        match (ret_type, find_return (block_body body)) with
+            | (Void, Some (ret, _)) ->
+                raise (NodeError (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))
+            | ((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))
+            | _ ->
+                FunDef (export, ret_type, name, params, body, loc)
+        )
+
+    (* Conditions in if-statements and loop must be type bool *)
+    | If (Type _ as cond, _, _)
+    | IfElse (Type _ as cond, _, _, _)
+    | While (Type _ as cond, _, _)
+    | DoWhile (Type _ as cond, _, _) ->
+        check_type Bool cond (*~msg:"condition should have type bool"*);
+        node
+    | If (cond, body, loc) ->
+        typecheck (If (typecheck cond, typecheck body, loc))
+    | IfElse (cond, tbody, fbody, loc) ->
+        typecheck (IfElse (typecheck cond, typecheck tbody, typecheck fbody, loc))
+    | While (cond, body, loc) ->
+        typecheck (While (typecheck cond, typecheck body, loc))
+    | DoWhile (cond, body, loc) ->
+        typecheck (DoWhile (typecheck cond, typecheck body, loc))
+
+    | _ -> transform_children typecheck node
+
+let rec phase input =
+    prerr_endline "- Type checking";
+    match input with
+    | Ast (node, args) ->
+        Ast (typecheck node, args)
+    | _ -> raise (InvalidInput "typecheck")

+ 26 - 20
stringify.ml

@@ -5,26 +5,23 @@ let tab = "    "
 (* string -> string *)
 let indent = Str.global_replace (Str.regexp "^\\(.\\)") (tab ^ "\\1")
 
-(* monop -> string *)
-let monop2str = function
+(* operator -> string *)
+let op2str = function
     | Neg -> "-"
     | Not -> "!"
-
-(* binop -> string *)
-let binop2str = function
-    | Add -> " + "
-    | Sub -> " - "
-    | Mul -> " * "
-    | Div -> " / "
-    | Mod -> " % "
-    | Eq  -> " == "
-    | Ne  -> " != "
-    | Lt  -> " < "
-    | Le  -> " <= "
-    | Gt  -> " > "
-    | Ge  -> " >= "
-    | And -> " && "
-    | Or  -> " || "
+    | Add -> "+"
+    | Sub -> "-"
+    | Mul -> "*"
+    | Div -> "/"
+    | Mod -> "%"
+    | Eq  -> "=="
+    | Ne  -> "!="
+    | Lt  -> "<"
+    | Le  -> "<="
+    | Gt  -> ">"
+    | Ge  -> ">="
+    | And -> "&&"
+    | Or  -> "||"
 
 (* ctype -> string *)
 let rec type2str = function
@@ -103,8 +100,9 @@ and node2str node =
     | ArrayScalar (value, _) -> str value
     | Var (v, _) -> v
     | Deref (name, dims, _) -> name ^ (str (ArrayConst (dims, noloc)))
-    | Monop (op, opnd, _) -> monop2str op ^ str opnd
-    | Binop (op, left, right, _) -> "(" ^ str left ^ binop2str op ^ str right ^ ")"
+    | Monop (op, opnd, _) -> op2str op ^ str opnd
+    | Binop (op, left, right, _) ->
+        "(" ^ str left ^ " " ^ op2str op ^ " " ^ str right ^ ")"
     | Cond (cond, t, f, _) -> (str cond) ^ " ? " ^ str t ^ " : " ^ str f
     | TypeCast (ctype, value, _) -> "(" ^ type2str ctype ^ ")" ^ str value
     | FunCall (name, args, _) -> name ^ "(" ^ (concat ", " args) ^ ")"
@@ -113,4 +111,12 @@ and node2str node =
     | VarUse (node, _, _)
     | FunUse (node, _, _) -> str node
 
+    | Type (node, ctype) -> str node ^ ":" ^ type2str ctype
+
     | _ -> raise InvalidNode
+
+(* ctype list -> string *)
+let rec types2str = function
+    | [] -> ""
+    | [ctype] -> type2str ctype
+    | ctype :: tail -> type2str ctype ^ " or " ^ (types2str tail)

+ 6 - 0
stringify.mli

@@ -1 +1,7 @@
+val op2str : Ast.operator -> string
+
 val node2str : Ast.node -> string
+
+val type2str : Ast.ctype -> string
+
+val types2str : Ast.ctype list -> string

+ 5 - 1
util.ml

@@ -174,9 +174,13 @@ let ctypeof = function
     | GlobalDec (ctype, _, _)
     | GlobalDef (_, ctype, _, _, _)
     | TypeCast (ctype, _, _)
-    | Type ctype
+    | Type (_, ctype)
         -> ctype
 
     | DimDec _ -> Int
 
     | _ -> raise InvalidNode
+
+let block_body = function
+    | Block nodes -> nodes
+    | _ -> raise InvalidNode

+ 3 - 0
util.mli

@@ -26,3 +26,6 @@ val flatten_blocks : Ast.node list -> Ast.node list
 
 (* Get function / expression type *)
 val ctypeof : Ast.node -> Ast.ctype
+
+(* Extract the node list from a Block node *)
+val block_body : Ast.node -> Ast.node list