Actions, part 3

At its heart, BOOL has a fundamental principle: Everything is an object. This includes some non-obvious things, such as definitions and even individual statements. In particular for this discussion: statements. For example, in BOOL, an if-else statement is a distinct object.

All statements in BOOL are references to some Action object, which is invoked via an Actor object (Actors perform Actions). In a BOOL program, there can be many Actors that perform a given Action. For example, each occurrence of an if-else statement in the code is an Actor object that invokes the if-else Action.

A BOOL design goal is a minimal base language syntax that implements basic flow control and data storage. The Gated List is the native flow control construct. Model objects and Object Instance objects provide data storage. Action objects are the sub-routines and methods, and Message objects act as expression operators.

BOOL’s core specification requires a few basic Models for numbers and strings, and some Object Instances as constants (e.g. “true” and “0”). A run-time environment is allowed to implement these as native, but they must act as if implemented in a BOOL library. Thus user-created Models and Instances act identically to core objects.

Also specified as required (but allowed to be native) are a set of statement Actions, such as if-else, while and switch. In most languages, statements are part of the language syntax parsed by the compiler. In BOOL, statements are discrete sub-routines (specifically: Generic Actions (more specifically: Actors that invoke a Generic Action)).

This actually makes if-else something of a challenge, because sometimes there’s no else clause, sometimes there’s just the if clause. It’s even more complicated if you want to have an elif middle clause (if-elif-else), because not only is the elif optional, it can repeat!

There’s no absolute requirement a programming language even have an else construct, let alone an elif construct, but they make writing code easier and cleaner. Since it turns out implementing a switch statement has the same constraints as an elif, BOOL has to provide a means to implement optional and repeating (multiple) clauses.

Which has been mildly daunting.

Here’s how it works. Let’s start with the if-elif-else definition:

@@if
>>  *() if-expr
>>  *() if-list
@@elif #optional #multi
>>  *() elif-expr
>>  *() elif-list
@@else #optional
>>  *() else-list
<<  *bool x = t:if-expr;
[if]=? x
.   if-list
[elif]=? not:x;
.   set:x t:elif-expr;
.   =? x
.   .   elif-list
[else]=? not:x;
.   else-list

The header for an Action may contain sub-clauses (which can have tags). These sub-clauses add keywords to the Action signature and provides a means to partition an Action into sub-parts, some of which can be repeatable. The tags on a sub-clause determine if the clause is optional and/or repeatable.

The code above creates a Generic Action object, which is like a sub-routine. The code to generate an Actor object to invoke the Action might look like this:

@if gt:0 angle
.   set:x cos:angle;
.   set:y sin:angle;
@else
.   set:x cos:neg:angle;;
.   set:x sin:neg:angle;;

Or it might not have an @else sub-clause:

@if eq:0 angle
.   set:x 0
.   set:y 0

Or it might have multiple sub-clauses:

@if lt:angle 90
.   set:x cos:angle;
.   set:y sin:angle;
@elif lt:angle 180
.   set:a sub:angle 90
.   set:x cos:a;
.   set:y sin:a;
@elif lt:angle 270
.   set:a sub:angle 180
.   set:x cos:a;
.   set:y sin:a;
@else
.   set:a sub:angle 270
.   set:x cos:a;
.   set:y sin:a;

Because these are actually calls to sub-routines, recognizing the syntax of variant Actors is only the start of the challenge. BOOL also needs a calling mechanism that supports the optional and multiple sub-clauses. (In reality, it’s the multiple ones that are killer; optional is easy.)

The signature for the above Action is:

@if*()*()@elif#opt#mul*()*()@else#opt*()<*bool

The compiler uses this signature to figure out a given bit of code matches, is an “if-else statement” to be made into the appropriate Actor object. However, the name of the Action is simply @if, which is the keyword the compiler uses to initially identify the possibility of a match.

The names of the sub-clauses act as tags under which the Actor groups parameters for the Action. If a sub-clause (such as elif) can have multiple groups of parameters, the Action repeats that sub-clause for each group. Within the Action body, a sub-clause is a labeled List. The label is the sub-clause name, and there can be only one such List per sub-clause. The List is not required.

By default, the first List, if unlabeled, takes the Action’s name as its label. Such an unlabeled first List prohibits another List with the Action name as a label. All Action Lists on the execlist must be labeled. Also, the labels (and their Lists) must appear in the same order as in the signature.

When an Action executes and encounters a sub-clause List tagged as multiple, it repeats the List for each group of matching parameters in the invoking Actor. All other objects maintain their state as if the repeats were unwound and executed in sequence. (In other words, other parameters are not reloaded or reset each time the sub-clause repeats.)

The action skips any sub-clauses tagged as optional if no parameters are provided in the invoking Actor. For example, an if statement with no elif or else parts generates an @if Actor with only two parameters: the if expression and the if list. When invoked, the Action does not execute the elif or else Lists.

Finally, only Generic Actions have have sub-clauses. The Action name of a Model Action is the Message name that invokes it (if sent to an Object Instance of that Model). There is no way for a Message to include the idea of sub-clauses, so they are prohibited in Model Actions.

Advertisements
%d bloggers like this: