Table of Contents

Introduction

Much of the Space Shuttle's flight software was written in the high-level language called HAL/S.  This language was not ultimately used for much else, and thus is not well-known today.  Moreover, while several HAL/S compilers were created back in the day, none are presently available in a useful form, as far as we know.  Therefore, in order to compile Space Shuttle flight software, a "modern" compiler for HAL/S is currently being developed by the Virtual AGC Project.

However, the lack of public awareness of HAL/S is not reflected merely in the unavailability of development tools for it, but also in a general lack of awareness of the language and of any means to acquire proficiency in it.

For that reason, as well as to facilitate debugging and development of the modern HAL/S compiler, the modern compiler is configured in such a way as to allow running it interactively as an interpreter that executes each HAL/S statement individually as it is input by the user.  Moreover, it provides means to examine the effect of that code on the values of variables, as well as to examine the compiled code.  This page is a user manual for the modern HAL/S compiler when run in this interpretive mode.

Readings

Before working with the HAL/S interpreter too much, you might want to read a few chapters in the excellent introductory book Programming in HAL/S and/or the more formal HAL/S Programmers' Guide.

Installation of the Interpreter (Linux, Windows, Mac)

Aside:  I'm going to be mentioning "colorization" fairly frequently for a while.  What is it?  Well, there can be a lot of screen clutter involved when you use the HAL/S interpreter, making it more confusing to use than it needs to be.  But the interpreter can colorize inputs and output differently, making it much easier to distinguish visually between what you input and what it outputs.  I personally consider this colorization to be extremely useful — enough so that I bothered writing the code to do it in the first place  — but you may not care about it at all.  It's your call whether or not to take my emphasis on colorization seriously.  You might want to briefly skim through the Hello World section below and try to imagine what all of the samples would like if they were simply black.

  1. Install Python 3 if it's not already installed on your computer.
  2. Install (using pip or pip3, whichever is appropriate for Python 3 on your system) the following Python modules, if not already installed.  Unfortunately this is not necessarily a complete list, depending on your system type, but it's in the right ballpark:
  3. Download the installation-file yaHAL-S-FC.zip.
  4. Unzip it someplace on your local drive.
  5. You may now proceed to the next section.  But if when you actually try out the interpreter you keep getting unexplained compiler errors, then come back here to step 6, because you may need to rebuild the parser program from source.  Otherwise you're good to go.
  6. If you experience problems with the provided parser program, you rebuild it thusly.  First cd into the yaHAL-S-FC folder from a command line.  Then:

Just to be clear, the HAL/S interpreter is very much a work in progress.  You're getting it as-is.  The HAL/S interpreter has been used extensively only on Linux Mint 21 with the gnureadline module installed in Python 3.  I have much less confidence in any other configuration.  I've done just enough with the interpreter in the now-ancient Windows 7 and Mac OS 10.7.5 merely to offer a very few tentative opinions about how the interpreter may or may not work on Windows or Mac OS in general.  If they don't work yet ... well then, they don't work yet!

Invocation of the Interpreter

Note that while using the HAL/S interactive interpreter, and invoking it, you'll always be working from a command line (otherwise known as a console). You'll also be using the folder you got by unzipping the installation file yaHAL-S-FC.zip (see prior section) as your working directory.  So the first thing to do is to 'cd' to that folder.

How to invoke the HAL/S interpreter depends on your operating system and system configuration:

./yaHAL-S-FC.py --interactive --colorize --no-library               # in Linux or Mac OS, or probably any other *NIX type system

or

yaHAL-S-FC.py --interactive --no-library                            # in Windows (or any other system), not having an ANSI-compatible command line.
or
yaHAL-S-FC.py --interactive --colorize --no-wrapper --no-library    # in Windows, with an ANSI-compatible command line (such as Cmder).
If after this you see some garbage printed on the screen or you see nothing in color, here are some trouble-shooting steps:

What NOT to do in the Interpreter (and the Modern Compiler)

If you read the contemporary material I've recommended — Programming in HAL/S and/or the HAL/S Programmers' Guide — you will notice that the symbol ¬ is the operator for logical negation, used in contexts like "IF X ¬= Y THEN ..." or "MYBOOLEAN = ¬YOURBOOLEAN".  You'll also notice that you probably have no clue in the world as to how to enter this character into your HAL/S source code.

