1962 - SNOBOL
The "StriNg Oriented and symBOlic Language". At first apparently called the Symbolic Expression Interpreter, 'SEXI', which then had to be changed to prevent the 1960-s era nerds from blushing when submitting their jobs. True story.
This was one of the first languages that could deal with strings and patterns natively. Indeed, the first version of SNOBOL had the string as its only datatype. Math was then done by parsing. The initial implementation was done on the IBM 7090. It seems to be long gone, at least, I couldn't find it. What I did find was the original paper describing it as well as a SNOBOL3 interpreter in Java, which can run on a modern computer.
The first SNOBOL had pretty much only pattern matching and basic arithmetic. SNOBOL 3 then added functions and changed the I/O, but otherwise seems to have remained backwards compatible. SNOBOL 4 changed the syntax, and from there it developed into Icon, which keeps the pattern matching but almost looks like a "normal" programming language otherwise.
The programs I have written use only functionality described in the original paper, so should work with the original SNOBOL with the exception of the I/O, which I did in SNOBOL3 style so that the Java interpreter could run them. From the paper, it seems that the difference is that SNOBOL1 uses pattern matching with a special SYS variable, whereas SNOBOL3 uses input and output variables:
- Input:
- 1
SYS .READ *DATA*
- 3
DATA = SYSPPT
- Output:
- 1
SYS .PRINT 'A STRING' AND VARIABLES
- 3
SYSPOT = 'A STRING' AND VARIABLES
Making these substitutions should get you 'real' SNOBOL 1. Of course, then you can't run it.
Task 1
START SYSPOT = 'SNOBOL WAS MADE IN 1962!'
Task 2
This shows math, string handling and flow control. SNOBOL3 has useful functions, like EQ to check equality; the original SNOBOL did not, so I haven't used them.
* READ N FROM INPUT
START SYSPOT = 'SIZE?'
SZ = SYSPPT
* INITIALIZE
CS = ''
ROW = '0'
* OUTPUT PREVIOUS ROW AND START NEXT ONE
ROW COL = '0'
SYSPOT = CS
CS = ''
COL SUCC = 'N'
EQ1 = COL
FAIL = 'CHKE'
EQ2 = '0' /(EQUAL)
CHKE FAIL = 'CHKR'
EQ2 = SZ - '1' /(EQUAL)
CHKR FAIL = 'SPACE'
EQ2 = ROW /(EQUAL)
* CONCATENATE THE RIGHT CHARACTER TO THE CURRENT LINE
SPACE CS = CS ' ' /(NEXT)
N CS = CS 'N' /(NEXT)
* FOR NUMBERS, SUBSTRING MATCH IS ENOUGH IF IT IS KNOWN A<=B
NEXT COL = COL + '1'
COL SZ /F(COL)
ROW = ROW + '1'
ROW SZ /F(ROW)S(FIN)
* THERE SEEMS TO BE NO EQUALITY CHECK, JUST SUBSTRING MATCHING
* OF COURSE, EQ1 == EQ2 IFF EQ1 CONTAINS EQ2 AND VICE VERSA
* THIS ALSO ILLUSTRATES INDIRECTION
EQUAL EQ1 EQ2 /F($FAIL)
EQ2 EQ1 /S($SUCC)F($FAIL)
* OUTPUT THE LAST LINE
FIN SYSPOT = CS
Task 3
First, the boring one. The only thing of note is the smaller-than check, showing exactly how string-oriented SNOBOL really was: (B - A) '-' means "does the result of B-A contain a minus?". SNOBOL3 can also do LE(B,A), but SNOBOL 1 could not (at least the paper doesn't mention it).
* READ A AND B
START SYSPOT = 'A?'
A = SYSPPT
SYSPOT = 'B?'
B = SYSPPT
* GCD LOOP
STEP '0' (A - B) /S(DONE)
(B - A) '-' /S(AB)F(BA)
AB A = A - B /(STEP)
BA B = B - A /(STEP)
DONE SYSPOT = 'GCD: ' A
Of course, when you have a language based entirely around strings and pattern matching, it would be a shame not to actually get to use the pattern matching and replacement. Thus, here is one of those unary-based GCDs, including routines for converting to and from unary.
* READ A AND B
START SYSPOT = 'A?'
A = SYSPPT
SYSPOT = 'B?'
B = SYSPPT
* CONVERT TO UNARY
UNA.IN = A
UNA.FIN = 'ADONE' /(UNA)
ADONE A = UNA.R
UNA.IN = B
UNA.FIN = 'BDONE' /(UNA)
BDONE B = UNA.R
* USE STRING MATCHING TO FIND GCD
STEP '' B /S(GDONE)
MATCH A B = /S(MATCH)
C = B
B = A
A = C /(STEP)
* CONVERT BACK TO DECIMAL
GDONE DEC.IN = A
DEC.FIN = 'DONE' /(DEC)
DONE SYSPOT = 'GCD: ' DEC.R /(FIN)
*****************************
* DECIMAL TO UNARY SUBROUTINE
UNA UNA.R =
UNA.DGT UNA.IN *.DGT/'1'* = /F($UNA.FIN)
.X = UNA.R
UNA.R =
UNA.MUL .X *.Y/'1'* = /F(UNA.ADD)
UNA.R = UNA.R '##########' /(UNA.MUL)
UNA.ADD '1' .DGT /S(UNA.1)
'2' .DGT /S(UNA.2)
'3' .DGT /S(UNA.3)
'4' .DGT /S(UNA.4)
'5' .DGT /S(UNA.5)
'6' .DGT /S(UNA.6)
'7' .DGT /S(UNA.7)
'8' .DGT /S(UNA.8)
'9' .DGT /S(UNA.9)
'0' .DGT /S(UNA.DGT)
UNA.1 UNA.R = UNA.R '#' /(UNA.DGT)
UNA.2 UNA.R = UNA.R '##' /(UNA.DGT)
UNA.3 UNA.R = UNA.R '###' /(UNA.DGT)
UNA.4 UNA.R = UNA.R '####' /(UNA.DGT)
UNA.5 UNA.R = UNA.R '#####' /(UNA.DGT)
UNA.6 UNA.R = UNA.R '######' /(UNA.DGT)
UNA.7 UNA.R = UNA.R '#######' /(UNA.DGT)
UNA.8 UNA.R = UNA.R '########' /(UNA.DGT)
UNA.9 UNA.R = UNA.R '#########' /(UNA.DGT)
*****************************
* UNARY TO DECIMAL SUBROUTINE
DEC DEC.R =
DEC.DGT '' DEC.IN /S($DEC.FIN)
.X = DEC.IN
DEC.IN =
DEC.DIV .X '##########' = /F(DEC.ADD)
DEC.IN = DEC.IN '#' /(DEC.DIV)
DEC.ADD '' .X /S(DEC.0)
'#' .X /S(DEC.1)
'##' .X /S(DEC.2)
'###' .X /S(DEC.3)
'####' .X /S(DEC.4)
'#####' .X /S(DEC.5)
'######' .X /S(DEC.6)
'#######' .X /S(DEC.7)
'########' .X /S(DEC.8)
'#########' .X /S(DEC.9)
DEC.0 DEC.R = '0' DEC.R /(DEC.DGT)
DEC.1 DEC.R = '1' DEC.R /(DEC.DGT)
DEC.2 DEC.R = '2' DEC.R /(DEC.DGT)
DEC.3 DEC.R = '3' DEC.R /(DEC.DGT)
DEC.4 DEC.R = '4' DEC.R /(DEC.DGT)
DEC.5 DEC.R = '5' DEC.R /(DEC.DGT)
DEC.6 DEC.R = '6' DEC.R /(DEC.DGT)
DEC.7 DEC.R = '7' DEC.R /(DEC.DGT)
DEC.8 DEC.R = '8' DEC.R /(DEC.DGT)
DEC.9 DEC.R = '9' DEC.R /(DEC.DGT)
FIN START