Upload
bertha-allison
View
222
Download
0
Tags:
Embed Size (px)
Citation preview
Lesson 3
• Two topics 1. Semantic Analysis
2. Translation to intermediate code
• Presented as separated topics but can be implemented together.
Symbols and symbol tables
• Given code implements structure Symbol.Symbol.symbol : string -> Symbol.symbol
Symbol.name : Symbol.symbol -> string
empty :'a table
enter :'a table * symbol * 'a -> 'a table
look :'a table * symbol -> 'a option
Symbols and symbol tables (2)
• When to convert a string to a symbol?
• What to put in the symbol table?
Depends on the use of the symbol table. For type checking one could associate:
• Type of the symbol.
• Nesting level
with each symbol.
Semantic Analysis
• The goal of the semantic analysis is to determine whether a program is semantically correct.
• That is, find errors that has to do with the meaning of the program (as opposed to the syntax of the program).
• For example, check that the program is correctly typed, e.g. don’t let the programmer write:
True + 42;
Semantic Analysis in Bar (1)
• What to detect.– Undefined symbols– Multiply defined symbols– Type errors– etc... (see definition and example files.)
• How to detect it.– By recursion over the Absyn structure.
Semantic Analysis in Bar (2)
Example: MINI-Float Goal: To detect undefined variables.Absyn: datatype expr =
FloatE of real | IdE of Symbol.symbol * pos | AddE of expr * expr * pos datatype stmnt = AssS of Symbol.symbol * expr * pos | PrintS of expr * pos datatype prog = BarProg of stmnt list
structure Env = struct
structure S = Symbol
type environment = {ok:bool, table: bool S.table}
val empty = {ok=true, table=S.empty}
fun enter({ok,table}, id) = {ok=ok, table = S.enter(table,id,true)}
fun set_ok({table,ok}:environment,newok) = {ok=ok andalso newok, table=table}
fun isok({ok,...}:environment) = ok
fun isDefined(id, {ok,table}) = case S.look(table, id) of SOME _ => true | NONE => false
end
Environment for semantic analysis of MINI-Float
fun msg_undef (id, pos) = (ErrorMsg.error pos ("Variable " ^ S.name(id) ^ " not defined."); false)
fun expr(A.FloatE(f), env) = true | expr(A.IdE(id, pos), env) = Env.isDefined(id, env) orelse (msg_undef (id,pos)) | expr(A.AddE(e1, e2, pos), env) = expr(e1, env) andalso expr(e2, env)
fun stmnt(A.AssS(id, e, pos), env) = Env.set_ok(Env.enter(env, id), expr(e, env))
| stmnt(A.PrintS(e, pos), env) =Env.set_ok(env, expr(e, env))
fun program(A.BarProg(statements)) =Env.isok(foldl stmnt Env.empty statements)
Semantic analysis of MINI-Float
Notes on semantic analysis of Bar
• The analysis of Bar will be much more complex.
• The type of expressions needs to be returned.
• Nested scopes needs to be handled.
• Return types of functions need to be handled.
• One-pass is OK, two-pass is optional.
Translation to intermediate code
• Turn the abstract synatax tree into a tree representing the instructions to be executed by the program.
• Also solved by recursion over the Absyn structure.
Given code:
• IR-Tree: The tree structure you are to translate to.
• Frames:A signature for the frame structure is given.(To implement the frame structure you will have to read up about calling conventions in the SPIM manual.)
• Backend:Names of runtime functions (such as PRINTBOOL) are given.
datatype etype = INT | FLOAT (* Expresion types *)datatype cond = LT | LE | EQ | NE | GE | GTdatatype unop = ILOAD (* load integer ( *expr ) *) | FLOAD (* load float *) | IMOVE of temp (* reg := expr *) | FMOVE of ftemp (* freg := expr *) | ITOF of ftemp (* freg := integer-expr *)datatype binop = IADD | ISUB | IMUL | IDIV (* int * int -> int *) | FADD | FSUB | FMUL | FDIV (* float * float -> float *) | AND | OR (* int * int -> int *) | ICMP of cond (* int * int -> int *) | FCMP of cond (* float * float -> int *) | ISTORE (* MEM[exp1] := exp2 *) | FSTORE (* MEM[exp1] := exp2 *) | RNGCHK (* if (0 <= exp1 < exp2) then exp1 else error *)datatype exp = TEMP of temp (* Temporary value in reg *) | FTEMP of ftemp (* Temporary value in reg *) | ICON of int (* Integer constant *)
| UNARY of unop * exp | BINARY of binop * exp * exp | LABREF of label | CALL of etype * label * ((etype * exp) list)
IR Tree (1)
datatype insn = LABDEF of label (* Label *) | JUMP of label (* GOTO *) | CJUMP of exp * label (* Conditional branch e=1 *) | IGNORE of etype * exp (* Expression used for
side-effects only *)
datatype dec = PROC of (* Function and procs *) {label: label, localsSize: int, insns: insn list} | DATA of (* Uninitialised data *) {label: label, size: int} | FCONST of (* Initialised fp-data *) {label: label, float: real}
datatype program = PROGRAM of dec list
IR Tree (2)
structure Env = struct type bindings = I.label type environment = {env: bindings S.table, globals: I.dec list}
val empty = {env = S.empty, globals=[]}:environment
fun enter_gvar({env, globals}, name, loc) = {env = S.enter(env, name, loc), globals= (I.DATA{label = loc, size = 2})::globals}:environment fun enter_float({env, globals}, f, lab) = {env = env, globals = (I.FCONST{label = lab, float = f}) ::globals}:environment
fun var({env,...}:environment, id) = case S.look(env, id) of
SOME loc => loc | _ => ErrorMsg.impossible("Trying to ref a nonvar.")
fun globals({globals,...}:environment) = globalsend
Environment for translation to IR of MINI-Float
fun expr2icode(A.FloatE(f), env) = let
val name = R.new_label() in
(Env.enter_float(env,f,name), I.UNARY(I.FLOAD, I.LABREF name)) end
| expr2icode(A.IdE(id,pos),env) = (env,I.UNARY(I.FLOAD,
I.LABREF(Env.var(env,id)))) | expr2icode(A.AddE(e1,e2,pos),env) =
let val (env1,icode1) = expr2icode(e1,env) val (env2,icode2) = expr2icode(e2,env1)
in (env2, I.BINARY(I.FADD, icode1, icode2))
end
Translation of expressions in MINI-Float
fun stmnt2icode(A.AssS(id, e, pos), env) = let val (env1, expricode) = expr2icode(e, env) val lab = R.varLabel id val newenv = Env.enter_gvar(env1, id, lab) in
(newenv, I.IGNORE(I.FLOAT, I.BINARY(I.FSTORE,
I.LABREF lab, expricode)))
end | stmnt2icode(A.PrintS(e, pos),env) = let val (env1,expricode) = expr2icode(e, env) in
(env1, I.IGNORE(I.INT, I.CALL(I.INT, R.labPrintFloat,[(I.FLOAT,expricode)])))
endand stmnts2icode(s::statements, env) = let val (newenv,icode) = stmnt2icode(s,env) val (finalenv,moreicode) = stmnts2icode(statements,newenv) in (finalenv, icode::moreicode) end| stmnts2icode([],env) = (env,[])
Translation of statements in MINI-Float
fun syn2icode(A.BarProg(stmnts)) = let val (env, insns) = stmnts2icode(stmnts, Env.empty) in
I.PROGRAM(I.PROC{label=R.labMain, localsSize=0, insns=insns}::Env.globals(env))
end
Translation of programs in MINI-Float
Translation to intermediate code
• The Bar language will be much more complicated.
• Function calls has to be handled.
• Locations of parameters, local-, static- and globalvaraibles has to be handled.
• Conversions between int and floats has to be handled.
The frame siganture (1)val wordSize : int
(* Locations for formals, locals, and the return value.*)type temp = inttype ftemp = intdatatype vtype = INT | FLOAT
datatype location = TEMP of temp | FTEMP of ftemp | OFF of temp * int
val RV : locationval FRV : location
The frame siganture (2)
(* Locations of formal parameters. *)type formalsval initFormals : unit -> formalsval nextFormal : formals * vtype -> formals * locationval formalsStackNeed : formals -> int
(* Locations of local variables. *)type localsval initLocals : unit -> localsval nextScalar : locals * vtype -> locals * locationval nextArray : locals * vtype * int -> locals * location
Runtime supportfun procLabel id = Symbol.symbol("P_" ^ (Symbol.name id))fun varLabel id = Symbol.symbol("V_" ^ (Symbol.name id))
val labMain = Symbol.symbol "main"val labPrintInt = Symbol.symbol "PRINTINT"val labPrintBool = Symbol.symbol "PRINTBOOL"val labPrintFloat = Symbol.symbol "PRINTFLOAT"val labTrunc = Symbol.symbol "TRUNC"val labRead = Symbol.symbol "READINT"val labReadBool = Symbol.symbol "READBOOL"val labReadFloat = Symbol.symbol "READFLOAT"val labRangeError = Symbol.symbol "RANGEERROR"local val labnr = ref 0 in fun new_label () = (labnr := !labnr + 1; Symbol.symbol("Label_" ^ (Int.toString (!labnr))))end