Eli   Documents

General Information

 o Eli: Translator Construction Made Easy
 o Global Index
 o Frequently Asked Questions
 o Typical Eli Usage Errors

Tutorials

 o Quick Reference Card
 o Guide For new Eli Users
 o Release Notes of Eli
 o Tutorial on Name Analysis
 o Tutorial on Scope Graphs
 o Tutorial on Type Analysis
 o Typical Eli Usage Errors

Reference Manuals

 o User Interface
 o Eli products and parameters
 o LIDO Reference Manual
 o Typical Eli Usage Errors

Libraries

 o Eli library routines
 o Specification Module Library

Translation Tasks

 o Lexical analysis specification
 o Syntactic Analysis Manual
 o Computation in Trees

Tools

 o LIGA Control Language
 o Debugging Information for LIDO
 o Graphical ORder TOol

 o FunnelWeb User's Manual

 o Pattern-based Text Generator
 o Property Definition Language
 o Operator Identification Language
 o Tree Grammar Specification Language
 o Command Line Processing
 o COLA Options Reference Manual

 o Generating Unparsing Code

 o Monitoring a Processor's Execution

Administration

 o System Administration Guide

Mail Home

Oil Reference Manual

Previous Chapter Next Chapter Table of Contents


Relating an OIL specification to library function calls

To explain the relationship between the specification and the abstract data type we will examine different extractions from some possible specifications and review the behavior of some of the related functions in the abstract data type.

Using Names

Each entity defined in an OIL specification is represented by a definition table key. The OIL value is accessible as a property of that definition table key. For example, suppose that iAdd was defined by an OPER statement in OIL. This would result in a known key (see How to specify the initial state of Definition Table) iAdd. iAdd would also have an OilOp property whose value was the actual OIL operator (of type tOilOp). Thus the actual OIL operator corresponding to iAdd could be obtained by the function call GetOilOp(iAdd,OilInvalidOp) (see Behavior of the basic query operations of Definition Table).

In order to avoid the overhead of querying a property for constant information, OIL also defines an identifier as the actual OIL operator. This identifier is constructed by prefixing the OIL identifier with OilOp. Thus the identifier OilOpiAdd denotes the value that would be obtained from the function call GetOilOp(iAdd,OilInvalidOp).

Similar conventions are used for OIL types and OIL classes: The OIL identifier denotes a known key, and the actual OIL entity (of type tOilType or tOilClass respectively) is denoted by prefixing either OilType or OilClass to that identifier. A known key denoting an OIL type also has an OilType property, and a known key denoting an OIL class has an OilClass property.

A simple example

Let us consider the following OIL specification:

iAdd ( int_t, int_t ): int_t;     /* the usual '+' operators for Pascal */
rAdd ( real_t, real_t ): real_t;
sUnion ( set_t, set_t ): set;

Plus: iAdd, rAdd, sUnion;  /* will be identified together */

COERCION Float( int_t ): real_t;    /* usual Pascal coercion from int to real */

Definitions from the specification

All of the identifiers in this specification will denote values to the library functions. The functions in the library will be applied to values constructed from these identifiers and will return values represented by these identifiers.

Operator Identification

The most basic operation is that of operator identification so we will start there. When semantically analyzing a binary expression formed with a plus sign (+), the compiler would use the function OilIdOp2 applied to the value denoted by Plus (which indicates the syntactic operator) and the types of the operands to the plus sign.

The invocation OilIdOp2( Plus, int_t, real_t ) would return the operator rAdd because int_t was coercible to real_t. Similarly :

OilIdOp2( Plus, set_t, set_t ) would return sUnion

OilIdOp2( Plus, int_t, int_t ) would return iAdd

OilIdOp2( Plus, real_t, real_t ) would return rAdd

Any combination of operand types like real_t and set_t would return a value denoting an erroneous operator. Example: OilIsValidOp( OilIdOp2( Plus, real_t, set_t ) ) would return an integer value of 0.

Operator Signatures

Once we have identified an operator will need to know its type signature so that we may return the type of the subexpression computed by the operator and so we may determine the types required by the operator from its respective operands. The function OilGetArg gives us that facility.

