catseye  committed e6df139

Add specification.

  • Participants
  • Parent commits b709224
  • Branches default

Comments (0)

Files changed (3)

File README.markdown

     It thus lacks an `IF` statement because, similar to [Strelnokoff][], it
     doesn't need one.
-For a full description of the language, see the [SICKBAY article][] on the
+This is the reference distribution for SICKBAY.  It contains:
+*   the normative description (i.e. specification) of the language -- see the
+    file `SICKBAY.markdown` in the `doc` directory.
+*   other notes on the language, also in the `doc` directory.
+*   SAWBONES, Cat's Eye Technologies' reference implementation of SICKBAY,
+    written in Python, in the `src` directory.
+*   several small example SICKBAY programs in the `eg` directory.
+For more information on the language, see the [SICKBAY article][] on the
 [ wiki][].
-This is the reference distribution for SICKBAY.  It contains SAWBONES,
-Cat's Eye Technologies' reference implementation of SICKBAY, written in
 [SICKBAY article]:
 [ wiki]:

File doc/Original_Notes.markdown

+SICKBAY: A BASIC with ring buffers
+(These are the original notes I wrote up for the idea of using a call ring
+buffer in a BASIC-like language, because I don't know where else to put them
+and it seems a shame to just throw them out -- Ed.)
+Oftentimes, hardware influences programming language design.  The most
+notorious example is probably the call stack, almost omnipresent in processor
+architecture.  The most common case for routines is where one routine calls
+another, and we expect control to return from the called routine and resume
+inside the first routine at a point just after the call was made.  This
+nesting of activations is reflected in the FIFO nature of the call stack.
+The control flow of exceptions may seem more complicated, but it too adheres
+to this FIFO nature; the only difference is that control may resume at a point
+several callers up the stack.
+However there are many flexible and useful patterns of control, such as
+first-class activation records, closures, co-routines, escape procedures, and
+general continuations, which do not adhere to this stricture.  The activation
+lifetime of each of these may outlive the activation lifetime of the thing
+that called it.  If we wish to support a less strict relationship between
+callers and callees, we need a correspondingly more flexible memory management
+structure than a stack.  Generally this is approached by allocating these
+control structures on the heap, possibly as an optimization assigning stack
+allocation to ones which upon analysis are indeed found to have FIFO behaviour.
+But here, we take a slightly different data structure as our backing store
+for control flow, and consider how much more flexibility this allows in our
+language's control constructs.  The data structure is more capable than a FIFO
+stack, but can be mapped just as efficiently to hardware; it is less flexible
+than a general heap, but decidedly less complex, requiring no garbage
+collection.  This data structure is the _ring buffer_.
+Recall that a ring buffer is a fixed span of memory cells which supports the
+operations of a deque: there is an allocated area inside the span, and records may
+be appended and removed from either end of it.  Should the appending of a record
+cause the allocated area to exceed either end of the span of memory, it "wraps
+around" and is stored at the other end of the span.  Thus the total capacity of the
+ring buffer is always the size of the span. The "wrap around" behaviour can be
+efficiently computed using binary logic if the size of the span is a power of two.
+The push and pop operations of the FIFO stack correspond to the call and
+return operations on routines.  Our ring buffer has two more operations, which we'll
+call "push-bottom" and "pop-bottom", which correspond to two new operations
+on routines, which we'll call "prepend" and "truncate".  Prepending is essentially
+adding a routine that is executed after the "main" routine quits, and truncating is
+essentially removing the main routine so that the program quits when returning
+from the last routine that main routine called.
+(Of course, "quit" is subjective.  When a program quits, all that really
+happens is that the operating system resumes.)

File doc/SICKBAY.markdown

+This document describes the SICKBAY programming language.  SICKBAY is an
+esoteric dialect of BASIC with two salient features:
+*   While most BASICs support a call stack which is used to implement `GOSUB`
+    and `RETURN`, SICKBAY uses a _call ring buffer_, which supports not only
+    `GOSUB` and `RETURN` but also `PROLONG` and `CUTSHORT`.
+*   While some BASICs support computed line numbers in `GOTO` and `GOSUB`,
+    SICKBAY supports computed line numbers only in line number definitions.
+    It thus lacks an `IF` statement because, similar to [Strelnokoff][], it
+    doesn't need one.
+A SICKBAY program is a series of lines.  Each line must have a line number
+(which may be an expression.)  Unlike BASIC, adjacent tokens must be
+separated by one or more spaces, if they would otherwise look like one word
+(e.g. you need `PRINT A%`, not `PRINTA%`.)
+The language's syntax is defined by the following EBNF (plus some
+pseudo-productions for terminals) grammar:
+    SICKBAY   ::= {Line}.
+    Line      ::= IntExpr Stmt {":" Stmt} Newline.
+    Stmt      ::= "REM" ArbText
+               | "LET" IntVar "=" IntExpr
+               | "GOTO" IntConst
+               | "GOSUB" IntConst
+               | "RETURN" | "END"
+               | "PROLONG" IntConst
+               | "CUTSHORT"
+               | "DIM" "RING" "(" IntExpr ")"
+               | "PRINT" (StrConst | IntExpr | "CHR$" IntExpr) [";"]
+               | "INPUT" (IntVar | "CHR$" IntVar)
+               .
+    IntExpr   ::= IntVar
+               | IntConst
+               | "RND%" "(" IntExpr ")"
+               | "(" IntExpr IntOp IntExpr ")"
+               .
+    IntOp     ::= "+" | "-" | "*" | "/".
+    IntVar    ::= IntId ["(" IntExpr ")"].
+    IntId     ::= /[A-Z][A-Z0-9]%/.
+    IntConst  ::= /[0-9][0-9]*/.
+    StrConst  ::= /"[^"]*"/.
+    ArbText   ::= /[^\n]*/.
+    Newline   ::= /\n+/.
+Many of the SICKBAY statements have meanings very similar to those in BASIC,
+and I appeal to your knowledge of that language to make this description
+### Execution ###
+Lines are executed in numerical order, which may have nothing to do with the
+order they appear in the program text; however, if two lines have the same
+line number, the one which appears first in the program text takes precedence
+(the other lines with the same number are not "seen" during execution.)
+Execution begins initially from the lowest-numbered line in the program.
+Line numbers are "live"; they are recomputed from their expressions each time
+execution progresses from one line to the next.  (Two acceptable ways to
+implement this are: every time a variable _x_ changes, recalculate the line
+number of every line that uses _x_ in its line expression; or, just before
+any jump or proceeding to the next line, recalculate all line numbers.)
+Attempting to proceed to the next line when there are no more higher-numbered
+lines in the program causes `END`.  `END` is an alias for `RETURN`.  `RETURN`
+(or `CUTSHORT`) with nothing on the call ring buffer ends the program and
+returns to the operating system.
+The call ring buffer is of fixed size, and contains line numbers (concrete
+line numbers, not expressions.)  If no size is chosen before any
+`GOSUB`/`RETURN`/`PROLONG`/`CUTSHORT` is executed, a default size of 10 line
+numbers will be used.  A `DIM RING` statement may be executed to set the size
+of the ring buffer if it has not yet been set.  (If it has already been set,
+an error occurs.)
+`GOSUB` pushes the current line number onto the top of the call ring buffer
+and moves execution to the line with the number given to it.  `RETURN` pops a
+line number from the top of the call ring buffer and moves execution to the
+next line in the program strictly following that line number.  `RETURN` does
+not continue to execute remaining statements on the same line as the `GOSUB`
+after colons (see clarifying example below.)
+`PROLONG` pushes the given line number onto the _bottom_ of the call ring
+buffer.  `CUTSHORT` pops a line number from the _bottom_ of the call ring
+buffer.  Neither of these change the flow of execution immediately.  The
+practical effect of `PROLONG` is to pretend that a `GOSUB` was made from a
+line number before the first real `GOSUB` was ever made, effectively adding
+some code that will be executed after the program ends.  The practical effect
+of `CUTSHORT` is to make the program end prematurely, when attempting to
+`RETURN` to the rootmost caller (initially this would be the "main program".)
+If space in the call ring buffer is exhausted, an error occurs.
+In `GOTO` and `GOSUB`, if the given line number does not exist at the time
+the statement is executed, an error occurs.
+### Variables ###
+All variables initially have the value zero.  Any variable may be used as an
+array; the variable itself is just an alias for the first element of the
+array, i.e. `H% = H%(0)`.  Arrays don't have bounds and don't need
+Integers may be negative.  However, the syntax for integer constants only
+allows non-negative integers; to give a negative constant, an expression such
+as `(0 - 100)` must be used.  Note that this means a negative line number
+cannot be jumped to, as `GOTO` et al must be followed by an integer constant,
+not an expression.  (However, a negative line number may be _returned_ to, as
+it is possible to write a program which begins executing at a negative line
+number and makes a `GOSUB` from it.)
+Operators have no precedence; parentheses must be used around all operations
+(see grammar).
+Like Strelnokoff, `/` is integer division, truncating downwards, and
+evaluating to zero if the divisor is zero (there is no division by zero
+The `RND%(`_n_`)` function evaluates to an integer from 0 to _n_-1, chosen
+randomly.  If _n_ is zero or negative, an error occurs.
+### I/O ###
+Integer expressions may be printed; they are formatted as decimal numerals,
+possibly preceded by a negative sign, but, unlike most BASICs, not preceded
+or followed by any spaces.  The ASCII character for a given integer value may
+be printed with the `PRINT CHR$` form.  Literal strings may also be printed,
+but only one thing may (and exactly one thing must) be printed per `PRINT`
+statement (so to just print a blank line, print a null string literal.)
+Anything printed with a `PRINT` statement will be followed by a newline,
+unless the semicolon is given after the statement, which suppresses the
+The `INPUT IntVar` form accepts an integer, formatted as decimal numerals,
+possibly preceded by a negative sign, from the input stream, and places it in
+the variable.  Any whitespace preceding, and the first whitespace following
+the integer is swallowed up; if the integer is not followed by at least one
+whitespace character, an error occurs.  The `INPUT CHR$ IntVar` form accepts
+a single character from the input stream and places its ASCII value in the