Matanuska ADR 018 - Looping Syntax
Josh Holbrook

Josh Holbrook @jfhbrook

About: I'm an Alaskan software developer with expertise in data, distributed systems and site reliability.

Location:
Alaska
Joined:
Jan 21, 2019

Matanuska ADR 018 - Looping Syntax

Publish Date: Aug 8
0 1

This article is a repost of an ADR from Matanuska BASIC, my attempt to write a BASIC interpreter in TypeScript.

Context

The next feature to implement in Matanuska is looping.

Classic BASIC's looping structures are... idiosyncratic, especially as compared to what's expected in a modern language. Therefore, we would like to compare what's offered by BASIC versus a modern language (in this case, Python), and make a decision based on these trade-offs.

It is worth clarifying what design heuristics are important in this decision:

  1. Matanuska BASIC should loosely mirror classic BASIC dialects, such as MSX BASIC.
  2. Matanuska BASIC should support modern features, such as those in Python, as appropriate.
  3. Matanuska BASIC should be internally consistent. Design heuristics leveraged in its conditionals should also be reflected in its looping.

Current Behavior

Blocks

Recall that Matanuska's compiler uses a Block abstraction to track which block is being compiled. These blocks carry context that's relevant to the block type, and uses a visitor pattern to introduce block-specific behavior for instructions.

Currently, conditionals are implemented. For am example Block class:

class IfBlock extends Block {
  kind = 'if';

  constructor(public elseJump: Short) {
    super();
  }

  visitElseInstr(else_: Else): void {
    const endJump = this.compiler.else_(this.elseJump);
    this.next(else_, new ElseBlock(endJump));
  }

  visitElseIfInstr(elseIf: ElseIf): void {
    const endJump = this.compiler.else_(this.elseJump);
    const elseJump = this.compiler.if_(elseIf.condition);
    this.next(elseIf, new ElseIfBlock(elseJump, endJump));
  }

  visitEndIfInstr(_endIf: EndIf): void {
    // TODO: Optimize for no 'else'
    const endJump = this.compiler.else_(this.elseJump);
    this.compiler.endIf(endJump);
    this.end();
  }
}
Enter fullscreen mode Exit fullscreen mode

However, these blocks are intended to meet other block-like use cases, such as looping and functions.

The good news is that, for abstractions which have clear blocks, the abstraction should gracefully carry state at compile time and help ensure that blocks are well-formed. The bad news is that, for things which don't create blocks but do create state, that state would need to be tracked separately.

Conditionals

Recall that in https://dev.to/jfhbrook/matanuska-adr-013-if-then-and-else-syntax-7m6, we introduced specific syntax for conditionals. Of particular note is that it followed BBC BASIC's lead, and closed multi-line if blocks with an ENDIF token. Ideally, we would like to continue those themes here.

Implementations in Other Languages

MSX BASIC

MSX BASIC, which is typical in terms of a classic BASIC, has only a for/next loop and the classic goto. The structure of a classic for/next loop is like so:

10 FOR x%=1 TO 10 STEP 1
20   FOR y%=1 TO 10 STEP 1
30     PRINT x%
40     PRINT y%
30 NEXT x%,y%
Enter fullscreen mode Exit fullscreen mode

Note that end is inclusive - ie, FOR x=1 TO 10 will print the numbers 1 to 10. Also note that STEP n and the variables to NEXT are optional.

The behavior of NEXT is a little idiosyncratic - it operates as both an end and a continue, and can mark which loop to continue based on a variable.

In terms of how this works under the hood, for loops are not structured as blocks as they are in Matanuska. Instead, they are treated as isolated statements which introduce runtime state. Note that this means MSX BASIC can't guard against "broken" nesting, something that Matanuska attempts to do at compile time.

For completeness, simple GOTO looks like so:

10 PRINT "hello world!"
20 GOTO 10
Enter fullscreen mode Exit fullscreen mode

GOTO is very low level, and can not take advantage of blocks.

BBC BASIC

⚠️ NOTICE ⚠️

