program --> program id {curr_scope <- AddEntry(id.lexval, Nil); UpdateType(curr_scope, Program, False)} ; var_decls subprogram_decls compound_statement . var_decls --> var decl_list | epsilon decl_list --> decl_list declaration | declaration declaration --> identifier_list : type ; {UpdateTypeList(identifier_list.l, type.t, True)} identifier_list --> identifier_list1 , id {identifier_list.l <- Cons(AddEntry(id.lexval, curr_scope), identifier_list1.l)} | id {identifier_list.l <- Cons(AddEntry(id.lexval, curr_scope), Nil)} type --> standard_type {type.t <- standard_type.t} | array [ num .. num1 ] of standard_type {type.t <- Array(num.lexval, num1.lexval, standard_type.t)} standard_type --> integer {standard_type.t <- Integer} | real {standard_type.t <- Real} | boolean {standard_type.t <- Boolean} subprogram_decls --> subprogram_decls subprogram_declaration | epsilon subprogram_declaration --> subprogram_head var_decls compound_statement ; {curr_scope <- Delete(curr_scope)} subprogram_head --> function id {curr_scope <- AddEntry(id.lexval, curr_scope)} arguments : standard_type ; {UpdateType(curr_scope, Func(arguments.t, standard_type.t), False)} | procedure id {curr_scope <- AddEntry(id.lexval, curr_scope)} arguments ; {UpdateType(curr_scope, Proc(arguments.t), False)} arguments --> ( parameter_list ) {arguments.t <- parameter_list.t} | epsilon {arguments.t <- Nil} parameter_list --> parameter_list1 ; parameter {parameter_list.t <- Append(parameter.t, parameter_list1.t)} | parameter {parameter_list.t <- parameter.t} parameter --> identifier_list : standard_type {parameter.t <- UpdateTypeList(identifier_list.l, standard_type.t, True)} | var identifier_list : standard_type {parameter.t <- UpdateTypeList(identifier_list.l, Var(standard_type.t), True)} compound_statement --> begin statement_list end statement_list --> statement_list ; statement | statement statement --> variable := expression {if not Compat(variable.t, expression.t) then Error} | procedure_statement | compound_statement | if expression then statement else statement {if not Compat(Boolean, expression.t) then Error} | while expression do statement {if not Compat(Boolean, expression.t) then Error} | repeat statement_list until expression {if not Compat(Boolean, expression.t) then Error} | epsilon variable --> id [ expression ] {if LookupType(id.lexval) = Array(m,n,s) and Compat(Integer, expression.t) then variable.t <- s else Error} | id {variable.t <- LookupType(id.lexval)} procedure_statement --> id ( expression_list ) {MatchProcArgs(LookupType(id.lexval), expression_list.t)} | id {MatchProcArgs(LookupType(id.lexval), Nil)} expression_list --> expression_list1 , expression {expression_list.t <- Cons(expression.t, expression_list1.t)} | expression {expression_list.t <- Cons(expression.t, Nil)} expression --> simple_expression relop simple_expression1 {expression.t <- MatchBinOp(relop.lexval, simple_expression.t, simple_expression1.t)} | simple_expression {expression.t <- simple_expression.t} simple_expression --> simple_expression1 add_operator term {simple_expression.t <- MatchBinOp(add_operator.lexval, simple_expression1.t, term.t)} | addop term {simple_expression.t <- MatchUnOp(addop.lexval, term.t)} | term {simple_expression.t <- term.t} add_operator --> addop {add_operator.lexval <- addop.lexval} | or {add_operator.lexval <- or.lexval} term --> term1 mul_operator factor {term.t <- MatchBinOp(mul_operator.lexval, term1.t, factor.t)} | factor {term.t <- factor.t} mul_operator --> mulop {mul_operator.lexval <- mulop.lexval} | and {mul_operator.lexval <- and.lexval} | div {mul_operator.lexval <- div.lexval} | mod {mul_operator.lexval <- mod.lexval} factor --> variable {if variable.t = Func(l,r) or variable.t = Over(l) then factor.t <- MatchFuncArgs(variable.t, Nil) else factor.t <- variable.t} | id ( expression_list ) {factor.t <- MatchFuncArgs(LookupType(id.lexval), expression_list.t)} | num {factor.t <- TypeOf(num.lexval)} | ( expression ) {factor.t <- expression.t} | not factor1 {factor.t <- MatchUnOp(not.lexval, factor1.t)}
This translation scheme uses two global variables, symbol_table and curr_scope, as discussed in class. It assumes that the following types have been declared: Entry and Type, as well as the list types EntryList and TypeList. For the list types we have the empty list constant Nil:List and the cons and append operations Cons: T x TList -> TList and Append: TList x TList -> TList. For the type Type there are the following constructor functions:
Integer, Real, Boolean, Program: Type Array: int x int x Type -> Type Func: TypeList x Type -> Type Proc, Over: TypeList -> Typeas well as the function Var: Type -> Type which sets a flag in the type structure to indicate that it is the type of a variable parameter (there is also a flag to indicate an lvalue; these will need to be distinguished for the code generation phase). There is also a procedure Error which terminates the compiler with an appropriate error message.
We assume the following functions for interacting with the symbol table and checking types: