git @ Cat's Eye Technologies SICKBAY / 34b73dd
Add specification. catseye 12 years ago
3 changed file(s) with 213 addition(s) and 5 deletion(s). Raw diff Collapse all Expand all
1010 It thus lacks an `IF` statement because, similar to [Strelnokoff][], it
1111 doesn't need one.
1212
13 For a full description of the language, see the [SICKBAY article][] on the
13 This is the reference distribution for SICKBAY. It contains:
14
15 * the normative description (i.e. specification) of the language -- see the
16 file `SICKBAY.markdown` in the `doc` directory.
17 * other notes on the language, also in the `doc` directory.
18 * SAWBONES, Cat's Eye Technologies' reference implementation of SICKBAY,
19 written in Python, in the `src` directory.
20 * several small example SICKBAY programs in the `eg` directory.
21
22 For more information on the language, see the [SICKBAY article][] on the
1423 [esolangs.org wiki][].
15
16 This is the reference distribution for SICKBAY. It contains SAWBONES,
17 Cat's Eye Technologies' reference implementation of SICKBAY, written in
18 Python.
1924
2025 [Strelnokoff]: http://catseye.tc/projects/strelnokoff/
2126 [SICKBAY article]: http://esolangs.org/wiki/SICKBAY
0 SICKBAY: A BASIC with ring buffers
1 ==================================
2
3 (These are the original notes I wrote up for the idea of using a call ring
4 buffer in a BASIC-like language, because I don't know where else to put them
5 and it seems a shame to just throw them out -- Ed.)
6
7 Oftentimes, hardware influences programming language design. The most
8 notorious example is probably the call stack, almost omnipresent in processor
9 architecture. The most common case for routines is where one routine calls
10 another, and we expect control to return from the called routine and resume
11 inside the first routine at a point just after the call was made. This
12 nesting of activations is reflected in the FIFO nature of the call stack.
13 The control flow of exceptions may seem more complicated, but it too adheres
14 to this FIFO nature; the only difference is that control may resume at a point
15 several callers up the stack.
16
17 However there are many flexible and useful patterns of control, such as
18 first-class activation records, closures, co-routines, escape procedures, and
19 general continuations, which do not adhere to this stricture. The activation
20 lifetime of each of these may outlive the activation lifetime of the thing
21 that called it. If we wish to support a less strict relationship between
22 callers and callees, we need a correspondingly more flexible memory management
23 structure than a stack. Generally this is approached by allocating these
24 control structures on the heap, possibly as an optimization assigning stack
25 allocation to ones which upon analysis are indeed found to have FIFO behaviour.
26
27 But here, we take a slightly different data structure as our backing store
28 for control flow, and consider how much more flexibility this allows in our
29 language's control constructs. The data structure is more capable than a FIFO
30 stack, but can be mapped just as efficiently to hardware; it is less flexible
31 than a general heap, but decidedly less complex, requiring no garbage
32 collection. This data structure is the _ring buffer_.
33
34 Recall that a ring buffer is a fixed span of memory cells which supports the
35 operations of a deque: there is an allocated area inside the span, and records may
36 be appended and removed from either end of it. Should the appending of a record
37 cause the allocated area to exceed either end of the span of memory, it "wraps
38 around" and is stored at the other end of the span. Thus the total capacity of the
39 ring buffer is always the size of the span. The "wrap around" behaviour can be
40 efficiently computed using binary logic if the size of the span is a power of two.
41
42 The push and pop operations of the FIFO stack correspond to the call and
43 return operations on routines. Our ring buffer has two more operations, which we'll
44 call "push-bottom" and "pop-bottom", which correspond to two new operations
45 on routines, which we'll call "prepend" and "truncate". Prepending is essentially
46 adding a routine that is executed after the "main" routine quits, and truncating is
47 essentially removing the main routine so that the program quits when returning
48 from the last routine that main routine called.
49
50 (Of course, "quit" is subjective. When a program quits, all that really
51 happens is that the operating system resumes.)
0 SICKBAY
1 =======
2
3 This document describes the SICKBAY programming language. SICKBAY is an
4 esoteric dialect of BASIC with two salient features:
5
6 * While most BASICs support a call stack which is used to implement `GOSUB`
7 and `RETURN`, SICKBAY uses a _call ring buffer_, which supports not only
8 `GOSUB` and `RETURN` but also `PROLONG` and `CUTSHORT`.
9 * While some BASICs support computed line numbers in `GOTO` and `GOSUB`,
10 SICKBAY supports computed line numbers only in line number definitions.
11 It thus lacks an `IF` statement because, similar to [Strelnokoff][], it
12 doesn't need one.
13
14 [Strelnokoff]: http://catseye.tc/projects/strelnokoff/
15
16 Syntax
17 ------
18
19 A SICKBAY program is a series of lines. Each line must have a line number
20 (which may be an expression.) Unlike BASIC, adjacent tokens must be
21 separated by one or more spaces, if they would otherwise look like one word
22 (e.g. you need `PRINT A%`, not `PRINTA%`.)
23
24 The language's syntax is defined by the following EBNF (plus some
25 pseudo-productions for terminals) grammar:
26
27 SICKBAY ::= {Line}.
28 Line ::= IntExpr Stmt {":" Stmt} Newline.
29 Stmt ::= "REM" ArbText
30 | "LET" IntVar "=" IntExpr
31 | "GOTO" IntConst
32 | "GOSUB" IntConst
33 | "RETURN" | "END"
34 | "PROLONG" IntConst
35 | "CUTSHORT"
36 | "DIM" "RING" "(" IntExpr ")"
37 | "PRINT" (StrConst | IntExpr | "CHR$" IntExpr) [";"]
38 | "INPUT" (IntVar | "CHR$" IntVar)
39 .
40 IntExpr ::= IntVar
41 | IntConst
42 | "RND%" "(" IntExpr ")"
43 | "(" IntExpr IntOp IntExpr ")"
44 .
45 IntOp ::= "+" | "-" | "*" | "/".
46 IntVar ::= IntId ["(" IntExpr ")"].
47 IntId ::= /[A-Z][A-Z0-9]%/.
48 IntConst ::= /[0-9][0-9]*/.
49 StrConst ::= /"[^"]*"/.
50 ArbText ::= /[^\n]*/.
51 Newline ::= /\n+/.
52
53 Semantics
54 ---------
55
56 Many of the SICKBAY statements have meanings very similar to those in BASIC,
57 and I appeal to your knowledge of that language to make this description
58 complete.
59
60 ### Execution ###
61
62 Lines are executed in numerical order, which may have nothing to do with the
63 order they appear in the program text; however, if two lines have the same
64 line number, the one which appears first in the program text takes precedence
65 (the other lines with the same number are not "seen" during execution.)
66 Execution begins initially from the lowest-numbered line in the program.
67
68 Line numbers are "live"; they are recomputed from their expressions each time
69 execution progresses from one line to the next. (Two acceptable ways to
70 implement this are: every time a variable _x_ changes, recalculate the line
71 number of every line that uses _x_ in its line expression; or, just before
72 any jump or proceeding to the next line, recalculate all line numbers.)
73
74 Attempting to proceed to the next line when there are no more higher-numbered
75 lines in the program causes `END`. `END` is an alias for `RETURN`. `RETURN`
76 (or `CUTSHORT`) with nothing on the call ring buffer ends the program and
77 returns to the operating system.
78
79 The call ring buffer is of fixed size, and contains line numbers (concrete
80 line numbers, not expressions.) If no size is chosen before any
81 `GOSUB`/`RETURN`/`PROLONG`/`CUTSHORT` is executed, a default size of 10 line
82 numbers will be used. A `DIM RING` statement may be executed to set the size
83 of the ring buffer if it has not yet been set. (If it has already been set,
84 an error occurs.)
85
86 `GOSUB` pushes the current line number onto the top of the call ring buffer
87 and moves execution to the line with the number given to it. `RETURN` pops a
88 line number from the top of the call ring buffer and moves execution to the
89 next line in the program strictly following that line number. `RETURN` does
90 not continue to execute remaining statements on the same line as the `GOSUB`
91 after colons (see clarifying example below.)
92
93 `PROLONG` pushes the given line number onto the _bottom_ of the call ring
94 buffer. `CUTSHORT` pops a line number from the _bottom_ of the call ring
95 buffer. Neither of these change the flow of execution immediately. The
96 practical effect of `PROLONG` is to pretend that a `GOSUB` was made from a
97 line number before the first real `GOSUB` was ever made, effectively adding
98 some code that will be executed after the program ends. The practical effect
99 of `CUTSHORT` is to make the program end prematurely, when attempting to
100 `RETURN` to the rootmost caller (initially this would be the "main program".)
101
102 If space in the call ring buffer is exhausted, an error occurs.
103
104 In `GOTO` and `GOSUB`, if the given line number does not exist at the time
105 the statement is executed, an error occurs.
106
107 ### Variables ###
108
109 All variables initially have the value zero. Any variable may be used as an
110 array; the variable itself is just an alias for the first element of the
111 array, i.e. `H% = H%(0)`. Arrays don't have bounds and don't need
112 dimensioning.
113
114 Integers may be negative. However, the syntax for integer constants only
115 allows non-negative integers; to give a negative constant, an expression such
116 as `(0 - 100)` must be used. Note that this means a negative line number
117 cannot be jumped to, as `GOTO` et al must be followed by an integer constant,
118 not an expression. (However, a negative line number may be _returned_ to, as
119 it is possible to write a program which begins executing at a negative line
120 number and makes a `GOSUB` from it.)
121
122 Operators have no precedence; parentheses must be used around all operations
123 (see grammar).
124
125 Like Strelnokoff, `/` is integer division, truncating downwards, and
126 evaluating to zero if the divisor is zero (there is no division by zero
127 error.)
128
129 The `RND%(`_n_`)` function evaluates to an integer from 0 to _n_-1, chosen
130 randomly. If _n_ is zero or negative, an error occurs.
131
132 ### I/O ###
133
134 Integer expressions may be printed; they are formatted as decimal numerals,
135 possibly preceded by a negative sign, but, unlike most BASICs, not preceded
136 or followed by any spaces. The ASCII character for a given integer value may
137 be printed with the `PRINT CHR$` form. Literal strings may also be printed,
138 but only one thing may (and exactly one thing must) be printed per `PRINT`
139 statement (so to just print a blank line, print a null string literal.)
140 Anything printed with a `PRINT` statement will be followed by a newline,
141 unless the semicolon is given after the statement, which suppresses the
142 newline.
143
144 The `INPUT IntVar` form accepts an integer, formatted as decimal numerals,
145 possibly preceded by a negative sign, from the input stream, and places it in
146 the variable. Any whitespace preceding, and the first whitespace following
147 the integer is swallowed up; if the integer is not followed by at least one
148 whitespace character, an error occurs. The `INPUT CHR$ IntVar` form accepts
149 a single character from the input stream and places its ASCII value in the
150 variable.