The Cool Reference Manual - Compilers - Lecture Notes | ECS 142, Assignments of Computer Science

Material Type: Assignment; Class: Compilers; Subject: Engineering Computer Science; University: University of California - Davis; Term: Winter 1995;

Typology: Assignments

Pre 2010

Uploaded on 07/30/2009

koofers-user-bkf
koofers-user-bkf 🇺🇸

9 documents

1 / 30

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
The Cool Reference Manual
Contents
1 Introduction 3
2 Getting Started 3
3 Classes 4
3.1 Features.............................................. 4
3.2 Inheritance ............................................ 6
4 Types 6
4.1 SELFTYPE ........................................... 7
4.2 TypeChecking .......................................... 7
5 Attributes 8
5.1 Void................................................ 8
6 Methods 8
7 Expressions 9
7.1 Constants ............................................. 9
7.2 Identifiers ............................................. 9
7.3 Assignment ............................................ 10
7.4 Dispatch.............................................. 10
7.5 Conditionals............................................ 10
7.6 Loops ............................................... 11
7.7 Blocks............................................... 11
7.8 Let................................................. 11
7.9 Case................................................ 12
7.10New ................................................ 13
7.11Isvoid ............................................... 13
7.12 Arithmetic and Comparison Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Copyright c
1995-2004 by Alex Aiken. All rights reserved.
1
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e

Partial preview of the text

Download The Cool Reference Manual - Compilers - Lecture Notes | ECS 142 and more Assignments Computer Science in PDF only on Docsity!

The Cool Reference Manual∗

  • 1 Introduction Contents
  • 2 Getting Started
  • 3 Classes
    • 3.1 Features
    • 3.2 Inheritance
  • 4 Types
    • 4.1 SELF TYPE
    • 4.2 Type Checking
  • 5 Attributes
    • 5.1 Void
  • 6 Methods
  • 7 Expressions
    • 7.1 Constants
    • 7.2 Identifiers
    • 7.3 Assignment
    • 7.4 Dispatch
    • 7.5 Conditionals
    • 7.6 Loops
    • 7.7 Blocks
    • 7.8 Let
    • 7.9 Case
    • 7.10 New
    • 7.11 Isvoid
    • 7.12 Arithmetic and Comparison Operations
  • 8 Basic Classes
    • 8.1 Object
    • 8.2 IO
    • 8.3 Int
    • 8.4 String
    • 8.5 Bool
  • 9 Main Class
  • 10 Lexical Structure
    • 10.1 Integers, Identifiers, and Special Notation
    • 10.2 Strings
    • 10.3 Comments
    • 10.4 Keywords
    • 10.5 White Space
  • 11 Cool Syntax
    • 11.1 Precedence
  • 12 Type Rules
    • 12.1 Type Environments
    • 12.2 Type Checking Rules
  • 13 Operational Semantics
    • 13.1 Environment and the Store
    • 13.2 Syntax for Cool Objects
    • 13.3 Class definitions
    • 13.4 Operational Rules
  • 14 Acknowledgements

This form loads the file, runs the program, and exits spim when the program terminates. Be sure that spim is invoked using the script /home/cs142/cool/bin/spim. There may be another version of spim installed on some systems, but it will not execute Cool programs. An easy way to be sure of getting the correct version is to alias spim to /home/cs142/cool/bin/spim. The spim manual is available on the course Web page. The following is a complete transcript of compiling and executing /home/cs142/cool/examples/list.cl. This program is very silly, but it does serve to illustrate many of the features of Cool.

% coolc list.cl % spim -file list.s SPIM Version 6.5 of January 4, 2003 Copyright 1990-2003 by James R. Larus ([email protected]). All Rights Reserved. See the file README a full copyright notice. Loaded: /home/cs142/cool/lib/trap.handler 5 4 3 2 1 4 3 2 1 3 2 1 2 1 1 COOL program successfully executed %

3 Classes

All code in Cool is organized into classes. Each class definition must be contained in a single source file, but multiple classes may be defined in the same file. Class definitions have the form:

