[Next] [Previous] [Up] [Top] [Contents] [Index]

2.6 Precedence Declarations

2.6.2 Precedence and Associativity Declarations in Cecil

Cecil allows the precedence and associativity of infix operators to be specified by programmers through precedence declarations. The syntax of these declarations is as follows:

prec_decl	::= "precedence" op_list [associativity] {precedence} ";"
associativity	::= "left_associative" | "right_associative" | "non_associative"
precedence	::= "below" op_list | "above" op_list | "with" op_list
op_list	::= op_name { "," op_name }

For example, the following declarations might appear as part of the standard prelude for Cecil:

precedence ** right_associative;  -- exponentiation
precedence *, / left_associative below ** above +;
precedence +, - left_associative below * above =;
precedence =, !=, <, <=, >=, > non_associative below * above;
precedence & left_associative below = above |;
precedence | left_associative below &;
precedence % with *;
precedence ! left_associative above =;  -- array indexing

By default, an infix operator has its own unique precedence, unrelated to the precedence of any other infix operator, and is non-associative. Expressions mixing operators of unrelated precedences or multiple sequential occurrences of an operator that is non-associative must be explicitly parenthesized.

The effect of a precedence declaration is to declare the relationship of the precedences of several binary operators and/or to specify the associativity of a binary operator. Like SML, the information provided by a precedence declaration is used during the scope of the declaration, and declarations of the same operator at one scope override any from an enclosing scope. Two precedence declarations cannot define the precedence of the same operator in the same scope.

A precedence declaration of the form

precedence bin-op1, ..., bin-opn
	associativity
	below bin-opB1, ..., bin-opBn
	above bin-opA1, ..., bin-opAn
	with bin-opW1, ..., bin-opWn;

declares that all the bin-opi belong to the same precedence group, and that this group is less tightly binding than the precedence groups of any of the bin-opBi and more tightly binding than those of the bin-opAi. If any bin-opWi are provided, then the bin-opi belong to the same precedence group as the bin-opWi; all the bin-opWi must already belong to the same precedence group. Otherwise, the bin-opi form a new precedence group. The associativity of the bin-opi is as specified by associativity, if present. If absent, then the associativity of the bin-opi is the same as the bin-opWi, if provided, and non-associative otherwise. As illustrated by the example above, the ordering of two precedence groups may be redundantly specified. Cycles in the tighter-binding-than relation on precedence groups are not allowed. All operators in the same precedence group must have the same associativity.

Taken together, precedence declarations form a partial order on groups of infix operators. Parentheses may be omitted if adjacent infix operators are ordered according to the precedence declarations, or if adjacent infix operators are from the same precedence group and the precedence group has either left- or right-associativity. Otherwise, parentheses must be included. For example, in the expression

v ! (i + 1) < (v ! i) + 1

the parentheses around i+1 and v!i are required, since ! and + are not ordered by the above precedence declarations. However, both ! and + are more tightly binding than <, so no additional parentheses are required.

In Cecil, a declaration within a declaration block is visible throughout the block, including during textually earlier declarations within the block. This applies to precedence declarations as well, somewhat complicating parsing. The implementation strategy used in the UW Cecil system parses expressions involving binary operators into a list of operators and operands, and these lists are converted into a traditional parse tree form only after all visible declarations have been processed.

Precedence declarations apply to infix message names, not to individual methods. Multiple methods may implement the same infix message, for different kinds of arguments, but all methods with a particular name share the same precedence in a given scope.