This ADR misunderstands the use of the end keyword in BBC BASIC examples. They keyword is used the same way as end in MSX BASIC, and are included in the examples to present complete programs.

BBC BASIC's for loops look a little different:

10 FOR x%=1 TO 10
20   PRINT x%
30   NEXT x%
40 END
Enter fullscreen mode Exit fullscreen mode

What's noteworthy here is that BBC BASIC uses an END keyword to close the loop, but also supports NEXT in a way similar to MSX BASIC. Note that END is inconsistent with other END{begin_block} tokens in BBC BASIC.

Unlike MSX BASIC, BBC BASIC also has while loops:

WHILE x% > 0
  x% = x% / 2
ENDWHILE
Enter fullscreen mode Exit fullscreen mode

Nothing too sophisticated here.

BBC BASIC also supports a repeat/until loop, that is about what one would expect:

REPEAT
  ...
UNTIL ...
END
Enter fullscreen mode Exit fullscreen mode

BASIC8

BASIC8's for loops are similar to those of MSX BASIC. Its while loops are similar to BBC BASIC, but use the WEND keyword instead of the ENDWHILE keyword.

It also has a do/until loop, similar to BBC BASIC, but with significantly different syntax:

DO
  ...
UNTIL ...
Enter fullscreen mode Exit fullscreen mode

Unlike BBC BASIC, BASIC8 does not require an END keyword. It treats UNTIL as similar to MSX BASIC's NEXT keyword.

Python

Python's for loops look like so:

for i in range(0, 10):
    print(i)
Enter fullscreen mode Exit fullscreen mode

What's noteworthy here is two things:

  1. Python takes an iterator as an argument. Classic BASIC does not have first class iterators. But it would be nice if Matanuska could implement them in the future.
  2. Python has proper blocks, like Matanuska.

Python also has a while loop:

while True:
    print "hey"
Enter fullscreen mode Exit fullscreen mode

It also supports the break and continue keywords. continue operates similarly to next in MSX BASIC. But break seems novel.

JavaScript

JavaScript supports a standard C-like for loop:

for (let x = 0; i < 10; i++) {
  ...
}
Enter fullscreen mode Exit fullscreen mode

But it also supports syntax for iterating over objects, namely arrays:

for (const x of xs) {
  ...
}
Enter fullscreen mode Exit fullscreen mode

Decision

For

Standard for loops will have the following syntax:

10 FOR x%=1 TO 10 [STEP 1]
20   PRINT x%
30   NEXT
40 ENDFOR
Enter fullscreen mode Exit fullscreen mode

This leans into the design of BBC BASIC, with a few differences.

First, for loops will be closed with an endfor keyword. This is inconsistent with BBC BASIC's implementation of for loops, but consistent with the syntax of its other looping constructs, as well as Matanuska's syntax for conditionals. Additionally, it will allow for clear block semantics.

Second, NEXT will be supported optionally in a way that mirrors continue in Python. However, it will not accept a variable. This is because Matanuska (unlike MSX BASIC) is block structured.

Note that, in this case, we will support BASIC's syntax with regard to the TO keyword. However, in the future, Matanuska will likely support an additional for loop structure that mirrors for...of in JavaScript:

10 FOR x% OF xs%
20   ...
30 ENDFOR
Enter fullscreen mode Exit fullscreen mode

The specifics of this syntax and implementation are out of scope for this ADR, and will be revisited when Matanuska BASIC supports arrays.

While and Repeat

Matanuska BASIC will also support WHILE, similar to BBC BASIC:

WHILE x% > 0
  x% = x% / 2
ENDWHILE
Enter fullscreen mode Exit fullscreen mode

It will also support repeat/until:

REPEAT
  ...
UNTIL ...
Enter fullscreen mode Exit fullscreen mode

Like BASIC8, it will close these blocks with the UNTIL keyword. Including an ENDREPEAT keyword would be redundant. However, it will use the REPEAT keyword in line with BBC BASIC.

GOTO

GOTO is considered out of scope for this ADR, and will be revisited at a later date.

Comments 1 total

    Add comment