Let's implement a simple program in SNOBOL on MTS to print today's date in different formats.

The problem

The problem is quite simple: take today's date and display it in ISO format (eg 2015-10-11) and a human readable format (eg Sunday, October 11, 2015). Further details and implementations in other languages can be found on Rosetta Code.

Getting today's date

There's a built in function in SNOBOL to return the date as an eight character string. Interestingly, the SNOBOL 4 Programming Language says this returns 'MM/DD/YY' but on MTS it returns 'MM-DD-YY'. Let's get this and store in a variable.

        NOW = DATE()

Breaking the date into components

We take the date and extract the month, day and year using SNOBOL's pattern matching facility and assign it to variables DAY, MONTH, and YEAR.

        PART = SPAN("0123456789")
        SEP  = "-"
        NOW (PART . MONTH) SEP (PART . DAY) SEP (PART . YEAR)

Y2K strikes again

The value returned from DATE() has a two digit year, so let's assume we are running this in the 21st century.

        CENTURY = 2000
        CYEAR = YEAR + CENTURY

Displaying in ISO format

So displaying the date in ISO format is now simply a case of concatenating the day, month and four digit year and then outputting it.

        ISO = CYEAR SEP MONTH SEP DAY
        OUTPUT = ISO

Day of the week

We now turn to the human readable form but there is a slight problem - we need to know which day of the week it is, eg Monday, and there is no facility in SNOBOL to calculate this. There may be a MTS external library we could call to get this, but instead we will use Gauss' algorithm to derive the day as a number from 0 (Sunday) to 6 (Saturday).

* GYEAR is the 4 digit year, unless Jan or Feb then subtract 2
* GMONTH is MONTH-2 modulus 12, Jan is 11, Feb is 12
        GT(MONTH, 2)               :S(G1)F(G2)
G1      GYEAR = CYEAR  
        GMONTH = MONTH - 2         :(GX)
G2      GYEAR = CYEAR - 1  
        EQ(MONTH, 1)               :S(G3)F(G4)
G3      GMONTH = 11                :(GX)  
G4      GMONTH = 12                :(GX)  
GX      WDAY = REMDR(DAY, 7)  
* Calculate the month term 
        MT = (2.6 * GMONTH) - 0.2
* Add the month term - the 0.00005 is needed due to lack of FP precision
        WDAY = WDAY + REMDR(CONVERT(MT + 0.00005, 'INTEGER'), 7)
        WDAY = WDAY + 5 * REMDR(REMDR(GYEAR, 4), 7)
        WDAY = WDAY + 4 * REMDR(REMDR(GYEAR, 100), 7)
        WDAY = WDAY + 6 * REMDR(REMDR(GYEAR, 400), 7)
        WDAY = REMDR(WDAY, 7)

Month and day names

We will need a way of translating a month and day number into a name, eg January or Monday. SNOBOL's arrays can be used for this. Note that the DAYS array is indexed from 0 to 6 instead of 1 to 7.

        MONTHS = ARRAY("12")
        MONTHS<1> = "January"
        MONTHS<2> = "February"
* ...
        MONTHS<11> = "November"
        MONTHS<12> = "December"


        DAYS = ARRAY("0:6")
        DAYS<0> = "Sunday"
        DAYS<1> = "Monday"
* ...
        DAYS<5> = "Friday"
        DAYS<6> = "Saturday"

Displaying in readable format

We now have all the components to display the date in readable format.

        READABLE = DAYS<WDAY> ", " MONTHS<MONTH> " " DAY ", " CYEAR
        OUTPUT = READABLE

Running the program

Here's what the output of the program looks like.

# $run *snobol4 5=date.sn
# Execution begins   21:09:05

 SNOBOL4 (VERSION 3.10, APRIL 1, 1973)
 (MTS IMPLEMENTATION MAY 1, 1975)

 0 SYNTACTIC ERROR(S) IN SOURCE PROGRAM


 2015-10-11
 Sunday, October 11, 2015


 NORMAL TERMINATION AT LEVEL  0
 LAST STATEMENT EXECUTED WAS   53


 SNOBOL4 STATISTICS SUMMARY
              38 MS. COMPILATION TIME
               1 MS. EXECUTION TIME
              41 STATEMENTS EXECUTED,       0 FAILED
              21 ARITHMETIC OPERATIONS PERFORMED
               1 PATTERN MATCHES PERFORMED
               0 REGENERATIONS OF DYNAMIC STORAGE
               0 READS PERFORMED
               2 WRITES PERFORMED
            0.02 MS. AVERAGE PER STATEMENT EXECUTED

# Execution terminated   21:09:05  T=0.045

Final thoughts on SNOBOL

Using SNOBOL feels close to modern scripting languages such as Perl, Python or Ruby. I really like the pattern matching facilities where you can do things like EXPR = TERM | *EXPR OP TERM which is much more powerful than regular expressions; I don't think there is any modern language that has this built in. The lack of control flow processing apart from GOTO is annoying; later versions of the language such as SPITBOL addressed this. I imagine that it was also rather slow when running on a mainframe, especially as it had to be compiled each time it was run.

I don't think SNOBOL is much in use today, but the maintainer of SPITBOL is still active.

Further information

Full source code for this program is on github.