390 likes | 509 Views
Lecture #15, March. 5, 2007. Judgments for mini-Java Multiple type environments Class Hierarchy Setting up an ML typechecker for mini-Java Subtyping Rules for Java Declarations as functions over environments. HW 10, Assigned last week, due Wed, excellent warmup for project 3
E N D
Lecture #15, March. 5, 2007 • Judgments for mini-Java • Multiple type environments • Class Hierarchy • Setting up an ML typechecker for mini-Java • Subtyping • Rules for Java • Declarations as functions over environments
HW 10, Assigned last week, due Wed, • excellent warmup for project 3 • Project 3 assigned today • See webpage for details
Types for mini-Java • Mini-Java has two kinds of programs • Expressions • Statements • We need a judgment for each kind • Expressions Th, C, TE |- exp : type • Statements R, Th, C, TE |= stmt • Subtyping judgment • C |~ t1≤ t2 • Equality judgment • t1= t2
Environments • TE - Type environments • TE(x) = t • The variable x has type t in the environment TE • R - Return mode • R = { void , explicit t , none} • void means the method has type return type void and no expression is expected after a return statement • Explicit t means the method has return type t and the argument of a return must present be a subtype of t • C - Class Hierarchy • The set of classes is arranged into a hierarchy • Class colorpoint extends point { . . . } • C defines x • There is a class definition for x • Th – the type of the self object “this”
Class Hierarchy object point boolean void numeric double int colorpoint C |~ int ≤ object C |~ colorpoint ≤ point C |~ boolen ≤ object C defines point
Type Checkers in ML • To build type-checkers in ML we need • A datatype to represent types • A datatype (or datatypes) to represent programs (whose type we are checking) • Expressions • Statements • Declarations • A means for computing equality and subtyping over types • A means for expressing type errors
Representing Types type Id = string; (** Representing types for mini-Java **) datatype Basic = Bool | Int | Real; datatype Type = BasicType of Basic | ArrayType of Basic | ObjType of Id | VoidType;
Representing Programs 1 - Auxillaries (* A slot of type (`x TC) is an option type. The *) (* parser places NONE there and the type-checker *) (* fills it in with (SOME x) when "x" is known *) type 'x TC = 'x option; (******** Representing Programs *******) datatype Op (* infix operators *) = ADD | SUB | MUL | DIV (* Arithmetic *) | AND | OR (* logical *) | EQ | NE | LT | LE | GT | GE (* relational *) datatype Constant (* Literal constants *) = Cint of int | Creal of string | Cbool of bool
Representing Programs 2 - expressions datatype Exp = Literal of Constant (* 5, 6.3, true *) | Binop of Op * Exp * Exp (* x + 3 *) | Relop of Op * Exp * Exp (* x < 7.7 *) | Not of Exp (* ! x *) | ArrayElm of Exp * Exp * (Basic TC) (* x[3] *) | ArrayLen of Exp (* x.length() *) | Call of Exp * Id *(Id TC)* Exp list (* x.f(1,z) *) | NewArray of Basic * Exp (* new int[3] *) | NewObject of Id (* new point() *) (* Coerce is used only in type checking *) | Coerce of Exp
Representing Programs 3 - statements datatype Stmt = Block of Stmt list (* {x:5; print(2)} *) | Assign of Exp option * Id * Exp option * Exp (* p.x[2]=5 p.x=5 x=5 *) | CallStmt of Exp * Id * (Id TC)* Exp list (* x.f(1,z) *) | If of Exp * Stmt * Stmt (* if (p<2) x=5 else x=6 *) | While of Exp * Stmt (* while (p) s *) | PrintE of Exp (* System.out.println(x) *) | PrintT of string (* System.out.println("zbc") *) | Return of Exp option; (* return (x+3) *)
Representing programs 4 - declarations datatype VarDecl = VarDecl of Type * Id * Exp option; datatype Formal = Formal of Type * Id; datatype MetDecl = MetDecl of Type * Id * Formal list * VarDecl list * Stmt list; datatype ClassDec = ClassDec of Id * Id * VarDecl list * MetDecl list; datatype Program = Program of ClassDec list;
Type Equality • In mini-Java we need both type equality and sub-typing • Type equality for testing that the primitive operators are applied to appropriate expressions of the correct type. Type equality is over basic types. • Sub-typing for method calls and assignments, testing that actual arguments are subtypes of the formal arguments. Subtyping is over all types.
Type Equality over basic types fun typeeq (x,y) = case (x,y) of (Real,Real) => true | (Int,Int) => true | (Bool,Bool) => true | (_,_) => false • Pair wise pattern match over the basic types
Type equality 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
Subtyping 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); Fun useTree class (x,y) = . . .
Expressing Errors • In ML we are lucky to have a rich exception mechanism. • Design a set of exceptions to report errors. • You might want to define helper functions • fun showt t = . . . • fun report exp computed expected = . . .
Type rules for mini-java • One rule for each piece of syntax • Use the different judgments accordingly Th, C, TE |- exp : type Th, R, C, TE |= stmt C |~ t1≤ t2 t1= t2 C defines x Th,C, TE |- Y.f :(s1, … ,sn) → t Th,C, TE |- Y.x : t [ ] Op <+> : (t1,t2) -> t3 t is basic x TE and TE (x) = t Expression has type Statement is well formed t1 is a subtype of t2 Class Y has method f with domain and range Class Y has variable x with type
Simple Expressions • Th,C, TE |- n : int n is integer constant like 5 • Th,C, TE |- r : double r is real constant like 5.3 • Th,C, TE |- this : Th • Th,C, TE |- true : boolean • Th,C, TE |- false : boolean
Variables TE (x) = t ---------------------------- R, Th,C, TE |= x : t Th,C, TE |- e1 : Y C defines Y Th,C, TE |- Y.x : t ----------------------------------- R, Th,C, TE |= e1 . x : t x TE C defines Th Th,C, TE |- Th.x : t ------------------------------- R, Th,C, TE |= x : t
Operators Th,C, TE |- e1 : t1 Th,C, TE |- e2 : t2 Op <+> : (t1,t2) -> t3 ---------------------------- Th,C, TE |- e1 <+> e2 : t3 Th,C, TE |- e1 : boolean ---------------------------- Th,C, TE |- ! e1 : boolean
Arrays Th,C, TE |- e1 : t [ ] Th,C, TE |- e2: int ---------------------------- Th,C, TE |- e1[ e2] : t Th,C, TE |- e : t [ ] ---------------------------- Th,C, TE |- e.length() : int
Method call Expressions • Note that f(x) is really shorthand for this.f(x) C defines Th Th,C, TE |- Th.f :(s1, … ,sn) → t Th,C, TE |- ei : ti C |~ ti≤ si ---------------------------- Th,C, TE |- f(e1, … ,en) : t Th,C, TE |- exp : X C definesX Th,C, TE |- X.f : (s1, … ,sn) → t Th,C, TE |- ei : ti C |~ ti≤ si ---------------------------- Th,C, TE |- exp . f(e1, … ,en) : t
New arrays and objects Th,C, TE |- e : t t is basic ---------------------------- Th,C, TE |- new t [e] : t [ ] C defines x ---------------------------- Th,C, TE |- new x ( ) : x
Judgments over Statements • We use a different notation for typing statements • The judgment for statements does not have a return type. • It also uses a different “turnstile” symbol. • R, Th,C, TE|= e1 . x [ e2] = e3
Static Implicit Assignment TE (x) = t [ ] TE |- e2 : int Th,C, TE |- e3 : t ---------------------------- R, Th,C, TE |= x [ e2] = e3 Static, because the variable is in the type environment. This means the variable is a either a parameter or local variable to the current method. TE (x) = t Th,C, TE |- e2 : t ---------------------------- R, Th,C, TE |= x = e2
Explicit Hierarchical Assignment Statements Th,C, TE |- e1 : Y C definesY Th,C, TE |- Y.x : t [ ] Th,C, TE |- e2 : int Th,C, TE |- e3 : t ---------------------------- R, Th,C, TE |= e1 . x [ e2] = e3 Th,C, TE |- e1 : Y C defines Y Th,C, TE |- Y.x : t Th,C, TE |- e2 : t ---------------------------- R, Th,C, TE |= e1 . x = e2 Explicit because the object for containg the variable is explicit in the code
Hierarchical Implicit Assignment x TE C defines Th Th,C, TE |- Th.x : t [ ] Th,C, TE |- e2 : int Th,C, TE |- e3 : t ---------------------------- R, Th,C, TE |= x [ e2] = e3 These rules hold only if the variable is not in the type environment x TE C defines Th Th,C, TE |- Th.x : t Th,C, TE |- e2 : t ---------------------------- R, Th,C, TE |= x = e2 Implicit because the object containg the variable is not in the code.
Note 3 forms of assignment static TE (x) = t Th,C, TE |- e2 : t ---------------------------- R, Th,C, TE |= x = e2 Explicit hierarchical Th,C, TE |- e1 : Y C defines Y Th,C, TE |- Y.x : t Th,C, TE |- e2 : t ---------------------------- R, Th,C, TE |= e1 . x = e2 Implicit hierarchical x TE C defines Th Th,C, TE |- Th.x : t Th,C, TE |- e2 : t ---------------------------- R, Th,C, TE |= x = e2
If statement R, Th,C, TE |= s1 Th,C, TE |- e1 : boolean ----------------------------------------- R, Th,C, TE |= if ( e1 ) s1 R, Th,C, TE |= s1 R, Th,C, TE |= s2 Th,C, TE |- e1 : boolean ----------------------------------------- R, Th,C, TE |= if ( e1 ) s1 else S2
While and Print statement R, Th,C, TE |= s1 Th,C, TE |- e1 : boolean ----------------------------------------- R, Th,C, TE |= while ( e1 ) s1 t is basic Th,C, TE |- x : t -------------------------------------------------------- R, Th,C, TE |= System.out.println ( x ) R, Th,C, TE |= System.out.println ( “literal string” )
Return statement Void, Th,C, TE |= return C |~ t≤ s Th,C, TE |- e : s -------------------------------------------------- Explicit t, Th,C, TE |= return e
Method call statements Th,C, TE |- ei : ti C defines Th Th,C, TE |- Th.f : (s1, … ,sn) → Void C |~ ti≤ si ---------------------------- R, Th,C, TE |= f(e1, … ,en) Th,C, TE |- ei : ti C defines X Th,C, TE |- X.f : (s1, … ,sn) → Void C |~ ti≤ si Th,C, TE |- exp : X ---------------------------- R,C, TE |= exp . f(e1, … ,en)
Sequences of statements R, Th,C, TE |= si ---------------------------- R, Th,C, TE |= { s1, … ,sn }
Declarations • Declarations are type-environment to type-environment functions. R, Th,C, TE |= decl → R, Th,C, TE There are four kinds of declarations • Var-declarations • Method-declarations • Are mutually recursive • Class-declarations • Main method declaration • There is only one of these in any program
One Var declaration R, Th,C, TE |= t x ; → R, Th,C, TE+(x:t) Th,C, TE |- e : t -------------------------------------------------------------------- R, Th,C, TE |= t x = e ; → R, Th,C, TE+(x:t)
Many Var declarations R, Th,C, TE |= d1 → R1, Th1,C1, TE1 R1, Th1,C1, TE1 |= d2 → R2, Th2,C2, TE2 R2, Th2,C2, TE2 |= d3 → R3, Th3,C3, TE3 -------------------------------------------------------------------------------------- R, Th,C, TE |= {d1; d2; d3; …} → R3, Th3,C3, TE3
Mutually recursive Method Declaration TEA = TE + (f1,method Th (t11, … ,tn1) →t1) + … + (fk,method Th (t1k, … ,tnk) →tk) TEA |= (t1k x1k, … tnk xnk) → TEBk TEBk |= (v1; … ; vi ) → TECk tk, Th,C,TECk |= {s1; … ;sj } ---------------------------------------------------------------------- R, Th,C, TE |= { public t1 f1 (t1 x1, … tn xn) { v1; … ; vi ; s1; … ;sj } ; … ; public tk fk (t1 x1, … tn xn) { v1; … ; vi ; s1; … ;sj } } → R, Th,C, TEA
Class Declarations • Class declarations extend the Class heirarchy R, Th,C+(y/x), TE |= { v1; … ; vi ; m1; … ;mj } → R2, Th2,C2 TE2 -------------------------------------------------------------- R, Th,C, TE |= Class x extends y { v1; … ; vi ; m1; … ;mj } → R2, Th2,C2 TE2
New Boiler plate for Parser ProgramTypes.sml structure ProgramTypes = struct (* Put type declarations here that you *) (* want to appear in both the parser *) (* and lexer. You can open this structure *) (* else where inside your application as well (* I’ve put the datatypes for the syntax here to start *) end; Mini.cm group is ProgramTypes.sml Mini.lex Mini.grm Driver.sml TypeCheck.sml $/basis.cm $/smlnj-lib.cm $/ml-yacc-lib.cm