class [ inherits ] { <feature_list> };

The notation [ ...] denotes an optional construct. All class names are globally visible. Class names begin with an uppercase letter. Classes may not be redefined.

3.1 Features

The body of a class definition consists of a list of feature definitions. A feature is either an attribute or a method. An attribute of class A specifies a variable that is part of the state of objects of class A. A method of class A is a procedure that may manipulate the variables and objects of class A. One of the major themes of modern programming languages is information hiding, which is the idea that certain aspects of a data type’s implementation should be abstract and hidden from users of the data type. Cool supports information hiding through a simple mechanism: all attributes have scope local to the class, and all methods have global scope. Thus, the only way to provide access to object state in Cool is through methods.

Feature names must begin with a lowercase letter. No method name may be defined multiple times in a class, and no attribute name may be defined multiple times in a class, but a method and an attribute may have the same name. A fragment from list.cl illustrates simple cases of both attributes and methods:

class Cons inherits List { xcar : Int; xcdr : List;

isNil() : Bool { false };

init(hd : Int, tl : List) : Cons { { xcar <- hd; xcdr <- tl; self; } } ... };

In this example, the class Cons has two attributes xcar and xcdr and two methods isNil and init. Note that the types of attributes, as well as the types of formal parameters and return types of methods, are explicitly declared by the programmer. Given object c of class Cons and object l of class List, we can set the xcar and xcdr fields by using the method init:

c.init(1,l)

This notation is object-oriented dispatch. There may be many definitions of init methods in many different classes. The dispatch looks up the class of the object c to decide which init method to invoke. Because the class of c is Cons, the init method in the Cons class is invoked. Within the invocation, the variables xcar and xcdr refer to c’s attributes. The special variable self refers to the object on which the method was dispatched, which, in the example, is c itself. There is a special form new C that generates a fresh object of class C. An object can be thought of as a record that has a slot for each of the attributes of the class as well as pointers to the methods of the class. A typical dispatch for the init method is:

(new Cons).init(1,new Nil)

This example creates a new cons cell and initializes the “car” of the cons cell to be 1 and the “cdr” to be new Nil.^1 There is no mechanism in Cool for programmers to deallocate objects. Cool has automatic memory management; objects that cannot be used by the program are deallocated by a runtime garbage collector. Attributes are discussed further in Section 5 and methods are discussed further in Section 6. (^1) In this example, Nil is assumed to be a subtype of List.

4.1 SELF TYPE

The type SELF TYPE is used to refer to the type of the self variable. This is useful in classes that will be inherited by other classes, because it allows the programmer to avoid specifying a fixed final type at the time the class is written. For example, the program

class Silly { copy() : SELF_TYPE { self }; };

class Sally inherits Silly { };

class Main { x : Sally <- (new Sally).copy();

main() : Sally { x }; };

Because SELF TYPE is used in the definition of the copy method, we know that the result of copy is the same as the type of the self parameter. Thus, it follows that (new Sally).copy() has type Sally, which conforms to the declaration of attribute x. Note that the meaning of SELF TYPE is not fixed, but depends on the class in which it is used. In general, SELF TYPE may refer to the class C in which it appears, or any class that conforms to C. When it is useful to make explicit what SELF TYPE may refer to, we use the name of the class C in which SELF TYPE appears as an index SELF TYPEC. This subscript notation is not part of Cool syntax—it is used merely to make clear in what class a particular occurrence of SELF TYPE appears. From Definition 4.1, it follows that SELF TYPEX ≤ SELF TYPEX. There is also a special conformance rule for SELF TYPE: SELF TYPEC ≤ P if C ≤ P Finally, SELF TYPE may be used in the following places: new SELF TYPE, as the return type of a method, as the declared type of a let variable, or as the declared type of an attribute. No other uses of SELF TYPE are permitted.

4.2 Type Checking

