BOOL Python Binding

Again the retirement free time, the pleasure of Python, and perhaps it just being the right time, conspire to move the BOOL project forward into new territory. The Reference Implementation is coming along nicely on several fronts. The design of various objects has become solid enough to use in writing bits of code.

In particular, it’s possible to discuss how objects are represented in Python, and it’s possible to start thinking about the code that works with those objects. With the BOOL source definition becoming more stable, it’s also possible to start thinking about compilers. This post introduces these things; in some cases, in some detail.

For dessert, I’ll briefly mention a BOOL XML binding.

If you are familiar with s-expressions (you might know them from Lisp), you will recognize them in BOOL objects. [I loved Lisp, and BOOL owes a great deal to its influence.] The binary binding for BOOL uses arrays of 32-bit “cells” to represent objects. The Python binding  uses tuples.

There are actually three Python bindings: Level 1 simulates the binary binding, each member of the tuple is an integer (with values in the 32-bit space); Level 2 is a more human-readable form, but which still uses numeric values; Level 3 is the most readable form. (Just to complicate things, there is also: Level 2A, which uses actual numbers; and Level 2B, which uses global symbols. Likewise, a set of global symbols is defined for common Level 3 strings.)

All BOOL code exists as part of some Action. Specifically, all executable objects are members of some BOOL List object, and all such List objects are part of some BOOL Action. Statements at the module level — which are outside any user-defined Action — belong to the @global Action created by the run-time executive.

In Python, all executable objects (which are tuples) are members of an array (a list, actually), which is part of some Action object (another tuple). Let’s first take a look at a rough sketch of an Action, named @foobar, in all Python bindings:

level1 = (4, 90001, ...)
level2A = (4, 90001, ...)
level2B = (ACT, A_foobar, ...)
level3 = ('ACT', 'foobar', ...)

In BOOL, the first cell of an object (in Python: the first member of a tuple) determines what the object is and who handles it. The BOOL RTE basically just identifies an object by that first value and then dispatches it off to its handler who does the work. With an Action, as shown above, each version begins with an identifier that marks the tuple as an Action.

Level 1 is rather “binary” and is mainly just a simple Python translation of BOOL objects. The Level provides a defined standard as a compiler target and a reference for “assemblers” or “disassemblers”. That’s all that needs to be said about it now.

In this simple sketch, Level 2A looks a lot like Level 1, but Level 2 (and Level 3) objects actually do have a different structure. Level 2B uses defined symbols in place of numbers to make the objects human-readable.

Level 3 uses strings, so doesn’t require the symbol definitions. It’s kind of the stand-alone “assembly language” of BOOL. There are symbols for common strings (such as “ACT”), but Level 3 is completely string-based. Usually the symbol is just an unquoted version of the string.

I’ll generally use Level 3 (and its common symbols) here.

With that aside, back to Actions! Here’s the Action template:

act = (ACT, name, initlist, exitlist, argslist, codelist, nametable)

All Actions follow this form, but  the contents obviously varies. The name is the key to the Action; all Actions have a unique name. Each of the list items is a Python list; the nametable is a Python dict.

An empty Action (named @foobar) — one that takes no arguments, returns no values, and does nothing — would look like this:

act = (ACT, 'foobar', [], [], [('foobar',0,None)], [], {})

In later articles, I’ll explain what the parts do. For now, I want to move on to the other object meta-types.

An easy one is the BOOL List. The template looks like this:

lst = (LST, count, items , attributes)

While Python sequence objects don’t need count fields, BOOL objects do, and the List object is one place that begins to show up. It’s actually needed here, because the list items can be followed by optional attributes. (One important attribute is the GATE marker, which indicates a Gated List!)

Incidentally, all Python BOOL tuples may have optional attributes. The set of allowed attributes depends on the meta-type. For example, Lists can have a GATE attribute, and Objects (see below) can have a Name attribute.

A three-item Gated List might look like this:

lst = (LST, 3, (addr,4),(addr,5),(addr,7), GATE)

The example above also introduces the representation of an object location (an “address”) in BOOL: a tuple identified as an Address and a value. The value is the index of the target object in the Action code list mentioned above.

Another simple object is the Message object. The template is:

msg = (MSG, name, target, [argument(s)])

The name is the message text. The target and the argument(s) are (addresses of) other BOOL objects. A set: Message would look like this:

msg = (MSG, 'set', (addr,12), [(addr,3)])

Two important objects are the Object Instance and, its counterpart, the Reference Instance.

An Object Instance is the meta-type for all BOOL data objects. (The object’s data type is defined by the linked Model.) The Python version looks like this:

obj = (OBJ, model, ds, initlist)

The model is a link to the Object’s data type object. The ds (data slot) is a key to the Object’s actual (binary) data. The initlist is (the address of) a list of value(s) used to initialize the Object Instance (or it can be None).

An int Object Instance (with no initializer list) would look like this (assuming it was allocated Data Slot #2):

obj = (OBJ, 'int', 2, None, ('name','foo'))

The name tuple is an example of an optional attribute for Objects (and References, as below). Such an attribute might be included by the compiler for debugging purposes.

A Reference Instance is a reference to an Object Instance. They are used exclusively as Action input parameters. They look nearly identical to Object Instances, but function differently.

ref = (REF, model, ds)

I’m undecided about initializer lists for References. Currently BOOL Actions do not support optional or defaulted parameters, so there’s not much sense in initializing References. On the flip side, it makes References less orthogonal with Objects (a Bad Thing in my book). And why can’t BOOL have optional or defaulted Action parameters?

Finally there is the BOOL Model, which defines a data object type. This design is still very much up in the air, but the template is something like this:

mod = (MOD, name, prototype, actionchain)

Obviously Models, like Actions, have names. The prototype is a template of the data type; all Instance objects of this Model have that template. The actionchain is a linked list of Model Actions that implement the data type’s methods.

[Actually, I’ve left one meta-type out; can you tell what it is? I’ll pick it up next time when I discuss Actions in more detail.]

This ran long, and I also forgot about the BOOL XML binding (that wasn’t what I was referring to above). Here’s an even briefer taste than planned:

<Action name="add" sig="*int*int<*int">
    <Inputs>
        <Reference model="int" ds="0" name="a" />
        <Reference model="int" ds="1" name="b" />
    </Inputs>
    <Outputs>
        <Instance model="int" ds="2" name="sum" />
    </Outputs>
    <Clause name="add" args="2">
        <List>
            <Message name="set">
                <Object name="sum" />
                <Message name="add">
                    <Object name="a" />
                    <Object name="b" />
                </Message>
            </Message>
        </List>
    </Clause>
</Action>
Advertisements
%d bloggers like this: