Previous Up Next

Primitive generation module
(genpro)

Introduction

This module provides a number of procedures that are used to generate the actual PASCAL program code. Roughly speaking, each PASCAL program consists of keywords, symbols, identifiers and comments. This module provides means to generate these quantities in a simple and efficient way. It ensures that keywords and identifiers are correctly generated. The module provides a simple layout mechanism, which consists of a variable left margin that is updated when keywords are generated. Because the keyword generation procedure influences the layout, it is possible to change the layout without the need for rewriting all the generation modules that make use of this module.

The module also provides an abstraction mechanism for the representation of different kinds of identifiers used in the generated program. This abstraction mechanism makes it possible to easily change the representation of these identifiers, without the need for rewriting all the generation modules.

One could go much further with this abstraction, but this would require a completely different and more complicated approach. For example, one could first build an abstract program tree, and pass the whole tree to a procedure that generates a complete PASCAL program. This procedure determines how the abstract program tree is represented as a correct PASCAL program, including layout and comments. This more complicated approach eliminates some of the administration and extra actions which are now done by the program modules which call the primitive generation module.

1 Controlling program generation

To begin the program generation process, the procedure open genfl is called. This procedure opens an output file with the same name as the input file, but with extension ".PAS". If this is not possible, then the program is halted. Otherwise, a number of variables controlling program generation are initialized. The header of this procedure looks like:

The output file is automatically closed when the program terminates.

2 Automatic layout and keywords generation

The layout of a program is determined by the position of line breaks, and the indentation levels used for each line. The spacing between keywords, symbols and identifiers also plays a role. This module provides procedures to generate line breaks and change the indentation level. Keywords generally influence indentation levels.

Indentation levels are implemented by a left margin. Each time a line break is generated the left margin is used to generate a number of spaces equal to the current left margin setting. Thus, a change of the left margin only becomes effective when a line break is generated.

We naturally think of line breaks as line terminators. To use the layout mechanism presented above, we need to employ the opposite view, that is, line breaks determine the beginning of a line. Hence, line breaks must be generated just before text is generated on a new line.

2.1 The left margin

Program generation is done between the left margin and the fixed right margin. There is one exception. If one of the program generation routines gets an argument that does not fit between the margins, then the argument will be generated right justified to the right margin. Initially, the left margin is zero. The value of the left margin is stored in the local variable left margin.

There are two ways in which the left margin can be changed. Firstly, the left margin can be automatically changed by the procedure that generates keywords. Secondly, the left margin can be changed manually. Both these methods are now described.

2.2 Generating keywords

The keywords generation procedure has the following heading:

The parameter keyw indicates the keywords to be generated. The type tkeyword is the enumeration type of all PASCAL keywords to be generated. This type is defined in the following way:

The values represent the PASCAL keywords with the equivalent names, except the values k s end, k varref and k od. The value k s end is used in combination with the values k const, k type and k var, and indicate the end of a constant, a type or a variable declaration part (respectively). When the keywords generation module is called with this value only the left margin is updated. The value k varref should be used for generating the "VAR"-keywords in the parameter list of a procedure or a function. The value k var is used as the beginning of a variable declaration part. The value k od is used in combination with the value k do, and indicates the end of the statement(s) that began with "DO". When the keywords generation procedure is called with this value only the left margin is updated.

2.3 Manually changing the left margin

The left margin can be moved to the right or to the left using the following procedure:

The parameter number determines the number of positions the left margin is moved to the left. A negative value moves the left margin to the right.

Sometimes one wants to move the left margin a distance depending on the length of a certain text rather than a fixed number of positions. For this purpose there are two procedures that allow the left margin to be changed temporarily to the current program generation position in the output file. These procedures have the following heading:

These two procedures must be successively called with the same integer variable, which is used to save the current left margin position. When the procedure set left margin is called after some text is generated, everything (as far as possible) is generated to the left of what was generated before, until the following call to the procedure reset left margin. After this call, program generation proceeds from the previous left margin position.

3 Page and line control

