The MDL Programming Environment

Chapter 3. Program Writing and Debugging Aids

This chapter concentrates on editing and debugging aids for MDL programming. The basis for editing and debugging in MDL is twofold: First, MDL is an interpreter, which permits interactive testing and debugging of software, Secondly, MDL programs (even compiled MDL programs) are structures and therefore may be manipulated by other MDL programs.

Packages useful in editing and debugging range from EDIT and PPRINT, which are preloaded, and which form the core of most editing or debugging systems, to more sophisticated aids such as DEBUGR and TRACE, which are more powerful, and useful for more complicated debugging.

It should be noted that, in addition to the editors discussed below, RMODE [5] and E:MAcs [2], TECO based text editors, understand much of the syntax and many of the conventions of MDL programs.

3.1. Pretty-Printing

The purpose of pretty printing is to clarify the structure of MD. objects by printing them in a more human-readuble format than that provided by the SUBRs PRINT, PRIN1, ctc. Objects are pretty-printed through the judicious insertion of spaces, tabs, and new-lines between tokens. Pretty-printed objects are reidable by the MDL Reader. Pretty printing is an aid to understanding and debugging MDL FUNCTIONs or other objects. You will probably find pretty printing to be extremely helpful, especially if you are working without a listing or with an old listing. In fact, pretty-printing is one way to make a new pretty listing after editing. PPRINT is preloaded in most initial MDLs. The name of the package containing PPRINT is "PP".

<PPRINT any channel>

pretty-prints any on channel. The second argument is optional, by default .OUTCHAN. If ay is an ATOM, PPRINT will cnclose it in an application of DEFINE, DEFMAC, SETG, or SET, as seems appropriate. COMMENTs found inside any are right-justified. PPRINT cannot outpul an RSUBR without FIXUPs (that is, ane that was READ in while KEEP-FIXUPS (see section 3.4) had no LVAL or had to FALSE LVAL); it will give the ERROR message CAN-NOT-BE-DUMPED. PPRINT returns,NULL, which is an ATOM whose PNAME is a single rubout, invisible on normal consoles,

<PPRINF ju:string-or-aiomr-or-list outfilerstring

widthzfix eval?:boolean>

pretty-prints all the contents of in into owifile. '

