proof-transforming compilation of eiffel contracts

100
Proof-Transforming Compilation of Eiffel Contracts Diploma Thesis By: Hasan Karahan Supervised by: Martin Nordio Prof. Bertrand Meyer Student Number: 02-908-887

Upload: others

Post on 28-Dec-2021

3 views

Category:

Documents


0 download

TRANSCRIPT

Proof-Transforming Compilationof Eiffel Contracts

Diploma Thesis

By: Hasan KarahanSupervised by: Martin Nordio

Prof. Bertrand Meyer

Student Number: 02-908-887

PROOF-TRANSFORMING COMPILATION

OF EIFFEL CONTRACTS

1

2

Table of Contents

1 Introduction.............................................................................................................................................5

1.1 Objective.........................................................................................................................................61.2 Outline.............................................................................................................................................7

2 Architecture.............................................................................................................................................9

2.1 Overview.........................................................................................................................................92.2 Source Code Language..................................................................................................................102.3 Eiffel Contract Language...............................................................................................................11

3 Implementation.....................................................................................................................................15

3.1 Internals of the Proof-Transforming Compiler..............................................................................153.2 Proof-Transforming Compiler's Processing Stages.......................................................................173.3 Proof-Transforming Compiler's Output Formats..........................................................................213.4 XML and ECL Parser Internals.....................................................................................................22

The XML Parser.............................................................................................................................22The Eiffel Contract Language Parser.............................................................................................26Abstract Syntax Tree for Eiffel Proofs...........................................................................................27Abstract Syntax Tree for the Eiffel Contract Language.................................................................33Proof- and Contract-Tree Integration.............................................................................................37Replacement Operation for References..........................................................................................39

4 Example................................................................................................................................................41

5 Conclusion............................................................................................................................................45

Bibliography.............................................................................................................................................46

Appendix..................................................................................................................................................47

7.1 Read-me for the Proof-Transforming Compiler............................................................................477.2 Schema Description for the XML Input........................................................................................51

Eiffel-Proof.....................................................................................................................................51Class-Proof.....................................................................................................................................51Routine-Proof.................................................................................................................................51Proof-Item.......................................................................................................................................52Assignment-Proof...........................................................................................................................52Composition-Proof.........................................................................................................................53If-Proof...........................................................................................................................................53Loop-Proof......................................................................................................................................53Retry-Proof.....................................................................................................................................54Create-Proof....................................................................................................................................54Check-Proof....................................................................................................................................54Strong-Proof...................................................................................................................................54Weak-Proof.....................................................................................................................................55Invocation-Proof.............................................................................................................................55Read-Proof......................................................................................................................................55

3

Write-Proof.....................................................................................................................................56Rescue-Proof...................................................................................................................................56Implementation-Proof.....................................................................................................................56Class-Rule.......................................................................................................................................56Subtype-Rule..................................................................................................................................57False-Axiom...................................................................................................................................57Standard-Rule.................................................................................................................................57Junction-Proof................................................................................................................................58

7.3 Remarks on the XML Schema Description...................................................................................597.4 Statements of a Routine-Proof......................................................................................................617.5 Eiffel Contract Language Parser...................................................................................................82

4

1 Introduction

Modern object-oriented software [1] is complex: it is highly complicated and can contain thousands if

not millions of lines of source code. Such huge software is nearly always written within a team of

developers, which have to rely on and to trust each other. However, even in the most experienced teams

it is possible if not the rule that – due to for example miscommunication – defects are introduced on

any stage of the development process.

It is possible to follow a certain development methodology to minimize the number of defects and to

ensure a moderate level of software quality, but this is not enough. It is necessary to establish a

thorough testing regime, where most of the defects are recognized and then eliminated.

To further improve the quality of software, there are several options. The first consists of further

enhancing the grip of the testing regime on the source code. This approach can be iterated until there

are no more tests left one can think of. Although it might be very possible that some defects have been

left undetected, it is currently to most practiced approach to deliver software products. But testing will

never be able to prove the absence of errors [2].

A second option is to apply software verification: this technique can be applied either before the

compilation on the source code level or after the compilation on the bytecode level.

Another option is to use Proof-Carrying Code (PCC) [3]. It enables to execute mobile code in a safe

way: “The key idea of Proof-Carrying is to attach to the code an easily-checkable proof that its

execution does not violate the safety policy of the receiving system.” However, it is only possible to

generate proofs automatically for basic properties such as type safety [4].

On the latter level, verification will ensure to avoid the execution of untrusted bytecode, but it is

necessary to provide a proof of its correctness. This can become a rough endeavor since bytecodes like

CIL or JVM do not contain some high-level knowledge like type or control flow information, that has

been provided by the developer in the original source. Since such high-level information can be crucial,

it is preferable to provide the proof directly on the original source code level.

5

Furthermore, state of the art proof development is rarely done in a fully automated fashion and at least

a limited form of interaction with the programmer is necessary. Such interactive verification of

bytecode can be used to prove functional properties, but by delivering the proof on the source code

level and armored with fully automatic tools this interaction can be eliminated.

At this point Proof-Transforming Compilation – the focus of this work – enters the arena: It takes a

source proof and produces a bytecode proof, which can later be verified by a proof checker. This means

that there is no trust obligation for a developer using the proof-transforming compiler (PTC), since the

proof checker will reject any erroneous proof or specification.

Provided that the source and target language are similar, such a proof-transforming compilation is

straightforward [5]. But if there are large differences between the different concepts of the two

languages the compilation will be difficult. For example while Eiffel supports multiple inheritance, CIL

supports multiple inheritance only for interfaces. Therefore, the PTC has to compile Eiffel classes to

CIL classes and to CIL interfaces.

The implemented PTC can be separated into two modules: (1) a specification translator translating

Eiffel contracts to CIL contracts and (2) a proof translator translating Eiffel proofs to CIL proofs.

In this work, the PTC takes an Eiffel proof and produces the corresponding bytecode specification in

CIL.

The specification translator takes Eiffel contracts and translates them to First Order Logic (FOL). Since

it is possible to write contracts containing types, the specification translator has to translate the

contracts to FOL mapping Eiffel types to CIL types. It has to decide whether to map Eiffel types to CIL

interface or implementation classes.

1.1 Objective

Summarized, the objective of the actual work consists of the implementation of a proof-transforming

compiler with the following goals:

1. The development of an XML parser for a provided XML schema description (XSD). The XML

6

input contains the source proof (for a subset of Eiffel).

2. The development of a parser for the Eiffel Contract Language (ECL) – not the whole contract

language is considered, but only a subset of.

3. The implementation of a specification translation from Eiffel contracts to FOL.

1.2 Outline

The next chapter provides a description of the proof-transforming compiler's architecture and the

covered source and contract languages. The following chapter about the implementation describes the

internal structure of the proof-transforming compiler. After discussing an example in chapter four, the

next chapter gives a conclusion.

7

8

2 Architecture

2.1 Overview

Figure 1 shows the general idea of the proof-transforming compiler: the developer provides proof for

an Eiffel class and the he has to write this proof in XML:

Once the XML input is prepared, the PTC is able to process the proof. The compiler processes the input

and creates internally a tree representing the proof's structure. Then for the contracts of routines the

translation to CIL bytecode is applied.

2.2 Source Code Language

The subset of the Eiffel language accepted by the proof-transforming compiler is described below using

the Back-Naur Formalization :

exp ::= literal | var | exp op exp

9

Figure 1: Overview of the Proof-Transforming Compiler

Proof-Transforming CompilerEiffel Source Code

XML (Source Proof)

CIL Bytecode

Tree Representation

instr ::= x := exp | instr ; instr | from instr until exp loop instr end| if exp then instr else instr end| check exp end| create {Type} routine_name ( exp )| x := y.Type@a| y.Type@a := exp| x := y.Type : routine_name ( exp )

once_routine ::= name (var : Type) : Type isrequire boolExp[ local var : Type, ... ]once instr[ rescue instr ]ensure boolExp

end

non_once_routine ::= name (var : Type) : Type isrequire boolExp[ local var : Type, ... ]do instr[ rescue instr ]ensure boolExp

end

routine ::= once_routine | non_once_routine| routine ; routine

feature ::= feature name : Type| feature ; feature

class ::= class namefeatureroutine

end

Above, the exp and BoolExp rules are Eiffel expressions. The ECL sub-parser is capable to parse both

exp and BoolExp rules.

2.3 Eiffel Contract Language

The proof-transforming compiler is able to process contract expressions by using its Eiffel Contract

Language sub-parser, which works with respect to the labeled-BNF grammar [6]:

-- ---------------------------------------------------------------------------

-- Begin l-BNF File:

-- ---------------------------------------------------------------------------

10

VIS_CONTRACT. Contract ::= ColonList ;

-- -----------------------------------------------------------------------

-- BoolExp

-- -----------------------------------------------------------------------

VIS_EQ. BoolExp ::= BoolExp "=" BoolExp1 ;

VIS_NEQ. BoolExp ::= BoolExp "!=" BoolExp1 ;

VIS_IMP. BoolExp1 ::= BoolExp2 "implies" BoolExp1 ;

VIS_XOR. BoolExp2 ::= BoolExp2 "xor" BoolExp3 ;

VIS_OR. BoolExp2 ::= BoolExp2 "or" BoolExp3 ;

VIS_ORELSE. BoolExp2 ::= BoolExp2 "or else" BoolExp3 ;

VIS_AND. BoolExp3 ::= BoolExp3 "and" BoolExp4 ;

VIS_ANDTHEN. BoolExp3 ::= BoolExp3 "and then" BoolExp4 ;

VIS_NOT. BoolExp4 ::= "not" BoolExp5 ;

VIS_TRUE. BoolExp5 ::= "True" ;

VIS_FALSE. BoolExp5 ::= "False" ;

VIS_LT. BoolExp6 ::= Exp "<" Exp ;

VIS_LTe. BoolExp6 ::= Exp "<=" Exp ;

VIS_GT. BoolExp6 ::= Exp ">" Exp ;

VIS_GTe. BoolExp6 ::= Exp ">=" Exp ;

VIS_EXP. BoolExp7 ::= Exp ;

VIS_TYPEFUNC. BoolExp7 ::= TypeFunc ;

-- BoolExp8 ::= "(" BoolExp ")" ; -- 1 s/r conflict (!)

-- -----------------------------------------------------------------------

-- TypeFunc

-- -----------------------------------------------------------------------

VIS_TO_CON. TypeFunc ::= TypeInfo "." "conforms_to" "(" TypeInfo ")" ;

VIS_IS_EQ. TypeFunc ::= TypeInfo "." "is_equal" "(" TypeInfo ")" ;

VIS_IS_NEQ. TypeFunc ::= TypeInfo "." "is_not_equal" "(" TypeInfo ")" ;

VIS_TTYPE. TypeInfo ::= "{" Ident "}" "." "type" ;

VIS_ETYPE. TypeInfo ::= Exp3 "." "type" ;

-- -----------------------------------------------------------------------

-- Exp

-- -----------------------------------------------------------------------

11

VIS_TERM. Exp ::= Exp TermOp Exp1 ;

VIS_FACT. Exp1 ::= Exp1 FactOp Exp2 ;

VIS_SIGN. Exp2 ::= SignOp Exp2 ;

VIS_VOID. Exp3 ::= "Void" ;

VIS_INTEGER. Exp3 ::= Integer ;

VIS_ID. Exp3 ::= Ident ;

VIS_CHARACTER. Exp3 ::= Char ;

VIS_STRING. Exp3 ::= String ;

VIS_OLD. Exp4 ::= "old" Ident ;

VIS_CREATE. Exp4 ::= "create" "{" Ident "}" "." Ident Args ;

VIS_CALL. Exp4 ::= Exp3 "." Ident Args ;

-- Exp5 ::= "(" Exp ")" ; -- 1 s/r conflict (!)

-- -----------------------------------------------------------------------

-- TermOp, FactOp & SignOp

-- -----------------------------------------------------------------------

VIS_ADD. TermOp ::= "+" ;

VIS_SUB. TermOp ::= "-" ;

VIS_MUL. FactOp ::= "*" ;

VIS_DIV. FactOp ::= "/" ;

VIS_POS. SignOp ::= "+" ;

VIS_NEG. SignOp ::= "-" ;

