Repository navigation
Structure Syntax
AEON is built around bindings.
The canonical binding shape is:
key\identity\@{attributes}:type = value
That means every binding can have these visible parts:
- a key
- an optional structural identity
- optional attributes
- an optional datatype annotation
- a value
In transport-style documents, the shorter form is also allowed:
key = value
A document is a sequence of bindings.
name:string = "AEON"
build:number = 1
active:toggle = on
At the top level, bindings are normally separated by newlines.
Inside objects, bindings may be separated by newlines or commas depending on layout:
app:object = {
name:string = "AEON"
build:number = 1
}
app:object = { name:string = "AEON", build:number = 1 }
Keys may be bare or quoted.
Bare keys:
user = 1
build42 = 2
_internal = 3
Quoted keys:
"user name" = 1
"a.b" = 2
'display label' = 3
Use quoted keys when the key contains spaces, punctuation-as-data, or other characters that are not safe in a bare key.
Attributes attach metadata to a binding head or node head.
user@{role:string = "admin"}:object = {
id:number = 1
}
The canonical order is:
key\id\@{...}:type = value
Not:
key:type@{...} = value
Attributes themselves contain bindings, so they use the same key/value style as the rest of AEON.
An attribute block may appear only once in a binding, node, or anonymous child head. Put multiple metadata entries inside the one block:
temperature@{unit:string = "celsius", source:string = "sensor"}:number = 21.5
Structural identities give a binding or anonymous child value a stable occurrence identity without turning that identity into ordinary data.
person\person-1\:object = {
name\name-1\:string = "Ada"
}
items:list = [
\item-1\:string = "red",
\item-2\@{source:string = "user"}:string = "green"
]
The syntax is \id\. It appears after the key or anonymous child marker position, and before attributes and datatype:
field\field-1\@{source:string = "user"}:string = "value"
Structural identities are preserved in parser, AES, and canonical output metadata. They do not change the canonical path of the binding. Within one document, structural identity values must be unique.
These are invalid:
field@{source:string = "user"}\field-1\:string = "value" // identity after attributes
id = \field-1\ // identity as value
Datatype annotations come after the key and attributes, and before =.
name:string = "AEON"
count:number = 42
items:list = ["a", "b"]
Generic arguments must be datatype descriptors, for example list<string>
or list<custom[1.0]>. Bare numeric or string arguments such as custom<1.0>
or custom<"name"> are invalid in AEON, accepted AES, Telex and validated Film.
Some datatypes may also carry datatype clarifiers:
size:sep["x"] = ^300x250
grid:set["x", "y"] = ^100x200y300
Numeric clarifiers follow value-number normalization and remain exact:
1.00 becomes 1.0, while 9007199254740993 is not rounded. In 0.16.0,
TypeScript and pure-Python parser ASTs retain numeric clarifiers as
NumberLiteral nodes; quoted clarifiers remain strings. Core preserves the
claim, while GP and datatype consumers check its meaning. For example,
radix[2.00] and radix[2e0] denote integral base 2, but
radix[2.0000000000000001] must not be rounded to base 2.
Values can be:
- scalar literals
- objects
- lists
- tuples
- nodes
- references
Examples:
title:string = "Hello"
settings:object = { theme:string = "dark" }
items:list = ["a", "b", "c"]
point:tuple = (1, 2)
copy:number = ~source
Objects use { ... } and contain bindings:
user:object = {
name:string = "Pat"
age:number = 49
}
Lists use [ ... ] and preserve order:
items:list = ["a", "b", "c"]
List elements can also carry anonymous datatype and attribute heads:
measurements:list = [@{unit:string = "cm"}:number = 3, :string = "unknown"]
Tuples use ( ... ) and also preserve order, but are a distinct structural kind:
point:tuple = (12, 42)
rgb:triple<number, number, number> = (255, 128, 0)
triple is a reserved tuple alias for three-position values.
Tuple elements use the same anonymous child-head forms:
point:tuple = (:number = 12, :number = 42)
Nodes are tagged child-bearing values.
content:node = <article(
<h1("Hello")>,
<p("World")>
)>
They are useful when you want named structural elements rather than only plain object/list containers.
Node children may also be anonymously typed or attributed:
content:node = <article(
@{role:string = "title"}:string = "Hello",
<p("World")>
)>
Anonymous child heads are valid only inside lists, tuples, and node children. They are not root bindings or object members:
:number = 3 // invalid
obj:object = { :number = 3 } // invalid
AEON has explicit reference syntax:
copy = ~a
alias = ~>a
-
~ais a clone-style reference -
~>ais a pointer-style reference
AEON uses a small number of separators consistently:
-
=assigns values -
:introduces datatypes -
@{...}introduces attributes -
,separates entries in inline structures - newline separates bindings in multiline structures
This small surface is part of what makes AEON readable. The same visual pieces repeat across the language instead of each value family inventing its own grammar.
aeon:mode = "strict"
app:object = {
name:string = "Hello World"
version:number = 1
active:toggle = on
tags:list = ["demo", "wiki"]
}
This one example includes:
- a header binding
- a typed object
- nested typed bindings
- scalar and container values
For the full formal rules, see the structure syntax and value type references in the spec set.
This wiki is the approachable AEON guide. Implementation information lives at aeon.altopelago.com. Specification publication lives at aeonite.org. Normative language behavior lives in aeonite-org/aeonite-specs, and conformance behavior lives in aeonite-org/aeonite-cts.