I’ve reached a huge milestone with BOOL, and it all happened through serendipity! I was creating canonical source examples and hand-compiling them into an off-the-cuff “language” to give me some sense of what the compiler would have to do to generate output. It’s probably the robot in me; when I invent off-the-cuff pseudo-languages to express an idea, I tend to make them very regular, very machine-readable.
This time that got me to thinking, “I bet it would be fairly easy to write a compiler for this stuff!” The figuring out part (the hard part) of the parsing is already done; the code I was generating (as a human compiler) just needed to be assembled.
Turns out I was right: it was very easy to write a compiler for “BOOL-M” (as I’m calling this new language; it stands for BOOL Macro). It took only two days for a working version, and two more to polish it. There’s maybe three more days formalizing the language and re-doing the hand-compiles.
It adds up to just over a week, but what a difference it makes!
A language like BOOL needs a run-time environment. Not just an environment, it needs a run-time engine. (Which unfortunately makes the acronym, RTE, somewhat ambiguous.) One difficulty designing an engine is the need for code to handle native objects.
If a language is going to add (or subtract or multiply or whatever) two number objects, somewhere there is language platform code that does that. A given language needs some kind of support for its native data types.
Generally speaking, a language wants a minimum of a numeric data type and a string data type and maybe some kind of array type. Many languages have integer and float number types, and many offer a date type (or types). Some consider characters a separate type, and others offer advanced types, such as regular expressions or files.
Whatever native data types a language designer chooses, the language has to provide code to implement all the operations supported on that data type.
Lacking a BOOL compiler, the only way to create BOOL code of any kind is by hand, which is extremely tedious. There are several native data types to support, and even though the support code is written in another language (such as Python), that code needs reasonable example BOOL code to work with.
Even developing code to execute an Action requires BOOL Action code to execute.
The BOOL-M assembler makes that possible for the first time in BOOL’s twenty-plus year development history. (Hip! Hip! Hooray!!)
To show you an example, let’s start with this simple bit of BOOL code:
@@muldiv >> *int a >> *int b >> *int c << *int r = *int t . set:t mul:a b;; . set:r div:t c;;
The hand-compiled BOOL-M code looks like this:
Action: CREATE muldiv argslist: INSERT muldiv Reference: CREATE a, int Reference: EMIT nametable: INSERT a, Reference, lastaddr() initlist: INSERT nametable(a) argslist: UPDATE muldiv Reference: CREATE b, int Reference: EMIT nametable: INSERT b, Reference, lastaddr() initlist: INSERT nametable(b) argslist: UPDATE muldiv Reference: CREATE c, int Reference: EMIT nametable: INSERT c, Reference, lastaddr() initlist: INSERT nametable(c) argslist: UPDATE muldiv Object: CREATE r, int Object: EMIT nametable: INSERT r, Object, lastaddr() initlist: INSERT nametable(r) exitlist: INSERT nametable(r) List: CREATE Object: CREATE t, int Object: EMIT nametable: INSERT t, Object, lastaddr() List: APPEND nametable(t) Message: CREATE set Message: TARGET nametable(t) Message: CREATE mul Message: TARGET nametable(a) Message: PARAM nametable(b) Message: EMIT Message: PARAM lastaddr() Message: EMIT List: APPEND lastaddr() Message: CREATE set Message: TARGET nametable(r) Message: CREATE div Message: TARGET nametable(t) Message: PARAM nametable(c) Message: EMIT Message: PARAM lastaddr() Message: EMIT List: APPEND lastaddr() List: EMIT argslist: UPDATE muldiv, lastaddr() Action: EMIT NameSpace: INSERT muldiv, Action, lastaddr()
It’s basically what the compiler encounters as it parses the code. Some objects it generates are nested because the source objects are nested. The language echos the BOOL principle that the first token (word, in this case) of an object (line, in this case) determines what that object is and who handles it.
The first word of each line (ignoring any leading spaces) identifies an Agent who handles the line. The Agents basically correspond to the BOOL meta-types and are responsible for generating the output objects of their type. A few other Agents handle specific tasks, usually updating compiler lists or tables.
The second word of the line is a verb that identifies what action the Agent should take. The remainder of the line depends on the Agent and the action it’s taking.
lastaddr() function returns the address of the last object emitted by the compiler. The
nametable(name) function returns the address of some known object given its name.
When fed to the BOOL-M assembler, the output looks like this:
@@muldiv: initlist: ('local', 0) ('local', 1) ('local', 2) ('local', 3) exitlist: ('local', 3) argslist: ['muldiv', 3, ('local', 9)] datalist:  None  None  None  None  None codelist:  ('REF', 'int', 0, None, ('name', 'a'))  ('REF', 'int', 1, None, ('name', 'b'))  ('REF', 'int', 2, None, ('name', 'c'))  ('OBJ', 'int', 3, None, ('name', 'r'))  ('OBJ', 'int', 4, None, ('name', 't'))  ('MSG', 'mul', ('local', 0), [('local', 1)])  ('MSG', 'set', ('local', 4), [('local', 5)])  ('MSG', 'div', ('local', 4), [('local', 2)])  ('MSG', 'set', ('local', 3), [('local', 7)])  ('LST', 3, ('local', 4), ('local', 6), ('local', 8)) nametable: a=('Reference', ('local', 0)) b=('Reference', ('local', 1)) c=('Reference', ('local', 2)) r=('Object', ('local', 3)) t=('Object', ('local', 4))
The key to the above is the
codelist, which shows the compiled
@muldiv Action’s BOOL objects (the entire thing comprises the Action itself).
So for the first time I have real BOOL code I can use to begin real work on a BOOL Run-Time Engine. That’s a pretty big deal to me!