There are four types of token in TLWNN:
!<>*\&@
[]
The whitespace characters are the space, carriage return, line feed and tab
characters, as well as all letters of the basic Latin alphabet.
Whitespace is required only to separate two numeric literals when the second
doesn't begin with -
, but may be used between tokens anywhere in
the code. TLWNN is a free-form language, i.e. all whitespace characters
are equivalent, and two or more consecutive whitespace characters are
equivalent to a single whitespace.
A numeric literal is denoted by the following syntax:
numeric-literal ::= digits numeric-literal ::= '-' digits numeric-literal ::= digits '/' digits numeric-literal ::= '-' digits '/' digits digits ::= decimal-digit digits ::= digits decimal-digit
The four syntaxes specify a positive integer, a negative integer, a positive rational number and a negative rational number respectively. No whitespace characters may occur within a numeric literal.
program ::= program ::= program element element ::= instruction element ::= numeric-literal element ::= '[' program ']'
The stack (last in first out buffer) is the basic data structure in TLWNN. There are two built-in stacks: the main stack and the auxiliary stack. Initially, the main stack contains TLWNN's runtime library (see section 4), and the auxiliary stack is empty.
Object type | Description | Effect of ! instruction |
---|---|---|
Rational number | TLWNN's only numeric type. Both the numerator and the denominator are of unlimited size, except that the denominator may never be zero. | Expands the number as a continued fraction, and outputs in order the Unicode character represented by each term of the expansion, beginning with the term that is the integer part of the original number. The number must be non-negative, and all terms of the expansion must be valid Unicode codepoints (no UTF-8 or UTF-16 surrogates). |
Packed stack | A stack that has been packed using the &
instruction. |
Pops each element from the packed stack in turn and pushes it to the auxiliary stack. The auxiliary stack will then contain the contents of the packed stack in the reverse order to that in which they were placed before the stack was packed. |
Code procedure | A procedure delimited using the [...]
notation. |
Executes the procedure. The code is executed in the same way as the program as a whole. The stacks are not reinitialised – the main program and all procedures called share the same stacks. |
Library procedure | A procedure in TLWNN's runtime library. | Executes the procedure. |
The elements (see section 1.3) of the program are processed in order from beginning to end.
Procedures and numeric literals are processed by pushing them to the main stack. The built-in instructions are processed according to the following table:
Instruction | Effect |
---|---|
! |
Pops an object from the top of the main stack, and then performs a type-dependent action on it as defined in section 2.2. |
< |
Moves an object from the top of the auxiliary stack to the top of the main stack. |
> |
Moves an object from the top of the main stack to the top of the auxiliary stack. |
* |
Duplicates the object at the top of the main stack. |
\ |
Swaps the objects at the respective tops of the main and auxiliary stacks. |
& |
Packs the auxiliary stack into a single object, which is then pushed to the main stack. The auxiliary stack will be left empty. |
@ |
Pops an object from the main stack, and discards it. |
The runtime library consists at present of three packed stacks, which are on the main stack when the program begins. Each packed stack contains a number of library procedures.
The topmost of the three packed stacks contains library procedures that perform arithmetic and logic operations. In order from top to bottom (and therefore bottom to top on the auxiliary stack after unpacking), they do the following:
The middle one the three packed stacks contains library procedures that perform input operations. In order from top to bottom, they do the following:
The bottommost of the three packed stacks contains library procedures that interrogate the state of the program's execution. In order from top to bottom, they do the following:
!
instruction is executed.An implementation may store data in any suitable manner. A method must be devised for representing TLWNN's dynamic typing in the implementation language.
One must consider how to store rational numbers to arbitrary precision. The method may be based on a library rational data type or an ad hoc method. An example of an ad hoc method is to implement the required rational arithmetic operations in terms of a big integer type used to store the numerator and denominator.
Memory management is something that will usually need to be thought about, especially considering that code procedures and packed stacks are inherently recursive structures. Typically, garbage collection would be used. It is possible to implement TLWNN without a garbage collector, by allocating and deallocating all objects as needed. However, the memory requirements would increase, as duplicating a procedure or packed stack would imply creating a second copy in memory of it, including any further procedures, packed stacks and numbers within.
Although procedures in TLWNN cannot call themselves, they can call copies of themselves, and two or more procedures can recursively call copies of each other. Indeed, this is the only way of implementing loops. As such, care must be taken to avoid running out of stack space while running a TLWNN program. This can be achieved using a generalised form of tail recursion optimisation, or by treating what is left to be executed as a flat list of instructions that is added to when a procedure is called.
Because TLWNN has no name, and no names are used within the language, TLWNN implementations should work in terms of programs having no names. This precludes the usual practice of taking the name of a program file on the command line. It is up to the implementer to decide on a method of acquiring code instead.
In a GUI-based interpreter, or indeed any interpreter that has its own code editing UI, this is straightforward. But an interpreter can indeed be written under the aforementioned constraint that communicates with the user only through the standard input and output streams. One possible method is to pick a character that is not legal in a TLWNN program and define it as an end-of-code marker, after which the program is executed and any further input is input to the TLWNN program.