SNOBOL4 for VAX OpenVMS 7.3

SNOBOL4 for VAX OpenVMS 7.3

My VAXstations now have SNOBOL4: the original Bell Labs SNOBOL4, the string-processing language with patterns as values, built from a 1997 public-domain C translation of its portable implementation, as a kit for OpenVMS VAX 7.3, with a handful of VMS-specific functions added: process, system and device information, logical names, wild-card file searches and RMS indexed files you can read and write from a SNOBOL4 program. The kit is a BACKUP saveset with a DCL installer and a DCL uninstaller; it needs nothing on the target but the tools every VMS has. The interpreter passes all the regression tests that come with it.

Claude Code assisted, which (as usual) is to say, did much of the hard work.

One correction first, because I made the mistake myself: this is not Macro SPITBOL. My notes said I had Dewar’s SPITBOL on the DECUS tapes. I do not: the directory named in the note has no SPITBOL, nobody publishes a VAX/VMS SPITBOL source that I could find, and what the DECUS tapes really hold is the Bell Labs SNOBOL4 in C (1997, 1994 and an earlier native VAX translation from TRW, 1982). The interpreter here is the genuine article, the language of the “green book” (Griswold, Poage and Polonsky), with the SPITBOL-compatible extras Phil Budne added.

Downloads, with SHA-256 sums in SHA256SUMS.TXT:

SNOBOL4 is in the public domain; the notice is quoted below.

What SNOBOL4 is

A SNOBOL4 program is a list of statements, each with an optional label, a subject, a pattern, a replacement and a goto on success or failure. Patterns are first-class values you build, store in variables and combine, and matching backtracks through alternatives. A word-counting program is a loop and one statement:

SCAN    LINE BREAK(&LCASE) SPAN(&LCASE) . W =     :F(READ)
        T<W> = T<W> + 1                           :(SCAN)

BREAK(&LCASE) skips to the next letter, SPAN(&LCASE) . W captures the run of letters into W, = deletes what matched from LINE, and the failure branch goes back for the next line. T is a table indexed by strings. Here is the keyword-in-context program of the picture above, doing the same with cursor positions (@A, @B):

SCAN    LINE POS(P) BREAK(LET) @A SPAN(LET) . W @B     :F(READ)

SNOBOL4 is where the idea of patterns as data comes from, in the work of David Farber, Ralph Griswold and Ivan Polonsky at Bell Labs in the 1960s; Icon (the sibling post, by Griswold at Arizona) kept strings and threw the patterns away for goal-directed evaluation.

What was built

CSNOBOL4, Phil Budne’s “C-MAINBOL”, “The Macro Implementation of SNOBOL4 in C”, version 0.99.4 of August 1997, is the original Bell Labs SIL (SNOBOL4 Implementation Language) version 3.11 of 1975, translated into C by a SNOBOL4 program. Its README is short on ceremony:

This material is in the public domain. You may use and copy this material freely. This privilege extends to modifications, although any modified version of this system given to a third party should clearly identify your modifications as well as the original source.

All my changes are marked as mine; the kit’s SOURCES.TXT lists them.

It builds natively on the VAX with Compaq C V6.4, in 55 modules, no MACRO, no MMS. What it needed (all eight changes are small and in the source kit):

  • The shipped VMS build files describe the 0.91 sources of 1994, not these: wrong file names, missing objects. A source list file and a 53-line DCL procedure replace them.
  • /NAMES=AS_IS, because SNOBOL4’s any, sin, delete, exit, link… collide with the C library once names are upper-cased.
  • Two library functions with the same names as the C library’s: the private getopt and the SIL procedure LINK.
  • The 1994 VAX C times() is gone: clock().
  • Two output bugs, both with the same cause (DEC C’s fwrite(p, 1, n, f) on a record file writes every byte as a record): the >file redirection helper and plain output into a DCL-made file (a batch log, SPAWN/OUTPUT=) – each line was printed as a column of single letters.
  • The -INCLUDE library directory is the logical name SNOLIB:.

Talking to VMS

SNOBOL4 on VMS cannot LOAD() a shared image, but the implementation has “Poor Man’s LOAD”: external functions linked into the interpreter. Ours are in vmscall.c:

GETJPI('USERNAME')   GETSYI('VERSION')   GETDVI('SYS$SYSDEVICE','FREEBLOCKS')
TRNLNM('SYS$NODE')   GETTIM()            VMSMSG(status)   FINDFIRST('SYS$LIBRARY:*.OLB')
RMSOPEN('BOOK.IDX', 12, 60)   RMSPUT   RMSGET   RMSDEL   RMSREWIND   RMSNEXT   RMSCLOSE

The RMS functions give a SNOBOL4 program a keyed, persistent database: an indexed file with fixed records and the first bytes as the primary key. A pipe to a DCL subprocess also works (INPUT(.X, 99, , '|SHOW TIME')), through the C library’s popen.

Installing

