Computer history: NOS/VE System Command Language
NOS/VE’s System Command Language (SCL)
SCL —System Command Language– was the name given to the language used for control statements and what can be called scripts (or what MS-DOS calls “batch files”). This section discusses both ordinary control statements and SCL features used in script programming.
Command Syntax
NOS/VE attempted to provide easy-to-understand, verbose commands that could also be abbreviated for faster typing. Commands in NOS/VE were given names consisting of English words strung together by underscores, with the first word being a verb. For example, the command to list file names (DIR in MS-DOS) was DISPLAY_CATALOG. Commands could be abbreviated by taking the first three letters of the first word in the command and appending the first letter of each subsequent word. For DISPLAY_CATALOG, the abbreviation was DISC. This was simply a convention adhered to by developers, not a rule built into the command processor.
By the way, a procedure or command could have multiple names. For many commands, the plural form was also available to the user, e.g. display_catalog_entries.
Command parameters could be positional or keyword=value. Each command had a specific set of parameters, declared in a specific order. Each parameter was declared with a name, but parameters could be specified either with param_name=value or with just positional values. Parameters could be declared as optional or required. Parameter names, like command names, took the form of English words strung together with underscores. The rules for abbreviating parameters were slightly different: abbreviations were made by taking the first letter of every word.
If you were specifying parameters positionally, you could skip over parameters (allowing them to default) by specifying commas on the command line.
A single parameter could have multiple values. For instance, you might want to rewind a bunch of files with one statement. This was accomplished with a list of values, surrounded by parentheses and separated by commas. E.g., (file1,file2)
Nearly all commands included a special Status parameter. When this was specified, it gave the name of a special structure which received the status of the command upon completion. If a command was completed successfully, it returned a status of Normal. A command would not abort if you specified Status=variable, because it was assumed you’d check the value of the variable. If you did not specify Status=variable and the command returned an abnormal status, a condition handler would be invoked, typically aborting the job.
Commands could be continued onto a subsequent line by placing “..” at the end of a line. Note that this was not a three-dot ellipsis, but rather the two-dot range operator inspired by Pascal. Comments could be included in control statements by surrounding them with double quotes. The lack of a closing quote meant that the rest of the line was a comment.
Names of commands and other entities were not case-sensitive. Identifiers could contain letters, digits, _, #, @, and $ (which was reserved for system-defined identifiers).
Here’s an example. Capitalisation is used here to illustrate how entities could be abbreviated. The capitalisation of commands was not required.
DISplay_Catalog “abbrev. DISC” Catalog=<catalog name> “abbrev. C, default $CATALOG” Display_Option=option “abbrev. DO, default ID” Output=file “abbrev. O, default $OUTPUT” Status=status var “special case; no abbrev.
The following commands are all equivalent:
DISC “Use all defaults” DISC ,,ID “Commas skip over C parameter” DISC $CATALOG ID “Must get right order when using positional” DISC DO=id “Case not important” DISC DISPLAY_OPTION=Id C=$CATALOG DISC O=$OUTPUT
Scripting Language
The programming aspects of SCL were reasonably conventional by modern standards. There were variables, operators, control structures, and system- and user-supplied procedures. But the mindset was strict type-checking. Don’t think UNIX sh or Perl-think Pascal.
Expressions
The following types were available in SCL:
Boolean | TRUE or FALSE |
Integer | |
Name | e.g., file names |
Status | special structure |
String | Length range 0-256 characters, dynamic length. Constants delimited by ‘ (apostrophe). |
Variables
Variables could be created implicitly by assignment (at least in most cases), or explicitly by the CREate_Variable command. For instance:
loop_count = 3
was equivalent to:
CREate_Variable loop_count kind=integer value=3
Single-dimension arrays were available. Note the Pascal-like array bounds syntax:
CREV picniclist kind=string dimension=1..20
Operators
The following operators were available:
Integer | + – * / ** |
Relational | > < >= <= = <> |
Boolean | OR XOR AND NOT |
String | // (concatenation) $SUBSTR(string,begpos,length) |
Built-in Functions
$CLOCK | microsecond real-time clock |
$DATE | date in your choice of formats (with ISO date format, no year 200 problem!) |
$TIME | time in your choice of formats |
$CATALOG | current working catalog |
$JOB | job attributes |
$PROGRAM | program/job attributes |
$CHAR | integer to ASCII string |
$ORD | ASCII char to integer |
$STRING | SCL name to string |
$STRREP | integer or boolean to string |
$VNAME | string to variable name |
Statements
Assignment
This was simply
variable = value
I’m surprised they didn’t use Pascal’s :=
Control Structures
There was no GOTO, but optional labels on some constructs allowed for flexible control of flow. In most statements, labels simply provided documentation, but with EXIT, you could break out of multiple nested loops with the use of a label.
I feel that SCL’s control flow structures were better than that of any other language.
IF boolean_exp THEN statements [ELSEIF boolean_exp THEN statements] [ELSE statements] IFEND [label:] WHILE boolean_exp DO statements WHILEND [label] [label:] REPEAT statements UNTIL boolean_exp [label:] FOR variable = initial TO final [BY step] DO statements FOREND [label] [label:] LOOP statements LOOPEND [label:] BLOCK statements BLOCKEND [label]
Interruption of Control Flow
These statements served the purpose of a break and continue in C, and were especially useful for BLOCK and LOOP. The only thing I don’t like about them is the optional Perl-like (or Ada-like) WHEN clause at the end of the statements.
CYCLE [label] [WHEN boolean_exp] Jumps up for another loop EXIT [label] [WHEN boolean_exp] Exits the loop EXIT_PROC [WITH status_exp] [WHEN boolean_exp] Exits current procedure
Condition Handling
These statements were like BASIC’s ON ERROR and RESUME. A condition handler was a block of code that looked like:
WHEN condition_name(s) DO statements WHENEND
Within a WHEN block, you could return to the main line code with the following. RETRY meant to return to the statement that caused the fault:
CONTINUE [RETRY] [WHEN boolean_exp]
A sample SCL Procedure
I don’t have the full specifications for SCL procedure usage, but this should give you a flavour. Procedure names as well as arguments could have multiple names, called aliases. This procedure displays the prime numbers between two given numbers. Its names are find_primes (or FINd_Primes, following the syntactic convention in this document) and finp. Note that in order to conform to the convention that a command can be abbreviated by the first three letters of the first word plus the first letter of each subsequent word, you had to explicitly declare both names.
PROC find_primes, finp { “These aliases happen to follow convention.” from, f : INTEGER = 1 “from and f are synonyms and default to 1.” to, t : INTEGER = $REQUIRED “This parameter is required; no default.” status) “Special parameter type is always status.”
IF $VALUE(from) < 2 THEN myfrom = 2 ELSE myfrom = $VALUE(from) IFEND
CREV primes DIMENSION=myfrom/2..$VALUE(to)/2 .. KIND=BOOLEAN VALUE=TRUE
CREV (try,possible_prime) “Note use of list”
divlp: FOR divisor = 3 TO $VALUE(to) EXIT divlp WHEN divisor**2 > $VALUE(to)
try = divisor * divisor
IF try < myfrom THEN
try = try + ((myfrom-try)/(2*divisor))*(2*divisor)
IF try < myfrom THEN
try = try + 2*divisor
IFEND
IFEND
WHILE try <= $VALUE(to) DO
primes(try/2) = FALSE try = try + 2*divisor
WHILEND
FOREND divlp FOR possible_prime = myfrom/2 TO $VALUE(to)/2 DO
IF primes(possible_prime) AND .. possible_prime*2+1<=$VALUE(to) THEN
display_value possible_prime*2+1
IFEND
FOREND
PROCEND find_primes
Prolog and Epilog Files
Prolog and Epilog files were files of SCL commands that were executed at the beginning and end of a job. There was a stack of prologs and epilogs: at the inner ring the user-specific ones, then the job_class ones and last not but least the system prologs/epilogs. The latter was used for instance for accounting purposes.
At TNO we made use of the FTP job_class specific prolog and epilogs. When a plot file was moved to the CYBER, automatically a job was generated to process the interpretable plot commands. (full story in Dutch)
Here’s a sample of prologs and epilogs. It was apparently used for a shared account.
“Set command list (like MS-DOS path)” SETCL delete=all add=($system, $user, .systems.util, $user.bin.stulib )
“Set current directory to user’s home” SET_Working_Catalog $user set_message_mode full
if $job(mode) = ‘INTERACTIVE’ then
set_program_attributes add_library=.systems.terminal_definitions set_terminal_attributes abort_line_character=$char(15)
set_terminal_attributes .. backspace_character=$char(8) cancel_line_character=$char(24) .. echoplex=true end_of_information=$char(3) .. hold_page=false output_flow_control=true set_terminal_attributes .. network_control_character=’%’ parity=even .. type_ahead=true prompt_string=” .. terminate_break_character=$char(20)
create_variable stemp# string accept_line stemp# input p=’Terminal model? ‘
setta terminal_model=$name(stemp#)
if (stemp# = ‘vt100’) or (stemp# = ‘vt200’) then
setta terminal_class=x364
ifend
“Get the user’s initials. If they don’t match one in a list, log the user off, else change catalog to one matching the initials.”
accept_line stemp# input p=’Your initials? ‘
if ((stemp# <> ‘ccb’) and .. (stemp# <> ‘clh’) and .. (stemp# <> ‘dwb’) and .. (stemp# <> ‘msb’) and .. (stemp# <> ‘pvp’)) then
logout
ifend
crev stemp2 string stemp2 = $string($catalog())//’.’//stemp#
setwc $fname(stemp2)
disv (‘Catalog changed to ‘//$string($catalog))
ifend
Multi-tasking
The TASK .. ENDTASK command sequence started of either an asynchronous or a synchronous task.
Example:
TASK Task_name=”backup1″
BACPF I=backup_commands_1 L=output_backup_1
TASKEND
TASK Task_name=”backup2″
BACPF I=backup_commands_2 L=output_backup_1
TASKEND
In the same way JOB..JOBEND could launch a separate job from within a command sequence or SCL procedure.