If you dig enough in the original HAL/S documentation, part of the rationale for using his symbol was that it was included in both the EBCDIC character set (as used by the IBM computers on which the original HAL/S compiler ran) and the ASCII character set (universally used today, and I guess that back in the early 1970's they could see it coming even then).  Well, both of those notions are wrong ... at least in ASCII as it is standardized today.  It is in some but not all extensions of 7-bit ASCII to 8 bits, and in some but not all variants of EBCDIC.  In fact, there is no way to use this symbol today, confining yourself to byte-sized character sets, that works on everybody's computer.  And not even on every (or most) Americans' computers.  Perhaps some day.  But as for today, what a mess!

For reasons which I think are good, but would probably bore you, the modern compiler/interpreter accepts any of the following as a logical-NOT operator:

You can use any of these that you can figure out how to input into your computer.  Whichever you use of ¬ ^ ~ is transparently converted internally by the compiler into ~.  My recommendation is to use ~ or to use NOT (which is part of the original HAL/S specification and doesn't need any rationalization of mine).

Similar comments apply to the cent symbol (¢).  Fortunately, this symbol is only rarely used in HAL/S source code, and only within certain quoted character strings, so it doesn't cause as much of a problem.  It is a kind of escape-code for embedding a character not in the HAL/S character set into a string literal if the string is going to be passed to non-HAL/S code for subsequent processing.  The modern compiler/interpreter will accept either:

Internally, either of these is converted transparently to the back-tick (`), and my recommendation is to just use ` if you find you need this feature of HAL/S.

Hello, World!

In this section, we take a very brief tour of the interpreter.

Upon successfully starting the interpreter, you should see a prompt something like the following:

Input HAL/S or else interpreter commands. Use `HELP for more info.
HAL/S >

This is an invitation for you to input a HAL/S "statement", which we'll temporarily pretend is any line ending with a semicolon as the final character.  When the --colorize command-line switch has been used, whatever you input is in black, whereas whatever the interpreter outputs is by default in magenta.  Otherwise, without the --colorize switch, everything is in black, or at least whatever default color your console's style setting dictate.  You can also change the coloring from magenta to something else.  (See the explanation of the interpreter's `colorize command.)

Input the following, making sure that the semicolon is the final character before you hit the Enter key:
HAL/S > WRITE(6) 'Hello, world!';
In response, you should see:
HAL/S > WRITE(6) 'Hello, world!';
        Hello, world!
Now, if before trying this you have skimmed a bit of the book Programming in HAL/S as I advised earlier, you may notice some peculiar things.  For one thing, HAL/S isn't supposed to have "global" lines of code like this that exist outside the context of a PROGRAM, FUNCTION, or PROCEDURE.  But the interpreter does allow HAL/S statements outside the context of PROGRAMs and subroutines, and that's perhaps the most-significant of the conveniences the interpreter allows you vs a compiler.

For another, in HAL/S source code can generally appear in a free-form fashion, in which most white-space is ignored, multiple statements can appear on the same line, individual statements can be broken across multiple lines, and so forth ... except that column 1 has a special purpose and you can't just put anything you like there.  In fact, the HAL/S documentation tells us that the only things that can appear in column 1 are a blank space or else one of the letters 'C', 'D', 'E', 'M', or 'S'.  So how come we were able to put the 'W' in WRITE(6) there?

Well, that's because it turns out that when you're working interactively, dedicating column 1 as having some special but very-rarely used interpretation is very, very inconvenient, because you simply keep forgetting about that when you're entering code.  The interpreter therefore has two modes of operation:  "strict mode", in which the special purpose for column is just as the documentation describes; and what we might call "loose mode".  Loose mode is the default for the interpreter, and in it the interpreter simply automatically sticks a blank space at the front of every line you input, leaving you free to start inputting HAL/S code at column 1 if you like.  But as a consequence, in loose mode you can't have full-line comments (C), compiler directives (D), or multi-line math input (E, M, S).  Instead, in loose mode, all input is truly free-form.

Loose mode has its own inconveniences, though.  If you try to cut-and-paste "real" HAL/S code into the interpreter, perhaps a sample from Programming in HAL/S, it will often fail, because such code will often contain things like full-line comments that aren't supported in loose mode.

A dilemma!  Fortunately, the interpreter allows you to switch back and forth from loose mode to strict mode relatively easily.  The interpreter command for enabling strict mode is
HAL/S > `strict
        STRICT on.
while you can return to loose mode with
HAL/S > `nostrict
        STRICT off.
Incidentally, this illustrates a general principle of the interpreter, namely that commands to the interpreter, as opposed to HAL/S source code lines, are always on a line by themselves and are always preceded by a back-tick character (`).  The back-tick isn't a legal character in HAL/S, at least not outside of quoted string literals, so that means we can always distinguish perfectly between interpreter commands and valid HAL/S source code.

HAL/S is a strictly-typed language, in which any variable must be explicitly declared before use.  Try the following HAL/S declaration:
HAL/S > DECLARE I INTEGER;
Notice that the interpreter simply accepts this silently.  Did it do actually anything or not?  Well, you can find out by asking the interpreter to show you what variables, constants, and other identifiers have been defined so far.  You do that like so:
HAL/S > `data
Scope 0, ROOT:
        I: {'integer': True}

If you want to understand this response in detail, you'll want to read what our page dedicated to the modern HAL/S compiler has to say about "scopes" and about the "data model".  But perhaps not just yet!  It's probably clear enough to you that this is saying I is an INTEGER, and that's good enough for right now.  And I'll say more about it below, anyway.

So now I has been declared, but it hasn't been assigned any value yet.  Let's try this:
HAL/S > I = 1; WRITE(6) 'I =', I;
        I = 1
HAL/S > `data
Scope 0, ROOT:
        I: {'integer': True, 'value': 1}

The HAL/S interpreter is really just a way of exploiting the HAL/S compiler.  In fact, when you enter a line of HAL/S source code into the interpreter, it basically just runs that line through the compiler and then executes the object code produced by the compiler. 
Aside:  Or more accurately, although you probably won't be too interested in it at this point, the compiler produces a sequence of instructions in what I call the PALMAT intermediate language.  To be clear, the original HAL/S compiler did something similar to this, except in an intermediate language it called HALMAT.  Unfortunately, not enough surviving documentation about HALMAT is presently available to meaningfully implement HALMAT, and that's part of the reason PALMAT was created.

The interpreter includes an emulator module that can and does execute the PALMAT code thus generated.  You can actually view the PALMAT code generated from the last source code that was input.  In the case of the HAL/S code just input in the last example, the generated PALMAT instructions look like the following:
HAL/S > `palmat
Scope 0, ROOT:
        0: {'number': '1'}
        1: {'storepop': (0, 'I')}
        2: {'string': 'I ='}
        3: {'fetch': (0, 'I')}
        4: {'write': '6'}

What do these PALMAT instructions do?  Instruction 0 pushes the number 1 onto the stack.  Instruction 1 pops that value from the stack and stores it in the variable I.  Instruction 2 pushes the string 'I =' onto the stack.  Instruction 3 pushes whatever value had been assigned to I onto the stack.  Instruction 4 takes everything currently on the stack, which in this case is 'I =' and 1, and prints it to the display.  If you're interested, you can read all about PALMAT on our HAL/S compiler page, though as I said, it's not too likely to be of interest to you unless you're interested in helping advance HAL/S compiler development to the next stage.
Here are a few other sample declarations:
HAL/S > DECLARE INTEGER, J INITIAL(5), K CONSTANT(10);
HAL/S > `data
Scope 0, ROOT:
        I: {'integer': True, 'value': 1}
        J: {'integer': True, 'initial': 5, 'value': 5}
        K: {'integer': True, 'constant': 10}

HAL/S doesn't allow re-declaration of variables:
HAL/S > DECLARE I SCALAR INITIAL(1.5);
        Already declared: I
But the interpreter is a little bit forgiving, and does allow us to remove identifiers we've already defined.  Subsequently we can redefine them once they've been removed:
HAL/S > `remove I
HAL/S > DECLARE I SCALAR INITIAL(1.5);
HAL/S > `data
Scope 0, ROOT:
        I: {'scalar': True, 'initial': 1.5, 'value': 1.5}
        J: {'integer': True, 'initial': 5, 'value': 5}
        K: {'integer': True, 'constant': 10}
By the way, you can get a list of all of the available interpreter commands as follows,
HAL/S > `help
        Note: Interpreter commands are case-insensitive, while
        HAL/S source code is case-sensitive.  Any input line
        beginning with a back-tick (`) is an interpreter command.
        The available interpreter commands are listed below:
        `HELP        Show this menu.
        `QUIT        Quit this interpreter program.
        .
        .
        .

I'll describe these commands in detail later, in the Tools section below.  Even though I've abbreviated this list, you can still see the most important interpreter command, `quit.

So far, the only example of arithmetic we've seen is a simple assignment, I=1, which isn't really very impressive.  Consider a quadratic equation with real coefficients:
A X2 + B X + C = 0.
The roots of the equation are given by the quadratic formula, which if we're lucky (i.e., if B2 ≥ 4 A C) will both be real, as opposed to complex. We can program this in HAL/S, say with A=1, B=3, C=2, as
HAL/S > DECLARE SCALAR, A, B, C, X1, X2;
HAL/S > A = 1;
HAL/S > B = 3;
HAL/S > C = 2;
HAL/S > X1 = (-B + (B**2 - 4 A C)**(1/2)) / (2 A);
HAL/S > X2 = (-B - (B**2 - 4 A C)**(1/2)) / (2 A);
HAL/S > WRITE(6) 'First root =', X1, ' and second root =', X2;
        First root = -1.00000000000000E+00  and second root = -2.00000000000000E+00
Or if we wanted to be really fancy, we could use HAL/S multi-line math to enter these formulas.  We'd have to turn on `strict mode first, so that the E and M in column 1 of the exponent lines and main lines were properly interpreted.  (No S lines for subscripts are needed in this example.)  We'd admittedly be daft to do so, because multi-line math so unwieldy for code entry, but it's certainly a possibility:
HAL/S > `strict
        STRICT on.
HAL/S > E              2        1/2
  ... > M X1 = (-B + (B - 4 A C)   ) / (2 A);
HAL/S > E              2        1/2
  ... > M X2 = (-B - (B - 4 A C)   ) / (2 A);
HAL/S >   WRITE(6) 'First root =', X1, ' and second root =', X2;
        First root = -1.00000000000000E+00  and second root = -2.00000000000000E+00
HAL/S > `nostrict
        STRICT off.
And of course, we could double-check the results by inserting the computed roots into the polynomial, and verifying that the polynomial evaluates to 0:
HAL/S > WRITE(6) A X1**2 + B X1 + C, A X2**2 + B X2 + C;
          0.0   0.0
All of which is fine if all we need is a rather simple-minded calculator.  But of course, we really wouldn't write the software in this manner.  We'd be more likely to create a FUNCTION to do the dirty work, and then we'd invoke that function (in this case called ROOTS):
HAL/S > `spool
        Now spooling input for later processing.
HAL/S > ROOTS: FUNCTION(A, B, C) VECTOR(2);
  ... >     DECLARE SCALAR, A, B, C;
  ... >     DECLARE V VECTOR(2);
  ... >     V$1 = (-B + (B**2 - 4 A C)**(1/2)) / (2 A);
  ... >     V$2 = (-B - (B**2 - 4 A C)**(1/2)) / (2 A);
  ... >     RETURN V;
  ... > CLOSE ROOTS;
  ... > `unspool
        Halting spooling of input. Processing already-spooled input ...
HAL/S >
WRITE(6) ROOTS(1, 3, 2);

         -1.00000000000000E+00  -2.00000000000000E+00
I won't bother to explain the HAL/S code here, because that's what books like Programming in HAL/S are for, and they do it better than I would.  And probably using less words than I would!  But as for interpreter commands ... `spool`unspool?  What are they?  Why are they there?

Well, the interpreter tries to compile each HAL/S statement as soon as you input it.  That works fine for basic statements that can fit on a single line, like declarations, assignments, or if/then statements, but it doesn't work out well at all for complex statements which are blocks of multiple statements spread out over multiple lines, such as a FUNCTION definition. 

There's more than one way to handle such multi-line constructs in the interpreter.  One simple workaround is simply to cram everything onto one very long line.  Or, use multiple lines, but with spaces at the end of every line, making sure that none of the lines end in a semicolon until the final line.  But while these are easy tricks if you're only working with 2-3 lines of code, they get pretty cumbersome and error-prone if you have lots of lines in something like a FUNCTION definition. 

An easier approach is usually to "spool" the input lines, via the interpreter's `spool command.  While spooling, the interpreter just saves everything (whether or not the lines end in semicolons or spaces) without attempting to process any of them, and then it processes all of the spooled input together when it sees the `unspool at the end.  In particular, if you were cut-and-pasting a bunch of "real" HAL/S code into the interpreter, you'd probably set `strict and `spool before pasting, and then `unspool and `nostrict after pasting.

By the way, when you create a definition of a HAL/S PROGRAM, FUNCTION, or PROCEDURE like this, the interpreter remembers the definitions, so you can use them over and over without re-inputting the definition ... until you `reset the workspace or leave the interpreter, of course.  (The next time you run the interpreter, it will remember nothing you've done in previous sessions except for the history of what you typed in at the prompts.  Though there are commands `write and `read to save your workspace and restore it later.  That's something you have to remember to do manually if you want it, and it doesn't happen automatically.)

Thus, now that ROOTS has been defined, we could follow up and do something like this if we wanted (though why you might want to I couldn't say):
HAL/S > `nostrict
HAL/S > DO FOR TEMPORARY I = 1 TO 100; WRITE(6) I, 2 I, I-1, ROOTS(I, 2 I, I-1); END;

         1  2  0   0.0  -2.00000000000000E+00
         2  4  1  -2.92893218813452E-01  -1.70710678118655E+00
         3  6  2  -4.22649730810374E-01  -1.57735026918963E+00
         4  8  3  -5.00000000000000E-01  -1.50000000000000E+00
        .
        .
        .
         100  200  99  -9.00000000000000E-01  -1.10000000000000E+00
Or for that matter, we could go whole-hog and really juice up ROOTS a lot more. In the version shown below, ROOTS allows you to repeatedly input the polynomial coefficients from the keyboard (until the illegal combination 0, 0, 0 is input), for which it computes not only real roots of the polynomial but also the complex roots, as well as checking the results by plugging the roots back into the polynomials:
HAL/S > `reset
HAL/S > `strict

        STRICT on.
HAL/S > `spool
        Now spooling input for later processing.
HAL/S > C/ This sample was adapted from p. 37 of "Programming in HAL/S".
  ... > C/ It computed the roots of 3 * X**2 + 4 * X - 10.  I pepped it up a bit,
  ... > C/ to have an input loop, to double-check the result by recomputing the
  ... > C/ polynomial using the roots, and to allow complex roots.
  ... >
  ... >   ROOTS: PROGRAM;
  ... >     DECLARE SCALAR, A, B, C, D, ROOT1, ROOT2;
  ... >     DO WHILE TRUE;
  ... >         WRITE(6) ;
  ... >         WRITE(6) 'Enter A, B, C (0,0,0 to quit):';
  ... >         READ(5) A, B, C;
  ... >         IF A = 0 AND B = 0 AND C = 0 THEN
  ... >             DO;
  ... >                 WRITE(6) 'Quitting ...';
  ... >                 EXIT;
  ... >             END;
  ... >         D = B**2 - 4 A C;
  ... >         IF D >= 0 THEN
  ... >             DO;
  ... >                 D = D**0.5;
  ... >                 ROOT1 = (-B + D) / (2 A);
  ... >                 ROOT2 = (-B - D) / (2 A);
  ... >                 WRITE(6) 'Real roots of', A, 'X**2 +', B, 'X +', C, 'are:',
  ... >                         ROOT1, ROOT2;
  ... >                 WRITE(6) 'Check:',
  ... >                         A ROOT1**2 + B ROOT1 + C,
  ... >                         A ROOT2**2 + B ROOT2 + C;
  ... >             END;
  ... >         ELSE
  ... >             DO;
  ... >                 TEMPORARY RE, IM, RE2, IM2;
  ... >                 D = (-D)**0.5;
  ... >                 RE = -B / (2 A);
  ... >                 IM = D / (2 A);
  ... >                 WRITE(6) 'Complex roots of', A, 'X**2 +', B, 'X +', C, 'are:',
  ... >                         RE, '+/-', IM, 'i';
  ... >                 RE2 = RE**2 - IM**2; /* Compute root squared RE2 +/- IM2 i. */
  ... >                 IM2 = 2 RE IM;
  ... >                 WRITE(6) 'Check:', A RE2 + B RE + C, '+/-', A IM2 + B IM, 'i';
  ... >             END;
  ... >     END;
  ... >   CLOSE ROOTS;
  ... > `unspool
        Halting spooling of input. Processing already-spooled input ...
HAL/S >
The `reset command at the beginning clears the interpreter's workspace.  I did that to make it easier to redefine ROOTS.  Notice that this version of ROOTS is now a HAL/S PROGRAM, rather than a FUNCTION, and we can't invoke it from HAL/S code any longer as we could have with a FUNCTION or PROCEDURE.  In point of fact, HAL/S provides no way to invoke a HAL/S PROGRAM from HAL/S code.  That's because a PROGRAM is run by the operating system's "monitor" rather than by other HAL/S code.  This allows multiple PROGRAMs to be run simultaneously in a time-sharing or distributed fashion.   However, we can still run a PROGRAM from the interpreter, by using the interpreter's `run command:
HAL/S > `run ROOTS
        Running as the primary thread.
       
        Enter A, B, C (0,0,0 to quit):
READ  > 1 2 3
        Complex roots of  1.00000000000000E+00 X**2 +  2.00000000000000E+00 X +  3.00000000000000E+00 are: -1.00000000000000E+00 +/-  1.41421356237310E+00 i
        Check: -4.44089209850063E-16 +/-  0.0 i
       
        Enter A, B, C (0,0,0 to quit):
READ  > 1 3 2
        Real roots of  1.00000000000000E+00 X**2 +  3.00000000000000E+00 X +  2.00000000000000E+00 are: -1.00000000000000E+00  -2.00000000000000E+00
        Check:  0.0   0.0
       
        Enter A, B, C (0,0,0 to quit):
READ  > 0, 0, 0
        Quitting ...
HAL/S >
Something you may wonder about from the source code of PROGRAM ROOTS is why the full-line comments begin with "C/" in columns 1 and 2, rather than "C ".  This has to do with a convention I will be using with the Shuttle flight software, if/when any if its source code becomes available for compilation.  Full-line comments originally present in the flight software will remain as-is, presumably beginning with "C ", but modern comments added by the Virtual AGC Project but not originally present will instead begin with "C/" to distinguish the modern from the original.  Of course, this is transparent to the compiler, which doesn't care one way or the other about whatever follows that initial "C".  Similarly, HAL/S allows inline comments wrapped between the delimiters "/*" and "*/", but by convention modern inline comments will use "/*/" and "*/".

Another point about the PROGRAM ROOTS that I think is worth mentioning is that the book Programming in HAL/S has been scoured for sample HAL/S code which can be used, possibly after extensive adaptation, for checking the HAL/S compiler and interpreter.  Those files have been included in the interpreter's installation file, where they have names like "NNN-XXXX.hal":  NNN represents the page number in the book, and XXXX is name (if any) that the book gives to the sample.  The ROOTS PROGRAM is actually just the contents of the sample file 037-ROOTS.hal.  But each of these source-code files from Programming in HAL/S, in principle, will eventually be available for cut-and-pasting into the interpreter.  (Just at the moment, since the compiler/interpreter is still under development, many of these samples won't yet work in an informative way.)

For example, consider the sample file 021-SIMPLE.hal, which repeatedly asks for values of a radius (R) and then computes πR2 until a negative radius is input:
HAL/S > `reset
HAL/S > `strict

        STRICT on.
HAL/S > `spool
        Now spooling input for later processing.
HAL/S > C/ This sample basically comes from PDF p. 21 of "Programming in HAL/S",
  ... > C/ but I've knocked it up a notch.  Bam!
  ... >
  ... >   SIMPLE: PROGRAM;
  ... > C CODE IN THIS TYPEFACE IS
  ... > C HAL/S SOURCE
  ... >           DECLARE PI CONSTANT (3.14159266);
  ... >           DECLARE R SCALAR;
  ... >           WRITE(6) 'Input values of R to compute PI R**2, or else -1 to quit.';
  ... >           DO WHILE TRUE;
  ... >                   READ(5) R;
  ... >                   IF R < 0 THEN EXIT;
  ... >                   WRITE(6) 'R =', R, 'and PI R**2 =', PI R**2;
  ... >           END;
  ... >   CLOSE SIMPLE;
  ... > `unspool
        Halting spooling of input. Processing already-spooled input ...
HAL/S > `run SIMPLE
        Running as the primary thread.
        Input values of R to compute PI R**2, or else -1 to quit.

READ  > 0
        R =  0.0 and PI R**2 =  0.0
READ  > 1 2 3 4
        R =  1.00000000000000E+00 and PI R**2 =  3.14159266000000E+00
        R =  2.00000000000000E+00 and PI R**2 =  1.25663706400000E+01
        R =  3.00000000000000E+00 and PI R**2 =  2.82743339400000E+01
        R =  4.00000000000000E+00 and PI R**2 =  5.02654825600000E+01
READ  > 5, 6 ,7 , 8,9
        R =  5.00000000000000E+00 and PI R**2 =  7.85398165000000E+01
        R =  6.00000000000000E+00 and PI R**2 =  1.13097335760000E+02
        R =  7.00000000000000E+00 and PI R**2 =  1.53938040340000E+02
        R =  8.00000000000000E+00 and PI R**2 =  2.01061930240000E+02
        R =  9.00000000000000E+00 and PI R**2 =  2.54469005460000E+02

READ  > -1
HAL/S >
This is perhaps a good time to briefly point out some of the underpinnings of the PALMAT intermediate language that are not shared (presumably) by the original HAL/S compiler or its own intermediate language (HALMAT).

You'll recall that when you interrogate the interpreter to find out the current values of all defined identifiers (like variables, constants, or symbolic labels), some aspects of what the interpreter displays for you aren't immediately understandable without additional explanations.  For example, let's use the interrogation commands (`data and `palmat) I've already showed you to see what the code sample we just used (PROGRAM SIMPLE) did to the definitions in our workspace:
HAL/S > `data
Scope 0, ROOT:
        l_SIMPLE: {'program': True, 'scope': 1}
HAL/S > `palmat
Scope 0, ROOT:
        (No generated code)
HAL/S > 

Not terribly informative!  Where is PI?  Where is R?  What is "l_SIMPLE"?  Where are all of the PALMAT instructions?

Well, let's take care of "l_SIMPLE" first.  For technical reasons, the modern HAL/S compiler/interpreter sometimes has to "mangle" names of identifiers a bit, rather than using the identifiers as-is from the HAL/S source code, so as to give some indication as to what datatype the identifiers are associated with.  This is done by adding a short prefix to the name.  Sometimes the interpreter hides this mangling-prefix from you (for your convenience in reading the interpreter's messages), and sometimes it may fail to do so.  The prefix "l_" happens to be the mangling used for symbolic names associated with some types of positions in the instruction stream, including names of PROGRAMs, PROCEDUREs, and some FUNCTIONs.  Similarly, "b_" is the mangling prefix for BIT/BOOLEAN variables, "c_" is the mangling prefix for CHARACTER variables, and so on.  In our present example, in other words, l_SIMPLE refers to SIMPLE.  For the most part, you can just ignore these mangling prefixes as if they weren't there at all.

Now as far as the missing variables PI and R are concerned, when the modern compiler compiles HAL/S source code into the PALMAT intermediate language, the PALMAT is partitioned into a hierarchy of what I call "scopes".  Each scope contains some of the identifiers and some of the PALMAT instructions; it's only the complete hierarchy of scopes that together comprise the complete set of identifiers and complete set of PALMAT instructions.  The interpreter commands we used above, `palmat and `data, only show us the very top-level scope:  i.e., what you might think of as the "global" scope.  All the global scope has in it is the definition of the SIMPLE PROGRAM, so that's all we see with these commands.

But there are extended forms of the `data and `palmat commands which show us all of the scopes:
HAL/S > `data *
Scope 0, ROOT:
        l_SIMPLE: {'program': True, 'scope': 1}
Scope 1, PROGRAM l_SIMPLE, parent scope 0:
        PI: {'constant': 3.14159266, 'scalar': True}
        R: {'scalar': True, 'value': -1.0}
        ue_2: {'label': [2, 0]}
        ur_2: {'label': [1, 3]}
Scope 2, DO WHILE, parent scope 1:
        ue_3: {'label': [3, 0]}
        ur_3: {'label': [2, 6]}
        ux_2: {'label': [2, 17]}
Scope 3, IF, parent scope 2:
        ux_3: {'label': [3, 6]}
HAL/S > `palmat *
Scope 0, ROOT:
        (No generated code)
Scope 1, PROGRAM l_SIMPLE, parent scope 0:
        0: {'string': 'Input values of R to compute PI R**2, or else -1 to quit.'}
        1: {'write': '6'}
        2: {'goto': [2, 0], 'symbolicLabel': (1, '^ue_2^')}
        3: {'noop': True, 'label': '^ur_2^'}
Scope 2, DO WHILE, parent scope 1:
        0: {'noop': True, 'label': '^ue_2^'}
        1: {'boolean': [(1, 1)]}
        2: {'iffalse': (2, '^ux_2^')}
        3: {'fetchp': (1, 'R')}
        4: {'read': '5'}
        5: {'goto': [3, 0], 'symbolicLabel': (2, '^ue_3^')}
        6: {'noop': True, 'label': '^ur_3^'}
        7: {'string': 'R ='}
        8: {'fetch': (1, 'R')}
        9: {'string': 'and PI R**2 ='}
        10: {'number': '2'}
        11: {'fetch': (1, 'R')}
        12: {'operator': '**'}
        13: {'fetch': (1, 'PI')}
        14: {'operator': ''}
        15: {'write': '6'}
        16: {'goto': [2, 0], 'symbolicLabel': (1, '^ue_2^')}
        17: {'noop': True, 'label': '^ux_2^'}
        18: {'goto': [1, 3], 'symbolicLabel': (1, '^ur_2^')}
Scope 3, IF, parent scope 2:
        0: {'noop': True, 'label': '^ue_3^'}
        1: {'number': '0'}
        2: {'fetch': (1, 'R')}
        3: {'operator': '<'}
        4: {'iffalse': [3, 6], 'symbolicLabel': (3, '^ux_3^')}
        5: {'goto': [2, 17], 'symbolicLabel': (2, '^ux_2^')}
        6: {'noop': True, 'label': '^ux_3^'}
        7: {'goto': [2, 6], 'symbolicLabel': (2, '^ur_3^')}
HAL/S >

Thus there are 4 scopes overall, numbered 0 through 3.  Aside from the global scope (0), a new scope is generated for each PROGRAM, FUNCTION, PROCEDURE, COMPOOL, DO, or IF.  So in our example, a new scope (1) is generated for the definition of PROGRAM SIMPLE, plus yet another scope (2) for the DO WHILE ... END loop within SIMPLE, and a final scope (3) for the IF THEN ... within the DO WHILE ... END loop.  As for the question of where PI and R are, they're in scope 1 (the SIMPLE PROGRAM) where they belong.

Because the PALMAT instructions are distributed throughout the hierarchy of scopes rather than being in a strict linear sequence as would be the case for (say) assembly-language instructions, the compiler's code-generator explicitly adds in program labels and PALMAT goto instructions it has constructed specifically for maneuvering back and forth between the scopes.  These are the identifiers like "ue_1" or "ur_2" cluttering the code.
Aside:  You may have noticed that the constructed program labels I just mentioned, such as "ue_1" sometimes appear in that form, and other times in what I call carat-quoted form as "^ue_1^".  Like the identifier mangling I told you about earlier, these carat-quotations are present for technical reasons, and the interpreter just isn't as good at always hiding them from you as perhaps it ought to be.  Like the identifiers' mangling-prefixes, you can just ignore carat-quoting.  An identifier represented as the carat-quoted "^ue_1^" is nothing more or less than the same identifier represented without carat-quoting just as "ue_1".
Finally, insofar as scope are concerned, as I mentioned they are in a hierarchical relationship that you can see as follows:
HAL/S > `scopes
Scope 0:
        type:       root
        parent:     None
        children:   [1]
Scope 1:
        type:       program
        parent:     0
        children:   [2]
Scope 2:
        type:       do while
        parent:     1
        children:   [3]
Scope 3:
        type:       if
        parent:     2
        children:   []
HAL/S >
For example, looking at scope 2 (the DO WHILE ... END scope), we see that its parent scope is 1 (the PROGRAM scope) and that scope 3 (the IF THEN ... scope) is its child.  The reason I call these "scopes" is that they determine the scopes of identifiers.  Any given scope (within certain limitations) can assess the identifiers in its ancestor (parent, granparent, ...) scopes, but not in its descendant scopes (children, grandchildren, ...), cousins, aunts and uncles, etc.  Thus scope 3 (IF THEN ...") and scope 2 (DO WHILE ... END) can access PI and R in scope 1 (PROGRAM), but scope 0 (global or "root") cannot.

By means of this "scope" system, any number of HAL/S PROGRAMs (or FUNCTIONs, PROCEDUREs, etc.) can coexist within the same PALMAT workspace.

Moreover, any number of PROGRAMs can be run simultaneously using it.  This involves cloning the scope hierarchy, with a separate copy of the hierarchy for each running program, so that changes to variables by one PROGRAM do not affect the other running PROGRAMs.  A slight proviso to that is that COMPOOL scopes are not cloned, but merely shared by all running PROGRAMs; moreover, the PALMAT instruction streams can all be shared as well, and don't need to be cloned, since HAL/S code is not self-modifying.   I.e., PALMAT instructions for each scope are generated at compile time, and never change afterward.

Tools Provided by the Interpreter

Earlier, I simply glossed over the various interpreter commands available, just discussing a few of them when I needed to.  Some of the tools I glossed over are pretty helpful, albeit sometimes more helpful for the compiler development I'm doing than (perhaps) for someone more interested in playing with HAL/S than in the underpinnings.  Still, here they are, in alphabetical order.

Note that the interpreter commands are case-insensitive, and thus can be entered in either upper-, lower-, or mixed-case.  However, HAL/S source code and HAL/S identifiers are case-sensitive.  When a HAL/S identifier is used in an interpreter command, the interpreter-command keyword can be in any case, but the HAL/S identifier must be in its correct case.

`BNF

(See also `lbnf and `noast, of which the latter is the default.)  The `bnf command enables display of a syntax tree in Backus-Naur Form (BNF) for HAL/S code submitted to the interpreter.  The "official" description of HAL/S used by the modern compiler is in LBNF rather than BNF, but BNF is more-commonly understood than LBNF and thus is provided as an alternative.  The full BNF definition of preprocessed HAL/S (derived from the "official" LBNF description) is the file "HAL-S BNF.pdf" provided in the interpreter's installation zipfile.

`CANCEL and `CANCEL *

Speaking for myself, it often happens that I enter part of a HAL/S source line or part of an interpreter command, or just a flat-out wrong interpreter command, and get stuck in a loop where I keep getting " ... >" prompts from the interpreter as it waits for me to put the final semicolon in place for my bogus input.  Well of course, you could do that, and suffer the indignity of an error message that your HAL/S code is bollixed.  Usually it's more-calming to use the `cancel command, which simply discards the previous line of input, or else the `cancel * command to discard all of the buffered input lines at once.

`CLONE

The `clone command is a variant of the `execute command (see below).  The `clone command executes (or re-executes as the case may be) the PALMAT instruction stream (beginning at scope 0 offset 0), but before doing so it clones the entire PALMAT scopes hierarchy.  Thus even if this (re-)execution changes the values of variables, those changes will be in the cloned workspace, and any changes to the workspace will revert after the (re-)execution completes.

`COLORIZE and `NOCOLORIZE

The `colorize C command changes the colorization from the default magenta to the color specified by C.  There are only 16 available choices for C, however, namely:  black, red, green, yellow, blue, magenta, cyan, white, gray, brightred, brightgreen, brightyellow, brightblue, brightmagenta, brightcyan, and brightwhite.  Thus `colorize magenta is the default.

The `nocolorize command disables colorization altogether.

`DATA

As already explained, the command `data shows the identifiers defined in scope 0 (i.e., globally), whereas `data * shows the identifiers of every scope.

Besides those, the command `data N, where N is some specific integer, shows just the identifiers defined in scope N.

`EXEC and `NOEXEC

By default, when you input HAL/S source code into the interpreter, the interpreter attempts to execute the PALMAT intermediate-language code it generates by compiling the HAL/S source.  If you use the --noexec command-line switch when invoking the interpreter, this is reversed:  The interpreter compiles the HAL/S source code you input, but does not attempt to immediately execute it.  Of course, for some types of input code, such as DECLARE statements or PROGRAM, FUNCTION, or PROCEDURE definitions, there's no code to immediately execute anyway because the definitions are just stored away for later, so the distinction doesn't matter.

The `exec and `noexec commands override those command-line switches, thus enabling and disabling automatic execution respectively.

Even if execution is delayed, the compiled code can still be executed afterward via the `execute command (see below) or `clone command (see above).

Delaying of execution isn't normally what a user of the interpreter wants, but it can be useful in several ways as a development tool.  For one thing, if there are implementation bugs in the emulator that cause the interpreter to abort back to a command line, delaying execution lets you see what the PALMAT instruction stream looked like prior to failure.  The `trace3 command (see below) is also useful in this regard.

`EXECUTE

The `execute command executes (or re-executes as the case may be) the PALMAT instruction stream (beginning at scope 0 offset 0).  See also `clone.

`GARBAGE

The `garbage command performs "garbage collection" on the PALMAT scopes hierarchy.  Of course, HAL/S does not support dynamic memory allocation, so it doesn't have memory-space "garbage" that needs cleaning in the conventional techno-babble sense of the word.  However, when you use the interpreter, you will often create scopes that are inaccessible to all future code, even though temporarily visible by interpreter commands such as `scopes, `data, `palmat, `execute, or `clone.  For example, if you input the HAL/S code

HAL/S > DO FOR TEMPORARY I = 1 TO 10; IF I > 5 THEN WRITE(6) I; END;
         6
         7
         8
         9
         10
HAL/S >

then new scopes will have been generated for the DO FOR ... END loop and for the IF loop inside of it.  But these scopes are inaccessible to any HAL/S code input after that.  Even if you input the identical source code line once again, the interpreter can't recognize that the object code it has already generated matches it, and hence will generate two new scopes instead.  To avoid this inexorable build-up of clutter, the interpreter automatically performs a "garbage collection" and removes unusable scopes before compiling any new HAL/S source-code you input.  (Admittedly, it's not perfect at doing so, and sometimes an unreachable scope will be buried among reachable scopes in a way that makes it inconvenient to remove; well, too bad!)  Garbage collection does not remove scopes containing definitions of PROGRAMs, FUNCTIONs, PROCEDUREs, COMPOOLs, or any of their descendant scopes.

What the `garbage command does is to perform this cleanup immediately, rather than waiting or you to enter new HAL/S source code.  Using the `scopes, `data, or `palmat commands you can then directly see what the effect of the cleanup was.  Though unless you want to examine the scopes in this way, there's no need ever to explicitly use the `garbage command.

`HELP

As has already been mentioned, `help displays all available interpreter commands, in a more-abbreviated form than the descriptions I'm giving you right now.

`LABELS and `NOLABELS

When using the `data command, it generally happens that the symbol table contains many more program labels than variables and constants.  If you're interested only in the declared variables and constants, these program labels in the symbol table may be a nuisance.  The program labels can be disabled my means of the `nolabels command, and can be reenabled by means of the `label command.

`LBNF

(See also `bnf and `noast, of which the latter is the default.)  The `lbnf command enables display of the syntax tree in Labeled Backus-Naur Form (LBNF) for HAL/S code submitted to the interpreter.  The description of the (preprocessed) HAL/S language used by the modern HAL/S compiler is in LBNF, so this format (as opposed to `bnf) is the most-useful way to understanding how the HAL/S code was parsed.  The full LBNF description is the file HAL-S.cf, included in the interpreter installation zipfile.

`NOAST

(See also `bnf and `lbnf.  The default, vs `bnf and `lbnf, is `noast.)  The `noast command disables display of the abstract syntax tree (AST) generated by the compiler when it parses preprocessed HAL/S source code.

`OPTIMIZE and `NOOPTIMIZE

Enables (default) or disables the compiler's crude optimizer.

`PALMAT