There are two procedures that can be used to control the generation of pages and line breaks. The procedure gen page skips to the next page. On the first line of the new page a number of spaces equal to the left margin is generated. The header of the procedure is:

The procedure gen newline generates a number of newlines equal to the value of its parameter. If this parameter is absent, a single newline is generated. After the last newline a number of spaces equal to the left margin is generated. The header of the procedure is:

The procedure gen tab can be used for moving to a tab position relative to the current left margin. It generates space characters until the tab position is reached. No spaces are generated when the current generation position in the line is beyond the tab position, or when the tab position is beyond the right margin. The header of the procedure is:

4 Program generation procedures

The procedures that generate symbols, keywords and comments generate their arguments on the output file as an adjacent row of characters. If the argument does not fit on the current line, it will be generated on the next line. The left margin is adjusted temporarily if the row does not fit between the margins, and this causes the row to be generated right adjusted to the right margin.

4.1 Generating plain text

VMS PASCAL has the capability of passing arguments of arbitrary length to procedures. The following two procedures take arguments of this kind. There is a restriction on the maximum length of the argument, which is the same as on the maximum length of the records in a text file, which in this case, is equal to 132. Arguments longer than 132 characters will result in a run time error.

The procedure gen simply echoes its argument on the output file. The header of this procedure is:

The procedure gen fixed generates a row of characters who's length is equal to the value of the second parameter size. If the string represented by the first parameter info is longer than the specified length, then only the initial characters are generated. If the length is shorter, spaces are added to the right. This procedure is useful when one wants to generate a table. The header of the procedure is:

4.2 Generating special identifiers

In this subsection procedures are presented that generate identifiers that are based on names of elements and attributes, and identifiers for local variables. Some of these procedures generate identifiers directly onto the output file, while others return their result. Most of these procedures are rather trivial in their implementation, but serve as an abstraction mechanism.

The function m elem returns the representation of the element which has the number equal to its argument. The header of this function is:

The function m attr returns the representation of the attribute which has the number equal to its argument. The header of this function is:

The following procedure is used to generate different identifiers of different kinds based on element and attribute names:

The first argument ch determines the kind of the identifier, and the second argument name determines the representation of the specific identifier of this kind.

The function m 1p attr returns the representation of an identifier that is determined by an attribute and a part number. The header of this function is:

The first parameter part nr represents the part number and the second parameter attr nr is the attribute number of the desired attribute.

The procedure gen 1p attr directly generates the representation returned by the previous function on the output file. The header of this procedure is:

The first parameter part nr represents the part number and the second parameter attr name is the name of the desired attribute.

The function m local returns the representation of the local variable with the number equal to its argument. The header of the function is:

5 Interface

This module uses declarations from the following modules:

5.1 Exported constants and types

The following constants and types are exported by this module:

The type tkeyword is described in subsection 2.2

5.2 Exported variable

The following variable is exported by this module:

5.3 Exported procedures and functions

The following procedures and functions are exported by this module:

This procedure opens the generation file, and initializes the variables controlling the generation process. See section 1

This procedure shifts the left margin number positions to the right. See subsection 2.3

This procedure sets the left margin on the current pos, and saves the old left margin in the reference parameter hold. See subsection 2.3

This procedure resets the left margin to the position held in the parameter hold. See subsection 2.3

This procedure generates nr newlines and sets the right margin. See section 3

This procedure generates a new page, followed by a newline. See section 3

This procedure prints the contents of the parameter info. See section 3

This procedure generates the contents of the first parameter name in a fixed number of positions determined by the second parameter size. See subsection 4.1

This procedure generates a tab to position tab pos relative of the left_margin, but does not go over the end of the line. See subsection 4.1

This function returns the alphabetic representation of the element with the number nr. See subsection 4.2

This function returns the alphabetic representation of the attribute with the number nr. See subsection 4.2

This procedure generates the identifier formed by '<ch>_<name>'. See subsection 4.2

This function returns the representation of the local attribute represented by the second parameter attr nr, for the part represented by the first parameter part nr. See subsection 4.2

