ARRAY BEGIN BODY ELSE ELSIF END EXIT IF LOOP PROCEDURE READ RECORD THEN TYPE WHEN WRITE PACKAGE PRIVATE IN OUT IS OFConstants are either integer, float, or string. Integer constants contain only digits. Float constants contain a decimal point. At least one digit must both precede and follow the decimal point. String constants begin and end with a double quote (") and contain any sequence of printable characters. If a double quote appears inside a string constant, it must be repeated (e.g. """Help!"" he cried"). The allowable range of integer constants and the range and precision of float constants are implementation-defined.
Let letter = 'A'..'Z','a'..'z'; digit = '0'..'9'; using a regular expression notation in which ',' represents set union , '.' represents set product, '*' represents Kleene closure, LAMBDA (λ) represents the null string, NOT represents set complement, and literals are delimited by quotes ('), the above definitions may be made more precise:
CONSTANT = INTCONST , FLOATCONST , STRCONST INTCONSTANT = digit . (digit)* FLOATCONSTANT = INTCONSTANT . '.' . INTCONSTANT STRCONSTANT = '"' . (NOT('"') , '"' . '"')* . '"'Identifiers are strings of letters and digits starting with a letter (not to include the reserved keywords). Thus identifiers can be specified as follows, where RESERVED represents the set of reserved keywords:
IDENTIFIER = (letter , (letter , digit)*) - RESERVEDThe length of an identifier must not exceed 32 characters, but all characters of a legal identifier must be used in determining its uniqueness.
The following are the remaining operators and delimiters ($EOF$ represents end-of-file):
ASSIGNOP = ":=" MULTOP = "*" , "/" PLUSOP = "+" , "-" RELOP = "<" , "<=" , ">" , ">=" , "=" , "/=" DELIM = ".." , ":", ";" , "," , "." , "(" , ")" , "$EOF$"Adjacent identifiers, reserved words, and numeric literals must be separated by white space (or comments). This guarantees that there is no ambiguity in how tokens are scanned (e.g. 'begina' is one identifier, not 'begin' followed by 'a').
*** In CS524, we will implement a specific type of Boolean since we will not work with Floats and I think it's important to deal with more than just integer types. ks **
TYPEIS ;
TYPE fubar IS integer;defines a new type, fubar, which behaves exactly like an integer. To describe more complex type structures, Macro provides two type specification forms for creating either a record or an array.
Example: TYPE arr1 IS ARRAY(1..9) OF integer; TYPE arr2 IS ARRAY(0..1) OF arr1; TYPE arr3 IS ARRAY(0..1) OF ARRAY(1..9) of integer;
TYPEFor example:IS RECORD END RECORD;
TYPE R1 IS RECORD i, j : float; c : integer; END RECORD; TYPE R IS RECORD e : integer; f : r1; END RECORD;A component declaration is syntactically identical to a variable declaration (see below). The field names within a record are local to that record and need be unique in that record only. As with arrays, a particular component may also be specifiec by a record form.
id, id, ..., id :;
Example: a, b : integer; c : RECORD a, b : integer; END RECORD;
If we use the above definition of record r to obtain
a, b : ARRAY(1 .. 10) OF r,all of the following are legal names:
a(1).e, a(1).f.c., b(I).fbut the following are illegal:
b(1).i, a.e, a.e(1), f.j.c
Operators at the same precedence level are evaluated from left to right. All operators are left-associative except the relational operators, which are not associative at all.
Example:
Given TYPE t1 IS ARRAY(1..10) OF float; TYPE t2 IS ARRAY(1..10) OF float; a, b : t1; c, d : t2; e, f : ARRAY(1..10) OF float;a := b, c := d and e := f are legal, but a := c and c := e are illegal.
There is however, one conversion which the compiler should recognize and perform. Namely, when an arithmetic expression contains both integers and floats, then the integers shall be coerced into floats and the result given as a float. Take special note that the reverse is not true, that is a float is never coerced into being an integer.
IF Boolean Expression THEN sequence of statements ELSIF Boolean Expression THEN sequence of statements . . . ELSIF Boolean Expression THEN sequence of statements ELSE sequence of statements END IFThe ELSE clause may be omitted. The semantics are the same as for the IF statement in PASCAL.
This is a "loop forever", but EXIT can be used to terminate iteration (see below). The ": LOOP sequence of statements END LOOP;
READLN
statement. writes in SPIM can either be WRITE or WRITELN ks 8/93 **
In programming language design, a recurring problem is whether to treat READ and WRITE as statement types, or as predefined procedures. There are problems with both approachs. Considering READ and WRITE to be predefined procedures (as Ada does) isn't really satisfactory because (unlike ordinary procedures), we'd like them to take an indefinite number of arguments and perhaps allow non-standard parameter syntax (such as "width specifications"). On the other hand, reserving READ and WRITE seems to obviate the chance of allowing users to extend these statements to handle new data types. In Macro, we'll essentially treat READ and WRITE as predefined procedures that are overloaded. To allow lists of input or output items (which Ada doesn't really support) for the commonly used READ and WRITE procedures, we'll introduce a notational extension in which only the procedures for READ and WRITE may contain an arbitrarily long list of parameters. Thus
WRITE ("Date =", month, day, year);would be allowed.
In Macro, only scalars may be read. That is, the standard package contains definitions for READ that accept integer and float values. READ is not predefined to handle arrays or records, though user-defined routines for such types can be written.
READ can handle components of structured types, as long as the components are scalars. Our preprocessor forces READs to handle items left to right so, e.g.,
READ (i, a(i)); is legal.
At the beginning of every subprogram is a list of all the formal parameters of the procedure. For each parameter its position, name, type and mode (IN, OUT, or IN OUT) is specified (the default is IN).
Example:
PROCEDURE F (x : float; y, z : IN OUT integer)Three formal parameters (x, y, z) are declared. All formal parameters are considered to be local to the body of the subprogram.
The types of all formal parameters must be specified with a type name. An explicit type generator may not be used.
Macro subprograms follow the same scope rules as Pascal: Every variable is automatically imported into any function, or procedure contained within the definition, unless that inner definition contains another declaration of the same variable. Macro allows no forward references, so the scope of a declaration actually extends from the point of its definition to the end of its containing scope.
A procedure can also declare local constants, variables, types and procedures. The general structure of a procedure body definition is like that of PASCAL:
PROCEDUREThe (( ) IS type, variable and subprogram declarations BEGIN Statement list END;
PROCEDURE( );
A conditional EXIT is implied by a "WHEN clause". This is of the form:
EXITThe EXIT is performed only if the Boolean expression is true. Why Ada doesn't simple use an IF statement is an interesting question. It appears they decided to "go for barroque."WHEN
PACKAGEThe PRIVATE part may be ommitted if there are no private types.IS type, variable and subprogram declarations PRIVATE type declarations BODY type, variable and subprogram definitions BEGIN Statement list END;
The type, variable and subprogram headers that appear in package declaration section can be made visible to other packages. They are therefore called the visible part of the package. Objects can be referenced by qualifying the object's name with the name of the package in which it appears. Thus P.A would name object A in package P.
We wish to use packages to implement abstract data types, but this leads to a problem. If we place the definition of a type in the visible part of a package, it's implementation is visible outside the package. This is undesirable, as we wish to characterize an abstract data type by its operations, not its implementation. Declarations placed in the body of a package are never visible outside the package, so the definition of an abstract can't be placed there. The PRIVATE part of a package declaration exits to address this problem. In the visible part of the package, the type is decalared to be PRIVATE. The implementation of the type is hidden in the PRIVATE part. Thus is the declaration
PACKAGE SetStuff IS -- This is the visible section TYPE Set IS PRIVATE; PROCEDURE InSet (i : integer; s : Set; j : OUT integer); PRIVATE -- This section shows the implementations of items -- in the visible section if the implementation wasn't -- given there. TYPE Set IS ARRAY(1 .. 10) OF integer; PROCEDURE InSet (i : integer; s : Set; j : OUT integer) IS BEGIN j := s(i); END; BODY -- The body section is for initialization, first -- the declarations are given... i : INTEGER; BEGIN -- and then the code! READ (i); s(i) := 1; END;The name is made visible, but not the fact that it is implemented as an array. If the full declaration were in the visible part, its implementation would be visible. By making types private, we can guarantee that they are manipulated only by operations defined in the package itself. All declarations in the package body are completely hidden outside the package. Declarations in the specification part are visible in the body, which is responsible for implementing the subprograms declared in the specification part.
The statements (if any) in the package body following the declarations are executed when the package body is executed. Package bodies are executed in the order in which package bodies are sequenced. Packages declarations may appear anywhere that a procedure, type or variable declaration can appear, and thus may be nested.
program -> {package-decl} $EOF$ package-decl -> PACKAGE IDENTIFIER IS {declaration} [private-decl] [body] END ; private-decl -> PRIVATE {declaration} body -> BODY {declaration} [ BEGIN statement {statement} ] declaration -> IDENTIFIER {, IDENTIFIER} : type ; -> TYPE IDENTIFIER IS type ; -> PROCEDURE IDENTIFIER [formals-list] [proc-body] ; -> package-decl type -> variable -> PRIVATE -> ARRAY ( expression .. expression ) OF type -> RECORD component {component} END RECORD component -> IDENTIFIER {, IDENTIFIER} : type ; proc-body -> IS {declaration} BEGIN statement {statement} END formals-list -> ( formal-param {; formal-param} ) formal-param -> IDENTIFIER {, IDENTIFIER} : [mode] type mode -> IN OUT -> IN -> OUT statement -> READ ( expression {, expression} ) ; -> WRITE ( write_expr {, write_expr} ) ; -> IF boolean THEN statement {statement} {ELSIF boolean THEN statement {statement}} [ELSE statement {statement}] END IF ; -> variable [:= expression] ; -> [IDENTIFIER :] LOOP statement {statement} END LOOP ; -> EXIT [IDENTIFIER] [WHEN boolean] ; write_expr -> STRINGCONSTANT -> expression boolean -> expression relational-op expression relational-op-> > -> < -> = -> >= -> <= -> /= expression -> expression add-op expression -> expression mult-op expression -> ( expression ) -> [unary-op] expression -> variable -> INTCONSTANT -> FLOATCONSTANT unary-op -> - -> + add-op -> + -> - mult-op -> * -> / variable -> IDENTIFIER var-rest var-rest -> ( expression {, expression} ) var-rest -> . variable ->If you look carefully at the above BNF it is apparent that it will allow the construction of some syntactic structures which are illegal. While this is true, it is being done for two reasons: