This page was written prior to the effort to port the original Intermetrics HAL/S compiler, HAL/S-FC, for modern usage. (See here.)
This page's use of the term "modern compiler" refers to the portion of
the HAL/S interpreter that functions as a stand-alone compiler (but has
now been obsoleted by HAL/S-FC). I apologize for this
confusion, but I don't quite see how to reduce the confusion other than
to warn you to watch out for it!
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.
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.
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.
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!
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:
If after this you see some garbage printed on the screen or you see nothing in color, here are some trouble-shooting steps:./yaHAL-S-FC.py --interactive --colorize --no-library # in Linux or Mac OS, or probably any other *NIX type system
yaHAL-S-FC.py --interactive --no-library # in Windows (or any other system), not having an ANSI-compatible command line.or
oryaHAL-S-FC.py --interactive --colorize --no-wrapper --no-library # in Windows, with an ANSI-compatible command line (such as Cmder).
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.
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:
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 HAL/S or else interpreter commands. Use `HELP for more info.
HAL/S >
HAL/S > WRITE(6) 'Hello, world!';In response, you should see:
HAL/S > WRITE(6) '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.
Hello, world!
HAL/S > `strictwhile you can return to loose mode with
STRICT on.
HAL/S > `nostrictIncidentally, 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.
STRICT off.
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 > `dataIf 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.
Scope 0, ROOT:
I: {'integer': True}
HAL/S > I = 1; WRITE(6) 'I =', I;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.
I = 1
HAL/S > `data
Scope 0, ROOT:
I: {'integer': True, 'value': 1}
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.Here are a few other sample declarations:
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 > `palmatWhat 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.
Scope 0, ROOT:
0: {'number': '1'}
1: {'storepop': (0, 'I')}
2: {'string': 'I ='}
3: {'fetch': (0, 'I')}
4: {'write': '6'}
HAL/S > DECLARE INTEGER, J INITIAL(5), K CONSTANT(10);HAL/S doesn't allow re-declaration of variables:
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 > DECLARE I SCALAR INITIAL(1.5);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:
Already declared: I
HAL/S > `remove IBy the way, you can get a list of all of the available interpreter commands as follows,
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}
HAL/S > `helpI'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.
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.
.
.
.
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;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 > 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
HAL/S > `strictAnd of course, we could double-check the results by inserting the computed roots into the polynomial, and verifying that the polynomial evaluates to 0:
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.
HAL/S > WRITE(6) A X1**2 + B X1 + C, A X2**2 + B X2 + C;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):
0.0 0.0
HAL/S > `spoolI 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?
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
HAL/S > `nostrictOr 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 > 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
HAL/S > `resetThe `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 > `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 >
HAL/S > `run ROOTSSomething 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 "*/".
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 >
HAL/S > `resetThis 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).
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 >
HAL/S > `dataNot terribly informative! Where is PI? Where is R? What is "l_SIMPLE"? Where are all of the PALMAT instructions?
Scope 0, ROOT:
l_SIMPLE: {'program': True, 'scope': 1}
HAL/S > `palmat
Scope 0, ROOT:
(No generated code)
HAL/S >
HAL/S > `data *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.
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 >
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 > `scopesFor 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.
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 >
(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.
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.
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.
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.
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.
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.
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;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.
6
7
8
9
10
HAL/S >
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.
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.
(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.
(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.
Enables (default) or disables the compiler's crude optimizer.
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.
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
The `read Filename command reads a PALMAT file
named Filename into the interpreter's workspace,
first clearing out anything already in the workspace.
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.
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.
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 >
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.
The `scopes command lists all of the scopes in the
PALMAT hierarchy.
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.
The `status command displays all of the current
interpreter settings, such as "strict" vs "nostrict", "exec" vs
"noexec", etc.
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.
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.
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.
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.
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!)
(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!)
The command `write Filename writes out the
entire interpreter workspace as a PALMAT file named Filename.