Skip to content

Structure Syntax

Patrik edited this page Oct 17, 2026 · 7 revisions

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

Bindings

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

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

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

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

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

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, Lists, and Tuples

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

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

References

AEON has explicit reference syntax:

copy = ~a
alias = ~>a
  • ~a is a clone-style reference
  • ~>a is a pointer-style reference

Separators and Layout

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.

Quick Example

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.

Clone this wiki locally