As already explained, the `palmat command displays the PALMAT instruction stream of scope 0 (the global context), while `palmat * displays the PALMAT instruction streams from all scopes.

Besides those, the command `palmat N, where N is some specific integer, shows just the PALMAT instruction stream from scope N.

`QUIT

The `quit command exits from the interpreter to the command line.  Incidentally, when it does so it saves a history file (yaHAL-S-FC.history), which will be automatically be reloaded the next time the interpreter runs.  This history file contains all of the inputs typed into the interpreter's prompt, and you can use the up/down arrow keys to browse through the list, as well as the right/left arrows and other keys to edit those lines and re-run them if you like

`READ

The `read Filename command reads a PALMAT file named Filename into the interpreter's workspace, first clearing out anything already in the workspace.

`REMOVE

As explained earlier, the command `remove identifier can be used to remove an identifier from scope 0 (the global context).  Besides that, `remove * will remove all identifiers from scope 0.

No mechanism is provided to remove identifiers from scopes other than 0, but see the `reset command.

`RESET

The `reset command removes all scopes from the PALMAT hierarchy other than scope 0 (the global context), and it removes all identifiers and all PALMAT instructions from scope 0.  In other words, it clears the entire workspace, as if you had exited from the interpreter and restarted it.

`REVIEW

This command is particularly useful when you've input a bunch of lines that haven't ended in semicolons, so that the interpreter hasn't compiled or executed them yet, but is simply waiting for you to finish up and put in a final semicolon.  The command shows all of the buffered input, in case you've gotten confused about what has been buffered and what has not.  For example, below I enter a bunch of nonsense (but not a complete HAL/S statement), review it, and then discard it.  Why?  Who knows?  Perhaps the cat was helping with code entry, and it turned out not to be all that helpful!

