Previous Up Next

Program generation module
(genera)

Introduction

This module contains the procedure that generates the PASCAL code for the attribute evaluator.

1 Generation of the program

The procedure gen program generates the entire program, based on the grammar read by the first pass, which is stored in types described in [bintree]. The parameters partition, part dir, one pass set and nr of passes describe the Simple Multi Pass solution that must be used. See also [allsmp].

Before any part of the program is generated two initializations are done. Firstly, the procedure make tree list is called, which constructs an alternative representation [bintree 1.7]. This alternative representation is used by the procedures that generated the tree walking procedures [gass 2].

Secondly, the one pass attribute relations are constructed. The global variable one pass attr [onepassrel 1] is initialized by calling the procedure make attr at elem for [allsmp 2.2], and the other relations derived from this relation are constructed by calling the procedure makeone pass relations [onepassrel 2].

After the generation file has been opened (by calling open genfl [genpro 1]), the header of the program is generated. Next, the global variables and the external procedure declarations are generated by calling the local procedures gen global variables and gen extern procedures, respectively.

In each pass, where the number of passes is determined by the parameter nr of passes, an evaluation procedure is generated, representing the attribute evaluations of that pass. To do this the following actions are taken. Firstly, the set of attributes that have to be evaluated is calculated by calling the procedure make attr at elem for [allsmp 2.2], and using the parameter partition. Secondly, the procedures update eval attr and maketo eval and at all [gass 1.2] are called, which constructs a number of attribute relations, which are used while generating the tree walking procedures. The procedure update eval attr is only called for the first pass. Thirdly, the procedure gen this pass is called, with the indication of the evaluation direction of the pass, taken from the parameter part dir. This procedure is described in the following section.

Finally the body of the program is generated, which consists of calls to the procedures that implement each pass.

2 Generation of an evaluation procedure for a pass

The procedure gen this pass generates the PASCAL code for the pass with the number equal to the first parameter nr, and an evaluation direction determined by the second parameter L direction. A left-to-right evaluation strategy is used when L direction is TRUE, otherwise a right-to-left evaluation strategy is used.

The generated pass evaluation procedure consists of three parts: the header, the declaration of local procedures, and the body. The local procedures that are generated represent the tree walking and attribute evaluating procedures. For each left side element such a tree walking procedure is generated. The body of the generated pass evaluation procedure consists of a call to the appropriate local tree walking procedure for the root element.

For each left hand side element a tree walking and attribute evaluating procedure must be generated. The generated tree walking procedures call each other recursively. Because of this, the forward declarations of the headers of the tree walking procedures are first generated. This is done by calling the procedure gen header [gass 2.1]. Next, the bodies of the tree walking procedures are generated by calling the procedure gen eval procedure [gass 2.2].

3 Interface

This module uses declarations from the following modules:

3.1 Exported procedure

The following procedure is exported by this module:

4 The listing

[ENVIRONMENT ('generator.pen'),
 INHERIT ('[-.screen]screen.pen',
          'perform.pen',
          'definitions.pen',
          'bintree.pen',
          'allsmp.pen',
          'genpro.pen',
          'onepassrel.pen',
          'gtypes.pen',
          'gass.pen')]

MODULE generator;

CONST
  apsgn_file      = '-inputfile :';
  apsgn_question  = '-question  : ';
  yes_or_no       = '(y/n)';
  question_length = 20;

TYPE
  query         = VARYING[question_length] OF char;

4.1 Generate global variables and external procedures

  PROCEDURE gen_global_variables;
  (* This procedure generates the global variables for the evaluator.         *)
  (* Those are: s_root.                                                       *)
  BEGIN
    enter_level('gen_global_variables');
    gen_newline(1);
    gen_keyw(k_var);
    gen_newline(1);
    gen_('s_root     : t_' + m_elem(root^.elem_nr) + ';');
    gen_keyw(k_s_end);
    exit_level
  END;

  PROCEDURE gen_extern_procedures;
  (* This procedure generates the extern procedure declarations, using the    *)
  (* information stored on the function save file, generated by PARSER.       *)
  (* The functions in the semantic rules are implemented as procedures, with  *)
  (* one VAR result argument, followed by n arg<i> arguments. The semantic    *)
  (* function declaration :                                                   *)
  (*                                                                          *)
  (*  <func_name>(<arg_type1>, .. ,<arg_typen>) : <result_type>.              *)
  (*                                                                          *)
  (*  PROCEDURE <func_name>(VAR result : <result_type>;                       *)
  (*            arg_A : <arg_type1>; .. arg_x : <arg_typen>); EXTERN;         *)
  (*                                                                          *)

    PROCEDURE seek_functions(root : pnode);
    (* seeks for the function definitions in the binary identifier tree and   *)
    (* generates the external procedures that belong to it.                   *)

      PROCEDURE gen_a_extern_procedure(func : pnode);
      (* This procedure generates one external procedure.                     *)
      VAR
        result_type : alfa;
        hold        : integer;

        PROCEDURE gen_arguments(args : pltype);
        (* This procedure generates the arguments declaration.                *)
        VAR
          ch : char;
        BEGIN
          ch := 'A';
          WHILE args <> NIL
          DO BEGIN
               gen_('; arg_' + ch + ' : ' + args^.first.type_name^.name);
               ch := succ(ch);
               args := args^.rest
             END
        END;

      BEGIN (* of gen_a_extern_procedures *)
        enter_level('gen_a_extern_procedure');
        WITH func^
        DO BEGIN
             gen_newline(1);
             gen_keyw(k_procedure);
             gen_(name + '(');
             set_left_margin(hold);
             gen_keyw(k_varref);
             gen_('result : ' + type_of_func.type_name^.name);
               gen_arguments(args);
             gen_('); EXTERN;');
             reset_left_margin(hold)
           END;
        exit_level
      END;

    BEGIN (* of seek_functions *)
      IF   root <> NIL
      THEN WITH root^
           DO BEGIN
                seek_functions(left);
                add_level_info(name);
                IF   kind = n_func
                THEN gen_a_extern_procedure(root);
                seek_functions(right)
              END
    END;

  BEGIN (* of gen_extern_procedures *)
    enter_level('seek_functions');
    seek_functions(nametree);
    exit_level
  END;

