System Command Language (SCL)

SCL--System Command Language--was the name given to the language used for control statements and what I call 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.

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

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 typechecking. 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
$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 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 return to the statement that caused the fault:

CONTINUE [RETRY] [WHEN boolean_exp]

Sample SCL Procedure

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

Prolog and Epilog files were files of SCL commands that were executed at the beginning and end of a job. I believe there could be both system-wide prologs and epilogs, as well as user-specific ones. Here's a sample. 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