The Cool type system guarantees at compile time that execution of a program cannot result in runtime type errors. Using the type declarations for identifiers supplied by the programmer, the type checker infers a type for every expression in the program. It is important to distinguish between the type assigned by the type checker to an expression at compile time, which we shall call the static type of the expression, and the type(s) to which the expression may evaluate during execution, which we shall call the dynamic types. The distinction between static and dynamic types is needed because the type checker cannot, at compile time, have perfect information about what values will be computed at runtime. Thus, in general, the static and dynamic types may be different. What we require, however, is that the type checker’s static types be sound with respect to the dynamic types.

Definition 4.2 For any expression e, let De be a dynamic type of e and let Se be the static type inferred by the type checker. Then the type checker is sound if for all expressions e it is the case that De ≤ Se.

Put another way, we require that the type checker err on the side of overestimating the type of an expression in those cases where perfect accuracy is not possible. Such a type checker will never accept a program that contains type errors. However, the price paid is that the type checker will reject some programs that would actually execute without runtime errors.

5 Attributes

An attribute definition has the form

: [ <- ];

The expression is optional initialization that is executed when a new object is created. The static type of the expression must conform to the declared type of the attribute. If no initialization is supplied, then the default initialization is used (see below). When a new object of a class is created, all of the inherited and local attributes must be initialized. Inherited attributes are initialized first in inheritance order beginning with the attributes of the greatest ancestor class. Within a given class, attributes are initialized in the order they appear in the source text. Attributes are local to the class in which they are defined or inherited. Inherited attributes cannot be redefined.

5.1 Void

All variables in Cool are initialized to contain values of the appropriate type. The special value void is a member of all types and is used as the default initialization for variables where no initialization is supplied by the user. (void is used where one would use NULL in C or null in Java; Cool does not have anything equivalent to C’s or Java’s void type.) Note that there is no name for void in Cool; the only way to create a void value is to declare a variable of some class other than Int, String, or Bool and allow the default initialization to occur, or to store the result of a while loop. There is a special form isvoid expr that tests whether a value is void (see Section 7.11). In addition, void values may be tested for equality. A void value may be passed as an argument, assigned to a variable, or otherwise used in any context where any value is legitimate, except that a dispatch to or case on void generates a runtime error. Variables of the basic classes Int, Bool, and String are initialized specially; see Section 8.

6 Methods

A method definition has the form

( : ,..., : ): { };

There may be zero or more formal parameters. The identifiers used in the formal parameter list must be distinct. The type of the method body must conform to the declared return type. When a method is invoked, the formal parameters are bound to the actual arguments and the expression is evaluated; the resulting value is the meaning of the method invocation. A formal parameter hides any definition of an attribute of the same name. To ensure type safety, there are restrictions on the redefinition of inherited methods. The rule is simple: If a class C inherits a method f from an ancestor class P, then C may override the inherited

7.3 Assignment

An assignment has the form

<-

The static type of the expression must conform to the declared type of the identifier. The value is the value of the expression. The static type of an assignment is the static type of .

7.4 Dispatch

There are three forms of dispatch (i.e. method call) in Cool. The three forms differ only in how the called method is selected. The most commonly used form of dispatch is

.(,...,)

Consider the dispatch e 0 .f(e 1 ,... , en). To evaluate this expression, the arguments are evaluated in left- to-right order, from e 1 to en. Next, e 0 is evaluated and its class C noted (if e 0 is void a runtime error is generated). Finally, the method f in class C is invoked, with the value of e 0 bound to self in the body of f and the actual arguments bound to the formals as usual. The value of the expression is the value returned by the method invocation. Type checking a dispatch involves several steps. Assume e 0 has static type A. (Recall that this type is not necessarily the same as the type C above. A is the type inferred by the type checker; C is the class of the object computed at runtime, which is potentially any subclass of A.) Class A must have a method f, the dispatch and the definition of f must have the same number of arguments, and the static type of the ith actual parameter must conform to the declared type of the ith formal parameter. If f has return type B and B is a class name, then the static type of the dispatch is B. Otherwise, if f has return type SELF TYPE, then the static type of the dispatch is A. To see why this is sound, note that the self parameter of the method f conforms to type A. Therefore, because f returns SELF TYPE, we can infer that the result must also conform to A. Inferring accurate static types for dispatch expressions is what justifies including SELF TYPE in the Cool type system. The other forms of dispatch are:

