Specification

Version 0.1

Contents

  1. Syntax
    1. Tokens
    2. Numeric Literals
    3. Program Structure
  2. Data Structures
    1. Stacks
    2. Object Types
  3. Execution
  4. Runtime Library
    1. Arithmetic and Logic
    2. Input
    3. State Interrogation
  5. Implementation Notes
    1. Data Structures and Types
    2. Pseudo-Recursion
    3. Anonymous Programs

1. Syntax

1.1. Tokens

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.

1.2. Numeric Literals

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.

1.3. Program Structure

program ::=
program ::= program element

element ::= instruction
element ::= numeric-literal
element ::= '[' program ']'

2. Data Structures

2.1. Stacks

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.

2.2. Object Types

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.

3. Execution

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.

4. Runtime Library

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.

4.1. Arithmetic and Logic

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:

  1. Pops an object from each stack.  Both must be numbers.  If the number popped from the main stack is less than the number popped from the auxiliary stack, then the next object from the auxiliary stack is popped and discarded.  Otherwise, the next object from the main stack is popped and discarded.
  2. Pops an object from each stack.  Both must be numbers.  Divides the number popped from the main stack by the number popped from the auxiliary stack, pushes the floor of the result to the main stack, and pushes the remainder to the auxiliary stack.  The results obey the relation dividend = divisor * quotient + remainder.
  3. Pops an object from each stack.  Both must be numbers.  Divides the number popped from the main stack by the number popped from the auxiliary stack, and pushes the result to the main stack.
  4. Pops an object from each stack.  Both must be numbers.  Subtracts the number popped from the auxiliary stack from the number popped from the main stack, and pushes the result to the main stack.

4.2. Input

The middle one the three packed stacks contains library procedures that perform input operations.  In order from top to bottom, they do the following:

  1. Reads a line of text from the standard input.  Pushes to the main stack a continued fraction made up of the Unicode values of successive characters, excluding the newline.  If end of file is reached without reading any more characters, nothing is pushed.  (This procedure does not distinguish whether the input ended with a newline.)
  2. Reads a character from the standard input.  Pushes its Unicode value to the main stack.  If end of file has been reached, nothing is pushed.

4.3. State Interrogation

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:

  1. Pushes to the main stack the number of objects that are currently on the auxiliary stack.
  2. Pushes to the main stack the number of objects that are currently on the main stack.  Because they are counted between this procedure being popped from the stack and the number being pushed, it will actually be one less than the number just before and just after the ! instruction is executed.

5. Implementation Notes

5.1. Data Structures and Types

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.

5.2. Pseudo-Recursion

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.

5.3. Anonymous Programs

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.