$<& LIBRARY OF THE UNIVERSITY OF ILLINOIS AT URBANA-CHAMPAICN ^0.421-42(0 top- Z s s /{-l-^f.- Report No. 422 AN ALGORITHM FOR EVALUATING ARRAY EXPRESSIONS IN OL/2 by John Lockhart Latch January 1971 griRRARYDEIHB NOV 9 1972 DEPARTMENT OF COMPUTER SCIENCE UNIVERSITY OF ILLINOIS AT URBANA-CHAMPAIGN URBANA, ILLINOIS Digitized by the Internet Archive in 2013 http://archive.org/details/algorithmforeval422latc Report No. 422 AN ALGORITHM FOR EVALUATING ARRAY EXPRESSIONS IN OL/2 by John Lockhart Latch January 1971 Department of Computer Science University of Illinois at Urbana-Champaign Urbana, Illinois 61801 This work was supported in part by the National Science Foundation under Grant No. US NSF-GJ-328 and was submitted in partial fulfillment for the degree of Master of Science in Computer Science, January 1971. 11 ABSTRACT This thesis is concerned with the evaluation of expressions in the OL/2 array language. OL/2 is a generalized array language based upon natural operations involving vectors, matrices, higher dimensional arrays, and dynamic partitioning of arrays. The technique used for the evaluation of array expressions is divided into three phases: the parsing of source statements into an expression tree, the determination and reduction of temporary storage for intermediate results, and the generation of code to compute the expression. This technique has been implemented using the TACOS compiler-compiler and PL/1 on the IBM SYSTEM/360. Ill ACKNOWLEDGEMENT I wish to thank my advisor, Professor J. R. Phillips for suggesting this thesis topic and for his guidance throughout its preparation. Additional thanks go to the other members of the OL/2 implementation team; Cory Adams, Dale Jurich, Bob Bloemer, and Barry Finkel for their cooperation and suggestions. Furthermore, I am indebted to Mrs. Diana Mercer, who did such a fine job of typing this report, and most expecially to my wife, Fran, for her patience, understanding and encouragement. iv TABLE OF CONTENTS Page 1 . INTRODUCTION 1 2. THE OL/2 LANGUAGE 5 2.1 OL/2 Data Types 5 2.2 Types of Expressions 7 3. ARRAY EXPRESSION EVALUATION 9 3 . 1 Expression Parsing 9 3 . 2 Producing Intermediate Code 12 3.2.1 Determining Temporary Storage Requirements 12 3.2.1.1 Left and Right Subtrees Expressions 14 3.2.1.2 Left Subtree an Expression and Right Subtree a Variable 17 3.2.1.3 Left Subtree a Variable and Right Subtree an Expression 18 3.2.2 Generation of Intermediate Code .... 19 4 . IMPLEMENTATION 2 4 4 . 1 OL/2 Syntax and Semantics 25 4.2 OL/2 Code Generator 35 4 . 3 Intermediate Routines 41 LIST OF REFERENCES 44 APPENDIX A. OL/2 SYNTAX FOR ASSIGNMENT STATEMENTS 45 B. OL/2 SEMANTIC ACTION ROUTINES FOR ASSIGNMENT STATEMENTS AND ARITHMETIC EXPRESSIONS 47 C. OL/2 CODE GENERATOR 61 D. OL/2 ASSIGNMENT STATEMENT EXAMPLES 80 LIST OF TABLES Page Table 1. OL/2 Variable's Root Node 7 2. OL/2 Operator Precedence 10 3 . Tree Node Contents 11 4 . Abbreviations 14 5. Multiply with Both Subtrees Expression 15 6. Multiply with Left Subtree Expression 17 7. Actions for Operator with Left Subtree Expression 18 8. Multiply with Right Subtree Expression 18 9. Operand Partitioning for Multiply 21 10. BNF vs IBNF Definitions 24 1 K Common Data Fields 25 12. Semantic Action Routine Functions 26 13. OL/2 Code Conventions 35 1 4 . Operand Parameters 33 15. OL/2 Intermediate Routines 43 VI LIST OF FIGURES Page Figure 1 . A Partitioned Matrix 6 2. Expression Tree for A = ALPHA+BETAxGAMMAxB. . 9 3. Expression Tree for R = AxB + Cx (D+E) 12 4. Compiling R = AxB + Cx(D+E), step 2 15 5. Compiling R = AxB + Cx(D+E), step 3 16 6. Compiling R = AxB + Cx(D+E), step 5 19 7. Compiling R = AxB + Cx(D+E), step 6 23 8. Parsing Stack for R = A + B after All Operands Recognized 29 9. Parsing Stack for R = A + B after Addition Operator Recognized 30 10. Parsing Stack for R = AxZx ALPHA after First Multiply Is Recognized 31 11. Parsing Stack for R = AxZx ALPHA upon Entry to ACTION_1 7 31 12. Parsing Stack for R = AxZx ALPHA after Execution of ACTI0:i_1 7 32 13. Parsing Stack for R - ALPHA+BETAxGA'IMAx (X, Y) x3 stage 1 33 14. Parsing Stack for R = AL^IIA+BETAxGA'TlAx (X , Y) xB stage 2 34 15. Expression Tree for Inner Product 34 16. Parsing Stack for R = AL^HA+BETAxGAMMAx (X, Y) x3 stage 3 34 17. Parsing Stack for R = 7vL-nHA+BET7\xGAM v lAx (X, Y) xB stage 4 34 18. Expression Tree for R = ALPHAx ( (Ax3) x (CxD) ) . 41 1 . INTRODUCTION Few high level languages have the ability to handle array expressions, especially in the case of matrix algebra. Two major problems in implementing an array language involve an efficient method of calculating array operations and the minimization of temporary storage required during that calculation. The evaluation technique presented in this paper has been implemented as part of the OL/2 language and operates not only on the usual data types for array languages but also on a wider class of array data types which reflect the needs of an array language . Several previous works in the area of array expression evaluation are of interest. Galler and Perlis [1], in 1962, described a method for calculating temporary storage requirements for a class of unparenthesized matrix expressions and proved that the method minimized the temporary storage. Reinfeld [2] discussed a method of evaluating vector expressions elomentwise, with the object of reducing stemporary storage. Wagner [3] considered in detail techniques for producing algorithms for the evaluation of fully parenthesized matrix expressions involving only operands which were square matrices. He considers an algorithm which requires the fewest arrays for holding the matrices, while not requiring more execution time than "standard" matrix expression evaluation. The most general of the previous works was described by Galler and Perlis in their recent book [4] . Their method is to extend the ALGOL language with new "definitions" which are placed in a context table along with the ALGOL construct that will replace it. The definitions are placed in the context table in a way that the precedence is determined when the table is searched from top to bottom. The source language is parsed into a tree and then matched with the syntax table. When a match occurs, the tree is changed to the desired construct. After completing translation through the context table, an expression will be represented bv a tree which can be coded in ALGOL. This method seems applicable mainlv to rectangular arrays. The authors do not state whether this system has been implemented, nor do they discuss the temporary storage requirements. The array evaluation technique presented here operates on a more generalized set of data types than any of these prior works. The OL/2 language allows the declaration of vectors, various matrices, and various higher dimensional arrays. In particular, the two dimensional matrices may be defined to have a specific geometric type such as lower triangular, upner triangular, tridiagonal , diagonal, and others where onl^ the non zero elements are stored. The ooerators that are allowed in OL/2 include addition, subtraction, multiplication, division, exponentiation by a scalar, as well as norm, and inner product of vectors. To efficiently perform such operations involving different types of operands a philosophy was adopted to develop some general assembly language routines which could be optimized for array operations . We consider the evaluation of array expressions in two parts: first the parsing of the expression into a binary tree, and second the generation of the code to compute the expression. OL/2 expressions are parsed into a binary tree by using the operator precedence relations defined in section 3. Each node of the tree contains sufficient information for code generation, including the type of node (operator or variable) , the type of expression (matrix, vector or scalar), and number of dimensions. The second section determines temporary storage requirements and generates the code to calculate the expression. The aim is to reduce temporary storage as much as possible without performing more operations than are required by "standard" matrix evaluation, and without degrading the performance of the assembly language calculating routines. The amount of temnorary storage is determined as the algorithm moves systematicallv down the expression tree. Whenever possible the calculation is organized so that row nartitions or column partitions of array variables are used at each stage of the calculation. The partition is controlled by a loop. This method reduces the temporary storage to a reasonable, if not optimal level. The code generated consists of calls to intermediate routines, v/hich in turn call the assembly language calculating routines. The code is produced when the terminal nodes are reached. It is also at this stage that the algorithm determines vzhether to partition the operands. 2. THE OL/2 LANGUAGE The 01/2 language allows a variety of data types. The usual matrix operations are part of the language and include vector inner product and norm. Other interesting operations such as dynamic partitioning of arrays are also provided [5] . For a description of the use of these features we refer to [6] . 2.1 OL/2 Data Types An OL/2 variable may be declared as a vector, a two dimensional matrix, or a higher dimensional array. The bounds for each dimension can be specified as constants or scalar expressions in bound pairs, the upper bound only or as the order of a matrix. One of the important features of OL/2 is to allow two dimensional arravs to be defined as a specific geometric type. The basic geometric types are lower triangular, strictlv lower triangular, upper triangular, strictly upper triangular, diagonal, tridiagonal, and rectangular. In each case, only the non- zero elements are stored which reduces the total storage significantly. The user may simoly use the name of an array in any OL/2 statement insuring only the compatability of dimensions; OL/2 will operate properly on any set of geometric types. An OL/2 variable may also be defined as sequence of arrays molulo some number n, and storage is allocated for n arrays. This feature is particularly useful in iterative algorithms [6]. Another important feature of OL/2 is the dynamic partitioning. For example, Figure 1 shows a rectangular matrix A partitioned after rows K and K-1 and after columns J and J-1 . - J-1 - J - A<1 ,1> A<2,1> A<3, 1> K-1 i K 'A<1,2> i I i 4- ' A<2,2> • -t. x I I , A<3,2> , ^ h A<1 ,3> A<2,3> A<3,3> Figure 1 A Partitioned Matrix Each of the partitioned parts can be given a name such as B = A <1,1>, C =A < 3,3> and used in any subsequent OL/2 statement. In certain instances, the partitioned parts may also be declared to be of type row vector or column vector or scalar. In addition to this type of partitioning, one can give new names to specific predefined parts of arrays. For example, if A is an N by N matrix, one could set L equal to the lower tirangular part of A, or set D equal to the diagonal of A, or both. A complete discussion of dynamic partitioning in OL/2 can be found in A.dams [5] . To define these generalized data types at execution time, a structure is created for each OL/2 variable, and is referred to as the root node. Table 1 defines fields for a root node. Field Designator Name .attributes Dimensionality lodulus Type Row Increment Diagonal Increment Origin Partition Pointer Bounds Meaning Name of variable Seven fields which indicate the base, mode, scale, and precision of the variable and whether it is defined, core contained, or a temporary Number of dimensions If variable is an array sequence the modulus, else Geometric type of the variable Number to increment bound to find next row .lumber to increment bound to find next diagonal element Starting core location Pointer to partitioning control information Upper, lower , and extent for each dimension Table 1 OL/2 Variable's Root Node 2 . 2 ypes of Expressions OL/2 includes the usual matrix operations of addition, subtraction, and multiplication for the various data types, as long as the operands are compatible. Exponentiation and scalar multiplication are also defined, as well as the inner product and norm of any vector expression. Also the transpose of any compatible operand or expression is allowed. Finally/ OL/2 allows PL/1 statements along with OL/2 statements in the program. The generality of the data types and operators make OL/2 a powerful algebraic language. To provide for this generality, a design philosophy was adopted which requires the use of intermediate routines. These routines are called by code generated by the compiler and prepare data for the assembly language calculating routines. The advantages to this design philosophy lie in the tremendous flexibility that one has in implementing current data types and adding other data types later. 3. ARRAY EXPRESSION EVALUATION The technique for handling array expressions in OL/2 involves two main procedures : 1 ) parsing the expression into a binary tree and 2) producing the intermediate code to compute the expression. 3. 1 Expression Parsing Array expressions in OL/2 are parsed with respect to the operator precedence relations shown in Table 2. As an expression is parsed, a binary tree of the expression is built with each node containing the information shown in Table 3. It should be emphasised that a string of scalar operations is not parsed but entered into the name field of a node as a string, and the node is considered as a single variable. For example, Figure 2 shows the expression tree generated by parsing A = ALPHA + BETA x GAMMA x B where A and B are arrays and ALPHA, BETA, and GAMMA are scalars. ALPHA + BETA x GAMMA B Figure 2 Expression Tree for A = ALPHA+BETAxGAMMAxB 10 1 i 1 + 1 - 2 ** 3 ( , ) 3 II II 4 x + _ ** / Level OL/2 Symbol Meaning Transpose Uniary Plus Uniary Minus (Negation) Exponentiation Inner Product of two Vectors Uorm of a Vector Operations with Scalar Operands -latrix multiplying Column Vector or Row Vector multinlying latrix 6 x Matrix multiplying Matrix or Column Vector multiplving Row Vector 7 / Matrix or Vector divided bv a Scalar 8 x Scalar multiplving Vector or Vector multiplying Scalar 9 x Matrix multiplying Scalar or Scalar multiplying Matrix 10 + - Axray pluT or minus Array Table 2 OL/2 Operator Precedence 11 Information at Parse Tine Node Field Name Sequence number Number of dimensions Number of temporary variables to free Loop end marker Type of node Type of expression Right link Left link Tor Variables Name of the pointer to the root node or scalar expression Number or expression defining which array of a sequence is to be used Number of dimensions of variable For Operators Null Null Number of dimensions of expression Indicates variable Type of operator Type of variable, i.e., vector, matrix, scalar Link to right subtree Link to left subtree Type of expression, i.e. , vector, matrix, scalar Link to right subtree Link to left subtree Table 3 Tree Mode Contents Figure 3 shows the expression tree for R = AxB + Cx(E+F) (where all variables are compatible matrices) with the name, number of dinensions , ani the type of exoression fields . a. 12 D 2 matrix Figure 3 Expression Tree for R = AxB + Cx(D+E) matrix 3 . 2 Producing Intermediate Code The procedure for generating intermediate code consists of a section which determines temporary storage requirements and a section which generates the required code. 3.2.1 Determining Temporary Storage Requirements The general aim of this section is to reduce the temporary storage required to hold intermediate results 13 computed while evaluating OL/2 array expressions. This is accomplished by organizing the calculation so that row partitions (RP) or column partitions (CP) of array variables are used at each stage and then repeating the calculation via a loop for each RP or CP in the result. The storage requirements are determined and loop controls are generated as the algorithm traverses the expression tree down toward the terminal nodes. Two stacks are employed during this phase of the operation. The first, called the traverse stack, holds the location of the higher level nodes of the tree passed to reach the current node (the "fathers" of the current node). The second, called the result stack, holds the name of the pointer to the root node and the type of result to be used by each operation of a higher level in the tree than the current node. The actions taken at each node of the tree are determined by the type of operator in the current node, by the type of nodes on the left and right subtrees, and by the type of result on the top of the result stack. It is expected that the root node of the expression tree will have the type of node field set to an equal operator. If the type of node on the right subtree is not a variable, the type of expression and name from the node on the left subtree are placed on the result stack. The procedure then moves to the right subtree. If the expression being compiled was that shown in Figure 3, after step 1 the result stack would contain name R and type matrix and the 14 traverse stack would contain the address of node a. The succeeding actions of this procedure can best be described by dividing it into three cases: 1) left and right subtree are both expressions, 2) left subtree is an expression and right subtree is a variable and 3) left subtree is a variable and right subtree is an expression. The abbreviations in Table 4 will be used in the succeeding subsections to describe the actions of this procedure. Abbreviation . Meaning TCV Place name of a temporary column vector on result stack, set type on result stack to column vector. TRV Same as TCV for row vector TM Same as TCV for matrix :JRS Dunlicate top of result stack RLC Generate control for a loop to contain row partitioning of variables CLC Same as RLC for columns Table 4 Abbreviations 3.2.1.1 Left and Right Subtrees Expressions Table 5 shows the actions ta3:en when the current node type is a multiply operator. 15 Type Expression Left Subtree Type Expression on Right Subtree ■"■ Column Vector Row Vector Matrix Column Vector Illegal TCV Walk left Illegal Row Vector TRV Walk left Illegal TRV Walk left Matrix TCV Walk right Illegal TM Walk left Table 5 Multiply with Both Subtrees Expressions When the dimensions of the subtrees differ (i.e., matrix x column vector) the algorithm moves to the subtree of lower dimensionality. Mo loop controls are generated in this case. When the current node type is an addition or subtraction operator, the ton of the result stack is duplicated, except in the event that the types of expressions on both subtrees are matrices, and that the type of result on top the result stack is matrix. In this case, control for a loop to contain column partitioning is generated. The calculation of each of the expressions on the subtrees will be organized to compute a column at each stage so that only a temporary column vector will be needed. In either case, the procedure then moves to the left subtree. For the eximole of ^igure 3, control for a column partitioning loop '-^oull be generated and we would move to the left subtree. The contents of the stacks at step 2 are shown in Figure 4. Result Stack TE<1P_C0LUMM1 column R matrix Traverse Stack address node b address node a Figure '\ Compiling R = AxB + Cx(D+E), step 2 16 In our example, code would now be generated to compute TEMP_C0LUMN1 = A x Column (B) . This is to be discussed fully in section 3.22. It suffices to state here that the expression tree and the stacks would appear as in Figure 5. a. TEMP_C0LU*1N1 2 column Result Stack Traverse Stack R matrix address node a Figure 5 Compiling R = \xB + Cx(D+E) step 3 17 For a node of type inner product operator, a temporary column vector name is placed on the result stack and the algorithm moves to the left subtree. None of the other operators can appear in a node with left and right subtrees both expressions. For division and expontiation the right subtree type expression must be scalar and therefore must be a variable. 3.2.1.2 Left Subtree an Expression and Right Subtree a Variable After completing processing at the current node, the algorithm will always move to the left subtree to compute the expression. Table 6 defines the actions taken when the current node type is a multiply operator. Type of Variable on Right Subtree Type Expression Loft Subtree Scalar Column Vector Row Vector Matrix Column Vector DRS Illegal TCV Illegal Row Vector DRS TRV Illegal TRV latrix CLC TCV RLC T RV Illegal RLC TRV Table 6 Multiply with Left Subtree Expression For the multiply onerator there are three combinations which lead to the partitioning of operands. For the cases matrix x scalar and matrix x matrix the type of result on top of the result stack must be matrix and there can not be a partitioning loon in effect. For the cases of scalar multiplication, no additional temporarv storage is necessary. Table 7 shows the actions taken for the other types of operators. 18 Type of Operator Addition Subtraction Inner Product Division (Matrix Result) Action DRS DRS TCV CLC TCV Division (other) DRS Exponentiation TM Table 7 Actions for Operators .;ith Left Subtree Expression The case described in this subsection does not appear in the example Figure 3. 3.2.1.3 Left Subtree a Variable and Right Subtree an Expression In this case, after completing processing at the current node, the algorithm always moves to the right subtree to calculate the expression. Table 8 shows the actions taken when the tv~>c of the current node is a multiply operator. Type Expression on Right Subtree j Column Vector . Row Vector Scalar DRS Type of Variable on Column Vector Illegal TRV Right Subtree ' Row Vector TCV Illegal Matrix TCV Illegal 'latrix CLC TCV Illegal CLC TCV CLC TCV Table 8 Multiply with Right Subtree Expression 19 The actions taken in this case are similar to those in the case discussed in the last section. When the current node type is an addition or subtraction operator, the ton of the result stack is duplicated, unless the left subtree is a row or column partition, in which case a tempory column vector name is placed on the result stack. For an inner product, or norm, a temporary column vector is also used. After the last stage of processing, our example v/as as shown in Figure 5. Since the left subtree is a partitioned column, at step 4 a temporary column vector name is placed on the result stack, and the address of node is placed on the traverse stack. In step 5 another temporary column name is placed on the result stack and the address of node d is placed on the traverse stack. After step 5 the stacks appear as shown in Figure 6. Result Stack TEMP_C0LUMN3 column TEMPJC0LUMN2 column R matrix Traverse Stack address node d address node b address node a Figure 6 Compiling R = AxB + Cx(D+E) step 5 3.2.2 Generation of Intermediate Code This section of the array exoression evaluation algorithm generates the intermediate code when the nodes of both subtrees are of type variable. This code consists of statements which call the intermediate routines. The major parameters for these intermediate routines are the pointer to the root node, the type of operand and the row 2t or column number to use if the operand or result is to be partitioned. The pointer to the root for the operand is available for the name field of the nodes of the tree, as is the type of operand. For the result, this data is available from the result stack. The row or column number for operands to be partitioned is the name of the variable used to control the partitioning loop. The major function of this part of the program is to determine when to partition an operand or result. This determination is made by utilizing the type of result from the result stack and the type of expression fields from the left and right subtree nodes. Table 9 shows the parameters generated for the multiply operation. The general format for this table is : Type of Result Type left operand Partition type Type right operand Partition type Type of result Partition type For addition and subtraction, the type of result from the result stack, the type of the operands, and the number of dimensions of the operands determine whether an operand is to be partitioned. The number of dimensions' field in the tree node is used to distinguish between operands which are "true" vectors and vectors which arise from operands being partitioned by this evaluation technique. If the dimension is one then the variable is a "true" vector and will not be partitioned; otherwise, 21 Scalar Column Vector Row Vector Matrix C o 1 u ni n V e c t o r R o w V e c t o r Column Column Scalar None Scalar None Column None Column Col Illegal Column None Column None Row . Scalar None Row Scalar None Row None Row Row Row None Row None Matrix Matrix Matrix Scalar None Scalar None Scalar None Column None Row None Matrix None Column Col Row Row Matrix None Column Column Vector Vector Column None Matrix Scalar None Column Col Column None Column . ) Row 1 i Illegal Vector Row Illegal ! Row t Matrix None ( Row None ! Matrix Matrix Column None Matrix None ] Scalar None Matrix None 1 Column Col Matrix None ; Row Row Vector Vector Row None Column Row None Scalar None Vector Matrix None Row None Row Column None Illegal Row None Matrix None Row Matrix Row None Row None Scalar None Matrix None Row 1 Row Row Row ! Column Column . ,,, Column Vector Vector Vector Column Col Matrix None Matrix None Scalar None Column None Column Col Column None Column None Column None Row Row Vector Vector Row Row Illegal Row Row Scalar None Matrix None Row None Row None j Matrix Matrix Matrix Matrix None Matrix None Matrix None Scalar None Column None Matrix None Matrix None Column Col Matrix None Table 9 Operand Partitioning for Multiply 22 the operand may be partitioned. The result of the operation will be partitioned if it is of type matrix and the operands are of type vector. The intermediate code for computing inner products and norm consists of a scalar assignment statement. The computation is accomplished by a function subroutine and is assigned to a temporary scalar variable. For the division operator, the parameters for the left operand and result are determined in the same manner as for addition. The right operand is alv/ays a scalar. After the intermediate code is generated, the type of node field in the current node is changed to variable and the name field is changed to the name on the top of the result stack. The result stack is then popped, and any temporary storage used on the level of the tree below is marked to be freed. Next, the algorithm moves to the node on the top of the traverse stack and pops the traverse stack. We may now complete our example. The algorithm is now at node e and finds both subtrees are variables. Since the type of result is a column (see Figure 6) and the number of dimensions of both variables is two (see Figure 5), both variables must be partitioned into columns. Codezis then generaged to add a column of D to a column of E each time through the loop. The name field of node e is changed to the name on the top of the result stack, 23 and the result stack is popped. The algorithm moves to the node on the top of the traverse stack and popszthis stack. At this stage of the process the stacks and the tree are as shown in Figure 7. a. matrix Figure 7 Compiling R = Ax3 + Cx(D+E) sten 6 The algorithm then follows the actions outlined in Table 3.8; it multiplies the matrix C times the temporary column vector and stores the result in TE*1P_C0LUMN2. Mode d is then changed to this variable and the algorithm pops the stacks and mov^s to node b. Here the two temporary column vectors are added and the result stored in the proper column partition of R. 24 4 . IMPLEMENTATION The OL/2 language has been implemented by using the TACOS compiler-compiler developed by Gaffney [7 ]. The syntax of the source language is specified in a modified BNF form, called IBNF. Productions are written as Phrase class ::= definition. For example, TERM ::= FACTOR TERM x FACTOR. To avoid left recursive phrase class definitions, such as the example above, TACOS uses repetition characters +, *, and ? which are defined in Table 10. A 1 ; = B A B A • : = empty A 3 A • ; = empty B IBNF A : := B + A : := B * A : := B 7 Table 10 BNF vs IBNF Definitions There are several special notations used to define a language for TACOS: *I causes an attempt to recognize an identifier (legal to PL/1) , while *N causes an attempt to recognize an unsigned intger. The last important notation for IBNF is #n, where n is an integer. This causes a transfer to a seoarately compiled semantic action routine defined by the number n. PL/1 is used as the semantic language for TACOS, so all semantic action routines for a language are placed in a recursive PL/1 procedure. This procedure is given n as a parameter, and it branches to the proper routine via a label array. The semantic action routines and TACOS communicate by sharing the set of common data fields shown in Table 11. ?s Data Field Usage TEMPCONST location of integer recognized by *N notation TEMPIDENT location of identifier recognized by *I notation CHAR source language input area OK success (1) or failure (0) of phrase class indicator INP subscript of CHAR pointing to current input character Table 11 Common Data Fields There are also two external procedures used by the semantic action routines: SCAM UNTIL PASS which scans the input string (CHAR) until one of the characters given as a parameter is found and increments the value of IMP accordingly; and SCAN_UNTIL_KEEP which scans similarly to SCAN_UNTIL_PASS but also places the string passed over into TEMPSTRING. 4.1 OL/2 Syntax and Semantics - Our purpose in this section is to describe some of the more important semantic action routines associated with array expressions. The reader is referred to Appendices A and B for listings of the syntax and action routines for expressions. Other sections of the OL/2 compiler generate data which is used during the evaluation of array expressions. When a variable is recognized in an OL/2 declaration, its name, type, and dimensionality are entered into the OL/2 identifier talbe. Juring this process, a tree node (Table 3) is also built. Later if this variable appears in an assignment statement this node will be used in constructing the tree [ 6 ] . Similar action is also taken when a partitioned part of an array is given a name [5 ] . Table 12 details the major semantic action routines and their function. Name Function ACTI0N_5 Print Source Statement ACTI0N_8 Determines if assignment statement follows, initialization ACTION 12 Builds Expression Tree ACTI0N_16 Determine Multiplication Precedence ACTI0i>J_17 Determine Multiplication Precedence ACTIO^_20 Determine if paren- thesized expression or inner product ACTION_23 Determine if identifier is OL/2 variable ACTION_24 lark beginning of scalar String Called by TACOS TACOS Calls TACOS, #16, #17 #18, #19, #21 #22, #29, #34 #42, #44 TACOS TACOS TACOS TV30S TACOS #12 #12 27 Name Function ACTIO.*J_25 Place sinqle scalar string in name field of tree node ACTION_27 Determine if identifier is OL/2 subscripted variable, or function entry ACTI0N_31 Determine if identifier is OL/2 scalar ACTIOM_3 4 Assign norm, inner product to temporary scalar variable ACTIO>J_39 Determine type of identifier ACTION_4 2 Set node for = operator in tree and call for code generation ACTION_4 8 Insure what follows is an inner product ACTIO:, T _104 Obtain the strinq identifying th Q sequence number ACTION__107 Output statement not recognized as OL/2 or PL/1 ACTI0M_113 Obtain string identifying partition part ACTION 140 Output PL/1 statements Called by TACOS TACOS TACOS TACOS, #36 TACOS TACOS TACOS TACOS TACOS TACOS TACOS Calls #12 CODER #12 CODER Table 12 Semantic Action Routine Functions 28 The semantic routines use a stack, called the parsing stack, to aid in building the expression tree. This stack has two entries: the first is a pointer to a subtree which is an operand of some operator (SUBTREE_PTR) ; and the second is the type of expression of this subtree (SUBTREEJTYPE) . Example 1, R = A + B; , where all the variables are arrays, will be used to illustrate the major steps in the compilation of assignment statements. In the first step for recognizing an ASSIGNMENT__STATEMENT, ACTI0N_8 insures that an equal operator is nresent in the statement. Secondly, we attempt to recognize 0L2_LEFT HAND SIDE by finding an 0L2_IDENTIFIER. The *I notation places the identifier R in TEMPIDENT; then ACTION_23 searches the OL/2 identifier table and determines that R is an OL/2 identifier. ACTION_23 then places the pointer to the tree node and type for R on the parsing stack. The 0L2 IDENTIFIER phrase class has been found and since none of the other constructs following it in 0L2_LEFT_HAND_SIDE are requiredzthe 0L2_LEFT_ HAND_SIDE phrase class has been recognized. VJe next find the euqal operator and start searching for 0L2_ARITHMETIC_EXPRESSI0N. Starting with 0L2JTERM we search down the svntax until 0L2_IDENTIFIER is found. ACTION 2 3 places the tree node pointer and type for A on the parsing stack. Moving back ur> the syntax, we discover 0L2 TERM has been found, and then we recognize the + operator. Again we move down the syntax, finding and stacking the identifier B. Next ACTION 10 would set 29 the CURRENT OP field to addition and we go to ACTI0N_12. At this point the parsing stack is as shown in Figure 8. SUBTREE PTR SUBTREE TYPE B matrix A matrix R matrix Figure 8 Parsing Stack for R = A + B after All Operands Recognized The semantic routine ACTI0N_12 actually links the nodes of the expression tree. »lCTI0N_12 is entered after an operator and its operand (s) have been recognized. Upon entry to ACTI0N_12, a tree node for the operator is allocated and the type of node field set to CURRENT_OP. It next branches to the proper subsection for the current type of operator where some simple error checking is accomplished and the type of expression and number of dimensions fields are entered. If the operator was binary, the pointer from the top of the parsing stack is placed in the right link field (RLINK) and the next level pointer in the left link field (LLINK) . The pointer to the operator node then replaces the top two levels of the stack. For uniary operators, the pointer in the top of the stack is placed in RLINK and the pointer to the onerator node replaces the top level. After execution of ACTI0N_12 the parsing stack for our operator would appear as in Figure 9. 30 SUBTREE PTR SUBTREE TYPE El El matrix + R matrix / \ A B Figure 9 Parsing Stack for R = A + B after Addition Operator Processed The OL2_ARITHMETIC__EXPRESSION has now been recognized. ACTION_42 next sets CURRENT_OP to an equal operator and calls ACTION_12 which results in a complete expression tree. ACTION_42 now calls the code generating program (CODER) , the semicolon is recognized, and the processing of this statement is complete. The operator precedence table (Table 2) shows that the interpretation of the multiply operator is dependent upon the context in which it appears. ACTION_16 and ACTIOM_17 determine this context by comparing the tvpe of expression parts of the parsing stack and using a precedence table. Consider example 2, Y = A x Z x ALPHA, where Y and Z are column vectors, A is a matrix and ALPHA is a scalar. This expression is parsed similarly to the previous example until the first multiply operator is reached; at this point ACTIO r T_13 places a marker on the parsing stack to indicate the start of multiply operations. Next multiply operator and the identifier Z are recognized, CURRENT JOP is set to multiply, and ACTION_16 entered. At this point, the parsing stack appears as shown in Figure 10. 31 SUBTREE PTR SUBTREE TYPE Z column vector A matrix MARKER Y column vector Figure 10 Parsing Stack for R = AxZx ALPHA after First Multiply Is Recognized ACTION_16 determines that a matrix times a column vector has the highest precedence and calls ACTION_1 2 to build that section of the tree. Next the scalar ALPHA is placed in the stack, and ACTION_16 determines that a vector x scalar is not of sufficiently high precedence to construct this part of the tree, therefore, ACTION_12 is not called. ACTION_17 is entered with the parsing stack as shown in Figure 1 1 . SUBTREE PTR SU3TREE TYPE El x ALPHA scalar El column vector MARKER Y column vector A Z Figure 1 1 Parsing Stack for R = AxZx ALPHA upon Entrv to ACTION_17 ACTIOM_17 then unstacks all operands in the parsing stack down to the marker by calling ACTIOM_12 for each pair and removes the marker. The parsing stack would appear as shown in Figure 12 after execution of ACTION 17. 32 SUBTREE PTR SUBTREE TYPE E2 E2 column vector X Y column vector El x AL' / \ A Z Figure 12 Parsing Stack for R = AxZx ALPHA after Execution of ACTI0N_17 The processing of this statement then continues as in example 1. In Section 3, it was noted that a string of scalar operations is not parsed, but passed as a string to the PL/1 compiler. Consider the example from Section 3, as example 3: A = ALPHA + BETA x GAMMA x B; where A and B are matrices and ALPHA, BETA, GAMMA are scalars. The identifier A and the = operator are recognized as in our previous examples. In attempting to recognize 0L2_ARITHMETIC_EXPRESSI0H, we will eventuallv attempt to recognize the SCALAR_EXP phrase class. ACTION_24 places a marker on the parsing stack to indicate the start of a scalar string and saves the current location in the input string (INP) . The scalar string is passed over, as it is recognized by the syntax. When the end of the string is recognized, ACTION 25 takes the string, the saved input location to the current value of INP, and places it in the name field of a tree node; and then places the node on the parsing stack. ACTION 25 subsequently removes the marker from the stack and the rest of the statement is parsed as in our previous examples. It is also possible to build and stack several nodes for a scalar string if the string includes an OL/2 33 variable with subscripts, an inner product, or a norm. As example 4 we will extend example 3 to include the inner product of two vectors X and Y; A = ALPHA + BETA x GAMMAx (X,Y) x B. The scalar string up to the inner product is recognized as in the previous example. ACTION_48, after determining that an inner product follows, places this string on the parsing stack via a call to ACTION_37 (Figure 13). Next, the operands for the inner product are recognized and placed on the parsing stack (Figure 14), CURRENT_OP is set to inner product, and ACTI0N_12 is called. An expression tree is then created assigning the inner product to a tempory scalar variable (Figure 15) and CODER is called by ACTION_3 4. The name of the temporary variable is then placed in a node on the parsing stack (Figure 16) ACTION_25 now takes the strings from all nodes above the marker, and places them in a single node and removes the marker (Figure 17). The identifier B is then recognized' and processing would continue as in example 3. SUBTREE PTR SUBTREE TYPE ALPIIA+BETAxGA'TIAx scalar '1ARKER matrix Figure 13 Parsing Stack for R = ALPHA+BETAxGAMMAx (X,Y) xB stage 1 SUBTREE PTR SUBTREE TYPE Y X ALPHA+BETAxGAMMAx A column vector column vector scalar MARKER matrix 34 Figure 14 Parsing Stack for R = ALPHA+3ETAxGAMMAx (X,Y) xB stage 2 TEMPOO Inner Product X""" Y Figure 15 Expression Tree for Inner Product SUBTREE PTR TEMPOO ALPHA+BETAxGAMMAx SUBTREE TYPE scalar scalar MARKER A matrix Figure 16 Parsing Stack for R = ALPHA+BETAxGAMMAx (X,Y) xB stage 3 SUBTREC PTR SUBTREE TYPE ALPIIA+BETAxGAM" !AxTEMPOO A scalar matrix Figure 17 Parsing Stack for R = ALPHA+BETAxGAMMAx (X,Y) xB stage 4 The actions described for inner product are identical to those accomplished for a norm. For an OL/2 variable with subscripts or an OL/2 variable declared to be a scalar, a function routine call would be placed on the stack by ACTION_27 instead of the temporary scalar variable; otherwise the process would be identical. These are the operations of the major semantic action routines. Appendix B gives the listing of all the semantic routines for building the expression tree. 35 4. 2 OL/2 Code Generator i The OL/2 code generator (CODER) has been implemented in PL/1 as an external procedure. The reader is referred to Appendix C for a complete listing of this program. CODER is called by semantic routines ACTION_34 or ACTION_42 when an expression tree has been completed, sending the root of the expression tree as the major parameter . Conventions have been developed for code generated by the OL/2 compiler to facilitate module interactions and readability. A summary of these which effect array expression are given in Table 13. Convention 'leaning $ (variable) Pointer to the root node for (variable) $TEMP (n1 ) (n2) Pointer to the root node for temporary variable where n1 is dimension and n2 a sequence number 30L (routine name) an OL/2 intermediate routine # (variable) a non array temporary variable Table 13 OL/2 Code Conventions As explained in section 3, the temporary storage and partitioning control requirements are determined by the type of subtrees of an operator node. A label array, CODER_OPER, is used to switch to the proper subsection of the program for each combination of operator and subtree types. There are eight subsections of CODER which place a pointer to root node name (PTR_TO_RESULT_NODE) and type of result (RESULT TYPE) on the result stack, two each for 3fi column vectors, row vectors, matrices, and duplicating the top of the result stack. One of each pair moves to the left subtree (WALK_LKFT) and one moves to the right subtree (WALK_RIGHT) . The operation of each of the eight subsections is similar. First, a position on top of the result stack is allocated; secondly, the next legal root node pointer name for the temporary variable required and its type are placed on the stack. In the third step, the current node is marked as requiring the temporary variable by setting the #TE'1P_T0_FREF field in the tree node to one. The program then moves to the proper subtree. There are two operator tvpes which are treated as special cases: transpose and uniary minus (negation). These tv/o operators do not truly create a nev; result but only modify an operand for another ooerator. For this reason, if one of the subtrees of an operator node is a transpose or uniary minus operator node, nothing is placed on the result stack at this point and the program simply moves to the subtree. If a transpose operator node has an expression as a subtree, a name and type to hold the result of that expression will be placed on the result stack. For a uniary minus operator node, the ton of the result stack is duolicatod. <7hen control for the partitioning of operands is required one of three subsections of CODER is executed, one controls row partitioning and the other two control column partitioning. Operand partitioning is controlled by generating a PL/1 DO loop which will increment a control 37 variable (#ROW or #COL) from the lower to the upper bound found in the root node named on the top of the result stack. Next, the current node is marked as having initiated an operand partition loop by setting #CEND_STATEMENTS or #REND_STATEMENTS to one. In addition, an indicator called LOOP_DEPTH is set to one to indicate that operand partitioning is in effect. For the example given in section 3, (Figure 3) when node b is reached the following code would be generated: DO #C0L1 = $R-> #L0WER(2) TO $R -> #UPPER ( 2 ) ; The generation of code to calculate results is accomplished when an operator node is reached and both subtrees are variables. The generated code has been called intermediate code since it is in the form of calls to intermediate routines an r l not directly to the assembly language computation programs. For binary operators, CODER generates the seven parameters shown in Table 1 4 for each operand. For the result, only the parameters name, sequence number, type, and row or column number are generated. The first five of the parameters for operands and the first two for the result are assembled by the internal procedure SET_VARIALBES . The name and sequence number parameters are taken from the tree nodes for the operands and the result stack for the result. The save indicator is always set to one and appears only to be compatible with another code generating program. The transpose and negation parameters are taken from indicators set for each level of the expression tree. The indicators are set when a transpose or uniary minus operator node is encountered with a variable as its subtree. 38 Parameter Name Sequence Number Transpose Save Negate Type Row or Column Number Contents Pointer to root node Expression indicating which array of a sequence to use; is no sequence 1 if operand to be transposed before use, otherwise if storage for this operand to be released, 1 if to be saved 1 if operand to be negated before use, otherwise 0-scalar, 2-column vector 3-row vector, 4-matrix The control variable of nartition loop or Table 1 1\ Operand Parameters The last two parameters for operands and results are entered by two major subsections of CODER. The first, SET_ROW_OR_COL, determines these parameters for addition, subtraction, and division, and onerates as described in section 3. The second subsection, COOER_MULT_VAR_VAR, implements Table 3.8 in section 3. We will take our example from section 3 (Figure 3.2) and assume we are at node c with stacks as shown in Figure 3.3. The type of result is column vector and the type of both variables is matrix so we partition the second operand by columns and the resultant code would be CALL 30L?iULT($ A, 0,0,1 , , 4 , , SB , , , 1 ,0,2,#COL1 , $TEMP1 , , 2 , 0) ; 39 After producing the code, the type of the current node ($TYPE_CODE) is set to variable and the name and type of expression in the node are changed to those on top of the result stack. The WALK_UP subsection of CODER is then executed. In WALK_UP , the current node is examined to determine if it caused the initiation of an operand partitioning loop. If it did, the PL/1 END statement is generated and the indicator LOOP_DEPTH is set to zero. Next, code is generated to release any temporary storage used to hold intermediate results for lower levels of the tree. The current node is set to the top of the traverse stack and the next action required is determined. We will pick up our example from section 3 with •2 as the current node and the stacks as shown in Figure 6. Both operands are matrices and a column result is required, so both operands are partitioned by columns and the generated code is CALL30LADD($D, 0,0,1 ,0,2,#COL1 ,$E, 0,0,1 ,0,2,#COL1 , $TEMP1 2 , , 2 , ) ; The name field of node e is set to $TEMP12 and its type to column vector. Next, at node d, we have a matrix times a column vector with a column result, so no partitioning is required. The code generated is CALL5)0L;iULT($C, 0,0,1 ,0,4,0,$TE T 1P12,0,0,1 , , 2 , , $TEMP1 1 ,0,2,0) ; We moved to node b through WALK_UP and could release the storage for the temporary $TE'1P12 at this point, but we do not, since we will need it each time through our operand partitioning loop. The code to realse temporary storage 40 is saved until the end of an operand partitioning loop. At node b, both operands are column vectors and the result is a matrix, so the result is partitioned and the code would be CALL SOLADD($TEMP 10, 0,0,1 ,0 , 2,0 , 5TEMP1 1 ,0,0,1 , 2 , , $R,0 , 2 , #C0L1 ) ; As we move to node through a V7ALK_UP , the END statement for the loop is generated, as is the code to release all temporary storage. CODER saves the lines of code it generates (in OUTLINE) and only outputs them when an operand partition loop is not in effect (LOOP_DEPTH=0) . This is done because it is possible for an assignment statement to be written so as to necessitate the addition or multiplication of a full matrix after an operand partitioning loop control has been generated. An example of such a statement is R = ALPIIAx ( (AxDx) CxD) ) whose expression tree is shown in Figure 18. The operations of CODER would be: 1 ) at node b, a loop to control operand partitioning bv column would be generated, 2) at node c, the name for a temporary matrix would be placed on the result stack, 3) and at node d the code to multiply the full matrices A and B would be generated. Obviously one does not want to do this operation inside a loop, so in this situation the code would be output and not saved. Appendix D contains a sample OL/2 program and the intermediate code generated by CODER. a. R 41 ALPHA A B C D Figure 18 Expression Tree for R = ALPHAx ( (AxB) x (CxD) ) 4 . 3 Intermediate Routines The intermediate routines perform the vital function of preparing the parameters needed by the assembly language calculating routines from the data generated by CODER. There are eleven intermediate routines to which calls are generated by CODER; several of them actually call other intermediate routines and have been used to improve readability of the generated code. Table 15 contains a list of these programs. The intermediate routines draw mainly on the information contained in the root node (Table 1 ) for each operand. The major parameters passed to the assembly language routines are the core location (origin), bounds, geometric type, and increments of each operand. For operands which are not partitioned or are not part of a sequence, the data is taken directly from the root node. For operands which are one of a sequence, the operand 42 origin is computed by adding the array length times the sequence number to the origin of the first array of the sequence. For partitioned operands the intermediate routines must compute the origin, bounds, and increments for the particular row or column required. This computation is accomplished in the same manner as for dynamic partitioning of variables [ 5] . For transposed operands the bounds are interchanged, and also the geometric type code is often changed. In addition to preparing the parameters for the assembly language routines, the intermediate programs allocate the storage and initialize a root node for temporary variables. They compute the minimum storage required depending on the geometric tvpes of the operand and then call a storage allocation routine. They also perform error detection, especially for computability of operands. 43 Operation Addition Name Function 30IADD Calls assembly language addition routine Subtraction 30LSUB Resets negation parameter of second operand and calls 30LADD Multiplication Division Inner Product 30L M .ULT If operands are both arrays, calls assembly language multi- plication routine. If one operand is scalar, calls assembly language scalar multiplication routine 30LDIVD Takes the recriprical of the second operand and calls 30LMULT 30LIPRD A function routine which returns the inner product value. Calls 30LMULT Norm Assignment OL/2 Scalar aOLNORM aOLASGN aOLSCAL Subscripted OL/2 30LELE'-! A function routine which returns the value of the norm. Calls norm assembly language routine Assign one OL/2 variable to another by calling aOLADD with one null operand. A function routine to return the value of an OL/2 scalar A function routine to return the value of a subscripted OL/2 variable Exponentiation aOLEXPN Free Storage 30LFSTR Call 30LMULT a variable number of times depending on exponent Releases storage for temporary variables Table 15 OL/2 Intermediate Routines 44 LIST OF REFERENCES [1] B. A. Galler and A. J. Perlis, "Compiling Matrix Operations", CACM , 5, (Dec. ,1962) , pp590-594. [2] J. Reinfelds, "An Implementation of Automatic Array Arithmetic by a Generalized Push>-Down Stack" , Interactive System for Experimental Applied Mathematics , Academic Press, New York, (1963) , pp441-422. [3] R. A. Wagner, "Some Techniques for Algorithm Optimization with Application to Matrix Arithmetic Expressions", PhD Thesis, Carnegie- Mellon Universitv, Pittsburgh, (1968). [4] B. A. Galler and A. J. Perlis, A View of Programming Languages , Addison-Wesley , Reading, ( 1970) , pp231-268. [5] H. C. Adams, "Dynamic Partitioning in the Array Language OL/2", "■■l.S. Thesis, Report No. 421, Department of Computer Science, University of Illinois, Urbana, (1971). [6] J. R. Phillips, "The Structure and Design Philosophy of OL/2 -An Array Language", Report No. 420, Department of Computer Science, University of Illinois, Urbana, (to be published). [7] J. L. Gaffney Jr, :TACOS: A Table Driven Comniler- Compiler System", 'l.S. Thesis, Report No. 32 5 Department of Computer Science, University of Illinois, Urbana, (1969). 45 APPENDIX A OL/2 SYNTAX FOR ASSIGNMENT STATEMENTS ::= ( I <#5> ( | <#107> ) )* ; ::= •*• <#140>; ::= I ; ::= <#8> ( •»' I* ( •=• I •<-• ) <#41> ( ( '5)« I •'»NULL ,, • ) <#46> | <#42> ) •;' ; ::= ( 'M <#104> ■ | • <#22> )? ( ( •<■ <#113> •>« )+ <#44> )? I (<#2A> <#25> I <*KO> ) ; ::= ( ( •+« <#10> I •-' <#11> ) <#12> )* ::= <#13> ( •*• <#14> <#16> )* <#17> ::= ( '/• (( '+' )? I ■-' <#19> | ( <#24> <#25> I <#AO> ) I ) <#15> <#16> )* ; ::= ( •**• <#18> )? ; ::= ( ( '+• )? I •-' <#19> | ( <#24> <#25> I <#40> ) ) ( •**• <#18> ) ? ; ::= ( ( '+•)? | '-• <#19> ) | | ; ::= •(• <#20> •)• 46 ( •••• <#21> )? ; ::= ( •-• <0L2_IDENT IF IER> <#19> I ( • + • )? <0L2_IDENTIFIER> ) ( 'I' <#104> »|' <#22>>? ( ( '<• <#113> •>• > + <#44> )? ( •••• <#21> )? ; <0L2_IDENTIFIER> :: = <*I> <#23> ; ::= <#2A> <#25> I <#40> ; : := ( ( '+' | •-• ) )* ; ::= ( ( '*' I •/• ) )* ; ::= ( ■**• )* ; ::= ( '+' I '-' )? ; ::= '(' <#20> •)' I | | I ; ::= ( •->• )* ; ::= ( '.' )* ? ::= <#26> <*I> <#39> ( '<• <#27> <0L2_ARITHMETIC_EXPRESSI0N> <#28> ( •»' <0L2_ARITHMETIC_EXPRESSI0N> <#28> <#29> )* ')• <#30> I <#31> ) <#3S> ; ::= <#32> ( <*N> )? ( '.' )? ( <*N> )? <#33> ( ^E' ( »+■ I '-' ) <*N> ( ' I • )? )? ; ::= i|M <#37> <0L2_AR I THMET IC_EXPRESS ION> Ml' <#34> ; ::= •(• <#48> <0L2_ARI THMET IC_EXPRESS ION> <#35> •,• <0L2_ARITHMETIC_EXPRESSI0N> <#35> •)« <#36> ; 47 APPENDIX B OL/2 SEMANTIC ACTION ROUTINES FOR ASSIGNMENT STATEMENTS AND ARITHMETIC EXPRESSIONS ACT: /* OL/2 SEMANTIC ACTION ROUTINE PROCEDURE */ PROCEDURE(WHICH_ACTION_NUM) RECURSIVE; DCL WHICH_ACTION_NUM FIXED BIN (31,0) , 1 BRIDGE EXTERNAL, 2 GOCONDITION FIXED BIN (31,0) , 2 TEMPCONST VARYING CHARACTER ( 1 5 ) , 2 TEMPIDENT VARYING CHARACTER ( 32 ) ♦ 2 TEMPSTRING VARYING CHARAC TER ( 100 ) , CHAR(32767) CHAR ( 1 ) EXTERNAL CONTROLLED, (OK , INP) FIXED BINARY (31,0) EXTERNAL , CARDNUM EXTERNAL ENTRY RETURNS ( F I XED BINARY (31,0)) , CARDCOL EXTERNAL ENTRY RETURNS ( F I XED BINARY (31,0)) ♦ ACTI0N(0:200) LABEL STATIC; ACTI0N_5: /* PRINT SOURCE STATEMENT */ CALL SKIP_AND_OUTPUT; I=INP; DO WHILE (CHARU ) = • ' ) ; 1*1*1; end; INP = 1 ; IF STATEMENT_PRINTED = YES THEN GO TO RETURN_TO_P ARSER ; OUTPUT_BUFFER='/* '; ACT5CNUM: L = ( ( INP-l)/72)+l ; DO WHILE ( L = ( ( INP-1 )/72)+l ) ; IF CHAR(INP) = • • THEN GO TO SET_OK_ZERO_AND_RETURN ; IF CHAR(INP) = •;» THEN GO TO ACT5END; OUTPUT_BUFFER=OUTPUT_BUFFER II CHAR(INP); INP=INP+l; END; CALL SKIP_AND_OUTPUT; GO TO ACT5CNUM; ACT5END: OUTPUT_BUFFER=OUTPUT_BUFFER I I • ; */ • ; CALL SKIP_AND_0UTPUT; INP=I ; CALL SKIP_AND_0UTPUT; GO TO return_to_parser; ACTI0N_8: /* INITIALIZE TO PROCESS AN ASSIGNMENT STATEMENT */ /* DO A SEMANTIC SCAN TEST FOR AN ASSIGNMENT STATEMENT */ I = INP ; CALL SCAN_UNTIL_PASS( ';• , •=• , '<-• ) ; J = INP ; INP = I ; IF CHARU) = •;' THEN GO TO SET_OK_ZERO_AND_RETURN ; STK_PTR = ; SCALAR_TEMP_VARIABLE_#=0; 43 VECTOR_TEMP_VARIABLE_#=0; MATRIX_TEMP_VARIABLE_#=0; CURRENT_ROW#,CURRENT_COL#=0; EXPRESSION_AREA = EMPTY ; GO TO RETURN_TO_PARSER ; ACTION_10: CURRENT_OP = PLUS; GO TO RETURN_TO_PARSER; ACTION_ll: CURRENT_OP = MINUS; GO TO return_to_parser; ACTION_12: /* THIS ROUTINE BUILDS THE EXPRESSION TREE. IT ALLOCATES A NODE FOR THE OPERATOR, INSERTS THE TYPE OF OPERATOR FROM THE CURRENT_OP FIELD, DETERMINES THE TYPE OF EXPRESSION AND NUMBER OF DIMENSIONS AND PLACES THEM IN THE NODE. IT LINKS THE PROPER SUBTREE(S) TO THE OPERATOR NODE AND PLACES THE RESULT IN THE TOP OF THE PARSING STACK. SOME SIMPLE ERROR CHECKING IS ALSO DONE. */ /* SUBTREE TYPE CODES ARE AS FOLLOWS: SCALAR FUNCTION 1 COL VECTOR 2 ROW VECTOR 3 MATRIX 4 NULL OPERAND 5 */ ALLOCATE TREE_NODE IN ( EXPRESS I ON_AREA ) ; $TYPE_CODE=CURRENT_OP ; STRING_POINTER , SEO_#_PTR = NULL ; RLINK,LLINK=NULL; NEGATE_TAG , TR ANSPOSE_TAG , IDENTITY_TAG = NO 5 #TEMP_TO_FREE,#CEND_STATEMENTS,#REND_STATEMENTS=0; CALL GOTO( ACTION_12_ROUTINE (CURRENT_OP) ) ; ACTION_12_ROUTINE_PLUS: ACT I ON_l 2_ROUT INE_M INUS : IF SUBTREE_TYPE(STK_PTR ) -.= SUBTRFE_TYPE ( STK_PTR-1 ) THEN CALL #ERROR( 12 ) ; XPTR1 = SUBTREE_PTR(STK_PTR) ; XPTR2 = SUBTREE_PTR(STK_PTR-1 ) ; IF XPTRl->$#DIMENSIONS-. = XPTR2->$#DIMENSIONS THEN CALL #ERROR( 12) ; $#DIMENSIONS=XPTRl->$tfDIMENSIONS; TYPE_EXP^XPTR1->TYPE_EXP; GO TO LINK_BINARY_0P; ACT ION_12_ROUTINE_DI VIDE: IF SUBTREE_TYPE(STK_PTR)-=0 THEN CALL #ERROR(13); XPTR1 = SUBTREE_PTR (STK_PTR-1) ; $#DIMENSIONS=XPTRl->$#DIMENSIONSj TYPE_EXP=XPTR1->TYPE_EXP; GO TO LINK_BINARY_0P; ACTION_12_ROUTINE_INNER_PRODUCT: IF SUBTREE_TYPE(STK_PTR) -* = 3 £ SUBTREE_TYPE(STK_PTR) -= 2 I 49 SUBTREE_TYPE(STK_PTR-1) -*= 3 £ SUBTREE_TYPE( STK_PTR-1 ) -.= 2 THEN CALL #ERR0R(14) SUBTREE_TYPE(STK_PTR-1 )=0; $#DIMENSIONS=0; TYPE_EXP=SCALAR; GOTO LINK v _BINARY_OP; ACTI0N_12_R0UTINE_EXP0NENTIATE: IF SUBTREE_TYPE(STK_PTR)-=0 THEN CALL #ERROR(15); IF SUBTREE_TYPE(STK_PTR-1 )-=4 THEN CALL #ERROR<15); XPTR1 = SUBTREE_PTR (STK_PTR-1) ; $#DI MENS I ONS = XPTRl->$#DI MENS IONS; TYPE_EXP=MATRIX; GO TO LINK > _BINARY_0P; ACTION_12_ROUTINE_NORM: IF SUBTREE_TYPE(STK_PTR) < 2 THEN CALL #ERR0R(17) ; SUBTREE_TYPE(STK_PTR)=0; $#DIMENSIONS=0; TYPE_EXP=SCALAR; GO TO LINK,_UNIARY_OP ; ACTION_12_ROUTINE_UNIMINUS: XPTR1=SUBTREE_PTR (STK_PTR) ; $#DIMENSIONS=XPTRl->S#DIMENSIONS; TYPE_EXP=XPTR1->TYPE_EXP; GO TO LINK^UNIARY_OP; ACTION_12_ROUTINE_TRANSPOSE: IF SUBTREE_TYPE(STK_PTR)=2 THEN SUBTREE_TYPE(STK_PTR),TYPE_EXP=3; ELSE IF SUBTREE_TYPE$#DI MENS IONS; GO TO LINK_UNIARY_OP; ACTION_12_ROUTINE_MULTIPLY: /* RIGHT SUBTREE OF TYPE SCALAR */ IF SUBTREE_TYPE(STK_PTR)<=1 THEN DO; XPTR1=SUBTREE_PTR(STK_PTR-1 ); $#DIMENSIONS=XPTRl->$#DIMENSIONS; TYPE_EXP=XPTR1->TYPE_EXP; GO TO LINK_BINARY_OP; END; /* LEFT SUBTREE OF TYPE SCALAR */ IF SUBTREE_TYPE(STK_PTR-1 )<=1 THEN DO; SUBTREE_TYPE(STK_PTR-1 ) =SUBTREE_TYPE ( STK_PTR ) ; XPTR1=SUBTREE_PTR(STK_PTR) ; SO ACTION_12_ROUTINE_SEQUENCE: XPTR1=SUBTREE_PTR(STK_PTR); TREE_NODE=XPTRl->TREE_NODE; SUBTREE_PTR(STK_PTR)=NODE_PO INTER; SEQ_#_PTR=POINTER_TO_STRING(TEMPSTRING r EXPRESSION_AREA) ; GO TO return_to_parser; action_12_routine_part_of: xptr1=subtree_ptr(stk_ptr); tree_node=xptri->tree_node; xptrl,subtree_ptr(stk_ptr)=node_po inter; XPTRl->STRING_POINTER=POINTER_TO_STRING(B string, EXPRESSION_AREA) ; . GO TO RETURN_TO_PARSER; ACTION_12_ROUTINE_SEPARATOR: $#DIMENSIONS=0; SUBTREE_TYPE(STK_PTR-1)=0; GO TO LlNK_BINARY_OP; ACTION_12_ROUTINE_FUNCTION: IF SUBTREE_TYPE(STK_PTR-1 ) = 1 THEN DO ; SUBTREE_TYPE(STK_PTR-1 ) = ; *#DIMENSIONS=0; GO TO LINK_BINARY_OP; END; ELSE CALL #ERR0R(19) ; /* ILLEGAL FUNCTION */ GO' TO return_to_parser; LINK_UNIARY_OP : RLINK=SUBTREE_PTR(STK_PTR); SUBTREE_PTR(STK_PTR)=NODE_PO inter; IF STK_PTR = THEN CALL #ERROR(18) ; GO TO RETURN_TO_PARSER; L1NK_BINARY_0P: LLINK=SUBTREE_PTR(STK_PTR-1 ) ; RLINK=SUBTREE_PTR (STK_PTR) ; STK_PTR=STK_PTR-1; IF STK_PTR = THEN CALL #ERR0R(18) ; SUBTREE_PTR(STK_PTR)=NODE_PO INTER; GO TO return_to_parser; /* INSERT MULTIPLY DELIMITER MARKER */ ACTION_13: SUBTREE_TYPE ( STK_PTR+1 ) =SUBTREE_TYPE ( STK_PTR ) ; SUBTREE_PTR(STK_PTR+1 ) =SUBTREE_PTR ( STK_PTR ) ; SUBTREE_TYPE( STK_PTR ) =MARKER ; STK_PTR=STK_PTR+l; GO TO RETURN_TO_PARSER; ACTI0N_1A: CURRENT_OP=MULTIPLY; GO TO RETURN_TO_PARSER; ACTION_15: CURRENT_OP=DI VI DE ; GO TO RETURN_TO_PARSER ; 51 /* TEST PRECEDENCE TABLE FOR MULTIPLY , OR PROCESS */ /* A SCALAR DIVIDE */ ACTI0N_16: IF CURRENT_OP = DIVIDE I PRECEDENCES A 8LE(SUBTREE_TYPE(STK_PTR-1), SUBTREE_TYPE(STK_PTR ) )=0 THEN DO ; IF SUBTREE_TYPE= 2 £ SUBTREE_TYPE(STK_PTR-2)=0 THEN DO ; STK_PTR=STK_PTR-1; CALL ACT( 12) ; SUBTREE_TYPE(STK_PTR+1 ) =SUBTREE_TYPE < STK_PTR + 2 ) ; SUBTREE_PTR(STK_PTR+1 )=SUBTREE_PTR ( STK_PTR+2 > ; STK_PTR=STK_PTR+1; END; CALL ACT(12) ; if subtree_type(stk_ptr-1 )=marker then go to return_to_parser; else current_op=multiply; go to acti0n_16; END ; GO TO RETURN_TO_PARSER ; /* UNSTACK ALL REMAINING MULTIPLICANDS DOWN TO MARKER */ /* AND REMOVE THE MARKER */ ACTION_17: IF SUBTREE_TYPE(STK_PTR-1 )=MARKER THEN DO; SUBTREE_TYPE(STK_PTR-1)=SUBTREE_TYPE(STK_PTR) ; SUBTREE_PTR(STK_PTR-1)=SUBTREE_PTR(STK_PTR) ; STK_PTR=STK_PTR-1 ; GO. TO RETURN_TO_PARSER; END; IF PRECEDENCE_TABLE(SUBTREE_TYPE(STK_PTR-1), SUBTREE_TYPE( STK_PTR ) )-.=2 THEN DO; CALL ACT( 12) ; GO TO ACTION_17; END; CALL #ERROR(17); GO TO RETURN_TO_PARSER ; ACTI0N_18: CURR ENT_OP = EXPONENT I ATE ; GO TO ACTION_12; ACTI0N_19: CURRENT_OP=UNI M INUS ; GO TO ACTION_12; /* CHECK FOR AN I NNER_PRODUCT CONSTRUCT STARTING AT INP */ /* ♦ IF FOUND SET OK = */ ACTION_20: PARN_COUNT= 1 ; ACT_20_INDEX=I NP; ACT_20_COMMA_CHK: IF CHAR(ACT_20_INDEX)=' ♦• £ PARN_COUNT =1 THEN DO; OK = 0; GO TO RETURN_TO_PARSER; END; ELSE IF CHAR( ACT_20_INDEX)=» ( • THEN PARN_COUNT=PARN_COUNT+l ; ELSE IF CHAR(ACT_20_INDEX)=« > • THEN DO; PARN_COUNT=PARN_COUNT-l ; 52 IF PARNjCOUNT-0 THEN 00; OKM; GO TO RETURN_TO_PARSER; END; END; ACT_20_INDEX = ACT_20_INDEX + 1 ; GO TO ACT 20_C0MMA_CHK; ACTI0N_2l: CURRENT_OP*TRANSPOSE; GO TO ACTI0N_12; ACTION_22: CURRENT_OP=SEQUENCE ; GO TO ACTI0N_12; ACTI0N_23: /* SEE IF IDENTIFIER IS OL/2 VARIABLE IF SO STACK VARIABLE NODE ON PARSING STACK */ XPTR1 = SEARCH(TEMPIDENT t J) ; IF XPTR1=NULL THEN DO; 0K=0; GO TO RETURN_TQ_PARSER; END; IF XPTR1 -> $TYPE_CODE = BLOCK_ARRAY | XPTR1 -> $TYPE_CODE = VECTOR_SPACE THEN CALL #ERROR( NOT_IMPLEMENTED ) ; SP = XPTR1 -> STRING_POINTER ; B_STRING = STRINGS ; SUBTREE_PTR(STK_PTR+1 )=XPTR1; SUBTREE_TYPE( STK_PTR+1 )=XPTR1->TYPE_EXP ; STK_PTR = STK_PTR + 1 ; IF ON_RIGHT_HAND_SIDE £ XPTR 1=LHS_IDENT THEN LHS_TEMP_NEEDED=YES; IF STK_PTR > MAX_STACK THEN CALL #ERR0R(100) ; GO TO RETURN TO PARSER ;. ACTI0N_2A: /* SET POINTER TO BEGINNING OF A SCALAR STRING • */ /* AND STACK A MARKER */ SCALAR_EXP_POINTER = INP ; STK_PTR = STK_PTR + 1 ; SUBTREE_TYPE(STK_PTR) = SCALAR_STR ING_MARKER ; GO TO RETURN_TO_PARSER ; ACTI0N_25: /* TAKE THE SCALAR STRINGS FROM ALL THE NODES ABOVE THE MARKER IN THE PARSING STACK AND PLACE THEM IN ONE NODE */ IF SCALAR_EXP_POINTER -.= INP THEN CALL BUILD_AND_STACK_SCALAR_NODE( SCAL AR_EXP_POINTER t INP-1 ); IF SUBTREE_TYPE(STK_PTR-1)-. = SCALAR_STRING_MARKER THEN DO; XPTR1=SUBTREE_PTR(STK_PTR) ; XPTR2=XPTR1->STRING_P0INTER; B_STRING=XPTR2->STRINGS; FREE XPTR1->TREE_N0DE IN ( EXPRESS ION_ARE A ) ; DO WHILE (SUBTREE_TYPE(STK_PTR-l)-.= SCALAR_STRING_MARKER) ; XPTR1= SUBTREE_PTR(STK_PTR-1) ; XPTR2=XPTR1->STRING_P0INTER; A_STRING=XPTR2->STRINGS; FREE XPTR1->TREE_N0DE IN ( EXPRESS ION_AREA ) ; A_STRING=A_STRING| |B_STRING; 53 B_STRING = A_STRING; STK_PTR=STK_PTR-1 ; END; STK_PTR=STK_PTR-l; CALL BUILD_AND_STACK_STRING_NODE (B_STRING ) ; END; STK_PTR = STK,_PTR - 1 ; SUBTREE_TYPE(STK_PTR) = SUBTREE_TYPE ( STK_PTR+1 ) SUBTREE_PTR(STK V _PTR) = SUBTREE_PTR < ST*_PTR+1 ) ; GO TO RETURN TO PARSER ACTION_26: /* SAVE A POINTER TO THE BEGINNING OF THE IDENTIFIER SAVER_POINTER = I NP ; ELEMENT_EXPRESSION_FOUND = NO ; GO TO RETURN_TQ_PARSER */ ACTION_27: ACTION_70: IF IDENTIFIER */ ) • ( • THEN THEN CALL PROCESS PARENTHESIZED CONSTRUCT AFTER THE TYPE_OF_ID -= OL2_ID THEN GO TO BNOT IF SCALAR_EXP_POINTER -* = SA VER_POINTER THEN CALL BUILD_AND_STACK_SCALAR_NODE( SCALAR_EXP_POINTER , SA VER_POINTER - 1 ) ; A_STRING = •• PARN_COUNT = 1 DO WHILE ( PARN_COUNT > ) CALL SCAN_UNTIL_KEEP( •)' t •(• , •;• A_STRING = A_STRING II TEMPSTRING CHARACTER_SCANNED = CHAR(INP) IF CHARACTER_SCANNED = •)• THEN DO <• PARN_COUNT = PARN_COUNT-l IF PARN_COUNT = THEN GO TO BREADY END ELSE IF CHARACTER_SCANNED = PARN_COUNT = PARN_COUNT + 1 ELSE IF CHARACTER_SCANNED = #ERROR( UNMATCHED_PARNS ) END BREADY: IF WHI CH_ACT I ON_NUM = 70 THEN GO TO RETURN_TO_PARSER INP = INP + 1 ; SAVER_POINTER = INP ; SCALAR_EXP_POINTER=INP; CALL BUILD_AND_STACK_STR ING_NODE( •(• II TEMPIDENT || ",' II A_STRING ELEMENT_EXPRESSION_FOUND = YES ; GO TO SET_OK_ZERQ_AND_RETURN ; BNOT: IF SCALAR_EXP_POINTER -= SA VER_POINTER THEN CALL BUILD_AND_STACK_SCALAR_N0DE( SCALAR_EXP_PO I NTER » SAVER_POINTER - 1 ) ; CALL BUILD_AND_STACK_SCALAR_N0DE( SAVER_PO I NTER , INP - 1 ) ; SUBTREE_TYPE ( STK_PTR ) = FUNCTION ; TO RETURN_TO_PARSER 'aOLELEM' ' ) • ) GO ACTI0N_28: /* SEE IF PL1 FUNCTION WITH 0L2 ARGUMENT */ IF SUBTREE_TYPE(STK_PTR )-=SCALAR & TYPE_OF_ID = OTHER THEN CALL #ERR0R(221 ) ; 54 60 TO RETURN_TO_PARSER ; ACTI0N_29: /* BUILD AN ARGUEMENT SUBTREE */ CURRENT_OP= SEPARATOR ; GO TO ACTI0N_12 ; ACTI0N_30: /* FINISH OFF A FUNCTION SUBTREE */ CURRENT_OP = FUNCTION_TYPE ; GO TO T0_34; ACTI0N_31: /* HAS AN OL/2 IDENTIFIER ALONE BEEN FOUND ? */ IF TYPE_0F_ID=0L2_ID £ ELEMENT_EXPRESSION_FOUND THEN DO; INP = SAVER_POINTER ; GO TO RETURN_TQ_PARSER; END ; • IF TYPE_OF_ID = 0L2_ID £ -. ELEMENT_EXPRESSION_FOUND THEN DO ; IF SAVE_IDENT -> $#DIMENSIONS « THEN DO ; IF SCALAR_EXP_POINTER -« SAVER_PO INTER THEN CALL BUILD_AND_STACK_SCALAR_NODE ( SCALAR_EXP_POINTER , SA VER_POINTER - 1 ) ; SP = SAVE_IDENT -> STR I NG_POINTER ; A_STRING = STRINGS ; CALL BUILD_AND_STACK__STRING_N0DE ( •30LSCAL(' II A_STRING || •)• ) ; SCALAR_EXP_POINTER = INP ; OK = 1 ; END ; ELSE DO ; ok = o ; FREE TYPE_OF_ID ; END ; END ; GO TO RETURN_TQ_PARSER ; ACTION_32: /* INITIALIZE TO TEST FOR AN ARITHMETIC CONSTANT */ TEMPCONST = • • ; GO TO RETURN_TQ_PARSER ; ACTION_33: /* HAS ONLY A ».» BEEN FOUND ? */ IF TEMPCONST = »• THEN OK = ; GO TO RETURN_TQ_PARSER ; ACTI0N_34: /* PROCESS A NORM */ CURRENT_OP = NORM ; /* INSERT AN ASSIGNMENT OF NORM, INNER-PRODUCT, OR SCALAR FUNCTION TO A TEMPORARY SCALAR VARIABLE. CALL COMPILER AND THEN PLACE THE NAME OF THE TEMPORARY SCALAR ON THE PARSING STACK */ TQ_34: CALL ACT(12); SCALAR_EXP_POINTER=INP; SUBTREE_TYPE( STK_PTR+1 ) =SUBTREE_TYPE ( STK_PTR ) ; SUBTREE_PTR(STK_PTR+1 ) =SUBTREE_PTR ( STK_PTR ) ; STK_PTR=STK_PTR-l; CALL BUILD_AND_STACK_STRING_N0DE ( •#TEMPO« II DIGIT_STRINGS(SCALAR_TEMP_VARIABLE_#) ); STK_PTR=STK_PTR+l; 55 current_op=equal; CALL ACT(12); CALL CODER( STK_PTR»SUBTREE_PTR(STK_PTR) » VECT0R_TEMP_VARIABLE_#»SCALAR_TEMP_VARIA6LE_#r MATRIX_TEMP_VARIABLE__#»CURRENT_COL#tCURRENT_ROW#) ; CALL BUILD_AND_STACK_STRING_NODE( •#TEMPO« II DIGIT_STRINGS(SCALAR_TEMP_VARIABLE_#) ); SCALAR_TEMP_VARIABLE_#=SCALAR_TEMP_VARIABLE_«+l; GO TO RETURN_TO_PARSER; ACTION_35: /* MOVE OVER A • , • OR A ' J • */ SCALAR_EXP_POINTER = INP+1 ; GO TO RETURN_TO_PARSER; ACTI0N_36: CURRENT_OP= INNER_PRODUCT ; GO TO T0_34; ACTION_37: /* PROCESS A POSSIBLE PREVIOUS SCALAR STRING */ K = INP - 3 ; TO_37: IF SCALAR_EXP_P0INTER -.= INP THEN DO ; CALL BUILD_AND_STACK_SCALAR_NODE( SCALAR_EXP_POINTER » K ); END ; GO TO RETURN_TO_PARSER ; ACTION_38: /* DON'T NEED THIS ANY MORE */ FREE TYPE_OF_ID ; GO TO RETURN_TO_PARSER ; ACTI0N_39: /* FIND THE TYPE OF THE IDENTIFIER */ ALLOCATE TYPE_OF_ID ; TEMP_POINTERl = SEARCH( TEMPIDENT , I ) ; IF TEMP_POINTERl = NULL THEN DO ; IF IS_AN_OL2_ENTRY( TEMPIDENT ) THEN TYPE_OF_ID = OL2_ENTRY ; ELSE TYPE_OF_ID = OTHER ; END ; ELSE DO ; TYPE_OF_ID = OL2_ID ; SAVE_IDENT = T EMP_POI NTER1 ; END ; go to return_to_parser ; action_ao: /* unstack scalar marker . */ stk_ptr = stk_ptr - 1 ; go to set_ok_zero_and_return ; action_41: /* save ptr to left hand side result */ lhs_ident = subtree_ptr(stk_ptr ) ; on_right_hand_side=yes; go to return_to_parser; ACTI0N_A2: /* INSERT ASSIGNMENT TO TEMPORARY VARIABLE WHEN SAME VARIABLE WAS USED ON BOTH SIDES OF = SIGN */ IF LHS_TEMP_NEEDED THEN DO; ALLOCATE TREE_NODE IN ( EXPRESS I ON_AREA ) ; 56 XPTR1«SUBTREE_PTR(STK_PTR-1); TREE_N0DE*XPTR1->TREE_N0DE; IF TYPE_EXP<4 THEN DO; STRING_POINTER«POINTER_TO_STRING< *STEMP1' I | - DIGIT_STRINGS(VECTOR_TEMP_VARIABLE #>t EXPRESSION_AREA); VECTQR_TEMP_VARIABLE_#-VECTOR TEMP VARIABLE #+1; END; ELSE DO; STRING_POINTER«POINTER_TO_STRING( '$TEMP2» II DIGIT_STRINGS(MATRIX_TEMP_VARIABLE_#), EXPRESSION_AREA); MATRIX_TEMP_VARIABLE_#«MATRIX_TEMP_VARIABLE #+1; END; SUBTREE_PTR(STK_PTR+1)*SUBTREE_PTR(STK_PTR); SUBTREE_TYPE(STK_PTR + 1)«SUBTREE_TYPE(STK_PTR) ; SUBTREE_PTR(STK_PTR)=*NODE_POINTER; SUBTREE_TYPE(STK_PTR)»TYPE_EXP; STK_PTR=STK_PTR+l; END; /* HANDLE ASSIGNMENT, MULTIPLR IF NECESSARY */ DO I = STK_PTR TO 2 BY -1 ; CURRENT_OP = EQUAL ; CALL ACT(12) ; END ; /* CALL COMPILER */ CALL CODER(STK_PTR t SUBTREE_PTR(STK_PTR) , VECTOR_TEMP_VARIABLE_#tSCALAR_TEMP_VARIABLE_#, MATRIX_TEMP_VARIABLE_#tCURRENT_COL#,CURRENT_ROW#); PUT SK1P(2); IF VECTOR_TEMP_VARIABLE_#-l>MAX_VECTOR_TEMP MAX_VECTOR_TEMP=VECTOR_TEMP_VARIABLE_#-l; IF MATRIX_TEMP_VARIABLE_#-1>MAX_MATRIX_TEMP MAX_MATRIX_TEMP=MATRIX_TEMP_VARIABLE_#-1 ; ON_RIGHT_HAND_SIDE=NO; LHS_TEMP_NEEDED=NO; GO TO RETURN TO PARSER ; THEN THEN ACTION_>4: /* PROCESS PART-OF CONSTRUCT CURRENT_OP = PART_OF ; GO TO ACTION_12 ; */ ACTI0N_A6: /♦ PROCESS A "NULL" OPERAND STK_PTR = STK_PTR +1 ; SUBTREE_PTR(STK_PTR) = SOL2NULL ; SUBTREE_TYPE( STK_PTR) = 5 ; GO TO RETURN_TO_PARSER ; */ ACTI0N_48: /* INSURE THAT THAT WHICH FOLLOWS IS AN INNER PRODUCT */ I=INP; ACT48_CHK: IF CHAR(I) = S' £ PARN_COUNT = l THEN DO; OK = l; K=INP-2; GOTO TO_37; END; ELSE IF CHAR( !)=•(• THEN PARN COUNT=PARN COUNT+1; 57 $#DIMENSI0NS=XPTR1->S#DIMENSI0NS; TYPE_EXP=XPTR1->TYPE_EXP; GO TO LINK^BINARY_0P; END; /* LEFT SUBTREE OF TYPE COLUMN VECTOR •/ IF SUBTREE_TYPE(STK_PTR-1 ) = 2 THEN DO; IF SUBTREE_TYPE(STK_PTR) =3 THEN DO; SUBTREE_TYPE(STK_PTR-1 )=4; $#DIMENSIONS=2; TYPE_EXP=MATRIX; GO TO LINK_BINARY_0P; END; ELSE CALL #ERR0R(16) ; GO TO RETURN_TO_PARSER; END; /* LEFT SUBTREE OF TYPE ROW VECTOR *•/ IF SUBTREE_TYPE< STK_PTR-1 ) = 3 THEN DO; IF SUBTREE_TYPE(STK_PTR) =2 THEN GO TO ACTION_36 IF SUBTREE_TYPE(STK_PTR)=4 THEN DO; XPTR1=SUBTREE_PTR(STK_PTR) ; $#DIMENSIONS=XPTRl->S#DIMENSIONS-l; IF J#DIMENSIONS=l THEN DO; SUBTREE_TYPE(STK_PTR-1 )=3 ; TYPE_EXP=ROW_VEC ; END; ELSE SUBTREE_TYPE(STK_PTR-1 ) , TYPE_EXP = MATR I X ; GO TO LINK_BINARY_OP; END; END; /* LEFT SUBTREE OF TYPE MATRIX */ IF SUBTREE_TYPE(STK_PTR-1 )=4 THEN DO; TYPE_EXP=MATRIX; XPTR1=SUBTREE_PTR(STK_PTR-1 ); XPTR2=SUBTREE_PTR(STK_PTR ); $#DIMENSIONS=XPTRl->S#DIMENSIONS+ XPTR2->$#DIMENSIONS-2; IF S#DIMENSIONS=l THEN SUBTREE_TYPE(STK_PTR-1) ,TYPE_EXP=COL_VEC ; GO TO LINK_BINARY_OP; END; ACTION_12_ROUTINE_EOUAL: IF SUBTREE_TYPE(STK_PTR) = SUBTREE_TYPE ( STK_PTR-1 ) I SUBTREE_TYPE(STK_PTR > = 5 £ SUBTREE_TYP E ( STK_PTR-1 ) > 1 I SUBTREE_TYPE(STK_PTR) = & SUBTREE_TYPE (STK_PTR-1) = 4 THEN DO ; XPTR1 = SUBTREE_PTR(STK_PTR-1) ; IF XPTR1 -> IDENTITY_TAG THEN CALL #ERROR<21) ; $#DIMENSIONS=XPTRl->$#DIMENSIONS; TYPE_EXP=XPTR1->TYPE_EXP; GO TO LINK_BINARY_OP; END ; CALL #ERROR(20) ; GO TO RETURN TO PARSER; 58 ELSE IF CHARd)* 1 ) 1 THEN 00; parn_c0unt=parn_c0unt-1 ; if parn_c0unt=0 then do; ok=o; goto return_to_parser; end; end; 1=1+1; IF CHAR(I)=«;» THEN CALL # ERROR( UNMATCHED_P ARNS ) ; . . GO TO ACT48_CHK; ACTI0N_104:/* PICK UP SEQUENCE EXPRESSION */ IF CHAR(INP) = «|« THEN GO TO SET_OK_ZERO_AND_RETURN ; CALL SCAN_UNTIL_KEEP( 'I' » •?» ) ; IF CHAR(INP) = »;• THEN CALL #ERROR (211) GO TO RETURN_TO_PARSER ; ACTI0N_107: /* SKIP TO NEXT ; AND OUTPUT STATEMENT WITH WARNING */ OUTPUT_BUFFER= •/* **STATEMENT NOT RECOGNIZED AS PL1 OR 0L2** */•; CALL SKIP_AND_OUTPUT; K=INP; CALL SCAN_UNTIL_PASS( •;• ) ; IF CHAR(INP) = • ' THEN GO TO SET_OK_ZERO_AND_RETURN ; CALL MOVCHAR(OUTPUT_BUFFER t K t 1NP) ; L107: CALL SK I P_AND_OUTPUT ; INP = IMP + 1 ; GO TO RETURN_TO_PARSER ; ACTI0N_113:/* PROCESS SUBARRAY INDICES */ IF IDENT_DEFINED = NO THEN DO ; CALL SCAN_UNTIL_PASS (•>',•;•) ; GO TO RETURN_TO_PARSER ; END ; A_STRING = • • ; PARN_COUNT , COMMA_COUNT = ; DO WHILE ( PARN_COUNT >= ) ; CALL SCAN_UNTIL_KEEP (•)•»•(•» •,' t ';' t •>' ); A_STRING = A_STRING II TEMPSTRING ; IF CHAR{ INP) = ' ) • THEN DO ; IF PARN_COUNT = THEN DO ; CALL #ERROR (210) ; CALL SCAN_UNTIL_PASS ( •>• t •?' ) ; PARN_COUNT = -1 ; GO TO NOT_PAST ; END ; ELSE PARN_COUNT = PARN_COUNT - 1 ; END ; ELSE IF CHAR (INP) = •(' THEN PARN_COUNT = PARN_COUNT + 1 ; ELSE IF CHAR(INP) = •,' THEN IF PARN_COUNT = THEN COMMA_COUNT = COMMA_COUNT + 1 ; ELSE ; ELSE IF CHAR(INP) -^= •»' THEN DO ; IF CHAR( INP) = ' ; • THEN DO ; CALL #ERROR (202) ; IDENT_DEFINED = NO ; GO TO RETURN_TO_PARSER ; END ; PARN_COUNT = -1 ; 59 GO TO NOT PAST END A_STRING = A_STRING II CHAR (INP) ; INP = INP + 1 ; NOT_PAST: END ; IF COMMA_COUNT = THEN A_STRING = A_STRING II 'tO 1 ELSE IF COMMA_COUNT > 1 THEN DO ; CALL #ERROR(204) ; IDENT_DEFINED = NO ; GO TO RETURN_TO_PARSER ; END ; B_STRING = '30LL0CN (• || B_STRING A_STRING I I ' ) • ; UNQUALIFIED = NO ; GO TO RETURN TO PARSER ; II II ACTI0N_140: /* SCAN OVER PL1 STATEMENTS DO WHILE(CHAR( INP)=« ' ); INP=INP+l; END; INP=INP-l; K=INP; CALL SCAN_UNTIL_PASS( ' ; S •%' ) ; CALL MOVCHAR(OUTPUT_BUFFER t K, INP-1 ) ; IF CHAR ( INP ) = • ; • THEN OUTPUT_BUFFER = OUTPUT_BUFFER I I CALL SKIP_AND_OUTPUT; IF CHAR(INP) = • ' THEN GO TO RETURN_TO_PARSER ; INP=INP+l; IF CHARUNP-D^Z* THEN GO TO RETURN_TO_P ARSER ; ELSE GO TO ACTION 140; */ SET_OK_ZERO_AND_RETURN: OK = RETURN_TO_PARSER: RETURN SKIP_AND_OUTPUT: PROCEDURE /* SKIP_ANO_OUTPUT IS A PROCEDURE WHICH OUTPUTS DATA TO BE PASSED TO THE PL/1 COMPILER. IT WILL NOT BE REPRODUCED HERE*/ END SKIP AND OUTPUT ; TERROR: PROCEDURE ( ERROR_CODE_# ) /=* #ERROR IS A PROCEDURE TO OUTPUT MESSAGES FOR ERRORS FOUND DURING COMPILATION. IT WILL NOT BE REPRODUCED HERE */ END TERROR /* POINTER_TO_STRING ALLOCATES A VARIABLE LENGTH STRING IN A GIVEN AREA AND RETURNS A POINTER TO THE STRING */ POINTER_TO_STRING: PROCEDURE ( STRING t AREA ) RETURNS ( POINTER ) DECLARE STRING CHARACTER (200) VARYING , AREA AREA( *) ; STRING_LENGTH = LENGTH ( STRING ) ALLOCATE VAR I ABLE_STRI NG IN ( AREA ) STRINGS = STRING RETURN ( SP ) END POINTER TO STRING /* SEARCH LOOKS FOR AN IDENTIFIER IN THE OL/2 IDENTIFIER TABLE AND 60 IF IT FINDS IT A POINTER TO THE COMPILE TIME NOOE FOR THAT VARIABLE IS RETURNED, ELSE A NULL POINTER IS RETURNED */ SEARCH: PROCEDURE ( IDENTIFIER , J ) RETURNS ( POINTER ) DECLARE IDENTIFIER CHAR(32> VARYING , TEMP_PP POINTER ( J t I ) FIXED BINARY (31,0) DO I = CURRENT_ID - 1 TO 1 BY -1 TEMP_PP = IDENTIFIER_NAME_POINTER( I ) IF TEMP_PP -> STRINGS = IDENTIFIER THEN DO J = I RETURN ( IDENTIFIER_NODE_POINTER( I ) ) END END J = RETURN ( NULL ) END SEARCH /* BUILD_AND_STACK PLACES A NODE ON THE PARSING STACK FOR A STRING GIVEN IN THE PARAMETERS */ BUILD_AND_STACK_SCALAR_NODE: PROCEDURE ( INP1 , INP2 ) DCL ( INP1 , INP2 ) FIXED BINARY (31,0) A_STRING = • • CALL MOVCHAR( A_STRING , INP1 , INP2 ) GO TO LABI BUILD_AND_STACK_STRING_N0DE: ENTRY ( STRING ) DCL STRING CHAR (100) VARYING A_STRING = STRING LABI: STK_PTR = STK_PTR + 1 IF STK_PTR > MAX_STACK THEN CALL #ERR0R(100) ALLOCATE TREE_N0DE IN ( EXPRESS I ON_ARE A ) SUBTREE _PTR( STK_PTR ) = NODE_POINTER SUBTREE_TYPE( STK_PTR ) = SCALAR LLINK , RLINK = NULL $#DIMENSIONS = STRING_P0INTER = POI NTER_TO_STR I NG ( A_STRING , EXPRESSION_AREA ) #_OF_TIMES_TO_USE = UNDEFINED $T Y PE_CODE, T YPE_ EX P= SCALAR; SEQ_#_PTR = NULL TRANSPOSE_TAG , NEGATE_TAG , IDENTITY_TAG = NO END 8UILD_AND_STACK_SCALAR_N0DE /* THIS PROCEDURE DETERMINES IF A FUNCTION IS TYPE OL/2 */ IS_AN_0L2_ENTRY: PROCEDURE ( TEMPIDENT ) RETURNS ( BIT (1) ) DCL TEMPIDENT CHAR(32) VARYING DCL OL2_ENTRIES(5) CHAR(7) INITIAL ('MAXRSUB', 'MAXCSUB •MINRSUB', 'MINCSUB 1 , • '); DO I = 1 TO 5; IF 0L2_ENTRIES(I )=TEMPIDENT END; RETURN I'O'B ) END IS_AN_0L2_ENTRY THEN RETURN( »1'B) 61 APPENDIX C OL/2 CODE GENERATOR • 10' •18« •11» , •19' t t CODER: PROCEDURE ( STK,_PTR , TREE_PTR t VECTOR_TEMP_VAR I ABLE_#, SCALAR_TEMP_VARIABLE_#, MATR I X_TEMP_VAR I ABL E_# ,CURRENT_COL#, CURRENT_ROW#) ; DCL (A_STRIMG, B_STRING, C_STR1NG) CHAR(IOO) VARYING, EXPRESSION_AREA AREA(1500)» OUTPUT_BUFFER CHAR(118) VARYING, (TREE_PTR, XPTR1, XPTR2) POINTER, H0LD2 POINTER, (SCALAR INITIAL(O), COL_VEC INITIAL(2), ROW_VEC INITIALO), MATRIX INITIAL!*), CURRENT_ROW# , STRING_LENGTH, LOOP_DEPTH, #LINES, CURRENT_COL#, VECTOR_TEMP_VAR I ABLE_# , MATRIX_TEMP_VARIABLE_#, SCAL AR_TEMP_VAR I ABLE_# ) FIXED BIN ( 15,0) , STK_PTR FIXED BIN (31,0), (LSE0,RSEO,RSLTSE0) CHAR(IOO) VARYING, DIGIT_STRINGS (0:25) CHAR(2) VARYING INITIAL ( «0' , '1 • , •2 I , •3' , '4 1 , '5 1 , '6« , '7' , «8' , ■*• , •12' , »13' , '14 1 , '15« , '16' , »17« , •20' , «21« , •22' , '23 1 , »24« , '25 1 ) 1 TREE_NODE BASED ( CURRENT_NODE ) , ( 2 RLINK , 2 LLINK , 2 SEO_*_PTR, 2 STRING_POINTER ) POINTER, ( 2 $#OIMENSIONS, 2 PART_SIZE, 2 #_OF_TIMES_TO_USE , 2 TYPE_EXP , 2 #TEMP_TO_FREE, 2 #CEND_ST ATEMENTS, 2 #REND_STATEMENTS, 2 $TYPE_CODE ) FIXED BINARY (15,0) , ( 2 NEGATE_TAG , 2 TR ANSPOSE_TAG , 2 IDENTITY_TAG ) BIT (1) , 1 VARIABLE_STRING BASED (SP), 2 LEN FIXED BIN (15,0), 2 STRINGS CHAR ( STR ING_LENGTH REFER (LEN)) (BEEN_THROUGH_HERE_BEFORE INITIAL MO'B), YES INITIAL! 'l'B) ) BIT(l) ALIGNED STATIC; DCL TRACEON BIT(l) ALIGNED EXTERNAL; DCL POINTER_TOCSTRING ENTRY (CHAR(IOO) VARYING,) RETURNS (POINTER); DCL CHAR1 CHAR(l), CHAR2 CHAR(2); DCL T_STRING CHAR(5) ; DCL C0DER_0PER(-14:-1 ,0:1,0:1 ) LABEL STATIC; DECLARE (ROOT,CURRENT_NODE) POINTER, (TYPE_RIGHT,TYPE_LEFT, OPERATOR INITIAL (0), VARIABLE INITIAL (1) ) FIXED BINARY (15,0); DECLARE TRAVERSE_STK POINTER CONTROLLED; DCL 1 RESULT_STACK CONTROLLED, 2 PTR_TO_RESULT_NODE POINTER, 62 2 RESULT_SEQ POINTER, 2 RESULT_TYPE FIXED BINARY (15,0); DCL HOLD_PTR POINTERt HOLD_TYPE FIXED BIN(15,0); DCL OUTLINES (20) CHAR(118); DCL (MULTOO( 1:4,1:4) ,MULTOV( 1 : 4,0:4 ) ,MULTVO( 0:4, 1 :4 ) ) LABEL ; DCL (LNODE,RNOOE) POINTER; DCL 1 FREE_ARRAY (-1:15), (2 LTRANSPOSE, 2 RTRANSPOSE, 2 LNEGATE, 2 RNEGATE) CHAR(l), 2 ASSIGNED FIXED BIN(15,0), 2 #FREE FIXED BIN (15,0), 2 FREE_PTR(10) POINTER; DCL LEVEL# FIXED BIN (15,0); INITIALIZE STATIC LABEL ARRAY TO BE USED AS SWITCH. THIS IS DONE ONLY ONCE DURING COMPILATION OF USER'S PROGRAM. 1ST SUBSCRIPT IS THE TYPE OF OPERATOR 2ND SUBSCRIPT IS TYPE OF NODE ON LEFT SUBTREE 3RD SUBSCRIPT IS TYPE OF NODE ON RIGHT SUBTREE IF BEEN_THROUGH_HERE_BEFORE THEN GO TO AROUND_LABEL ; /* FUNCTION OPERATOR */ CODER_OPER (-14,0,0 ) =C0DER_ILL EG AL; CODER_OPER (-1 4, 0,1)=C0DER_ ILLEGAL; C0DER_0PER(-14,1 ,0 ) =CODER_FUNC_VAR_OP ; CODER_OPER (-14, 1 , 1 ) =CODER_FUNC_VAR_VAR ; /* FUNCTION ARGUMENT SEPARATOR OPERATOR */ C0DER_0PER(-1 3,0,0 )=CODER_SEP_OP_OP; C0DER_OPER(-13,0,l ) =CODER_SEP_OP_VAR ; C0DER_0PER(-13,1,0)=C0DER_SEP_VAR_0P; C0DER_0PER(-13,1,1 ) =CODER_SEP_VAR_VAR ; /* NOT USED */ C0DER_0PER(-12,0,0 ) =CODER_I LLEGAL ; CODER_OP ER(-1 2, 0,1)=C0DER_ ILLEGAL; C0DER_0PER(-1 2, 1,0)=C0DER_I LLEGAL; CODER_OPER ( -12, 1,1)=C0DER_IL LEGAL; C0DER_0PER(-1 1,0, 0)=CODER_I LLEGAL; C0DER_0PER(-11,0,1)=C0DER_ ILLEGAL; CODER_OPER(-11,1,0)=CODER .ILLEGAL; C0DER_0PER(-11,1,1)=C0DER_ILLEGAL; /* TRANSPOSE OPERATOR */ C0DER_0PER(-10,0,0)=C0DER_TRAN_0P; C0DER_0PER(-10,0,1)=C0DER_TRAN_VAR; CODER_OPER (-10, 1,0 )=CODER_ ILLEGAL; COOER_OPER(-10,1,1 )=CODER_I LLEGAL; /* UNIARY MINUS OPERATOR */ C0DER_OPER(-O9,0,0)=CODER_UNIM_OP; C0DER_0PER(-09,0,1)=C0DER_UNIM_VAR; CODER_OP ER( -09, 1,0)=CODER_I LLEGAL; CODER_OPER ( -09, 1,1)=C0DER_ ILLEGAL; /* NORM OPERATOR */ C0DER_0PER(-08,0,0)=C0L_WALK_RIGHT; C0DER_0PER(-08 ,0 , 1 ) =C00ER_N0RM_VAR ; CODER_OPER (-08, 1,0 )=CODER_ ILLEGAL; 63 C0DER_0PER(-08,1,1 ) =CODER_I LL6GAL ; /* EQUAL OPERATOR */ CODER_OPER(-07,0,0 ) =CODER_I LLEGAL ; CODER_OPER( -07, 0,1)=CODER_ ILLEGAL; CODER_OPER(-07,l,0)=CODER_EQUL_VAR_OP; C0DER_0PER(-07,1,1)=C0DER_EQUL_VAR_VAR; /* INNERPRODUCT OPERATOR */ C0DER_0PER(-06,0,0)=C0L_WALK_LEFT; C0DER_0PER(-06,0,1 ) =COL_WALK_LEFT ; C0DER_0PER(-06,1,0)=C0L_WALK_RIGHT{ C0DER_0PER(-06,1 ,1 ) =COdIr_I PRD_VAR_VAR ; /* EXPONENTIATION OPERATOR */ CODE R_OPER< -05, 0,0)=CODER_I LLEGAL; C0DER_0PER(-05,0,1)=C0DER_EXPN_0P; C0DER_0PER(-05,1,0)=C0DER_ILLEGAL; CODER_OPER(-05,1,1)=CODER_EXPN_VAR; /* MULTIPLY OPERATOR */ CODER_OPER( -04,0,0 )=CODER_MULT_OP_OP; CODER_OPER( -04,0,1 ) =CODER_MULT_OP_VAR ; C0DER_0PER(-04,1,0)=C0DER_MULT_VAR_0P; C0DER_0PER(-04,1 ,1 ) =CODER_MULT_VAR_VAR ; /* DIVIDE OPERATOR */ CODE R_OPER( -03,0,0 )=CODER_I LLEGAL; C0DER_0PER(-03,0,1)=C0DER_DIVD_0P; CODER_OPER( -03, 1,0)=C0DER_I LLEGAL; C0DER_0PER(-03,1,1)=C0DER_DIVD_VAR; /* SUBTRACTION OPERATOR */ C0DER_OPER(-02,0,0)=C0DER_SUBT_OP_OP; C0DER_0PER(-02,0,1 ) =CODER_SUBT_OP_VAR ; C0DER_0PER(-02,1,0)=C0DER_SUBT_VAR_0P; C0DER_0PER(-O2,l ,1 ) =CODER_SUBT_VAR_VAR ; /* ADDITION OPERATOR */ CODER_OPER(-01,0,0)=CODER_ADDS_OP_OP; C0DER_0PER(-01,0,1)=C0DER_ADDS_0P_VAR; CODER_OPER(-01 ,1 ,0 )=CODER_ADDS_VAR_OP ; C0DER_0PER(-01,1,1)=C0DER_ADDS_VAR_VAR; BEEN_THROUGH_HERE_BEFORE=YES; RETURN; AROUND_LABEL : R00T,CURRENT_NODE=TREE_PTR; IF TREE CONSISTS OF ONLY A VARIABLE THEN RETURN IF ROOT->$TYPE_CODE >= THEN RETURN; INITIALIZE INDICATORS AND COUNTERS DO I = -1 TO 15; LTRANSPOSE( I ) ,R TRANSPOSE ( I )='0' ; LNEGATEt I ) ,RNEGATE( I )='0« ; ASSIGNED( I )=0; #FREE(I)=0; END; LEVEL#,LOOP_DEPTH,#LINES=0; ON ERROR BEGIN; Six ON ERROR CALL IHEDUMP; PUT DATA (A_STRING, B_STRINGt C_STRING T TYPEJUGHT, TYPE_LEFT, OUTPUT_BUFFER ) ; PUT SKIP; PUT LIST ( '$TYPE_CODE=« t$TYPE_CODEt •TYPE_EXP=«, TYPE_EXP) SKIP; DO I = 1 TO #LINES; PUT LIST (OUTLINESU) ) SKIP; .END; CALL CLOSETR; END; set types of right £ left node and branch through switch to proper routine next_node: lnode=llink; rnode=rlink; if lnode=null i lnode->$type_code<0 then type_left=operator; else type_left=variable; if rnode->$type_ccde<0 then type_r ight=operator ; else type_right=variable; call goto(coder_oper(current_node->$type_codettype_left, type_right) ) ; stack name for new temporary col vector and walk right col_walk_right: MULTVOU,2): MULTVO(3t2>: MULT00(4,2): IF RNODE->$TYPP_CODE=-10 I RN0DE->$TYPE_C0DE=-9 THEN GO TO WALK_RIGHT; ALLOCATE RESULT_STACK ; #TEMP_TO_FREE=*TEMP_TO_FREE+l ; PTR_TO_RESULT_NODE=POINTER_TOCSTRING( 'STEMPl' || DIGIT_STRINGS( VECTOR_TEMP_VAR I ABLE_# ) ,EXPRESSI ON_AREA ) ; RESULT_SEO=NULL; VECTOR_TEMP_VARIABLE_#=VECTOR_TEMP_VARIABLE_#+l; RESULT_TYPE = COL_VEC; GO TO WALK_RIGHT; /♦***#*♦#****#*********#****♦* STACK NAME FOR NEW TEMPORARY ROW VECTOR AND WALK RIGHT ROW_WALK_RIGHT: MULTVO(2,3): ALLOCATE RESULT_STACK ; #TEMP_TO_FREE=#TEMP_TO_FREE+l ; RESULT_SEQ=NULL; PTR_TO_RESULT_NODE=POINTER_TOCSTRING( •STEMPl 1 I I DIGIT_STRINGS( VECTOR _TEMP_VAR I ABLE_# ) t EXPRESS I ON_AR EA ) ; VECTOR_TEMP_VARIABLE_#=VECTOR_TEMP_VARIABLE_#+l ; RESULT_TYPE=ROW_VEC; GO TO WALK_RIGHT; CODER_ADDS_VAR_OP: " 65 CODER_SUBT_VAR_OP : IF PTR_TO_RESULT_NODE=LNODE->STRING_POINTER THEN DO; IF RESULT_TYPE=COL_VEC THEN GO TO COL_WALK_R IGHT ; IF RESULT_TYPE = ROW_VEC THEN GO TO ROW_WALK_R IGHT ; END; MULTVO(0»2): MULTV0(0,3): CODER_UNIM_OP : IF LEFT SUBTREE IS ROW OR COL OF MATRIX AND RIGHT SUBTREE IS MATRIX EXPRESSION GO TO GET NEW TEMPORARY IF LNODE--=NULL I LNODE->TYPE_EXPTYPE_EXP=ROW_VEC THEN GOTO ROW_WALK_RIGHT ; IF LNODE->TYPE_EXP=COL_VEC THEN GOTO COL_WALK_R IGHT; END; /* **************************** RESTACK CURRENT TEMPORARY VARIABLE AND WALK RIGHT RESTACK_RIGHT: HOLD_PTR=PTR_TO_RESULT_NODE; H0LD2=RESULT_SE0; HOLO_TYPE=RESULT_TYPE; ALLOCATE RESULT_STACK ; #TEMP_TO_FREE=#TEMP_TO_FREE+l ; PTR_TO_RESULT_NODE=HOLD_PTR ; RESULT_SEO=HOLD2; RESULT_TYPE=HOLD_TYPE; GO TO WALK_RIGHT; /***************************** STACK NAME FOR NEW TEMPORARY COL VECTOR AND WALK LEFT COL_WALK_LEFT: MULTOO(2,3): MULTOV(2 t 3): IF LNODE->$TYPE_CODE=-10 I LN0DE->$TYPE_C0DE=-9 THEN GO TO WALK_LEFT; ALLOCATE RESULT_STACK; #TEMP_TO_FREE=#TEMP_TO_FREE+l ; PTR_TO_RESULT_NODE=POINTER_TOCSTRING( «$TEMP1» || DIGIT_STRINGS(VECTOR_TEMP_VARIABLE_#),EXPRESSION_AREA) ; RESULT_SEQ=NULL; VECTOR_TEMP_VARIABLE_#=VECTOR_TEMP_VARIABLE_#+l; RESULT_TYPE=COL_VEC; GO TO WALK_LEFT; SET UP ROW PARTITIONING LOOP it***************************/ MULTOVU,$#D I MENS I ONS= 1 ) £ LOOP_DEPTH = f. LNDDE->$TYPE_CODE-^=-10 & LN0DE->STYPE_C0DE-=-9 THEN DO; CURRENT_ROW#=CURRENT_ROW#+l ; C_STRING=PTR_TO_RESULT_NODE->STRINGS; OUTPUT_BUFFER='DO #ROW' I I DI G I T_STR INGS ( CURRENT_ROW#) I I » = « || C_STRING II «->#LOWER(l) TO • H C_STRI NG | I '->#UPPER( 1) ;• 66 CALL SKIP_ANDCOUTPUT; L00P_DEPTH=L00P_DEPTH+1 ; #REND_STATEMENTS=#REND_STATEMENTS+1; END; IF RESULT_TYPE=COL_VEC THEN GO TO MATR I X_WALK J_EFT ; STACK NAME FOR NEW TEMPORARY ROW VECTOR AND WALK LEFT ROW_WALK_LEFT: MULTOO(3t2): MULT00(3,4): MULT0V(3,4): MULTOV(3t2): if lnode->$type_code=-10 i ln0de->$type_c0de=-9 then go to walk_left; allocate result_stack ; #temp_to_free=#temp_to_free+l ; ptr_to_result_node=pointer_tocstring( 'stempl' i i digit_strings( vector _temp_var i able_# ) texpress i on_ar ea ) ; result_seq=null; vector_temp_variable_#=vector_temp_variable_#+l; result_type = row_vec; go to walk_left; MULTOVUtO): CODER_DIVD_OP: CODER_ADDS_OP_OP: CODER_SUBT_OP_OP: SET UP COLUMN PARTITIONING LOOP IF RESULT_TYPE=MATRIX £ LNODE->$TYPE_CODE-=-l & LN0DE->$TYPE_C0DE-.=-9 THEN DO; CURRENT_COL#=CURRENT_COL#+l ; C_STRlNG=PTR_TO_RESULT_NODE->STRINGS; OUTPUT_BUFFER=«DO #COL • II DI GI T_STR INGS ( CURRENT_COL# ) II ' = ' II C_STRING || '->#LOWER(2) TO • I I C_STRING II '->#UPPER(2) ; • ; CALL SKIP_ANDCOUTPUT; #CEND_STATEMENTS = #CEIMD_STATEMENTS+1 ; LOOP_DEPTH = LOOP_DEPTH + l ; GO TO COL_WALK_LEFT; END; CODER_ADDS_OP_VAR : COOER_SUBT_OP_VAR : IF PTR_TO_RESULT_NODE=RNODE->STRlNG_POINTER THEN DO; IF RESULT_TYPE=ROW_VEC THEN GO TO ROW_WALK_LEFT ; ELSE GO TO COL_WALK_LEFT; END; MULTOV(2,0): MULTOV(3,0): RESTACK CURRENT TEMPORARY VARIABLE AND WALK LEFT HOLD_PTR=PTR_TO_RESULT_NODE; HOLD2=RESULT_SEO; HOLD_TYPE=RESULT_TYPE; ALLOCATE RESULT_STACK ; #TEMP_TO_FREE=#TEMP_TO_FREE+l; 67 ptr_to_result_node=hold_ptr; result_seq=h0ld2; result_type=holo_type; go to walk_left; coder_adds_var_var : output_buffer='call 30ladd(« go to set_operanos; coder_subt_var_var: output_buffer='call 30lsub(« go to set_operands; coder_divd_var: output buffer=«call 30ldivd(» SET_OPERANDS: CALL SET_VARIABLES; /************************* SET SYSTEM PARTITIONING DATA FOR EACH OPERAND » ♦ * * ******* SET_ROW_OR_COL: IF RESULT_TYPE=ROW_VEC THEN DO ; C_STRING=C_STRING II «3,0' IF LNODE->$#DIMENSIONS=l THEN A_STRING=A_STRING II ^O" A_STRING=A_STRING I I ^^ROW 1 CURRENT_ROW#) ; IF RNODE->$#DIMENSIONS=l THEN B_STRING=B_STRING I I '3,0« B_STRING=B_STRING II •3,#ROW« CURRENT_ROW#); /*********** **********************/ ; ELSE I I DIGIT STRINGS! ELSE DIGIT STRINGS! *********** (NOT PARTITIONED) ******* IF BOTH OPERANDS WERE TRUE VECTORS AND ARE INSIDE PARTITIONING LOOP THEN OUTPUT CODE NOW SO WON'T BE RECOMPUTED EACH TIME THRU LOOP ***************/ L RNODE->$#DIMENSIONS=l ************** IF LNODE->$#DIMENSIONS=l THEN DO; IF LOOP_DEPTH>0 THEN DO; IF $TYPE_CODE=-3 THEN DO; B_STRING=XPTR2->STRINGS; B_STRING=B_STRING I I ' ,0, 1 ,0,0 t O • ; END; OUTPUT_BUFFER=OUTPUT_BUFFER II A_STRING II •,' II B_STRING || ',' II C_STRING I I •);•; PUT FILE(SYSPRINT) LIST < OUTPUT_BUFFER > SKIP; OUTPUT_BUFFER=' • ; TYPE_EXP=ROW_VEC; STRlNG_POINTER=PTR_TO_RESULT_NODE; GO TO WALK_UP; END; END; CURRENT_NODE->TYPE_EXP=ROW_VEC; END; ELSE IF RESULT_TYPE=COL_VEC THEN DO; C_STRING=C_STRING I I «2,0» ; IF LN0DE->$#DIMENSI0NS=1 THEN A_STRING = A_STRING I I ^O' ; ELSE 68 A_STRING=A_STRING I I ^ttfCOL* II DIGIT_STR INGS ( CURRENT_COL#) ; IF RNODE->$#DIMENSIONS=l THEN B_STRING=B_STRING II »2 t 0' *, ELSE B_STRING=B_STRING I I •2»#COL' II DIG IT_STR INGS ( CURRENT_COL#) ; IF BOTH OPERANDS WERE TRUE VECTORS (NOT PARTITIONED) AND ARE INSIDE PARTITIONING LOOP THEN OUTPUT CODE NOW SO WON'T BE RECOMPUTED EACH TIME THRU LOOP IF LNODE->$#DIMENSIONS=1 £ RNODE->$#DIMENSI ONS = l THEN DO; if loop_depth>0 then do; if $type_c0de=-3 then do; b_string=xptr2->strings; b_string = b_string ii • » 0, 1 , 0» o t • ; end; output_buffer=output_buffer ii a_string ii s' ii b_string || •♦• ii c_string ii •);•; put file(sysprint) list ( output_buffer ) skip; output_buffer=' • ; type_exp=col_vec; string_pointer=ptr_to_result_node; GO TO walkjjp; END; END; CURRENT_NODE->TYPE_EXP=COL_VEC; END; ELSE IF RESULT_TYPE=MATRIX THEN DO; IF LNODE->TYPE_EXP=ROW_VEC I RNODE->T YPE_EXP=ROW_VEC THEN 00; IF LNODE->TYPE_EXP=ROW_VEC THEN A_STR ING=A_STR I NG II •3,0'; ELSE A_STRING = A_STRING I I •BttfROW 1 II DIG IT_STR INGS ( CURRENT_ROW#) ; IF RNODE->TYPE_EXP=ROW_VEC THEN B_STR ING=B_STR I NG II «3,0'; ELSE B_STRING=8_STRING II '3,#R0W' II DIG IT_STR INGS ( CURRENT_ROW#) ; C_STRING = C_STRING I I •3t#R0W' II DIG IT_STR INGS ( CURRENT_ROW#) ; END; ELSE IF LNODE->TYPE_EXP=COL_VEC I RNODE->TYPE_EXP= COL_VEC THEN DO; IF LNODE->TYPE_EXP=COL_VEC THEN A_STR I NG=A_STR I NG II «2t0'; ELSE A_STRING = A_STRING II ^ttfCOL 1 II DIGI T_STR I NGS ( CURRENT_COL#) 5 IF RNODE->TYPE_EXP=COL_VEC THEN B_STR I NG=B_STR I NG II '2 t 0'; ELSE B_STRING=B_STRING II ^ttfCOL* II DIG I T_STR I NGS ( CURRENT_COL#) ; C_STRING=C_STRING II »2»#C0L' II DIG I T_STR I NGS ( CURRENT_COL#) ; END; ELSE DO; A_STRING = A_STRING II '^tO 1 ; B_STRING = B_STRING I I "*<,0\; €9 C_STRING=C_STRING II '4,0»; IF BOTH OPERANDS WERE TRUE MATRICES (NOT PARTITIONED) AND ARE INSIDE PARTITIONING LOOP THEN OUTPUT CODE NOW SO WON'T BE RECOMPUTED EACH TIME THRU LOOP • ft**************************/ IF LOOP_DEPTH>0 THEN DO; IF $TYPE_CODE=-3 THEN DO; B_STRING=XPTR2->STRINGS; B_STRING = B_STRING I I • ♦ 0, 1 , 0,0, • ; END; 0UTPUT_BUFFER=0UTPUT_8UFFER II A_STRING II ',• II B_STRING II ' ,» II C_STRING II •);' ; PUT LIST(OUTPUT_BUFFER) SKIP; OUTPUT_BUFFER=» • ; STRING_POINTER=PTR_TO_RESULT_NODE; GO TO WALK_UP; END; END; CURRENT_NODE->TYPE_EXP= MATRIX; END; IF $TYPE_C0DE=-3 THEN DO; B_STR I NG=XPTR2-> STRINGS; B_STRING = B_STRING I I •,0,1,0,0,0'; END; OUTPUT_BUFFER = OUTPUT_BUFFER I I A_STRING II ',' II B_STRING II », • II C_STRING I I ' ) ;• ; CALL SKIP_ANDCOUTPUT; CURRENT_NODE->STRING_POINTER=PTR_TO_RESULT_NODE; GO TO WALK_UP; CODER_NORM_VAR: XPTR2=RN0DE->STRING_P0INTER; B_STRING=XPTR2->STRINGS; IF RNODE->SEQ_#_PTR=NULL THEN RSEQ='0'; ELSE DO; SP=RNODE->SEQ_#_PTR; RSEO=STRINGS; END; B_STRING=B_STRING II ',' II RSEQ II ',0,1, • II RNEGATE(LEVEL#> ; CURRENT_NODE->STRING_POINTER=POINTER_TOCSTRING( •aOLNORM(' || B_STRING II •)• , EXPRESS ION_AREA ) ; CURRENT_NODE->TYPE_EXP=SCALAR; GO TO WALK_UP; CODER_I PRD_VAR_VAR : CALL SET_VARIABLES; CURRENT_NODE->STRING_POINTER=POINTER_TOCSTRING( •aOLIPRDM II A_STRING II ',' II B_STRING II ')' , EXPRESSION_AREA) ; CURRENT_NODE->TYPE_EXP= SCALAR; GO TO WALK_UP; /***************************** SET NEGATE INDICATORS CODER_UNIM_VAR: HOLD_PTR=TRA VERSE_STK ; IF HOLD_PTR->RLINK=CURRENT_NODE THEN DO; 70 IF RNEGATE(LEVEL#)= , 0« £ LNEGATE ( IE VEL# )= • • THEN RNEGATE(LEVEL#-1)= , 1' ; ELSE RNEGATE(LEVEL#-1)= , 0« ; END; ELSE DO; IF LNEGATE(LEVEL#)='0» £ RNEGATE ( LEVEL# ) = »0 • THEN LNEGATE(LEVEL#-1)= , 1'; ELSE LNEGATE ( LEVEL#-1 )=• • end; string_pointer=rnode->string_pointer; go to walk up; « coder_expn_op : allocate result_stack ; #temp_to_free=#temp_to_free+l ; ptr_to_result_node=p0inter_t0cstring( «$temp2' i i digit_strings(matrix_temp_variable_#) texpressi on_ar ea ) ; result_seo=null; matrix_temp_variarle_#=matrix_temp_varia8le_#+l; result_type=matrix; go to walk_left; coder_expn_var: call set_variables; output_buffer=output_buffer ii 'call 30pexpnm ii a_string ii •,' || b_string ii • t' II C_STRING II ');• ; CALL SKIP_ANDCOUTPUT; TYPE_EXP=MATRIX; GO TO WALK_UP; CODER_TRAN_OP : IF RNODE->$TYPE_CODE=- ALLOCATE RESULT_ST ACK ; result_seq=null; hold_ptr=traverse_stk; hold_ptr->«temp_to_fre IF RNODE->TYPE_EXP=MAT PTR_TO_RESULT_NODE DIGIT_STRINGS(MAT EXPRESSION_AREA) ; MATRIX_TEMP_VARI AB RESULT_TYPE=MATRIX GO TO WALK_RIGHT; END; ELSE IF RNODE->TYPE_EX PTR_TO_RESULT_NODE DIGIT_STRINGS(VECT EXPRESSION_AREA) ; VECTOR_TEMP_VARIAB RESULT_TYPE = COL_VE GO TO WALK RIGHT; END; ELSE DO; PTR_TO_RESULT_NODE DIGIT_STRINGS(VECT EXPRESSION_AREA) ; VECTOR_TEMP_VARI A3 RESULT_TYPE = ROW_VE GO TO WALK_RIGHT; /»******.**** 10 THEN GO TO WALK_RIGHT; E=HOLD_PTR->#TEMP_TO_FREE+l; RIX THEN DO; =P0INTER_T0CSTR1NG( 'STEMPa 1 II RIX_TEMP_VARI ABLE_#) r LE #=MATRIX TEMP VARIABLE # + l *, P=C0L_VEC THEN DO; =POINTER_TOCSTRING( «STEMP1» OR_TEMP_VARIABLE_#) » LE_#=VECTOR_TEMP_VARIABLE_#+l c; = POINTER_TOCSTRING( '$TEMP1« I I OR_TEMP_VAR I ABLE_# ) t LE_#=VECTOR_TEMP_VARI ABLE_#+1 ; C; END; 71 SET TRANSPOSE INDICATORS CODER_TRAN_VAR : HOLD_PTR=TR AVERSE_STK ; "if hold_ptr->rlink=current_nooe THEN do; IF RTRANSPOSE(LEVEL#) = , O l £ LTRANSPOSE ( LEVEL* ) = • • THEN RTRANSPOSE(LEVEL#-l )=*1 ' ; ELSE RTRANSPOSEtLEVELtf-lJ-'O' ; END; ELSE DO; IF RTRANSPOSE(LEVEL#)» , O l £ LTRANSPOSE ( LEVEL* )'* • THEN LTRANSPOSE(LEVEL#-l l-'l 1 ; ELSE LTRANSPOSE(LEVEL#-l)='0» ; END; STRING_POINTER=RNODE->STRING_POINTER; GO TO WALK_UP; SWITCH TO PROPER MULTIPLY ROUTINE BY TYPE OF EXPRESSION OR VARIABLE ON THE SUBTREES CODER_MULT_OP_OP: IF LNODE->TYPE_EXP=SCALAR | RNODE->TYPE_EXP=SCALAR THEN GO TO coder_illegal; go to multoo( lnode->type_exp,rnode->type_exp) ; coder_mult_op_var: if lnode->type_exp=scalar then go to coder_illegal ; go to multov(lnode->type_exp,rnode->type_exp) ; coder_mult_var_op : if rnode->type_exp=scalar then go to coder_i llegal ; go to multvotype_exp,rnode->type_exp); mult00(4,4): /* left and right ops matrices */ if lnode->$type_code=-10 i ln0de->$type_c0de=-9 then go to walk_left; matrix_walk_left: allocate result_stack ; #temp_to_free=#temp_to_free+l; result_seo=null; result_type=matrix; ptr_to_result_node=pointer_tocstring( 'stempz' ii digit_strings(matrix_temp_variable_jm , express i on_area ) ; matrix_temp_variable_#=matrix_temp_variable_#+1; go to walk_left; multv0(4,4): multv0(3t4): multvo<0,4>: output column partitioning loop if result_type = matrix £ rnode->$type_code-.=-10 £ rn0de->$type_c0de-- = -9 then do; current_col«=current_col#+l ; c_string=ptr_to_result_node->strings; output_buffer=«do #col' ii dig i t_str ings ( current_col# ) ii • = ' ii c_string ii '->#lower(2) to • i i c_string || «->#upper(2) ; • ; #cend_statements=#cend_statements+1; loop_depth = loop_depth + l ; call skip_andcoutput; go to col_wal^_right; end; if lnode->type_exp=scalar then go to restack_r ight ; 72 ELSE GO TO COL_WALK_RIGHT; CODER_MULT_VAR_VAR: CALL SET_VARIABLES; OUTPUT_BUFFER=OUTPUT_BUFFER I I 'CALL 30LMULK • /********************* SET UP OPERAND PARTITIONING FOR MULTIPLY ********************* IF LNODE->TYPE_EXP=MATRlX THEN DO; /* MATRIX*MATRIX */ IF RNODE->TYPE_EXP=MATRIX THEN DO; IF RESULT_TYPE=COL_VEC THEN DO; COL_RESULT: A_STRING=A_STRING II 'AtO* ; B_STRING=B_STRING II •ZttfCOL 1 I CURRENT_COL#) ; C_STRING=C_STRING I I •ZtO 1 ; TYPE_EXP=COL_VEC; GO TO SET_TYPE_WALK_UP; END; IF RESULT_TYPE=ROW_VEC THEN DO; ROW_RESULT: A_STRING=A_STRING II '3t#ROW» I CURRENT_ROW# ) ; B_STRING=B_STRING II "V^O* ; C_STRING=C_STRING II '3»0« ; TYPE_EXP=ROW_VEC; GO TO SET_TYPE_WALK_UP; END; IF RESULT_TYPE=MATRIX THEN DO; MATRIX_RESULT: A_STRING = A_STRING I I B_STRING = B_STRING I I C STRING=C STRING ******** ********/ I DIGIT_STRINGSI I DIGIT_STRINGS( I I IF LOOP DEPTH>0 THEN •4,0' do; A_STRING ||B_STRING II '»• || C_STRING BUFFER) SKIP; OUTPUT_BUFFER=OUTPUT_BUFFER II A_ I I ' •) ;•; PUT LIST(OUTPUT J=LENGTH(OUTPUT_BUFFER ) ; PUT FILE(SYSPUNCH) EDIT ( SUBSTR ( OUTPUT_BUFFER , I, J) ) (X( 1),A(79) ) ; string_pointer=ptr_to_result_node; output_buffer=' » ; go to walk_up; end; to set type walk up; END: GO END; /* MATRIX*COLUMN VECTOR */ IF RNODE->TYPE_EXP=COL_VEC THEN DO; A_STRING=A_STRING II •A,0 I ; B_STRING = B_STRING II »2tO'; IF RESULT_TYPE=MATRIX THEN DO; C_STRING=C_STRING || •ZttfCOL' CURRENT_COL#) ; RESULT_TYPE=MATRIX; GO TO SET_TYPE_WALK_UP; ENO; II DIGIT_STRINGS( 73 ELSE DO; C_STRING»C_STRING I I •2,0»; END; TYPE_EXP=COL_VEC; GO TO SET_TYPE_WALK_UP; ENO; /* MATRIX*SCALAR */ IF RNODE->TYPE_EXP=SCALAR THEN DO; B_STRING=B_STRING II 'OfO'; IF RESULT_TYPE « MATRIX THEN DO; A_STRING=A_STRING II '^O'; C_STRING = C_STRING I I '^tO'; GO TO SET_TYPE_WALK_UP; END; IF RESULT_TYPE = COL_VEC THEN DO; A_STRING = A_STRING I I '2,#COL' II DIGI T_STRI NGS < CURRENT_COL#) ; C_STRING=C_STRING II '2tO'; TYPE_EXP=COL_VEC; GO TO SET_TYPE_WALK_UP; END; IF RESULT_TYPE=ROW_VEC THEN DO; A_STRING = A_STRING II «3,#ROW« II DIG I T_STRI NGS ( CURRENT_ROW#) ; C_STRING=C_STRING I I »3,0»; TYPE_EXP=ROW_VEC; GO TO SET_TYPE_WALK_UP; END; ELSE GO TO CODER_ILLEGAL; end; ELSE GO TO CODER_ILLEGALJ END; IF LNODE->TYPE_EXP=ROW_VEC THEN DO; /* ROW VECTOR*MATRIX */ IF RNODE->TYPE_EXP=MATRIX THEN DO; A_STRING=A_STRING II ^tO'; B_STRING=B_STRING I I 'AtO'; IF RESULT_TYPE=MATRIX THEN DO; C_STRING = C_STRING II ^t^ROW 1 I I DIG I T_STR I NGS ( CURRENT_ROW#) ; TYPE_EXP=MATRIX; GO TO SET_TYPE_WALK_UP; END; ELSE DO; C_STRING = C_STRING I I «3,0'; TYPE_EXP=ROW_VEC; GO TO SET_TYPE_WALK_UP; END; END; /* ROW VECTOR*COLUMN VECTOR */ IF RNODE->TYPE_EXP=COl_VEC THEN DO; IF RESULT_TYPE=COL_VEC THEN DO; A_STRING=A_STRING II '3,0'; B_STRING=B_STRING II '2tO»; C_STRING = C_STRING II •3 t #R0W' I I DIG I T_STR I NGS ( CURRENT_ROW# ) ; TYPE_EXP=COL_VEC; GO TO SET_TYPE_WALK_UP; END; END; 74 ELSE GO TO CODER_ILLEGAL; /* ROW VECTOR*SCALAR */ IF RNODE->TYPE_EXP=SCALAR THEN DO; A_STRING=A_STRING II ^tO'; B_STRING=B_STRING II '0,0«; IF RESULT_TYPE=MATRIX THEN C_STRING = C_STRING I I •3»#ROW' CURRENT_ROW#) ; ELSE C_STRING = C_STRING I I , 3,6 I ; GO TO SET_TYPE_WALK_UP; END; END; II DIGIT_STRINGS( IF LNODE->TYPE_EXP=COL_VEC THEN DO; /* COLUMN VECTOR*ROW VECTOR */ IF RNODE->TYPE_EXP=ROW_VEC THEN DO; IF RESULT_TYPE=COL_VEC THEN GO TO COL_RESULT; ELSE IF RESULT_TYPE=ROW_VEC THEN GO TO ROW_RESULT; ELSE IF RESULT_TYPE=MATRIX THEN GO TO MATRIX_RESULT; END; /* COLUMN VECTOR*SCALAR */ IF RNODE->TYPE_EXP=SCALAR THEN DO; A_STRING = A_STRING II ^tO'; B_STRING=B_STRING II '0,0«; IF RESULT_TYPE=MATRIX THEN C_STRING=C_STRING II , 2»#COL l CURRENT_COL#) ; ELSE C_STRING=C_STRING II •2,0«; GO TO SET_TYPE_WALK_UP; END; END; II DIGIT_STRINGS( if lnode->type_exp=scalar then do; a_string=a_string ii '0»0'; /* scalar-matrix */ if rnode->type_exp=matr if result_type=matr b_string = b_stri c_string=c_stri go to set_type_ if result_type=row_ b_string=b_strT current_row# ) ; c_string=c_stri type_exp=row_ve go to set_type_ if result_type=col_ b_string=b_strT current_col#) ; c_string=c_stri type_exp=col_ve go to set_type_ END; /* SCALAR*COLU IF RNODE->TYPE B_STRING = B. IF RESULT. C STRI IX THEN DO; IX THEN DO; NG I | •4,0« ; NG I I '^tO 1 ; WALK_UP; END; VEC THEN DO? NG II •3»#ROW || DIGIT_STRINGS( NG I I '3,0« ; c; walk_up; end; vec then do; NG II '2,#COL' II DIGIT_STRINGS( NG || '2»0« ; C; WALK UP; END; MN VECTOR */ _EXP=COL_VEC THEN DO; _STRING I I '2»0' ; TYPE=MATRIX THEN NG = C STRING I I ^ttfCOL 1 I I DIGIT_STRINGS( 75 CURRENT_COL#>; ELSE C_STRING=C_STRING II •2,0'; GO TO SET_TYPE_WALK_UP; END; /* SCALAR*ROW VECTOR */ IF RNODE->TYPE_EXP=ROW_VEC THEN DO; B_STRING = B_STRING I I •3,0 I ; IF RESULT_TYPE=MATRIX THEN C_STRING = C_STRING I I ^tiROW' I I DIG I T_STR I NGS ( CURRENT_ROW# ) ; ELSE C_STRING=C_STRING II ^tO 1 ; GO TO SET_TYPE_WALK_UP; END; END; SET_TYPE_WALK_UP: OUTPUT_BUFFER=OUTPUT_BUFFER I I A_STRING I I »»* II B_STRING II 't« II C_STRING II •);• ; CALL SKIP_ANDCOUTPUT; CURRENT_NriDE->STRING_POINTER = PTR_TO_RESULT_NODE; GO TO WALK_UP; CODER_FUNC_VAR_VAR : XPTRl=LNODE->STRING_POINTER; XPTR2= RNOD E->STR I NG_ POINTER; A_STRING=XPTR1->STRINGS; B_STRING=XPTR2->STRINGS; STRING_POINTER = POINTER_TOCSTRING(A_STRING I I B_STRING II ')• ♦ EXPRESSION_AREA); TYPE_EXP=SCALAR ; GO TO WALK_UP; CODER_FUNC_VAR_OP : LOOP_DEPTH=LOOP_DEPTH+l; IF RNODE->$TYPE_CODE=-13 THEN GO TO WALK_RIGHT; ELSE GO TO CODER_SEP_VAR_OP; CODER_SEP_OP_OP: CODER_SEP_OP_VAR: IF LNODE->$TYPE_CODE=-13 THEN GO TO WALK_LEFT; IF LNODE->TYPE_EXP=MATRlX THEN GO TO MATR IX_W ALK_LEFT ; ELSE IF LNODE->TYPE_EXP=COL_VEC THEN GO TO COL_WALK_LFFT ; ELSE IF LNODE->TYPE_EXP=ROW_VEC THEN GO TO ROW_WALK_LEFT ; ELSE GO TO coder_illegal; CODER_SEP_VAR_OP: IF RNODE->TYPE_EXP=MATRIX THEN DO; ALLOCATE RESULT_STACK ; RESULT_SEQ=NULL; #TEMP_TO_FREE=#TEMP_TO_FREE+l ; PTR_TO_RESULT_NODE=POINTER_TOCSTRING( 'STEMPZ' II DIGIT_STRINGS(MATRIX_TEMP_VARIABLE_#) t EXPRESSION_AREA) ; MATRIX_TEMP_VARIABLE_#=MATRIX_TEMP_VARIABLE_#+1 ; RESULT_TYPE=MATRIX; GO TO WALK_RIGHT; END; ELSE IF RNODE->TYPE_EXP=COL_VEC THEN GO TO COL_WALK_R IGHT ; ELSE IF RNODE->TYPE_EXP=ROW_VEC THEN GO TO ROW_WALK_R I GHT ; ELSE GO TO CODER_ILLEGAL; 76 CODER_SEP_VAR_VAR : XPTR1=LN0DE->STRING_P0INTER; XPTR2=RN0DE->STRING_P0INTER; A_STRING=XPTR1->STRINGS; B_STR I NG=XPTR2-> STRINGS; STRING_POINTER=POINTER_TOCSTRING(A_STRING II •,' II B_STRING , EXPRESSION_AREA) ; GO TO WALK_UP; CODER_EQUL_VAR_OP: IF RNODE->$TYPE_COOE=-10 THEN GO TO WALK_RIGHT; ALLOCATE RESULT_STACK ; ■ • #TEMP_TO_FREE=#TEMP_TO_FREE+l; PTR_TO_RESULT_NODE=LNODE->STRING_POINTER; RESULT_TYPE=LNODE->TYPE_EXP; RESULT_SEO=LNODE->SEO_«_PTR; GO TO WALK_RIGHT; CODER_EQUL_VAR_VAR : XPTRl=LNODE->STRING_POINTER; XPTR2=RN0DE->STRING_P0INTER; A_STR I NG=XPTRl-> STRINGS; B_STRING=XPTR2->STRINGS; IF LNODE->TYPE_EXP=SCALAR £ RNODE->T YPE_EXP=SCALAR THEN 00; CHAR1=A_STRING; CHAR2=A_STR I NG ; /* IF IT IS AN OL/2 SCALAR */ IF CHAR1='$« £ CHAR2-='ST» THEN GO TO 0L2ASGN; OUTPUT_BUFFER = OUTPUT_BUFFER I I A_STRING II '=• II B_STRING II ';• ; CALL SKIP_ANDCOUTPUT; GO TO WALK_UP; END; IF ASSIGNEO(LEVEL#)=0 THEN DO; FOR SIMPLE ASSIGNMENT OR UNFINISHED MULTIPLE ASSIGNMENT STATEMENT THEN OUTPUT CODE *******/ 0L2ASGN: IF LNODE->SEQ_#_PTR=NULL THEN LSEO='0'; ELSE DO; SP=LNODE->SEQ_#_PTR ; LSEO=STRINGS; END; IF RNODE->SEO_#_PTR=NULL THEN RSEO= , 0'; ELSE DO; SP=RNODE->SEQ_#_PTR ; RSEO=STRINGS; END; OUTPUT_BUFFER='CALL 30LASGN(» I I A_STRING I I LSEO I I • t 0,l,0« I | B_STRING || ', ' II RSEO I LEVEL*) II 'tit' II RNEGATE(LEVEL#) II '); call skip_andcoutput; end; string_pointer=lnode->string_po inter; go to walk up; WALK_LEFT: ALLOCATE TR AVERSE_STK ; TRAVERSE_STK=CURRENT_NODE; CURRENT_NODE=CURRENT_NODE->LLINK; LEVEL#=LEVEL#+l; 77 go to next_node; walk_right: allocate traverse_stk; traverse_stk=current_node; current_node=current_node->rlink; LEVEL#=LEVEL#+l; GO TO next_node; WALK_UP: if -allocation(traverse_stk) then go to leave_coder; current_node->stype_code=o; ltranspose( level*) ,r transpose ( level* )=' • ; lnegate( level* ) , rnegate ( level* )=' ' ; output partitioning loop end statements do while (#cend_statements>0>; output_buffer= , end; ' ; call skip_andcoutput; *cend_statements=*cend_statements-1; current_col*=current_col*-l ; loop_depth=loop_depth-l; end; do while urend_statements>0); output_buffer= , end; • ; call skip_andcoutput; *rend_statements=*rend_statements-1; current_row#=current_row#-l ; loop_depth=loop_depth-l; end; current_node = traverse_stk; free traverse_stk; level*=level*-1; stack temporary variables to be freed *********************♦*******/ DO WHILE (#TEMP_TO_FREE>0) ; *FREE( LEVEL *-l)=*FREE(LEVEL#-l)+l; FREE_PTR(LEVEL*-1,*FREE(LEVEL*-1) >=PTR_TO_RESULT_NODE ; FREE RESULT_STACK; IF ALLOCATION(RESULT_STACK) THEN IF FREE_PTR(LEVEL*-1»#FREE(LEVEL#-1> )= PTR_TO_RESULT_NODE THEN *FREE ( LEVEL*-1 ) = #FREE(LEVEL#-1 )-l; *TEMP_TO_FREE = #TEMP_TO_FREE-l ; END; IF LEVEL*=0 THEN GO TO NEXT_NODE; FREE TEMPORARY VARIABLES WHEN OUTSIDE A SYSTEMS PARTITIONING LOOP ft***********:*****************/ IF LOOP_DEPTH=0 THEN DO; DO I=LEVEL* TO 15; DO WHILE (#FREE(I )>0> ; XPTR1 = FREE_PTR( I,*FREE( I) ) ; C_STRING=XPTR1->STRINGS; T_STRING=C_STRING; IF T_STRING=«$TEMP» THEN DO; OUTPUT_BUFFER=«CALL SOLFSTRC I I C_STRING I I • ); • ; 78 CALL SKIP_ANDCOUTPUT; END; #FREE(I )=#FREE(I )-l; END; END; END; /***************************** OUTPUT CODE COMPILED UP UNTIL NOW IF NOT INSIDE SYSTEMS PARTITIONING LOOP *****************************/ IF LOOP_DEPTH = £ #LINES->=0 THEN DO; DO 1= 1 TO #LINES; OUTPUT_BUFFER=OUTLlNES( I ) ; J=LENGTH (OUTPUT_BUFFER ) ; PUT FILE(SYSPUNCH) EDIT ( SUBSTR ( OUTPUT_BUFFER t It J) ) (X(1),A(79) ); PUT FILE(SYSPRINT) LIST ( OUTL INES ( I ) ) SKIP; END; #LINES=0; END; GO TO NEXT_NODE; *********** ***********/ *********** LEAVE_CODER: STK_PTR=STK_PTR-1 ; /****************** INSURE THAT ALL STACKS ARE EMPTY ****************** DO WHILE (ALLOCATION(RESULT_STACK) ) ; FREE RESULT_STACK; END; DO WHILE (ALLOCATION(TRAVERSE_STK) ) ; FREE TRAVERSE_STK; END; /******#*********** FREE ANY REMAINING TEMPORARY VARIABLES *****************************/ DO 1= TO 15; DO WHILE(#FREE( I ) >0 ) ; XPTR1 = FREE_PTR( I,#FREE( I ) ); C_STRING=XPTR1->STRINGS; T_STRING=C_STRING; IF T_STRING=' STEMP' THEN DO; OUTPUT_BUFFER='CALL 30LFSTRM II C_STRING II ');•; CALL SKIP_ANDCOUTPUT; END; #FREE( I ) = #FREE(I )-l ; END; END; /************** OUTPUT ANY REMAINING CODE ************** IF #LlNES-^0 THEN DO; DO 1= 1 TO #LINES; OUTPUT_BUFFER=OUTLINES( I ) ; J=LENGTH ( OUTPUT_BUFFER ) ; PUT FILE(SYSPUNCH) EDIT ( SUBSTR ( OUTPUT_BUFFER , ltJ) ) (X(1),A(79) ) ; PUT FILE(SYSPRINT) LIST (OUTL INES ( I ) ) SKIP; END; END; RETURN; *************** ***************/ 79 CODER_ILLEGAL: MULT00U.3): MULT00(3»3): MULT00(2»4>: MULTOO(2t2): MULT0V(4,3): MULT0V(3,3): MULT0V(2 t A): MULTOV(2t2): MULTV0<4,3): MULTV0(3,3): MULTV0(2t4): MULTV0(2,2): PUT LIST ('CODER ILLEGAL') SKIP; PUT LIST ( 'TYPE_CODE=' , $TYPE_CODEt ' TYPE__EXP» • , TYPE_EXP) SKIP; PUT DATA (TYPE_RIGHT, TYPE_LEFT) SKIP-; GO TO LEAVE_CODER; /»*♦***♦***********♦****»»*♦♦* SAVE GENERATED CODE SKIP_ANDCOUTPUT: PROCEDURE; #LINES=#LINES+1; OUTLINES(#LINES)=OUTPUT_BUFFER; OUTPUT_BUFFER = • • ; END SKIP_ANDCOUTPUT; POINTER_TOCSTRING: PROCEDURE ( STR ING, AREA ) RETURNS ( POINTER ) ; DECLARE STRING CHARACTER (200) VARYING ♦ AREA AREA (*) ; STRING_LENGTH = LENGTH ( STRING ) ; ALLOCATE VAR I ABLE_STR I NG IN ( AREA ) ; STRINGS = STRING ; RETURN ( SP ) ; END POINTER_TOCSTRING; f SET UP OPERAND NAMES AND SEOUENCE #t TRANSPOSEt £ NEGATE INDICATORS it****************************/ SET_VARIABLES: PROCEDURE; XPTR1=LN0DE->STRING_P0INTER; XPTR2=RN0DE->STRING_P0INTER; A_STRING=XPTR1->STRINGS; B_STRING=XPTR2->STRINGS; C_STRING=PTR_TO_RESULT_NODE->STRINGS; IF LNODE->SEQ_*_PTR=NULL THEN LSEO='0'; ELSE DO; SP=LNODE->SEQ_#_PTR; LSEQ= STRINGS; END; IF RNODE->SEQ_#_PTR=NULL THEN RSEO='0'; ELSE DO; SP=RNODE->SEQ_#_PTR; RSEO=STRINGS; END; IF RESULT_SEO=NULL THEN RSLTSEQ='0'; ELSE DO; sp=result_seq; rsltse0=str1ngs; end; A_STRING = A_STRING II ',' I I LSEO II ',' I I LTRANSPOSE(LEVEL#) II '»1»' II LNEGATE ( LEVEL* ) II N'; B_STRING = B_STRING I I •»• I I RSEO II ' t ' II RTRANSPOSE(LEVEL#) II M,' II RNEGATE ( LEVEL* ) II 't' ; C_STRING=C_STRING II ',' || RSLTSEQ MS' ; ASSIGNED(LEVEL#-1 )=1; END SET_VARIABLES; END CODER; 80 APPENDIX D OL/2 ARRAY EXPRESSION EXAMPLES The first five examples are those used in the text. The first is used in sections 3 and 4, and the others occur in section 4. Example 6 shows that if parenthesis are not used in example 5, less temporary storage would result. The seventh statement shows that the precedence of the multiply operation is context dependent. Notice that the expression, because of the precedence relations, is parsed as R = X*((Y'*A)*B) and this requires the least number of operations. Statements 8 and 9 illustrate how a sequence of multiplications or additions is compiled for array operands. 81 EXAMPLE 1: R=A*8+C*(D+E) ; DO #COLl = $lRl->*mwFR ( 2 ) TO $1 R1->*UPP ER ( 2 ) ; CALL aOLMULT(SlAl,0,0,l,0,4,0,$lBl,0,0,l,0,2,#COLl,STEMP10,0,2,0>; 30LADD(S1D1,0,0,1,0,2,#COL1,S1E1,0,0,1,0,2,#COL1,$TEMP12,0,2,0) ; aOLMULT(SlCl,0,0,l, 0,4,0, STEMP12,0,0, 1,0,2,0, STEMP1 1,0,2,0) ; 30L ADD ( STEMP 10, 0,0, 1,0, 2,0, STEMP 11, 0,0, 1,0, 2,0, SIR 1,0, 2, #CGL1> ; CALL CALL CALL END? CALL CALL CALL 30LFSTRI STEMP11 ) ; 30LFSTRISTEMP10) J 30LFSTR($TEMP12) ; EXAMPLE 2: R=A+B; CALL aOLADDI $1A1,0,0,1,0,4,0,$1B1,0,0,1,0,4,0,$1R1,0,4,0); EXAMPLE 3: R=ALPHA+BETA*GAMMA*B; CALL 30LMIJLT(ALPHA+BETA*GAMMA,0,0,1,0,0,0,$1B1,0,0,1,0,4,0,SIR1,0,4,0); EXAMPLE 4: R=ALPHA+BETA*GAMMA*(X,Y )*B; #TEMP00=anLIPR0( $1X1,0,0,1,0,$1Y1,0,0,1,0); CALL aOLMULK ALPHA +BETA*GAMM A* #TEMP00, 0,0, 1,0, 0,0, $1B 1,0,0, 1,0,4,0,$1R1,0,4,0) ; EXAMPLE 5: R=ALPHA*( ( A*B )*(C*D) ) ; CALL 30L MIJLT($1A1, 0, 0,1,0, 4, 0,$1B1, 0,0, 1,0, 4,0, STEMP 20, 0,4,0); DO «CnLl=SlRl->#LOWER (2) TO $1 R 1->#UPP ER ( 2 ) ; CALL aOLMULK SIC 1 , 0, 0, 1 , 0, 4 t , $1 Dl , , 0, 1 , 0, 2 , #CDL 1 , STFMP1 1 , 0, 2 , ) ; CALL 30LMULT(STEMP2 0,0, 0,1, 0,4,0, STEMP 11, 0,0, 1,0, 2,0, STEMP 10, 0,2,0) ; CALL aOLMULT(ALPHA,0,0,l,0,0,0,$TEMP10,0,0,l,0,2,0,$lRl,0,2,#COLl) ; END; CALL aOLFSTR( STEMP10) ; CALL aOLFSTR($TFMPll ) ; CALL aOLFSTR( STEMP20) 5 EXAMPLE 6: R= ALPHA*A*B*C *D ; DO *CnLl = $lRl->#LDWER<2 ) TO $ 1R 1->«IJPPER ( 2 ) ; CALL anLMl)LT($lCl ,0,0,1 ,0 ,4 ,0 , $ 1D1 , 0, , 1 ,0 ,2 , #C0L 1 , STEMP 12 , 0, 2 , ) ; CALL 30LMULT ( SI Bl, 0,0, 1,0, 4,0, STEMP 12, 0,0, 1,0, 2,0, STEMP 11, 0,2,0) ; CALL aOLMULT ( SI A 1,0, 0, 1,0,4,0, STEMP 11, 0,0, 1,0, 2,0, STFMP 10, 0,2,0): CALL aOLMIJLT( ALPHA, 0,0, 1,0,0,0, STEMP 10, 0,0, 1,0,2,0, SIR 1,0, 2, #C0L1) ; END; CALL 30LFSTR( STEMP10) ; 82 CALL aOLFSTRUTFMPll ) ; CALL aOLFSTR($TEMP12) ; EXAMPLE 7: R=X*Y»*A*B; CALL aOLMULT(SlYl,0,l,l,0,3,0,SlAl,0,0,l,0,4,0,$TEMPll,0,3,0) 1 CALL 30LMULT($TEMPll t 0,0,l,0,3,0,SlBl,0,0,l,0,4,0,STEMP10,0,3,0) CALL 30L MULT (SIX 1,0,0, 1,0, 4,0, STEMP 10, 0,0, 1,0, 4,0, SIR 1,0,4,0) J CALL 30LFSTR($TEMP11 ) ; CALL aOLESTR(STEMPlO) ; EXAMPLE 8: R=A-B*C*D; 00 #C0L1=S1R1->#L0WER(2 ) TO $ 1R1->#UPPER ( 2 ) ; CALL 30LMULT($1C1 ,0,0,1 ,0 ,4 ,0 , $ 101 , 0, , 1 ,0, 2 , #CDL 1 , STEMP 1 1 , 0, 2 ,0 ) ; CALL 30LMULT ( $1 B 1, 0, 0, 1,0, 4,0, ST FMP1 1,0, 0,1, 0,2,0, STEMP 10, 0,2,0) : CALL aOLMULT(SlAl,0,0,l,0,4,0,$TEMP10,0,0,l,0,2,0,$lRl,0,2,#COLl) ; ENO; CALL aOLFSTR(STEMPlO) ; CALL aOLFSTR( STEMP11 ) ; EXAMPLE 9: R=A+B+C+D; CALL a0LA0D(SlAl,0,0,l,0,4,0,SlBl,0f0fl,0.4,0.$lR1.0.4.0); CALL anLAOD(SlRl,0,0,l,0,4,0»SlC1.0.0.1.0.4,0.$lR1.0.4.0): CALL aOLADD( SIR 1, 0, 0, 1, 0, 4, 0,S1D1, 0,0, 1,0,4,0, SIR 1,0, 4,0) ; #t o^