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.
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:
PROCEDURE open_genfl;
The output file is automatically closed when the program terminates.
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.
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.
The keywords generation procedure has the following heading:
PROCEDURE gen_keyw(keyw : tkeyword);
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:
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 );
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.
The left margin can be moved to the right or to the left using the following procedure:
PROCEDURE shift_margin(number : integer);
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:
PROCEDURE set_left_margin(VAR hold : integer); PROCEDURE reset_left_margin(hold : integer);
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.
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:
PROCEDURE gen_page;
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:
PROCEDURE gen_newline(nr : integer := 1);
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:
PROCEDURE gen_tab(tab_pos : integer);
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.
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:
PROCEDURE gen_(info : VARYING[s] OF char);
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:
PROCEDURE gen_fixed(info : VARYING[s] OF char; size : integer);
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:
FUNCTION m_elem(nr : integer) : alfa;
The function m attr returns the representation of the attribute which has the number equal to its argument. The header of this function is:
FUNCTION m_attr(nr : integer) : alfa;
The following procedure is used to generate different identifiers of different kinds based on element and attribute names:
PROCEDURE gen_ident(ch : char; name : alfa);
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:
FUNCTION m_1p_attr(part_nr, attr_nr : integer) : beta;
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:
PROCEDURE gen_1p_attr(part_nr : integer; attr_name : integer);
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:
FUNCTION m_local(nr : integer) : alfa;
This module uses declarations from the following modules:
openfiles, screen, definitions, bintree.
The following constants and types are exported by this module:
CONST max_output_line = 130; max_outp_pl_1 = 131; beta_length = 40; TYPE beta = VARYING[beta_length] OF char; 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 );
The type tkeyword is described in subsection 2.2
The following variable is exported by this module:
VAR genfl : TEXT;
The following procedures and functions are exported by this module:
PROCEDURE open_genfl;
This procedure opens the generation file, and initializes the variables controlling the generation process. See section 1
PROCEDURE shift_margin(number: integer);
This procedure shifts the left margin number positions to the right. See subsection 2.3
PROCEDURE set_left_margin(VAR hold : integer);
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
PROCEDURE reset_left_margin(hold : integer);
This procedure resets the left margin to the position held in the parameter hold. See subsection 2.3
PROCEDURE gen_newline (nr:integer := 1);
This procedure generates nr newlines and sets the right margin. See section 3
PROCEDURE gen_page;
This procedure generates a new page, followed by a newline. See section 3
PROCEDURE gen_(info: VARYING[s] OF char);
This procedure prints the contents of the parameter info. See section 3
PROCEDURE gen_fixed(name : VARYING[s] OF char; size : integer);
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
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. See subsection 4.1
FUNCTION m_elem(nr : integer) : alfa;
This function returns the alphabetic representation of the element with the number nr. See subsection 4.2
FUNCTION m_attr(nr : integer) : alfa;
This function returns the alphabetic representation of the attribute with the number nr. See subsection 4.2
PROCEDURE gen_ident(ch: char; name : alfa);
This procedure generates the identifier formed by '<ch>_<name>'. See subsection 4.2
FUNCTION m_1p_attr(part_nr, attr_nr : integer) : beta;
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
PROCEDURE gen_1p_attr_id(part_nr : integer; attr_name : alfa);
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
FUNCTION m_local(nr : integer) : alfa;
This procedure returns the representation of the local variable with the number equal to the value of the parameter nr. See subsection 4.2
PROCEDURE gen_keyw(keyw : tkeyword);
This procedure generates the PASCAL representation of the keyword represented by the parameter keyw, and adjusts the layout. See subsection 2.2
[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 *) |
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; |
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; |
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