(,...,) @.id(,...,)

The first form is shorthand for self.(,...,). The second form provides a way of accessing methods of parent classes that have been hidden by redefinitions in child classes. Instead of using the class of the leftmost expression to determine the method, the method of the class explicitly specified is used. For example, [email protected]() invokes the method f in class B on the object that is the value of e. For this form of dispatch, the static type to the left of “@”must conform to the type specified to the right of “@”.

7.5 Conditionals

A conditional has the form

if then else fi

The semantics of conditionals is standard. The predicate is evaluated first. If the predicate is true, then the then branch is evaluated. If the predicate is false, then the else branch is evaluated. The value of the conditional is the value of the evaluated branch. The predicate must have static type Bool. The branches may have any static types. To specify the static type of the conditional, we define an operation t (pronounced “join”) on types as follows. Let A,B,D be any types other than SELF TYPE. The least type of a set of types means the least element with respect to the conformance relation ≤.

A t B = the least type C such that A ≤ C and B ≤ C A t A = A (idempotent) A t B = B t A (commutative) SELF TYPED t A = D t A

Let T and F be the static types of the branches of the conditional. Then the static type of the conditional is T t F. (think: Walk towards Object from each of T and F until the paths meet.)

7.6 Loops

A loop has the form

while loop pool

The predicate is evaluated before each iteration of the loop. If the predicate is false, the loop terminates and void is returned. If the predicate is true, the body of the loop is evaluated and the process repeats. The predicate must have static type Bool. The body may have any static type. The static type of a loop expression is Object.

7.7 Blocks

A block has the form

{ ; ... ; }

The expressions are evaluated in left-to-right order. Every block has at least one expression; the value of a block is the value of the last expression. The expressions of a block may have any static types. The static type of a block is the static type of the last expression. An occasional source of confusion in Cool is the use of semi-colons (“;”). Semi-colons are used as terminators in lists of expressions (e.g., the block syntax above) and not as expression separators. Semi-colons also terminate other Cool constructs, see Section 11 for details.

7.8 Let

A let expression has the form

let : [ <- ], ..., : [ <- ] in

