1 / 26

Lecture #16, March 7, 2007

Lecture #16, March 7, 2007. Mutual Recursion Symbol Tables Class Hierarchies Type checkers that rebuild code. Assignments. Reading Finish Chapter 4. Sections 4.4, 4.5, and 4.6 pages 188-208 Read Section 5.7 on Symbol tables page 238-248 Possible Quiz next Monday.

buffy
Download Presentation

Lecture #16, March 7, 2007

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Lecture #16, March 7, 2007 • Mutual Recursion • Symbol Tables • Class Hierarchies • Type checkers that rebuild code

  2. Assignments • Reading • Finish Chapter 4. Sections 4.4, 4.5, and 4.6 pages 188-208 • Read Section 5.7 on Symbol tables page 238-248 • Possible Quiz next Monday. • Project 3 is due Monday March 19

  3. Mutual Recursion • Mutual recursion is found in almost all real languages • Mutual recursion requires different techniques • Multiple passes. • One pass to build a temporary table with just the information about the mutually recursive entities • Second pass to type the recursive entities where the current scope is extended with the temporary table. • Inside this pass, the table is often extended even more to capture information about entities declared in a single one of the mutually recursive entities. • Finally, the temporary table is exported to the scope in which the mutually recursive entities exist.

  4. Example in ML x: int x: int f: int -> int val x = 56; fun f x = x + 5; fun even n = if n=0 then true else odd (n-1) and odd n = if n=0 then false else if n=1 then true else even (n-1); val main = even(f x) x: int f: int -> int even: int->bool odd: int->bool x: int f: int -> int even: int->bool odd: int->bool n: int x: int f: int -> int even: int->bool odd: int->bool main: bool

  5. Concrete Example and Dec = Valdec of (string*MLtype)*Exp | Fundec of (string*MLtype*MLtype)*string*Exp | Mutdec of Dec list • Assume we represent declarations as above. • Every val and fun declaration contains explicit information about their types (as is true in Mini-Java) • Then the concrete syntax may look like: val (x:int) = 56; fun (f:int->int) (x:int) = x + 5;

  6. Type Checking Declarations • Type checking will depend upon two attribute computations. • Checking declarations will map an environment to a delta environment. • New environment will be the old one plus the delta. TCExp:Exp ->(string * MLtype)list ->MLtype TCDec:Dec ->(string * MLtype)list ->(string * MLtype) list fun (even:int ->bool) (n:int) = if n=0 then true else odd (n-1) and (odd:int -> bool) (n:int) = if n=0 then false else if n=1 then true else even (n-1); x: int f: int -> int even: int -> bool odd: int -> bool

  7. Non mutually recursive cases fun TCDec (Valdec((nm,t),exp)) cntxt = let val bodyt = TCExp exp cntxt in if typeeq(t,bodyt) then [(nm,t)] else unexpected exp bodyt t end | TCDec (Fundec((f,dom,rng),x,body)) cntxt = let val ft = Arrow(dom,rng) val bodyt = TCExp body((x,dom)::cntxt) (* f is not recursive unless inside a Mutdec *) in if typeeq(bodyt,rng) then [(f,ft)] else unexpected body bodyt rng end

  8. Mutually recursive case | TCDec (Mutdec ds) cntxt = let fun pass1 [] cntxt = cntxt | pass1 (Valdec(p,b)::ds) cntxt = pass1 ds (p::cntxt) | pass1 (Fundec((f,d,r),x,b)::ds) cntxt = pass1 ds ((f,Arrow(d,r))::cntxt) val temp = pass1 ds cntxt val pass2 = map (fn d => TCDec d temp) ds in List.concat pass2 end; result temp pass2 cntxt even: int->bool odd: int->bool x: int f: int -> int odd: int->bool even: int->bool x: int f: int -> int even: int->bool odd: int->bool

  9. Symbol Tables • Symbol Tables map names to information • Many kinds of Names • Variables • Procedure and function names • Labels • Data Structure names (Constructors in ML or named Struct in C) • Types • Many kinds of information • Type • Physical location in source (line number) • Size • Storage class • Lexical location (Inside what class) • Location in memory in the translation

  10. Multiple symbol tables • Compilers might use one big table, or many smaller ones. • Multiple tables have advantages • Each table stores uniform items • Tables might have limited scope and can be recycled • Each table can use different implementation • Hash Table • Linked List • Balanced Tree • Each table probably has different access patterns • Create Once, then read only • Block Structured, so stack allocation is possible • Insertions only • Both insertions and deletions • Tables can have mutable or non-mutable data • Language may have multiple name spaces • Types, labels, variables, selectors, . . .

  11. Nested Scope • Many tables record information with block structure or nested scope. • Stack allocation is possible • Easy to implement in a functional language like ML • Use inherited attribute computation • Table becomes a parameter to the function • Entering new block means a new call to function with the parameter augmented. • Exiting scope means restoring the old parameter • Natural in a functional style since the old parameter is always still around.

  12. Operations on nested scope • Initialize Scope • Insert name value Scope • Lookup name Scope • Finalize Scope type ('name,'value)table = ('name * 'value) list; type ('name,'value)Scope = (('name,'value)table) list ; fun initialize scope = [ ] :: scope fun insert name value (table::scope) = ((name,value)::table)::scope fun Lookup name (table::scope) = case List.find (fn (x,y) => x=name) table of NONE => error | SOME (x,y) => y fun finalize (table::scope) = scope More sophisticated kinds of tables can be used here rather than a linked list of pairs. Hash Tables, Balanced trees, etc.

  13. Data Structures for the MiniJava Type Checker • Scope in object oriented languages like mini-java is attached to the class structure. • Each class has its own symbol table • Usually this table is an ordinary nested scope table • Each class must link to the symbol table for its super-class. • The Class hierarchy is a tree of symbol tables. • The set of operations on the tables can be found by inspecting the type rules.

  14. Judgments from the type rules Th, C, TE |- exp : type Th, R, C, TE |= stmt C |~ t1≤ t2 t1= t2 C defines x In C p has method f In C p has variable x Op <+> : (t1,t2) -> t3 t is basic

  15. object point numeric boolean void double int colorpoint Data Structure datatype CTab = Node of Id * (* Class Name *) (Id * Type)list * (* Class Variables *) (Id * Type list * Type)list * (* Methods *) CTab ref * (* Parent Class *) CTab list (* Sub Classes *) | NullClass; val root = Node("object",[],[],ref NullClass,[]); CTab for class table is an n-way branching tree.

  16. newClass fun newClass name vars methods parent NullClass = NullClass | newClass name vars methods parent (n as Node(nm,vs,ms,p,subs)) = if parent=nm then let val p1 = ref n val new = Node(name,vars,methods,p1,[]) val newP = Node(nm,vs,ms,p, new :: subs) val _ = p1 := newP in newP end else Node(nm,vs,ms,p, map (newClass name vars methods parent) subs) • newClass creates a new class hierarchy from the old one. The old one is unchanged.

  17. val t1 = newClass "point" [] [] "object" root; root Node(“object”, [ ], [ ], ref NullClass,[ ]) new Node(“point”, [ ], [ ], ref _ ,[ ]) newP Node(“object”, [ ], [ ], ref NullClass, [Node(“point”, [ ], [ ], ref _ ,[ ])]) • The final step is to overwrite the pointer to root with newP, and then return newP • newClass rebuilds the part of the tree that are unchanged • The references make cyclic structures

  18. val t1 = newClass "point" [] [] "object" root;val t2 = newClass "colorpoint" [] [] "point" t1; t2 Node(“object”, [ ], [ ], ref NullClass ,[ _ ]) Node(“point”, [ ], [ ], ref _ ,[ _ ]) Node(“colorpoint”, [ ], [ ], ref _ ,[ ])

  19. t3 Node(“object”, [ ], [ ], ref NullClass ,[ _ , _ ]) Node(“point”, [ ], [ ], ref _ ,[ _ ]) Node(“colorpoint”, [ ], [ ], ref _ ,[ ]) Node(“person”, [ ], [ ], ref _ ,[ ]) val t1 = newClass "point" [] [] "object" root;val t2 = newClass "colorpoint" [] [] "point" t1;val t3 = newClass "person" [] [] "object" t2

  20. C defines x fun defines name NullClass = false | defines name (Node(n,vs,ms,p,ss)) = if name=n then true else List.exists (defines name) ss;

  21. t1= t2 fun basiceq (x,y) = case (x,y) of (Real,Real) => true | (Int,Int) => true | (Bool,Bool) => true | (_,_) => false fun typeeq (x,y) = case (x,y) of (BasicType x,BasicType y) => basiceq(x,y) | (ArrayType x,ArrayType y) => basiceq(x,y) | (ObjType x,ObjType y) => x=y | (VoidType,VoidType) => true | (_,_) => false

  22. C |~ t1≤ t2 fun useTree NullClass (x,y) = false | useTree (Node(nm,vs,ms,p,ss)) (x,y) = if nm = y then List.exists (defines x) ss else List.exists (fn t => useTree t (x,y)) ss fun subtype classH (x,y) = case (x,y) of (x,ObjType "object") => true | (BasicType Int,ObjType "numeric") => true | (BasicType Real,ObjType "numeric") => true | (ObjType x,ObjType y) => useTree classH (x,y) | (_,_) => typeeq(x,y)

  23. Type checkers that rebuild the syntax tree • Some type checkers use inference to add things to the syntax tree that are missing. • Coercions • 3 + 5.6 -> toFloat 3 + 5.6 • Implicit class information • X.f(3,5) -> X.ffrompoint(2,5) • Implicit type information • (fn x => x + 1) -> (fn (x:Int) => x + 1) • Such type checkers return a tuple. • The tuple includes the synthesized attributes and a new syntax tree. Compare the two types • TCExp2: Exp -> (string * MLtype) list -> MLtype * Exp • TCExp: Exp -> (string * MLtype) list -> MLtype

  24. TCExp2 When a term has no sub terms, the old term can usually be returned fun TCExp2 x cntxt = case x of Lit c => (TCConstant c,x) | Var s => (case List.find (fn (nm,t) => nm=s) cntxt of SOME(nm,t) => (t,x) | NONE => error x "Undeclared variable") | Infix(l,x,r) => let val (ltype,l2) = TCExp2 l cntxt val (rtype,r2) = TCExp2 r cntxt val (lneed,rneed,result) = TCOp x in case (typeeq(ltype,lneed),typeeq(rtype,rneed)) of (true,true) => (result, Infix(l2,x,r2) ) | (true,false) => unexpected r rtype rneed | (false,true) => unexpected l ltype lneed | (false,false) => unexpected l ltype lneed end Sub terms are rebuilt and the returned term is constructed from these.

  25. Why Bother • If all we ever do is return the same tree, why bother? • We use type (or other) information to add little bits of syntax. Infix(l,x,r) => let val (ltype,l2) = TCExp2 l cntxt val (rtype,r2) = TCExp2 r cntxt val (lneed,rneed,result) = TCOp x in case (typeeq(ltype,lneed),typeeq(rtype,rneed)) of (true,true) => Fix result (x,ltype,rtype) l2 r2 | (true,false) => unexpected r rtype rneed | (false,true) => unexpected l ltype lneed | (false,false) => unexpected l ltype lneed end fun Fix result info left right = case info of (Plus,Int,Real) => (result,Infix( Int2Real left ,Plus,right)) | (Plus,Real,Int) => (result,Infix(left,Plus, Int2Real right )) | (oper, _ , _ ) => (result,Infix(left,oper,right))

  26. Class exercise • As an in class exercise, lets continue the function on the previous pages. • Start with an non rebuilding type-checker • Make a copy of it. • Rename every call to a new name (so it doesn’t conflict) • For each returned expression add a another value to the tuple. • For each recursive call, accept (or pattern match against) an additional value.

More Related