This module contains the parser of the input grammar. All of the type checking and some other semantic checks are done by this module.
In the following sections we describe the procedures that parse the production rules using the non-terminals of the grammar. For each procedure we also describe which of the semantic restrictions are tested.
The input grammar is described by the following grammar:
input grammar : ( OPTIONS , option LIST , . )OPTION : ( CLASSES , class LIST , . )OPTION , NODE TYPES , node type LIST , . , ( TYPES , user defined type identifier LIST , . )OPTION , ( FUNCTIONS , semantic function definition SEQ )OPTION , ATTRIBUTES , attribute definitions SEQ , ( INPUT , interface rule SEQ)OPTION , ( OUTPUT , interface rule SEQ)OPTION , RULES , rule SEQ , ROOT , element .
The procedure parser reads the input according to the input grammar. This procedure has the following structure:
PROCEDURE parser; BEGIN read_options; read_classes; read_node_types; read_types; read_semantic_functions; read_attributes; read_input_rules; read_output_rules; read_rules; read_root; skip_rubbish(inpas, []) END;
In the following sections we describe each of the procedures that are called.
The procedure read options reads the options according to the following grammar:
( OPTIONS , option LIST , . )OPTION options : questions ; no questions ; names ; no names ; all names ; no all names ; trace ; no trace ; statistics ; no statistics ; warnings ; errors ; fatals ; orthogonal ; no orthogonal ; all ; none .
The procedure read classes reads the enumeration of the classes according to the grammar:
( CLASSES , class LIST , . )OPTION . class : identifier .
This procedure calls the procedure read names, with the argument n class. The header of this procedure is:
PROCEDURE read_names(kind : tnode_kind);
This procedure is used for reading the enumeration of the classes, the node types and the types, due to their similarity. The parameter kind determines whether an enumeration of classes, node types or types is to be read. The procedure double enumerated [errors 3.3.1] is used to signal double enumerations to the error-handler.
The procedure read node types reads the enumeration of the node types according to the grammar:
NODE , TYPES , node type LIST , . . node type : identifier .
This procedure calls the procedure read names, with the argument n node.
The procedure read node types reads the enumeration of the node types according to the grammar:
( TYPES , user defined type identifier LIST , . )OPTION . user defined type identifier : identifier .
This procedure calls the procedure read names, with the argument n type.
The procedure read semantic functions reads the enumeration of the function definitions according to the grammar:
( FUNCTIONS , semantic function definition SEQ )OPTION . semantic function definition : semantic function identifier , type identifier LIST PACK , : , type identifier , . . semantic function identifier : identifier .
This procedure calls the procedure read one semantic function which reads the declaration of one semantic function, and stores the declaration in the binary identifier tree [bintree 1.10.4].
The procedure read attributes reads the enumeration of attribute definitions according to the following grammar:
ATTRIBUTES , attribute definition SEQ attribute definition : attribute identifier , : , type identifier , (synthesized symbol ; inherited symbol) , OF , element LIST , . .
attribute identifier : identifier . synthesized symbol : SYN ; SYNTHESIZED . inherited symbol : INH ; INHERITED .
This procedure calls the procedure read one attribute, which reads one attribute definition, and stores the type of the attribute in the binary identifier tree [bintree 1.10.3]. The global variables g syn attr and g inh attr [bintree 2.3] are updated depending on whether the attribute is declared as a synthesized or an inherited attribute. The relation "attr of" is updated with the attribute for each element that is enumerated in the attribute definition, by calling the procedure add attr [bintree 1.8,1.10.2].
The procedures read input rules and read output rules reads the input and the output rules according to the following grammar:
( INPUT , interface rule SEQ )OPTION , ( OUTPUT , interface rule SEQ )OPTION interface rule : attribute identifier , ( AT , element LIST )OPTION , . .
Both procedures call the procedure read io rules, with an argument indicating what kind of rules need to be read. The procedure read io rules reads all the interface rules. The parameter io kind determines whether input or output interface rules are read. The interface rules with no AT-clause are stored in the global variables g input attr and g output attr [bintree 2.3]. For the interface rules with an AT-clause and a list of elements, the record field attr is updated by calling the procedure set io attr at elem [bintree 1.8,1.10.2].
The procedure read rules reads the grammar rules according to the grammar:
RULES , rule SEQ
This procedure calls the procedure exp rule, which reads one grammar rule according to the grammar:
rule : (tree rule ; class definition) , . . tree rule : element , => , (part name , : , element)LIST , attribute assignments OPTION . class definition : class , = , { , element LIST , } .
This procedure tries to determine whether a tree rule or a class definition is expected, and if successful, will call either the procedure exp tree rule or exp class rule. These procedures are explained in the following subsections.
The procedure exp class rule reads a class definition. The parameter class ptr of type pnode points to the class identifier that has been read before this procedure is called. The equal and open bracket symbol have also been read before this procedure is called. This procedure checks whether the class identifier is a class which has no class definition as yet. An error is generated when this is not the case, and the rest of the class definition is skipped without checking the syntax. Otherwise, the record field class rule [bintree 1.10.2] of the class identifier is filled with the result of the function exp cl elements, which reads the enumeration of elements of this class, and the consistency of the rules is checked by calling the procedure test consistency, see subsection 9.3
The function exp cl elements returns the element that forms the elements of the class. These elements are read and added to the returned set by calling the procedure add element in class.
The procedure add element in class tries to add the element in the class definition. A warning is generated if the element is already in the class definition. Error messages are also generated if adding the element would create a recursive class definition, or if an element is in more than one class in the case that the option was given that forbids this. The boolean function c in clos c or and is used to test whether a recursive class definition would be introduced.
The boolean function c in clos c or and returns TRUE if the element pointed to by the first parameter class is equal to, or in the closure of the class of the element pointed to by the second parameter elem from. Otherwise it returns FALSE.
The procedure exp tree rule reads the tree rule, followed by the optional attribute assignments. The parameter elem ptr of type pnode points to the left-hand side element of the tree rule. The right-hand side of the tree rule is read by calling the function exp tree def, and the attribute assignments are read by calling the function exp list attr ass, whenever these are given. The record fields tree rule, kind of rule and attr ass [bintree 1.10.2] of the left-hand side element are updated, and error messages are generated whenever this leads to errors. The consistency [4.7.2.1] is tested by calling the procedure test consistency, see following subsection.
The function exp tree def returns the right-hand side part of the tree rule, which is read behind the arrow symbol. The function test partnames is called to test whether there are no duplicate part names in the tree rule. Error messages are generated whenever syntax errors are detected.
The consistency restrictions on the tree rules and class definitions are performed on-the-fly, whenever a rule is read. These restrictions are needed to make the definition of the "tree production of" function possible. This function is represented in the record field tree rule of the element identifiers in the binary tree, and is step-wise constructed when the consistency is checked.
The procedure test consistency tests the consistency in the class hierarchy under the element pointed to by the parameter elem ptr. In the case that there has been a class definition the test is performed by propagating the tree production to all the elements of the class definition of this element, and the procedure is called recursively for all these elements. If the procedure detects in the process of propagation that more than one tree production is defined with the same element, a fatal error message is generated.
The function exp list attr ass of type plattr ass [bintree 1.5] returns the representation of a list of attribute assignments read by the function, according to the following grammar:
attribute assignments : [ , attribute assignment LIST , ] . attribute assignment : simple attribute assignment ; selective assignment .
The first parameter followers contains the set of followers which is the set of symbols that may not be skipped. The second boolean parameter topnivo is equal to TRUE if the attribute assignments that are read are not within a selective assignment, otherwise it has the value FALSE.
The parameters allowedpn and correctpn are used by the process that finds a correct part name within a selective assignment. The fourth boolean parameter correctpn is set to TRUE when a correct part name has been found. Correct part names are the part names of the tree rule with these attribute assignments. If a correct part name has been found, the third parameter allowedpn contains, the representation of the first found correct part name, otherwise the alphabetic representation of the last found incorrect part name. These parameters are updated by the procedure process partname, which is not described further here.
The function exp list attr ass calls the procedure exp simp ass if a simple attribute assignment has been detected, and the procedure exp sel ass if a selective assignment has been detected. These procedures are discussed in the following subsections.
The procedure exp simp ass reads a simple attribute assignment according to the following grammar:
simple attribute assignment : attribute identifier , OF , part name , = , expression .
The parameter attr ptr points to the attribute identifier that has been read before the procedure was called. The procedure calls the function exp expression to read the expression in the simple attribute assignment. The function exp expression is described in section 11
The procedure exp sel ass reads a selective assignment according to the following grammar:
selective assignment : CASE , part name , OF , selection alternatives , ESAC .
The parameter hpartname nr contains the part name number of the selective assignment that has been read before the procedure was called.
The function exp sel alt of type plalt ass returns the list of selection alternatives that are read by the function according to the following grammar:
selection alternatives : selector , : , attribute assignment LIST , ( ; , selection alternatives )OPTION ; OTHERS , : , attribute assignment LIST .
The parameter previous contains all the elements that have been used in the preceding selectors. The selectors are read by the function exp selectors, see section 12
The function exp expression of type pexpr returns the expression read by the function according to the following grammar:
expression : attribute occurrence ; semantic function expression ; case expression .
The first parameter type ptr of type ttype [bintree 1.9] contains the representation of the formal type of the expression. The second parameter followers contains the symbols that may not be skipped in a skipping action within the expression that is being read by the function.
This function calls the function exp simple expr if a simple expression has been detected, the function exp sem func expr if a semantic function expression has been detected, and the function exp case expr if a case expression has been detected. These procedures are described in the following subsections.
The function exp expression is quite complicated because it first reads a number of symbols and tries to choose the best function (from the three above). This is done to increase the robustness of parsing of the expressions.
The function exp simple expr of type pexpr returns the representation of an attribute occurrence, according to the following grammar:
attribute occurrence : attribute identifier , OF , part name .
The first parameter type ptr of type ttype [bintree 1.9] contains the representation of the formal type of the expression. The second parameter attr ptr of type pnode contains the attribute identifier and the third parameter hpartname nr contains the number of the part name that was recognized by the function exp expression before it called this function. This function checks whether this attribute is an applied attribute, using the procedure applied attr (see subsection 15.2), and whether the type of the expression matches the type of the attribute, using the procedure test types [errors 4].
The function exp sem func expr of type pexpr returns the representation of a semantic function application, according to the following grammar:
semantic function application : semantic function identifier , expression LIST PACK .
The first parameter type ptr of type ttype [bintree 1.9] contains the representation of the formal type of the expression. The third parameter hsym of type alfa contains the alphabetic representation of the semantic function identifier, which has been read before this procedure was called. The second parameter ident of type pnode points to the node in the binary tree of the identifier with the alphabetic representation hsym, if it exists, otherwise the second parameter is equal to NIL. The fourth parameter followers contains the symbols that may not be skipped in a skipping action within the expression that is being read by this function. The open bracket symbol of the pack has been read before this procedure is called.
The function exp sem func expr reads the argument list of the semantic function application and checks this list against the type of the function. There are three functions that are used to read this argument list stored by means of the type plexpr [bintree 1.4]. The function read test is used to read a list of arguments and checks the types of arguments against the list of types that is represented by its first argument typel ptr. The function read simple is used to read a list of arguments without type checking. The procedure is used when there are more arguments given than in the function definition, and when the identifier represented by ident is not a semantic function identifier. The function read create reads a list of arguments and returns in its first parameter typel ptr a list of the types of the arguments read. The procedure is used to derive a type declaration of a (deduced) semantic function identifier whenever this identifier has not been declared before.
The function exp case expr of type pexpr returns the representation of a case expression according to the following grammar:
case expression : CASE , part name , OF , case expression alternatives , ESAC .
The first parameter type ptr of type ttype [bintree 1.9] contains the representation of the formal type of the expression. The second parameter hpartname nr contains the part name number of the case expression that has been read before the procedure was called. The third parameter followers contains the symbols that may not be skipped in a skipping action within the expression that is being read by the function.
The function exp case alt of type plalt expr returns the list of selection alternatives that are read by the function according to the following grammar:
case expression alternatives : selector , : , expression , ( ; , case expression alternatives)OPTION ; OTHERS , : , expression .
The parameter previous contains all the elements that have been used in the preceding selectors. The selectors are read by the function exp selectors, see next section.
Selectors are found as a part of selective assignments (see 10.2) and case expressions (see 11.3). The procedure exp selectors reads selectors according to the following grammar:
selector : element identifier LIST .
The first reference parameter selector of type elements contains the elements of the selector read by the procedure. The second reference parameter previous of type elements contains the elements of the previously read selectors on entering the procedure, and is updated with the elements of the selector read on leaving the procedure. The third parameter followers contains the symbols that may not be skipped in a skipping action within the expression that is being read by the function.
The function exp element of type pnode tries to read an element identifier. A syntax error message is generated when an identifier is not found. If an undefined identifier is found, it is defined by the function as a deduced element identifier, and an error message is generated. If it was defined, but not as an element identifier, an error message is generated.
The procedure is class checks if the element pointed by the parameter elem ptr is defined as a class identifier. Nothing happens when the identifier is not defined at all. An error message is generated when it is a node type identifier. When the identifier is a concluded element identifier it is changed into a concluded class identifier.
The procedure exp type tries to read a type identifier and returns in the parameter result of type ttype the formal type of the identifier. The parameter result is equal to the formal type variable und type if no type identifier is found. If the identifier is not yet defined it will be defined as a deduced type identifier by the procedure. If the identifier is not a type identifier, it is registered that it is used as a type identifier. Appropriate error messages are generated.
The function expr attr name of type pnode reads and returns an attribute identifier. When no identifier is found the function returns NIL, and an error message is generated. An error message is generated when the identifier is not defined as an attribute identifier.
The procedures inherited attr and synthesized attr check whether the attribute pointed to by the first parameter attr ptr is an inherited attribute or a synthesized attribute, respectively. If this is not true, an error message of the kind determined by the second parameter err is generated. If the attribute is not defined as synthesized or inherited, it is changed into a deduced inherited or a deduced synthesized attribute, respectively.
The procedures assigned attr and applied attr check whether the attribute pointed to by the first parameter attr ptr is an assigned attribute or an applied attribute, respectively, depending on the part name as represented by the second parameter partname. If the part name is not defined, no check is performed. Otherwise, one of the procedures synthesized attr or inherited attr is called.
In this section we will describe a number of functions that are used in reading and processing the part names in the attribute assignments and the expression.
The function exp partname of type alfa returns the alphabetic representation of a part name. This can either be the alphabetic representation of an identifier or the symbol #. When none of these is found the value of the constant error partname is returned, and an error message is generated.
The function nr of partname of type integer returns the number of the partname with the alphabetic representation stored in the parameter hulpsym. It uses the global variable partnames of type ptree rule which points to the last read tree rule. The alphabetic representation of the main part name is converted into the value of the constant main part nr. The function returns the value of the constant error part nr if the alphabetic representation is not a part name of the last read tree rule, otherwise it returns the index of part name in the tree rule. The index of the first part name is one.
The function exp test partname of type integer returns the number of the part name which it reads. If it does not read a valid part name, the value that is returned is equal to the value of the constant error part nr, and an error message is generated. The function makes use of the two functions described in the previous subsections.
The function partname with of type alfa returns the alphabetic representation with the part name number of the parameter partname nr. The value '<ERROR>' is returned if partname nr is equal to the value of the constant error part nr.
The procedure read root reads the root element identifier and stores it in the global variable root [bintree 2.2].
This module uses declarations from the following modules:
screen, definitions, perform, errors, scanner, bintree.
The following constant declarations are exported:
main_partname = '# '; { alfa representation of main partname } error_partname = '*ERRORPART'; { alfa representation of error partname } undef_partname = '*UNDEFPART'; { alfa representation of undefined partname } skip = TRUE; do_not_skip = FALSE;
This module only exports the following procedure:
PROCEDURE parser;
In This section the complete listing is given. The listing has been split into separate subsections to display the logical subdivision of the entire listing.
[ENVIRONMENT ('parser.pen'), INHERIT ('[-.screen]screen.pen', 'definitions.pen', 'perform.pen', 'errors.pen', 'scanner.pen', 'bintree.pen')] MODULE parser; (* This program contains the parser of the input. The procedures in this *) (* program do the syntactical parsing of the input, and perform some type *) (* checking, both of the used identifiers(names) and the expressions. It *) (* can been seen as the first pass of this compiler-compiler. *) |
CONST main_partname = '# '; (* alfa repr. of main partname *) error_partname = '*ERRORPART'; (* alfa repr. of error partname *) undef_partname = '*UNDEFPART'; (* alfa repr. of undefined partname *) skip = TRUE; do_not_skip = FALSE; |
VAR (* local for this program *) error_ptr : [HIDDEN] pnode; (* used when reading double definitions *) partnames : [HIDDEN] ptree_rule; (* holding last read tree rule *) |
PROCEDURE exp_comma; (* This procedure is used when a comma in a list-construct is wanted *) BEGIN IF scansym = comma_sym THEN nextsym(inpas) ELSE IF scansym = some_sym THEN error(m_sex, ',') END; PROCEDURE sup_comma; (* This procedure is used for a superfluous comma *) BEGIN error(s_scm); nextsym(inpas) END; FUNCTION test_kind(p:pnode; k:tnode_kind) : boolean; (* This function tests whether the kind of (possibly empty) node p is equal *) (* kind k *) BEGIN IF p = NIL THEN test_kind := FALSE ELSE test_kind := (p^.kind = k) END; |
(* The procedures on this page are for reading the options from the input, *) (* according the grammar: *) (* *) (* grammar : (options symbol , options LIST , period symbol)OPTION . *) (* *) (* options : QUESTIONS ; NO_QUESTIONS ; NAMES ; NO_NAMES ; ALL_NAMES *) (* ; NO_ALL_NAMES ; TRACE ; NO_TRACE ; STATISTICS ; NO_STATISTICS *) (* ; WARNINGS ; ERRORS ; FATALS ; ORTHOGONAL ; NO_ORTHOGONAL ; *) (* ; ALL ; NONE *) (* . *) (* *) PROCEDURE read_options; (* This procedure reads the options from the input, if the keyword OPTIONS *) (* is found *) VAR i : integer; PROCEDURE init_options; (* This procedure initializes the options to their default values *) BEGIN ask := FALSE; with_names := FALSE; with_all_names := FALSE; with_tracing := FALSE; with_statistics := FALSE; min_error_kind := t_fat; with_orthogonal := FALSE; END; PROCEDURE read_one_option; (* This procedure processes one option, using the alfa representation. *) BEGIN add_level_info(sym); CASE sym.length OF 3 : IF sym = 'all' THEN BEGIN ask := FALSE; with_names := TRUE; with_all_names := TRUE; with_tracing := TRUE; with_statistics := TRUE; min_error_kind := t_war; with_orthogonal := TRUE; END; 4 : IF sym = 'none' THEN init_options; 5 : IF sym = 'names' THEN with_names := TRUE ELSE IF sym = 'trace' THEN with_tracing := TRUE; 6 : IF sym = 'errors' THEN min_error_kind := t_err ELSE IF sym = 'fatals' THEN min_error_kind := t_fat; 8 : IF sym = 'no_names' THEN with_names := FALSE ELSE IF sym = 'no_trace' THEN with_tracing := FALSE ELSE IF sym = 'warnings' THEN min_error_kind := t_war; 9 : IF sym = 'questions' THEN ask := TRUE ELSE IF sym = 'all_names' THEN with_all_names := TRUE; 10 : IF sym = 'statistics' THEN with_statistics := TRUE ELSE IF sym = 'orthogonal' THEN with_orthogonal := TRUE; 12 : IF sym = 'no_all_names' THEN with_all_names := FALSE ELSE IF sym = 'no_questions' THEN ask := TRUE; 13 : IF sym = 'no_statistics' THEN with_statistics := FALSE ELSE IF sym = 'no_orthogonal' THEN with_orthogonal := FALSE OTHERWISE error(e_uop) END; nextsym(inpas) END; BEGIN (* of read_options *) skip_rubbish(inpas, [options_sym,classes_sym,node_sym,types_sym,functions_sym, types_sym,functions_sym,input_sym,output_sym,rules_sym, root_sym]); enter_level('options'); init_options; IF scansym = options_sym THEN BEGIN nextsym(inpas); WHILE scansym IN [some_sym,comma_sym] DO IF scansym = some_sym THEN BEGIN read_one_option; exp_comma END ELSE sup_comma; back_on_the_rails(inpas, [period_sym], [classes_sym,node_sym,types_sym, functions_sym,attribute_sym,input_sym,output_sym, rules_sym,root_sym], '.', m_sex, skip); (* When ALL_NAMES is given, NAMES is included so: *) IF with_all_names THEN with_names := TRUE END; exit_level END; |
(* The procedures and functions on this page are for reading and testing *) (* elements. *) (* The following procedures are used to read the class and node type *) (* definitions, using the grammar: *) (* *) (* class definitions *) (* : (classes symbol , class LIST period symbol)OPTION . *) (* node type definitions *) (* : node types symbol , node type LIST , period symbol . *) (* element : class ; node type . *) PROCEDURE read_names(kind: tnode_kind); (* This procedure reads names of kind kind closed with period_sym and *) (* separated by single commas. The names are tested on double definitions. *) (* If not so defined as names of kind kind *) (* This procedure starts with a call to nextsym(inpas) ! *) VAR name_ptr : pnode; BEGIN nextsym(inpas); WHILE scansym IN [some_sym,comma_sym] DO IF scansym = some_sym THEN BEGIN IF NOT insert_name(nametree, sym, name_ptr, kind) THEN double_enumerated(name_ptr,kind); nextsym(inpas); exp_comma END ELSE sup_comma; back_on_the_rails(inpas, [period_sym], [node_sym,types_sym,functions_sym, attribute_sym,input_sym,output_sym,rules_sym,root_sym], '.', m_sex,skip) END; PROCEDURE read_classes; (* This procedure reads the classes from the input, if the keyword CLASSES *) (* is found using the procedure read_names. *) BEGIN enter_level('classes'); skip_rubbish(inpas, [classes_sym,node_sym,types_sym,functions_sym, attribute_sym,input_sym,output_sym,rules_sym,root_sym]); IF scansym = classes_sym THEN read_names(n_class); exit_level END; PROCEDURE read_node_types; (* This procedure reads all the node types from the input using read names. *) (* Syntax errors are generated when the keywords NODE TYPES are not found. *) BEGIN enter_level('node types'); back_on_the_rails(inpas, [node_sym], [types_sym,functions_sym,attribute_sym, input_sym,output_sym,rules_sym,root_sym], 'NODE', s_mk1, do_not_skip); IF scansym = node_sym THEN BEGIN nextsym(inpas); expect(inpas, types_sym, do_not_skip, 'TYPES'); read_names(n_node) END; exit_level END; (* The following procedures are used when reading the tree and class rules. *) FUNCTION exp_element : pnode; (* This function tries to read an element from the input. If no name is *) (* found a syntax error is generated. If the name was not yet defined, it *) (* is defined as a concluded element, otherwise it is tested whether it is *) (* defined as an element. Appropriate error messages are generated *) VAR elem_ptr : pnode; BEGIN enter_level('element'); IF scansym = some_sym THEN (* a name is found *) BEGIN IF insert_name(nametree, sym, elem_ptr, n_elem) THEN (* name not yet defined *) BEGIN error(e_nye); conclude(elem_ptr); exp_element := elem_ptr END ELSE IF NOT (elem_ptr^.kind IN [n_elem,n_class,n_node]) THEN (* name not defined as element *) BEGIN wrong(elem_ptr, n_elem); exp_element := NIL END ELSE exp_element := elem_ptr; nextsym(inpas) END ELSE (* no name is found *) BEGIN error(s_nex); exp_element := NIL END; exit_level END; PROCEDURE is_class(VAR elem_ptr:pnode); (* This procedure tests whether element is a class. When it is not a class, *) (* it is checked whether it might have been concluded as an element. If so *) (* this element is converted into a concluded class. In both cases a normal *) (* error message is generated. The returned elem_ptr is a class or NIL. *) BEGIN IF elem_ptr <> NIL THEN IF elem_ptr^.kind = n_node THEN (* it is defined as a node type *) BEGIN error(e_esc); elem_ptr := NIL END ELSE WITH elem_ptr^ DO IF kind = n_elem THEN (* it is defined as a concluded element *) BEGIN error(e_nyc); kind := n_class END END; |
(* The procedures on this page are for reading types. The grammar is: *) (* *) (* standard type identifiers *) (* : INTEGER ; REAL ; BOOLEAN ; CHAR ; ASCII . *) (* user type identifier definitions *) (* : (type symbol *) (* , user defined type identifier LIST , period symbol *) (* )OPTION . *) (* type identifier *) (* : standard type identifier *) (* ; user defined type identifier *) (* . *) (* The following procedure is used for reading the user defined type *) (* definitions. *) PROCEDURE read_types; (* This procedure reads all the user defined types from the input, using *) (* read_names, if the keyword TYPES is found. *) BEGIN enter_level('types'); skip_rubbish(inpas, [types_sym,functions_sym,attribute_sym, input_sym,output_sym,rules_sym,root_sym]); IF scansym = types_sym THEN read_names(n_type); exit_level END; (* The following procedure is used; to read types within semantic function *) (* definitions and attribute definitions. *) PROCEDURE exp_type(VAR result:ttype); (* This function tries to read a type from the input. If no name is found a *) (* syntax error is generated. If the name was not yet defined, it will be *) (* defined as a concluded type. If it was defined as something else an *) (* error is generated and it is recorded that this name is used as a type. *) (* In both cases result will held a formal type. *) BEGIN enter_level('type'); IF scansym = some_sym THEN (* a name is found *) WITH result (* : RECORD type_name : pnode ; conc : boolean END *) DO BEGIN IF insert_name(nametree, sym, type_name, n_type) THEN (* name is not yet defined *) BEGIN error(e_nyt); conc := TRUE END ELSE IF type_name^.kind <> n_type THEN (* name not defined as type *) BEGIN wrong(type_name, n_type); conc := TRUE; error_define(type_name,n_type) END ELSE (* a type name is found *) conc := FALSE; nextsym(inpas) END ELSE (* no name is found *) BEGIN error(s_nex); result := und_type END; exit_level END; |
(* The procedures on this page are for reading the semantic function *) (* definitions, using the grammar : *) (* *) (* semantic function definitions *) (* : (functions symbol , semantic function definition SEQ)OPTION . *) (* semantic function definition *) (* : semantic function name *) (* , type identifier LIST PACK *) (* , colon symbol , type identifier , period symbol *) (* . *) PROCEDURE read_semantic_functions; (* This procedure reads semantic function definitions from the input, if *) (* the keyword FUNCTIONS is found, according to the above grammar. *) PROCEDURE read_one_semantic_function; (* This procedure reads one semantic definition from the input. If a name *) (* is already defined an error message is generated, and actions are *) (* taken so that the formal specification of the function is read but not *) (* added to the identifier, by making use of the error_ptr. *) VAR func_ptr : pnode; FUNCTION read_arguments(VAR arg : pltype) : integer; (* This function gives the number of arguments read, and puts them in *) (* the variable arg. *) BEGIN skip_rubbish(inpas, [some_sym,comma_sym,close_sym,colon_sym,period_sym, attribute_sym,input_sym,output_sym,rules_sym,root_sym]); IF scansym IN [comma_sym,some_sym] THEN BEGIN new(arg); WITH arg^ DO BEGIN exp_type(first); exp_comma; read_arguments := 1 + read_arguments(rest) END END ELSE BEGIN arg := NIL; read_arguments := 0 END END; BEGIN(* of read_one_semantic_function *) IF NOT insert_name(nametree, sym, func_ptr, n_func) THEN (* name is already defined *) BEGIN double_define(func_ptr, n_func); func_ptr := error_ptr; func_ptr^.kind := n_func END; WITH func_ptr^ (* : RECORD nr_of_arg : integer ; type_of_func : ttype ; *) (* args : pltype END (part of tnode !!) *) DO BEGIN nextsym(inpas); expect(inpas, open_sym, skip, '('); nr_of_arg := read_arguments(args); expect(inpas, close_sym, skip, ')'); expect(inpas, colon_sym, skip, ':'); exp_type(type_of_func); END; back_on_the_rails(inpas, [period_sym], [attribute_sym,input_sym,output_sym, rules_sym,root_sym], '.', m_sex, skip) END; BEGIN (* of read semantic function definitions *) enter_level('functions'); skip_rubbish(inpas, [functions_sym,attribute_sym,input_sym,output_sym, rules_sym,root_sym]); IF scansym = functions_sym THEN BEGIN nextsym(inpas); WHILE scansym = some_sym DO read_one_semantic_function END; exit_level END; |
(* The procedures on this page are for reading and testing attributes. *) (* Following procedure reads the attribute definitions, using the grammar: *) (* *) (* attribute definitions *) (* : attributes symbol , attribute definition SEQ . *) (* attribute definition *) (* : attribute name , colon symbol , type identifier *) (* , (synthesized symbol ; inherited symbol) , of symbol *) (* , element LIST , period symbol *) (* . *) PROCEDURE read_attributes; (* This procedure reads all the attribute definitions according to the *) (* above grammar from the input. If the keyword ATTRIBUTES is missing a *) (* syntax error message is generated. *) PROCEDURE read_one_attribute; (* This procedure reads one attribute definition from the input. If a *) (* name is already defined an error message is generated, and error_ptr *) (* is used to read the definition, but not to add anything. *) VAR attr_ptr , elem_ptr : pnode; PROCEDURE add_attr(VAR attr : attributes; attr_nr : integer); (* This procedure adds an attribute, with number attr_nr, to the set of *) (* attributes attr. If it is already in this set, a warning is given. *) BEGIN IF attr_nr <> error_nr THEN IF attr_nr IN attr THEN error(w_era) ELSE attr := attr + [attr_nr] END; PROCEDURE read_attr_kind(attr_nr : integer); (* This procedure reads the kind of an attribute. *) BEGIN CASE scansym OF inh_sym : BEGIN nextsym(inpas); IF attr_nr <> error_nr THEN g_inh_attr := g_inh_attr + [attr_nr] END; syn_sym : BEGIN nextsym(inpas); IF attr_nr <> error_nr THEN g_syn_attr := g_syn_attr + [attr_nr] END; OTHERWISE error(m_sex) END END; BEGIN (* of read_one_attribute *) IF NOT insert_name(nametree, sym, attr_ptr, n_attr) THEN (* name is already defined *) BEGIN double_define(attr_ptr,n_attr); attr_ptr := error_ptr; attr_ptr^.kind := n_attr END; nextsym(inpas); WITH attr_ptr^ (* RECORD type_of_attr : ttype ; attr_kind : tattr_kind; *) (* input,output : boolean END (part of tnode) *) DO BEGIN expect(inpas, colon_sym,skip, ':'); exp_type(type_of_attr); read_attr_kind(attr_nr); expect(inpas, of_sym,skip, 'OF'); (* process the element names as a list-construct: *) WHILE scansym IN [some_sym,comma_sym] DO IF scansym = some_sym THEN BEGIN elem_ptr := exp_element; IF (attr_ptr <> error_ptr) AND (elem_ptr <> NIL) THEN add_attr(elem_ptr^.attr[nor_gen],attr_ptr^.attr_nr); exp_comma END ELSE sup_comma END; back_on_the_rails(inpas, [period_sym], [input_sym,output_sym,rules_sym,root_sym], '.', m_sex, skip) END; BEGIN (* of read_attributes *) enter_level('attributes'); skip_rubbish(inpas, [attribute_sym,input_sym,output_sym,rules_sym,root_sym]); IF scansym = attribute_sym THEN BEGIN nextsym(inpas); WHILE scansym = some_sym DO read_one_attribute END; exit_level END; (* the following function and procedures are used when reading and testing *) (* attribute assignments. *) FUNCTION exp_attr_name : pnode; (* This function tries to read an attribute name. It gives error messages *) (* when no attribute name is found. *) VAR attr_ptr : pnode; BEGIN enter_level('attribute'); IF scansym = some_sym THEN (* a name is found *) BEGIN IF get_name(nametree, sym, attr_ptr) THEN (* name is defined *) IF attr_ptr^.kind <> n_attr THEN (* name not defined as attribute *) BEGIN wrong(attr_ptr, n_attr); attr_ptr := NIL END; nextsym(inpas) END ELSE (* no name is found *) BEGIN error(s_nex); attr_ptr := nil END; exp_attr_name := attr_ptr; exit_level END; PROCEDURE inherited_attr(attr_ptr : pnode; err : errors); (* This procedure tests whether the attribute, pointed by attr_ptr, is an *) (* inherited attribute. It generates an error message if not. *) BEGIN WITH attr_ptr^ DO IF NOT (attr_nr IN g_inh_attr) THEN IF attr_nr IN g_syn_attr THEN error(err,,concluded(attr_ptr)) ELSE BEGIN conclude(attr_ptr); g_inh_attr := g_inh_attr + [attr_nr] END END; PROCEDURE synthesized_attr(attr_ptr : pnode; err : errors); (* This procedure tests whether the attribute, pointed by attr_ptr, is a *) (* synthesized attribute. It generates an error message if not. *) BEGIN WITH attr_ptr^ DO IF NOT (attr_nr IN g_syn_attr) THEN IF attr_nr IN g_inh_attr THEN error(err,,concluded(attr_ptr)) ELSE BEGIN conclude(attr_ptr); g_syn_attr := g_syn_attr + [attr_nr] END END; PROCEDURE assigned_attr(attr_ptr:pnode; partname:integer); (* This procedure tests whether the given attribute with the partname is an *) (* assigned attribute. If not an error message is generated. *) (* If the kind of attribute is undefined, the kind is updated and the error *) (* status of the attribute becomes concluded. *) BEGIN IF partname <> error_part_nr THEN WITH attr_ptr^ DO IF partname = main_part_nr THEN synthesized_attr(attr_ptr, b_aap) ELSE inherited_attr(attr_ptr, b_aap) END; PROCEDURE applied_attr(attr_ptr:pnode; partname:integer); (* This procedure tests whether the given attribute with the partname is an *) (* applied attribute. If not an error message is generated. *) (* If the kind of attribute is undefined, the kind is updated and the error *) (* status of the attribute becomes concluded. *) BEGIN IF partname <> error_part_nr THEN WITH attr_ptr^ DO IF partname = main_part_nr THEN inherited_attr(attr_ptr, b_aas) ELSE synthesized_attr(attr_ptr, b_aas) END; |
(* The procedures on this page are for reading and testing the input and *) (* output rules, using the following grammar *) (* *) (* input rules *) (* : (input symbol , interface rule SEQ)OPTION . *) (* output rules *) (* : (output symbol , interface rule SEQ)OPTION . *) (* interface rule *) (* : attribute name , (at symbol , element LIST)OPTION *) (* , period symbol *) (* . *) (* *) (* Because there are two kinds of interface rules, these are implemented in *) (* two different ways. Rules with no at-part will result in condition set at *) (* the attribute, which is done by the procedure set_all_io_attr. Rules with *) (* an at-part will result in condition set at all the attribute element *) (* pairs with the elements taken from after the at-symbol, using the *) (* procedure set_io_attr_at_elem. *) PROCEDURE read_io_rules(io_kind : tinout_kind); (* This procedure reads interface rules of kind io_kind *) PROCEDURE read_one_io_rule; (* This procedure reads one interface rule *) VAR attr_ptr , elem_ptr : pnode; attrnr : integer; PROCEDURE set_all_io_attr; (* This procedure sets the in- or output condition for an attribute, *) (* and warnings are generated when this has already been done. *) BEGIN WITH attr_ptr^ DO CASE io_kind OF n_input : IF attr_nr IN g_input_attr THEN error(w_idr) ELSE g_input_attr := g_input_attr + [attr_nr]; n_output : IF attr_nr IN g_output_attr THEN error(w_odr) ELSE g_output_attr := g_output_attr + [attr_nr] END END; PROCEDURE set_io_attr_at_elem; (* This procedure sets the in- or output condition for an attribute *) (* element pair. An error message is generated when this pair is not *) (* defined in the attribute rules. Warnings are generated when the *) (* condition has already been set. *) BEGIN IF attrnr <> error_nr THEN WITH elem_ptr^ DO IF attrnr IN attr[nor_gen] THEN CASE io_kind OF n_input : IF attrnr IN (attr[nor_in] + g_input_attr) THEN error(w_iar) ELSE attr[nor_in] := attr[nor_in] + [attrnr]; n_output : IF attrnr IN (attr[nor_out] + g_output_attr) THEN error(w_oar) ELSE attr[nor_out] := attr[nor_out] + [attrnr] END ELSE error(b_nea,,error_defined(elem_ptr, n_attr)) END; BEGIN (* of read_one_io_rule *) attr_ptr := exp_attr_name; IF attr_ptr <> NIL THEN IF scansym = at_sym THEN (* rule with at-part *) BEGIN nextsym(inpas); attrnr := attr_ptr^.attr_nr; (* this part reads and processes: element LIST *) WHILE scansym IN [some_sym,comma_sym] DO IF scansym = some_sym THEN BEGIN IF get_name(nametree, sym, elem_ptr) THEN IF elem_ptr^.kind IN [n_class,n_node,n_elem] THEN set_io_attr_at_elem ELSE wrong(elem_ptr,n_elem) ELSE missing(n_elem); nextsym(inpas); exp_comma END ELSE sup_comma END ELSE (* rule with no at-part *) set_all_io_attr ELSE wrong_or_missing(attr_ptr,n_attr); back_on_the_rails(inpas, [period_sym], [input_sym,output_sym,rules_sym,root_sym], '.', m_sex, skip) END; BEGIN (* of read_io_rules *) nextsym(inpas); WHILE scansym = some_sym DO read_one_io_rule END; PROCEDURE read_input_rules; (* This procedure reads the input rules, if the keyword INPUT is found. *) BEGIN enter_level('input_rules'); skip_rubbish(inpas, [input_sym,output_sym,rules_sym,root_sym]); IF scansym = input_sym THEN read_io_rules(n_input); exit_level END; PROCEDURE read_output_rules; (* This procedure reads the output rules, if the keyword OUTPUT is found. *) BEGIN enter_level('output rules'); skip_rubbish(inpas, [output_sym,rules_sym,root_sym]); IF scansym = output_sym THEN read_io_rules(n_output); exit_level END; |
(* The functions on this page are used to read the partnames in the *) (* attribute assignments. *) FUNCTION exp_partname : alfa; (* This function tries to read a representation of a partname, which can be *) (* a name or the main symbol. The result is in the form of an alfa *) (* representation. *) BEGIN enter_level('partname'); IF scansym = main_sym THEN (* "#" found *) BEGIN exp_partname := main_partname; nextsym(inpas) END ELSE IF scansym = some_sym THEN (* name found *) BEGIN exp_partname := sym; nextsym(inpas) END ELSE (* no "#" or name found *) BEGIN error(m_sex, 'partname'); exp_partname := error_partname END; exit_level END; FUNCTION nr_of_partname(hulpsym:alfa) : integer; (* This function converts an alfa representation of a partname to a number *) (* using the global variable partnames, which holds the description of the *) (* last read tree rule. The main representation is converted to the *) (* main partname_nr. If the name hulpsym is within the list, the order is *) (* returned, which means that the name is a partname of this tree rule. *) (* Otherwise the error partname number will be returned. *) VAR list : ptree_rule; nr , result : integer; BEGIN IF hulpsym = main_partname THEN nr_of_partname := main_part_nr ELSE BEGIN list := partnames; nr := 1; result := error_part_nr; WHILE (list <> NIL) AND (result = error_part_nr) DO IF list^.partname = hulpsym THEN result := nr ELSE BEGIN nr := nr + 1; list := list^.rest END; nr_of_partname := result END END; FUNCTION exp_test_partname : integer; (* This function tries to read a partname. It does this by first trying *) (* a representation of a partname, and then tries to convert it. An error *) (* message is generated if a name is found which is not a partname. *) VAR hulpsym : alfa; hulpnr : integer; BEGIN hulpsym := exp_partname; IF hulpsym = error_partname THEN exp_test_partname := error_part_nr ELSE BEGIN hulpnr := nr_of_partname(hulpsym); IF hulpnr = error_part_nr THEN error(e_nip); exp_test_partname := hulpnr END END; FUNCTION partname_with(partname_nr : integer) : alfa; (* This function returns the alfa representation with partname_nr *) VAR list : ptree_rule; nr : integer; BEGIN IF partname_nr = error_part_nr THEN partname_with := '<ERROR>' ELSE IF partname_nr = main_part_nr THEN partname_with := '#' ELSE BEGIN list := partnames; nr := 1; WHILE (nr < partname_nr) AND (list <> NIL) DO BEGIN list := list^.rest; nr := succ(nr) END; IF list = NIL THEN partname_with := '<***>' ELSE partname_with := list^.partname END END; (* NAME OF *) FUNCTION name_of(node : pnode) : alfa; (* This function returns the alfa representation of node. *) BEGIN IF node = NIL THEN name_of := '<UNDEFINED>' ELSE name_of := node^.name END; |
(* The procedure on this page is for reading selectors, which are found both *) PROCEDURE exp_selectors(VAR selector, previous : elements; followers : symbol_set); (* This procedure reads a selector consisting of a number of elements. *) (* If the selector is empty, a warning is generated. *) VAR no_more_sel : boolean; name_ptr : pnode; FUNCTION name_in_till : boolean; (* This function tests whether the next name is defined as an attribute *) (* or as a function. In that case it will return TRUE. A side effect is *) (* that the variable name_ptr is set. *) BEGIN IF get_name(nametree, sym, name_ptr) THEN name_in_till := (name_ptr^.kind IN [n_attr,n_func]) ELSE name_in_till := FALSE END; PROCEDURE add_name_in_sel; BEGIN WITH name_ptr^ DO IF elem_nr <> error_nr THEN IF elem_nr IN previous THEN error(e_eps) ELSE IF elem_nr IN selector THEN error(w_ers) ELSE selector := selector + [elem_nr] END; BEGIN (* of exp_selectors *) enter_level('selector'); selector := []; no_more_sel := FALSE; REPEAT skip_rubbish(inpas, followers); IF scansym = some_sym THEN IF name_in_till THEN (* an attribute or function name is found *) no_more_sel := TRUE ELSE BEGIN IF name_ptr <> NIL THEN IF name_ptr^.kind IN [n_class,n_node,n_elem] THEN (* name is an element *) BEGIN add_level_info(name_ptr^.name); add_name_in_sel END ELSE wrong(name_ptr, n_elem) ELSE missing(n_elem); nextsym(inpas); (* looks whether there is more to read *) skip_rubbish(inpas, followers); IF scansym = comma_sym THEN nextsym(inpas) ELSE (* no comma *) BEGIN no_more_sel := TRUE; IF scansym = some_sym THEN IF get_name(nametree, sym, name_ptr) THEN IF name_ptr^.kind IN [n_class,n_node,n_elem] THEN (* next symbol is an element name *) BEGIN error(m_sex, ','); no_more_sel := FALSE END END END ELSE IF scansym = comma_sym THEN sup_comma ELSE no_more_sel := TRUE UNTIL no_more_sel; IF selector = [] THEN error(w_ese); previous := previous + selector; exit_level END; |
(* The procedures and functions on this page are used to read expressions *) (* within the attribute assignments. The grammar of the expressions is *) (* given with the different functions. *) FUNCTION exp_expression(VAR type_ptr:ttype; followers:symbol_set) : pexpr; (* This procedure is used for reading an expression. Here there is only a *) (* forward declaration. The grammar of an expression is: *) (* *) (* expression : attribute occurrence *) (* ; semantic function application *) (* ; case expression *) (* . *) (* *) (* This function tries to determine the kind, and calls one of the *) (* procedures: exp_simple_expr, exp_sem_func_expr or exp_case_expr. *) (* In the comment it is stated what has been found so far, while reading *) (* the input. It can been seen what conclusions are made. *) VAR res : pexpr; ident : pnode; hsym : alfa; partname_nr : integer; start_line : integer; FUNCTION exp_simple_expr(VAR type_ptr:ttype; attr_ptr:pnode; hpartname_nr:integer) : pexpr; (* This function returns a simple expression, which is an attribute *) (* occurrence. The grammar is : *) (* *) (* attribute occurrence *) (* : attribute name , of symbol , part name . *) (* *) (* This function tests the type of the expression, using type_ptr. The *) (* attribute name has been read and is representated by attr_ptr. The part *) (* name has been read and converted into a number and is given by *) (* hpartname_nr. It also tests whether the attribute is applied. *) VAR expr_ptr : pexpr; BEGIN add_level_info(name_of(attr_ptr) + ' OF ' + partname_with(hpartname_nr)); wait(0.8); IF test_kind(attr_ptr,n_attr) THEN BEGIN applied_attr(attr_ptr, hpartname_nr); test_types(attr_ptr^.type_of_attr, type_ptr) END ELSE wrong_or_missing(attr_ptr,n_attr); new(expr_ptr,e_atoc); WITH expr_ptr^ DO BEGIN kind := e_atoc; partnamenr := hpartname_nr; attr := attr_ptr; line_nr := start_line END; exp_simple_expr := expr_ptr END; FUNCTION exp_sem_func_expr(VAR type_ptr:ttype; ident:pnode; hsym:alfa; followers:symbol_set) : pexpr; (* This function reads a semantic function application, and returns the *) (* result. The grammar is : *) (* *) (* semantic function application *) (* : semantic function name , expression LIST PACK . *) (* *) (* This function tests the type of the expression, using type_ptr. The *) (* function name is represented in hsym, and when already defined ident *) (* holds a pointer to the node. The open symbol has been read. It reads the *) (* applied argument expressions and they are processed. Type checking is *) (* done if possible, and the number of arguments is checked with the *) (* definition. In the case the function name is not defined it will be *) (* defined as a concluded function name, and the formal type is retrieved *) (* from the context. *) VAR stoppers , infollowers : symbol_set; result : pexpr; PROCEDURE test_correct_comma(first:boolean); (* This procedure tests whether there should be a comma or not, and is *) (* used in the tree procedures below to test the list-construct. If first *) (* is TRUE, no comma should be found, otherwise a comma must be found. *) BEGIN IF scansym = comma_sym THEN BEGIN IF first THEN error(s_scm); nextsym(inpas) END ELSE BEGIN IF NOT first THEN error(m_sex, ',') END END; FUNCTION read_simple(first:boolean) : plexpr; (* This function reads the arguments without any type checking and does *) (* not count the number of arguments in case there is no information *) (* available of the applied function. It is also used when there are more *) (* applied arguments than defined arguments. *) VAR result : plexpr; BEGIN skip_rubbish(inpas, infollowers); IF scansym IN stoppers THEN read_simple := NIL ELSE (* more arguments *) BEGIN test_correct_comma(first); new(result); WITH result^ DO BEGIN first := exp_expression(err_type, followers); type_of_arg.length := 0; rest := read_simple(FALSE) END; read_simple := result END END; FUNCTION read_test(typel_ptr : pltype; first:boolean) : plexpr; (* This function reads the arguments and tests whether the types and the *) (* number of arguments is correct, using the type description represented *) (* in typel_ptr. *) (* Error messages are generated when the number is not correct. *) VAR result : plexpr; BEGIN skip_rubbish(inpas, infollowers); IF scansym IN stoppers THEN (* all arguments read *) BEGIN IF typel_ptr <> NIL THEN (* more arguments defined *) error(b_tfa,,concluded(ident)); read_test := NIL END ELSE (* more arguments to read *) IF typel_ptr = NIL THEN (* no more arguments defined *) BEGIN test_correct_comma(first); error(b_tma,,concluded(ident)); read_test := read_simple(TRUE) END ELSE BEGIN test_correct_comma(first); new(result); WITH result^ DO BEGIN first := exp_expression(typel_ptr^.first,followers); WITH typel_ptr^.first DO IF type_name = NIL THEN type_of_arg.length := 0 ELSE type_of_arg := type_name^.name; rest := read_test(typel_ptr^.rest,FALSE) END; read_test := result END END; FUNCTION read_create(VAR typel_ptr : pltype; first:boolean):plexpr; (* This function reads a number of arguments, and uses this information *) (* to construct a formal type definitions in the variable typel_ptr. *) VAR result : plexpr; BEGIN skip_rubbish(inpas, infollowers); IF scansym IN stoppers THEN BEGIN typel_ptr := NIL; read_create := NIL END ELSE (* more arguments to read *) BEGIN test_correct_comma(first); new(typel_ptr); typel_ptr^.first := und_type; new(result); WITH result^ DO BEGIN first := exp_expression(typel_ptr^.first,followers); WITH typel_ptr^.first DO IF type_name = NIL THEN type_of_arg.length := 0 ELSE type_of_arg := type_name^.name; result^.rest := read_create(typel_ptr^.rest,FALSE) END; read_create := result END END; FUNCTION number_of(args : pltype) : integer; (* This function finds the number of arguments represented by the list of *) (* types args. *) VAR result : integer; BEGIN result := 0; WHILE args <> NIL DO BEGIN result := result + 1; args := args^.rest END; number_of := result END; BEGIN (* of exp_sem_func_expr *) followers := followers + [comma_sym,close_sym]; stoppers := followers + [end_of_file] - [comma_sym]; infollowers := followers + [case_sym,some_sym]; new(result,e_func); WITH result^ (* RECORD func : pnode; args : plexpr END (part of texpr!!) *) DO BEGIN kind := e_func; IF hsym.length = 0 THEN (* no name found *) BEGIN add_level_info('<MISSING>( '); missing(n_func); func := NIL; args := read_simple(TRUE) END ELSE (* a name is found *) IF ident <> NIL THEN (* name is defined *) BEGIN func := ident; add_level_info(ident^.name + '('); IF ident^.kind = n_func THEN (* name defined as function *) BEGIN test_types(ident^.type_of_func, type_ptr); args := read_test(ident^.args, TRUE) END ELSE (* name not defined as function *) args := read_simple(TRUE) END ELSE (* name is not yet defined *) BEGIN add_level_info(hsym + '('); error(e_nyf); insert_name(nametree, hsym, ident, n_func); func := ident; args := read_create(ident^.args, TRUE); ass_conc_type(ident^.type_of_func, type_ptr); ident^.nr_of_arg := number_of(ident^.args); conclude(ident) END; line_nr := start_line END; expect(inpas, close_sym, skip, ')'); exp_sem_func_expr := result END; FUNCTION exp_case_expr(VAR type_ptr : ttype; hpartname_nr : integer; followers : symbol_set) : pexpr; (* This function reads a case expression, and returns the result. *) (* The grammar is: *) (* *) (* case expression *) (* : case symbol , part name , of symbol *) (* , (selector , colon symbol , expression)CHAIN semicolon symbol *) (* , (semicolon symbol , colon symbol , expression)OPTION *) (* , esac symbol *) (* . *) (* selector : element LIST . *) (* *) (* This function takes care of the testing of the types by proceeding the *) (* variable type_ptr to the function that reads the expressions. The *) (* selectors are read and tested by the procedure exp_selectors. The case *) (* symbol and the partname have been read. The partname is represented by *) (* hpartname_nr. *) VAR expr_ptr : pexpr; st_of_alt , st_of_expr , inselfollowers : symbol_set; FUNCTION exp_case_alt(previous : elements) : plalt_expr; (* This procedure reads one alternative of a case expression. *) (* The alternative has the grammar: *) (* *) (* alternative : (selector ; others symbol) , colon symbol , expression . *) (* *) VAR altexpr_ptr : plalt_expr; BEGIN skip_rubbish(inpas, st_of_alt); IF scansym IN st_of_alt THEN (* more alternatives *) BEGIN new(altexpr_ptr); WITH altexpr_ptr^ (* = RECORD selectors : plnode; expr : pexpr; *) (* rest : plalt_expr END (???) *) DO BEGIN line_nr := input_line_nr; IF scansym = others_sym THEN BEGIN other_sel := TRUE; selectors := [0..max_elem_nr] - previous; nextsym(inpas) END ELSE BEGIN other_sel := FALSE; exp_selectors(selectors,previous,inselfollowers) END; back_on_the_rails(inpas, [colon_sym], st_of_expr, ':', m_sex, skip); expr := exp_expression(type_ptr, followers); skip_rubbish(inpas, followers); IF NOT other_sel AND (scansym = semicolon_sym) THEN BEGIN nextsym(inpas); rest := exp_case_alt(previous) END ELSE rest := NIL END; exp_case_alt := altexpr_ptr END ELSE exp_case_alt := NIL END; BEGIN (* of exp_case_expr *) followers := followers + [semicolon_sym,esac_sym]; st_of_expr := followers + [case_sym,some_sym,of_sym,open_sym]; inselfollowers := st_of_expr + [comma_sym,colon_sym]; st_of_alt := followers + [others_sym,some_sym,comma_sym,colon_sym]; new(expr_ptr,e_case); WITH expr_ptr^ DO BEGIN add_level_info('CASE ' + partname_with(hpartname_nr) + ' OF'); kind := e_case; headpnnr := hpartname_nr; back_on_the_rails(inpas, [of_sym], st_of_alt, 'OF', m_sex, skip); alter := exp_case_alt([]); IF alter = NIL THEN error(e_nae); line_nr := start_line END; exp_case_expr := expr_ptr; back_on_the_rails(inpas, [esac_sym],followers,'ESAC', m_sex, skip) END; BEGIN (* of exp_expression *) enter_level('expression'); skip_rubbish(inpas, [case_sym,some_sym,of_sym,open_sym,main_sym]+followers); start_line := input_line_nr; IF scansym = case_sym THEN (* " CASE " found *) BEGIN nextsym(inpas); res := exp_case_expr(type_ptr, exp_test_partname, followers) END ELSE BEGIN IF scansym = some_sym THEN (* name found *) BEGIN hsym := sym; IF get_name(nametree, sym, ident) THEN (* name is defined *) IF ident^.kind IN [n_attr,n_func] THEN (* name defined as attribute or function *) partname_nr := error_part_nr ELSE (* try whether it is a partname *) partname_nr := nr_of_partname(sym) ELSE (* try whether it is a part name *) partname_nr := nr_of_partname(sym); nextsym(inpas) END ELSE (* no name is found *) BEGIN ident := NIL; hsym.length := 0; IF scansym = main_sym THEN (* mainpartname found *) BEGIN partname_nr := main_part_nr; nextsym(inpas) END ELSE partname_nr := error_part_nr END; (* hsym holds the alfa representation, if a name is found. *) (* ident holds a pointer to a node, if a defined name is found. *) (* partname holds an correct partname number if a partname is *) (* found that in case of a name is not defined as a attribute or *) (* a function. *) skip_rubbish(inpas, [of_sym,open_sym,case_sym,some_sym,main_sym]+followers); CASE scansym OF of_sym : IF partname_nr = error_part_nr THEN (* name "OF" found *) BEGIN nextsym(inpas); res := exp_simple_expr(type_ptr, ident, exp_test_partname) END ELSE (* partname "OF" found *) BEGIN error(m_sex,'CASE'); res := exp_case_expr(type_ptr, partname_nr, followers) END; open_sym : (* (name, "#" or nothing) "(" found *) BEGIN nextsym(inpas); res := exp_sem_func_expr(type_ptr, ident, hsym, followers) END; main_sym : (* (name or "#") "#" found *) BEGIN error(m_sex, 'OF'); nextsym(inpas); res := exp_simple_expr(type_ptr, ident, main_part_nr) END; some_sym : (* (name, or "#") name found *) BEGIN partname_nr := nr_of_partname(sym); IF partname_nr = error_part_nr THEN (* (name or "#") partname found *) BEGIN error(m_sex, 'OF'); res := exp_simple_expr(type_ptr, ident, partname_nr) END ELSE (* (name or "#") no-partname found *) BEGIN error(m_sex, '('); res := exp_sem_func_expr(type_ptr, ident, hsym, followers) END END; case_sym : (* (name, "#") "CASE" found *) IF test_kind(ident, n_func) THEN (* function-name "CASE" found *) BEGIN error(m_sex, '('); res := exp_sem_func_expr(type_ptr, ident, hsym, followers) END ELSE (* (no-function-name or "#") "CASE" found *) BEGIN error(s_rbc); res := exp_case_expr(type_ptr, exp_test_partname, followers) END OTHERWISE BEGIN error(s_ure); res := NIL END END END; exp_expression := res; exit_level END; |
(* The procedures on this page are for reading the attribute assignments. *) (* The grammar is: *) (* *) (* attribute assignments *) (* : open bracket symbol *) (* , attribute assignment LIST *) (* , close bracket symbol *) (* . *) (* attribute assignment *) (* : attribute occurrence , equal symbol , expression *) (* ; selective assignment *) (* . *) FUNCTION exp_list_attr_ass(followers:symbol_set; topnivo:boolean; VAR allowedpn:alfa; VAR correctpn:boolean ):plattr_ass; (* This function reads a list of attribute assignments by calling the *) (* procedures exp_simp_ass and exp_sel_ass. *) (* The variables allowedpn and correctpn are used by the process that finds *) (* a correct partname within a selective assignment, as described in the *) (* procedure below. The boolean topnivo is TRUE if this procedure is called *) (* outside a selective assignment, otherwise FALSE. This is because the two *) (* are somewhat different concerning the partnames that are allowed. *) VAR result : plattr_ass; hulp_sym : alfa; hpartname_nr : integer; name_ptr : pnode; start_line : integer; PROCEDURE process_partname(test : boolean); (* This procedure simulates a process which seeks the first correct *) (* partname in a selective assignment and does not generate superfluous *) (* error messages. The boolean test is TRUE if a correct partname is *) (* found. *) BEGIN IF hulp_sym <> error_partname THEN (* a alfa representation of partname has been read *) IF allowedpn = undef_partname THEN (* still no alfa representation of a partname found *) BEGIN allowedpn := hulp_sym; IF test THEN (* a correct partname found *) correctpn := TRUE ELSE (* name was not a correct partname *) error(e_npn) END ELSE (* an alfa representation of a partname has been found before *) IF allowedpn <> hulp_sym THEN (* that alfa representation is unequal the last found *) IF test THEN (* The last one is a correct partname *) BEGIN error(w_wpc); correctpn := TRUE; allowedpn := hulp_sym END ELSE (* The last one is not a correct partname *) error(e_wpn) END; PROCEDURE exp_simp_ass(attr_ptr:pnode); (* This procedure reads a simple attribute assignment, with the grammar: *) (* *) (* simple attribute assignment : *) (* : attribute occurrence , equals symbol , expression . *) (* *) (* This procedure tests the whether the attribute is used as an applied *) (* attribute and the type of the attribute is given to the procedure that *) (* reads the expression. The attribute name of the attribute occurrence *) (* and the of symbol have been read. *) VAR hulp_part_nr : integer; BEGIN back_on_the_rails(inpas, [of_sym], [some_sym,main_sym,equal_sym,case_sym] + followers,'OF', m_sex, skip); hulp_sym := exp_partname; hulp_part_nr := nr_of_partname(hulp_sym); add_level_info(name_of(attr_ptr) + ' OF ' + partname_with(hulp_part_nr) + ' := '); IF topnivo THEN (* outside any selective assignments *) BEGIN IF hulp_part_nr = error_part_nr THEN error(e_npn) END ELSE (* within a selective assignment *) process_partname(hulp_part_nr <> error_part_nr); IF test_kind(attr_ptr,n_attr) THEN assigned_attr(attr_ptr, hulp_part_nr); back_on_the_rails(inpas, [equal_sym], [case_sym,some_sym] + followers, '=', m_sex, skip); new(result,a_simp); WITH result^ (* RECORD attr : pnode; partname_nr : integer *) (* expr : pexpr END (part of tlattr_ass !!) *) DO BEGIN kind := a_simp; attr := attr_ptr; partnamenr := hulp_part_nr; IF test_kind(attr,n_attr) THEN expr := exp_expression(attr^.type_of_attr,followers) ELSE expr := exp_expression(err_type, followers); line_nr := start_line END END; PROCEDURE exp_sel_ass(hpartname_nr:integer); (* This procedure reads a selective assignment using the grammar : *) (* *) (* selective assignment *) (* : case symbol , part name , of symbol *) (* , ( selector , colon symbol , attribute assignment LIST *) (* )CHAIN semicolon symbol *) (* , ( semicolon symbol , others symbol , colon symbol *) (* , attribute assignment LIST *) (* ) OPTION *) (* , esac symbol *) (* . *) (* selector : element LIST . *) (* *) (* The selectors are read and tested by the procedure exp_selector. The *) (* case symbol and the partname have been read. The partname is *) (* partname is represented by hpartname_nr. *) VAR st_of_ass , st_of_alt , inselfollowers : symbol_set; FUNCTION exp_sel_alt(previous : elements) : plalt_ass; (* This procedure reads one alternative of a selective assignment, with *) (* the grammar : *) (* *) (* alternative : (selector ; others symbol) , colon symbol *) (* , attribute assignments LIST *) (* . *) VAR altass_ptr : plalt_ass; BEGIN back_on_the_rails(inpas, st_of_alt, followers, 'starters of alt', m_sal, do_not_skip); IF scansym IN st_of_alt THEN (* more alternatives *) BEGIN new(altass_ptr); WITH altass_ptr^ (* = RECORD line_nr : integer; *) (* other_sel : boolean; *) (* selectors : elements; *) (* attr_ass : plattr_ass; *) (* rest : plalt_ass END *) DO BEGIN line_nr := input_line_nr; IF scansym = others_sym THEN BEGIN nextsym(inpas); other_sel := TRUE; selectors := [0..max_elem_nr] - previous END ELSE BEGIN other_sel := FALSE; exp_selectors(selectors, previous,inselfollowers) END; back_on_the_rails(inpas, [colon_sym],st_of_ass, ',',m_sex, skip); attr_ass := exp_list_attr_ass(followers, FALSE, allowedpn, correctpn); skip_rubbish(inpas, followers); IF NOT other_sel AND (scansym = semicolon_sym) THEN BEGIN nextsym(inpas); rest := exp_sel_alt(previous) END ELSE rest := NIL END; exp_sel_alt := altass_ptr END ELSE exp_sel_alt := NIL END; BEGIN (* of exp_sel_ass *) expect(inpas, of_sym, skip, 'OF'); followers := followers + [esac_sym,semicolon_sym]; st_of_ass := followers + [case_sym,some_sym,of_sym,main_sym, equal_sym]; st_of_alt := [others_sym,some_sym,comma_sym,colon_sym]; inselfollowers := st_of_ass + [some_sym,comma_sym,colon_sym]; new(result,a_sele); WITH result^ DO BEGIN kind := a_sele; IF topnivo THEN (* outside any selective assignments *) BEGIN allowedpn := undef_partname; correctpn := FALSE END; add_level_info('CASE ' + partname_with(hpartname_nr) + ' OF'); alter := exp_sel_alt([]); headpnnr := hpartname_nr; IF alter = NIL THEN error(w_naa); IF topnivo THEN (* outside any selective assignment *) IF correctpn THEN headpnnr := nr_of_partname(allowedpn) ELSE headpnnr := error_part_nr; back_on_the_rails(inpas, [esac_sym],followers,'ESAC',m_sex, skip); line_nr := start_line END END; BEGIN (* of exp_list_attr_ass *) IF scansym IN followers THEN (* no more attribute assignments *) exp_list_attr_ass := NIL ELSE BEGIN back_on_the_rails(inpas, [case_sym,some_sym,of_sym,main_sym,equal_sym, comma_sym], followers, 'starters of ass', m_saa, do_not_skip); start_line := input_line_nr; IF scansym IN followers THEN (* no more attribute assignments *) exp_list_attr_ass := NIL ELSE IF scansym = comma_sym THEN BEGIN error(s_scm); nextsym(inpas); exp_list_attr_ass := exp_list_attr_ass(followers,topnivo,allowedpn,correctpn); END ELSE BEGIN enter_level('assignments'); IF scansym = case_sym THEN (* "CASE" found *) BEGIN nextsym(inpas); exp_sel_ass(nr_of_partname(exp_partname)) END ELSE IF scansym = some_sym THEN (* name found *) BEGIN IF get_name(nametree, sym, name_ptr) THEN (* name defined *) IF name_ptr^.kind = n_attr THEN hpartname_nr := error_part_nr ELSE hpartname_nr := nr_of_partname(sym) ELSE hpartname_nr := nr_of_partname(sym); hulp_sym := sym; nextsym(inpas); IF hpartname_nr = error_part_nr THEN exp_simp_ass(name_ptr) ELSE (* partname found *) exp_sel_ass(hpartname_nr) END ELSE (* no name found *) exp_simp_ass(NIL); skip_rubbish(inpas, [comma_sym,case_sym,some_sym]+followers); IF scansym = comma_sym THEN nextsym(inpas) ELSE IF scansym IN [case_sym,some_sym] THEN error(m_sex, ','); exit_level; result^.rest := exp_list_attr_ass(followers,topnivo, allowedpn,correctpn); exp_list_attr_ass := result END END END; |
(* The procedures on this page are for reading and testing the rules, using *) (* the grammar : *) (* *) (* grammar : rules symbol , rule SEQ . *) (* rule : (tree rule ; class rule) , period symbol . *) (* *) (* The following procedures are used for testing and constructing some *) (* relations between the rules. *) FUNCTION c_in_clos_c_or_and(class,from_elem:pnode) : boolean; (* This function tests whether class and from_elem are equal or whether *) (* class in closure of from_elem. This function is used when testing *) (* recursive class definitions, which are not allowed. *) VAR elemnr : integer; elems : elements; found : boolean; BEGIN enter_level('test rec classes'); add_level_info('from ' + from_elem^.name); IF class = from_elem THEN c_in_clos_c_or_and := TRUE ELSE WITH from_elem^ DO IF (kind <> n_class) OR (class_rule = []) THEN c_in_clos_c_or_and := FALSE ELSE (* from_elem is a class and has a class rule *) BEGIN found := FALSE; elems := class_rule; elemnr := start_nr; REPEAT next_elem(elemnr, elems); found := c_in_clos_c_or_and(class,element[elemnr]) UNTIL found OR (elems = []); c_in_clos_c_or_and := found END; wait(0.8); exit_level END; PROCEDURE test_consistency(elem_ptr:pnode); (* This procedure tests whether the tree production is consistent with *) (* earlier definitions. This is done by propagating a new tree rule along *) (* the class rules. If at some element two tree productions are defined *) (* a fatal error message is generated. *) VAR elemnr : integer; class : elements; tree : ptree_rule; found : boolean; BEGIN enter_level('test consistency'); add_level_info('from ' + elem_ptr^.name); WITH elem_ptr^ DO IF (class_rule <> []) AND (tree_rule <> NIL) THEN (* propagate tree rule along class rule *) BEGIN class := class_rule; tree := tree_rule; elemnr := start_nr; REPEAT next_elem(elemnr, class); WITH element[elemnr]^ DO IF tree_rule <> NIL THEN (* there is already a tree rule assigned at element *) IF tree_rule <> tree THEN (* and it is different, which leads to : *) error(f_con) ELSE (* and it is the same, which is allowed *) ELSE (* no tree rule at element[elemnr] placed yet *) BEGIN tree_rule := tree; kind_of_rule := r_indi; test_consistency(element[elemnr]) END; UNTIL class = [] END; wait(0.8); exit_level END; (* The following procedures are for reading the rules *) PROCEDURE exp_rule; (* This procedure reads one tree rule or one class rule, using the grammar: *) (* *) (* rule : (class rule ; tree rule) , period symbol . *) (* class rule *) (* : class , equal symbol *) (* , open class symbol , element LIST , close class symbol *) (* . *) (* tree rule *) (* : element , arrow symbol *) (* , (part name , colon symbol , element)LIST *) (* , attribute assignments OPTION *) (* . *) (* *) (* This procedure decides what kind of rule there is, and calls one of the *) (* procedures: exp_tree_rule or exp_class_rule. *) VAR elem_ptr : pnode; starters , infollowers : symbol_set; PROCEDURE exp_tree_rule(elem_ptr:pnode); (* This procedure reads one tree rule, including the attribute *) (* assignments. *) VAR hulpattr_ass : plattr_ass; allowedpn : alfa; correctpn : boolean; FUNCTION exp_tree_def : ptree_rule; (* This procedure reads partnames and the element that describe the *) (* tree rule. It checks whether the partnames are unique and that the *) (* elements are elements. Is also tested whether this rule, when added, *) (* causes double tree productions. *) (* Not more than max_part_nr parts are allowed. If exceeded an error is *) (* generated. *) VAR result , lastchain : ptree_rule; hpartname : alfa; elem_ptr : pnode; infollowers : symbol_set; nr_of_parts : integer; (* number of parts minus 1 *) PROCEDURE add_at_end; (* of result *) (* This procedure adds an new part at the end of the definition. *) PROCEDURE make_new_chain(VAR hulp:ptree_rule); BEGIN new(hulp); WITH hulp^ DO BEGIN partname := hpartname; element := elem_ptr; rest := NIL END; lastchain := hulp END; BEGIN (* of add at end *) nr_of_parts := nr_of_parts + 1; IF nr_of_parts = max_part_nr THEN (* insertion of the max_part_nr + 1th partname *) error(i_tmp); IF result = NIL THEN make_new_chain(result) ELSE make_new_chain(lastchain^.rest) END; FUNCTION test_partnames(list:ptree_rule):boolean; (* This functions tests whether the partnames are not used more than *) (* once, by checking whether the new partname has been used in this *) (* tree rule before. *) VAR result : boolean; BEGIN result := TRUE; WHILE result AND (list <> NIL) DO BEGIN result := (list^.partname <> hpartname); list := list^.rest END; test_partnames := result END; BEGIN (* of exp_tree_def *) infollowers := [comma_sym,some_sym,period_sym,open_bracket_sym, case_sym,of_sym,root_sym]; result := NIL; skip_rubbish(inpas, infollowers); nr_of_parts := -1; WHILE scansym IN [some_sym,colon_sym,comma_sym] DO BEGIN IF scansym = some_sym THEN BEGIN hpartname := sym; nextsym(inpas) END ELSE BEGIN error(m_pna); hpartname.length := 0 END; back_on_the_rails(inpas, [colon_sym],infollowers, ',', m_sex, skip); elem_ptr := exp_element; IF hpartname.length = 0 THEN IF elem_ptr = NIL THEN (* no partname and no element found *) error(w_nat) ELSE (* element without partname found *) add_at_end ELSE IF test_partnames(result) THEN add_at_end ELSE (* double partname found *) error(e_dpn); exp_comma; skip_rubbish(inpas, infollowers) END; exp_tree_def := result END; BEGIN (* of exp_tree_rule *) enter_level('tree rule'); partnames := exp_tree_def; skip_rubbish(inpas, [open_bracket_sym,case_sym,some_sym,period_sym,root_sym]); IF scansym IN [open_bracket_sym,case_sym,some_sym] THEN (* attribute assignments found *) BEGIN expect(inpas, open_bracket_sym, skip, '['); hulpattr_ass := exp_list_attr_ass ([comma_sym,close_bracket_sym,period_sym,root_sym], TRUE,allowedpn,correctpn); back_on_the_rails(inpas, [close_bracket_sym], [period_sym,root_sym], ']', m_sex,skip) END ELSE hulpattr_ass := NIL; IF elem_ptr = NIL THEN (* no element for tree rule was found *) error(w_itl) ELSE WITH elem_ptr^ (* RECORD tree_rule : ptree_rule; *) (* kind_of_rule : trule_kind; *) (* attr_ass : plattr_ass *) (* END (part of tnode!!) *) DO IF (kind_of_rule IN [r_undf,r_empt]) AND (attr_ass = NIL) THEN (* no earlier tree rule at this element *) BEGIN IF partnames <> NIL THEN (* a non-empty tree rule read *) BEGIN tree_rule := partnames; kind_of_rule := r_dire; END ELSE (* an empty tree rule read *) BEGIN IF hulpattr_ass = NIL THEN (* also no attribute assignments found *) error(w_etr); kind_of_rule := r_empt END; (* add tree rule and test consistency: *) attr_ass := hulpattr_ass; test_consistency(elem_ptr) END ELSE IF kind_of_rule = r_indi THEN (* indirect tree production at this element *) BEGIN tree_rule := partnames; attr_ass := hulpattr_ass; error(f_itr); kind_of_rule := r_dire END ELSE (* there has been a tree rule at this element before *) error(f_rtr); write_elapsed_time; exit_level END; PROCEDURE exp_class_rule(class_ptr:pnode); (* This procedure reads a class rule and adds it to the other definitions *) (* and checks if no double tree productions are generated. The class name,*) (* the equal symbol and the open class symbol have been read. It is *) (* tested whether the class name really is defined as a class. If *) (* not, or the class has already a class rule, the rest of the rule is *) (* skipped, without checking the syntax. *) FUNCTION exp_cl_elements : elements; (* This function reads and tests the elements, defining the class, as a *) (* list-construct. Warnings are generated when elements appear more *) (* then once. Elements are not added at the definition, if they cause a *) (* recursive class definition or when they were already in a class rule.*) VAR result : elements; elem_ptr : pnode; infollowers : symbol_set; PROCEDURE add_element_in_class; (* This procedure adds an element in the set of elements. If it is *) (* already in another class, or if it will generate a recursive class *) (* definition, it will not be added and an error message is given. *) (* a warning is generated if the element was already in the set. *) VAR nr : integer; BEGIN nr := elem_ptr^.elem_nr; IF nr <> error_nr THEN IF nr IN result THEN error(w_erc) ELSE IF (elem_ptr^.parent <> []) AND with_orthogonal THEN error(f_etc) ELSE IF c_in_clos_c_or_and(class_ptr, elem_ptr) THEN error(f_rec) ELSE BEGIN result := result + [nr]; WITH elem_ptr^ DO parent := parent + [class_ptr^.elem_nr] END END; BEGIN (* of exp_cl_elements *) infollowers := [comma_sym,some_sym,close_class_sym,period_sym,root_sym]; skip_rubbish(inpas, infollowers); result := []; WHILE scansym IN [some_sym,comma_sym] DO IF scansym = some_sym THEN BEGIN elem_ptr := exp_element; IF elem_ptr <> NIL THEN add_element_in_class; exp_comma END ELSE sup_comma; exp_cl_elements := result END; BEGIN (* of exp_class_rule *) enter_level('class rule'); is_class(class_ptr); IF class_ptr = NIL THEN (* skip rest of the rule *) skip_rubbish(inpas, [close_class_sym,period_sym,root_sym]) ELSE WITH class_ptr^ DO IF class_rule = [] THEN (* no class rule defined at this class yet *) BEGIN class_rule := exp_cl_elements; test_consistency(class_ptr) END ELSE (* there has been a class rule with this class before *) BEGIN error(f_rcr); skip_rubbish(inpas, [close_class_sym,period_sym,root_sym]) END; back_on_the_rails(inpas, [close_class_sym],[period_sym,rules_sym,some_sym, becomes_sym,equal_sym],'}', m_sex, skip); exit_level END; BEGIN (* of exp_rule *) infollowers := [some_sym,equal_sym,becomes_sym,colon_sym,open_class_sym, period_sym,open_bracket_sym,case_sym,root_sym]; elem_ptr := exp_element; skip_rubbish(inpas, infollowers); IF scansym = equal_sym THEN (* name "=" found *) BEGIN nextsym(inpas); skip_rubbish(inpas, infollowers); IF scansym = open_class_sym THEN (* name "= {" found *) BEGIN nextsym(inpas); exp_class_rule(elem_ptr) END ELSE IF test_kind(elem_ptr,n_node) THEN (* node-name "=" found *) BEGIN error(s_arr); exp_tree_rule(elem_ptr) END ELSE (* no-node-name "=" found *) BEGIN error(m_sex, '{'); exp_class_rule(elem_ptr) END END ELSE IF scansym = becomes_sym THEN (* name "=>" found *) BEGIN nextsym(inpas); exp_tree_rule(elem_ptr) END ELSE IF scansym = open_class_sym THEN (* name "{" found *) BEGIN error(m_sex, '='); nextsym(inpas); exp_class_rule(elem_ptr) END ELSE IF test_kind(elem_ptr,n_node) THEN (* node-name found *) BEGIN error(m_sex, '=>'); exp_tree_rule(elem_ptr) END ELSE (* nothing that can be recognized as a rule *) BEGIN error(s_urr); skip_rubbish(inpas, [period_sym,root_sym]) END; back_on_the_rails(inpas, [period_sym],[root_sym,some_sym,becomes_sym,equal_sym], '.', m_sex,skip) END; PROCEDURE read_rules; (* This procedure reads the rules from the input. *) VAR infollowers : symbol_set; BEGIN enter_level('rules'); infollowers := [some_sym,period_sym,becomes_sym,equal_sym,open_class_sym, root_sym,end_of_file]; back_on_the_rails(inpas, [rules_sym],infollowers,'RULES', s_mk2,skip); skip_rubbish(inpas, infollowers); WHILE scansym IN [some_sym,becomes_sym,equal_sym,open_class_sym] DO BEGIN exp_rule; skip_rubbish(inpas, infollowers) END; exit_level END; PROCEDURE read_root; (* This procedure reads the root from the input and stores the element in *) (* the variable root. *) BEGIN enter_level('root'); back_on_the_rails(inpas, [root_sym],[],'ROOT', s_mk3,skip); root := exp_element; exit_level END; |
(* This page has the procedure scanner that reads the entire input, *) (* according to the grammar : *) (* *) (* input grammar *) (* : (options symbol , options LIST , period symbol)OPTION *) (* , (classes symbol , class LIST , period symbol)OPTION *) (* , node types symbol , node type LIST , period symbol *) (* , (types symbol , type identifier LIST , period symbol)OPTION *) (* , (functions symbol , semantic function definition SEQ)OPTION *) (* , attributes symbol , attribute definitions SEQ *) (* , (input symbol , interface rule SEQ)OPTION *) (* , (output symbol , interface rule SEQ)OPTION *) (* , rules symbol , rules SEQ *) (* , root symbol , element *) (* . *) PROCEDURE parser; BEGIN (* for performance analysis: *) enter_level('parser'); g_inh_attr := []; g_syn_attr := []; g_input_attr := []; g_output_attr := []; new(error_ptr); perf_start_time(perf_r_o_c_n); read_options; write_elapsed_time; read_classes; read_node_types; perf_end_time (perf_r_o_c_n); perf_start_time(perf_r_t_f); read_types; write_elapsed_time; read_semantic_functions; perf_end_time (perf_r_t_f); perf_start_time(perf_r_a_io); write_elapsed_time; read_attributes; write_elapsed_time; read_input_rules; read_output_rules; perf_end_time (perf_r_a_io); perf_start_time(perf_r_r_r); write_elapsed_time; read_rules; read_root; write_elapsed_time; skip_rubbish(inpas, []); perf_end_time (perf_r_r_r); exit_level END; END. |
In this section we present the complete grammar as being parsed by the procedure parser.
element : class ; node type . rule : ( class rule ; tree rule ) , . . class rule : class , = , { , element LIST , } . tree rule : element , => , (part name , : , element)LIST . class : identifier . node type : identifier . part name : identifier .
attribute definition : attribute name , : , type identifier , (synthesized symbol ; inherited symbol) , OF , element LIST , . . attribute name : identifier . synthesized symbol : SYNTHESIZED ; SYN . inherited symbol : INHERITED ; INH . of symbol : OF .
interface rule : attribute name , ( AT , element LIST)OPTION , . .
semantic function definition : semantic function name , type identifier LIST PACK , : , type identifier , . . semantic function name : identifier .
tree rule : element , => , (part name , : , element)LIST , attribute assignments OPTION .
attribute assignments : [ , attribute assignment LIST , ] . attribute assignment : attribute occurrence , = , expression ; selective assignment .
attribute occurrence : attribute name , OF , node name . node name : part name ; # .
semantic function application : semantic function name , expression LIST PACK .
case expression : CASE , part name , OF , (selector , : , expression )CHAIN ; , ( ; , OTHERS , : , expression )OPTION , ESAC . selector : element LIST .
expression : attribute occurrence ; semantic function application ; case expression .
selective assignment : CASE , part name , OF , ( selector , : , attribute assignment LIST)CHAIN ; , ( ; , OTHERS , : , attribute assignment LIST )OPTION , ESAC .
My life as a hacker | My home page