ML/I

ML/1 (Macro Language/One) is a powerful general-purpose macro processor.

Typical uses of ML/1 include:
 * editing, modifying, correcting, or reformatting text files
 * translating source code from one programming language to another
 * acting as a source-code preprocessor to allow the user to add new syntactic forms to an existing programming language
 * supporting program source-code parameterization (e.g. a parameter might determine whether debugging statements are to be included in the program source code that is passed to the compiler)

ML/1 was developed in 1966 by Peter J. Brown as part of PhD research at Cambridge University in England.

In 1984, Robert D. Eager, one of Peter Brown's colleagues at the University of Kent, rewrote ML/I, first in BCPL in 1981, and later in C in 1984, which increased its portability.


 * Note that Peter Brown's original name for the language was ML/I, where (as in IBM's PL/I) the last character is the Roman numeral "I", not the Arabic numeral "1". Most subsequent implementations however have been called ML/1 (where the last character is the Arabic numeral "1").

Since then, ML/1 has been ported to many platforms and operating systems, including VMS, MVS, MS-DOS, OS/2, and UNIX. In his implementations of ML/1, Robert D. Eager has added features and capabilities in addition to those originally specified in Peter Brown's thesis.

That version is available for multiple platforms via the ML/1 web site, http://www.ml1.org.uk. The ML/1 web site provides further information about ML/1, as well as documentation (including a tutorial, simple introductory guide, and full user manual).

Although the total number of ML/1 users in the world is small, there are ML/1 users all over the world, and Bob has corresponded with ML/1 users in the United States, Canada, Australia, New Zealand, Germany, Holland, and India.

In a 1976 paper, Andrew S. Tanenbaum describes using ML/I as a compiler-compiler.

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.