4.2 Generate program

  PROCEDURE gen_program(partition : tpartition;
                        part_dir : tpart_dir;
                        one_pass_set : tset_node;
                        nr_of_passes : integer);

  (* This procedure generates the whole program, making use of the            *)
  (* information stored in the bintree and the variables element and          *)
  (* attribute, which are global for this procedure and the procedures that   *)
  (* are called. The generation process is controlled by the input parameters *)
  (* that contain the SMP-solution.                                           *)
  (*   nr_of_passes  = the number of passes of the solution.                  *)
  (*   partition     = for each pass the attribute-element pairs that have to *)
  (*                       evaluated in the pass.                             *)
  (*   part_dir      = for each pass the evaluation direction of the pass.    *)
  (*   one_pass_attr = the attribute-element pairs that have the one-pass     *)
  (*                   property.                                              *)
  (* remark: both partition and one_pass_set make use of the internal graph   *)
  (*         representation used in the SMP-algorithm. For conversion we make *)
  (*         use of the procedure make_attr_at_elem_for, as explained further *)
  (*             on.                                                          *)
  (*                                                                          *)
  (* BEGIN                                                                    *)
  (*   make_tree_lists;                                                       *)
  (*   gen_program_header;                                                    *)
  (*   gen_types;                                                             *)
  (*   gen_global_variables;                                                  *)
  (*   gen_extern_procedures;                                                 *)
  (*                                                                          *)
  (*   make_attr_at_elem_for( one_pass_set, one_pass_attr );                  *)
  (*   makeone_pass_relations;                                                *)
  (*                                                                          *)
  (*   FOR pass_nr := 1 TO nr_of_passes                                       *)
  (*   DO BEGIN                                                               *)
  (*            make_attr_at_elem_for( partition[pass_nr], eval_attr );       *)
  (*            make_to_eval_and_at_all;                                      *)
  (*        gen_this_pass( pass_nr, part_dir[pass_nr] = left )                *)
  (*      END;                                                                *)
  (*                                                                          *)
  (*   gen_body_of_program                                                    *)
  (* END                                                                      *)
  (*                                                                          *)
  (* Global variables used :                                                  *)
  (*   one_pass_attr : tattr_at_elem = for each element the attributes at the *)
  (*                   element that have the one-pass property.               *)
  (*   eval_attr     : tattr_at_elem = for each element the attributes at the *)
  (*                   element to be evaluated in the current pass.           *)
  VAR
    pass_nr : integer;

    PROCEDURE make_tree_lists;
    (* This procedure fills the tree_list and nr_of_parts fields of all       *)
    (* left side elements. The tree_list field contains an alternative        *)
    (* representation of the information already stored in the trans_ass and  *)
    (* tree_rule fields of the element. The nr_of_parts field will contain    *)
    (* the number of part names in the tree rule. These fields are used in the*)
    (* generation procedures.                                                 *)
    (*                                                                        *)
    (* functionality of the tree_list and nr_of_parts :                       *)
    (*                                                                        *)
    (*    ALL left_side_elem_nr IN left_side_elements                         *)
    (*      WITH element[left_side_elem_nr]^                                  *)
    (*        ALL 0 <= nr <= nr_of_parts                                      *)
    (*          WITH tree_list^[nr]^                                          *)
    (*            partname  = name of part nr, whereby name of part 0 = "#"   *)
    (*            element   = element of part nr                              *)
    (*            trans_ass = transformed attribute assignments of part nr    *)
    VAR
      left_elem_nr : integer;
      htree_rule   : ptree_rule;
      msg : VARYING[60] OF char;
    BEGIN
      enter_level('make_tree_lists');
      FOR left_elem_nr := 0 TO nr_of_elem
      DO IF   left_elem_nr IN left_side_elements
         THEN WITH element[left_elem_nr]^
              DO BEGIN
                 add_level_info('elem : ' + name);
                 (* add element for part 0 at tree_rule in htree_rule : *)
                   new(htree_rule);
                   htree_rule^.partname  := main_part_name;
                   htree_rule^.element   := element[left_elem_nr];
                   htree_rule^.trans_ass := trans_ass;
                   htree_rule^.rest         := tree_rule;
                   new(tree_list);
                 (* fill tree_list with content of htree_rule : *)
                   nr_of_parts := start_nr;
                   WHILE htree_rule <> NIL
                   DO BEGIN
                        nr_of_parts := nr_of_parts + 1;
                        tree_list^[nr_of_parts] := htree_rule;
                        htree_rule := htree_rule^.rest
                      END
                 END;
      exit_level
    END;

    PROCEDURE gen_this_pass(nr : integer; L_direction : boolean);
    (* This procedure generates the evaluation procedures for the nr-th pass   *)
    (* in a left-to-right pass if L_direction is TRUE, otherwise in a right-   *)
    (* to-left pass.                                                           *)
    (*                                                                         *)
    (* BEGIN                                                                   *)
    (*   gen_main_procedure_heading_for_this_pass;                             *)
    (*                                                                         *)
    (*   FOR left_elem_nr := 0 TO nr_of_elems                                  *)
    (*   DO IF left_elem_nr IN left_side_elements                              *)
    (*      THEN BEGIN                                                         *)
    (*             gen_header(left_elem_nr, TRUE);                             *)
    (*             gen_('FORWARD;')                                            *)
    (*           END;                                                          *)
    (*                                                                         *)
    (*   FOR left_elem_nr := 0 TO nr_of_elems                                  *)
    (*   DO IF left_elem_nr IN left_side_elements                              *)
    (*      THEN gen_eval_procedure(left_elem_nr, L_direction);                *)
    (*                                                                         *)
    (*   gen_body_main_evaluation_procedure_for_this_pass                      *)
    (* END;                                                                    *)
    (*                                                                         *)
    VAR
      left_elem_nr : integer;
      msg : text_info;
    BEGIN
      enter_level('gen_this_pass');
      writev(msg, 'pass nr : ', nr:2);
      add_level_info(msg);

    (* Generate procedure heading for this pass *)
      gen_newline(1);
      gen_keyw(k_procedure);
      write(genfl, 'e_valpass', nr:1, ';');
      shift_margin(2);
      gen_newline(1);

    (* Generate forward declarations for all procedures *)
      FOR left_elem_nr := 0 TO nr_of_elem
      DO IF   left_elem_nr IN left_side_elements
         THEN BEGIN
                gen_newline(1);
                gen_header(left_elem_nr, TRUE);
                gen_keyw(k_forward);
                gen_(';')
              END;

    (* Generate procedure bodies *)
      FOR left_elem_nr := 0 TO nr_of_elem
      DO IF   left_elem_nr IN left_side_elements
         THEN gen_eval_procedure(left_elem_nr,L_direction);
      shift_margin(-2);
      gen_newline(2);

    (* Generate body of evaluation procedure *)
      gen_keyw(k_begin);
      write(genfl, '(* of e_valpass', nr:1 ,' *)');
      gen_newline(1);
      gen_('e_' + m_elem(root^.elem_nr) + '(s_root)');
      gen_keyw(k_end);
      gen_(';');
      exit_level
    END;

  BEGIN (* of gen_program *)
      perf_start_time(perf_g_decl);

    make_tree_lists;

  (* make one pass relations *)
    make_attr_at_elem_for(one_pass_set, one_pass_attr);
    makeone_pass_relations;

  (* generate program header and types *)
    open_genfl;
    gen_keyw(k_program);
    gen_('evaluator;');
    gen_newline(3);
    gentypes;

  (* generate extern procedures *)
    gen_newline(2);
    gen_global_variables;
    gen_newline(2);
    shift_margin(2);
    gen_extern_procedures;
    gen_page;
    {gen_readtree;}
      perf_end_time  (perf_g_decl);


  (* generate evaluation procedures for the passes *)
      perf_start_time(perf_g_prgr);
    FOR pass_nr := 1 TO nr_of_passes
    DO BEGIN
         gen_page;
         make_attr_at_elem_for(partition[pass_nr], eval_attr);
         IF   pass_nr = 1
         THEN update_eval_attr;
         maketo_eval_and_at_all;
         gen_this_pass(pass_nr, part_dir[pass_nr] = left)
       END;
    shift_margin(-2);
    gen_page;

  (* generate body of program *)
    gen_keyw(k_begin);
    gen_newline(1);
    {gen_('r_eadtree;');}
    FOR pass_nr := 1 TO nr_of_passes
    DO BEGIN
         gen_newline(1);
         gen_('e_valpass' + chr(pass_nr MOD 10 + ord('0')));
       END;
    gen_keyw(k_end);
    gen_('.');
      perf_end_time  (perf_g_prgr)
  END;

END.


My life as a hacker | My home page