Common use of SYNTAX CHECK Clause in Contracts

SYNTAX CHECK. Before the wizard compiles the generated Eiffel classes we wanted to check them for syntax errors. There were many approaches until we came up with the final solution. I will discuss them briefly. Our first idea was to use Gobo Eiffel Lint (gelint) [3]. Gelint is a tool which is able to analyze Eiffel source code and report validity errors. It uses the tools Gobo Eiffel Lex (gelex) and Gobo Eiffel Yacc (geyacc). Regrettably gelint only works on classic Eiffel, not on Eiffel for .NET. The problem is that the ISE Eiffel compiler relies on some XML files to make the correspondence between .NET and Eiffel for .NET classes; and this format is not published. The second approach was to implement an Eiffel assertion parser. I took the grammar description from the Gobo Eiffel Parse Library and swept everything out that is not needed to parse an assertion. The start condition was no more %start Class_declarations but %start Assertions. Then I used geyacc to generate an Eiffel assertion parser. Whenever the user added a new assertion (invariant, pre- or postcondition) the assembly dialog checks it using the assertion parser. A drawback of this approach is that the assertion grammar description of the yacc file has to be adapted manually when the Eiffel syntax changes so it does not come automatically with a new gobo release. However the assertion checker still exists and can be rebound in the wizard easily. I tried to come up with an algorithm that checks for validity of the assertion expression. I made a list of identifiers occurring in the assertion and checked if they match a feature name of the current class or a parameter of the feature in case of a pre- or postcondition. This approach was too restrictive: an identifier is also valid when it relies on a parent feature but then it got rejected. The next idea was to generate a second set of classes that only contains classic Eiffel code and then analyze the classes with gelint. The Contract Wizard generates wrapper classes as listed in Table 10 on the left hand side. I created a new Eiffel visitor class (CW_EIFFEL_TEST_VISITOR) based on the existing Eiffel visitor which generates Eiffel classes as listed in Table 10 in the right column. The wrapper features just have a "do end" as body and no dotnet_proxy feature. dotnet_proxy: DOTNET_A These classes are Eiffel classes. Thus, they can be parsed by gelint to determine whether assertions are valid or not. The generation of the classes worked fine but they still had references to .NET classes (argument type of procedures or functions, and return type of functions). Table 11 shows an example: IENUMERATOR relies on a type from the .NET Framework and thus gelint cannot handle it. frozen get_enumerator: IENUMERATOR is do We than had the following idea: get the list of referenced assemblies from the assembly given as input to the Contract Wizard. The referenced assemblies are needed to compile the given assembly and are listed in the “assembly” clause of the Ace file. Generate the wrapper classes corresponding to the classes in those referenced assemblies. So the work that the Contract Wizard does for only one assembly would be done to all referenced assemblies. Every wrapper class only has "do end" bodies and no dotnet_proxy attribute. To refer to the example in Table 11: among others the wizard generates a wrapper class for IENUMERATOR. Save those wrapper classes in a temporary directory. When checked with gelint whether contracts are valid we include that directory in the Ace file and remove the “assembly” clause. If not errors occured, we regenerate the new assembly with contracts as the Contract Wizard always did before (with “assembly” clause in the Ace file and no inclusion of that temporary directory). We considered incrementality for performance reasons. I started to implement this idea and noticed that the Contract Wizard does not yet handle overloaded .NET methods. In .NET languages (C#, C++) it is possible to overload a method with different signatures. ▇▇▇▇▇▇ does not support feature overloading and therefore the feature names of overloaded methods have to be adapted (see section 4.2.4). After this odyssey – where I learnt quite a few things – we came up with the final solution: test the generated classes on syntax errors and then start the Eiffel compiler to verify the source code. The class CW_GELINT, based on gelint, takes over the syntax check. The root feature reads an Ace file as input and looks through the system clusters to map the Eiffel class names with the corresponding file names. I generated the Ace file using the class CW_LACE_TEST_GENERATOR. The difference with the standard Ace file generated by the Contract Wizard is that it has no “assembly” clause and no attributes which set .NET properties. When the parsing of the Ace file produces no errors, it returns a universe (ET_UNIVERSE) which is used to parse the classes. Foremost the creation procedure sets the compiler to the ISE Eiffel compiler and redirects the error reporting to a file (error_file_out). This forces the parser to write errors in a file from where they can be retrieved later on. In the next step the features calls parse_root_class. It is very important to call error_file_out.close after the parsing otherwise the file will remain empty even if syntax errors occurred. The creation procedure of CW_GELINT receives along with a string of the directory (directory) where the Ace file and the classes to be parsed are stored, a class list (class_list) with the name of all generated Eiffel classes. The procedure parse_root_class iterates through all classes of the universe but only parses classes in the root cluster. It is not necessary to check the classes in other clusters because they are library classes (base, base_net) and have a correct syntax. When the cluster equals the root cluster the feature iterates through class_list and parses all classes in it. This means that only the generated classes are parsed.

Appears in 3 contracts

Sources: Master Thesis, Master Thesis, Master Thesis