This procedure generates the representation of the local attribute represented by the second parameter attr nr, for the part represented by the first parameter part nr. See subsection 4.2

This procedure returns the representation of the local variable with the number equal to the value of the parameter nr. See subsection 4.2

This procedure generates the PASCAL representation of the keyword represented by the parameter keyw, and adjusts the layout. See subsection 2.2

6 The listing

[ENVIRONMENT ('genpro.pen'),
 INHERIT     ('[-.screen]openfiles.pen',
              '[-.screen]screen.pen',
              'definitions.pen',
              'bintree.pen')]

MODULE genprocedures;

(*  This module contains procedures that generate the evaluator in pascal.    *)


(*      DECLARATIONS                                                          *)

CONST
  max_output_line   =  130;
  max_outp_pl_1     =  131;
  beta_length       =  40;

TYPE
  beta          = VARYING[beta_length] OF char;

(* Begin of the declaration for the elementary generation procedures         *)
  tkeyword      = (k_program, k_const, k_type, k_record, k_var, k_s_end
                  ,k_procedure, k_function, k_varref, k_forward
                  ,k_begin, k_end, k_case, k_of
                  ,k_with, k_do, k_od
                  );


(*      VARIABLES                                                             *)

VAR
(* local for this program                                                     *)
  pos                ,  (* the current position in a line of the gen file     *)
  leftmargin            (* the current left margin in the gen file            *)
                     :[HIDDEN]  integer;
  buffer             :[HIDDEN] VARYING[max_output_line] OF char;

(* from this program, shared with GENERA , GTYPES and GASS                    *)
  genfl              :  TEXT; (* gen file pointer                             *)

6.1 Procedures

  PROCEDURE open_genfl;
  BEGIN
    IF NOT open_output_file(genfl,new_filename('PAS')) THEN halt;
    pos             := 0;
    leftmargin      := 0;
  END;

  PROCEDURE shift_margin(number: integer);
  (* This procedure shifts the margin to the right.                           *)
  BEGIN
    leftmargin := (leftmargin + number) MOD max_outp_pl_1
  END;

  PROCEDURE set_left_margin(VAR hold : integer);
  (* This procedure sets the left margin on the current pos, and saves the    *)
  (* old left margin in hold.                                                 *)
  BEGIN
    hold := leftmargin;
    leftmargin := pos
  END;

  PROCEDURE reset_left_margin(hold : integer);
  (* This procedure resets the left margin to the position held in hold.      *)
  BEGIN
    leftmargin := hold
  END;

  [HIDDEN] PROCEDURE replace_left_margin (VAR save: integer; new_pos: integer);
  (* This procedures replaces the current value of the right margin by new    *)
  (* pos. Before doing so, the old value is saved.                            *)
  BEGIN
    save       := leftmargin;
    leftmargin := new_pos
  END;

  PROCEDURE gen_newline (nr:integer := 1);
  (* This procedure generates nr newlines and sets the right margin.          *)
  VAR
    i : integer;
  BEGIN
    FOR i := 1 TO nr
    DO BEGIN
         writeln(genfl);
         IF   with_gen_echo
         THEN writeln_screen
       END;
    writev(buffer, ' ': leftmargin);
    write(genfl, buffer);
    IF   with_gen_echo
    THEN write_screen(buffer);
    pos := leftmargin
  END;

  PROCEDURE gen_page;
  (* This procedure generates a new page, followed by a newline.             *)
  BEGIN
    page(genfl);
    IF   with_gen_echo
    THEN BEGIN
           writeln_screen;
           writeln_screen('<FF>')
         END;
    gen_newline
  END;

  [HIDDEN] PROCEDURE checkpos (number: integer);
  (* This procedure checks the current position on the output line. If, added *)
  (* with number, it is too large, a newline is generated.                    *)
  VAR save_pos : integer;
  BEGIN
    IF pos + number > max_output_line
    THEN IF leftmargin + number > max_output_line
         THEN BEGIN
                replace_left_margin (save_pos, alfaleng);
                gen_newline;
                leftmargin := save_pos
              END
         ELSE gen_newline(1);
    pos := pos + number
  END;

  PROCEDURE gen_(info: VARYING[s] OF char);
  (* This procedure prints info.                                              *)
  VAR
    size : integer;
  BEGIN
    size := info.length;
    checkpos (size);
    write(genfl, info);
    IF   with_gen_echo
    THEN write_screen(info)
  END;

  PROCEDURE gen_fixed(name : VARYING[s] OF char; size : integer);
  (* This procedure generates the name in a fixed size.                       *)
  BEGIN
    checkpos(size);
    IF   size > name.length
    THEN writev(buffer, name, ' ':(size-name.length))
    ELSE writev(buffer, name : size);
    write(genfl, buffer);
    IF   with_gen_echo
    THEN write_screen(buffer)
  END;

  PROCEDURE gen_tab(tab_pos : integer);
  (* this procedure generates a tab to position tab_pos relative of           *)
  (* the left_margin, but does not go over the end of the line.               *)
  BEGIN
    tab_pos := tab_pos + leftmargin;
    IF   tab_pos > pos
    THEN IF   tab_pos < max_output_line
         THEN BEGIN
                writev(buffer, ' ':(tab_pos - pos));
                write(genfl, buffer);
                IF   with_gen_echo
                THEN write_screen(buffer);
                pos := tab_pos
              END
  END;

