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

Tutorial on Type Analysis

Previous Chapter Next Chapter Table of Contents


Functions

This chapter introduces definitions and calls of parameterized functions. Type analysis has to check that the signature of a function call matches the signature of the called function, and that functions return a value of the specified type.

Here is an example program that defines some functions. The grammar for function calls and return statements is given below.

FunctionExamp[90]==

begin
  var   int i, int j,
        bool b, bool c,
        real r, real s;

  fun f (int x, real y) real
  begin r = x * y; return r;end;

  fun g (real z) void
  begin r = z; return; end;

  s = f (i+1, 3.4);
  g (f (j, s));
  return;
end
This macro is attached to a product file.

We first extend the grammar by productions for function declarations: Abstract function syntax[91]==

RULE: Declaration ::=  FunctionDecl END;
RULE: FunctionDecl ::= 'fun' DefIdent Function ';' END;
RULE: Function ::=     FunctionHead Block END;
RULE: FunctionHead ::= '(' Parameters ')' TypeDenoter END;
RULE: Parameters LISTOF Parameter END;
RULE: Parameter ::= TypeDenoter DefIdent END;
This macro is invoked in definition 100.

A function type is characterized by its signature, i.e. the sequence of the types of its parameters and the result type. (Note: If we had more than one mode of parameter passing, the abstraction of a parameter in the function signature would be a pair: parameter passing mode and parameter type.)

We first consider the name analysis aspect of a function declaration: The Function subtree is a range where the parameter definitions are valid. The function Block is nested in that range. Since the DefIdents of parameters are already completely specified for name analysis, we need only:

Function range[92]==

SYMBOL Function INHERITS RangeScope END;
This macro is invoked in definition 100.

Now we consider a function declaration as a definition of a typed entity, and apply the same specification pattern as used for variable declarations. Furthermore, each Parameter is also a TypedDefinition. There is no problem in nesting definitions of typed entities this way.

Function declaration types[93]==

SYMBOL FunctionDecl INHERITS TypedDefinition END;

RULE: FunctionDecl ::= 'fun' DefIdent Function ';' COMPUTE
  FunctionDecl.Type = Function.Type;
END;

RULE: Function ::= FunctionHead Block COMPUTE
  Function.Type = FunctionHead.Type;
END;

SYMBOL Parameter INHERITS TypedDefinition END;

RULE: Parameter ::= TypeDenoter DefIdent COMPUTE
  Parameter.Type = TypeDenoter.Type;
END;
This macro is invoked in definition 100.

Next, we specify how the type of a function is composed. The FunctionHead, which contains the signature, is treated as a TypeDenotation for a function type.

Function type[94]==

SYMBOL FunctionHead INHERITS TypeDenotation, OperatorDefs END;
This macro is invoked in definition 100.

Furthermore, a function declaration introduces an operator. This is indicated by the role OperatorDefs. The computation ListOperator creates a new operator, identified by FunctionHead.Type. The types of the parameters together with the result type TypeDenoter.Type form its signature.

Function signature[95]==

RULE: FunctionHead ::= '(' Parameters ')' TypeDenoter COMPUTE
  FunctionHead.GotOper +=
     ListOperator (
       FunctionHead.Type,
       FunctionHead.Type,
       Parameters.OpndTypeList,
       TypeDenoter.Type);
END;

SYMBOL Parameters INHERITS OpndTypeListRoot END;
SYMBOL Parameter INHERITS OpndTypeListElem END;

RULE: Parameter ::= TypeDenoter DefIdent COMPUTE
  Parameter.DefTableKeyElem = TypeDenoter.Type;
END;
This macro is invoked in definition 100.

Function calls are integrated in the expression syntax of our language. We chose a very general form of an Expression to denote the function to be called. That allows us to later expand the language by expressions which yield a function. That feature does not create additional problems for type analysis.

We also introduce return statements into our language:

Abstract call syntax[96]==

RULE: Expression ::=    Expression '(' Arguments ')' END;
RULE: Arguments  LISTOF Argument END;
RULE: Argument   ::=    Expression END;

RULE: Statement  ::=    'return' ';' END;
RULE: Statement  ::=    'return' Expression ';' END;
This macro is invoked in definition 100.

Type analysis for a function call is straight-forward: A call is treated as an operation which takes the arguments as operands. Expression[2] yields the function to be called. Its type provides the operator indication, which may be overloaded with several operations, as stated in the context of the function definition. The precoined computation ListContext connects the expression subtree of the arguments with Expression[1] representing the result.

Call types[97]==

SYMBOL Arguments INHERITS OpndExprListRoot END;
SYMBOL Argument INHERITS OpndExprListElem END;

RULE: Expression ::= Expression '(' Arguments ')' COMPUTE
  ListContext (Expression[1], , Arguments);
  Indication (Expression[2].Type);

  IF(BadOperator,
    message
      (ERROR,
       "Call does not match the functions' signatures",
       0, COORDREF));
END;
This macro is invoked in definition 100.

The following context connects the Argument node with the expression subtree forming the actual parameter. If they had the same type properties, we would have used a TransferContext computation. However, in our language we want to allow that the type of the Expression need not match exactly the type required for the Argument as specified in the signature of the function. As in assignments it shall be allowed that the expression yields a value of type real which then is converted to an int value if required by the function signature, e.g. in f(3.4).

Hence, we use a ConversionContext which allowes to connect the Argument via an operator with the Expression node. The indication assignOpr is specified for this context. It states that the same conversion operators as in assignments (i.e. rToi) and all coercion operators (i.e. iTor) may be used to convert the result of the Expression to the type of the Argument, if necessary:

Arguments[98]==

RULE: Argument ::= Expression COMPUTE
  ConversionContext (Argument, , Expression);
  Indication (assignOpr);
END;
This macro is invoked in definition 100.

A return statement refers to the immediately enclosing function declaration. It has to be checked that a value of a type is returned that is compatible to the result type, if the latter is not void. A return from the outermost program level is considered as if the program was a void function. Conversions that are additionally applicable are specified in the same way as in the Argument context above.

The attribute value Function.ResultType stems from the context of a type denotation. Hence, its value may not be used directly in a compare with a type key as voidType. The function FinalType has to access the related type key, and the precondition INCLUDING Program.TypeIsSet has to be stated.

Return statements[99]==

ATTR ResultType: DefTableKey;

RULE: Statement ::= 'return' Expression ';' COMPUTE
  RootContext (
    INCLUDING (Function.ResultType, Program.ResultType), , Expression);
  Indication (assignOpr);
END;

RULE: Statement ::= 'return' ';' COMPUTE
  IF (NOT (EQ (voidType,
               FinalType (
                  INCLUDING (Function.ResultType, 
                             Program.ResultType)))),
  message (ERROR, "return value required", 0, COORDREF))
  <- INCLUDING Program.TypeIsSet;
END;

SYMBOL Program COMPUTE 
  SYNT.ResultType = voidType;
END;

RULE: Function ::= FunctionHead Block COMPUTE
  Function.ResultType = FunctionHead.ResultType;
END;

RULE: FunctionHead ::= '(' Parameters ')' TypeDenoter COMPUTE
  FunctionHead.ResultType = TypeDenoter.Type;
END;
This macro is invoked in definition 100.

Function.lido[100]==

Abstract function syntax[91]
Abstract call syntax[96]
Function declaration types[93]
Function range[92]
Function type[94]
Function signature[95]
Call types[97]
Arguments[98]
Return statements[99]
This macro is attached to a product file.

Function.con[101]==

Function declaration syntax[140]
Call syntax[141]
This macro is attached to a product file.


Previous Chapter Next Chapter Table of Contents