|
|
|
|
|
3. SYNTAX
A syntax declaration defines the way a usage of a statement or operator must be written. For example a call of the 'not' operator looks like:
not okay
To describe the syntax of the 'not' operator we write:
$ syntax expr: .not.() is <- 13;
This means that a 'not' expression is constructed with the symbol 'not' followed by a parameter. The place of the parameter is marked with the () sign. The syntax description contains no information about the types of the parameters. At the syntax level a parameter may be anything. With '<-' the associativity of the 'not' operator is specified as right associative. This means that the right operand is allowed to have the same priority as the operator symbol. So the expression
not not okay
is legal and means
not (not okay)
When the associativity of the 'not' operator is specified with '->' instead of '<-' the 'not not' expression above is not legal. With 13 the priority of the whole 'not' operator is determined. As convention priorities from 1 to 20 are used by operators and priority 25 is used by statements. Arithmetic operators have priorities from 1 to 11 and comparisons have priority 12. To define the 'not' operator completely there must be also a semantic definition which is as follows:
const func boolean: not (in boolean: aBool) is func
result
var boolean: result is FALSE;
begin
if aBool then
result := FALSE;
else
result := TRUE;
end if;
end func;
In the declaration the 'not' operator is written exactly in the same way it is written when it is called. The syntax definition is used at both places: declaration and call. The syntax and semantic declarations define precisely how the 'not' operator works. As next example we try an infix operator like the 'and' operator. A call of the 'and' operator may look like:
okay and not error
To describe the syntax of the 'and' operator we write:
$ syntax expr: ().and.() is -> 14;
This means that an 'and' expression is constructed with the symbol 'and' surrounded by parameters. The '->' defines the 'and' operator as left associative. This means that an expression like
A and B and C
is interpreted as
(A and B) and C
With 14 the priority of the whole 'and' operator is determined. Since priority 14 is weaker than the priority of the 'not' operator which is 13 the example expression is evaluated as:
okay and (not error)
Note that the expression
okay and not error
makes no sense when the 'and' operator has priority 12 instead of 14. As next let's try the syntax declaration of a statement. For example a call of the while-statement looks like:
while element_index > 0 and okay do
processElement;
write(".");
end while;
To describe the syntax of the while-statement we write:
$ syntax expr: while.().do.().end.while is -> 25;
This means that the while-statement is an expression with the symbols 'while', 'do', 'end' and 'while'. With '->' the associativity of the while-statement is specified as left associative. The associativity has no meaning for the while-statement since there is no parameter before the first symbol or after the last symbol. With 25 the priority of the whole while-statement is determined. The semantic definition of the while-statement is as follows:
const proc: while (ref func boolean: condition) do
(ref proc: statement) end while is func
begin
if condition then
statement;
while condition do
statement;
end while;
end if;
end func;
The syntax definition is used at the declaration and at the call. This declaration defines precisely how the while-statement works. It is based on the if-statement and uses recursion to emulate the repetition of the loop body. Another example for a syntax description is the repeat-statement
repeat
processElement;
write(".");
until element_index = 0 or not okay;
which has the following syntax description:
$ syntax expr: repeat.().until.() is -> 25;
This means that the repeat-statement is an expression with the symbols 'repeat' and 'until' and a parameter between 'repeat' and 'until' and after 'until'. With 25 the priority of the whole repeat-statement is determined. With '->' the associativity of the repeat-statement is specified as left associative. This allows priorities from 0 to 24 for the parameter after 'until'. Since statements have priority 25 it is not possible to write a statement direct behind 'until'. As next let's take a more complex statement: The if-statement. A usage of the if-statement might be:
if okay then
writeln("okay");
else
writeln("not okay");
end if;
As syntax description we use
$ syntax expr: .if.().then.().end.if is -> 25;
$ syntax expr: .if.().then.().else.().end.if is -> 25;
Note that this description allows if-statements with and without else-parts. As semantic description we use
const proc: if (in boolean: condition) then
(in proc: statement)
end if is func
begin
case condition of
when {TRUE}: statement;
end case;
end func;
const proc: if (in boolean: condition) then
(in proc: statement1)
else
(in proc: statement2)
end if is func
begin
case condition of
when {TRUE}: statement1;
when {FALSE}: statement2;
end case;
end func;
We define the two forms of the if statement based on the case statement. A more complex if-statement can be:
if number < 0 then
write("less");
elsif number = 0 then
write("equal");
else
write("greater");
end if;
Here we use a more complex syntax description:
$ syntax expr: .if.().then.().end.if is -> 25;
$ syntax expr: .if.().then.().().end.if is -> 25;
$ syntax expr: .elsif.().then.() is <- 60;
$ syntax expr: .elsif.().then.().() is <- 60;
$ syntax expr: .else.() is <- 60;
Note that the second line of syntax description is different to the syntax description of the previous example. The 'else' is taken out to form an expression of its own. |
|