6.2 Generation procedures for identifiers

  FUNCTION m_elem(nr : integer) : alfa;
  BEGIN
    m_elem := element[nr]^.name
  END;

  FUNCTION m_attr(nr : integer) : alfa;
  BEGIN
    m_attr := attribute[nr]^.name
  END;

  PROCEDURE gen_ident(ch: char; name : alfa);
  (* this procedure generates the identifier formed by '<ch>_<name>'.      *)
  BEGIN
    gen_(ch+'_'+name)
  END;

  FUNCTION m_1p_attr(part_nr, attr_nr : integer) : beta;
  BEGIN
    m_1p_attr := 'v'
               + chr(part_nr MOD 10 + ord('0'))
               + attribute[attr_nr]^.name
  END;

  PROCEDURE gen_1p_attr_id(part_nr : integer; attr_name : alfa);
  VAR
    size : integer;
  BEGIN
    gen_('v'+chr(part_nr MOD 10 + ord('0'))+attr_name);
  END;

  FUNCTION m_local(nr : integer) : alfa;
  CONST
    empty_loc = 'h  ';
  VAR
    result : alfa;
  BEGIN
    result := empty_loc;
    result[2] := chr((nr DIV 10) MOD 10 + ord('0'));
    result[3] := chr(nr MOD 10 + ord('0'));
    m_local := result
  END;

6.3 Generating keywords

  PROCEDURE gen_keyw(keyw : tkeyword);
  (* This procedure generates the PASCAL representation of the keyword keyw,  *)
  (* and adjusts the layout.                                                  *)

    PROCEDURE shftgen(name : alfa);
    BEGIN
      gen_(name);
      shift_margin(2)
    END;

  BEGIN
    CASE keyw OF
      k_program   : gen_('PROGRAM ');
      k_const     : shftgen('CONST');
      k_type      : shftgen('TYPE');
      k_record    : shftgen('RECORD');
      k_var       : shftgen('VAR');
      k_s_end     : shift_margin(-2);
      k_procedure : gen_('PROCEDURE ');
      k_function  : gen_('FUNCTION ');
      k_varref    : gen_('VAR ');
      k_forward   : gen_('FORWARD');
      k_begin     : shftgen('BEGIN');
      k_end       : BEGIN
                      shift_margin(-2);
                      gen_newline(1);
                      gen_('END')
                    END;
      k_case      : BEGIN
                      gen_('CASE ');
                      shift_margin(2)
                    END;
      k_of        : gen_('OF');
      k_with      : gen_('WITH ');
      k_do        : BEGIN
                      gen_('DO ');
                      shift_margin(3)
                    END;
      k_od        : shift_margin(-3)
    END
  END;

END.


My life as a hacker | My home page