List Syntax

A design goal BOOL had from the beginning is that it should not use block scope delimiters. That is, I wanted neither the C family’s {curly braces}, nor did I want the ALGOL family’s BEGIN and END keywords. But how to indicate scope? If tokens at the ends are forbidden, the items themselves must somehow indicate their scope.

I hadn’t yet heard of Python and its elegant use of whitespace, but that’s the sort of thing I wanted. I came up with something similar, called “dot lists.” (Just not trusting the idea of indentation alone — turns out it’s totally bitchin’!) As it happens, since you can re-define the “dot” character, by changing it to a TAB character, you can have Python’s whitespace indentation!

With a couple extra BOOL-ish wrinkles!!

If you’re familiar with Python, this will seem like a training-wheel version. Which it pretty much is. Like I said, I didn’t trust the idea that a parser could use indentation. Actually, it’s the programmer I didn’t trust.  But it turns out it’s pretty natural and very successful.

But dot lists are one of those ugly BOOL features that I intend to keep. It’s classic BOOL, part of the first definition. And if you do re-define the dot character to a TAB, it winds up being Python cool!

Let’s start with the idea that BOOL is whitespace insensitive in the following sense:

.   object1 object2  object3

Is the same as:

.   object1
.   object2
.   object3

The dots you see along the left margin aren’t typesetting errors, they’re list level indicators.

All BOOL objects belong to some list. Which list depends, in part, on how many dots are between the left margin and the objects on that line. List level is identical to saying indentation level. The dots are showing the indentation level visibly.

The other thing that determines which list owns which objects is the order in which they appear. Objects belong to the most recent list for their level. When the indentation moves to a lower level, higher-indentation lists above it are “closed” and no further objects will belong to it.

Objects  with no dots — level zero — belong to the top-level program list, which is implicit.  This is similar to how most script languages work; statements not wrapped in functions or classes belong to the file scope. (Such objects would normally be along the left margin, but one advantage of actual dot characters is that BOOL is insensitive to varying dot spacing and leading whitespace.)

With this groundwork, let’s reconsider the example above:

.   object1
.   object2
.   object3

We have three objects, each has a dot level of one, so all are mutual items on some list with level one. No list is defined explicitly. If this is all we specify, the compiler creates an unnamed list. The rule is that the compiler creates lists as needed when indentation increases.

There is another way lists are created implicitly: comma lists. If we change the first example thus:

.   object1, object2, object3

Now we’re creating two lists. The object(s) listed have dot level one. But the comma following object1 indicates it’s part of an implicit (comma) list. The two commas join all three objects in a single list. So the line actually just has one object: a(n implicit comma) list. That object has dot level one and belongs to another implicit list created to contain it.

It’s as if we wrote this:

.   =   object1
.   .   object2
.   .   object3

Which begins to show how lists are explicitly defined.

The equals operator specifies (explicitly) a list. The one-line version above could be re-written like this:

.   = object1, object2, object3

Without changing the logic. The commas alone indicate a list; the equals operator is redundant in this case. But if you wanted a list with a single item, the equals operator is required.

A vital point: unlike most languages, the equals operator means neither assignment nor equality. Its use in Object instantiation makes it look vaguely like an assignment operator, but this is an illusion! (A deliberate joke on my part, truth be told.)

Let’s consider the single-item case (it’s actually part of what drove writing this article tonight):

.   *int n = 42

This defines an integer, named n, and also defines an initialization list (I emphasize list) for it. That list contains one item, the value 42. The syntax of Object definition specifies that, if a list follows the object definition, that list is the object initializer. Another way to write the above is:

.   *int n
.   = 42

This does mean some care must be taken about lists following object definitions. Providing an initializer is the easiest way to insure they don’t bind to the object as its initializer.

Now consider the case of an Action:

.   @if gt:0 index;
.   .   set:index decr:index;

It may be helpful to re-write it as:

.   @if
.   =   gt:0 index;
.   =   set:index decr:index;

The first equals operator is actually optional; the indentation level alone creates the list. But the @if action expects two objects (not one list with two objects), so the second equals operator is required to create the second object (a list). And, of course, we could also write it like this:

.   @if gt:0 index; set:index decr:index;

This is actually the same as our very first example: three objects on one line, all have dot level one. Which means that this works, too:

.   @if
.   gt:0 index;
.   set:index decr:index;

Although it’s not very good style!

(Now the previous Gated List article should make more sense!)

%d bloggers like this: