ml1.org.uk Hints and tips

Overview

This page contains a few hints and tips on using ML/I. Further contributions are welcome.

Global and local operation macros

Four of the operation macros (MCDEF, MCINS, MCSKIP and MCWARN) have both local and global versions. The global versions are distinguished by having their names suffixed with G. It is important to understand the difference between these.

The first thing to remember is that the global version is not used very often; there is no need. Use of (say) MCDEF at the outermost level (i.e. the source text) is almost the same as using MCDEFG, the only difference being that a local definition can be invalidated by the use of MCNODEF, which again is rarely used at the outermost level. Use of MCNODEF does not free the storage occupied by the definition(s) being invalidated, so there is often a better solution.

Given the above, it may be wondered which version should be used by default. It happens that using MCDEFG incurs significantly more processing that the use of MCDEF, so the answer is that the local version should almost always be used, especially at the outermost level.

Having said that, it may not be obvious why MCDEFG should be used at all. Simply, any use of MCDEF at an inner level (i.e. while processing replacement text) is automatically cancelled as soon as that text is left (in an outer direction). Thus, to cause replacement text to define a new macro, it is necessary to use MCDEFG, as global definitions are never forgotten.

All of the above commentary on MCDEF and MCDEFG applies also to MCINS(G), MCSKIP(G) and MCWARN(G).

Using character variables

It is possible to do without character variables entirely, and in fact ML/I has not always supported them. To remember a piece of text for later insertion, one can simply define a global macro with the replacement text being whatever has to be remembered. A call of that macro will then retrieve the remembered text. The main problems with this approach are that there is no way to release the storage occupied by the definition (even if the macro is redefined), and that setting up global macros is relatively expensive.

Character variables provide a partial solution. However, for reasons of efficiency they are effectively stored as fixed length items (but with an internal length value), which means that they can be wasteful if some pieces of remembered text are large. The maximum length must also be known in advance, when MCCVAR is first called.

The temptation is to use MCCVAR to define just one variable, and then to call it again with a larger argument each time a new variable is required; the length cannot be changed after the first call. However, each call of MCCVAR is roughly as expensive as defining a global macro! A better solution is to set up a block of character variables, and record the total number and number remaining unused in two permanent variables. When this block is exhausted, MCCVAR is called to allocate another block.

Using startlines

Insertion of the imaginary startline character on input is useful if it is desired to treat the input text in a line oriented fashion. However, beginners often have trouble visualising what is going on.

First, it is important to realise that the startline character is only inserted on input. If it is not inserted then, it will not magically appear later. On the other hand, if that input includes a macro definition (for example), then the startline will be embedded in the replacement text of that macro, which is usually undesirable. For that reason, startline insertion is usually turned on only when processing of definitions has finished, and input of the text to be processed is about to start. By default, startline insertion is turned off, and it is turned on by setting S1 to 1.

However, even this can cause problems. It may be that startline itself has been declared as a macro name, on its own, e.g.:

MCDEF SL AS ...

The problem here is that the line after the MCSET that turns startlines on will have a startline preceding it, but this is probably going to be a MCSET that switches input streams, and thus the closing delimiter of that startline macro call will never be found.

The solution is to define a skip using a startline followed by some innocuous flag character, and use that when the startline is to be ignored, viz.:

MCSKIP SL WITH $
MCSET S1=1
$MCSET S10=2

This will not affect the bare SL macro, because ML/I always tries to make the longest match that it can.

More about locally defined macros

It is sometimes useful to define a temporary macro (or skip, insert, or warning marker) within some replacement text. This is often to implement a locally significant action when something is inserted; the temporary item disappears when that level is left.

Beginners sometimes have trouble with this. A local macro is defined, something is inserted but the local macro is ignored. For example:

MCINS %.
MCSKIP MT,<>
MCDEF TEST NL AS <MCDEF XXX AS YYY
%A1.>
TEST XXX

One might expect the output here to be YYY, the replacement text for XXX, but the actual output is XXX! The reason is that, by default, inserts are protected against the current enviroment, so the MCDEF XXX ... is ignored.

This is easily remedied by using an unprotected insert; this is defined by adding the U option in the call of MCINS. The above then becomes:

MCINS U,%.
MCSKIP MT,<>
MCDEF TEST NL AS <MCDEF XXX AS YYY
%A1.>
TEST XXX

The output now would be YYY. As the ML/I User's Manual says:

the initial local name environment when inserted text is evaluated is as follows
  • If the insert is a protected insert then it is the local name environment that was in force when the call containing the inserted text was encountered.
  • If the insert is an unprotected insert then it is the local name environment that was in force when the insert was encountered.

Valid XHTML 1.0! Valid CSS!

This site is copyright © 2018 Bob Eager
Last updated: 11 Oct 2018