HAL/S > bm
  ... > bam
  ... > poozer
  ... > potato .
  ... > `review
        Review of spooled input:
         bm
         bam
         poozer
         potato .
  ... > `cancel *
HAL/S >

`RUN and `RUN *

The command `run ProgramName, as already mentioned, runs an already-input HAL/S PROGRAM (named ProgramName) lurking somewhere in the interpreter's workspace.  It does this in the "foreground", meaning that you can only run one program at a time using this method.

In contrast, `run ProgramName * runs the program as well, but it first clones the workspace and runs the program it as an independent thread or process, allowing you to run multiple programs or multiple copies of the same program at once.  Or at least it will do so, once I have time to get it working fully.

`SCOPES

The `scopes command lists all of the scopes in the PALMAT hierarchy.

`SPOOL and `UNSPOOL

As already mentioned, once spooling due to a `spool command, the interpreter just saves up whatever you're inputting without trying to compile or execute it, and only processes it once it sees an `unspool.  This is useful for inputting complex statements like PROGRAM definitions or for cut-and-pasting in HAL/S source code, and is often paired with `strict and `nostrict respectively when cutting-and-pasting.

`STATUS

The `status command displays all of the current interpreter settings, such as "strict" vs "nostrict", "exec" vs "noexec", etc.

`STRICT and `NOSTRICT