-- -----------------------------------------------------------------------

-- Args

-- -----------------------------------------------------------------------

VIS_NIL. Args ::= ;

VIS_ARGs. Args ::= "(" CommaList ")" ;

-- -----------------------------------------------------------------------

-- Lists (for BoolExp's)

-- -----------------------------------------------------------------------

VIS_COMMAHEAD. CommaList ::= BoolExp ;

VIS_COMMATAIL. CommaList ::= BoolExp "," CommaList ;

VIS_COLONHEAD. ColonList ::= BoolExp ;

VIS_COLONTAIL. ColonList ::= BoolExp ";" ColonList ;

12

-- -----------------------------------------------------------------------

-- Extra Stuff

-- -----------------------------------------------------------------------

entrypoints Contract ;

coercions BoolExp 8 ;

coercions Exp 5 ;

-- ---------------------------------------------------------------------------

-- End l-BNF File.

-- ---------------------------------------------------------------------------

The grammar is not standard BNF but rather an extended version called labeled BNF: For every rule it

has a label which provides for the considered rule a short name. It is used by the BNF Converter tool ,

which produces on input of such a grammar a working parser. This tool has been used to develop

proto-type parsers for different l-BNF grammars until to above grammar has been found.

13

14

3 Implementation

3.1 Internals of the Proof-Transforming Compiler

The proof-transforming compiler (PTC) has two logical parts: a front-end and a back-end. Its front-end

is responsible for parsing the XML input and the Eiffel Contract Language (ECL) expressions. The

result of this parsing process is an abstract syntax tree. On the tree the translation to CIL interfaces and

implementations are applied. Afterwards the back-end is able to produce different kind of outputs.

In the front-end the XML parser uses the ECL sub-parser. This relationship is important to understand,

since the final product of the parsing process is one tree which is produced using both parsers. This tree

for the whole input is called the tree representation for the Eiffel-Proof which has two nested but

distinct sub-parts: an abstract syntax tree for the proof (proof-tree) and abstract syntax trees for every

contracts, pre- and postconditions (contract-tree).

This split into two logical processing units is necessary, since the input consists also of two nested but

different entities: the XML input describing the general structure of an Eiffel-Proof but containing ECL

expressions.

Figure 2 depicts this relationship between these two processing units of the proof-transforming

compiler's front-end and their individual products. The upper rectangle represents the proof-

transforming compiler and the lower rectangle represents the Eiffel-Proof tree. The figure shows only

the most important parts of the compilation process: The compiler itself and the intermediate Eiffel-

Proof tree. These two parts are the focal points of this project, but the proof-transforming compiler's

translation process includes all the following parts:

● Input: XML file describing an Eiffel-Proof on the source code level.

● Generation of the Eiffel-Proof tree with the distinct proof-tree and contract-trees.

● Translation to the Bytecode tree (produced from the Eiffel-Proof tree).

In the last step of the above list, the proof-transforming compiler translates the Eiffel-Proof tree into a

Bytecode tree. Since the purpose of the proof-transforming compiler is to output CIL bytecode, this

translation is necessary because the underlying structures of an Eiffel-Proof and a Bytecode-Proof are

different. The scope of the actual project did not include the whole translation process but only the

translation of the contracts (require and ensure) for the routine-proofs to CIL bytecode.

15

16

Figure 2: Proof-Transforming Compiler's Front-End and it's Eiffel-Proof Tree Output

3.2 Proof-Transforming Compiler's Processing Stages

The proof-transforming compiler processes a provided XML file by applying the following actions on

it: at first the XML input is opened by using its path and filename information; if no such information

has been provided the standard input channel will be used to obtain the proof.

Once the source is determined, the stream of XML information is parsed and the input channel is

closed. After this first parsing the result is an Eiffel-Proof tree. The Bytecode-Proof tree will be

generated later.

Since the original XML file may contain references from particular elements to other elements the

result will contain lists of references instead of a proper tree structure. These references need to be

replaced by the corresponding objects. An example will highlight this replacement operation: for the

following Eiffel source

indexing

description: eiffel-proof

class TEST_CLASS

feature

composition_example is

local

x: INTEGER

i: INTEGER

do

x := 0

i := 1

end

end

the corresponding XML contains the corresponding proof of the TEST_CLASS example.

<?xml version="1.0" encoding="UTF-8"?>

<eiffel-proof

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocation="../../xsd/eiffel-proof.xsd">

<class-proof>

<name>test-class</name>

17

<routine-proof>

<name>composition-example</name>

<local>

<name>x</name>

<type>integer</type>

</local>

<local>

<name>i</name>

<type>integer</type>

</local>

<composition-proof ID="comp-0">

<precondition>true</precondition>

<postcondition>

<normal>x{=}0{and}i{=}1</normal>

</postcondition>

<s1 ref="ass-0" />

<s2 ref="ass-1" />

</composition-proof>

<assignment-proof ID="ass-0">

<precondition>true</precondition>

<postcondition>

<normal>x{=}0</normal>

</postcondition>

<target>x</target>

<expression>0</expression>

</assignment-proof>

<assignment-proof ID="ass-1">

<precondition>x{=}0</precondition>

<postcondition>

<normal>x{=}0{and}i{=}1</normal>

</postcondition>

<target>i</target>

<expression>1</expression>

</assignment-proof>

</routine-proof>

</class-proof>

</eiffel-proof>

The important elements are highlighted: Relevant are the elements for the statements inside the

routine-proof. There is a composition-proof (ID=”comp-0”) referencing to the two assignment-proofs

(ID=”ass-0” and ID=”ass-1”). Before applying the replacement operation the product of the XML

parser for the above input is the following syntax tree (only the relevant statements of the routine-

18

proof's body are shown):

(CPX_ROUTINE_PROOF

(PIT_COMPOSITION_PROOF:ID=comp-0

(REF_S1:ID=ass-0)

(REF_S2:ID=ass-1))

(PIT_ASSIGNMENT_PROOF :ID=ass-0

(STR_TARGET:x)

(STR_EXPRESSION

(Exp (INT:0))))

(PIT_ASSIGNMENT_PROOF :ID=ass-1

(STR_TARGET:i)

(STR_EXPRESSION

(Exp (INT:1)))))))

This syntax tree reflects exactly the XML input's hierarchical structure. After the replacement

operation the syntax tree changes to:

(CPX_ROUTINE_PROOF

(PIT_COMPOSITION_PROOF :ID=comp-0

(PIT_ASSIGNMENT_PROOF :ID=ass-0

(STR_TARGET:x)

(STR_EXPRESSION

(Exp (INT:0))))

(PIT_ASSIGNMENT_PROOF :ID=ass-1

(STR_TARGET:i)

(STR_EXPRESSION

(Exp (INT:1))))))))

The list of statements inside the routine-proof's body ([comp-0, ass-0, ass-1]) is replaced by a tree

structure ([(comp-0 (ass-0, ass-1))]). Such a structure is preferable since it is easier to traverse it and to

re-produce for example Eiffel source code from it.

After this reference-by-object replacement the contract expressions are parsed by the Eiffel Contract

Language sub-parser. The resulting sub-trees are put back into the main tree. For example the

following assignment-proof sub-tree

(PIT_ASSIGNMENT_PROOF

(STR_PRECONDITION:”false”)

(CPX_POSTCONDITION

(STR_NORMAL :”x=0”))

(STR_TARGET:x)

(STR_EXPRESSION:”0”) )

is replaced by

19

(PIT_ASSIGNMENT_PROOF

(STR_PRECONDITION

(CONTRACT (False)))

(CPX_POSTCONDITION

(STR_NORMAL

(CONTRACT (EQ (Exp(ID:x))(Exp (INT:0))))))

(STR_TARGET:x)

(STR_EXPRESSION

(Exp(INT:0)))) .

Once the contracts are processed by the ECL sub-parser, the Eiffel-Proof tree is translated to the

Bytecode-Proof tree (without destroying or modifying the original Eiffel-Proof tree). Over the

ep_tree.root and bc_tree.root features of the proof-transforming compiler both trees' roots

can be accessed.

The following table summarizes the proof-transforming compiler's different processing stages (and

provides a short description for each of them):

# Processing Stage

1. Determination of the input source (file or standard input).

2. Parsing of the XML input; generates an Eiffel-Proof tree (ep_tree.root).

3. Replacement of references by the corresponding objects (in the Eiffel-Proof tree).

4. Parsing of the Eiffel Contract Language expressions and their replacement by the corresponding sub-trees (again in the Eiffel-Proof tree).

5. Generation of the Bytecode-Proof tree (by traversing the Eiffel-Proof tree). The tree's root is accessible by bc_tree.root.

20

3.3 Proof-Transforming Compiler's Output Formats

Once the two trees are generated, the proof-transforming compiler is able to produce the following four

different output formats (by either traversing the Eiffel-Proof tree or the Bytecode tree):

● Eiffel-Code:

Eiffel syntax representing the original input without the pre- and postconditions for the

statements inside a routine's body. This output is useful to compare the XML file with the

original Eiffel source code. If the XML file has been correctly written, the Eiffel-Code output

and the original Eiffel source code should be identical.

● Eiffel-Source:

Eiffel syntax representing the original input with the pre- and postconditions for the

different statements in place. Again this output is useful to check the correctness of the XML

file, but this time it is also possible to verify the pre- and postconditions' correctness.

● Byte-Code:

CIL bytecode without the proof statements inside a routine's body (but with the require

and ensure contracts) [7].

● Byte-Proof:

CIL bytecode with the proof statements of a routine's body in place (again, with the

require and ensure contracts) [7].

Since in the actual implementation the output rendering of a routine's body for byte-code and byte-

proof are not present yet, these two outputs are identical; i.e. a routine's CIL bytecode consists only of

its return type, name, parameter list, require and ensure contracts. The bytecode rendering of a

routine's body will be implemented within the scope of the next project on the proof-transforming

compiler.

The proof-transforming compiler has many output possibilities. Therefore a special logger has been

implemented to separate these outputs into different (logical) channels: Some of the channels either

directly print to the standard or error output and others print to an internal buffer. Later, this buffer can

be converted into a string.

21

3.4 XML and ECL Parser Internals

The proof-transforming compiler has two parsers incorporated: one to handle the XML input and one

the handle the Eiffel Contract Language expressions. This subsection will cover an in depth discussion

on the implementation of both parsers and on their mutual integration.

The XML Parser

This parser is build upon a callback mechanism – which is implemented using the GOBO library's

XML handling features – to process XML documents. For every XML tag or content a corresponding

callback procedure is invoked. This callback procedure creates a raw XML object representing the

input and puts it onto an internal stack.

There are tags which indicate the beginning or the end of an XML element. For both different tag types

there exist corresponding callback procedures. The procedure for a begin tag does nothing but to put an

XML_BEGTAG object onto the stack, but the procedure for an end tag invokes – after having put an

XML_ENDTAG object onto the stack – a dispatch routine.

This dispatch routine has the responsibility to call the correct delayed creation for the given XML

element on the stack. This delayed creation is necessary, since the main goal of the parser is to produce

from the raw XML objects on the stack a tree representing the proof. A simplified version of the actual

implementation the dispatch routine is provided below:

dispatch is

require

s2a_table /= Void

ast_stack /= Void

local

tag: XML_ENDTAG

do

tag ?= ast_stack.item

s2a_table [tag.path].call ([])

ensure

ast_stack.count < old ast_stack.count

end

This dispatching is applied after every recognition of an XML_ENDTAG. Therefore when this routine

is invoked the top-most element on the internal stack (ast_stack) should be an end tag. Every tag

possesses a path feature which describes the absolute position of the XML element. By using this path

22

information as a key the correct agent from a string-to-agent table (s2a_table) is invoked. For example

on an </eiffel-proof> end-tag, the in-line agent from the following piece of code is called (delayed

invocation to realize delayed creation):

------------------------------------------------------------------

-- eiffel-proof

------------------------------------------------------------------

a_path :=

"eiffel-proof"

s2a_table [a_path] :=

agent: AST_BASE do

Result := create {EIFFEL_PROOF}.process (ast_stack,

ref_table)

ensure

ast_stack.count = 1

end

------------------------------------------------------------------

This code is used to initialize the s2a_table. Since an Eiffel-Proof is the top-most element, the path for

