MP/1(ID:5992/mp:002)for Macro Processor 1 McLeod Queen's University, Kingston, Ontario, Canada Macroprocessor for FORTRAN, replaced the list-structure of SP/1 with a stack based system. Largely for reasons of efficiency, but also to enable extra features Related languages
References: External link: Online copy Extract: Introduction This paper outlines the main features of MP/l, a macroprocessor developed specifically for use with FORTRAN. The main aim of the design has been to produce an efficient macroprocessor for FORTRAN programmers in which operations for particular classes of problems can be expressed more naturally than in the existing FORTRAN syntax. By its relation to FORTRAN MP/1 differs from other recent macroprocessors such as LIMP (Waite, 1967) and ML/1 (Brown, 1967) which are not biased towards any one language, but are intended for more general use. This paper also compares MP/1 with these and other macroprocessors. As far as possible the notation used by Brown in his recent survey of macroprocessors (Brown, 1969) has been followed. The use of a macro is termed a macro call. A macro definition is made up of a macro name, which defines the syntax of the macro call, and replacement text, which specifies the object code with which the macro call is to be replaced. Operations used inside the replacement text to control the actual generation of code are called macro-time operations. The language generated by the macroprocessor is called the base language. As well as the question of efficiency, three major aspects need to be taken into consideration in the evaluation of a macroprocessor. First, the way in which macro names can be defined is of primary importance in macroprocessors designed for use with high level languages where one of the major applications will be to enrich the syntax of the base language. This consideration is less important in macro assemblers such as the 360 macro assembler (Freeman, 1966) where the macros are similar in format to assembler language statements. This paper is, however, more concerned with macroprocessor applications to high level languages. Second, the way in which parameters are handled is important both from the point of view of ease of use and also in that it can affect the range of allowable name constructions. Finally, the macro-time operations should be straightforward and easy to use. This is particularly important if a macroprocessor is to develop beyond being a specialised tool largely restricted to software writers and become a programming aid easily usable by less sophisticated programmers. The tailoring of a macroprocessor to a specific language allows a significant advantage in this respect since the syntax of the macro-time statements can be derived from that of the analogous statements in the base language. Extract: Macro name syntax Macro name syntax In MP/l, a macro name is made up of a set of one or more delimiters separated by a formal parameter. In this respect the name construction differs from that used by Brown who defines the macro name to be the initial delimiter of the construction. In addition the macro may begin or end with a formal parameter. For example, ADD @@ TO @@ @@ + @@ are both instances of valid names. (The @@ symbol denotes a formal parameter). Thus MP/1 differs from most other macroprocessors in that a formal parameter can precede the first delimiter of the call. While this necessarily increases the time required to match macro calls, it allows a greater range of macro names. Infix constructions, for example, which would seem to be desirable in a high level language macro facility are allowable. Both GPM (Strachey, 1965) and the current PL/1 macro facility (IBM, C28-6571, 1966) have much more restricted name formats. In PL/1 for example the macro name takes the same form as a procedure name, thereby providing very little in the way of syntactic extension. LIMP allows names to be constructed in much the same way as in MP/1 but has no provision for allowing a parameter to represent a variable length list of elements. For example, in MP/1 the macro name ADD @AND@ TO @@ would match both of the calls ADD A AND B AND C TO X ADD C TO D i.e. the formal parameter @AND@ corresponds to a variable length list of actual parameters where each element of the list is separated by a predefined delimiter which in this case is the symbol AND. This facility can however be simulated in LIMP, though presumably less efficiently, using the macro-time facilities available. ML/1 has a similar feature, but macro names representing infix operations cannot be constructed. PL/1 requires macros to have a fixed number of arguments. Macro calls are delimited by a warning character, normally a % character, and the implicit end of line character. Where calls are continued over more than one line, the normal FORTRAN convention for indicating continuation lines is used. Additionally MP/1 can require arguments to be balanced algebraically with respect to left and right parentheses, a facility also available in Waite's Stage 2 macroprocessor, (Waite, 1970), and also allows default values to be given. For example, in the macro name ADD @(, ) = '1'@ TO @@ the first formal parameter corresponds to a list of balanced arguments separated by commas. If no actual argument is present in the call the default value is 1. Thus for ADD A,B(1,1), C TO D there are three elements in the first argument and ADD TO D in which the first argument is omitted, is equivalent to ADD 1 TO D Of all the current macroprocessors, XPOP (Halpern, 1967) offers the greatest variety of name constuctions. In addition to the normal delimiter constructions, XPOP allows 'noise words' which may be optionally included in the macro call and keyword parameters, similar in principle to the equivalent facility in the 360 macro assembler, which allow parameters to appear in any order. Thus the same macro call can often be written in many different ways. While this degree of flexibility can sometimes be useful its value in conjunction with a FORTRAN type of base language is debatable. Macro-time facilities and parameter inserts In MP/1 the macro-time statements are designed to resemble as far as possible equivalent execution time statements in FORTRAN. For example, the macro name given above could be defined as follows: % DEF ADD @( , ) = '1'@ TO @@ % CALL ARGS (@I @, #LV) % #LW = 0 % 10 #LW = #LW + 1 @2@ = @2@ + @l:#LW@ % IF (#LW.LT. #LV) GO TO 10 % END Variables are preceded by a # symbol, @I@ refers to the first argument and @2@ to the second, while @1 :#LW@ refers to the #LWth element of the first argument, which is assumed to be a list. The subroutine ARGS stores the number of elements in the first argument into the variable #LV. All macro-time statements are preceded by a % sign. Thus ADD A,B,C(l, 2) TO X generates X = X + A X = X + B x = x + C(1,2) Alternatively, the macro could be written as %DEF ADD @( , ) = '1'@ TO @@ @2@ = @2@ + @I:+@ %END In this case the above call generates X = X + A + B + C ( 1 , 2 ) This indicates how an argument list can be reproduced with a different argument separator. MP/1 also allows both string variables and limited string matching operations using the same FORTRAN type logical IF statement. The full range of macro-time statements and facilities are described elsewhere (Macleod, 1969), but the first macro definition given above illustrates their FORTRANlike nature. In the PL/1 preprocessor, the macro-time statements are virtually identical to the execution-time PL/1 statements. The replacement text for LIMP, on the other hand, is written in a SNOBOL-like language. While this provides an extremely powerful string processing facility there is a corresponding increase in macro expansion time. It is interesting to note that Waite's second macroprocessor, Stage 2, has abandoned the SNOBOL type statements. Most other macroprocessors have either developed their own syntax for macro-time operations or take the GPM approach of treating macro-time statements as ordinary macros. Extract: Other features Other features In common with most macroprocessors, MP/1 allows macro calls to appear in non-macro statements and as arguments nested in other macro calls. In both cases the nested macros are delimited by square brackets. The same delimiters are used to denote macro calls inside definitions. One interesting feature of MP/I is that macro calls can be generated dynamically in the replacement text and this property can often be utilised to avoid bracketing of nested macro calls. For example, the macro definitions associated with the call L - > LIST A, (B, (C, D)), E would generate the following steps: L = NULIST([*A], [*(B, C, D))], [*El) L = NULIST (A, [*(B, (C, D))], [*El) L = NULIST (A, NULIST([*B], [*C, D)]), [*El) and so on, until finally the expansion is output as L = NULIST (A, NULIST(B, NULIST(C, D)), E) This particular series of definitions illustrates how complex macro expansions may be handled by the system. Note in this case that the submacro [*(B, (C, D))] could be taken as a call of either the first or second macros defined. This apparent ambiguity is resolved in the matching process by searching backwards through the defined names so that an attempt will initially be made to match the call against the most recently defined macro. This particular example would thus be matched against the name *(@( , )@) in preference to the name *@@. As indicated earlier, macro calls are normally preceded by a % character which flags the beginning of a macro call. However, the user may specify that some alternative character is to be used as a warning flag. For example, a blank can be used, in which case the format of a macro call becomes identical to that of a FORTRAN statement. A further feature of MP/1 is that macro-time statements are compiled rather than interpreted which increases markedly the efficiency of looping operations within the replacement text, such as, for example, indexing through an argument list. Extract: Summary Summary MP/1 was originally implemented on a list structure basis, as is LIMP. While in principle this gives a greater degree of flexibility in designing macro-time operations, it was found that for MP/l a stack based system was much more efficient and that all the features considered desirable could still be easily incorporated. This finding supports Brown's suggestion (Brown, 1969) that the use of list processing techniques may well generate overheads making the use of the macroprocessor impractical. As mentioned earlier, MP/1 uses a character by character scan in matching macro calls against the appropriate macro name, this being necessitated by the FORTRAN convention of treating blanks as non-significant characters, thereby preventing the use of an atom based match of the type used by ML/l. Certainly if this consideration were not important a much faster name matching algorithm could be constructed using an atom based system in conjunction with a hash table. The tailoring of MP/1 to FORTRAN has allowed the implemented system to be both efficient and easy to use, in that References the macro-time statements are FORTRAN-like thus following McIlroy's suggested approach to the design of macro systems (McIlroy, 1960). Further, the macro name syntax allows the system to be used to provide a significant extension to the base language. The PL/1 preprocessor, while providing macro-time facilities whose syntax is essentially that of PL/1 itself, has an extremely restricted name format, making the processor unsuitable for syntactic extension. Some of the defects of the current preprocessor are illustrated in a recent work on the incorporation of a pattern directed string processor in PL/I (Oppen, 1970). It is the author's belief that the tailoring of a special purpose macroprocessor to a particular language is justified both because the processor can cater more efficiently for the idiosyncrasies of that language, and also because the facilities of the processor can be incorporated into a syntax already familiar to programmers in that language, In the context of language extendability it is certain that some sort of macro-like facility will become common in future programming languages. It is noteworthy that a recent proposal (Davies and Paradine, 1969), has advocated extensive changes in the PL/1 preprocessor which will allow the construction of 'trigger' macros. The macro name syntax in this case is similar to that of ML/l but the arguments of trigger macro may themselves be macros, called syntax macros, which have a less restrictive notation and can for example be used to represent infix expressions. While it is not claimed that MP/1 is the ideal macroprocessor, it is hoped that it will be of use in the design of similar facilities in both present and future programming languages. Applications of MP/1 include the incorporation of a SNOBOL like extension into FORTRAN-IV (Macleod, 1970). The system is currently being extended to allow its use in developing interactive problem solving languages (Mandil, 1970). in The Computer Journal 14(3) May 1971 view details |