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.
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.
Thus for many commands the plural form, e.g. display_catalog_entries
was also available to the user.
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. However 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 parameters 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 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 elipsis, 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. Capitalization is used here to illustrate how entities could be abbreviated and 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
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 typechecking. Don't think UNIX sh or Perl--think Pascal.
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 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
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 |
This was simply
variable = value
I'm surprised they didn't use Pascal's :=
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]
These statements served the purpose of 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
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 return to the statement that caused the fault:
CONTINUE [RETRY] [WHEN boolean_exp]
I don't have the full specifications for SCL procedure usage, but this should give you a flavor. 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 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 it concerned a plot file being 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
.. 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 TASKENDIn the same way JOB..JOBEND could launch a separate job from within a command sequence or SCL procedure.