Bläddra i källkod

Implemented most of typechecking, some bugfixes in other phases

Taddeus Kroes 12 år sedan
förälder
incheckning
96ecd75efb
10 ändrade filer med 217 tillägg och 47 borttagningar
  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