Next: TypeProp, Previous: Overloading, Up: Top
Most language definitions allow a user to supply operands of one type
to an operator requiring operands of another under certain circumstances.
For example, it would be reasonable for the extended version of Mystery
(see Overloaded Indications and Procedures)
to allow an addition with one integer and one real operand.
In that case, analysis computation would assume a coercion operator
to convert the value of the integer operand to a real value.
The rAddOpr would then be applied to the result of the coercion and
the original real operand.
The Expression module uses applicable coercions automatically,
provided that one or more coercion operators have been defined.
Like normal operators, the language-defined coercions can be specified in
the file whose name is the referto parameter of the PreDefOp
module
(see Language-Defined Operators).
PreDefCvt(`opr',`sig'), specifies a coercion:
PreDefCvt and PreDefOpr
(see Relating operators to indications)
invocations with the same `opr' values are not allowed.
A coercion from integer to real for the extended Mystery can be specified by:
PreDefCvt(iTor, (intType):realType);
The known key iTor is defined by the module.
No further specification is necessary.
A coercion defines an edge of the coercion graph, a directed graph
whose nodes correspond to types used in the program.
If the coercion graph contains cycles, then it is difficult to describe the
semantics of an expression.
For example, suppose that we defined a coercion from real values to integer
values in addition to iTor, creating a cycle in the coercion graph.
Given that cycle, what is the effect of the following expression?
1.2 + 3
The extended Mystery has only two operators for which PlusInd
is the indication: iAddOp and rAddOp.
Should we coerce 1.2 to an integer value and use iAddOp,
or coerce 3 to a real value and use rAddOp?
Of course we could define the language in such a way that this decision is unambiguous, and guarantee the appropriate coercions by defining them with appropriate costs (see Overload resolution), but the definition would be more complex because of the cycle. Thus we recommend that the coercion graph not contain cycles.
A cast context is an expression context in which a specific type is required. For example, consider the extended Mystery program:
VAR i: INTEGER; VAR a: REAL;
BEGIN i := 1; i := 2.3; a := 4; a := 5.6 END;
In each assignment, the type of the left-hand side determines the type that must be yielded by the right-hand side (see Values).
CastContext(`type',`rator',`expr')
is used to provide the necessary type computations:
If a result of type `type' cannot be obtained directly
from the tree rooted in `expr',
a cast operator can be selected from those overloading
the indication `rator'.
Argument `type' is a definition table key, `rator' is a grammar
symbol playing the OperatorSymbol role, and `expr' is a grammar
symbol playing the ExpressionSymbol role.
Here is the definition of assignment for extended Mystery:
RULE: Stmt ::= Expr ':=' Expr
COMPUTE
CastContext(Expr[1].Type,,Expr[2]);
Indication(castInd);
ChkRator;
END;
Consider how the type analysis would proceed for each of the assignments in our extended Mystery program:
i := 1i := 2.3castInd indication must be
available to provide the conversion.
a := 4a := 5.6The cast operator might be defined by:
PreDefOpr(castInd, rToi, (realType):intType)
Many languages also provide explicit cast contexts, which break the normal type relationship between two adjacent expression nodes (see Values). Here is an example, using C syntax:
RULE: Expr ::= '(' Type ')' Expr
COMPUTE
PrimaryContext(Expr[1],Type.Type);
CastContext(Type.Type,,Expr[2]);
Indication(castInd);
ChkRator;
END;
The type of Expr[1] is known to be of the type Type,
and therefore Expr[1] acts as a leaf of the expression
tree containing it.
Expr[2], on the other hand, acts as the root of an expression tree:
the type of value it should produce is known.