ML/1 (originally named ML/I) is a powerful general-purpose macro processor developed in 1966 by Peter J. Brown at Cambridge University. It is commonly used for editing text, translating source code, acting as a source-code preprocessor, and supporting parameterization. In 1984, Robert D. Eager of the University of Kent rewrote ML/1 first in BCPL and then in C, enhancing its portability. ML/1 has since been ported to platforms like UNIX, MS-DOS, VMS, and MVS. Further details and documentation are available at the official ML/1 website. Notably, Andrew S. Tanenbaum described ML/I as a compiler-compiler in 1976.
Overview
ML/I accepts input in completely free form, treating data as a stream of bytes rather than a series of lines or records. It does not require any particular flag to indicate a macro expansion, which makes it particularly useful for processing arbitrary text. Replacements of text can be simple (e.g. PIG is to be replaced by DOG) or complex (e.g. replace the item between the third and fourth commas after the last full stop, by the contents of some counter).
ML/I was used to implement several items of portable software, including itself. It was originally written in a special descriptive language, then mapped into a suitable language for each target system. This mapping was done using ML/I itself. There were two different forms of this descriptive language; high level and low level.
After this mapping ML/I was often used to implement SIL's (system implementation languages, such as C) for the new generation of 16-bit architecture minicomputers.
How ML/1 works
In the most basic terms, here's how ML/1 works.
- The user supplies ML/1 with a file containing input text.
- In another file (or, optionally, in the same file) the user supplies a set of ML/1 macros. The macros tell the ML/1 interpreter what insertions, deletions, expansions, translations and other modifications the user wants made to the input text.
- When ML/1 is run on the input text, ML/1 follows the instructions in the ML/1 macros, changes the text, and writes out a new file containing the modified text.
Distinctive features of ML/1
There are several ways in which ML/1 is more powerful than simple "scan and replace" utilities.
ML/1 does not process text on a character-string by character-string basis; it processes text on a word by word (or, in ML/1's terminology, on an "atom by atom") basis. For many applications, it is extremely useful to be able to process a text as a sequence of atoms rather than a sequence of characters. Suppose, for example, that we wish to translate a program from a programming language that has a DO ... END syntax, into a language that has a BEGIN ... END syntax. We therefore wish to replace "DO" with "BEGIN". If we do the replacement with an ordinary scan-and-replace utility, all occurrences of the string "DO" will be changed to "BEGIN", including any "DO"s that are embedded in words such as "DOCUMENT" (which will become "BEGINCUMENT"). With ML/1, in contrast, this will not happen because the string "DO" will trigger text-replacement only when it occurs as a word (that is, when it is preceded and followed by delimiters such as spaces, tabs, newlines, or punctuation characters).
ML/1, rather than operating on a line-by-line basis, recognizes patterns of text that can be quite complex, nested, with multiple delimiters, and spanning many lines. ML/1 can, for instance, process a pattern such as the common programming language IF ... THEN ... ELSE ... ENDIF structure that spans multiple lines, and contains embedded text that itself may include a nested IF ... THEN ... ELSE ... ENDIF structure.
ML/1 can recognize embedded comments and literal quotations, and protect them from alteration. Ordinary scan-and-replace utilities change strings indiscriminately, whether they occur in the program text as a keyword or variable name, embedded in a comment, or in a quoted literal.
In order to deal with such complicated patterns, ML/1 needs to be a programming language in its own right. Like other programming languages, ML/1 supports variables and assignment statements, GOTOs and labels, IF... THEN tests and loops. These features give ML/1 an unusual degree of power and flexibility.
Limitations
ML/1 is case-sensitive, so it does not support case-insensitive text processing.
External links
References
A. J. Cole (26 November 1981). Macro Processors. CUP Archive. p. 85. ISBN 978-0-521-28560-5. 978-0-521-28560-5 ↩
Brown, P. J. (1967). "The ML/I macro processor". Communications of the ACM. 10 (10): 618–623. doi:10.1145/363717.363746. ISSN 0001-0782. https://doi.org/10.1145%2F363717.363746 ↩
Tanenbaum, A.S. (1976). "A General-Purpose Macro Processor as a Poor Man's Compiler-Compiler". IEEE Transactions on Software Engineering. SE-2 (2): 121–125. doi:10.1109/TSE.1976.233539. ISSN 0098-5589. S2CID 16317510. /wiki/Doi_(identifier) ↩