The optional expressions are initialization; the other expression is the body. A let is evaluated as follows. First is evaluated and the result bound to . Then is evaluated and the result bound to , and so on, until all of the variables in the let are initialized. (If the initialization

7.10 New

A new expression has the form

new

The value is a fresh object of the appropriate class. If the type is SELF TYPE, then the value is a fresh object of the class of self in the current scope. The static type is .

7.11 Isvoid

The expression

isvoid expr

evaluates to true if expr is void and evaluates to false if expr is not void.

7.12 Arithmetic and Comparison Operations

Cool has four binary arithmetic operations: +, -, *, /. The syntax is

expr1 expr

To evaluate such an expression first expr1 is evaluated and then expr2. The result of the operation is the result of the expression. The static types of the two sub-expressions must be Int. The static type of the expression is Int. Cool has only integer division. Cool has three comparison operations: <, <=, =. For < and <= the rules are exactly the same as for the binary arithmetic operations, except that the result is a Bool. The comparison = is a special case. If either or has static type Int, Bool, or String, then the other must have the same static type. Any other types, including SELF TYPE, may be freely compared. On non-basic objects, equality simply checks for pointer equality (i.e., whether the memory addresses of the objects are the same). Equality is defined for void. In principle, there is nothing wrong with permitting equality tests between, for example, Bool and Int. However, such a test must always be false and almost certainly indicates some sort of programming error. The Cool type checking rules catch such errors at compile-time instead of waiting until runtime. Finally, there is one arithmetic and one logical unary operator. The expression ~ is the integer complement of . The expression must have static type Int and the entire expression has static type Int. The expression not is the boolean complement of . The expression must have static type Bool and the entire expression has static type Bool.

8 Basic Classes

8.1 Object

The Object class is the root of the inheritance graph. Methods with the following declarations are defined:

abort() : Object type_name() : String copy() : SELF_TYPE

The method abort halts program execution with an error message. The method type name returns a string with the name of the class of the object. The method copy produces a shallow copy of the object.^3

8.2 IO

The IO class provides the following methods for performing simple input and output operations:

out_string(x : String) : SELF_TYPE out_int(x : Int) : SELF_TYPE in_string() : String in_int() : Int

The methods out string and out int print their argument and return their self parameter. The method in string reads a string from the standard input, up to but not including a newline character. The method in int reads a single integer, which may be preceded by whitespace. Any characters following the integer, up to and including the next newline, are discarded by in int. A class can make use of the methods in the IO class by inheriting from IO. It is an error to redefine the IO class.

8.3 Int

The Int class provides integers. There are no methods special to Int. The default initialization for variables of type Int is 0 (not void). It is an error to inherit from or redefine Int.

8.4 String

The String class provides strings. The following methods are defined:

length() : Int concat(s : String) : String substr(i : Int, l : Int) : String

The method length returns the length of the self parameter. The method concat returns the string formed by concatenating s after self. The method substr returns the substring of its self parameter beginning at position i with length l; string positions are numbered beginning at 0. A runtime error is generated if the specified substring is out of range. The default initialization for variables of type String is "" (not void). It is an error to inherit from or redefine String.

8.5 Bool

The Bool class provides true and false. The default initialization for variables of type Bool is false (not void). It is an error to inherit from or redefine Bool.

(^3) A shallow copy of a copies a itself, but does not recursively copy objects that a points to.

10.4 Keywords

The keywords of cool are: class, else, false, fi, if, in, inherits, isvoid, let, loop, pool, then, while, case, esac, new, of, not, true. Except for the constants true and false, keywords are case insensitive. To conform to the rules for other objects, the first letter of true and false must be lowercase; the trailing letters may be upper or lower case.

10.5 White Space

White space consists of any sequence of the characters: blank (ascii 32), \n (newline, ascii 10), \f (form feed, ascii 12), \r (carriage return, ascii 13), \t (tab, ascii 9), \v (vertical tab, ascii 11).

11 Cool Syntax

Figure 1 provides a specification of Cool syntax. The specification is not in pure Backus-Naur Form (BNF); for convenience, we also use some regular expression notation. Specifically, A∗^ means zero or more A’s in succession; A+^ means one or more A’s. Items in square brackets [.. .] are optional. Double brackets [[ ]] are not part of Cool; they are used in the grammar as a meta-symbol to show association of grammar symbols (e.g. a[[bc]]+^ means a followed by one or more bc pairs).

11.1 Precedence

The precedence of infix binary and prefix unary operations, from highest to lowest, is given by the following table:

. @ ~ isvoid

  • /

<= < = not <-

All binary operations are left-associative, with the exception of assignment, which is right-associative, and the three comparison operations, which do not associate.

12 Type Rules

This section formally defines the type rules of Cool. The type rules define the type of every Cool expression in a given context. The context is the type environment, which describes the type of every unbound identifier appearing in an expression. The type environment is described in Section 12.1. Section 12. gives the type rules.

program ::= [[class; ]]+

class ::= class TYPE [inherits TYPE] { [[feature; ]]∗} feature ::= ID( [ formal [[, formal]]∗^ ] ) : TYPE { expr } | ID : TYPE [ <- expr ] formal ::= ID : TYPE expr ::= ID <- expr | expr[@TYPE].ID( [ expr [[, expr]]∗^ ] ) | ID( [ expr [[, expr]]∗^ ] ) | if expr then expr else expr fi | while expr loop expr pool | { [[expr; ]]+} | let ID : TYPE [ <- expr ] [[, ID : TYPE [ <- expr ]]]∗^ in expr | case expr of [[ID : TYPE => expr; ]]+esac | new TYPE | isvoid expr | expr + expr | expr − expr | expr ∗ expr | expr / expr | ˜expr | expr < expr | expr <= expr | expr = expr | not expr | (expr) | ID | integer | string | true | false

Figure 1: Cool syntax.

The rule should be read: In the type environment for objects O, methods M , and containing class C, the expression e has type T. The dots above the horizontal bar stand for other statements about the types of sub-expressions of e. These other statements are hypotheses of the rule; if the hypotheses are satisfied, then the statement below the bar is true. In the conclusion, the “turnstyle” (“”) separates context (O, M, C) from statement (e : T ). The rule for object identifiers is simply that if the environment assigns an identifier Id type T , then Id has type T. O(Id) = T O, M, C Id : T

[Var]

The rule for assignment to a variable is more complex: O(Id) = T O, M, C e 1 : T ′ T ′^ ≤ T O, M, C Id ← e 1 : T ′^

[ASSIGN]

Note that this type rule—as well as others—use the conformance relation ≤ (see Section 3.2). The rule says that the assigned expression e 1 must have a type T ′^ that conforms to the type T of the identifier Id in the type environment. The type of the whole expression is T ′. The type rules for constants are all easy:

O, M, C ` true : Bool [True]

O, M, C ` f alse : Bool [False]

i is an integer constant O, M, C ` i : Int

[Int]

s is a string constant O, M, C ` s : String

[String]

There are two cases for new, one for new SELF TYPE and one for any other form:

T ′^ =

{ SELF TYPEC if T = SELF TYPE T otherwise O, M, C ` new T : T ′^ [New]

Dispatch expressions are the most complex to type check. O, M, C e 0 : T 0 O, M, C e 1 : T 1 .. . O, M, C ` en : Tn

T 0 ′ =

{ C if T 0 = SELF TYPEC T 0 otherwise M (T 0 ′, f ) = (T 1 ′,... , T (^) n′, T (^) n′+1) Ti ≤ T (^) i′ 1 ≤ i ≤ n

Tn+1 =

{ T 0 if T (^) n′+1 = SELF TYPE T (^) n′+1 otherwise O, M, C ` e 0 .f (e 1 ,... , en) : Tn+

[Dispatch]

O, M, C e 0 : T 0 O, M, C e 1 : T 1 .. . O, M, C ` en : Tn T 0 ≤ T M (T, f ) = (T 1 ′,... , T (^) n′, T (^) n′+1) Ti ≤ T (^) i′ 1 ≤ i ≤ n Tn+1 =

{ T 0 if T (^) n′+1 = SELF TYPE T (^) n′+1 otherwise O, M, C ` e 0 @T.f (e 1 ,... , en) : Tn+ [StaticDispatch]

To type check a dispatch, each of the subexpressions must first be type checked. The type T 0 of e 0 determines which declaration of the method f is used. The argument types of the dispatch must conform to the declared argument types. Note that the type of the result of the dispatch is either the declared return type or T 0 in the case that the declared return type is SELF TYPE. The only difference in type checking a static dispatch is that the class T of the method f is given in the dispatch, and the type T 0 must conform to T. The type checking rules for if and {-} expressions are straightforward. See Section 7.5 for the definition of the t operation.

O, M, C e 1 : Bool O, M, C e 2 : T 2 O, M, C e 3 : T 3 O, M, C if e 1 then e 2 else e 3 fi : T 2 t T 3 [If]

O, M, C e 1 : T 1 O, M, C e 2 : T 2 .. . O, M, C en : Tn O, M, C { e 1 ; e 2 ;... en; } : Tn

[Sequence]

The let rule has some interesting aspects.

T 0 ′ =

{ SELF TYPEC if T 0 = SELF TYPE T 0 otherwise O, M, C e 1 : T 1 T 1 ≤ T 0 ′ O[T 0 ′/x], M, C e 2 : T 2 O, M, C ` let x : T 0 ← e 1 in e 2 : T 2

[Let-Init]

First, the initialization e 1 is type checked in an environment without a new definition for x. Thus, the variable x cannot be used in e 1 unless it already has a definition in an outer scope. Second, the body e 2 is type checked in the environment O extended with the typing x : T 0 ′. Third, note that the type of x may be SELF TYPE.