You need OpenVMS VAX 7.3, SYSTEM (or SYSPRV), and about 11,000 free blocks on the system disk while installing. No compiler.

  1. Put the saveset and the two .COM files into one directory on the VAX. Transfer modes matter: the .BCK in binary, the .COM files in ASCII.

  2. A binary transfer loses the saveset’s record attributes; the installer puts them back itself:

     $ SET FILE/ATTRIBUTE=(RFM:FIX,LRL:32256,MRS:32256,RAT:NONE) -
           SNOBOL4_VAX_20261012.BCK
    
  3. As SYSTEM:

     $ @SNOBOL4_INSTALL
    

    (12 seconds on the simulator, 40 on the VAXstation 4000/96, for 375 files.)

  4. Each user, at the prompt or in LOGIN.COM:

     $ @SYS$COMMON:[SYSLIB.SNOBOL]SNOBOL4_SETUP
     $ SNOBOL4 "-b" HELLO.SNO
     $ SNOBOL4 "-b" "-u" "BOOK.IDX" RMSBOOK.SNO "<COMMANDS.TXT" ">OUT.TXT"
    

DCL lower-cases an unquoted argument and SNOBOL4’s options are case-sensitive, so the options are quoted.

@SNOBOL4_UNINSTALL CHECK says what an uninstall would remove, and @SNOBOL4_UNINSTALL does it: only files the manifest lists and that are still as installed.

Examples (all with expected output and a test)

RUN_TESTS.COM runs 12 test cases from 10 programs and compares each with an expected output made on the Linux host by an independent Python implementation of the program’s specification (not by running the VAX program):

ProgramWhat it shows
WORDFREQtables, sorting, BREAK/SPAN
CALCrecursive descent over patterns, error reporting, truncating integer division
QUEENSrecursion and backtracking: 2, 10, 4, 40, 92 solutions for N = 4..8
KWIC@ cursor assignment, SORT of a two-column table (the picture at the top)
BF (+ HELLO.BF, REVERSE.BF)a Brainf*** interpreter (a TABLE as the tape, computed goto), with bracket matching
SYSINFOGETJPI/GETSYI/GETDVI/TRNLNM checked against DCL’s own lexical functions
RMSBOOK (two runs)an address book in an RMS indexed file; the second run finds the first run’s entries
FILESFINDFIRST/FINDNEXT, then each file read with ordinary SNOBOL4 input
VMSLIB_DEMO-INCLUDE of a library found through the logical name SNOLIB:
PIPESDCL subprocess output through INPUT

Results

Measured on a simulated MicroVAX 3900 (SIMH, OpenVMS VAX 7.3), except where the real machine is named:

  • Build, link included: 45 s for 55 modules (the largest, the 215 KB generated ISNOBOL4.C, 22 s). The recipe from the original sources to a tested kit takes 1 min 7 s in one batch job. Simulated-VAX times say little about a real VAX; they are ratios.
  • The shipped regression tests: 64 of 64 pass (three of the test files need 8-bit or CR data that the text bundle used to move the sources cannot carry; they are reported as not in the tree).
  • The 12 example tests: 12 of 12 as a fresh unprivileged user with the installed kit, 2.7 s.
  • The kit: 5,166 blocks (2,644,992 bytes); 375 files once installed (4,283 blocks).
  • QUEENS for N = 4..9 executes 459,992 statements, 165,845 of them failing, in 4.94 s of simulated CPU; the Linux host build executes exactly the same number.
  • The lifecycle test (install, refuse a second install, uninstall CHECK, uninstall that keeps a changed file and a user file, REPLACE, reinstall): 21 of 21 checks.
  • On my VAXstation 4000/96, installed system-wide on 12-OCT-2026: the 12 example tests passed, 9 s, and the 64 shipped tests passed, 30 s.
  • The 1982 native MACRO-32 SNOBOL4 by Wendell Turner of TRW (sno.exe, from DECUS VAX 84A), linked on a VAX/VMS of 1982-84, runs unmodified on VMS V7.3 and prints a factorial table: a different implementation of the same language, more than four decades apart.

What I did not check

  • Real hardware, mostly: only the VAXstation 4000/96 (above). Not the 4000/60 or any other VAX.
  • LOAD() of shared images: not available on VMS; external functions are linked in.
  • Macro SPITBOL, as said above: no VAX source exists in any collection I could find. CSNOBOL4 2.x (a much later version) I did not attempt.
  • Alpha and Itanium: nothing; this is a VAX build.

Rebuilding

REBUILD.md in the source kit has every command, in order, from snobol-0_99_4.tgz (hash given) to a tested kit with the time of each step. The VAX has no tar, so the tree goes over as one ASCII bundle made by a small Python 3 script and is unpacked by a 60-line C program; the whole rebuild is then one batch job.

How this was done

As with Emacs, Perl and Poplog: Claude sessions with written briefs, on a VAXstation and a simulator, with the rule that nothing counts until a test shows it, and the transcripts kept. Every number above comes from one of those transcripts.

Credits: David J. Farber, Ralph E. Griswold and Ivan P. Polonsky for SNOBOL4; Philip L. Budne for the C translation (CSNOBOL4) and its VMS build files; the DECUS library, which kept all of it; and Claude Code (Anthropic’s Claude models) for much of the building, debugging, testing and writing.