BinaryExpr.action – Ints, all Ops - “a.isInt() && b.isInt()” works for a few Ops, others? - The general concept is “expression compatibility”, which also computes a “result type”, so let’s write code like that: OpSTO opSTO = (OpSTO) symTab.lookup(opString); if (!BinaryExpr.elaborate(result, a, opSTO, b)) { ... } return a.isInt() && b.isInt() return a.isInt() && b.isInt() boolean elaborate(ExprSTO result, STO a, OpSTO op, STO b) { if (op.compatible(a, b)) { // a, b in ParamList later result.putType(op.resultType(a, b)); return true; return TypeValues.integerType } … return TypeValues.integerType Bodies of new operations merely contain code that they replaced – better modularity and abstraction! (Still int/+, but now easily expandable to others)
Coercion and Overloading Overloading is having one name for two different operators: int x; float y; x = x + x; /* + for ints */ y = y + y; /* + for reals */ Coercion (or casting) converts a value from one type to another: y = x + y; /* val retrieved from x coerced to float so can be added to y */ y = ( (float) x) + y; // this is equivalent float t1 = (float) x; // compiler produces hidden var y = t1 + y; Similarities outweigh the differences with regard to convenience versus confusion
Explained by Subtyping Review: A type is • A set of values • A set of operations on those values • Rules for their combination Type S is a subtype of T if an S always behaves like a T, but sometimes T could behave unlike an S • Cats are animals, but not all animals are cats • All ints are like reals, but not all reals are like ints • Think of this as an ”is-a” relationship Statically, in a programming language, type S is a “subtype” of T if: • S contains only values of T • S supports all operations of T (but perhaps more) Example • The ints contain only reals (1, 2, ...), • The ints support all real operations (+, *, ...) • Ints support extra operations like DIV, MOD
Automatic Coercion & Overloading Subtype behavior is what allows safe automatic operator overloading int x; float y; x = x + x; // + for ints +: int x int --> int y = y + y; // + for reals +: float x float --> float y = y + x; // + for reals, x is coerced to float first 1. Pick operation with smallest inclusive type 2. Coerce value to type of operation
E + E with Coercion & Overloading Expr ::= Expr:e1 Op:op Expr:e2 {: RESULT = BinaryExpr.action(e1, op, e2); :} Operator | first operand second operand result type ------------------------------------------------------------------------------------ + - * | numeric numeric smallest numeric | type including | both operands
How is Our Old Code Affected? // Only code in type class changes: clients are stable! boolean elaborate(ExprSTO result, STO a, OpSTO op, STO b) { if (op.compatible(a, b)) { // a, b in ParamList later result.putType(op.resultType(a, b)); return true; } … boolean PlusMinusMulOpType::compatible(Type a, Type b) { return (a.isInt() && b.isInt()); a.isNumeric() && b.isNumeric() } Type PlusMinusMulOpType::resultType(Type a, Type b) { return TypeValues.integerType; // doesn’t hold for divide! } smallestInclusiveType(a, b)
How Handle Other Grammar Rules? P ::= D ';' S D ::= D ';' D D ::= ID ':' T T ::= integer | real | boolean E ::= ID | INTLIT | REALLIT | BOOLLIT E ::= E '+' E E ::= E '<' E S ::= S ';' S S ::= ID ':=' E S ::= IF E THEN S
What You Can Do Now (Phase I) Add remaining Type class operations Extend ExprSTO’s to query type properties, also := boolean VarSTO::assignable(STO other) { return this.m_type.assignable(other.type()); } Can treat assignment like binary operator: In CUP file or action(): opSTO = symTab.lookup(“:=“); In check: op.compatible(a, b); // no resultType, though Test, Test, Test – syntax-directed Program to the specification and interface to aid clarity, robustness, and reuse
Modular Error Handling boolean elaborate(ExprSTO result, STO a, OpSTO op, STO b) { if (opSTO.isCompatible(a, b)) result.setType(opSTO.resultType(a, b)); return true; else { opSTO.reportError(a, b); // Error also dependent on operator result.setType(ErrorType.typeOnError(TypeValues.integerType)); return false; } } • Avoid cascading messages; guessing type is hard • Keep error-handling from pervading compiler • Introduce ErrorType, subclass of Type? • “Natural” extension, but how should it behave?
Conceptualizing Errors • Compiler has to behave well “outside” the specification • Extend normal specs to include error behavior Operator | first operand second operand result type ------------------------------------------------------------------------------------ + - * | numeric numeric smallest numeric | type including | both operands “Error Type” | non-numeric numeric | numeric non-numeric | non-numeric non-numeric
ErrorType is Stealth Internal Type class ErrorType extends Type { // error message already printed, play nice boolean isError() { return true; } boolean isInt() { return true; } boolean isReal() { return false; } // dicey boolean isBool() { return true; } ... String toString() { return "ERROR"; } // should never print! } This method is inferior. Instead, have This method is inferior. Instead, have Oberon example: ErrorType return “false” to everything ErrorType return “false” to everything except isError(). With this approach, except isError(). With this approach, VAR x, y : INTEGER; ErrorType causes every check to fail, in a ErrorType causes every check to fail, in a VAR b : BOOLEAN; cascade. To surpress cascading errors, cascade. To surpress cascading errors, then, modify the error printer to not print then, modify the error printer to not print BEGIN error types. (You might want to have a error types. (You might want to have a x := (x + b) + y; debug mode that does print them.) debug mode that does print them.) x := (x + b) + y + b END.
Syntax-Directed Testing PROGRAM ArithTest VAR x, y : INTEGER; r, s : REAL; b, c : BOOLEAN; BEGIN (* assignments here only to make program syntactically correct *) x := x + y; (* no error, expression is type INTEGER *) r := r + s; (* no error, expression is type REAL *) r := x + r; (* no error, expression is type REAL *) r := r + x; (* no error, expression is type REAL *) r := x + b; (* error, numeric expected, got BOOLEAN *) r := b + x; (* error, numeric expected, got BOOLEAN *) r := r + b; (* error, numeric expected, got BOOLEAN *) r := b + r; (* error, numeric expected, got BOOLEAN *) (* might report two errors *) x := b + c (* error, numeric expected, got BOOLEAN *) END.
Recommend
More recommend