Oil Reference Manual
The library functions are grouped according to classes of functions.
Within each class a C definition of the function is
presented, followed by a brief description of the semantics of the function.
The next section describes the five C types used and defined by the
library. These types are defined entirely by the functions in OIL's
The follow-on section describes operator identification using
set of possible types which is the most general identification
method supported by OIL.
Should a call to an OIL function for operator identification or coercion
sequence construction fail, special values are returned.
Functions are supplied to test for these special values allowing
production of error messages for these error cases.
Besides operator identification using set of possible types, there is
also a simpler identification algorithm which is strictly bottom up. It
is useful when the full power of OIL is not necessary and the efficiency
of a one pass algorithm can be utilized.
Looking at an operator's type signature is fundamental to propagating
the constraints of an operator's type signature out from the node it
labels. OIL supplies a function to examine any given operator's
Coercion sequences are fundamental to most uses of OIL and constructing
and examining coercion sequences is critical to examining them for code
Creating instances of specified classes.
OIL's CLASS construct allows for easy support of most type
constructors. OIL allows a type constructor to be specified and then
instantiated to create a type which conforms to the specification of the
Names are an enumeration of the identifiers in the specification for
easy comparisons. When a class is instantiated all its operators have
new unique signatures based on the created type, but the names of
corresponding operators are the same as those used in the specification
of the class operator.
Construction of types, operators, identifications and classes can be
performed by the library functions. Thus should a particular
application need to build and define a unique class, coercion or any OIL
object it can be done during the compilation using these functions.
The semantics of the functions are described in terms of the basic types
understood by OIL.
- This type is associated with the
identifiers defined as (type) in the OIL specification and
represents type denotations. It is used to define function signatures
for operators and thus the coercion graph.
- This type is associated with
the identifiers defined as (operator) in the OIL specification.
An element of this type can have associated with it either a function
signature or a list of identifiable operators. All coercions are
- Is a private type of the OIL library functions which
represents a set of type denotations and represents the set of
possible types concept.
- This type is associated with identifiers defined as (class) in
the OIL specification and identifies the set of operators and coercions
defined for that class. It is used as the handle for instantiating a
class. The instantiation operation creates a new object of type:
tOilType and adds new instances of the operators and coercions
defined for the class.
- This type
represents a sequence of coercion operators which will transform
a value of one type into a desired type. The coercions sequence may be
empty or of an arbitrary length. Each element in a coercion sequence is
a coercion operator.
There is one other type which is important for use of the ADT and that
is the name of a class, type or operator. A name is
represented as a definition table key
(see The Definition Table Module of Definition Table)
and each identifier in the OIL specification
has a unique name associated with it. This allows a class
operator to be treated the same regardless of its arguments. But since
the argument signature of an instantiated class operator will refer to
the types used to instantiate the class, the type specific information
can be referenced as needed.
identify the set of possible result types given the operator
and the set of possible result types (ats*) for each operand.
The set of types (tOilTypeSet) returned describes the the union of the
result types of any of the operators which can be identified by (oi)
with any combination of argument types selected by the argument type sets,
Also in this set are types which can be reached from identified operators by
means of a coercion sequence.
tOilTypeSet OilIdResultTS1( oi:tOilOp, ats:tOilTypeSet );
tOilTypeSet OilIdResultTS2( oi:tOilOp, ats1,ats2:tOilTypeSet );
tOilTypeSet OilIdResultTS3( oi:tOilOp, ats1,ats2,ats3:tOilTypeSet );
OilIdOpTS* identify an operator given
the operator indication (oi),
the result type (rt) and
the sets of possible argument types (ats*.)
tOilOp OilIdOpTS1( rt:tOilType, oi:tOilOp, ats:tOilTypeSet );
tOilOp OilIdOpTS2( rt:tOilType, oi:tOilOp, ats1,ats2:tOilTypeSet );
tOilOp OilIdOpTS3( rt:tOilType, oi:tOilOp, ats1,ats2,ats3:tOilTypeSet );
Suppose that an operator indication can identify only a single operator,
but it is used in an inappropriate context for that operator.
OilIdOpTS* will return an invalid operator in that case.
In many situations, however, it is preferable to return the one possible
operator and report errors in the context.
OilNoOverload is used in these situations.
tOilOp OilNoOverload( oi:tOilOp, OilIdOpTS*( ... ));
constructs a set of types from a given type denotation.
The set of types returned contains t and
all the type denotations which can be reached from t
by any sequence of coercion operators.
tOilTypeSet OilTypeToSet( t:tOilType );
selects a type from a given set of types. The type selected
is the type which is both in the set ts and can be coerced to all the
types in the set.
tOilType OilSelectTypeFromTS( ts:tOilTypeSet );
The following equation is true for any type
OilSelectTypeFromTS( OilTypeToSet( t ) ) = t
selects a type which can be coerced to all the elements
which sets ts1 and ts2 have in common. This operation
corresponds with function of type balancing in typed expression analysis
from compiler and programming language theory.
tOilType OilBalance( ts1,ts2:tOilTypeSet );
The following is true for all type sets
OilBalance( ts1,ts2 ) = OilSelectTypeFromTS( ts1 AND ts2 )
One other important operation on sets is a test for set membership.
This operation is performed on a type set by the function
OilSetIncludes, which returns true if the set
int OilSetIncludes( s:tOilTypeSet, t:tOilType );
OilSetIncludes will be the usual mechanism for testing if a
expression can be coerced to a particular type. The expression's set of
possible types is calculated and
OilSetIncludes is used to check
if the type in question is in the set of possible types.
OilIsValidOp validates that a given value denotes
a valid operator.
Since any operator identification operation will return some
operator indication, we need to validate that the operator identified
was not the catchall illegal operator.
int OilIsValidOp( op: tOilOp );
identify the operator associated with the indication (oi)
which has an argument type to which (at) can be coerced.
In general these are less powerful than the `set of result type'
but for simple languages they are both faster and easier to use. You can
probably use these if you can identify the correct operator from the
the types of the operands alone without regard to the context.
tOilOp OilIdOp1( oi: tOilOp, at: tOilType );
tOilOp OilIdOp2( oi: tOilOp, at1,at2: tOilType );
tOilOp OilIdOp3( oi: tOilOp, at1,at2,at3: tOilType );
OilGetArgType allows us to get the type of the
n'th argument (arg) of an operator(op.) The 0'th argument
returns the result type from the function signature.
tOilType OilGetArgType( op:tOilOp, arg:int );
allows us to construct a sequence of coercion operators
from type t1 to t2.
The first operator in the sequence (see
will have a result type of t2. The last operator in the
sequence will have a source type of t1.
OilCoerce will always return a coercion sequence. But if there is no
valid coercion sequence between the types then the catchall
error coercion sequence is produced.
tOilCoercionSeq OilCoerce( t1,t2:tOilType );
These operations on coercion sequences (
tOilCoercionSeq) allow us to
step through a coercion sequence and perform an action for each
operator in the sequence.
OilEmptyCS will test a coercion sequence to see if it
is empty. The result will be true is the argument is empty and false otherwise.
OilHeadCS returns the first operator in the sequence.
The operator returned by
OilHeadCS will have been defined by a
coercion statement. Or it will be the error operator in the case of an
error coercion sequence.
OilTailCS returns the rest of the sequence once the first
operator in the sequence is removed.
int OilEmptyCS( cs: tOilCoercionSeq );
tOilOp OilHeadCS( cs: tOilCoercionSeq );
tOilCoercionSeq OilTailCS( cs: tOilCoercionSeq );
allows us to validate a coercion sequence. It is crucial
to detect invalid typing for a subexpression since every call to
OilCoerce will return a coercion sequence and we need to know if
the sequence returned was the catchall error coercion.
int OilIsValidCS( cs: tOilCoercionSeq );
When a class is instantiated a new type is created and the set of
operators and coercions defined for that class are created using the
created class and the types indicated by the parameters to build the
actual function signatures for the created operators. The `is coercible
to' relation is enhanced by all the coercions defined by the
Classes can be instantiated by calling one of the functions:
tOilType OilClassInst0( c:tOilClass, n:DefTableKey );
tOilType OilClassInst1( c:tOilClass, n:DefTableKey, at:tOilType );
tOilType OilClassInst2( c:tOilClass, n:DefTableKey, at1,at2:tOilType );
The number of parameters defined for the class must match the number of
types supplied as arguments.
Each type, operator and class has a name associated with it.
There is a function for retrieving the name associated with a specific
type during attribution:
DefTableKey OilTypeName( t:tOilType );
DefTableKey OilOpName( op:tOilOp );
DefTableKey OilClassName( c:tOilClass );
If the identifier
MyType is a type in a specification then the
MyType will have the value of
The OIL library has all of the necessary functions for the construction
of OIL entities during the execution of the generated compiler. This
capability allows the changing of the OIL schema in more detailed ways
than simply instantiating an already specified class. The different
capabilities for modifying the schema are:
- Type Constructor
- A new type may be constructed without using Classes.
- Operator Constructor
- A new operator may be explicitly constructed with a given
signature(remember operators are use to represent indications also.)
- Signature Constructor
- An argument signature for an operator can be constructed from
- Coercion Constructor
- A properly constructed operator can be declared to be a
- Identification Constructor
- Any operator can be defined to `indicate' any other operator.
- Class Constructor
- Can be built from class operators and coercions.
In addition to class instantiation, a new type may be constructed with the
OilNewType. Its only argument is the name to be associated
with the new type.
tOilType OilNewType( id:DefTableKey );
Constructing a new operator is a two step process, first a new argument
signature must be constructed and then a new operator with that signature
can be constructed using the function
OilNewOp. Besides the
OilNewOp requires the name of the new
id), and the cost of the new operator(
Argument signatures are built in two steps: an empty signature is
OilNewArgSig and then a type is pushed onto the
front of the signature using
tOilArgSig OilAddArgSig( arg:tOilType, sig:tOilArgSig );
Note that by convention, the last type pushed onto the signature is the
result type of the created operator.
Any operator with an argument signature of length 2 can be a coercion by
OilAddCoercion on it.
int OilAddCoercion( op:tOilOp );
A check is not made that the signature is of length 2.
A relationship between an operator indication (
ind) and an operator
op) is established by simply
supplying them to the
OilAddIdentification returns the value of
tOilOp OilAddIdentification( ind, op:tOilOp );
A check is not made regarding the redundancy of the new identification
with respect to the existing schema. You can have ambiguity of which
operator is identified by any given indication and operand signature.
OIL will choose the most recently declared, least cost identification.
Classes are very complex entities and are constructed in stages. First an
empty class in created using
OilNewClass. The argument
specifies the name of the class and the argument
how many parameters the class has.
To an existing class(
c) we can add an operator with a given class
sig) and given cost(
cost) with the function
tOilClassOp OilAddClassOp(id:DefTableKey,sig:tOilClassArgSig,cost:int, c:tOilClass);
With a class operator we can create an identification of an instantiated
op) by an existing operator(
ind) with the
OilAddClassCoercion is used to
define an existing class operator(
op) to be a coercion.
Building a class argument signature is similar to constructing a simple
argument signature but it is complicated by the fact that a class argument needs
to be described in terms of a parameter binding. A class argument's
parameter binding determines the value of the parameter when the class
is instantiated. Like simple signatures we first build an empty class
signature and then push arguments onto it. An empty class signature is
OilNewClassSigArg and an argument description is added
The extra arguments to
OilAddClassSigArg describe the possible
bindings which will instantiate the class signature.
- Can be
eClassRef to indicate that a reference to the created type
replaces this argument,
eParamRef to indicate that one of the
parameters to the class instantiation will replace this argument or
eSpecTypeRef to indicate
that a specific type will replace this argument.
- Specifies which explicit type will replace this argument.
- Selects which parameter of the class instantiation will replace this