diff --git a/kazm/ast.ml b/kazm/ast.ml index 91120df..dce7571 100644 --- a/kazm/ast.ml +++ b/kazm/ast.ml @@ -28,12 +28,12 @@ type stmt = | While of expr * stmt | Break | EmptyReturn + | Initialize of bind * expr option type func_decl = { typ : typ; fname : string; formals : bind list; - locals : bind list; body : stmt list; } @@ -104,7 +104,6 @@ let string_of_fdecl fdecl = string_of_typ fdecl.typ ^ " " ^ fdecl.fname ^ "(" ^ String.concat ", " (List.map snd fdecl.formals) ^ ")\n{\n" ^ - String.concat "" (List.map string_of_vdecl fdecl.locals) ^ String.concat "" (List.map string_of_stmt fdecl.body) ^ "}\n" diff --git a/kazm/checker.ml b/kazm/checker.ml index b9aa886..e3717dd 100644 --- a/kazm/checker.ml +++ b/kazm/checker.ml @@ -34,12 +34,12 @@ let check (globals, functions) = typ = Void; fname = name; formals = [(ty, "x")]; - locals = []; body = [] } map + body = [] } map in let smap = StringMap.add "next_int" { typ = Int; fname = "next_int"; formals = []; - locals = []; body = [] } StringMap.empty + body = [] } StringMap.empty in List.fold_left add_bind smap [ ("print", String); ("println", String); ("int_print", Int); @@ -73,9 +73,9 @@ let check (globals, functions) = let _ = find_func "main" in (* Ensure "main" is defined *) let check_function func = - (* Make sure no formals or locals are void or duplicates *) + (* Make sure no formals are void or duplicates *) check_binds "formal" func.formals; - check_binds "local" func.locals; + (* check_binds "local" func.locals; *) (* Raise an exception if the given rvalue type cannot be assigned to the given lvalue type *) @@ -85,32 +85,34 @@ let check (globals, functions) = (* Build local symbol table of variables for this function *) let symbols = List.fold_left (fun m (ty, name) -> StringMap.add name ty m) - StringMap.empty (globals @ func.formals @ func.locals ) + StringMap.empty (globals @ func.formals ) in (* Return a variable from our local symbol table *) - let type_of_identifier s = - try StringMap.find s symbols - with Not_found -> raise (Failure ("undeclared identifier " ^ s)) + let rec type_of_identifier s locals = + try StringMap.find s locals + with Not_found -> try StringMap.find s symbols + with Not_found -> raise (Failure ("undeclared identifier " ^ s)) in (* Return a semantically-checked expression, i.e., with a type *) - let rec expr = function + let rec expr e locals = + match e with Literal l -> (Int, SLiteral l) | Dliteral l -> (Double, SDliteral l) | BoolLit l -> (Bool, SBoolLit l) | CharLit c -> (Char, SCharLit c) | StringLit s -> (String, SStringLit s) | Noexpr -> (Void, SNoexpr) - | Id s -> (type_of_identifier s, SId s) + | Id s -> (type_of_identifier s locals, SId s) | Assign(var, e) as ex -> - let lt = type_of_identifier var - and (rt, e') = expr e in + let lt = type_of_identifier var locals + and (rt, e') = expr e locals in let err = "illegal assignment " ^ string_of_typ lt ^ " = " ^ string_of_typ rt ^ " in " ^ string_of_expr ex in (check_assign lt rt err, SAssign(var, (rt, e'))) | Unop(op, e) as ex -> - let (t, e') = expr e in + let (t, e') = expr e locals in let ty = match op with Neg when t = Int || t = Float -> t | Not when t = Bool -> Bool @@ -119,8 +121,8 @@ let check (globals, functions) = " in " ^ string_of_expr ex)) in (ty, SUnop(op, (t, e'))) | Binop(e1, op, e2) as e -> - let (t1, e1') = expr e1 - and (t2, e2') = expr e2 in + let (t1, e1') = expr e1 locals + and (t2, e2') = expr e2 locals in (* All binary operators require operands of the same type *) let same = t1 = t2 in (* Determine expression type based on operator and operand types *) @@ -143,7 +145,7 @@ let check (globals, functions) = raise (Failure ("expecting " ^ string_of_int param_length ^ " arguments in " ^ string_of_expr call)) else let check_call (ft, _) e = - let (et, e') = expr e in + let (et, e') = expr e locals in let err = "illegal argument found " ^ string_of_typ et ^ " expected " ^ string_of_typ ft ^ " in " ^ string_of_expr e in (check_assign ft et err, e') @@ -152,22 +154,25 @@ let check (globals, functions) = in (fd.typ, SCall(fname, args')) in - let check_bool_expr e = - let (t', e') = expr e + let check_bool_expr e locals = + let (t', e') = expr e locals and err = "expected Boolean expression in " ^ string_of_expr e in if t' != Bool then raise (Failure err) else (t', e') in (* Return a semantically-checked statement i.e. containing sexprs *) - let rec check_stmt = function - Expr e -> SExpr (expr e) - | If(p, b1, b2) -> SIf(check_bool_expr p, check_stmt b1, check_stmt b2) + let rec check_stmt stmt locals = + match stmt with + Expr e -> SExpr (expr e locals) + | Initialize (bd, None) -> raise (Failure ("Initialize stmts are inside Block.")) + | Initialize (bd, Some e) -> raise (Failure ("Initialize stmts are inside Block.")) + | If(p, b1, b2) -> SIf(check_bool_expr p locals, check_stmt b1 locals, check_stmt b2 locals) | For(e1, e2, e3, st) -> - SFor(expr e1, check_bool_expr e2, expr e3, check_stmt st) - | While(p, s) -> SWhile(check_bool_expr p, check_stmt s) + SFor(expr e1 locals, check_bool_expr e2 locals, expr e3 locals, check_stmt st locals) + | While(p, s) -> SWhile(check_bool_expr p locals, check_stmt s locals) | EmptyReturn -> SEmptyReturn | Break -> SBreak - | Return e -> let (t, e') = expr e in + | Return e -> let (t, e') = expr e locals in if t = func.typ then SReturn (t, e') else raise ( Failure ("return gives " ^ string_of_typ t ^ " expected " ^ @@ -176,20 +181,31 @@ let check (globals, functions) = (* A block is correct if each statement is correct and nothing follows any Return statement. Nested blocks are flattened. *) | Block sl -> - let rec check_stmt_list = function - [Return _ as s] -> [check_stmt s] + let rec check_stmt_list stmts locals = + match stmts with + [Return _ as s] -> [check_stmt s locals] + | Initialize (bd, None) :: ss -> + let (typ, name) = bd in + if StringMap.mem name locals = true + then raise (Failure ("cannot initialize " ^ name ^ " twice")) + else SInitialize(bd, None) :: check_stmt_list ss (StringMap.add name typ locals) + | Initialize (bd, Some e) :: ss -> + let (typ, name) = bd in + let se = expr e locals in + if StringMap.mem name locals = true + then raise (Failure ("cannot initialize " ^ name ^ " twice")) + else SInitialize(bd, Some se) :: check_stmt_list ss (StringMap.add name typ locals) | Return _ :: _ -> raise (Failure "nothing may follow a return") - | Block sl :: ss -> check_stmt_list (sl @ ss) (* Flatten blocks *) - | s :: ss -> check_stmt s :: check_stmt_list ss + | Block sl :: ss -> check_stmt_list (sl @ ss) locals (* Flatten blocks *) + | s :: ss -> check_stmt s locals :: check_stmt_list ss locals | [] -> [] - in SBlock(check_stmt_list sl) + in SBlock(check_stmt_list sl locals) in (* body of check_function *) { styp = func.typ; sfname = func.fname; sformals = func.formals; - slocals = func.locals; - sbody = match check_stmt (Block func.body) with + sbody = match check_stmt (Block func.body) StringMap.empty with SBlock(sl) -> sl | _ -> raise (Failure ("internal error: block didn't become a block?")) } diff --git a/kazm/codegen.ml b/kazm/codegen.ml index dcfbc80..0c64b16 100644 --- a/kazm/codegen.ml +++ b/kazm/codegen.ml @@ -90,8 +90,13 @@ let gen (bind_list, sfunction_decls) = let add_var scope name l = let Scope(p, map) = scope in - let map' = SMap.add name l map in - Scope(p, map') + Scope(p, SMap.add name l map) + in + + let initialize_var ctx vtyp name = + let Ctx(builder, scope) = ctx in + let var = L.build_alloca (typ_to_t vtyp) name builder in + Ctx(builder, add_var scope name var) in (* Codegen for an expression *) @@ -160,7 +165,6 @@ let gen (bind_list, sfunction_decls) = let typ = func.styp in let name = func.sfname in let formals = func.sformals in - let locals = func.slocals in (* Defines the func *) let fn = SMap.find name all_funcs in @@ -171,7 +175,7 @@ let gen (bind_list, sfunction_decls) = match stmt with (* For expressions we just codegen the expression *) SExpr(e) -> - let (ctx', ex) = codegen_expr ctx e in + let (ctx', _) = codegen_expr ctx e in ctx' | SEmptyReturn -> build_default_return typ ctx | SReturn(expr) -> @@ -248,6 +252,16 @@ let gen (bind_list, sfunction_decls) = Ctx(end_builder, sp) (* For a block of statements, just fold *) | SBlock(stmts) -> List.fold_left codegen_stmt ctx stmts + | SInitialize((vtyp, name), None) -> + initialize_var ctx vtyp name + | SInitialize((vtyp, name), Some e) -> + (* Evaluate expression first *) + let (ctx', e') = codegen_expr ctx e in + (* Then initialize the var *) + let Ctx(builder, sp) = initialize_var ctx vtyp name in + (* Finally store *) + ignore (L.build_store e' (find_var sp name) builder); + Ctx(builder, sp) in let fn_builder = L.builder_at_end context (L.entry_block fn) in @@ -261,12 +275,8 @@ let gen (bind_list, sfunction_decls) = in let vars = List.fold_left2 add_param vars formals (Array.to_list (L.params fn)) in let fn_scope = Scope(None, vars) in - let initialize_var scope (vtyp, name) = - let var = L.build_alloca (typ_to_t vtyp) name fn_builder in - add_var scope name var - in - let fn_scope' = List.fold_left initialize_var fn_scope locals in - let fn_ctx = Ctx(fn_builder, fn_scope') in + (* let fn_scope' = List.fold_left initialize_var fn_scope locals in *) + let fn_ctx = Ctx(fn_builder, fn_scope) in (* Build all statements *) let ctx' = codegen_stmt fn_ctx (SBlock body) in ignore (add_terminator ctx' (build_default_return typ)) diff --git a/kazm/parser.mly b/kazm/parser.mly index 97a57a5..e1c0299 100644 --- a/kazm/parser.mly +++ b/kazm/parser.mly @@ -47,16 +47,14 @@ program: decls: /* nothing */ { ([], []) } - | decls var_decl { ((fst $1 @ [$2]), snd $1) } | decls fdecl { (fst $1, (snd $1 @ [$2])) } fdecl: - typ IDENTIFIER PAREN_L formals_opt PAREN_R BRACE_L var_decls stmts BRACE_R + typ IDENTIFIER PAREN_L formals_opt PAREN_R BRACE_L stmts BRACE_R { { typ = $1; fname = $2; formals = List.rev $4; - locals = List.rev $7; - body = List.rev $8 } } + body = List.rev $7 } } formals_opt: /* nothing */ { [] } @@ -74,13 +72,6 @@ typ: | DOUBLE { Double } | STRING { String } -var_decls: - { [] } - | var_decls var_decl { $2 :: $1 } - -var_decl: - typ IDENTIFIER SEMI { ($1, $2) } - stmts: { [] } | stmts stmt { $2::$1 } @@ -89,6 +80,7 @@ stmt: expr SEMI { Expr $1 } | return_stmt SEMI { $1 } | break_stmt SEMI { $1 } + | var_decl SEMI { $1 } | if_stmt { $1 } | while_stmt { $1 } | for_stmt { $1 } @@ -113,6 +105,10 @@ while_stmt: for_stmt: FOR PAREN_L expr SEMI expr SEMI expr PAREN_R BRACE_L stmts BRACE_R { For($3, $5, $7, Block(List.rev $10)) } +var_decl: + typ IDENTIFIER { Initialize(($1, $2), None) } + | typ IDENTIFIER ASSIGN expr { Initialize(($1, $2), Some $4) } + expr: INT_LITERAL { Literal($1) } | STRING_LITERAL { StringLit($1) } diff --git a/kazm/sast.ml b/kazm/sast.ml index cecebc3..0abd3ec 100644 --- a/kazm/sast.ml +++ b/kazm/sast.ml @@ -25,12 +25,12 @@ type sstmt = | SWhile of sexpr * sstmt | SBreak | SEmptyReturn + | SInitialize of bind * sexpr option type sfunc_decl = { styp : typ; sfname : string; sformals : bind list; - slocals : bind list; sbody : sstmt list; } @@ -76,7 +76,6 @@ let string_of_sfdecl fdecl = string_of_typ fdecl.styp ^ " " ^ fdecl.sfname ^ "(" ^ String.concat ", " (List.map snd fdecl.sformals) ^ ")\n{\n" ^ - String.concat "" (List.map string_of_vdecl fdecl.slocals) ^ String.concat "" (List.map string_of_sstmt fdecl.sbody) ^ "}\n" diff --git a/kazm/tests/initialize_after_start.kazm.disable b/kazm/tests/initialize_after_start.kazm similarity index 100% rename from kazm/tests/initialize_after_start.kazm.disable rename to kazm/tests/initialize_after_start.kazm diff --git a/kazm/tests/simple_initialization.kazm.disable b/kazm/tests/simple_initialization.kazm similarity index 100% rename from kazm/tests/simple_initialization.kazm.disable rename to kazm/tests/simple_initialization.kazm