The `strict command enables the interpreter's "strict" mode, in which column 1 has the dedicated purpose described in the HAL/S documentation and can only contain one of the characters ' ', 'C', 'D', 'E', 'M', or 'S'.  Whereas `nostrict (the default) instead enables "loose" mode, in which column 1 has no particular special interpretation, and full-line comments, compiler directives, and multi-line math input lines are not allowed.

`TRACE1 and `NOTRACE1

The `trace1 command enables detailed tracing of the parser's analysis of preprocessed HAL/S source code.  This is useful for tracking down bugs in the LBNF description of the HAL/S language or sometimes in the action of the preprocessor.  The `notrace1 command disables that tracing.

`TRACE2 and `NOTRACE2

The `trace2 command enables tracing of the compiler's code-generator.  This is useful for tracking down bugs in the code generator, and particularly in implementing code-generation for HAL/S features not previously supported.  The `notrace2 command disables the tracing.

`TRACE3 and `NOTRACE3

The `trace3 command enables tracing of PALMAT instructions during emulation.  Specifically, it displays each PALMAT instruction as it's executed, along with the state of the computation stack at the time the instruction is executed.  The `notrace3 command disables the tracing.

`TRACE4 and `NOTRACE4

The `trace4 and `notrace4 commands are similar to `trace3 and `notrace3, except that the tracing is performed at compile-time rather than emulation-time.  It's needed because the compiler's code-generator actually uses the PALMAT-emulation module to determine which expressions are computable at compile-time, and to compute their values.  For example, in the HAL/S statement DECLARE X INITIAL(12 (34+32/7)), the INITIAL value is computed at compile-time rather than at run-time.  (In fact, all INITIAL and CONSTANT clauses are computed at compile-time, but all other expressions are candidates for compile-time computation as well.  Admittedly, the modern compiler isn't very good at this, since it works only with entire expressions rather than with the sub-expressions, but at least it gives it a shot!)

`WINE and `NOWINE

(Linux only.)  The `wine command enables use of the Windows version of the HAL/S parser, running under Linux's WINE subsystem.  The `nowine command reverses that instead re-enables use of the native Linux version of the HAL/S parser.  (This is useful only for debugging the Windows version of the parser without my having to actually run Windows to do it.  So most likely, this is a feature only useful for me.  By the way, it's monstrously slow!)

`WRITE

The command `write Filename writes out the entire interpreter workspace as a PALMAT file named Filename.




This page is available under the Creative Commons No Rights Reserved License
Last modified by Ronald Burkey on 2023-02-14

Virtual AGC is
              hosted by ibiblio.org