The expression OilGetArg( iAdd, 0 ) would return int_t as the type of the result of the operator iAdd. Likewise OilGetArg( sUnion, 1 ) would return set_t as the required type of the first operand to the `sUnion' operator.

Coercion sequence

Once we have the type returned by an operator and know the type required of this sub-expression (from the parent context) we may need to apply a sequence of coercions on the result of the operator to satisfy the requirements of the parent context. The function OilCoerce supplies the necessary function.

In the case of our example we might require a real_t result from an iAdd operator (which returns int_t.) The expression OilCoerce( int_t, real_t ) would return a coercion sequence which represented the coercion of an int_t type value to a real_t type value. This coercion sequence (call it cs) would then be analyzed with the functions: OilEmptyCS, OilHeadCS and OilTailCS. The expression OilHeadCS( cs ) would evaluate to Float and OilEmptyCS( OilTailCS( cs ) ) would evaluate to true. These expressions describe the fact that only the coercion operator Float was necessary to transform int_t to real_t.

If no coercions were necessary then OilEmptyCS( cs ) would have yielded the value true. Likewise to detect an impossible coercion, the function OilIsValidCS would be used. The expression OilIsValidCS( OilCoerce( real_t, set_t ) ) would yield the value false to indicate that such a coercion was not possible.

A more complex example

Not all operator identification schemes can be implemented with the simple bottom-up type evaluation shown in the previous section. Sometimes the desired result type will affect which operator denotation is identified with a given operator indication. OIL supplies this capability with the set of types operations.

Below is an example OIL specification which is designed to use set of types. The specification shows that there are two multiplication operators(sMulS sMulD) on type single. One multiplication operator returns a double length result (double) the other returns a single length result(single.) These declarations have a natural correspondence with many machine architectures. The operator indication Mul is defined to identify either sMulS or sMulD.

sMulS ( single, single ): single;
sMulD ( single, single ): double;
dMulD ( double, double ): double;

COERCION iCvtStoD ( single ): double;

Mul: dMulD, sMulD, sMulS

Using type sets

To use type set functions we must begin with constructing the possible result type set of a terminal. For this we use the function OilTypeToSet. Like so:

OilTypeToSet( single ) yields  [ single, double ]

OilTypeToSet( double ) yields  [ double ]

For the rest of this example we will use the identifiers ss and ds to represent the type sets for single and double, respectively.

To analyze an entire expression with type sets we must also be able to determine the set of types associated with an operator indication and its set of operands. For this we use the OilIdResultTS* functions. Like so:

OilIdResultTS2( Mul, ss, ss) yields  [ single, double ]

OilIdResultTS2( Mul, ds, ss) yields  [ double ]

OilIdResultTS2( Mul, ds, ds) yields  [ double ]

When we get to the root of an expression (like in an assignment) we would then use a desired type determined from the context of the root of the expression (like the destination type of the assignment) to determine which operator we wanted to select. For this we use the OilIdOpTS* functions. Like so:

OilIdOpTS2( single, Mul, ss, ss) yields  sMulS

OilIdOpTS2( double, Mul, ss, ss) yields  sMulD

OilIdOpTS2( double, Mul, ds, ss) yields  dMulD

By using type sets, the operator indication Mul with single operands can identify sMulD, thus directly producing a double result; whereas with the simple scheme used previously (see A simple example.) an additional coercion would be needed to return a double result.

Using Classes

There are three steps to using classes: (1)specifying them with OIL, (2)instantiating them using the OilClassInst* functions and (3)identifying the enriched indication mappings with enriched coercion graph.

The following OILspecification allows us to define any number of Sets during compilation. And we specify the overloading of the `+'(loPlus) operator to allow set union.

CLASS Set( element ) BEGIN
  COERCION coElemToSet(element):Set;
  OPER soUnion(Set,Set):Set;
END;

OPER soIadd(tInt,tInt):tInt;
OPER soRadd(tReal,tReal):tReal;

INDICATION loPlus: soIadd, soRadd, soUnion;
We can then construct a simple binary expression compiler which uses a constant set for one of its possible operand types.
NONTERM Expr: op:tOilOp, st:tOilType;

RULE Dyadic: Expr::= Term Ind Term
STATIC
  Expr.st:= OilClassInst1( Set, Set_name, tInt );
  Expr.op:= OilIdOp2( Ind.op, Term[1].type, Term[2].type )
END;

NONTERM Term: type:tOilType;

RULE Set: Term::= 's'
STATIC Term.type:= INCLUDING Expr.st END;

RULE Integer: Term::= 'i'
STATIC Term.type:= tInt END;

RULE Real: Term::= 'r'
STATIC Term.type:= tReal END;

NONTERM Ind: op:tOilOp;

RULE Plus: Ind::= '+'
STATIC Ind.op:= loPlus END;
We use the following request to construct the compiler:
test3.specs :exe>test3.exe


Previous Chapter Next Chapter Table of Contents