arithmetic ::= < arithlist > arithlist ::= arith | arithlist ; arith arith ::= assignment | call | relation | function assignment ::= identifier <- expression expression ::= function | primary | expression op primary | expression ↑ integer | expression ↑ - integer primary ::= identifier | - integer | integer op ::= + | - | & | ! | : function ::= identifier subarg subarg ::= [ nodename ] | [ expression ] call ::= identifier subarg relation ::= identifier rop expression rop ::= = | # | > | <
Translators generated by TREE-META have a set of 50 global integer variables which can be set and reset as a program is being compiled. The system provides a set of functions and subroutines which, together with assignment statements, allows these variables to be manipulated. Comparisons involving these variables can be used to control the output produced by the compiler.
A possible item in an output alternative is a set of statements involving these variables and enclosed between the two symbols < and >. The statements are separated by semicolons and consist of either assignment statements, subroutine calls, relations or functions. The last two generate a truth value which will be the result of the complete item if it is the last statement in the list.
There are no formal declarations for these variables. The first appearance of a variable in an arithmetic statement will cause it to be added to the set already declared. The names of the variables can be of any length. The three variables, TYPE, VALUE and LEVEL, are predeclared by the system. There is nothing to stop these being used in any way. However, they do have special significance in connection with Symbol Rules described in Chapter 7. It is, therefore, sensible for the user to keep these variables for this specific use.
A typical Code Rule involving arithmetic statements might be:
STORE[-] => < A<-0 ; OUT[CV] ; B<-CV+l > *1 ;
This statement uses the variables A, CV and B.
In its simplest form, an assignment statement consists of an identifier followed by the symbol <- and a simple primary on the right-hand side. The primary can be:
Typical examples are:
A<-B ; C<-3 ; D<- -7 ;EE<-LEN[*1] ; FG<-POP[0]
These simple assignments set the left-hand side variable to the value of the expression on the right-hand side. The meanings of the possible functions are given in Section 6.4.
The simple assignment statement can be extended by following it with any number of operator/operand pairs. All operators are of equal precedence so that evaluation of the right-hand side proceeds strictly from left to right. The possible operator/operands pairs are:
Meaning | Operator | Operand |
---|---|---|
ADD | + | identifier, positive or negative integer |
SUBTRACT | - | identifier, positive or negative integer |
AND | & | identifier, positive or negative integer |
OR | ! | identifier, positive or negative integer |
EXCLUSIVE OR | : | identifier, positive or negative integer |
SHIFT LEFT | ↑ | positive or negative integer |
An example is:
<A<-6 l; B<- 2+A-3&4↑-1 >
The variable B is set to the value 2. The intermediate operations are:
Note that right shifts are specified by placing a negative integer after the ↑ symbol.
Functions are provided which return integer results depending on the form of the node specified by the single argument of the function. For example:
A<-LEN[*1]
would set A to the length of the string which is the first branch of the current node. The function argument is enclosed in square brackets and can be a complex nodename. The only restriction is that the node specified must be a leaf of the tree which is either a string or a single character. In the examples following, the current node is assumed to be the top node of the tree:
This could have been generated by the Syntax Rule:
ST = .ID .NUM :LFT[2] .SR .HEX .LET :RGT[2] :RES[3] * ;
where the input was:
ABCD 27 'GHI' A1 C
The possible functions are:
LEN[*1 :*2] is the length of 27 which is 2 LEN[*2] is the length of GHI which is 3 LEN[*3:*2] is the length of C which is 1The string GHI is stored without the string quotes which is why the result has the value 3 and not 5.
A subroutine is provided for adding items to the System Stack. As long as items are added and retrieved in the same rule, the user need not worry about any system use of this stack. It provides a means of storing values which will be required at a later time in the code generation. For example, it might be necessary to store the current value of a variable if the rule called itself recursively.
Subroutine calls are allowed in an arithmetic list and have a similar form to function calls. The name of the subroutine is followed by a single argument enclosed in square brackets. Some subroutines have a nodename as argument while others may have a general arithmetic expression that could be the right-hand side of an assignment statement. The possible subroutines are:
< PUSH[23] ; PUSH[A+3] >
< A<-3 ; OUT[A+5] >would output the digit 8.
< OUTL[*1:*1] ; OUTL[*3:*2] >would output 4 followed by 1.
< OUTC[*3:*2] >would output the character C.
Additional functions and subroutine calls, ENTER, LOOK and CLEAR, are available to add and delete entries from the Symbol Table. These are described in detail in Chapter 7.
The complete arithmetic list will return a value true unless the last statement in the list is a relation or the function LOOK. For example:
STORE[-] => < A<-3 ; B<-2 > *1 / 'END' ;
is not a sensible Code Rule as the first item of the first alternative always returns the result true and so the second alternative will never be obeyed. However:
STORE[-] => < A<-3 ; B=2 > *1 / 'END' ;
would set A to the value 3 and then execute the first or second alternative depending on whether the variable B had the value 2.
Each relation consists of a variable followed by a relational operator and any arithmetic expression which could appear on the right-hand side of an assignment statement. The possible operators are:
= equals # not equals > greater than < less than
For example:
OUTR[] => <T#O> 'NONZERO' / 'ZERO' ;
will output NONZERO if T does not have the value zero and ZERO if it does. More complex relations are:
< T > LEN[*2:*1]-3 > < T # CONV[*1]&5↑-3 >
A relation appearing anywhere in an arithmetic list other than at the end will have no effect on the alternatives obeyed. However, it may have side effects produced by a function call within the arithmetic expression. For example:
< A#POP[0] ; B<-7 >
would pop the Stack and the result would be lost.
A major use of integer variables in TREE-META is to produce counts of certain properties of the tree. This is particularly useful in a situation where the code to be generated needs to output the number of items of a particular type before the individual items themselves are output. Consider the program:
.META PRIDS PRIDS = .ID $ ( ',' .ID :DD[2] ) '.END' :IDENTS[1] * ; IDENTS[-] => COUNT[*1] 'THERE ARE '< OUT[A] > ' IDENTIFIERS' % *1 ; COUNT[DD[-,-]] => COUNT[*1:*1] < A<-A+1 > [.ID] =>< A<-1 > ; DD[-,-] => *1 % *2 ; .END
This would accept:
ALPHA BETA GAMMA .END
and generate the tree:
The initial item in the Code Rule for IDENTS counts the number of identifiers in the tree using the rule COUNT. Note that this rule does not generate any output at all. COUNT will set the value of A to 3 in the example. The rest of the rule outputs:
THERE ARE 3 IDENTIFIERS ALPHA BETA GAMMA
Note that the recursive use of COUNT and DD allows the repetition involved in counting and outputting the identifiers.