If in is an ATOM or a LIST of ATOMs, its VALUE(s) are the abiects to be PPRINTed. In this case, ouifile is by default a file whose first name is produced by taking the PNAME of in (or in's first element, if in is aL IST).

If in is a STRING, if specifics a file containing objects to PPRINT. In this case, ou/file is by default "TPL: ", width is the maximum width of output lines (although output lines are prevented from being extremely long); It is optional, by default <13 , OUTCHAND>. eval? tells PPRINF whether or not to EVAL everything in the file: it is optional, by default a FALSE (don't EVAL). eval? is meaningless if in is nota STRING, PPRINF returns either "DONE" vr a FALSE if it couldn't open fujile or wuitfile. PPRINF inserts page boundaries in ewtfile, between objects, every 60 lines or fewer: you may want to move these afterward to more logical places. PPRINF binds KEEP-FIXUPS and REDEFINE to T, and QUICKPRINT (see below) to a FALSE,

3.1.1. PPRINT Control Switches

PPRINT's output is affected by the local values of several ATOMs. Each value is examined only for truth, .QUICKPRINT If this ATOM's LVAL is a FALSE, you are in slow mode: otherwise (including the case of no LVAL), you are in fast made. 'The behavioral difference is this: in fast mode, there may be COMMENTS in the pretty-printed object(s) which PPRINT misses. Also, fast mode is indeed faster than slow mode. Fast mode is the default, that is, QUICKPRINT is initially true. The modes are really distinguished by the depth of recursion to which PPRINT resorts. In slow mode, it recurses all the way down to every monad in the thing pretty-printed; in fast made, it goes down only far enough to find something that will fit on a line, . LOOKAHEAD PPRINT uses full recursive lookahead to avoid packing things against the right margin and, as a result, not being able to fit things within the right margin. The lookahead results in very good formatting of deeply-nested MAPFed and FUNCTIONs: all but the most bizarre cases should be very legible. However, it can result in noticeable 'pauses' in the printing operation and, in some cases, a net speed slightly less than with limited lookahead. Since this can be a disadvantage when using PPRINT interactively on a heavily-loaded system, the lookahead can be disabled: if the LVAL of LOOKAHEAD is a FALSE, no lookahead will be performed; otherwise ithappens. LOOKAHEAD is initially true, that is, lookahead happens by default.

  • VERTICAL If LOOKAHEAD is a FALSE, the formatting can cause too many objects to be squeezed against the right margin. So that particular cases can be made legible, the format when lookahead is not in use can be manually set: ifthe LVAL of VERTICAL is non-FALSE, PPRINT will indent very little whenever indenting is

called for. (VERTICAL being true means a 'more vertical' format.) VERTICAL is initially FALSE. The value of VERTICAL is ignored when LOOKAHEAD is true; the lookahead effectively chooses different values for VERTICAL for different parts of the object pretty-printed.

3.1.2. Lower-level Pretty Printing

It is sometimes desirable to use some of the functions that PPRINT uses. but in a different way. For example, a specialized pretty-printer for Program Abstracts would want to insert indented field names into the output and pretty-print field values with the same indentation. 'The names of lower-level pretty-print functions are included inthe ROOT OBLIST for such purposes. pretty-prints anyon .OUTCHAN to the right of left-margin. The second argument is optional, by default (see below). EPRIN1 isto EPRINT as PRING isto PRINT, .LEFT-MARGIN 'This is the ATOM that EPRINT binds to its second argument. You can SET it outside calls to EPRINT in order to make a permanent left margin, Its initial LVAL is 0. outputs tabs and/or spaces to advance the output column (<14) channel>) to column, if it is not already past. =~; pretty-prints any on channel (by default .OUTCHAN) between the margins /efi-margin (by default <14 channel>, the current column) and right-margin (by default <13> channel>, the rightmost column). All arguments but the first are optional. COLPP returns,NULL. For example, <COLPP any .OUTCHAN 10 70> would leave a J0-character margin at left and right on an 80-column OUTCHAN. Also, <PROG () <COLPP,FOO>> would result in output like AAAAAAAAAAAAAAA #FUNCTION ((X GGGGGGGGGGGGGGGGGGGEGG) <+ X 1d) EPRINT, EPRINI, and COLPP are affected by the truth of .QUICKPRINT, .LOOKAHEAD, and VERTICAL. i] Pretty-Printing

3.1.3. Ampersand Printing

"Ampersand printing' consists of printing any object on a single line by using the character & (ampersand) to mean "There's more stuff here. (This technique Is borrowed from the InterL.isp editor.) ] There are two ways in which & is used by this printer as an abbreviation: l. An & appearing between some variety of brackets indicates "that there is a big object of the indicated TYPE there. 2. The characters ..&or &.. on the left or right of a structure mean that there are more objects to the left or right which have not been printed, Fxumples: #FUNCTION ((A BC D) <&>) This is a FUNCTION with four arguments in its argument LIST, and the FUNCTION body contains one FORM which was too big to print in the remainder of the line. <PROG () <KRK <+ .A 5>> <PRINC .Q> <SET BAR > <&> &..> 'This is a large FORM, namely, a PROG. In addition to the element printed, there are more elements to the right, and there is one FORM which was too big to fit Ampersand printing is effected by two pure RSUBRs: & analogous to PRINT, and &1, analogous to PRINI. A related RSUBR, &LIS, can be applied to no arguments t put you into an endless READ-EVAL-& luap, instead of the normal READ-EVAL-PRINT loop.

3.1.4. Examining the Stack

FRM fix> returns the fixth FRAME down from the top application of ERROR or LISTEN, pretty-prints how-many FRAMEs (by printing the FRAME number (suitable as an argument to FRM), FUNCT, and ARGS of the FRAME), starting with . Both arguments are optional: start defaults to 0, and how-many defaults to a large integer. A FRAME whose FUNCT is an ATOM whose VALUE is an FSUBR is not printed, if the same information is found in the next lower FRAME. <FR& how-manyzfix start:fix> is like FRAMES but uses ampersand printing instead of pretty printing. It is handy for summarizing FUNCTs and ARGS that are large or unprintable (like RSUBRs with no fixups).

<FRATM fiow-manyzfix startzfix>

is like FRAMES but gives an abbreviated view of the stack. It prints FUNCTs only, and only for FRAMEs connected with named FUNCTIONs, RSUBRs, and RSUBR-ENTRYs. It is handy when a FRAME contains a nan-LEGAL? object. how-many fix Startzfix? prints out the stacked bindings of atom, going through how-many FRAMES, starting with , 'The two numeric arguments are optional: how-many defaults to a large integer, and start defaults to 0. The format of the printing is two columns: the first column is the number of the FRAME in which <atom has a binding: the second column is the value bound, or a message proclaiming the lack of a value. is precisely the same as FRLVAL, except that the values are ampersand printed instead of PRINTed.

Finally, the "FRMSP* PACKAGE contains analogues of many of the preceding functions, but each takes as its first argument a PROCESS, by default . 'These are all named by adding a *P' to the end of the usual name. For example,

<FR&P <MAIND?

does a <FR&> in the PROCESS MAIN. There is one additional function of interest in "FRMSP". is like FRAMES, but gives only the TYPEs of the arguments to each. 'This is useful in those situations when the stack shows illegal FRAMEs or other unprintable objects.

3.2. The MDL Editor

EDIT allows a MDL user to make incremental changes in MDL structured objects, without leaving MDL and with the ability to save the results in a file, and to set or clear conditional breakpoints of various sorts in objects that will be evaluated, such as FUNCTIONs.

EDIT is an editor/debugger written in, written for, and running under MDL. It comprises the package "EDIT" and several smaller packages which will be mentioned later in this section. EDIT is preloaded in most initial MDLs.

'To start editing, apply EDIT to no arguments or to the name of the object you wish to edit:

causes entry into EDIT and opens the last object edited; causes entry into EDIT and opens object for editing. Permissible objects include:

  • ATOMs. 'The GVAL (preferably) or the LVAL of the ATOM is opened. If it has no value, EDIT returns a FALSE,
  • A PRIMTYPE LIST. The PRIMTYPE LIST is opened.
  • A FIX. The stack frame with that number is opened (i.c., <ARGS >). Part of EDIT's efficiency comes from forbidding it to delve into objects that are not of PRIMTYPE LIST. that is, not LISTs. FORMs. FUNCTIONs, ctc. Attempts to edit objects of other PRIMTYPEs will result in error messages. 'These objects can, however, be treated as units when inserting, searching, ctc.; or they can be changed into LISTs, edited, and then changed back to their original types.

3.2.1. The Edit "LISTEN Loop"

3.2.1.1. The Reader

When in EDIT, you are typing ata special, non-standard, input function: The EDIT Reader. The Reader allows you to type EDIT commands and have them executed, and also to evaluate MDL expressions normally. [is characteristics are as follows:

  • Asin the normal MDL Reader, nothing is done until you type ESC. DEL, tL, tD, +G, and tS also work normally.
  • All EDIT commands are terminated when an ESC is encountered in the input stream. I[n addition, most commands will terminate whenever the maximum number of arguments required his been input or whenever an argument of the wrong type is encountered. In the former case the next object is taken as a new command: in the latter case the object of the wrong type is taken asa new command. EDIT commands may be typed in either upper or lower case.
  • IF you type something that EDIT does not recognize as a command, normal MDL evaluation and printing are performed on that something. 'This evaluation will have no effect on your position in the object you are editing. 2
  • While editing a function which is part of a PACKAGE (determined from an examination of the OBLIST containing the ATOM whose value is the function), EDIT causes the OBLIST path to be set up to what it was in the environment of that PACKAGE. 'This has the advantage of reducing the number of trailers printed, and causes newly entered ATOMs to full on the correct OBLIST (the internal OBLIST of the PACKAGE). It has the slight disadvantage that it disables the dynamic loader (which depends on unbound variables falling on the INITIAL OBLIST). If the GVAL of E-PKG is a FALSE, this feature is disabled, and the normal OBLIST path is in effect during

editing. R 5$ Causes execution of EDIT command R, with argument 5. <cR $>$ Causes application of the function R to 5.

3.2.1.2. The Ampersand Printer

Your current position is displayed by 'ampersand printing' (see section 3.1.3). 'This consists of printing any object on a single line by using the character & (ampersand) to mean "There's more stuff here."

'The ampersand printer used in EDIT is much like the standard one, with the addition that your current position (see below) is displayed by the glyph 6.

When you initially enter EDIT, you are in a mode called "non-verbose," in which ampersand printing is not automatically done following execution of EDIT commands. The V command is used to toggle you in and out of verbose mode (see below).

#FUNCTION ( 8 (ABC OD) <&>>

Indicates that your position is just to the left of a FUNCTION's argument list, and the FUNCTION body contains one FORM which was too big to print. <..& <KRK <+ .A 5>> & <SET BAR > <&> &. .> Indicates that you are in the middle of a large FORM (c.g., a REPEAT or a PROG), positioned just to the left of the <SET BAR >. In addition to the objects printed, there are more objects to both the left and the right, and there is one FORM which was too large to fit on the line.

3.2.2. Edit Commands

3.2.2.1. General

A sequence of EDIT commands is executed as soon as you type ESC. [fone command Fails, subsequent commands up to the ESC are ignored, and EDIT types out an appropriate error message. A failing EDIT command generally has no effect whatsoever; but see individual descriptions. Note that al/ arguments to EDIT functions must be legal MDL objects. In particular, you can't search for

<SET. since the <>'s aren't balanced. Nor can you insert it. (But you can, for instance, search for and insert
<SET THING 1>.,)

[Fa command expects an argument and doesn't't gct one, an error message will be printed.

Many EDIT commands take FI Xes as arguments. 'Those that do interpret the ATOM * as an argument to mcan 'as many as pussible',

Whenever you are in EDIT, you have a well-defined 'position'. A position is a 'place' inside a MDL structure; this place' is either between two elements of the structure, or between an element and either end of the structure, or inside an empty structure. All editing, movement and printing commands operate relative to your current position, 'The term 'cursor' is used in the following descriptions to refer to an embodiment of a position.

'The format used in each of the following command descriptions is: Command as Typed English Name Description

3.2.2.2. General Commands

? duh?

Causes a short summary of all EDIT commands to be typed out. The same summary appears later in this chapter,

2? huh?

Similar to the above, but the summary is even shorter, and should fit entircly on the screcn of an Imlac terminal.

Q Quit

Leave EDIT and return to Mil. (Causes EDLT to return the ATOM T.)

QR fix Quit and Retry

Quit from EDIT and then retry the frame specified, or by default, the one originally given to an open command or, if none was given, the frame beneath the last ERROR or LISTEN frame. tF Control-F

This is not really an EDIT command; rather, it is 4 character, obtained from the input stream at interrupt

level. which is used to return you to the EDIT Reader from some higher level of application, <.g.. an ERROR's LISTEN. It is the EDIT equivalent of ERRET with no arguments. +F (or tS) typed during execution of an EDIT command is similar to normal MDL +S but returns to the EDIT Reader instead of the MDL LISTEN loop. 0) abject : Open Equivalent to Q followed by . Positions the cursor just to the left of the first element of the entire object specified. O1 Open This If the object to the right of the cursor is an ATOM, or a FORM whose first element is an ATOM, and the ATOM's value is openable. then it is opened. 'This command is useful when tracing a calling sequence through severl functions.

3.2.2.3. Movement Commands

UT Up to the Top Places the curso: at the position it had following an 0. R fix Right Moves the cursor fix objects to the right, by default one. If fix is too large, ic., there are not that many positions to the right of the current position, EDIT prints an error comment and the cursor stays where It is. Muves the cursor as far to the right as possible. L fix Left Moves the cursor fix positions to the left, by default one. If fix is too large, EDIT prints an error message. F Front Moves the cursor as far to the Iefi as possible. DL Down Left Pusitions the cursor just to the right of the rightmost element within the object to the left of the cursor, if that object is of PRIMTYPE LIST. Visually, the cursor moves left over one 'close bracket'.

DR Down Right Positions the cursor just to the left of the leftmost element within the object to the right of the cursor, if that object is of PRIMTYPE LIST. Visually, the cursor moves right over one 'open bracket'. If the cursor is to the left of an element that is notof PRIMTYPE LIST, EDIT prints an error message. . Down Equivalent to DR. UR fix Up Right Positions the cursor just to the right of the object the cursor is currently within, Dues so fix times, by default once. UL fix Up Left Positions the cursor just to the left of the object the cursor is currently within, [Joes su_fix times. by default once. U fix Up Identical to UL. S object Search Does a depth-first, left-first tree-walk, (i.c., left-to-right) starting with the object to the right of the cursor, until the cursor is just to the right of an object structurally equal (i.c., =?) to its argument. An occurrence of the object will not be found if it is inside anything not of PRIMTYPE LIST. On failure. the cursor does not move. If the argument is omitted, the last object searched for is used. SR object Search Right . Same as $, SL ubject Search Left Same as S, but the tree-walk is depth-first, right-first (i.c., right-to-left) and you end up to the left of the vbject for which you were searching.

3.2.2.4. Printing Commands

The Empty Command

Causes the normal 'ampersand print' ty be done. This is principally useful when you are in 'silent' mode: see the V command. By the way, an 'empty' command is typed by typing ESC without having typed any visible characters before it. p Print PPRINTs (not 'ampersand prints') the object to the right of the cursor. PU Print Up PPRINTs the object the cursor is in. 'This is similar to doing a U and then a P, ulthough the cursor is not moved, PT Print Top PPRINTs the whole object you have open. V Verbosity Toggles the verbosity mode between 'verhuse' (most commands cause ainmpersand printing) and 'silent' (printing of any sort is done only when some explicit print command is used, or when an error occurs). The current state of verbosity is the GVAL of E-VERBOSE. In silent mode, absolutely nothing is printed after each command. not even new-lines or prompts. However, normal MDL evaluation still causes normal MDL printing.

3.2.2.5. Editing Commands

I] gny ... Insert Inserts all its arguments immediately to the right of the cursor. None of its arguments are evaluated: you can insert unevaluated FORMs without using QUOTE. 'The cursor ends up to the right of the last object msered, G gny ... Get Same as I, but its arguments are evaluated. 'his is useful in conjunction with the X command (see below), I: iypecatom fix Insert Type Grabs fix objects to the right of the cursor, inserts them into a newly created object of TYPE type, deletes them from the original structure, and inserts the newly created object in their place. In other words, it 'inserts'

une appropriate open and close brackets for dpe at the cursor and fix objects to the right. By default fix is one, ype is LIST. An error message is printed if fix is larger than the number of objects to the right of the cursor. There is no way to directly insert or delete single parentheses, brackets, etc., using EDIT. Instead, use K: (see below) to remove pairs of brackets, and [: to insert them. I* indicator:atom new structure Imbed Imbed looks for all occurrences of indicator in new-structure and replaces these occurrences with objects taken and deleted from the right of the cursor, It then inserts the result. IFonly sen-structure is given. the indicator is the ATOM . If there aren't enough objects to the right of the cursor to replace each indicator, remaining indicators are left untouched and a warning incssage is printed, If no indicators are found, the new structure is inserted, but a warning message is printed. [ is generally used to insert one or more structures into another complex structure in one operation, instead of several. For example: <SEF X & 12 .Y>> I* <COND (<NOT <LENGTH? .Y 11>> *)>$ <SET X <COND (<NOT <LENGTH? .Y 11>> <12 .Y>)> BD places a protective conditional around an NTH to prevent an out-of-bounds error. IG any. Insert into Group Inserts into a group. IG is similar to 1, but assumes that the object you are in is a group (as produced by GROUP-LOAD). Arguments to [G which are not ATOMs are inserted as in I. Objects which are ATOMs and which have a value insert a FORM which DEF INEs, SETGs, or SETs the ATOM as appropriate. 'has, to add a new function F to a group G, one could type O GSIG FSQS K fix Kill Deletes fix objects to the right of the cursor. Defaults to one. Negative fix causes deletion to the left of the cursor. C any Change Changes the one object to the right of the cursor to its single argument, Does not move the cursor. Does not evaluate its argument. C is more efficient than K plus I,

Changes the type of object to the right of-the cursor to ape. Attempts to do something reasonable for

every Lype change. If you tell it to change a STRING to a LIST, you get a LIST of CHARACTERs. If you

utlempt to change a structure whose elements are other than CIIARACTERs and STRINGs to a STRING, you

will get a MID. error,

K: Kill Type Deletes the brackets around the object to the right of the cursor. Le. kills the object and inserts its

elements into the structure of which it was a part.

SU) new old Substitute The Substitute command takes two arguments. All occurrences of wld from the current location to the end

of the open object (actually a search-right is done) are replaced by new. Once the search for old fails, the

command terminates, and the number of substitutions performed is printed, 'The cursor is left after the last

object replaced.

x atom Transfer SETs the atom to the object to the right of the cursor. X can be used with K and G to meve things around

within the object being edited.

SW Swap Swilps the two objects to right of the cursor, leaving the cursor pointing at the same object. The effect is to :

muwve the cursor and the object it points at one object to the right. Repeated SWs move cursor and object

further and further to the right.

3.2.2.6. Macro Facility

M macro Macro 'Takes either a STRING or something which EVALs to a STRING and performs all of the commands in the

STRING. For complete assurance that your commands will be done properly, put an ESC between

commands.

I! fix macro Iterate This command (also called DO) takes a fix and macro as if an argument to M. This command will loop

through the macro fix times or until an error is generated. When the iteration ends, the user is told how many

complete passes have been made of the macro,

In both of the above commands, if an EDIT error is generated, the macro will be terminated, and the macro itself will be printed, with an arrow pointing to the offending command. The cursor will remain at the place where the last legal command left it.

The SU command is, internally:

DO * "S of/$LSC news"

3.2.2.7. Cursors

Cursors are locations in objects being EDI Ted, In addition to the main cursor, which is where editing occurs, other locations (also called cursors) may be remembered. 'The main cursor may be moved to another cursor in a single operation, potentially saving many motion commands. In large FUNCTIONS cursors may also reduce confusion by distinguishing among several similar areas of code,

The PACKAGE for dealing with cursors is not normally loaded in an initial MDL so the UC command loads it and makes the cursor commands available. The PACKAGE loaded is "CURSOR".

CU afar. Cursor

CU lakes an ATOM argument and SETs the ATOM to an object of type CURSOR, which tries to be clever in the event you change the object. Also, if you use the X command tu name a substructure and then move copy it with G or [, the cursors in the substructure will follow to the new location.

There are some restrictions, Cursors in empty LISTs are okay but they will not follow the object to new locations. Also this 'following' feature is effective only at the first G or I after the X. To move the substructure again you have to X again,

I* is somewhat incompatible with CURSORs, Cursors in lmbedded structures will sometimes disappear.

GO cursor Go

GO takes a cursor (normally the LVAL of an ATOM previously given as an argument to CU) and GOecs to that position. [fF the cursor is illegal (not in the current top-level structure), an error message will be printed and you will remain in your previous position.

KC atom Kill Cursor .

Kill the cursor assigned to atom. PC. Print Cursors Prints all cursors in the structure to the right of the main cursor, PA Print All Cursors Prints all cursors in the currently open structure.

3.2.2.8. Breakpoints

BK predicate any ... Breakpoint Inserts a breakpoint 'around' the object to the right of the cursor, 'Takes any number of arguments. Subsequently, whenever dit object would have been evaluated, you instead hit a breakpoint function which:

  1. Evaluates predicate. If the value is FALSE, evaluation continues as if there were no breakpoint. If the value is non-FALSE, or if BK was given no arguments:
  2. Types BREAK.
  3. For each argument after the first that you gave BK, types are = EVAL ofarg
  4. Enters LISTEN. You continue by applying ERRET to one argument, just as from an ERROR; the argument's value is ignored. Breakpoints are implemented by inserting a BREAKR (a PRIMTYPE LIST with APPLYTYPE FORM) which consists of the function BREAKR and arguments, including the object breakpointed. A breakpoint prints as a glyph similar to the cursor: Bobject If the ATOM SHORT-PRINT is assigned and FALSE, the actual BREAKR LIST is printed. The breakpoint function returns EVAL of the thing it is put "around, and there are cases where this does not work, There are always equivalent places that do work. |. Breakpoint on the first element of a FORM does not work. Put it on the whole FORM,
  5. Breakpoint on a LIST which is an argument to a COND does not work. Put it on the first FORM in the LIST. GA predicate any .., Break After

Similar to BK, but puts the break point afer the object at the cursor. Its action is like that of BK except that the break occurs after the object it is un is EVALed, 'This sort of breakpoint prints like the 'before' sort, But with the glyph after the object broken: vdjeci*

The predicate for a BA breakpoint may check the value returned by AVAL for the object the breakpoint is on.

This value is assigned by BREAKR to the ATOM VALUE,

KT Kill This

Kemoves the breakpoint (if any) from the object to the right of the cursor.

KB Kill Breakpoints

Removes all breakpoints in the currently open abject.

3.2.2.9. Edit Monitors

There are several commands in EDIT which provide a simple interface tu the "MONITOR" PACKAGE. These allow placing of monitors on references to or modifications of LVALs in interpreted MDL code.

Fora more complete discussion of the use of monitors, see section 3.7.

The PACKAGEs for dealing with monitors are nut normally loaded in an initial MDL, so the UM command loads them and makes the three commands for creating monitors available. 'The PACKAGEs loaded are "MONTTR", which is the general monitor PACKAGE, and "EMONIT", which is the interface between EDIT and "MONITR", RW atom predicate any ... Read-write Monitor

The most general type of monitor that can be set is a read-write monitor. [t will catch any reference to or attempt to modify the LVAL of the arom specified. The restrictions on placement of breakpoints also apply to monitors, with the addition that a monitor on an LVAL must be placed after that LVAL has become ASSIGNED?,

'The second, third (and so on) arguments to RW are the same as those for BK. The predicate may be dependent on either the new or old value of the variable: 'These are available as the LVALs of NEWVAL and OLDVAL, respectively.

When a monitor is triggered, it prints the type of monitor, the variable being monitored, and any other information requested by the user, and then calls LISTEN. A monitor prints as yet another glyph: M[ atom] object where atom is the ATOM being monitored, and object is the object on which the call to MONITOR is placed,

Edit monitors are objects of type BREAKR, and thus they arc killed by the same commands that kill normal breakpoints: KB, KT, and so on.

RM atom predicate 'any sti Read Monitor

RM is analogous to RW, but is only triggered by reading the variable,

WM atom predicate any ... 'Write Monitor

WM is analogous to RW, but is only triggcred by writing the variable.

3.2.2.10. User-defined Edit Commands

lt is possible to add user-defined commands to EDIT. 'The value of EDIT-TABLE should be a VECTOR of STRINGs (commands) and APPLICABLE objects. EDIT will search EDIT-TABLE before its own command lable. If a match is found, the APPLICABLE will be applied to three arguments: the command string, the LOCATIVE containing the item currently being edited (the immediately surrounding object) and the position in that item.

Note that user-defined commands should not be added except by constructing a new value of LDIT-TABLE from the commands to be added and the old value. Otherwise, any existing user-defined commands may be lost when new ones are added.

The Monitor commands described in section 3.2.2.9 are effectively 'installed' user-defined commands. Hhey add elements to EDIT-TABLE when loaded by the UM command.

3.2.3. Examples

3.2.3.1. Simple Editing

Suppuse you have the FUNCTION a The MDL Editor

#FUNCTION (('A) <EVAL .A>)

48 the global value of the ATOM SIMP. and you wish to change it to #FUNCTION (("BIND" 8 'A) (<EVAL .A .B> .A)) using EDIT. The tollawing example does just that: it includes doing the editing and applying of SIMP to an igument. Console input and output are shown below oxactly as they would be in non-silent mode. (Console input consists of those characters to the left of every $). Note that there is nothing in SIMP which is big enough to warrant use ofan &. <EDIT $ V$ #FUNCTION ( @ ('A) <EVAL .A>) DS ( $ 'A) [ "BIND" BS ("BIND" BB 'A) 3 .AS$ <EVAL .A BF> I .BS <EVAL .A .8 B> URS #FUNCTION (("BIND" B 'A) <EVAL .A .B> 8 ) I .AS AFUNCTION (({"BIND" B 'A) <EVAL .A .8> .A @f ) L 2$ #FUNCTION (("BIND" B 'A) $ <EVAL .A .B> .A) I: LIST 2$ #FUNCTION (("BIND" B 'A) @ (<EVAL .A .8> .A)) CSIMP <+ 1 2>>$ (3 <+ 12>) #FUNCTION (("BIND" B 'A) B (<EVAL .A .B> .A)) OST

3.2.3.2. X and G Commands

In this exaniple we have the FUNCTION <DEFINE F (X)> <G .X% 10> CH 23 <= .K 1>d>$ 4y applying the X and G commands to the appropriate FORMs, we are able to swap the FORMs within the FUNCTION.

<DEFINE F (X)
<G .X 102
<H 23 "<- .X 13334

F <EDIT FO$ HFUNCTION ( B (X) <G .X 10> CH 23 <- .X 15>) RS #FUNCTION ((X) B <G .X 10> <H 23 <- .X 14>} X MOVERS #FUNCTION ((X) EB <G .X 10> <H 23 <- .X 13>) KE #FUNCTION ((X) B <H 23 <- .X 13>) R43 WFUNCTION ((X%) <H 23 <<- .X 15> EH ) G .MOVERSS #EFUNCTION ((X) <H 23 <- .K 13> <G .X 10> & QF] . MOVERS <G .x 10>

3.2.3.3. Unconditional Breakpoints

liinsert unconditional breakpoints into the FUNCTION in the next example, do the following:

|. Define F 1B and test the FUNCTION a few times.

  1. hnter EDIT and position the cursor appropriately,

  2. Insert the breakpoint.

4_|.cave EDIT and mum the FUNCTION again for the value 3. The breakpoint is exercised 5 times during this run.

kB The MDL Edito:

'OEFINE FIB (x) <COND (<L=? .xX L> A) (ELSE <+ CFIB <- .X 259 <FIB <= .K 131$ FI8 <FIB $>$ <FIB 6>$ <FIB 10>$ EDIT FIBD$ RSS #FUNCTION ((X) & <&>) BK T .XSQST <FIB 3>$ BREAK k= j $ BREAK A = 1 LISTENING=-AT-LEVEL 2 PROCESS 1 $ BREAK A= 2 <ERRET Ta$> BREAK kA = 0 $ -". BREAK* = A= 1 $

3.2.3.4. Conditional Breakpoints

We continue from the previous example and demonstrate conditional breakpoints with the following: I. Enter EDIT and kill the breakpoint from the previous example, 2. Position the curser and insert a conditional breakpoint with a predicate of <0? .X>. 3. Leave EDIT and nun the FUNCTION again for the value 10. 4. Enter EDIT and remove the breakpoint.

<EDIT>S$

$ #FUNCTION ((X) B 8<&>) KBSS MFUNCTION ((X) B <&>) BK <0? .X>

3.2.4. Edit Command Summary

NAME ARGS VIEANING ? none type out short summary, ?? none type out this summary 0 any Open object or the value of an atom oT none Open object at the cursor 0 none Quit and return to MDL QR fix Quit and Retry frame V none toggle Verbosity Movement commands L fix move Deft fix objects R fix move Right fix objects u Six move Up fix levels D none move Down one level B none move to Back of object ' F none move to Front of object UR fix move Up fix objects and to the Right OL fix move Down fix objects and to the Left . I any... Insert arguments to the right of cursor ! I: iype fix make next # objects into a fype; [* atom,object imbed command: replace all occurrences of atom (default ) in edject with objects to right of cursor IG any... Insert into group ) SU new.old $Ubstitute new for old, x atom set the atom to the object to right of cursor G any... Get EVAL of arguments, insert to right of cursor Sw none $Wap the two objects to the right of cursor C any Change the next object to arg t fpe Change the type of the next object to (ype K fix Sill (delete) the next fix objects K: none Kill (remove) the 'brackets' around the next object Search Commands; 3/S5R ay Search (Right) until match (=?) is found for any SL any Search Left as above
Macro Commands [

M siring execute the string as if typed to EDIT 1T/D0 fix.siring ITerate the execute string fix times Printing commands P none PPRINT the next object PU none PPRINT the next Upper level PT none PPRINT the whole object open Cursor commands uc none Use Cursors Cu atom set atom to CUrrent cursor position G0 cursor GO to the specified cursor position PC none Print Cursor positions in the current object PA none Print All cursor positions in the top-level object KC atom Kill the Cursor assigned to the atom - Debugging commands BK pred,any... set Break point at next object; if pred evaluates to FALSE, don't break; rest of arguments are printed out at break

BA pred.any... set Breakpoint After next object KB none Kill all Breakpoints in open object KT none Kill 'This breakpoint in the object to the right of cursor Monitor commands UM none Use Monitors RW gtom,pred.any... set Read-Write monitor on atom RM alo, pred.any... set Read Monitor on atom wi atom,pred.any... set Write Monitor on atom

tF and tS return you to EDIT from a higher level.

The ATOM * may be used as a fix argument whose value is the largest legal value for that command.

3.3. Debugging and the Interpreter

Before continuing the discussion of the various packages that are used in the debugging of MDL code, we will expand on the discussion of ERROR, FRAME, (and so on) in Chapter 16 of [3]. 'To summarize that chapter, whenever an ATOM is bound or a FUNCTION or RSUBR is MCALLed in ML, information is added to the contol stack. 'This information, normally 'invisible', may be examined using the functions described in a previous section (FRAMES, FR&, FRLVAL, ctc.). "An invocation of ERROR puts MDL into a LISTEN-like loop.

| iio

Successive ERRORS stack up and arc reflected in the LISTENING-AT-LEVEL message printed whenever ERROR or LISTEN is called. .

In addition to being examined, the stack may be modified as part of the debugging procedure. For example, the SUBRs SET and LVAL take an optional second argument which may be (among several pussible TYPEs) a FRAME. EVALing

<SET X 10 <FRM n>

would change the LVAL of X in the nearest binding lower in the stack than the FRAME FRAMEs lower than the most recent call to ERROR or LISTEN. Similarly

<LVAL & <FRM n>

examines the LVAL ina particular FRAME.

The most common use of the MDL interpreter in debugging is to invoke the SUBR ERRET. With no arguments, it drops all the way to the bottom of the stack and then calls LISTEN: Itsays 'I give up (although side effects are not undone). More commonly, ERRET is given a single argument, which causes the last invocation of ERROR or LISTEN to return that argument. For example, suppose 2 program contains, FOO but FOO has no GVAL. MDL would respond

ERROR UNASS IGNED-VARIABLE FOO GVAL You could give up, saying , but it is often more reasonable to say "Oh, yes, FOO was supposed to be 1000', and then; <ERRET 10005 Still better is <ERRET <SETG FOO 1000>>> which will prevent future ERRORs from the same cause,

Finally, ERRET may be given a second argument of a FRAME, which means to return the first argument as the value of the invocation of that FRAME. In the previous example, the programmer might look at the stack (with FR& or FRAMES) and see

i GVAL [FOO] 2 EVAL [, FOO} 3 EVAL [<+ .X .Y,FOO>] 4 EVAL [<LOSER .A .B>] . 5 EVAL [</,GOOD-GVAL <LOSER .A .B>>] 6 EVAL [<WINNER 1.0 2.03] 7 LISTEN []

After some thought, he may just say "Well, LOSER apparently needs some debugging, but for now I'm

interested in WINNER', in which case he can 'fuke' a reasonable return from LOSER by typing <ERRET 342.0 <FRM 4>>> which returns 342.0 exactly as though LOSER had returned it.

More complex errors are sometimes more difficult to fix, requiring the use of EDIT (at least). In the above caumple, the programmer might decide to debug LOSER after all. 'There are two ways to go about this: First the problem is localized, the FRAME itself may be edited (which is to say, the contents of the FRAME may be edited). Changes will show up in the FUNCTION from which the FRAME's contents were derived. 'The newly corrected FRAME may then be RETRYcd. lor example,

<EDIT 3>$
.. various editing commands

ORS

second. the function itself may be edited. In the process, it may be so changed that the FORM which caused the ERROR no longer even exists. Often, the easiest solution is to retry the invocation of the EDI Ted FUNCTION from scratch: in this case

<RETRY <FRM 4>>$>

\s always, the major restriction to remember is that side-cffects are not undone by RETRY.

3.4. Loading and Dumping

GROUP-LOAD and GROUP-DUMP are used tu load and dump files of MDL programs in such a way that the vantents of the file are made available in a MDL structure called a group. Many other PACKAGEs in the MDL environment operate on or change groups: Among them are "EDIT", "GLUE", "PDUMP", and the MDL compiler,

GROUP-LOAD and GROUP-DUMP are aimust as widely used as FLOAD as a way of dealing with groups of MDL functions. Consequently, they are already loaded in most initial Mins, as part of the package "GRLOAD"

"GROUP-LOAD fie-name-siring : eroup mane; aint? : Tle-nante:strine is the file to load, sroup-neresdion: is The name ty give the group, It is optional and by default the ATOM formed by PARSE of the first name of the file to load. The group will be stored as the LVAL of group-name. aROUP-ODUMP is the opposite oF GROUP-LOAD. It outputs the group from the MDL to the file given as its first argument Functions unchanged since the last GROUP-LOAD are copied from the original input file. Functions that have been edited are output using the routine given as the third argument to GROUP -DUMP. "<GROUP-DUMP filr-name-siring Crap ties aloe prini-routine Ail break points?> He-namecsiring is the only required argument. [t isthe file to whieh to output the group. group-namesaton is optional, and defaults as it does for GROUP-LOAD, but of course gives an ERROR if the zroup doesn't't already exist. orth routine is optional, and defaults ta,PPRINT unless the group contained NBIN formut RSUBRs, in which case, PRINC is used. vill-break points? is optional, by defaule T, in which case GROUP-DUMP kills all EDIT breakpoints and monitors in objects being dumped. Giving a fourth argument of a FALSE to GROUP-DUMP prevents this.

On the surface, it appears that little happens in the process of loading a file and makiny it into a group.

Hlawever, a great deal of information about the group has been stored away in associations for later use. Some of this information is of use to the MDL programmer:

|. On an association between group-name and the ATOM CHANNEL is stored a LIST giving the name of the file that was GROUP-LOADed to form the group. Removing this association before GROUP-DUMPing has the effect of making the entire group be output from cuore rather than copied from the original source,

*. On an association between group-name and the ATOM MAGIC-RSUBR the ATOM T is stored if the group contained any RSUBRs in fast (NBIN) format. It is this association which is used to Uetermine the default print-rautine in GROUP-DUMP.

3.The OBLIST path in effect at any time during the load is available. The original path is sured on an association between growp-nwre and the ATOM BLOCK. Within the group, the path changes are stored in an association between the group RESTed to the point of change and the ATOM BLOCK.

  1. [Ff the second element of a FUNCTION definition is not an ATOM, the actual FUNCTION name gutten by EVAL of that element is stored as an assuciation between the original element and the

_oading and Dumping 34

ATOM VALUE. 5. The location of a function within the input file is stored as a LIST of the starting and ending offsets (in characters) of the function, under an association between a locative to the GVAL of the : of FUNCTION name and the indicator DEFINE. 'This association is removed by EDIT (and other editors) to indicate drat the FUNCTION has been changed, He There are additionally several switches that affect the operation of GROUP-LOAD: 'le. .KEEP-FIXUPS If the LVAL of KEEP-FIXUPS 'Js true (and GROUP-LOAD binds it that wav during loading), the fixups of WSUBRs GROUP-LOADed will be kept. .EXPFLOAD lt the LVAL of EXPFLOAD its truce. FLOADs will be expanded. 'That is. the objects in the file FLOADed will be uwlded to die group in place of the FLOAD, the initial setting of EXPFLOAD is a FALSE. -EXPSPLICE 1<ithe LVAL of EXPSPLICE is true, any objects returned within SPLICEs will be inserted directly into the vioup as described above. 'The initial sctting of EXPSPLICE is a FALSE. : 'h

3.5. The One-step Debugger

d The MDL One-step debugger allows the user to step through the evaluation of any MDL expression one operation' ata time. Between steps, variables may be examined or changed, functions edited, and so on. luis is possible because the debugger runs in a different Mil. PROCESS than the expression being stepped, vida MDL PROCESS may ISTEP another [3]. Vo load the Debugger. <USE ""DEBUGR">. The MDL Debugger can be in any of three states. In the initial state, OF F, no one-stepping occurs and the lichugger does not listen for any special interrupt characters. The Debugger is, therefore. completely inactive. hy ivping to MDL, vou leave the OFF state and center the READY state. In the READY state no one-siepping occurs, however the Debugger does listen for interrupt characters. By typing the interrupt haracter +B, you enter the ON state and one-stepping begins. In addition. if you were stopped at an EDIT ieakpoint when the tB was typed, the breakpoint will automatically be exited and evaluation continued in lim one-Si<pping State. While in the ON state, the Debugger will proceed through the execution of any MDL objects one step ate ine In-essenee, the Debugger stops ust before and just-after every call to EVAL, At each step the Dchugee: i indicaie its current condition as foliows. If EVAL is recursively entered at level, m, with input, edjecs. the iesplaey WHT be:

=> object i (where object is ampersand printed). IF EVAL is returning fromm level, 1, with result, object. the display will be: ac= object (where abject is ampersand printed).

The Debugger will stop at each such step and wait for directions., here are four interrupt characters that may be typed to proceed further in the program: tN, 10, tR and tA. They each take an optional prefix argument thal serves as a repeat count

tH causes the Hebugger to perform the next step of the current evaluation.

al) causes the current object to be completely evaluated without any one-stepping and then stops with the result of that evaluation. tO is useful for stepping uver COND predicates that you know will not succeed, of more generally, uninteresting parts of a program,

tA is similar to tO, but specific to the evaluation of the argument list of a FUNCTION, PROG, or REPEAT. Typing 'A during such evaluation allows the rest of the argument list to be evaluated without one-stepping and then stops before evaluating the body of a FUNCTION, PROG, or REPEAT or retuming of a result.

TR is most effectively used in a REPEAT or PROG loup. 'Typing tR causes evaluation to proceed until control returns to the point in the body of the REPEAT/PROG at which tR was typed. It thus allows you to go once around a loop. =

It should be noticed that, when stopped at one of these steps, you can examine and modify program variables, do a FRAMES or FR&, EDIT FUNCTIONs and set breakpoints, and in general perform any valid MDL operations. Also, when you stop, the LVAL of the ATOM LAST-OUT will be set to the object the Debugger last typed out. This is useful if the & performed by the Debugger did not show a particular detail that you ure interested in.

Use the interrupt character tE to leave the ON state and return to the READY state. Use the interrupt character tQ to leave either the ON state or the READY state and return to the OFF state. When leaving the ON state as described, the execution currently being ane-stepped will be finished in the usual manner.

The function REPAIR attempts to fix any errors in the Debugger that you might happen to invoke. These errors are easily distinguished since they never occur in MDL's MAIN PROCESS, Therefore, you will see:

Ls; LISTENING-AT-LEVEL mr PROCESS <i (where nis not 1), REPAIR turns off the Debugger and returns you to running in the MAIN PROCESS (no longer one-stepping). Because REPAIR turns off the Debugger, you must do again if you wish to ry any further one-stepping.

3.5.1. MDL Debugger Command Summary

<USE "DEBUGR"> loads the Debugger.
<DEBUG> makes the Dcbugger ready.

tB begins one-stepping. tN performs the nex! step of the computation, tO steps completely over the next computation, then stops and continues one-stepping. tA evaluates the arguments of the current object then stops and continues one-stepping through the body. tR continues evaluation until you return to this point. TE ends one-stepping. +Q quits one-stepping and makes the Debugger unready (turned off). prints a command summary. attempts to repair any Debugger errors you might invoke.

3.5.2. MDL Debugger Special Features

The following flags have special importanec to the Debugger: . INDENT-INC is the amount by which to indent for each level (by default 2 spaces). , INDENT-MOD The indentution-level is the real level taken modulo this number. 'The default is 10. Indentation 'restarts' when level gets here. If you don't like this feature, make the number large. , INDENT-OIF is the minimum amount of free space to reserve on euch line that indentation must not touch (by default 20). Therefore at level L the indentation is exactly:

t <MIN <*,INDENT-INC <MOD .L, INDENT-MOD>> <- <13,OUTCHAN>, [INDENT-DIF>> .OUT-FAST iFtrue the Debugger will not stop when leaving a level with arcsult. The default is T. , OUT-UNIQUE if both this and previous flag are true successive 'outs' of the same item will not be displayed (defaults to T). , 5ELF-FAST if true the Debugger will not stop when entering a level with an object which EVALs to itself (e.g. ATOMs, Fikes, STRINGS). The default is T. 'The display will be: n> object .FORM-FAST if true the Lebugger will not stop when entering a level with any of the 'short' FORMs (e.g. <>,. FOO,, BAR, "ANY THING). The default is T. The display will be: a: FOO = /val Any of these flags can be SETGed by you to tailor the Debugger to your own tastes.

3.6. Execution Tracing

The "TRACE" PACKAGE provides a facility for observing the arguments and returned values of selected FUNCTIONS and RSUBRs. It is possible to print the arguments on entry to the function, print the value returned, and to break on entry to and exit from the function. All actions may be performed cunditionally. To load TRACE, type <USE "TRACE"> .

3.6.1. Using TRACE

TRACE is invoked by what is either an ATOM or a LIST of ATOMs, naming the things to be traced. These may include SUBRs, FUNCTIONs, and RSUBRs: however, anything which is traced must EVAL all of its arguments. options specifies the behavior of TRACE with respect to the specified function. There are five switches, as follows: IN-BREAK means break (cause a Mint. ERROR) before culling the function. Normally off.

IN-PRINT means & function arguments on entry. Normally on. OUT-PRINT . means & function value on exit. Nunnally on. OUT-BREAK break after executing the function call. Normally off, VERBOSE means & the arguments to the function one per line. 'This is useful if the arguments are long. Normally off. 'To cause a given option to be unconditionally on, include its name (an ATOM) in the wprions TUPLE. To cause an option to be unconditionally off, include a two-element LTST, composed of the option name and a FALSE. If the second element of the LIST is neither FALSE nor an ATOM, it will be EVALed each time TRACE examines the setting of the given option for the function. 'This allows conditional breakpoints, for example, Thus: <TRACE FOO (OUT-PRINT <>)> will cause FOO's arguments to be printed on entry, but the value will not be printed. <TRACE FOO (OUT-PRINT '<G?

3.6.2. Understanding TRACE

TRACE works by CHTYPEing the specified functions to new types which have an APPLYTYPE associated with them. This means that one cannot trace calls to RSUBRs or RSUBR-ENTRYs which are already linked. In addition, it means that UNTRACE must be used to get the old value back. 'To determine the status of a function with respect to tracing, say <GET applicable TRACE: . this returns FALSE if applicable is not traced; otherwise, it returns an object which describes the scitings of the various options. The object hasa PRINTTYPE which associates the name of each option with its setting:

<GET,FOO TRACE>$

FOO IN-BREAK: #FALSE () IN-PRINT: T QUT-PRINT: <G?

3.7. Monitors

A common problem in debugging is the mysterious 'clobbering' of same value or element of a data "nicture. Mb! has imbedded in it 4 mechanism for triggering interrupts on references, either for reading or writing, to values of variables and elements of structures.

'The "MONITOR" PACKAGE is designed to be a readily aceessible user interface to these "READ" and "WRITE" interrupts in the MDL interpreter.

To obtain "MONITOR",

<USE "MONITOR">

There are three basic kinds of 'things' which can be monitored: values of ATOMs, elements of STRUCTUREDs (the TYPE of the element is not important), and ASSOCIATIONs.

For ATOMs, the LVAL or the GVAL may be monitored, If the LVAL is to be monitored, tne ATOM must be ASSIGNED?. lor the GVAL, the ATOM must be GBOUND?. If these conditions cannot be met, a monitor cannot be generated.

For STRUCTUREDs. the monitor is on the nth element, where n is specified when the monitor is created. Remember, the monitor is on a slot of the STRUCTURED, not on the contents of that slot!

For ASSOC LATIONS. the monitor is on the association itself.

3.7.1. Monitor Internals

This section expands on the discussion of monitors in the MDL document itself [3].

Mini. defines two types of monitors: Read and Write. These are implemented in the language by two

intermupis, READ!-INTERRUPTS and WRITE! -INTERRUPTS, respectively. In addition, the "MONITOR" PACKAGE can allow read-writc monitors. The "MONITOR" PACKAGE is at base a set of functions to create and handle these interrupts. A monitor is triggered in the following cases: Read monitor:

For LVALs -- via LVAL

For GVALS == via GVAL

For STRUCTUREDs -- via NTH

For ASSOCIATIONs -- viaGET and GETPROP Write monitor:

For LVALs -- via SET or "AUX" bindings

For GVALSs -- via SETG

l-or STRUCTUREDs -- via PUT, SUBSTRUC

For ASSOCIATIONs -- viu PUT and PUTPROP

Nate that PUTRESTs of LISTs which may alter the wth element of a LIST, do not access the old ath element of the LIST and therefore do not cause a write monitor to trigger.

Internally, MDL performs monitoring on LOCATIVEs to STRUCTUREDs. In fact, LVAL and GVAL are really pointers to an internal structure. 'This need not concern the user except in the case of LVALs of ATOMs. In this case. MDL will monitor a LOCATIVE to shat (exactly that unique) binding of the ATOM. When that binding becomes invalid, or more precisely,

CNOT <LEGAL? Jocative>>

a function in the "MONITOR" PACKAGE will make the monitor vanish. [llegal monitors print as #MONITOR [ILLEGAL ] (if you ever get a pointer to one}. Remember that if you want to monitor the LVAL of an ATOM bound in a FUNCTION (or PROG, ctc.), you must create a new monitor each time, as a new binding is created each time. One way to do this is to edit into the FUNCTION a call to MONITOR (see below) after the ATOM becomes ASSIGNED?. Fortunately, EDIT (see section 3.2.2.9) has commands to do exactly that.

3.7.2. Creating MONITORs

Creation of all monitors is done through a call to MONITOR (which returns an object of TYPE MONITOR), as follows:

i ae

<MONITOR sype;siring

object where; predicate lodo:tuple> where: nipe is one of "READ", "WRITE", or "RW". object is either an ATOM or a STRUCTURED, or an ASSOCIATION item. where ig either LVAL or GVAL (if object is an ATOM) or a FIX, (if object is a STRUCTURED), or an ASSOCIATION INDICATOR. predicate is something which is EVALed to determine whether the monitor is to be triggered: this defaults to rT The "MONITOR" PACKAGE defines three variables which can be referenced in the test: OLOVAL is the old value of the object monitored. NEWVAL is the new value of the object monitored, MONOBJ is the object monitored (ATOM or STRUCTURED). Here valwe means LVAL, GVAL, or element. Obviously, NEWVAL is not set for "READ" monitors. 'odo is any number of things to be EVALed and PRINTed when the monitor is triggered. Note that predicate and fodo are identical to the analogous arguments of the ED IT 8K command.

3.7.3. Monitor Events

When a monitor is triggered, the following is printed (remember the predicate \s evaluated before this), and then LISTEN iscalled. To continue, . Read: READ of where of objeci Value: oldval todol = resultl todo? = result2 Write:

WRITE of where of object Old value: oldval New value: newval : todo! = result] lodo2 = resuli2 A slightly different first line format is used for associations.

3.7.4. Killing Monitors

Killing a MONITOR is accomplished by calling KILL-MONTTOR as follows: <KILL-MONITOR mignitor? or In the latter case, type, object, and where arc as given in the original call to MONITOR. To kill all MONITORS, use .

3.7.5. Other Monitor Routines

<MONOB) smonitor>

returns the objec! monitored. returns the where of the MONITOR, flushes invalid MONITORs from the MONITOR LIST. 'This is done internally and need not be called routinely, .>MONITORS is a LIST of all current MONTTORs.

3.7.6. What You Can't Do with Monitors

You can't monitor the LVAL of something BOUND? but not ASSIGNED?. F.g.,

<DEFINE WRONG ("AUX" BAR)

MONITOR "READ" BAR LVAL> cote 2 You can't expect compiled cade tu cause monitors to be triggered. Naturally, you can't place monitors in compiled code; however, a compiled reference to a monitored ATOM will not usually cause the monitor to trigger either.

3.8. FINDATOM

'The "FINDATOM" PACKAGE is intended to reduce the problems caused by multiple OBLISTs and lengthy ATOM names in MDL. [it allows one to find all ATOMs whose PNAMEs match some specification, which need not be exact: in addition, one may place constraints on the values of the ATOMs found. F INDATOM is invoked as: <FINDATOM speesir:siring searchilist constraints . oulobl:list> specsir is a STRING describing the PNAMEs of the ATOMs one wishes to find. 'Three special characters are recognized in this S1RING: : matches anything, including an empty string =: matches any single character tQ: quotes the following character Search strings may be an arbitrary concatenation of normal and special characters, For example: "SOM": matches any ATOM containing "SDM" anywhere in its PNAME. "6=SOM"-: matches any ATOM containing "SOM" in its PNAME, provided that at least one character precedes the "SOM". "+Q*": matches any ATOM with PNAME "*", "e": matches any ATOM. If tQ is the only special character in the string, it need not be quoted: "tQ" searches for ATOMs with PNAME "tQ", searchlist specifies the OBLISTs to search. Possible values are: #FALSE (): search all OBLISTsin .OBLIST

<WFALSE (oblists-ur-forms): search all but the OBLISTs specified,;

ablist. search only this OBLIST, list-of-oblists: search only the OBLISTs in this list, else. search all OBLISTs. This is the default. consiramts is a TUPLE describing the value of each ATOM found. It may consist of any number of valid TYPE names, along with arbitrary structures and the following special objects: T: if present, overrides any other constraints: if no other constraints are specificd, this is assumed, Any ATOM matching specsir will be accepted. ANY: overrides any constraint other than T. Any ATOM matching specsir which has a value (either GVAL or LVAL) will be accepted, <>: any ATOM which has no value will be aceepted. Note that giving both ANY and <> is equivalent to giving T. LINK: any LINK will be accepted. If other constraints are provided, they work as follows: all valid TYPE names given (ones for whom f VALID-TYPE? returns T) are stored in a structure: when a value is encountered. its TYPE is MEMOQed on this structure. If the ATOM does not succeed here. it is next checked against the 'arbitrary . structures." Anything in constraints which is neither one of the above 'special objects' nor a valid type is treated as a DECL specification. All such objects are put in a FORM starting with OR, which has the effect of gencrating a single DECL specification. When a value is found, DECI? is called with the value as its first argument and the generated FORM as its second. If DECL? returms T, meaning that the FORM is valid as a DECL for the VALUE, the ATOM is accepted. ATOM FALSE '<LIST [REST FIX]> specifics that any ATOM accepted must have either a GVAL or an LVAL which is of type ATOM or FALSE, or which is a LIST of FIXes. < '<LIST [REST OBLIST]> specifics that any ATOM accepted must match the DECL SOR <LIST [REST OBLIST]>> outobl, if present, is a LIST of OBLISTs which is the LVAL of OBLIST when FINDATOM prints things. 'has, one may force all ATOMs to be printed with full trailers by providing an empty LIST here. 'The fast argument given to FINDATOM, provided itis a LIST, is assumed to be ourobl, FINDATOM prints the name of each ATOM it accepts, followed by the STRING "Gassigned" and the type of GVAL if the ATOM has one; this will be followed by the STRING "Assigned" and the type of the

  1. The MDL Programming Environment LVAL if the ATOM has one. [t prints the number of ATOMs found when it finishes,

3.9. "PINFO"

"PINFO" is an informational PACKAGE. It is used to examine the OBLISTs of the PACKAGES loaded into an MDL There are two major entries in PINFO. <PCK-INFO package:siring internal?:boolean> Both arguments to PCK-INFO are optional. If neither argument is given, the names of the PACKAGES loaded into the Midt. are listed. If a package is given, the contents of the package's ENTRY OBLIST are listed, as weil as information about the VALUE of each ENTRY. If jnernal?is provided and non-FALSE the contents of the internal OBLIST are also listed. PCK- INFO prints an error message if package is not loaded. i lists the names of PACKAGEs USEd by package or returns a FALSE if package is not loaded.

3.10. Debugging in a Run-time Environment

A fairly common occurrence when running 'debugged' code is to find that if was not after all completely debugged. It is useful to be able to load interpreted versions of some FUNCTIONs in a PACKAGE into the compiled environment for debugging. "DFL*. "ROFL", and "UNLINK" are PACKAGES written to simplify this procedure,

3.10.1. DFL

The "DFL" ('Debugging Fload') PACKAGE is a set of routines for loading and dumping of small numbers of FUNCTIONs from a larger file. It is useful in debugging already running systems, or ones which have not been GROUP-LOADed. To get "DFL" <USE ""DFL"> 'The main entry of the "DFL" PACKAGE is DFL: <DFL fiene-names file-name:siring unlink?:boolean> where all arguments are optional and fune-names is the name(s) of the DEF INEd FUNCTION(s) to be obtained from this file. It may be an ATOM, a STRING, of a structure of ATOMs or STRINGs: if ATOMs are given, their SPNAMEs are used. 'The default is the argument last given to DFL or RDFL. file-name is the file to obtain the FUNCTION(s) from. 'The default is the last file DFLed or RDFLed. An ATOM may be given, in which case its SPNAME is uscd for the first file name. unlink? \f this is true, and if one or more of the values replaced by the DFLed FUNCTIONs were RSUBRs or

oe

  • RSUBR-ENTRYs, the reference VECTORs of all RSUBRs, including pure ones, will be searched for rs occurrences of the old value; such occurrences will be replaced by the ATOM. 'This is the inverse of ma RSUBR-LINKing. Pure structures will be unpurified: this does not change their address in core, but
  • simply makes the page they live in read/write.

In the normal case, if an RSUBR or RSUBR-ENTRY is being replaced, unlinking will occur automatically in garbage-callector space only if RSUBR-LINK is T. Also, remember that unlinking is not the same as substituting: only RSUBRs stored at top level in reference VECTORs are found: if the old value itself was ina

; structure (such as a dispatch table), it will not be replaced.

3.10.2. RDFL

1 ROFL is similir to DFL but is for reloading RSUBRs rather than FUNCTIONs. RDFL is contained in the PACKAGE "ROFL". <RDFL func-names file-name unlink? ghie?>

  • 'The first three arguments are us for DFL. The only difference between RDFL and DFL (barring the effect of the fourth argument) is that ROFL searches in the file for"<SETG 'rather than *<DEFINE '. 4 glue? If non-FALSE, RDFL will READ and EVAL the next object in the file following each RSUBR read. This : will in the normal case obtain the "glue bits' for the RSUBR (see section 6.1), 'The default for glue? is 4 <AND <ASSIGNED? GLUE!-> .GLUE!-> : This is the FORM uscd in NBIN files to determine whether glue bits should be kept, a Note that RDFL will work to reload any SETGed object, not just RSUBRs. J RDFLing an RSUBR-ENTRY does not work and may well be fatal: you must RDFL the RSUBR in which sl the RSUBR-ENTRY is an entry, as well.

3.10.3. UN-DFL

UN-DFL is for writing out DFLed FUNCTIONs after EDITing. <UN-DFL atoms filnam force?> atoms is an ATOM or a list of ATOMs, which will be UN-DFLed. The FUNCTIONs defined must all be from the same file, or UN-DFL will not work. UN-DFL can only UN-DFL things which were previously loaded by DFL. filnam The default is the file the ATOMs originally came from. force? Normally, UN-DFL will object if there is a versiun between the file the FUNCTIONS came from and the file which UN-DFL will create: it thinks it will likely destroy useful information. Providing an ATOM here causes this scruple to be ignored. It is almost always unwise to do so, For example:

DFL (FOO BAR)> <UN-DFL BAR? will cause UN-DFL to fail. Moral: DFL and UN-DFL your FUNCTIONs together.

The "UNLINK" PACKAGE contains three entries: UNLINK, PURE?, and UNPURIFY. UNLINK is sometimes called by DFL; PURE? and UNPURTFY are good ways to I*kuratively defeat the sufety 'interlock' of MDL UNLINK is used to unlink RSUBRs after they have been linked. (Sec the discussion of RSUBR-LINK in [3)). <UNLINK atoms pure?> atoms is alist of the ATOMs to be unlinked, or a FALSE, meaning unlink every RSUBR in the MDL, of a gruup-name, meaning unlink calls to all FUNCT TONs and RSUBRs in the group. pure? is optional and defaults to FALSE, but if true, even pure RSUBRs will be searched. UNLINK examines all the OBLISTs in the MDL, looking for RSUBRs; if an RSUBR exists only in a structure, and not at j top level in any RSUBR's reference VECTOR, it will not be found. PURE? takes an object and determines if the right half of the value word is greater than the number : contained in the MDL location PURBOT, which is the lowest pure location in MDL. Ergo, 'Is the object I gave you pure?' It is only meaningful for structures. UNPURIFY takes a single argument, which must be of PRIMTYPE VECTOR or UVECTOR (i.c., it must have an AOBJN pointer for its value word). It causes the pages in which that object lives to became impure, and returns [. lccause there is no way on ITS to make a read-only page an impure page directly, the following algorithm is used by UNPURIFY: |. Is the object pure, according to PURE? If not, leave. 2. ls UNPURIFY-PAGE! -IUNLINK GASSIGNED? If not, get a page from the interpreter, and SETG the aforementioned ATOM to its number. [.c., the page is more or less permanently taken for use of UNPURIFY. }. For each page occupied by the object: a) If the page is already impure, do nothing: b) otherwise,

: map the page on top of UNPURIFY-PAGE: c) create a new, impure page where the old page was. ' d) copy the contents of UNPURIFY-PAGE back to the old, now impure page.

  • 'Thus, no pointers are changed: as far as MDL is concerned, in fact, nothing has changed. 'The unpurified
  • pages are still pure, according to Its page map. However, you may freely change the unpurified object.

If your change to the newly unpurified object consists of PUTing a pointer into garbage-collected space into the object, you may lose completely unless the pointer points to a frozen object. The MDL garbage collector does nuf examine unpurified objects. UNLINK can only use UNPURIFY because all ATOMs referenced by pure RSUBRs are indeed frozen.

For the above reason, use of UNPURIFY is not recommended for the general user,

3.11. CRITIC

"CRITIC" is a PACKAGE designed to aid the user in debugging (and perhaps increasing the efficiency of) his programs. It accumulates and prints ina readable format information about the interactions of the various FUNCTIONs (and LVALs and GVALS) in a group. Jt also warns the user about various conditions it considers to be either non-optimal or erroneous, such as incorrect use of SPECIAL, forgetting to QUOTE some structure,

and so on. Like most critics, it is sometimes wrong, but it tries to perform a useful service. To load "CRITIC" say <USE "CRITIC"> There are two major entries, one of which prints more information than the other. CRITIC group-name vutput-file> where grouwp-name is the ATOM returned by a GROUP-LOAD, and the optional ow/pui-file is a STRING giving the name of the file to output to (by default with second file name "CRITIC*). This can also be a CHANNEL if you are planning to do several CRITICS into one file. CRITIC prints information about intcractions among the FUNCTIONs in a group (as described below). is similar but only prints 'errors' and 'warnings' -- things that might be problems with the FUNCTIONs in the group. The output format (for each FUNCT IOW and for the group as a whole) is as follows: Junction (object number of function in group) Called-by: a list of all the functions which call function ' Ln =

Calls:a list of all the functions called by function SETG: external glubals SETGed by function GVAL: external globals referenced by function SET: external variables SET by function LVAL: external variables referenced by fiunetion SPECIAL: variables declared' SPECIAL by function USE-DATUM: DATUMs used by function

The above table is printed by CRITIC but not by CRITIC-NOTES. 'Extemal' as used above means "External to fanetion'. CRITIC-NOTES and CRITIC both print information about possible defects or errors in each FUNCTION. 'These can be any or all of the following (explanations follow where needed).

3.11.1. Global Problems with the Group

FLOAD in file. 'This is pretty minor: FLOADs at top level are discouraged if you can avoid them. ) BLOCK or ENDBLOCK at top level in PACKAGE. PACKAGEs should not have to resort to this. agiom-nane: MANIFESTed structure. The ATOM given is a structure but was MANIFESTed. Since a MANIFEST is copied within the reference VECTOR of any RSUBR that uses it, it is usually nota good idea.. ENTRYS not bound, assumed locals: atomr-list The ATOMs given were made ENTRYs in the PACKAGE, but were not bound, so CRITIC has assumed they are locals, for lack of something better to do. Packages USEd but never referenced: package-names These PACKAGES were in USE statements but no ATOM was ever found which fell on their OBLISTs. 'There will sometimes be incorrect entries in this list if you USE a PACKAGE which sets up a funny ENTRY OBLIST (RPACKAGES included) or no OBLISTs atall. Internal functions unused: alonriist 'These are FUNCT LONs DEFINEd but apparently never referenced and not entries. There will sometimes be incorrect entries in this list if you have FUNCTIONs invoked only by funny dispatching mcthods, such as

: APPLYing or EVALing an element of a structure. Internal globals unused: avonrlist . ATOMs SETGed at top level but never referenced. Internal manifests unused: atom-lisi ATOMs SETGed and MANIFESTcd at top level but never referenced.

3.11.2. Parameter List Problems

ATOM afonr-name used twice in parameter list. 'The ATOM named was bound twice in the same parameter LIST within the FUNCTION. MDL doesn't't worry about this, but you might. Untasteful re-use of ATOM afonr-name in ROOT. SUBR or FSUBR. This is repurted because the ATOM will have to be unpurified, which is expensive, "BIND" illegally located. | A "BIND" was found other than at the beginning of a parameter LIST. "CALL"/"ARGS" illegally located. A "CALL" or "ARGS" was found after the "AUX" in a parameter LIST. "OPTIONAL" illegally located. "OPTIONAL" was found after "AUX" in a parameter LIST. "TUPLE" illegally located. "TUPLE" was found after "AUX" in a parameter LIST, atom "AUX" illegally QUOTEd. The ATOM named was given as a quoted argument in the "AUX" part of the parameter LIST. External locals set but unbound and unDECLed: atonrlisi External locals set but unbound: atonn-lisi Two different classes of hacking an external local. In both cases it means that the ATOMs did not appear to

I be improperly SPEC TALed, since no one bound them higher in the call tree (or at top level). 'These are most ulten indications of misspelling or forgetti ng tO put a temporary in the parameter LIST, External locals used but unbound and unDECLad: arom-lisy External locals used but unbound: atom-list A reference to an external local which was not bound anywhere is probably a misspelling of a SPECIAL bound elsewhere or the result of forgetting to put the ATOMs in the FUNCTION's parameter LIST. External locals set but unDECLed: atom-list External locals used but UNDECLed: atom-list An external used but not DECLed usually means that the cumpiler will produce poorer code,

3.11.3. Unused ATOMs

Argument unused: atont-lisi The arguments listed were never referenced. Unused: atom-lisi; The ATOMs listed were bound at top level of the FUNCTION and never referenced. Unused in PROG: atom-lisy Similar to the above, but the ATOMs were bound within a PROG. Unused in REPEAT: avonrlist Similar to the above, but the ATOMs were bound within a REPEAT. Unused in FUNCTION: atom-jisy Similar to the above, but the ATOMs were bound within a nameless FUNCTION, such as the second argumcnt to a MAPF /MAPR, Unused SPECIALS: atonrlisy 'The same as above (including ... im FUNCTION'. cle.), except that the ATOM was SPECIAL. This message results from really looking down the call tree, so it is more accurate about this problem than the; compiler, which only looks at the FUNCTION in which the ATOM is bound. &

3.11.4. Function Calling Errors

: Calls undefined function atom. : : The FUNCTION calls an undefined FUNCTION (undefined at the time CRIT IC ran). Calls fuiction with too few arguments. Calls function with too many arguments. External FUNCTION function The FUNCTION named is called but duesn't seem to fall on any Of the OBLISTs associated with the group.

3.11.5. SPECIAL/UNSPECIAL Problems

| SPECIALS never used as SPECIALS: <atomelisi The ATOMs were made SPECIAL but never used outside the FUNCTION in which they were bound. atom-name is unused or should be SPECIAL. a A very specific error which means that the ATOM given (always one of INCHAN. OUTCHAN, or OBLIST) br was bound but never referenced within the FUNCTION, and was not SPECIAL: [ither you bound it for Et effect and forgot to SPECIAL it, or you didn't need to bind it. Ue aieom unbound in paths: path-list [f the FUNCTION is called by one of the paths given, the atom will be unbound. A path is just a list of calls CRITIC has found are pussible, such as (FOO BAR BLECH), meaning "FOO is called by BAR which is called | by BLECH'. The ATOM atom used in jen! should be special in fen?2. This note will appear with both FUNCTIONs mentioned. It means that atom is referenced in Jen/ and the nearest FUNCTION that binds it and calls down to fen/ is fen2.

3.11.6. DECLing Problems

RSUBR has no DECL. FUNCTION has no DECL. Parameters not DECLed: atom-iisy The ATOMs given were bound but not DECLed in the parameter list of a FUNCTION, PROG, or REPEAT. ee '

No DECL in DECL for: atom-list 'The ATOMs in the atom-/ist given had no associated declarations, NEWTYPE not DECLed: fype-name A NEWTYPE of a structured type was made but no DECL argument was included. In a structured NEWTYPE. including a DECL of the interior can greatly increase the efficiency of compiled code. Illegal DECL: afonrlist decl reason The DECL pair given had illegal syntax for the reason given. 'These can include: "Not a legal type": Anobject appeared ina DECL that was notan ATOM, FORM, or SEGMENT. "Type-name not a type: afta": Something other than a type-name or special symbol (such as ANY) appeared where a type was expected. This is sometimes caused by not having your environment completely set up when CRITIC is run. "FORM/SEGMENT too short": A FORM/SEGMENT construction of only one element was found. "SPECIAL/UNSPECIAL with three or more elements" "Bad PRIMTYPE type": The type given in a PRIMTYPE was .:ot a type-name. "PRIMTYPE with three or more elements" "Bad type of structured type": The type-name given as the type of a structured type was not a type. For example, where FOO is not a type. "Bad BYTES specification": A BYTES spccification was not of the form , or the byte sinc was greater than 4. "BYTES DECL too short": A BYTES construction of only one element was encountered. "BYTES DECL too long": A BYTES construction of more than three elements was encountered. "VECTOR in OR specification": An NTH/REST/OPT construction was found at top level of an OR. "Nth/REST/OPT too short": A one-cicment NTH/REST/OPT. "Only REST or OPT may follow OPT": Something other than a REST or OPT was found after an OPT.; "REST must terminate DECL": Something was found -- REST in the DECL.

3.11.7. Miscellaneous

Possibly should be QUOTEd: sfruciure.

'The structure given will be =? to itself iF EVALed. CRITIC lists these under the assumption that you might have forgotten to QUOTE a structure that should have been. It says "possibly" because you obviously want to build new structure sometimes. One way to do this without offending CRITIC is to build new structure with explicit calls to LIST, VECTOR, etc.

3.12. Program Environments

'The ENV PACKAGE makes it easier to load programs into different environments. Itallows certain actions to be taken during loading only if'a given 'feature' is present. E NV has three ENTRYs, and is preloaded, If given no arguments, FEATURES returns the current feature LIST. If its first argument is nota FALSE, the arguments are added to the feature LIST. if the first argument is FALSE, the remaining arguments are removed from the feature LIST. 'Thus, <FEATURES "COMPILER"> says that we are currently in a compiler. All of the 'feature' arguments may be either STRINGs or ATOMs, internally features are stored as STRINGs to avoid OBLIST problems. <FEATURE? features: tuple> returns T if any of its arguments is on the feature LIST. consequences: tuple> uses the first argument to decide whether to evaluate the remaining arguments. features specifies which feature(s) to look for. Tt may be a single feature or a LIST of features. In the latter case, if the first element is a FALSE, what is checked for is the absence of the features listed. Note that this argument is often a LIST created out of arguments to F EATURE?. consequences are things to be evaluated only if the features ure present (or absent, in the FALSE case). For example, <EVAL-WHEN GLUE <SETG FOO 12)

would perform the SETG only if it's evaluated in a GLUE (or some other environment defining that feature).

<EVAL-WHEN (<> COMPILER) <SETG BAR 2>>

would not perform the SETG in the compiler environment

Unfortunately, the ENV PACKAGE is a relatively recent innovation, and so many programs do not set up appropriate environments.