BINARY-NAT contains a rich set of notational models for the specification of operators, and illustrates clearly the idea of an extended signature for a module. Nevertheless, the operators in this module will generate, in most cases, complex and subtle inconsistencies, ambiguities and unintuitive results, as shown in the following examples. The concepts of precedence and gathering provide a flexible way of avoiding these ambiguities without having to write unnecessary parentheses.
Maude> red 1 0 + 1 0 . result Bits: 1 1 0
The expected result is 1 0 0. The reason for getting the unexpected result is that Maude is really processing the term 1 (0 + 1) 0, which generates 1 1 0.
Maude> red not 0 1 0 . result Bits: 1 1 0
Maude> red (1 0) + (1 0) * (1 0) . result Bits: 1 1 0 Maude> red (1 0) * (1 0) + (1 0) . result Bits: 1 0 0 0
Maude detects this ambiguity, and selects randomly one of the parses, which may lead to unexpected results in more complex terms.
Maude> red 1 1 > 1 ? 1 : 0 . WARNING: <standard input>, line 894: Ambiguous term, two parses are: (1 1) > 1 ? 1 : 0 -versus- 1 (1 > 1) ? 1 : 0 Arbitrarily taking the first as correct. result Bit: 1
At this point we may say a few words about the treatment of ambiguities in Maude. The MSCP parser obtains all parses of a term. However, since the number of ambiguous parses can sometimes be quite large, Maude presents only two of them to show the ambiguity, and lets the user solve the problem. For example, the following expression in fact has three different parses, but only two are given.
Maude> red 1 1 1 > 1 ? 1 : 0 . WARNING: <standard input>, line 895: Ambiguous term, two parses are: (1 1 1) > 1 ? 1 : 0 -versus- 1 ((1 1) > 1) ? 1 : 0 Arbitrarily taking the first as correct. result Bit: 1
The third parse is 1 1 (1 > 1) ? 1 : 0.
The precedence of infix operators determines the order in which the operators are to be applied. For example, given two operators delta1 and delta2, if delta1 takes precedence over delta2, this means that an expression like E1 delta2 E2 delta1 E3 will be evaluated as E1 delta2 (E2 delta1 E3). The operator _*_ takes precedence over (has a higher precedence than) the operator _+_, and this is the reason why we expect that (1 0) + (1 0) * (1 0) should be parsed as (1 0) + ((1 0) * (1 0)).
Let us now consider expressions with the same operator appearing several consecutive times. For associative operators, this is not a problem, as it happens in:
Maude> red (1 1) + (1 1) + (1 1) + (1 1) . result Bits: 1 1 0 0
But for nonassociative operators, the so-called syntactic order of evaluation18, that is, the order in which the operator should be associated in several contiguous occurrences, may modify the result. In other words, expressions with nonassociative operators appearing consecutively are in fact grammatically ambiguous:
Maude> red (1 1) ^ (1 1) ^ (1 1) . WARNING: <standard input>, line 896: Ambiguous term, two parses are: (1 1) ^ ((1 1) ^ 1 1) -versus- ((1 1) ^ 1 1) ^ 1 1 Arbitrarily taking the first as correct. result Bits: 1 1 0 1 1 1 0 1 1 1 1 0 1 1 1 1 0 0 1 0 0 0 0 0 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 0 1 1
We can distinguish two types of operators from the point of view of their syntactic order of evaluation:
Although all the different parenthesized associations of an associative operator are guaranteed to yield the same result, nevertheless, as far as their syntactic order of evaluation is concerned such operators are grammatically ambiguous. In the extended signature of a module Maude incorporates rules to solve this ambiguity (Section 2.7.3). For nonassociative infix operators this source of ambiguity is solved by defining an order of evaluation.
These syntactic problems (grammatical scope, precedence and syntactic order of evaluation of operators) are solved in OBJ3 [27] and in Maude by means of precedence and gathering patterns.
In Maude, each operator has associated to it a precedence value and a gathering pattern. They can be specified by the user by means of the precedence (abbreviated prec) and gather attributes. If not specified, Maude assigns default values as explained in Section 2.7.5.
A precedence value is an integer greater than or equal to 0, which may be understood as an output value associated to terms having the corresponding operator as their top symbol.
On the other hand, gathering patterns are associated to the arguments of an operator. Gathering patterns are given as nonempty sequences of the following possible pattern:
We can illustrate the notions of precedence and gathering by considering a variant BINARY-NAT-PREC of the module BINARY-NAT whose only difference is that we have now specified precedence values and gathering patterns for the operators as follows.
op __ : Bits Bits -> Bits [assoc prec 1 gather (e E)] . op |_| : Bits -> MachineInt . *** Length op not_ : Bits -> Bits [prec 2 gather (E)] . op normalize : Bits -> Bits . op _+_ : Bits Bits -> Bits [assoc comm prec 5 gather (E e)] . op _*_ : Bits Bits -> Bits [assoc comm prec 4 gather (E e)] . op _^_ : Bits Bits -> Bits [prec 3 gather (e E)] . op _>_ : Bits Bits -> Bool [prec 6 gather (E E)] . op _?_:_ : Bool Bits Bits -> Bits [prec 7 gather (& E E)] .
Constants have precedence 0. In our example, this rule is applied to 0, 1 and nil. The precedence value of an operator is associated to the terms generated by this operator. In our example, the term 1 has precedence 0, the term 1 0 1 has precedence 1, the term 1 0 + 1 has precedence 5, and 1 0 * 1 has precedence 4.
To illustrate the behavior of the gathering patterns, let us focus on the declaration of the _*_ operator. From it we can infer the following consequences:
The simultaneous use of precedence and gathering attributes allows specifying any kind of precedence relations between operators and different types of syntactic order of evaluation.
To show in practice how this strategy works, we analyze some of the problems detected in BINARY-NAT using the signature of BINARY-NAT-PREC.
The first problem appeared with the term 1 0 + 1 0. Without any
information about precedence values and gathering patterns, we can think of
two different analyses for this term: (1 0) + (1 0) and
1 (0 + 1) 0. This ambiguity can be solved by the use of different
precedence values for the operators involved, namely, __ and
_+_. In the module BINARY-NAT-PREC these operators have
been defined as follows:
op __ : Bits Bits -> Bits [assoc id: nil prec 1 gather (e E)] .
op _+_ : Bits Bits -> Bits [assoc comm prec 5 gather (E e)] .
Let us consider the two previous analyses proposed for the term
1 0 + 1 0:
Of course, using parentheses is always a way of resolving ambiguities. In fact, it is worth noting that the parentheses operator automatically included in the extended signature of a module has precedence 0. Therefore, the term 1 (0 + 1) 0 is grammatically correct in BINARY-NAT-PREC.
Maude> red 1 0 + 1 0 . result Bits: 1 0 0 Maude> red 1 (0 + 1) 0 . result Bits: 1 1 0
Another subtle problem in the BINARY-NAT module was the different behaviors of the terms (1 0) + (1 0) * (1 0) and (1 0) * (1 0) + (1 0). In that module, both operators _+_ and _*_ had no precedence attribute explicitly given. As both had the assoc attribute, the gathering patterns generated by default for both of them was (e E), so the operators were associated from right to left.
In BINARY-NAT-PREC these two operators have different precedence values, and the gathering patterns have also been specified:
op _+_ : Bits Bits -> Bits [assoc comm prec 5 gather (E e)] . op _*_ : Bits Bits -> Bits [assoc comm prec 4 gather (E e)] . op _^_ : Bits Bits -> Bits [prec 3 gather (e E)] .
The main consequence of this definition is that, as already explained, an addition cannot be an argument of a multiplication unless it is enclosed in parentheses. Thus, we have the following reductions.
Maude> red (1 0) + (1 0) * (1 0) . result Bits: 1 1 0 Maude> red (1 0) * (1 0) + (1 0) . result Bits: 1 1 0
The following examples illustrate how the precedence and gathering model of the operators of BINARY-NAT-PREC solves the problems of precedence and order of evaluation of operators.
op _+_ : Bits Bits -> Bits [assoc comm prec 5 gather (E e)] . op _*_ : Bits Bits -> Bits [assoc comm prec 4 gather (E e)] .
Taking into account the precedences of these operators, the expression will be evaluated by Maude as an addition of a number and a multiplication.
Maude> red 1 0 + 1 0 * 1 0 . result Bits: 1 1 0
Figure 4 shows the correct interpretation of this term in the signature of BINARY-NAT-PREC, that is, 1 0 + (1 0 * 1 0), while Figure 5 represents the incorrect interpretation (1 0 + 1 0) * 1 0 and the precedence/gathering error that avoids such an interpretation.
Figure 4: Correct interpretation of 1 0 + 1 0 * 1 0.
Figure 5: Incorrect interpretation of 1 0 + 1 0 * 1 0.
op _^_ : Bits Bits -> Bits [prec 3 gather (e E)] .
Without parentheses, which modify the precedence and order of evaluation of the operators, the term 1 0 1 1 1 0 is evaluated using the right to left gathering pattern of the operator __. Figures 6 and 7 draw graphically the correct and incorrect interpretation of this term.
Maude> red 1 0 ^ 1 1 ^ 1 0 . result Bits: 1 0 0 0 0 0 0 0 0 0 Maude> red (1 0 ^ 1 1) ^ 1 0 . result Bits: 1 0 0 0 0 0 0
Figure 6: Correct interpretation of 1 0 1 1 1 0.
Figure 7: Incorrect interpretation of 1 0 1 1 1 0.