• No se han encontrado resultados

4. EL DESPIDO COLECTIVO CAUSAS.

4.3. Comparativa europea

The main function of the generated C program is responsible for calling the matching and application functions as designated by the command sequence of the GP 2 program. The program is a set of rule applications organised with an imperative syntax. The code generator writes a short code fragment for each rule call and translates each GP 2 control construct into the equivalent C control construct. In order to preserve the meaning of the program, the code must provide graph backtracking when appropriate. In the presented code, we assume that graph backtracking is implemented via a graph change stack as discussed in section 5.8.

The runtime code is supported by a number of global variables, including the host graphs and morphisms. The main function first calls a function to populate the host graph’s data structure via the host graph parser, then it allocates mem- ory for each morphism. Morphisms are passed to the matching and application functions, and they are reset to contain dummy values after the rule is applied or after the rule fails to match. A global boolean variable success, initialised to true, is used to store the outcome of a computation to support the control flow of the program.

Rule Type Generated Code Empty apply R ( b ) ;s u c c e s s = t r u e ; Predicate i f ( matchR (m) ) s u c c e s s = t r u e ; e l s e < f a i l u r e code> Standard i f ( matchR (m) ) { applyR (m, b ) ; s u c c e s s = t r u e ; } e l s e < f a i l u r e code>

Figure 5.31.: Generated C code for rule calls

The code generator carries some information about the command it is currently processing to support generation of correct code. Some aspects of the generated code, such as failure handling, are dependent on the context of the command. For instance, failure in a loop body is treated differently from failure at the top level. Information about graph backtracking is also present, obtained from the static analysis described in subsection 5.8.1. Each rule application function takes a boolean argument that pushes changes onto the graph change stack if set to true.

Code generated from rule calls, the basic unit of a GP 2 program, is dependent on the rule’s structure. Figure 5.31 shows the code generated for each rule type. The letters m and b are used to represent the morphism and boolean arguments to rule-related functions. Empty rules do not have a matching function and their application function does not take a morphism argument. Predicate rules do not have an application function. The failure code is shown at the end of the section. Figure 5.32 summarises the translation of each GP 2 control construct to C. Command sequences and procedure calls are not shown. Command sequences are easily handled by generating the code for each command in the designated order. When a procedure call is encountered in the program text, the code generator inlines the command sequence of the procedure at the point of the call. The condition of a branching statement is generated in C’s do-while loop: if the command sequence fails before the last command, C’s break statement is called to exit the condition, where control is assumed by the then/else branch. GP 2’s loop translates directly to a C while loop. One subtlety is the looped command sequence, where the line if(!success) break; is printed after the code for all commands except the last, the same mechanism as used in rule set calls. A second subtlety is that success is set to true after a loop exits because GP 2’s semantics states that a loop cannot fail.

The non-deterministic constructs are handled in different ways. The rule set call {R1, R2} is tackled by applying the rules in textual order until one rule matches or they all fail. C’s do-while loop is used to exit the sequence after a rule application: it would be incorrect to try and match R2 if R1 succeeds. In contrast,

Command Generated Code {R1, R2} do { i f ( matchR1 (M R1 ) ) { <s u c c e s s code> break ; } i f ( matchR2 (M R2 ) ) <s u c c e s s code> e l s e < f a i l u r e code> } while ( f a l s e ) if C then P else Q i n t r e s t o r e p o i n t = <c u r r e n t GCS frame >; do C while ( f a l s e ) ; undoChanges ( h o s t , r e s t o r e p o i n t ) ; i f ( s u c c e s s ) P e l s e Q; try C then P else Q i n t r e s t o r e p o i n t = <t o p o f GCS>; do C while ( f a l s e ) ; i f ( s u c c e s s ) P e l s e { undoChanges ( h o s t , r e s t o r e p o i n t ) ; Q } (P; Q)! i n t r e s t o r e p o i n t = <t o p o f GCS>; while ( s u c c e s s ) { P i f ( ! s u c c e s s ) break ; Q i f ( s u c c e s s ) d i s c a r d C h a n g e s ( r e s t o r e p o i n t ) ; } s u c c e s s = t r u e ; P or Q i n t random = rand ( ) ; i f ( ( random % 2 ) == 0 ) <program c o d e f o r P> e l s e <program c o d e f o r Q>

Figure 5.32.: C code for GP 2 control constructs

C’s pseudo-random number generator chooses between the two subprograms of the or statement P or Q. We chose to implement rule sets in a deterministic way because in our experience, rule sets are used to elegantly model deterministic be- haviour. For example, the series-parallel recognition program loops a rule set to reduce a graph to a basic structure, behaviour that is globally deterministic de- spite the local non-determinism. Another common use case occurs in the acyclic checking program, in which a rule set is used to test two structural properties — the existence of a looping edge or a non-looping edge — in a single command. The rule set calls can be used practically even with the knowledge that the imple- mentation is deterministic. On the other hand, implementing the or statement in the same way defeats the purpose of the construct. We acknowledge that users may wish to use rule sets in a genuinely nondeterministic way (one use case could be graph generation), and we aim to implement rule sets by pseudorandom choice

Context Failure Code Top Level p r i n t f a i l u r e message ; g a r b a g e C o l l e c t ( ) ; return 0 ; Condition break ;s u c c e s s = f a l s e ; Loop s u c c e s s = f a l s e ;

<pop and undo graph c h a n g e s >

Figure 5.33.: Generated C code for failure.

in the future. Finally, we note that both approaches are sound with respect to the semantics.

Restore points are created and assigned to the top of the graph change stack before entering a critical subprogram requiring backtracking. The function un- doChanges restores a previous host graph state by popping and undoing changes from the stack until the restore point is reached. The function discardChanges pops the changes but does not undo them. It is only called at the end of a success- ful loop iteration to prevent a failure in a future loop iteration from causing the host graph to roll back beyond the start of its preceding iteration. Each restore point has a unique identifier in the code to facilitate multiple graph backtracking points.

The failure code is context-sensitive as shown in Figure 5.33. If there is a failure at the top level, the program is terminated after reporting to the user and garbage collecting. The failure message either states the name of the rule that failed to match or that the fail statement was invoked. Failure in a condition sets the success flag to false so that the subsequent code takes the else branch of the conditional statement. Failure in a loop sets the success flag to false and calls undoChanges to restore the host graph to the state it was in at the start of the most recent loop iteration.

Documento similar