semantic analysis checking symbol tables
play

Semantic Analysis/Checking Symbol tables Semantic analysis: the - PDF document

Semantic Analysis/Checking Symbol tables Semantic analysis: the final part of analysis half of compilation Key data structure during semantic analysis, code generation afterwards comes synthesis half of compilation Stores info about names


  1. Semantic Analysis/Checking Symbol tables Semantic analysis: the final part of analysis half of compilation Key data structure during semantic analysis, code generation • afterwards comes synthesis half of compilation Stores info about names used in program Purposes: • a map (table) from names to info about them • perform final checking of legality of input program, • each symbol table entry is a binding “missed” by lexical and syntactic checking • a declaration adds a binding to map • name resolution, type checking, break stmt in loop, ... • a use of a name looks up binding in map • “understand” program well enough to do synthesis • report a type error if none found • e.g. relate assignments to & references of particular variable Craig Chambers 96 CSE 401 Craig Chambers 97 CSE 401 Example A bigger example class C { class C { int x; int x; boolean y; boolean y; int f(C c) { int f(C c) { int z; int z; ... ... ... z ... c ... new C() ... x ... f(..) ... { } boolean x; } C z; int f; .. z .. c .. new C() .. x .. f(..) .. } .. z .. c .. new C() .. x .. f(..) .. } } Craig Chambers 98 CSE 401 Craig Chambers 99 CSE 401

  2. Nested scopes Name spaces Can have same name declared in different scopes Sometimes can have same name refer to different things, but still unambiguously Want references to use closest textually-enclosing declaration Example: • static/lexical scoping, block structure class F { • closer declaration shadows declaration of enclosing scope int F(F F) { // 3 different F ’s are available here! ... new F() ... Simple solution: ... F = ... • one symbol table per scope ... this.F(...) ... • each scope’s symbol table refers to its lexically enclosing scope’s symbol table } • root is the global scope’s symbol table } • look up declaration of name starting with nearest symbol table, proceed to enclosing symbol tables if not found In MiniJava: three name spaces locally • classes, methods, and variables We always know which we mean for each name reference, All scopes in program form a tree based on its syntactic position Simple solution: symbol table stores a separate map for each name space Craig Chambers 100 CSE 401 Craig Chambers 101 CSE 401 Information about names Generic typechecking algorithm Different kinds of declarations store different information about To do semantic analysis & checking on a program, their names recursively typecheck each of the nodes in the program’s AST, each in the context of the symbol table for its enclosing • must store enough information to be able to check later scope references to the name • on the way down, create any nested symbol tables & context needed A variable declaration: • recursively typecheck child subtrees • its type • on the way back up, check that the children are legal in the • whether it’s final , etc. context of their parents • whether it’s public , etc. • (maybe) whether it’s a local variable, an instance variable, Each AST node class defines its own typecheck method, a global variable, or ... which fills in the specifics of this recursive algorithm A method declaration: Generally: • its argument and result types • declaration AST nodes add bindings to the current symbol • whether it’s static , etc. table • whether it’s public , etc. • statement AST nodes check their subtrees • expression AST nodes check their subtrees and return a A class declaration: result type • its class variable declarations • its method and constructor declarations • its superclass Craig Chambers 102 CSE 401 Craig Chambers 103 CSE 401

  3. MiniJava typechecker implementation Class, variable, and method information In SymbolTable subdirectory: lookupClass returns a ClassSymbolTable • includes all the information about the class’s interface Various SymbolTable classes, organized into a hierarchy: SymbolTable lookupVariable returns a VarInterface • stores the variable’s type GlobalSymbolTable A hierarchy of implementations: NestedSymbolTable ClassSymbolTable VarInterface CodeSymbolTable LocalVarInterface InstanceVarInterface Support the following operations (and more): • declareClass , lookupClass lookupMethod returns a MethodInterface • declareInstanceVariable , • stores the method’s argument and result types declareLocalVariable , lookupVariable • declareMethod , lookupMethod Craig Chambers 104 CSE 401 Craig Chambers 105 CSE 401 Some key AST typecheck operations Forward references void Program.typecheck() Typechecking class declarations is tricky: need to allow for forward references from the bodies of earlier classes to the throws TypecheckCompilerExn; declarations of later classes • typecheck the whole program class First { Second next; // have to allow this forward reference void Stmt.typecheck(CodeSymbolTable) throws TypecheckCompilerExn; int f() { • typecheck a statement in the context of the given symbol ... next.g() ... // and this forward reference table } } class Second { ResolvedType Expr.typecheck(CodeSymbolTable) First prev; throws TypecheckCompilerExn; int g() { • typecheck an expression in the context of the given symbol ... prev.f() ... table, returning the type of the result } } Craig Chambers 106 CSE 401 Craig Chambers 107 CSE 401

  4. Supporting forward references An example typechecking operation Simple solution: class VarDeclStmt { typecheck a program’s class declarations in multiple passes String name; • first pass: remember all class declarations Type type; { First → class{?}, Second → class{?}} • second pass: compute interface to each class, checking void typecheck(CodeSymbolTable st) class types in headers throws TypecheckCompilerException { { First → class{ next : Second }, st.declareLocalVar(type.resolve(st), name); Second → class{ prev : First }} } • third pass: check method bodies, given interfaces } resolve checks that a syntactic type expression is a legal type, void ClassDecl.declareClass(GlobalSymbolTable) and returns the corresponding resolved type throws TypecheckCompilerExn; • declare the class in the global symbol table declareLocalVar checks for duplicate variable declaration in this scope void ClassDecl.computeClassInterface() throws TypecheckCompilerExn; • fill out the class’s interface, given the declared classes void ClassDecl.typecheckClass() throws TypecheckCompilerExn; • typecheck the body of the class, given all classes’ interfaces Craig Chambers 108 CSE 401 Craig Chambers 109 CSE 401 An example typechecking operation An example typechecking operation class AssignStmt { class IfStmt { String lhs; Expr test; Expr rhs; Stmt then_stmt; Stmt else_stmt; void typecheck(CodeSymbolTable st) throws TypecheckCompilerException { void typecheck(CodeSymbolTable st) VarInterface lhs_iface = st.lookupVar(lhs); throws TypecheckCompilerException { ResolvedType lhs_type = lhs_iface.getType(); ResolvedType test_type = test.typecheck(st); ResolvedType rhs_type = rhs.typecheck(st); test_type.checkIsBoolean(); rhs_type.checkIsAssignableTo(lhs_type); then_stmt.typecheck(st); } else_stmt.typecheck(st); } } } lookupVar checks that the name is declared as a var checkIsBoolean checks that the type is a boolean checkIsAssignableTo verifies that an expression yielding the rhs type can be assigned to a variable declared to be of the lhs type • initially, rhs type is equal to or a subclass of lhs type Craig Chambers 110 CSE 401 Craig Chambers 111 CSE 401

  5. An example typechecking operation class BlockStmt { List<Stmt> stmts; void typecheck(CodeSymbolTable st) throws TypecheckCompilerException { CodeSymbolTable nested_st = new CodeSymbolTable(st); foreach Stmt stmt in stmts { stmt.typecheck(nested_st); } } } (Garbage collection will reclaim nested_st when done) Craig Chambers 112 CSE 401

Recommend


More recommend