The pymdl book

1. What pymdl is

pymdl implements MDL (originally called Muddle), MIT's Lisp-family language, in Python. It includes the language interpreter, a package and library system, and development tools written in MDL. It also runs MIT's MDL compiler and assembler. A PDP-10 emulator executes the machine code they produce and compatible binaries preserved in the archive.

This book presents the language, environment, and implementation as one account. Part II teaches MDL; Part III covers packages, libraries, editing, and debugging. Part IV explains compiled programs, the compilers, the assembler, and the emulator.

Its foundations are The MDL Programming Language by Galley and Pfister (1979) and The MDL Programming Environment by Lebling (1980). Their material belongs in this book, together with the additional information needed to use pymdl. You do not need to read them separately.

1.1 Which MDL

The 1979 manual documents two MDLs at once: release 55 on ITS and release 105 on Tenex and TOPS-20. They are one language with a handful of operating-system splits, and the manual marks each split as it comes. pymdl provides personalities for both, with ITS as the default. These select the release number and operating-system conventions described in chapter 3. They do not provide a complete installation of either historical operating system; the relevant chapters explain how its facilities are implemented.

,MUDDLE                       ⇒ 55

,MUDDLE is the switch as era code observed it, since no other global flag exists in MDL for the purpose: the environment tools set TENEX? to <G? ,MUDDLE 100> and branch on that. The default personality is ITS, release 55, because that is the machine the era compiler's output was built for and the one an oracle exists for (chapter 37). Chapter 3 says how to choose the other.

pymdl's own. Where the two releases differ, the manual's own words say which is which, and this book keeps those marks. Where pymdl had to choose a default, the choice is stated as such.

1.2 What pymdl runs

A fresh interpreter has the built-in SUBRs and FSUBRs of Appendix A, all 275 of them (Appendix A has each beside pymdl's own line), the standard OBLISTs, the package system loaded from the era's own npck source, and the two manuals' machinery: #DECL checking, associations, locatives, coroutines, interrupts, the listener with its ERROR levels.

Beside the interpreter the repository carries the era's MDL programs that make it an environment rather than a language:

directory what from
src/pymdl/mdl/mudbug/ the MUDBUG debugging tools: EDIT, PP, DEBUGR, TRACE, FRMSP, FINDATOM, CRITIC and the rest (chapter 29) the MIT tapes, byte for byte
src/pymdl/mdl/libmud/ the library system's own sources: npck (packages), nlib (the database), nlup (LUP, the library maintainer) (chapter 30) the tapes
src/pymdl/mdl/compil/ MIT's MDL compiler, 56 modules, the source it was built from (chapter 34) the tapes, with every departure recorded in CHANGES.md
src/pymdl/mdl/mprog/ library packages: DATIME, ITIME, DOW, NOW, LSRTNS, TIMFCN era MDL where it survives, pymdl's own MDL where the era had only assembly
src/pymdl/mdl/mudsav/ the release-55 symbol table, as text the tapes
demo/ four era programs that load, compile and run here, DOCTOR among them (chapter 2) the tapes

Every vendored file's provenance -- which tape, which revision, whether it is byte-identical -- is in src/pymdl/mdl/MANIFEST.tsv, derived by content matching against the archive and checked by the test suite.

Three things are not here. The archive itself, some two hundred megabytes of MIT tapes from 1971 to 1987, lives outside the repository and is needed only by the tools that measure against it (chapter 3). Zork, which was the project's first goal and is the reason MDL is remembered, is a program pymdl runs and lives in its own repository. And apsim, the PDP-10 simulator that runs the real MDL 55 for measurement, is a separate project.

1.3 Compiled code, and this machine's code for it

On the original systems, much of MDL ran compiled: the compiler itself, the library routines, the debugging tools' assembly parts. Compiled MDL is PDP-10 machine code, and the tapes hold it in three forms -- NBIN and FBIN files, and the pure-code database described in chapter 30.

pymdl supplies Python implementations of routines that MDL programs originally called as compiled or assembled code. These implementations are called pymacs, or Python peers. They cover such facilities as library database primitives, clock routines, and compiler helpers. When a peer is registered for a compiled routine, the interpreter can call it in place of the PDP-10 code. The project's tests compare their observable behavior.

The PDP-10 emulator executes compiled code that has no selected peer, including code produced by MIT's compiler. Setting PYMDL_NO_PDP10 disables that execution path. Interpreted MDL, native Python code, and registered peers can still run; an unpeered machine-code call raises an error. Chapter 36 explains the boundary and the compiler configurations tested without the emulator.

1.4 How pymdl knows it is right

The manuals provide the language specification. Their worked examples, and the executable examples in this book, are checked against pymdl. But manuals are silent in places and wrong in a few, and for those the reference is the machine. MDL 55 itself runs under apsim, and the project asks it questions, form by form, whenever the text does not settle a point: what REMOVE does to an atom's identity, what INCHAN is while a file loads, what an empty <AND> compiles to. Each answer becomes a test, and each test is quoted in this book where it applies, in a box like this one:

Measured, MDL 55. A bare <LENGTH .OBLIST> at the listener answers 4 on the release-55 image: (INITIAL ROOT PP!-PACKAGE EDIT!-PACKAGE). The manual's Appendix 5 gives the initial path as (INITIAL ROOT); the MDL as distributed has the pretty-printer and the editor loaded, and so does pymdl.

The compiler comparison described in chapter 34 uses 640 test shapes. Of these, 635 produce the same machine words under pymdl and MDL 55; the chapter accounts for the five exceptions. This is evidence about that corpus and those compiler configurations, rather than a guarantee for every MDL program.

1.5 This book and the two manuals

This book brings the contents of the two earlier books into a single pymdl manual. Editing should make that material clearer and more coherent, without abridging it or replacing explanations with references elsewhere. The original texts remain available as historical sources; appendix G credits their authors and records the provenance.

Historical behavior and pymdl behavior are distinguished where they differ. Three kinds of note provide supporting detail:

  • Measured -- an observation, identified by release or measurement tool and accompanied by its evidence. A measurement on MDL 55 uses apsim; a pymdl benchmark measures the host implementation.
  • pymdl's own -- a facility or a default the era never had.
  • Found along the way -- something true of MDL that the manuals do not say, discovered while making pymdl agree with the machine.

Each fenced mdl block runs in one fresh interpreter. Forms within the block share state, and values after are checked by the test suite. Type only the forms, omitting the arrows and results. Historical and interactive transcripts marked mdl-x are not run by this test:

<SET X (1 2 3)>               ⇒ (1 2 3)
<REST .X>                     ⇒ (2 3)
<DEFINE SQUARE (N) <* .N .N>> ⇒ SQUARE
<SQUARE 12>                   ⇒ 144

Appendices A, B and C are generated from the code; tools/book_gen.py --check checks that they are current. Appendix E collects recorded differences and discoveries. Appendix G gives the references and provenance.