this case is just a plain ”eiffel-proof” string. The actual entry for the table is an in-line agent and it

creates on it's invocation an EIFFEL_PROOF node for the Eiffel-Proof object representing the structure

of the XML input.

The creation method process takes two arguments: The internal stack and a table to store reference-to-

object relations: XML elements may contain references to other elements in the document. Those

references are stored in the ref_table table. After having processed all sub-objects of an Eiffel-Proof

object the creation method puts the Eiffel-Proof object itself onto the stack.

After now having processed the whole input – after having read the </eiffel-proof> end tag – the

internal stack contains only one element. This element is the Eiffel-Proof object and it is the root of the

tree structure representing the input.

The above mechanism (which has been explained using the EIFFEL_PROOF as an example) is applied

in a similar manner also on the other element types, like CLASS_PROOF or ROUTINE_PROOF.

One further feature of the creation method process is that it checks if the provided XML input is valid

with respect to the XML schema description: If it discovers an unexpected element on the internal stack

it will throw an exception (which is processed in the full implementation of the dispatch routine).

23

The following figure summarizes the described mechanism to parse an example input (only the two

top-most elements are shown):

On the begin tag callback for <eiffel-proof> an object of the type XML_BEGTAG is put onto the stack

(number #1 in the above figure). The same is also true for the <class-proof> begin tag. After

processing the content of a class-proof the end tag </class-proof> is put onto the stack (number #2).

Since a whole class-proof element is now present on the stack, the corresponding elements are replaced

by a CLASS_PROOF object (number #3). This object represents a class-proof node for the final Eiffel-

Proof tree.

After the creation of the CLASS_PROOF node, an </eiffel-proof> XML_ENDTAG is put onto the

stack (number #4). Now the situation is analogous as before for class-proof: Since a whole eiffel-proof

element is now present on the stack, the corresponding elements are replaced by an EIFFEL_PROOF

object (number #5). This class represents the root of an Eiffel-Proof tree and contains internally the

CLASS_PROOF object. This is the reason why it is possible to remove the CLASS_PROOF object

from the stack, because it is still possible to access it over EIFFEL_PROOF object.

Above, the processing of the class-proof's content is not shown, but the content is handled using the

same algorithm that is used to handle the eiffel-proof and class-proof elements:

24

Figure 3: Example XML Input and Internal Stack

XML Input #1

</class-proof>

</eiffel-proof>

...

<class-proof>

<eiffel-proof>

Stack #3

<eiffel-proof>

CLASS_PROOF

Stack #5

EIFFEL_PROOF

Stack #1

<eiffel-proof>

XML Input #2

</class-proof>

</eiffel-proof>

...

<class-proof>

<eiffel-proof>

XML Input #3

</class-proof>

</eiffel-proof>

...

<class-proof>

<eiffel-proof>

Stack #2

<eiffel-proof>

<class-proof>

...

</class-proof>

Stack #4

<eiffel-proof>

CLASS_PROOF

</eiffel-proof>

1. Read the begin tag for an XML element (using the callback mechanism).

2. Create a corresponding XML_BEGTAG raw object.

3. Put the raw object onto the stack.

4. Process the content of the XML element (using this algorithm).

5. Read the end tag for the XML element (using the callback mechanism).

6. Create a corresponding XML_ENDTAG raw object.

7. Put the raw object onto the stack.

8. Create a tree node to replace the corresponding objects on the stack.

9. Remove the corresponding objects from the stack.

10. Put the created tree node onto the stack.

In the above algorithm step #4 might look like a recursive invocation of the algorithm, but this is not

the case: Since the reading of the XML elements is a passive process (the callback mechanism

provided by the GOBO library invokes the features implementing steps #1 and #5) there is no need to

explicitly invoke the algorithm.

Steps #8, #9 and #10 are crucial:

● The creation of a tree node at step #8 is handled using the agents stored in s2a_table. To

determine the correct agent the path information of an XML_ENDTAG (from step #6) is

utilized.

● The removing of the raw XML and node objects from the stack at step #9 is implemented in the

creation methods (all named process) of the different classes modeling the nodes of the Eiffel-

Proof tree.

● At step #10 the process creation feature puts after having processed the objects on the stack, the

object it belongs to onto the stack.

25

The Eiffel Contract Language Parser

The Eiffel Contract Language parser processes expressions which follow the BNF grammar provided

in the architecture chapter. This grammar has been translated into a parser description file for the

GOBO Eiffel Yacc tool. Using this tool and the description file the ECL parser has been generated.

In the appendix the ecl_parser.gey file containing the parser description is available. provides a

detailed discussion how the GOBO Eiffel Yacc tool interprets this input and generates the desired ECL

parser.

The parser requires also a scanner, which delivers the tokens needed by the latter to construct the

abstract syntax tree for the ECL expressions. This scanner has been implemented using the GOBO

Eiffel Lex tool. This tool needs also an file containing regular expressions describing the expected

input; in the appendix this ecl_scanner.gel file has been provided. gives an overview how the

GOBO Eiffel Lex tool produces from the above description the desired scanner.

All keywords for the Eiffel Contract Language are case insensitive and it is possible to write curly

brackets around them; this holds also for operators. This possibility to write the keywords and operators

within curly brackets is necessary, because the ECL expressions occur as contents between XML begin

and end tags. But the XML parser cannot handle (in the actual implementation) spaces within such a

content; therefore it is necessary to provide a possibility to write the ECL expressions without spaces.

For example, instead of <False implies 1=0> it is necessary to write <False{implies}1=0> or

<False{implies}1{=}0> is also possible. In keywords which contain spaces these spaces can be

replaced by an hyphen. The following list shows these cases:

● “and then” can be replaced by “and-then” and

● “or else” can be replaced by “or-else”.

This restriction is due the XML parser's shortcoming to handle spaces within XML contents correctly!

But the ECL parser itself is able to recognize both versions (with and without curly brackets/spaces).

To solve this problem the XML parser might be modified in such a way, that it could recognize the

differences between superfluous spaces (like trailing ones) and spaces within ECL expressions. For this

purpose the callback feature – reacting on the XML content event – has to be modified (a flag showing

that a processing of an ECL expression is in progress might be part of a solution).

26

Abstract Syntax Tree for Eiffel Proofs

The following illustration depicts the design for the Eiffel-Proof tree:

In the above figure, AST_BASE is the central class from which all the other classes for the Eiffel-Proof

Tree inherit. This base class itself inherits from AST_SHARED. This class contains properties which

hold for the whole tree.

In the lower part of the illustration the classes beginning with CPX model the elements from the XML

schema description which are of a complex type: This means those elements are made of several sub-

elements. For example for CPX_ARGUMENT the XML schema description looks like:

<xsd:element name="argument" maxOccurs="unbounded" minOccurs="0">

<xsd:complexType> <xsd:sequence>

<xsd:element name="name" type="xsd:string"></xsd:element>

<xsd:element name="type" type="xsd:string"></xsd:element>

</xsd:sequence> </xsd:complexType>

</xsd:element>

27

Figure 4: Eiffel-Proof Tree Overview

Since these types are described in the XSD as <xsd:complexType> for they are prefixed with CPX.

There are four important deferred classes which also inherit from AST_BASE. These deferred classes

are:

● XML_NODE:

This is the base class for the raw XML objects. These raw object are created – as explained

above – on a callback and put onto the internal stack.

● AST_CONTENT:

The classes inheriting from this deferred class model the different string elements which are

used in the XML input.

● AST_PROOF_ITEM:

The classes inheriting from this deferred class model the statement proofs within the body of a

routine-proof.

● AST_IDREF:

Some element in the XML input contain references to other elements. The classes inheriting

from this deferred class model these references.

The following diagrams show the classes which inherit from these deferred classes:

XML_BEGTAG and XML_ENDTAG – both inheriting from XML_TAG – model the corresponding

begin and end tags for XML elements. XML_ATTRIBUTE models an attribute of an XML element

and XML_CONTENT is the raw object for the string elements. These raw objects are later translated

to the mentioned AST_CONTENT's descendants.

28

Figure 5: Descendants of XML_NODE

29

Figure 6: Descendants of AST_CONTENT

There are two groups that model the raw string objects: The descendants from the first group (depicted

on the right side of the illustration showed above) represent plain strings and the descendants from the

second group (which inherit from ECL_CONTENT which in turn inherits from AST_CONTENT)

represent strings which contain Eiffel Contract Language expressions.

For all these strings the different types have been introduced to ensure that the validation – while

parsing the XML input – is executed correctly.

Figure 7 on page 31 shows the descendants of AST_PROOF_ITEM. These model the instructions of a

routine-proof's body and they are:

● PIT_ASSIGNMENT_PROOF,

● PIT_CHECK_PROOF,

● PIT_CLASS_RULE,

● PIT_COMPOSITION_PROOF,

● PIT_CONJUNCTION_PROOF,

● PIT_CREATE_PROOF,

● PIT_DISJUNCTION_PROOF,

● PIT_FALSE_AXIOM,

● PIT_IF_PROOF,

● PIT_IMPLEMENTATION_PROOF,

● PIT_INVOCATION_PROOF,

● PIT_JUNCTION_PROOF,

● PIT_LOOP_PROOF,

● PIT_READ_PROOF,

● PIT_RESCUE_PROOF ,

● PIT_STANDARD_RULE,

● PIT_STRONG_PROOF,

● PIT_SUBTYPE_RULE,

● PIT_WEAK_PROOF and

● PIT_WRITE_PROOF.

From all the above proofs composition-proof takes a special role, because it is responsible to glue the

different proof-items inside a routine-proof's body into a list of consecutive proofs.

30

31

Figure 7: Descendants of AST_PROOF_ITEM

The below figure depicts the descendants of the deferred AST_IDREF class; they model the mentioned

references. After having parsed an XML input, these references are then replaced by the corresponding

objects they refer to.

Again, for every reference a separate class has been introduced to ensure the correct validation of a

provided proof in an XML input file. The reference classes are:

● REF_BLOCK,

● REF_BODY,

● REF_DESCENDANT_ROUTINE,

● REF_ELSE_PART,

● REF_FROM_PART,

● REF_IMPLEMENTATION,

● REF_LOOP_BODY,

● REF_S1,

● REF_S2,

● REF_SUBTYPE and

● REF_THEN_PART.

For example every composition-proof contains a s1-reference (first statement) and a s2-reference

(second statement): Those two references are modeled by the REF_S1 and REF_S2 classes.

32

Figure 8: Descendants of AST_IDREF

Abstract Syntax Tree for the Eiffel Contract Language

The following figure depicts the design for the Eiffel Contract Language syntax tree:

The design of the syntax tree for the contract language is analogous to the BNF description of this

language. Since a visitor pattern is implemented all classes are derived from AST_VISITABLE and the

corresponding visitors are derived from AST_VISITOR:

SXP_PRINTER outputs the abstract syntax tree using s-expressions , VIS_LINEAR provides a

linearized view of the tree and VIS_TRANSLATOR applies the translation of the original class names

in Eiffel to the modified class names in CIL (for example: STRING might be translated to

STRING_CLASS or STRING_INTERFACE – depending on the context ).

Every contract is represented by the AST_CONTRACT class, which contains a list VIS_COLONLIST

of boolean expressions – modeled by AST_BOOLEXP – which are separated by semicolons. Similarly

AST_ARGS represents a list of arguments (boolean expressions) which are separated by commas

(therefore a VIS_COMMALIST is used).

33

Figure 9: Descendants of AST_VISITABLE

Figure 10: Descendants of DEF_VISITOR

34

Figure 11: Descendants of AST_BOOLEXP

Apart from classes like VIS_EQ, VIS_NEQ until VIS_GTE – these are self-describing – the

relationship between VIS_TYPEFUNC and AST_TYPEFUNC needs further clarification: A

VIS_TYPEFUNC is derived from AST_BOOLEXP and contains a feature of the deferred type

AST_TYPEFUNC, that is used to model functions on types; the following figure depicts these:

The VIS_CONFORMS class models expressions like object.type-info.conforms_to, VIS_IS_EQ

models expressions like object.type-info.is_equal and VIS_IS_NEQ is used to represent for expressions

like object.type.is_not_equal.

The deferred class AST_TYPEINFO has the following descendants: To model expressions like

{identifier} “.” type the class VIS_TTYPE is used (where the identifier should be a class name) and to

model expressions like expression “.” type the class VIS_ETYPE is used.

Boolean expressions may contain “arithmetic” expressions modeled by VIS_EXP. This class contains

a feature of the type AST_EXP, from which classes like VIS_TERM, VIS_FACT, VIS_SIGN,

VIS_INTEGER and VIS_ID descend.

Further, the classes VIS_CHARACTER and VIS_STRING are used to represent single characters and

strings (inside a list of arguments).

35

Figure 12: Descendants of AST_TYPEFUNC

Figure 13: Descendants of AST_TYPEINFO

VIS_OLD models expressions like old identifier. To express method invocations like

expression.identifier arguments the VIS_CALL class is necessary. VIS_CREATE is used to represent

expressions like create {identifier}.identifier arguments. The latter part is of the type VIS_ARGS. To

model voids the VIS_VOID class is necessary.

The below figure depicts the descendants of the VIS_TERMOP, VIS_FACTOP and VIS_SIGNOP

classes (which are used inside the VIS_TERM, VIS_FACT and the VIS_SIGN classes):

It is important to realize that the syntax tree for the Eiffel Contract Language reflects exactly the BNF

description of this language! The BNF has been setup to reduce the number of shift-reduce conflicts as

much as possible (down to just one harmless conflict).

Therefore it might be possible that the above design for the syntax tree needs some modifications to fit

the requirements which will be expressed in the next development stage of the proof-transforming

compiler.

36

Figure 14: Descendants of AST_EXP

Figure 15: Descendants of AST_TERMOP, AST_SIGNOP and AST_FACTOP

Proof- and Contract-Tree Integration

Since the proof-tree and the contract trees are produced by different parsers it is necessary to integrate

the contract-tree into the proof-tree. To understand this integration it is crucial to see the inheritance

relations between the AST_BASE, AST_CONTENT, ECL_CONTENT, AST_VISITABLE and

VIS_CONTRACT classes. The following figure depicts this relation:

To elaborate the reason why the above inheritance structure has been set up as above it is necessary to

explain the meanings of the different classes:

● AST_BASE:

Is the parent of the classes given in the proof-tree and AST_CONTENT inherits also from this

class. It plays the role of an anchor which enables the integration of the proof- and contract-

trees.

● AST_CONTENT:

Descendants of this class represent strings in the XML input. Therefore all those descendants

start with a STR prefix.

● ECL_CONTENT:

The descendants from this class contain either strings representing ECL expressions or they

play the role of a container holding the corresponding trees for these strings.

● VIS_CONTRACT:

This class is the root of trees representing Eiffel contracts or pre- and postconditions of

statements given in a routines body.

37

Figure 16: Tree Integration

● AST_VISITABLE:

Every descendant of this class can be visited by the descendants of the AST_VISITOR class.

Since VIS_CONTRACT and all the nodes in the tree below it inherit from AST_VISITABLE

the whole tree can be visited by an appropriately implemented visitor.

Here ECL_CONTENT plays the key role in the integration mechanism; it has a the following features

(only the relevant features are shown):

deferred class ECL_CONTENT

inherit AST_CONTENT

feature {NONE}

attr: TUPLE [content: LIST [AST_BASE]] -- XML_CONTENT / VIS_CONTRACT

Every feature holding data has been modeled as a tuple of lists. This is true for all descendants of the

AST_BASE class. The advantage of such a uniform representation is that is possible to write a generic

traversion algorithm for the whole tree. A further advantage of using polymorphic lists as containers is

that above it is possible to replace the XML_CONTENT class holding the strings after the parsing with

the corresponding trees' root of the VIS_CONTRACT type. A disadvantage of this design is that it can

sometimes be difficult to work with a tuple of lists.

feature {ANY}

set_contract (a_contract: VIS_CONTRACT) is

require else

a_contract /= Void

do

attr.content.wipe_out

attr.content.extend (a_contract)

ensure then

attr.content.first = a_contract

end

end

This feature replaces the original string with its corresponding tree (represented by its root a_contract).

38

Replacement Operation for References

An XML element may contain references to other elements. Once the XML input is parsed these

references need to be replaced by the corresponding objects they refer to. The basic idea for this

references-by-object replacement is the same as the idea for the string-by-tree replacement:

Descendants of AST_IDREF represent the references and this class inherits from AST_BASE. The

referred objects also inherit from AST_BASE. Since the references are stored in polymorphic lists a

corresponding replacement is possible.

The objects are either instances of the classes beginning with a CPX prefix or they are the descendants

of the AST_PROOF_ITEM class; the figure below provides an overview:

replace (a_idx: INTEGER; a_obj: AST_BASE) is

local

a_list: LIST [AST_BASE]

do

a_list ?= attr @ a_idx

a_list.wipe_out

a_list.extend (a_obj)

end

The replacement operation is implemented in the AST_BASE class. All descendants of it have a tuple

– named attr – of lists (to hold data). By using the integer a_idx the desired list can be selected. This

list's content is wiped out and the replacement object a_obj is put into the list. This approach works

because a_list is of the type LIST [AST_BASE].

39

Figure 17: Relevant Classes for the Reference-by-Object Replacement

40

4 Example

The following example demonstrates a sample input for the proof-transforming compiler: A bank

account is modeled by the BANK_ACCOUNT class and APPLICATION provides a very simple use

case for it:

indexing description: bank-account

class BANK_ACCOUNT

feature make (n: STRING) is

require (n/=Void)

do name := n balance := 0

ensure ((name=n) and (balance=0))

end feature

deposit (v: INTEGER) is

require (v>0)

do balance := (balance+v)

ensure (balance=((old balance)+v))

end feature

withdraw (v: INTEGER) is

require (balance>v)

do balance := (balance-v)

ensure (balance=((old balance)-v))

end end

The APPLICATION class looks like:

indexing description: application for bank-account

class APPLICATION

feature account: BANK_ACCOUNT

feature make (a_account: BANK_ACCOUNT) is

require (a_account/=Void)

do account := a_account

ensure (account=a_account)

end end

41

The XML file for BANK_ACCOUNT looks like:

<?xml version="1.0" encoding="UTF-8"?>

<eiffel-proof

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../xsd/eiffel-proof.xsd">

<class-proof> <name>bank_account</name>

<routine-proof> <name>make</name>

<argument> <name>n</name> <type>string</type> </argument>

<require>n{/=}Void</require> <ensure>(name{=}n){and}(balance{=}0)</ensure>

<composition-proof ID="comp-0"> <precondition>n{/=}Void</precondition> <postcondition> <normal>(name{=}n){and}(balance{=}0)</normal> </postcondition> <s1 ref="ass-0" /> <s2 ref="ass-1" /> </composition-proof>

<assignment-proof ID="ass-0"> <precondition>n{/=}Void</precondition> <postcondition> <normal>name{=}n</normal> </postcondition> <target>name</target> <expression>n</expression> </assignment-proof>

<assignment-proof ID="ass-1"> <precondition>name{=}n</precondition> <postcondition> <normal>(name{=}n){and}(balance{=}0)</normal> </postcondition> <target>balance</target> <expression>0</expression> </assignment-proof> </routine-proof>

<routine-proof> <name>deposit</name>

<argument> <name>v</name> <type>integer</type> </argument>

<require>v{GT}0</require> <ensure>balance{=}{old}balance{+}v</ensure>

<assignment-proof ID="ass-2"> <precondition>v{GT}0</precondition> <postcondition> <normal>balance{=}{old}balance{+}v</normal> </postcondition> <target>balance</target> <expression>balance{+}v</expression> </assignment-proof> </routine-proof>

<routine-proof> <name>withdraw</name>

42

<argument> <name>v</name> <type>integer</type> </argument>

<require>balance{GT}v</require> <ensure>balance{=}{old}balance{-}v</ensure>

<assignment-proof ID="ass-3"> <precondition>balance{GT}v</precondition> <postcondition> <normal>balance{=}{old}balance{-}v</normal> </postcondition> <target>balance</target> <expression>balance{-}v</expression> </assignment-proof> </routine-proof>

</class-proof> </eiffel-proof>

And the corresponding XML file for APPLICATION looks like:

<?xml version="1.0" encoding="UTF-8"?> <eiffel-proof

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../xsd/eiffel-proof.xsd">

<class-proof> <name>application</name>

<feature> <name>account</name> <type>bank_account</type> </feature>

<routine-proof> <name>make</name>

<argument> <name>a_account</name> <type>bank_account</type> </argument>

<require>a_account!=Void</require> <ensure>account=a_account</ensure>

<assignment-proof ID="r0-ass-0"> <precondition>a_account!=Void</precondition> <postcondition> <normal>account=a_account</normal> </postcondition> <target>account</target> <expression>a_account</expression> </assignment-proof> </routine-proof> </class-proof> </eiffel-proof>

The XML files contain more information than the Eiffel source code: These are the pre- and

postconditions for the single statements inside a routine-proof's body. By using the following

command, the stand-alone version for the PTC should reproduce the Eiffel source code:

cat bank_account.xml | ./ptc -EC ;

cat application.xml | ./ptc -EC ;

43

44

5 Conclusion

This work's goal of implementing a proof-transforming compiler for Eiffel contracts has been achieved.

The compiler has two parts – a specification translator and a proof translator – and the objective of this

project was to implement the first and to prepare the second part. These two parts have been

implemented successfully.

The proof-transforming compiler takes the proofs as an XML file. Therefore the development of a

validation XML parser for a given XML schema description was necessary. The implemented parser

works correctly and creates on correct input an abstract syntax tree for the proof and on erroneous input

the faulty line is indicated.

Since the proof may contain contracts, these need to be parsed by another parser. This sub-parser has

been implemented and is able to recognize a substantial subset of the Eiffel Contract Language. It

produces an abstract syntax tree for the contracts. Further, the sub-parser is able parse expressions

which are not necessarily contracts, but which may be present inside of instructions. Since the language

of the contracts and of such expressions is nearly identical, it has been decided to use the sub-parser for

both cases.

Both, the XML parser and the contract sub-parser produce abstract syntax trees. Since the tree of the

latter parser is part of the former parser's tree, it was necessary to integrate both trees into one tree.

There are conceptual differences between Eiffel and CIL with respect to multiple inheritance. These

differences needed special attention, but the transformation from Eiffel contracts to CIL has been

implemented as described in [4].

45

6 Bibliography

[1] B. Meyer. Object-Oriented Software Construction. Prentice Hall, second edition. 1997.

[2] E. W. Dijkstra, “Notes on Structured Programming”, Structured Programming, O.-J. Dahl, E. W. Dijkstra, and C. A. R. Hoare, Editors, Academic Press, London (1972), pp. 1–82.

[3] G. Necula. Compiling with Proofs. PhD thesis, School of Computer Science, Carnegie Mellon University, 1998.

[4] M. Nordio, P. Müller, and B. Meyer. Proof-Transforming Compilation of Eiffel programs. TOOLS-EUROPE, 2008.

[5] P. Müller and M. Nordio. Proof-transforming compilation of programs with abrupt termination. In Sixth International Workshop on Specification and Verification of Component-Based Systems (SAVCBS 2007), pages 39-46, 2007.

[6] M. Forsberg. Three Tools for Language Processing: BNF Converter, Functional Morphology, and Extract. PhD thesis, Göteborg University and Chalmers University of Technology, September 2007.

[7] M. Guex. Implementing a Proof-Transforming Compiler from Eiffel to CIL. Semester Thesis, ETH Zurich, February 2007.

[8] M. Nordio, P. Müller, and B. Meyer. Formalizing Proof-Transforming Compilation of Eiffel programs. Technical Report 587, ETH Zurich, 2008.

[9] B. Meyer (editor). ISO/ECMA Eiffel standard (Standard ECMA-367: Eiffel: Analysis, Design and Programming Language), June 2006. Available at http://www.ecma-international.org/publications/ standards/Ecma-367.htm.

[10] B. Meyer. Multi-language programming: how .net does it. In 3-part article in Software Development. May, June and July 2002, especially Part 1.

[11] C. A. R. Hoare, An axiomatic basis for computer programming, Communications of the ACM, v.12 n.10, p.576-580, Oct. 1969 [doi>10.1145/363235.363259]

[12] A. Zeller. Why Programs Fail: A Systematic Guide to Debugging. Morgan Kaufmann, October 2005.

[13] D.E. Knuth, Selected Papers on Computer Science, CSLI, 1996.

[14] A. Hunt and D. Thomas, The Pragmatic Programmer, Addison-Wesley, 1999.

[15] M. Fowler, Refactoring, Addison-Wesley, 1999.

46

7 Appendix

7.1 Read-me for the Proof-Transforming Compiler

It is possible to compile the PTC as a stand-alone tool which can be used over a command line

interface. As such, the stand-alone tool has several options that are described in the following read-me

file:

NAME

ptc - A proof-transforming compiler for XML files containing proofs for Eiffel programs.

SYNOPSIS

./ptc [-F|--file FILENAME]

[-I|--info]

[-W|--warning]

[-G|--error]

[-R|--no-ref2obj]

[-X|--pseudo-xml]

[-S|--s-expression]

[-E|--eiffel-out]

[-B|--bytecode-out]

[-C|--code-out]

[-P|--proof-out]

[-D|--debug]

DESCRIPTION

The proof-transforming-compiler takes an XML file, produces an internal abstract syntax tree for

it and possibly produces some output. The XML files have to contain proofs for Eiffel programs

and they should be valid with respect to the XSD description "eiffel-proof.xsd". If not the

compiler will stop processing the input at the erroneous location. There are six possible

outputs: pseudo-XML, s-expressions, eiffel-out, bytecode-out, code-out and proof-out

(combinations of the last four).

OPTIONS

47

--file (-F)

XML file to be processed; if this option is omitted the compiler will process the

standard input until an EOF is detected.

--info (-I)

--warning (-W)

--error (-G)

Print info, warning or error messages; since these are printed using the error output,

one may also redirect these channels to /dev/null to suppress them (.. 2> /dev/null).

--no-ref2obj (-R)

The XML input might contain elements which do refer to other elements by using a string

identifier. By default such references are replaced by the actual object after having

processed the input; but there might be cases where this replacement is not desired (e.g.

test cases which contain cyclic references): For such input this option will omit the

reference-to-object replacement.

--pseudo-xml (-X)

--s-expression (-S)

Enable pseudo-XML or s-expressions; since those are printed using the error output, one

may also redirect these channels to /dev/null to suppress them (.. 2> /dev/null).

Pseudo-XML: *While* parsing the input file and constructing the corresponding syntax tree

an output similar to XML is produced; if some error (e.g. validation) occurs, the

processing stops at the line of the failure.

S-Expressions: *After* having processed the XML input and *after* the full construction

of the syntax tree, the latter is printed to the standard output.

--eiffel-out (-E)

--bytecode-out (-B)

--code-out (-C)

--proof-out (-P)

The following combinations for eiffel-out, bytecode-out, code-out and proof-out make

sense:

1. -EC : eiffel - code - out [--eiffel-out --code-out]

2. -EP : eiffel - proof - out [--eiffel-out --proof-out]

3. -BC : bytecode - code - out [--bytecode-out –code-out]

4. -BP : bytecode - proof - out [--bytecode-out --proof-out]

48

If none of the above flag combinations is provided the default output of the proof-

transforming compiler is nothing!

--debug (-D)

Prints some debug related information (if a failure has happened). The parser uses an

internal stack to process the XML elements. If something goes wrong, using this option

some information related to this stack is produced.

TESTS

A simple test script written in Bash to verify the PTC (or the XML files' validity – if you

trust the PTC) could look like:

for fn in $(ls ../pool/manual/*.xml) ; do \

\

./ptc -F $fn -X -EBCP 1> /dev/null | echo "$? @ $fn" ; \

\

done

If everything goes fine, a "0 @ $file-name" for every valid XML file should be printed,

otherwise a "1 @ $file-name". Such files can then further be analyzed with:

./ptc -F $file-name -IWGD -X

The program will start parsing and *during* the parsing process a pseudo-XML output will be

produced. If the input XML contains a validity/syntax error the parser will stop the output at

the erroneous location.

If one wants to check the internal representation (abstract syntax tree) for a given XML input,

the following combination of options will be useful:

./ptc -F $file-name -IWGD -S

This will omit any pseudo-XML, but will only print the abstract syntax tree using s-expressions.

I.e. for every node in the tree the output will look like "(" $node-name $node-children ")",

where the $node-children themselve are again tree-nodes (recursive-definition).

BUGS

XML files which do have cyclic references might create an endless-loop. Further the body of

routines are not translated yet to bytecode-proofs; this part has to be implemented in another

project on proof-transforming-compilation.

49

FILES

../pool/autogen/*.xml

Those XML files have been automatically generated according to "eiffel-proof.xsd". They have

been used to test the correctness of the parser. They may contain cyclic references! Therefore

it is recommended to use the --no-ref2obj option while testing the PTC with these files.

../pool/manual/*.xml

Handcrafted XML files which contain proofs for particular Eiffel programs.

../xsd/eiffel-proof.xsd

Describes how the XML files should look like. They should contain a proof for Eiffel programs.

AUTHORs

Hasan Karahan <[email protected]> is the author of this proof-transforming-compiler's

version; it has been written during his diploma thesis at the ETH Zurich from January to May

2008.

Michel Guex <[email protected]> has written a preliminary version of the proof-transforming-

compiler during a semester thesis from July 2006 to February 2007.

Martin Nordio <[email protected]> has supervised the development of both the preliminary

and the actual version of the proof-transforming-compiler.

50

7.2 Schema Description for the XML Input

An XSD file describes the schema for the expected XML input, which the proof-transforming compiler

processes. The proof provided within such an XML input should be valid to this XSD, otherwise the

compiler will stop processing the proof. Below, the whole schema description follows:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<xsd:element name="eiffel-proof" type="TEiffelProof"></xsd:element>

■ TEiffelProof

<xsd:complexType name="TEiffelProof"> <xsd:sequence> <xsd:element name="class-proof" type="TClassProof"></xsd:element> </xsd:sequence> </xsd:complexType>

■ TClassProof

<xsd:complexType name="TClassProof"> <xsd:sequence> <xsd:element name="name" type="xsd:string"></xsd:element> <xsd:element maxOccurs="unbounded" minOccurs="0" name="feature"> <xsd:complexType> <xsd:sequence> <xsd:element name="name" type="xsd:string"></xsd:element> <xsd:element name="type" type="xsd:string"></xsd:element> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element maxOccurs="unbounded" minOccurs="1" name="routine-proof" type="TRoutineProof"></xsd:element> </xsd:sequence> </xsd:complexType>

■ TRoutineProof

<xsd:complexType name="TRoutineProof"> <xsd:sequence> <xsd:element name="name" type="xsd:string"></xsd:element> <xsd:element maxOccurs="unbounded" minOccurs="0" name="local"> <xsd:complexType> <xsd:sequence> <xsd:element name="name" type="xsd:string"></xsd:element> <xsd:element name="type" type="xsd:string"></xsd:element> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element maxOccurs="unbounded" minOccurs="0" name="argument"> <xsd:complexType> <xsd:sequence> <xsd:element name="name" type="xsd:string"></xsd:element> <xsd:element name="type" type="xsd:string"></xsd:element> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element minOccurs="0" name="return-type" type="xsd:string"/> <xsd:element minOccurs="0" maxOccurs="unbounded" name="require" type="xsd:string"/> <xsd:element minOccurs="0" maxOccurs="unbounded" name="ensure" type="xsd:string"/>

<xsd:choice maxOccurs="unbounded" minOccurs="0"> <xsd:element maxOccurs="unbounded" name="composition-proof" type="TCompositionProof"/> <xsd:element maxOccurs="unbounded" name="assignment-proof"

51

type="TAssignmentProof"/> <xsd:element maxOccurs="unbounded" name="if-proof" type="TIfProof"/> <xsd:element maxOccurs="unbounded" name="loop-proof" type="TLoopProof"/> <xsd:element maxOccurs="unbounded" name="invocation-proof" type="TInvocationProof"/> <xsd:element maxOccurs="unbounded" name="implementation-proof" type="TImplementationProof"/> <xsd:element maxOccurs="unbounded" name="read-attribute" type="TReadProof"/> <xsd:element maxOccurs="unbounded" name="write-attribute" type="TWriteProof"/> <xsd:element maxOccurs="unbounded" name="create-proof" type="TCreateProof"/> <xsd:element maxOccurs="unbounded" name="check-proof" type="TCheckProof"/> <xsd:element maxOccurs="unbounded" name="rescue-proof" type="TRescueProof"/> <xsd:element maxOccurs="unbounded" name="disjunction-proof" type="TJunctionProof"/> <xsd:element maxOccurs="unbounded" name="conjunction-proof" type="TJunctionProof"/> <xsd:element maxOccurs="unbounded" name="strong-proof" type="TStrongProof"/> <xsd:element maxOccurs="unbounded" name="weak-proof" type="TWeakProof"/> <xsd:element maxOccurs="unbounded" name="false-axiom" type="TFalseAxiom"/> <xsd:element maxOccurs="unbounded" name="class-rule" type="TClassRule"/> <xsd:element maxOccurs="unbounded" name="subtype-rule" type="TSubtypeRule"/> <xsd:element maxOccurs="unbounded" name="invariant-rule" type="TStandardRule"/> <xsd:element maxOccurs="unbounded" name="substitution-rule" type="TStandardRule"/> <xsd:element maxOccurs="unbounded" name="all-rule" type="TStandardRule"/> <xsd:element maxOccurs="unbounded" name="ex-rule" type="TStandardRule"/> </xsd:choice>

</xsd:sequence> <xsd:attribute name="once" type="xsd:boolean" default="false"/> </xsd:complexType>

■ TProofItem

<xsd:complexType name="TProofItem"> <xsd:sequence> <xsd:element name="precondition" type="xsd:string"></xsd:element> <xsd:element name="postcondition"> <xsd:complexType> <xsd:sequence> <xsd:element name="normal" type="xsd:string"></xsd:element> <xsd:element minOccurs="0" name="exception" type="xsd:string"></xsd:element> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:sequence> </xsd:complexType>

■ TAssignmentProof

<xsd:complexType name="TAssignmentProof"> <xsd:complexContent> <xsd:extension base="TProofItem"> <xsd:sequence> <xsd:element name="target" type="xsd:string"></xsd:element> <xsd:element name="expression" type="xsd:string"></xsd:element> </xsd:sequence> <xsd:attribute name="ID" type="xsd:ID" use="required"></xsd:attribute> </xsd:extension> </xsd:complexContent> </xsd:complexType>

52

■ TCompositionProof

<xsd:complexType name="TCompositionProof"> <xsd:complexContent> <xsd:extension base="TProofItem"> <xsd:sequence> <xsd:element name="s1"> <xsd:complexType> <xsd:attribute name="ref" type="xsd:IDREF" use="required"></xsd:attribute> </xsd:complexType> </xsd:element> <xsd:element name="s2"> <xsd:complexType> <xsd:attribute name="ref" type="xsd:IDREF" use="required"></xsd:attribute> </xsd:complexType> </xsd:element> </xsd:sequence> <xsd:attribute name="ID" type="xsd:ID" use="required"></xsd:attribute> </xsd:extension> </xsd:complexContent> </xsd:complexType>

■ TIfProof

<xsd:complexType name="TIfProof"> <xsd:complexContent> <xsd:extension base="TProofItem"> <xsd:sequence> <xsd:element name="expression" type="xsd:string"></xsd:element> <xsd:element name="then-part"> <xsd:complexType> <xsd:attribute name="ref" type="xsd:IDREF" use="required"></xsd:attribute> </xsd:complexType> </xsd:element> <xsd:element minOccurs="0" name="else-part"> <xsd:complexType> <xsd:attribute name="ref" type="xsd:IDREF" use="required"></xsd:attribute> </xsd:complexType> </xsd:element> </xsd:sequence> <xsd:attribute name="ID" type="xsd:ID" use="required"></xsd:attribute> </xsd:extension> </xsd:complexContent> </xsd:complexType>

■ TLoopProof

<xsd:complexType name="TLoopProof"> <xsd:complexContent> <xsd:extension base="TProofItem"> <xsd:sequence> <xsd:element name="from-part"> <xsd:complexType> <xsd:attribute name="ref" type="xsd:IDREF" use="required"></xsd:attribute> </xsd:complexType> </xsd:element> <xsd:element name="until-part" type="xsd:string"></xsd:element> <xsd:element name="loop-body"> <xsd:complexType> <xsd:attribute name="ref" type="xsd:IDREF" use="required"></xsd:attribute> </xsd:complexType> </xsd:element> </xsd:sequence> <xsd:attribute name="ID" type="xsd:ID" use="required"> </xsd:attribute> </xsd:extension> </xsd:complexContent> </xsd:complexType>

53

■ TRetryProof

<xsd:complexType name="TRetryProof"> <xsd:complexContent> <xsd:extension base="TProofItem"> <xsd:attribute name="ID" type="xsd:ID" use="required"> </xsd:attribute> </xsd:extension> </xsd:complexContent> </xsd:complexType>

■ TCreateProof

<xsd:complexType name="TCreateProof"> <xsd:complexContent> <xsd:extension base="TProofItem"> <xsd:sequence> <xsd:element name="target" type="xsd:string"></xsd:element> <xsd:element name="type" type="xsd:string"></xsd:element> <xsd:element name="argument" minOccurs="0" maxOccurs="unbounded"> <xsd:complexType> <xsd:sequence> <xsd:element name="name" type="xsd:string"> </xsd:element> <xsd:element name="type" type="xsd:string"> </xsd:element> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:sequence> <xsd:attribute name="ID" type="xsd:ID" use="required"> </xsd:attribute> </xsd:extension> </xsd:complexContent> </xsd:complexType>

■ TCheckProof

<xsd:complexType name="TCheckProof"> <xsd:complexContent> <xsd:extension base="TProofItem"> <xsd:sequence> <xsd:element name="expression" type="xsd:string" minOccurs="0" maxOccurs="unbounded"></xsd:element> </xsd:sequence> <xsd:attribute name="ID" type="xsd:ID" use="required"></xsd:attribute> </xsd:extension> </xsd:complexContent> </xsd:complexType>

■ TStrongProof

<xsd:complexType name="TStrongProof"> <xsd:complexContent> <xsd:extension base="TProofItem"> <xsd:sequence> <xsd:element name="s1"> <xsd:complexType> <xsd:attribute name="ref" type="xsd:IDREF" use="required"></xsd:attribute> </xsd:complexType> </xsd:element> <xsd:element name="implication"> <xsd:complexType> <xsd:sequence> <xsd:element name="lhs" type="xsd:string"> </xsd:element> <xsd:element name="rhs" type="xsd:string"> </xsd:element> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:sequence> <xsd:attribute name="ID" type="xsd:ID"

54

use="required"></xsd:attribute> </xsd:extension> </xsd:complexContent> </xsd:complexType>

■ TWeakProof

<xsd:complexType name="TWeakProof"> <xsd:complexContent> <xsd:extension base="TProofItem"> <xsd:sequence> <xsd:element name="s1"> <xsd:complexType> <xsd:attribute name="ref" type="xsd:IDREF" use="required"></xsd:attribute> </xsd:complexType> </xsd:element> <xsd:element name="implication"> <xsd:complexType> <xsd:sequence> <xsd:element name="lhs" type="xsd:string"> </xsd:element> <xsd:element name="rhs" type="xsd:string"> </xsd:element> </xsd:sequence> <xsd:attribute name="mode" use="required"> <xsd:simpleType> <xsd:restriction base="xsd:string"> <xsd:enumeration value="normal"> </xsd:enumeration> <xsd:enumeration value="exception"> </xsd:enumeration> </xsd:restriction> </xsd:simpleType> </xsd:attribute> </xsd:complexType> </xsd:element> </xsd:sequence> <xsd:attribute name="ID" type="xsd:ID" use="required"></xsd:attribute> </xsd:extension> </xsd:complexContent> </xsd:complexType>

■ TInvocationProof

<xsd:complexType name="TInvocationProof"> <xsd:complexContent> <xsd:extension base="TProofItem"> <xsd:sequence> <xsd:element name="target" type="xsd:string"></xsd:element> <xsd:element name="routine" type="xsd:string"></xsd:element> <xsd:element name="object" type="xsd:string"></xsd:element> <xsd:element name="argument" type ="xsd:string" minOccurs="0" maxOccurs="unbounded"> </xsd:element> </xsd:sequence> <xsd:attribute name="ID" type="xsd:ID" use="required"></xsd:attribute> </xsd:extension> </xsd:complexContent> </xsd:complexType>

■ TReadProof

<xsd:complexType name="TReadProof"> <xsd:complexContent> <xsd:extension base="TProofItem"> <xsd:sequence> <xsd:element name="target" type="xsd:string"></xsd:element> <xsd:element name="object" type="xsd:string"></xsd:element> <xsd:element name="field" type="xsd:string"></xsd:element> </xsd:sequence> <xsd:attribute name="ID" type="xsd:ID" use="required"></xsd:attribute> </xsd:extension> </xsd:complexContent> </xsd:complexType>

55

■ TWriteProof

<xsd:complexType name="TWriteProof"> <xsd:complexContent> <xsd:extension base="TProofItem"> <xsd:sequence> <xsd:element name="object" type="xsd:string"></xsd:element> <xsd:element name="field" type="xsd:string"></xsd:element> <xsd:element name="expression" type="xsd:string"></xsd:element> </xsd:sequence> <xsd:attribute name="ID" type="xsd:ID" use="required"></xsd:attribute> </xsd:extension> </xsd:complexContent> </xsd:complexType>

■ TRescueProof

<xsd:complexType name="TRescueProof"> <xsd:complexContent> <xsd:extension base="TProofItem"> <xsd:sequence> <xsd:element name="implication"> <xsd:complexType> <xsd:sequence> <xsd:element name="lhs" type="xsd:string"></xsd:element> <xsd:element name="rhs" type="xsd:string"></xsd:element> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="body"> <xsd:complexType> <xsd:attribute name="ref" type="xsd:IDREF" use="required"></xsd:attribute> </xsd:complexType> </xsd:element> <xsd:element name="block"> <xsd:complexType> <xsd:attribute name="ref" type="xsd:IDREF" use="required"></xsd:attribute> </xsd:complexType> </xsd:element> </xsd:sequence> <xsd:attribute name="ID" type="xsd:ID" use="required"></xsd:attribute> </xsd:extension> </xsd:complexContent> </xsd:complexType>

■ TImplementationProof

<xsd:complexType name="TImplementationProof"> <xsd:complexContent> <xsd:extension base="TProofItem"> <xsd:sequence> <xsd:element name="routine" type="xsd:string"></xsd:element> <xsd:element name="body"> <xsd:complexType> <xsd:attribute name="ref" type="xsd:IDREF" use="required"></xsd:attribute> </xsd:complexType> </xsd:element> </xsd:sequence> <xsd:attribute name="ID" type="xsd:ID" use="required"></xsd:attribute> </xsd:extension> </xsd:complexContent> </xsd:complexType>

■ TClassRule

<xsd:complexType name="TClassRule"> <xsd:complexContent> <xsd:extension base="TProofItem">

56

<xsd:sequence> <xsd:element name="subtype"> <xsd:complexType> <xsd:attribute name="ref" type="xsd:IDREF" use="required"></xsd:attribute> </xsd:complexType> </xsd:element> <xsd:element name="implementation"> <xsd:complexType> <xsd:attribute name="ref" type="xsd:IDREF" use="required"></xsd:attribute> </xsd:complexType> </xsd:element> </xsd:sequence> <xsd:attribute name="ID" type="xsd:ID" use="required"></xsd:attribute> </xsd:extension> </xsd:complexContent> </xsd:complexType>

■ TSubtypeRule

<xsd:complexType name="TSubtypeRule"> <xsd:complexContent> <xsd:extension base="TProofItem"> <xsd:sequence> <xsd:element name="routine" type="xsd:string"></xsd:element> <xsd:element name="descendant-routine"> <xsd:complexType> <xsd:attribute name="ref" type="xsd:IDREF" use="required"></xsd:attribute> </xsd:complexType> </xsd:element> <xsd:element name="type" type="xsd:string"></xsd:element> <xsd:element name="subtype" type="xsd:string"></xsd:element> </xsd:sequence> <xsd:attribute name="ID" type="xsd:ID" use="required"></xsd:attribute> </xsd:extension> </xsd:complexContent> </xsd:complexType>

■ TFalseAxiom

<xsd:complexType name="TFalseAxiom"> <xsd:complexContent> <xsd:extension base="TProofItem"> <xsd:sequence> <xsd:element name="s1"> <xsd:complexType> <xsd:attribute name="ref" type="xsd:IDREF" use="required"></xsd:attribute> </xsd:complexType> </xsd:element> </xsd:sequence> <xsd:attribute name="ID" type="xsd:ID" use="required"></xsd:attribute> </xsd:extension> </xsd:complexContent> </xsd:complexType>

■ TStandardRule

<xsd:complexType name="TStandardRule"> <xsd:complexContent> <xsd:extension base="TProofItem"> <xsd:sequence> <xsd:element name="s1"> <xsd:complexType> <xsd:attribute name="ref" type="xsd:IDREF" use="required"></xsd:attribute> </xsd:complexType> </xsd:element> </xsd:sequence> <xsd:attribute name="ID" type="xsd:ID" use="required"></xsd:attribute> </xsd:extension> </xsd:complexContent>

57

</xsd:complexType>

■ TJunctionProof

<xsd:complexType name="TJunctionProof"> <xsd:complexContent> <xsd:extension base="TProofItem"> <xsd:sequence> <xsd:element name="s1"> <xsd:complexType> <xsd:attribute name="ref" type="xsd:IDREF" use="required"></xsd:attribute> </xsd:complexType> </xsd:element> <xsd:element name="s2"> <xsd:complexType> <xsd:attribute name="ref" type="xsd:IDREF" use="required"></xsd:attribute> </xsd:complexType> </xsd:element> </xsd:sequence> <xsd:attribute name="ID" type="xsd:ID" use="required"/> </xsd:extension> </xsd:complexContent> </xsd:complexType>

</xsd:schema>

58

7.3 Remarks on the XML Schema Description

Every eiffel-proof contains one class-proof, which has a name, several features and at least one routine-

proof. Such a routine-proof has a name, local features, arguments, possibly a return-type and contracts

(require and ensure). Further a routine-proof can be declared once using a boolean attribute. After the

contracts the body of a routine with different kind of statements follows. Below are the logical models

for the class-proof's and routine-proof's structures:

For routine-proof the proof structures of the body's statements are shown in the next sub-chapter (the

lowest pictograph in the above illustration with the four vertical squares symbolizes the routine-proof's

body).

In Eiffel it is possible to express class invariants, but in the above model this possibility is omitted.

Therefore the present version of the proof-transforming compiler is not capable to recognize such

invariants. The possibility to express them will be included within the scope of another project on

proof-transforming compilation.

59

Figure 19: Logical Model for Class-Proof

Illustration 18: Logical Model for Eiffel-Proof

60

Figure 20: Logical Model for Routine-Proof

7.4 Statements of a Routine-Proof

Since there are many different kind of statements for a routine-proof's body, for them – except for

assignment-proof – only graphical presentations are shown.

The figure on the next page is a logical model of an assignment-proof: Since it derives from a proof-

item it has a precondition and a postcondition. Such a postcondition has a normal mode and possibly an

exception mode. Further an assignment-proof has a target and an expression.

Using Hoare triples it is possible to present the assignment proof's structure in a textual form, where the

assignment operator is also visible:

{precondition} target := expression {postconditionnormal, postconditionexception}.

Below the logical models for all proof structures of the different statements (of a routine-proof's body)

follows:

61

Figure 21: Logical Model for Assignment-Proof

62

Figure 22: Logical Model for Composition-Proof

63

Figure 23: Logical Model for Ex-Rule

64

Figure 24: Logical Model for False-Axiom

65

Figure 25: Logical Model for If-Proof

66

Figure 26: Logical Model for Implementation-Proof

67

Figure 27: Logical Model for Invariant-Rule

68

Figure 28: Logical Model for Invocation-Proof

69

Figure 29: Logical Model for Loop-Proof

70

Figure 30: Logical Model for Read-Attribute

71

Figure 31: Logical Model for Rescue-Proof

72

Figure 32: Logical Model for Strong-Proof

73

Figure 33: Logical Model for Substitution-Rule

74

Figure 34: Logical Model for Subtype-Rule

75

Figure 35: Logical Model for Weak-Proof

76

Figure 36: Logical Model for Write-Attribute

77

Figure 37: Logical Model for All-Rule

78

Figure 38: Logical Model for Conjunction-Proof

79

Figure 39: Logical Model for Disjunction-Proof

80

Figure 40: Logical Model for Class-Rule

81

Figure 41: Logical Model for Create-Proof

7.5 Eiffel Contract Language Parser

The Eiffel Contract Language parser is generated by using the GOBO Lex and GOBO Yacc tools; the

lexer generator GOBO Lex requires the ecl_scanner.gel file and the describes the recognized

tokens using regular expressions:

%{

class ECL_SCANNER

inherit

YY_COMPRESSED_SCANNER_SKELETON

redefine

make, reset

select

make, reset

end

YY_COMPRESSED_SCANNER_SKELETON

rename

make as make_scanner_skeleton,

reset as reset_scanner_skeleton

export

{NONE} all

end

ECL_TOKENS

create

make

%}

%option line

%option position

LETTER [a-zA-Z]

DIGIT [0-9]

IDENT [a-zA-Z][a-zA-Z0-9_]*

%%

82

"="|"{=}"|"{"[eE][qQ]"}" {

set_last_token (SYMB_EQ)

}

"!="|"{!=}"|"{"[nN][eE][qQ]"}" {

set_last_token (SYMB_NEQ)

}

"<"|"{<}"|"{"[lL][tT]"}" {

set_last_token (SYMB_LT)

}

"<="|"{<=}"|"{"[lL][tT][eE]"}" {

set_last_token (SYMB_LTe)

}

">"|"{>}"|"{"[gG][tT]"}" {

set_last_token (SYMB_GT)

}

">="|"{>=}"|"{"[gG][tT][eE]"}" {

set_last_token (SYMB_GTe)

}

"+"|"{+}"|"{"[aA][dD][dD]"}"|"{"[pP][oO][sS]"}" {

set_last_token (SYMB_ADD)

}

"-"|"{-}"|"{"[sS][uU][bB]"}"|"{"[mM][iI][nN]"}" {

set_last_token (SYMB_SUB)

}

"*"|"{*}"|"{"[mM][uU][lL]"}" {

set_last_token (SYMB_MUL)

}

"/"|"{/}"|"{"[dD][iI][vV]"}" {

set_last_token (SYMB_DIV)

}

"("|"{(}"|"{"[lL][nN][bB]"}" {

set_last_token (SYMB_LPAR)

}

")"|"{)}"|"{"[rR][nN][bB]"}" {

83

set_last_token (SYMB_RPAR)

}

"{"|"{{}"|"{"[lL][cC][bB]"}" {

set_last_token (SYMB_LCB)

}

"}"|"{}}"|"{"[rR][cC][bB]"}" {

set_last_token (SYMB_RCB)

}

"."|"{.}"|"{"[dD][oO][tT]"}" {

set_last_token (SYMB_DOT)

}

","|"{,}"|"{"[cC][oO][mM][mM][aA]"}" {

set_last_token (SYMB_COMMA)

}

";"|"{;}"|"{"[sS][eE][mM][iI]"}" {

set_last_token (SYMB_SEMICOLON)

}

[iI][mM][pP][lL][iI][eE][sS] |

[\{][iI][mM][pP][lL][iI][eE][sS][\}] {

set_last_token (SYMB_IMPLIES)

}

[aA][nN][dD][ ][tT][hH][eE][nN] |

[\{][aA][nN][dD][-][tT][hH][eE][nN][\}] {

set_last_token (SYMB_ANDTHEN)

}

[aA][nN][dD] |

[\{][aA][nN][dD][\}] {

set_last_token (SYMB_AND)

}

[oO][lL][dD] |

[\{][oO][lL][dD][\}] {

set_last_token (SYMB_OLD)

84

}

[oO][rR][ ][eE][lL][sS][eE] |

[\{][oO][rR][-][eE][lL][sS][eE][\}] {

set_last_token (SYMB_ORELSE)

}

[oO][rR] |

[\{][oO][rR][\}] {

set_last_token (SYMB_OR)

}

[xX][oO][rR] |

[\{][xX][oO][rR][\}] {

set_last_token (SYMB_XOR)

}

[nN][oO][tT] |

[\{][nN][oO][tT][\}] {

set_last_token (SYMB_NOT)

}

[cC][oO][nN][fF][oO][rR][mM][sS][_][tT][oO] |

[\{][cC][oO][nN][fF][oO][rR][mM][sS][_][tT][oO][\}] {

set_last_token (SYMB_CONFORMS_TO)

}

[iI][sS][_][eE][qQ][uU][aA][lL] |

[\{][iI][sS][_][eE][qQ][uU][aA][lL][\}] {

set_last_token (SYMB_IS_EQUAL)

}

[iI][sS][_][nN][oO][tT][_][eE][qQ][uU][aA][lL] |

[\{][iI][sS][_][nN][oO][tT][_][eE][qQ][uU][aA][lL][\}] {

set_last_token (SYMB_IS_NOT_EQUAL)

}

[tT][yY][pP][eE] |

85

[\{][tT][yY][pP][eE][\}] {

set_last_token (SYMB_TYPE)

}

[fF][aA][lL][sS][eE] |

[\{][fF][aA][lL][sS][eE][\}] {

set_last_token (SYMB_FALSE)

}

[tT][rR][uU][eE] |

[\{][tT][rR][uU][eE][\}] {

set_last_token (SYMB_TRUE)

}

[vV][oO][iI][dD] |

[\{][vV][oO][iI][dD][\}] {

set_last_token (SYMB_VOID)

}

[cC][rR][eE][aA][tT][eE] |

[\{][cC][rR][eE][aA][tT][eE][\}] {

set_last_token (SYMB_CREATE)

}

{DIGIT}+ {

set_last_token (SYMB_INTEGER)

}

{IDENT} {

set_last_token (SYMB_IDENT)

}

"'"{LETTER}"'" {

set_last_token (SYMB_CHARACTER)

}

\"{LETTER}+\" {

set_last_token (SYMB_STRING)

}

86

[ \t\r\n\f]+ { -- ignore white space

}

%%

feature {NONE} -- initialization

make is

do

make_scanner_skeleton

create buffer.make (256)

end

feature -- reset scanner

reset is

do

reset_scanner_skeleton

buffer.wipe_out

end

feature -- access

buffer: STRING -- scanner's buffer

invariant

buffer /= Void

end

The GOBO Yacc parser generator requires the ecl_parser.gey file describing the Eiffel-Contract

Language:

%{

class ECL_PARSER

inherit

YY_PARSER_SKELETON

rename

make as make_parser

redefine

87

report_error

end

ECL_SCANNER

rename

make as make_scanner

end

create

make, execute

feature {ANY}

ast_root: VIS_CONTRACT

%}

-- ---------------------------------------------------------------------------

-- start token

-- ---------------------------------------------------------------------------

%start Contract

-- ---------------------------------------------------------------------------

-- tokens

-- ---------------------------------------------------------------------------

-- %token _ERROR_

%token <STRING> SYMB_EQ "{=}"

%left SYMB_EQ

%token <STRING> SYMB_NEQ "{!=}"

%left SYMB_NEQ

%token <STRING> SYMB_LT "{<}"

%nonassoc SYMB_LT

%token <STRING> SYMB_LTe "{<=}"

%nonassoc SYMB_LTe

%token <STRING> SYMB_GT "{>}"

%nonassoc SYMB_GT

%token <STRING> SYMB_GTe "{>=}"

%nonassoc SYMB_GTe

88

%token <STRING> SYMB_ADD "{+}" -- SYMB_POS

%left SYMB_ADD

%token <STRING> SYMB_SUB "{-}" -- SYMB_NEG

%left SYMB_SUB

%token <STRING> SYMB_MUL "{*}"

%left SYMB_MUL

%token <STRING> SYMB_DIV "{/}"

%left SYMB_DIV

%token <STRING> SYMB_DOT "{.}"

%left SYMB_DOT

%token <STRING> SYMB_COMMA "{,}"

%left SYMB_COMMA

%token <STRING> SYMB_SEMICOLON "{;}"

%left SYMB_SEMICOLON

%token <STRING> SYMB_IMPLIES "implies"

%right SYMB_IMPLIES

%token <STRING> SYMB_ANDTHEN "and then"

%left SYMB_ANDTHEN

%token <STRING> SYMB_AND "and"

%left SYMB_AND

%token <STRING> SYMB_ORELSE "or else"

%left SYMB_ORELSE

%token <STRING> SYMB_OR "or"

%left SYMB_OR

%token <STRING> SYMB_XOR "xor"

%left SYMB_XOR

%token <STRING> SYMB_NOT "not"

%right SYMB_NOT

%token <STRING> SYMB_CONFORMS_TO "conforms_to"

%token <STRING> SYMB_IS_EQUAL "is_equal"

%token <STRING> SYMB_IS_NOT_EQUAL "is_not_equal"

%token <STRING> SYMB_TYPE "type"

%token <STRING> SYMB_FALSE "False"

%token <STRING> SYMB_TRUE "True"

%token <STRING> SYMB_VOID "Void"

%token <STRING> SYMB_CREATE "create"

%token <STRING> SYMB_OLD "old"

%token <STRING> SYMB_LPAR "{(}"

%token <STRING> SYMB_RPAR "{)}"

89

%token <STRING> SYMB_LCB "{{}"

%token <STRING> SYMB_RCB "{}}"

%token <STRING> SYMB_LEB "{[}"

%token <STRING> SYMB_REB "{]}"

-- ---------------------------------------------------------------------------

-- types

-- ---------------------------------------------------------------------------

%type <VIS_CONTRACT> Contract

%type <AST_BOOLEXP> BoolExp

%type <AST_BOOLEXP> BoolExp1

%type <AST_BOOLEXP> BoolExp2

%type <AST_BOOLEXP> BoolExp3

%type <AST_BOOLEXP> BoolExp4

%type <AST_BOOLEXP> BoolExp5

%type <AST_BOOLEXP> BoolExp6

%type <AST_BOOLEXP> BoolExp7

%type <AST_BOOLEXP> BoolExp8

%type <AST_TYPEFUNC> TypeFunc

%type <AST_TYPEINFO> TypeInfo

%type <AST_EXP> Exp

%type <AST_EXP> Exp1

%type <AST_EXP> Exp2

%type <AST_EXP> Exp3

%type <AST_EXP> Exp4

%type <AST_EXP> Exp5

%type <AST_TERMOP> TermOp

%type <AST_FACTOP> FactOp

%type <AST_SIGNOP> SignOp

%type <AST_ARGS> Args

%type <VIS_COLONLIST> ColonList

%type <VIS_COMMALIST> CommaList

-- ---------------------------------------------------------------------------

-- basic tokens & types

-- ---------------------------------------------------------------------------

%token <INTEGER> SYMB_INTEGER

%type <INTEGER> TYPE_INTEGER

90

%token <STRING> SYMB_IDENT

%type <STRING> TYPE_IDENT

%token <STRING> SYMB_STRING

%type <STRING> TYPE_STRING

%token <CHARACTER> SYMB_CHARACTER

%type <CHARACTER> TYPE_CHARACTER

-- ---------------------------------------------------------------------------

-- expect 1 shift/reduce conflict because of BoolExp8 & Exp5 rules (!)

-- ---------------------------------------------------------------------------

%expect 1

%%

-- ---------------------------------------------------------------------------

-- Contract

-- ---------------------------------------------------------------------------

-- VIS_CONTRACT. Contract ::= BoolExp ;

-- ---------------------------------------------------------------------------

Contract: ColonList { $$ := create {VIS_CONTRACT}.make ($1); ast_root := $$ }

;

-- ---------------------------------------------------------------------------

-- BoolExp

-- ---------------------------------------------------------------------------

-- VIS_EQ. BoolExp ::= BoolExp "=" BoolExp1 ;

-- VIS_NEQ. BoolExp ::= BoolExp "!=" BoolExp1 ;

--

-- VIS_IMP. BoolExp1 ::= BoolExp1 "implies" BoolExp1 ;

-- VIS_XOR. BoolExp2 ::= BoolExp2 "xor" BoolExp3 ;

-- VIS_OR. BoolExp2 ::= BoolExp2 "or" BoolExp3 ;

-- VIS_ORELSE. BoolExp2 ::= BoolExp2 "or else" BoolExp3 ;

-- VIS_AND. BoolExp3 ::= BoolExp3 "and" BoolExp4 ;

-- VIS_ANDTHEN. BoolExp3 ::= BoolExp3 "and then" BoolExp4 ;

-- VIS_NOT. BoolExp4 ::= "not" BoolExp4 ;

--

-- VIS_TRUE. BoolExp5 ::= "True" ;

-- VIS_FALSE. BoolExp5 ::= "False" ;

--

-- VIS_LT. BoolExp6 ::= Exp "<" Exp ;

-- VIS_LTe. BoolExp6 ::= Exp "<=" Exp ;

-- VIS_GT. BoolExp6 ::= Exp ">" Exp ;

-- VIS_GTe. BoolExp6 ::= Exp ">=" Exp ;

91

--

-- VIS_EXP. BoolExp7 ::= Exp ;

-- VIS_TYPEFUNC. BoolExp7 ::= TypeFunc ;

-- ---------------------------------------------------------------------------

BoolExp : BoolExp "{=}" BoolExp1 { $$ := create {VIS_EQ}.make ($1, $3) }

| BoolExp "{!=}" BoolExp1 { $$ := create {VIS_NEQ}.make ($1, $3) }

| BoolExp1 { $$ := $1 }

;

BoolExp1: BoolExp2 "implies" BoolExp1

{ $$ := create {VIS_IMP}.make ($1, $3) }

| BoolExp2 { $$ := $1 }

;

BoolExp2: BoolExp2 "xor" BoolExp3

{ $$ := create {VIS_XOR}.make ($1, $3) }

| BoolExp2 "or" BoolExp3

{ $$ := create {VIS_OR}.make ($1, $3) }

| BoolExp2 "or else" BoolExp3

{ $$ := create {VIS_ORELSE}.make ($1, $3) }

| BoolExp3 { $$ := $1 }

;

BoolExp3: BoolExp3 "and" BoolExp4

{ $$ := create {VIS_AND}.make ($1, $3) }

| BoolExp3 "and then" BoolExp4

{ $$ := create {VIS_ANDTHEN}.make ($1, $3) }

| BoolExp4 { $$ := $1 }

;

BoolExp4: "not" BoolExp4 { $$ := create {VIS_NOT}.make ($2) }

| BoolExp5 { $$ := $1 }

;

BoolExp5: "True" { $$ := create {VIS_TRUE}.make }

| "False" { $$ := create {VIS_FALSE}.make }

| BoolExp6 { $$ := $1 }

;

BoolExp6: Exp "{<}" Exp { $$ := create {VIS_LT}.make ($1, $3) }

| Exp "{<=}" Exp { $$ := create {VIS_LTe}.make ($1, $3) }

| Exp "{>}" Exp { $$ := create {VIS_GT}.make ($1, $3) }

| Exp "{>=}" Exp { $$ := create {VIS_GTe}.make ($1, $3) }

| BoolExp7 { $$ := $1 }

92

;

BoolExp7: Exp { $$ := create {VIS_EXP}.make ($1) }

| TypeFunc { $$ := create {VIS_TYPEFUNC}.make ($1) }

| BoolExp8 { $$ := $1 }

;

BoolExp8: "{(}" BoolExp "{)}" { $$ := $2 } -- 1 s/r conflict (!)

;

-- ---------------------------------------------------------------------------

-- TypeFunc:

-- ---------------------------------------------------------------------------

-- VIS_CONFORMS. TypeFunc ::= TypeInfo "." "conforms_to" "(" TypeInfo ")";

-- VIS_IS_EQ. TypeFunc ::= TypeInfo "." "is_equal" "(" TypeInfo ")" ;

-- VIS_IS_NEQ. TypeFunc ::= TypeInfo "." "is_not_equal" "(" TypeInfo ")" ;

-- ---------------------------------------------------------------------------

TypeFunc: TypeInfo "{.}" "conforms_to" "{(}" TypeInfo "{)}"

{ $$ := create {VIS_CONFORMS}.make ($1, $5) }

| TypeInfo "{.}" "is_equal" "{(}" TypeInfo "{)}"

{ $$ := create {VIS_IS_EQ}.make ($1, $5) }

| TypeInfo "{.}" "is_not_equal" "{(}" TypeInfo "{)}"

{ $$ := create {VIS_IS_NEQ}.make ($1, $5) }

;

-- ---------------------------------------------------------------------------

-- TypeInfo

-- ---------------------------------------------------------------------------

-- VIS_TTYPE. TypeInfo ::= "{" Ident "}" "." "type" ;

-- VIS_ETYPE. TypeInfo ::= Exp3 "." "type" ;

-- ---------------------------------------------------------------------------

TypeInfo: "{{}" TYPE_IDENT "{}}" "{.}" "type"

{ $$ := create {VIS_TTYPE}.make ($2) }

| Exp3 "{.}" "type"

{ $$ := create {VIS_ETYPE}.make ($1) }

;

-- ---------------------------------------------------------------------------

-- Exp

-- ---------------------------------------------------------------------------

-- VIS_TERM. Exp ::= Exp1 TermOp Exp1 ;

-- VIS_FACT. Exp1 ::= Exp2 FactOp Exp2 ;

-- VIS_SIGN. Exp2 ::= SignOp Exp2 ;

93

--

-- VIS_VOID. Exp3 ::= "Void" ;

-- VIS_INT. Exp3 ::= Integer ; -- TYPE_INTEGER ;

-- VIS_ID. Exp3 ::= Ident ; -- TYPE_IDENT ;

-- VIS_CHARACTER Exp3 ::= Char ; -- TYPE_CHARACTER ;

-- VIS_STRING Exp3 ::= String ; -- TYPE_STRING ;

--

-- VIS_OLD. Exp4 ::= "old" Ident ;

-- VIS_CREATE. Exp4 ::= "create" "{" Ident "}" "." Ident Args ;

-- VIS_CALL. Exp4 ::= Exp3 "." Ident Args ;

-- ---------------------------------------------------------------------------

Exp : Exp TermOp Exp1 { $$ := create {VIS_TERM}.make ($1, $2, $3) }

| Exp1 { $$ := $1 }

;

Exp1: Exp1 FactOp Exp2 { $$ := create {VIS_FACT}.make ($1, $2, $3) }

| Exp2 { $$ := $1 }

;

Exp2: SignOp Exp2 { $$ := create {VIS_SIGN}.make ($1, $2) }

| Exp3 { $$ := $1 }

;

Exp3: "Void" { $$ := create {VIS_VOID}.make }

| TYPE_INTEGER { $$ := create {VIS_INTEGER}.make ($1) }

| TYPE_IDENT { $$ := create {VIS_ID}.make ($1) }

| TYPE_CHARACTER { $$ := create {VIS_CHARACTER}.make ($1) }

| TYPE_STRING { $$ := create {VIS_STRING}.make ($1) }

| Exp4 { $$ := $1 }

;

Exp4: "old" TYPE_IDENT { $$ := create {VIS_OLD}.make ($2) }

| "create" "{{}" TYPE_IDENT "{}}" "{.}" TYPE_IDENT Args

{ $$ := create {VIS_CREATE}.make ($3, $6, $7) }

| Exp3 "{.}" TYPE_IDENT Args

{ $$ := create {VIS_CALL}.make ($1, $3, $4) }

| Exp5 { $$ := $1 }

;

Exp5: "{(}" Exp "{)}" { $$ := $2 } -- 1 s/r conflict (!)

;

-- ---------------------------------------------------------------------------

-- TermOp

94

-- ---------------------------------------------------------------------------

-- VIS_ADD. TermOp ::= "+" ;

-- VIS_SUB. TermOp ::= "-" ;

-- ---------------------------------------------------------------------------

TermOp: "{+}" { $$ := create {VIS_ADD}.make }

| "{-}" { $$ := create {VIS_SUB}.make }

;

-- ---------------------------------------------------------------------------

-- FactOp

-- ---------------------------------------------------------------------------

-- VIS_MUL. FactOp ::= "*" ;

-- VIS_DIV. FactOp ::= "/" ;

-- ---------------------------------------------------------------------------

FactOp: "{*}" { $$ := create {VIS_MUL}.make }

| "{/}" { $$ := create {VIS_DIV}.make }

;

-- ---------------------------------------------------------------------------

-- SignOp

-- ---------------------------------------------------------------------------

-- VIS_POS. SignOp ::= "+" ;

-- VIS_NEG. SignOp ::= "-" ;

-- ---------------------------------------------------------------------------

SignOp: "{+}" { $$ := create {VIS_POS}.make }

| "{-}" { $$ := create {VIS_NEG}.make }

;

-- ---------------------------------------------------------------------------

-- Args

-- ---------------------------------------------------------------------------

-- VIS_NIL. Args ::= ;

-- VIS_ARGs. Args ::= "(" [BoolListExp] ")" ;

-- ---------------------------------------------------------------------------

Args: { $$ := create {VIS_NIL }.make }

| "{(}" CommaList "{)}" { $$ := create {VIS_ARGs}.make ($2) }

;

-- ---------------------------------------------------------------------------

-- CommaList

-- ---------------------------------------------------------------------------

95

-- _. CommaList ::= ;

-- _. CommaList ::= BoolExp ;

-- _. CommaList ::= BoolExp "," CommaList ;

-- ---------------------------------------------------------------------------

CommaList:

{ $$ := Void }

| BoolExp

{ $$ := create {VIS_COMMALIST}.make_tail ($1) }

| BoolExp "{,}" CommaList

{ $$ := create {VIS_COMMALIST}.make_node ($1, $3) }

;

-- ---------------------------------------------------------------------------

-- ColonList

-- ---------------------------------------------------------------------------

-- _. ColonList ::= ;

-- _. ColonList ::= BoolExp ;

-- _. ColonList ::= BoolExp ";" ColonList ;

-- ---------------------------------------------------------------------------

ColonList: { $$ := Void }

| BoolExp { $$ := create {VIS_COLONLIST}.make_tail ($1) }

| BoolExp "{;}" ColonList

{ $$ := create {VIS_COLONLIST}.make_node ($1, $3) }

;

-- ---------------------------------------------------------------------------

-- Basic Types

-- ---------------------------------------------------------------------------

TYPE_INTEGER : SYMB_INTEGER { $$ := text.to_integer }

;

TYPE_IDENT : SYMB_IDENT { $$ := text }

;

TYPE_CHARACTER : SYMB_CHARACTER { $$ := text_item (2) }

;

TYPE_STRING : SYMB_STRING { $$ := text.substring (2,text.count-1) }

;

%%

-- ---------------------------------------------------------------------------

-- ECL_PARSER.make

-- ---------------------------------------------------------------------------

96

make (a_string: STRING) is

require

a_string /= Void

local

a_buffer: YY_BUFFER

do

make_parser

make_scanner

a_buffer := new_string_buffer (a_string)

set_input_buffer (a_buffer)

parse

ensure

syntax_error /= True

ast_root /= Void

end

-- ---------------------------------------------------------------------------

-- ECL_PARSER.execute

-- ---------------------------------------------------------------------------

execute is

do

make_parser

make_scanner

parse

ensure

syntax_error /= True

ast_root /= Void

end

-- ---------------------------------------------------------------------------

-- ECL_PARSER.report_error

-- ---------------------------------------------------------------------------

report_error (a_message: STRING) is

do

-- ignore default behavior

end

end

97

To generate the Eiffel-Contract Language parser the following command is necessary:

geyacc -t ecl_tokens ecl_parser.gey > ecl_parser.e

The description file ecl_parser.gey is interpreted and since by default the generator outputs the

parser's source to the standard output, the latter is redirected to the ecl_parser.e file. The tool

generates a further file ecl_tokens.e which contains definitions of constants (to represent tokens)

that are required by the scanner. To generate the scanner the following command is required:

gelex ecl_scanner.gel > ecl_scanner.e

Since the scanner generator outputs like the generator for the parser the source code by default to the

standard output, the latter is redirected to the ecl_scanner.e file. The generator is able to produce

the scanner by interpreting the ecl_scanner.gel description.

98