1. Examples.
1.1. The LIFE game.
{Bd, Temp: [[0,0],GETPOINT "Board limits"] ¡ 1; -- Bd is the board. ¡ means "ARRAY OF DEPTH".
Hsum: ((Bdh[0,1]) + (Bdf[0,1]) 2); -- h is "SHIFTED BY", f is "SHIFTED BY -". is "DEPTH".
Vsum: ((Bdh[1,0]) + (Bdf[1,0]) 2); -- (x + y k) gives k lowest-order bits of the sum
Nsum: (Vsum + (Hsum + (Hsumv[1,0] + Hsumt[1,0] 3)));
NewG: ((Nsum V Bd) = (ALL 011u)); -- Value of (x = y) is 1 where equal, 0 where different
LOOP{EDIT Bd; -- displays magnified Bd and allows user "mousing" on it
-- (probably a subroutine written in MUMBLE)
LOOP {SHOW Bd; -- displays Bd in true scale.
SCAN (Temp ← NewG);
SCAN (Bd ← Temp);
ANYCHAR Ò {GETCHAR; EXIT}}
}
}
1.2. The EDIT routine.
EDIT: {Ar: ARRAY|
Sz: HIGH Ar - LOW Ar;
SzD: HIGH DISPLAY - LOW DISPLAY;
ArI: (Art(LOW Ar)) CUT AT [[0,0], SzD/4];
Blob: [[0,0], [4,4]] ¡ 1;
SCAN (Blob ← ELLIPSE [[0,2], [2,2], 2]);
InvIt: {Ix: POINT | SET [ArI, Ix, ((ArI Ix) • 1)]};
ShowIt: {Pos: POINT | SCAN DISPLAY ← DISPLAY 4 BlobhPos};
SHOW (ALL 0);
FOR Pos IN BOUNDS Ari DO {Ari Pos Ò ShowIt Pos};
LOOP {C: GETOBJ;
C IS POINT Ò {ShowIt C; InvIt (C-LOW DISPLAY)/4};
C = "QUIT" Ò {SHOW (ALL 0); EXIT}}
}
2. Grammar
-- This is a two-level grammar: it consists of a conrtext-free
meta-grammar and a set of
rule skeletons which together generate the definitive context-free grammar for the language.
--
Meta-variables are in capital italics (like
TYPE
), and
meta-productions have
::
between left and right sides. By applying the meta-productions in a context-free manner, each meta-variable generates a set of terminal strings (in lowercase, as array
, statement
, etc) which are called its
instances. (Any terminal string is an instance of the special meta-variable
ANYTHING
).
-- By taking a rule skeleton (with ==) form the list below and replacing consistently every meta-variable in it by one of its instances, we get a good honest BNF rule (with no capital italics).
-- The final grammar is the set of all rules that can be derived in this manner. Non-terminal symbols are strings delimited by <>, and everything else is terminal.
TYPE
:: array | formula | proc | int | point | box | record | ...
OBJTYPE
:: array | formula
<command>
== <id> : <
TYPE expr>
-- Evaluates <expr> and (re)defines the <id> as the result. If the <expr> is of type formula, the result is the data structure - not the picture it represents.
== SCAN <formula expr>
-- The picture formula (a data structure) returned by the <formula expr> is evaluated for all pixels in the picture plane. The picture resulting from the execution is discarded. For the SCAN to have tangible effects, the formula should contain assignment or SWAP statements.
== SHOW <formula expr>
-- Equivalent to SCAN (DISPLAY ← formula).
== <transform statement>
== <proc call>
== FOR <id> IN [<int expr> .. <int expr>] DO <command>
== <empty>
== ...
<transform statement>
== SHIFT <
OBJTYPE id> BY <vector expr>
== REFLECT <
OBJTYPE id> ABOUT <I or J> = <int expr>
== ROTATE <
OBJTYPE id> <Xwise> AROUND <point expr>
== TRANSPOSE <
OBJTYPE id> AROUND <point expr>
== SAMPLE <
OBJTYPE id> BY <vector expr>
-- The object which is the current value of <id> (an array or a picture formula) is modified as prescribed. The statement SHIFT A BY [2, 3] is equivalent to A: A SHIFTED BY [2, 3], except that the former affects also any formula containing A as subformula, while the latter creates a modified copy of A.
<
ANYTHING
SEP list>
== <
ANYTHING>
== <
ANYTHING>
<SEP> <
ANYTHING
SEP list>
SEP :: comma | semicolon
<comma> == ,
<semicolon> == ;
<expr> == <
TYPE expr>
<
TYPE expr> == <
TYPE id>
<
TYPE id> == <id>
-- whose current value is of type <type>
.
<vector expr> == <point expr>
GEOTYPE
:: formula | array | point | box
<
GEOTYPE expr>
== <
GEOTYPE expr> <geometric operation>
<geometric operation>
== SHIFTED BY <vector expr>
== REFLECTED ABOUT <I or J> = <int expr>
== ROTATED <Xwise> AROUND <point type>
== TRANSPOSED AROUND <point type>
== SAMPLED BY <vector expr>
-- When the value of the expression is a formula or array descriptor, these operations make a modified copy of it, so that evaluating the result at pixel [i, j] is equivalent to evaluating the original at T-1[i, j], where T is the given transformation. The copy of an array shares the elemnts with the original (only the descriptor is copied).
<I or J> == I | J
<XWise> == CLOCKWISE | COUNTERCLOCKWISE
-- Hopefully, the phrases 'SHIFTED BY', 'REFLECTED ABOUT I =', 'ROTATED COUNTERCLOCKWISE AROUND', etc. will each be denoted by single symbols (like v, r, etc).
<array expr>
== <box expr> ARRAY OF DEPTH <int expr>
== <array expr> CUT AT <box expr>
== <array expr> FROM LAYER <integer expr>
== <array expr> DEPTH <int expr>
== DISPLAY
<formula expr>
== NIL
-- NIL has depth zero.
== (<naked formula> <optional bottom layer> <optional depth>)
-- Builds a Piction for the given naked formula. The formula is actually evaluated only when SCANned.
== ELLIPSE [TOP: <point expr>,
CENTER: <point expr>, HALFWIDTH: <int expr>]
== RECTANGLE <box expr>
== POLYGON [<point expr>, <point expr>, <point expr comma list>]
== ...
<optional depth>
== DEPTH <int expr> | <empty>
-- The optional depth specifies the number of bits to retain in each pixel of the result; the default depends on the operation.
<optional bottom layer>
== FROM LAYER <int expr> | <empty>
-- The optional bottom layer specifies the layer of the naked formula that should become the lowest-order layer of the result. Should be non-negative (zero by default).
<naked formula>
== ALL <int expr>
-- A picture all of whose pixels have the specified value.
== <array expr>
-- Returns a Reader piction for the given array. When SCANned, the formula (A) will yield zeros outside the bounds of A, and its current contents inside them.
== OPAQUE <array expr>
-- similar to above, but appends a lowest-order layer that is 0 outside the bounds of the array, and 1 inside. Used with the OVER operator.
== REPEAT <array expr>
-- Returns a Pattern piction for the given array. When SCANned, the formula (REPEAT A) will yield a picture consisting of the contents of A repeated indefinitely over the whole plane. This infinite picture will coincide with A inside the array bounds.
== <unary op> <formula expr>
== <formula expr> <binary op> <formula expr>
== IF <formula expr> THEN <formula expr> ELSE <formula expr>
== IF <formula expr> THEN <formula expr>
-- Only evaluates the THEN (resp ELSE) part where the predicate is 1 (resp 0).
== <array expr> ← <formula expr>
-- Returns a Modifier for the left-hand side. When SCANned, the formula (A ← P) will evaluate P inside the bounds of A, and store the resulting pixels in A. The result of the assignment is NIL.
== <array expr> SWAP <array expr>
-- When SCANned, (A SWAP B) exchanges the contents of corresponding pixels of A and B (wherever both exist).
== <formula expr>, <formula expr comma list>
-- Stacks the pictures one on top of the other.
<unary op> == NOT | -
<binary op> == OR | AND | ERASE | XOR | EQV | + | > | = | MAX
| MIN | OVER | ...
-- ERASE
is same as AND NOT
. The operations +
, MAX
, MIN
, >
treat each pixel as an unsigned binary number.
-- The construct (A ← A OR B)
should be automatically optimized to the Modifier
(A ← * OR B)
; (A ← NOT A)
is optimized to (A ← NOT *)
etc.
-- (A OVER B) is equivalent to (IF A DEPTH 1 = 1 THEN (A FROM LAYER 1) ELSE B). That is, the lowest-order layer of A is treated as an opaque paper sheet, on which the rest of A is painted.
<OBJTYPE expr>
== COPY <OBJTYPE expr>
<proc expr>
== {<command semicolon list>}
== {<parameter comma list> | <command semicolon list>}
<parameter>
== <id>
== <id>: <parameter structure>
<parameter structure>
== [<parameter>, <parameter comma list>]
== POINT
-- equivalent to [I:INTEGER, J:INTEGER]
== BOX
-- equivalent to [LOW: POINT, HIGH: POINT]
== INT
== ARRAY
== FORMULA
== PROC
<proc call>
== <proc expr>
== <proc expr> <named expr comma list>
--
Matching an argument list to a parameter list means checking the consistency of both and building a third named list of values, the
result of the matching. First the lengths of the two lists are checked for equality, and then the following rules are applied to each corresponding pair of argument value and parameter:
1. If the argument has a name <id>
, the corresponding parameter name must also be <id>
.
2. If the argument has no name and <id>
is the parameter name, <id>:
is prefixed to the argument.
3. If the parameter has ARRAY, FORMULA, INT or PROC as its declared structure, the argument value <val> must be of the specified type.
4. If the parameter has structure [<p1>, <p2>, ...], the argument value should be a record [<a1>, <a2>, ...]. The lists <ai> and <pi> are matched recursively as above, and the result list <ri> is made into a record [<r1>, <r2>, ...] that is substituted for the argument value <val>.
5. The pair <id>: <val> as determined above is appended to the result list.
-- The final result of the matching is a list of named values <idi>: <vali>. Inside the procedure body, things happen as if the assignments <idi>: <vali> had been performed on entry. Note that the field names of <vali> are determined in part by the parameter description.
<named expr>
== <expr>
== <id>: <expr>
<record expr>
== [<named expr>, <named expr comma list>]
== []
<
TYPE expr>
== <record expr>.<id>
-- if the value of the <expr> has a field named <id> of type TYPE.
<point term>
== <record expr> -- conformable to [I: INT, J: INT]
== <int term> * <point term>
== ...
<point expr>
== <point term>
== <point expr> <add op> <point term>
== ...
<box expr>
== <record expr> -- conformable to [LOW: POINT, HIGH: POINT]
== <array expr>.BOUNDS
== ...
<int expr>
== ...
<int term>
== ...