This repository holds the tree conversion language used by the Whide IDE. The purpose of this language is to validate the formats of WHILE binary trees, and to convert them to a more human-friendly representation.
This language was loosely inspired by TypeScript’s type declaration syntax;
namely by its union operator (|), its any type, and its support of literal types.
|
Note
|
The term "type" is used in this document to refer to any valid atom or structure in the expression language. |
Formally, the language’s syntax can be expressed as the following context-free grammar:
ROOT = <ROOT.ROOT> //Binary tree
| ROOT[] //List of type ROOT
| [FIX_INT] //List of a fixed structure
| (ROOT) //Parentheses
| ROOT\|ROOT //Accept any of these types
| ATOM //Accept atomic values
| NAT //Accept all the natural numbers
//Fixed structure list
FIX_LST = [] //Empty list
| [FIX_INT] //Populated list
FIX_INT = ROOT,FIX_INT //Comma separated list of types
| ROOT //Final list element
| ... //Ignore any further elements
//Built-in atomic types
ATOM = any //Accept any type
| bool //Accept either 'true' or 'false'
| boolean //Same as 'bool'
| false //Accept the value 'false' ('nil')
| int //Integer
| nil //The value `nil` exactly
| true //Accept the value 'true' ('<nil.nil>')
//The natural numbers
NAT = 0|1|2|...The language contains several built-in atomic types. See Data Structures for a description of the tree forms of these types.
-
nilmatches the nil value -
anymatches any valid tree, and returns it unchanged -
falsematches the value 'false' (represented asnil) -
truematches the value 'true' (represented as<nil.nil>) -
bool/booleanmatches eithertrueorfalse. -
intmatches any valid number, and converts it to its numerical representation. E.g:-
nilbecomes0 -
<nil.nil>becomes1 -
<nil.<nil.nil>>becomes2
-
-
Number literals (0, 1, 2, …) match exactly that number
Alternative types can be easily specified using the | operator;
the string A|B first attempts to use type A, then type B.
For example int|any first attempts to convert the tree to an integer, but returns the tree unchanged if it is invalid.
Binary trees are represented in the form <L.R> where L is the type of the left-hand node, and R is the type of the right-hand node.
For example, <int.nil> matches any tree with an integer as the left child.
There are two supported types of list in the language; generic and fixed lists.
Generic lists (A[] or (A|B)[]) represent arbitrary length lists of a fixed type.
For example:
* A[] represents a list where each element is of type A
* (A|B)[] represents a list where each element is of type A or type B
|
Note
|
[] binds more tightly than | so A|B[] would be equivalent to A|(B[]).
|
Fixed structure lists match against lists which have a fixed length and each element has a known type:
-
[]represents the empty list. -
[A]represents a list with exactly one element of typeA. -
[A,B,C]represents a list with exactly 3 elements of typeA,B, andCrespectively. -
[A,B,…]represents a list with 2 or more elements, the first two of typesAandBrespectively, and any further elements of any type.
|
Note
|
Any type can be turned into a generic list by adding [] to the end; int[][] would match a list of lists of integers, and <int.int>[] would match a list of trees of integers.
|
If the provided tree does not match the conversion string, the converter returns the original tree in place of the mismatched token, with an error message describing the issue.
For example, the tree <<nil.nil>.nil> with conversion string int would return the tree <<nil.nil>.nil> with the error "not a valid number".
Similarly, conversion string <any.nil> acting on tree nil would return nil with the error message "expected a tree".
If the error is non-recoverable (e.g. a syntax error) the converter throws an exception.
| Conversion String | Input tree |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
In addition to the conversion language, this module also provides a stringify method.
This accepts a converted binary tree (the resulting type of the conversion) and converts it to a string representation.
The format used by this method is similar to that used in this documentation:
-
nilnodes are shown asnil -
Trees are shown as
<A.B.C…>whereA,B, andCare the stringified representations of each of the child nodes. In most cases there will be only 2 children. -
Lists are shown as
[A,B,C,…]whereA,B, andCare the stringified lsit elements. -
Numbers are shown as numbers (i.e. 1 is
1etc) -
Booleans are shown as
trueorfalse -
Other strings are shown as-is, wrapped in "double quotes"
Data structures are based on the types provided by Dr Bernhard Reus in his textbook Limits of Computation.
Lists are represented by a tree of depth N, where each "left" node at depth n represents the nth element in the list, and the final right-node is null, acting as a terminator.
Each integer n is represented as a list of nils of depth n.
The language accepts trees represented as objects in the following format.
Nodes can be null (representing nil) or point to left and right nodes.
type BinaryTree = {
left: BinaryTree,
right: BinaryTree,
}|null;The conversion produces a tree of type ConvertedBinaryTree.
This tree is represented differently to the input type due to having a more flexible format.
Every node may contain the following information:
-
A list of children (each a
ConvertedBinaryTree)There may be more than 2 children to any given node
-
A string value describing the node
-
A boolean describing whether the node represents a list rather than a tree
-
A string containing an error message for the current node
export type ConvertedBinaryTree = {
children?: ConvertedBinaryTree[],
value?: string|number|null,
list?: boolean,
error?: string,
};