Skip to content

Ast Schema

This document specifies the structured JSON representation of the fully-typed Axon Abstract Syntax Tree (AST). The JSON format allows external AI agents and IDE language servers (axon-agent) to query, analyze, and manipulate codebases without parsing raw text.


A serialized module forms a single JSON object containing metadata, the string table, the type table, and the module’s root AST node:

{
"version": 1,
"filepath": "tests/programs/arith.axs",
"strings": [
"",
"main",
"x",
"y"
],
"types": [
{ "id": 0, "kind": "void", "size": 0, "align": 0 },
{ "id": 1, "kind": "bool", "size": 1, "align": 1 },
{ "id": 2, "kind": "i64", "size": 8, "align": 8 }
],
"root": {
"kind": "ND_MODULE",
"type": 0,
"line": 1,
"col": 1,
"is_pub": false,
"module": {
"decl_count": 1,
"decls": [
{ ... }
]
}
}
}
  • version: integer schema version (currently 1).
  • filepath: path to the primary source file parsed.
  • strings: string table array, mapping StrId index -> raw string value. String index 0 is always "".
  • types: type table array, mapping TypeId index -> type descriptor. Type index 0 is always TY_VOID (representing invalid/unresolved or void).
  • root: the recursive AST root node of kind ND_MODULE.

Types are serialized with their type kind, size, alignment, and any composite parameters.

  • void, bool, never
  • i8, i16, i32, i64
  • u8, u16, u32, u64
  • f32, f64
  • string
  • Pointer (TY_PTR): { "id": 5, "kind": "ptr", "size": 8, "align": 8, "ptr": { "pointee": 2 } }
  • Array (TY_ARRAY): { "id": 6, "kind": "array", "size": 80, "align": 8, "array": { "elem": 2, "count": 10 } }
  • Slice (TY_SLICE): { "id": 7, "kind": "slice", "size": 16, "align": 8, "slice": { "elem": 2 } }
  • Vector (TY_VEC): { "id": 8, "kind": "vec", "size": 24, "align": 8, "vec": { "elem": 2 } }
  • Map (TY_MAP): { "id": 9, "kind": "map", "size": 32, "align": 8, "map": { "key": 2, "val": 2 } }
  • Struct (TY_STRUCT):
    {
    "id": 10,
    "kind": "struct",
    "size": 16,
    "align": 8,
    "struct": {
    "name": "Point",
    "fields": [
    { "name": "x", "type": 2, "offset": 0 },
    { "name": "y", "type": 2, "offset": 8 }
    ]
    }
    }
  • Function Signature (TY_FUNC) / Function Pointer (TY_FNPTR):
    {
    "id": 11,
    "kind": "func",
    "size": 8,
    "align": 8,
    "func": {
    "ret": 0,
    "params": [2, 2],
    "is_variadic": false
    }
    }

Every node has a common header:

  • kind: string representing the NodeKind (e.g. "ND_INT_LIT", "ND_BINOP").
  • type: type index (TypeId) of the node’s evaluated type.
  • line & col: 1-based source position.
  • is_pub: boolean indicating if the declaration has public visibility.

Depending on kind, a specific details object is present:

  • ND_INT_LIT: { "int_lit": { "value": 42, "lit_type": 2 } }
  • ND_FLOAT_LIT: { "float_lit": { "value": 3.14, "lit_type": 12 } }
  • ND_BOOL_LIT: { "bool_lit": { "value": true } }
  • ND_STR_LIT: { "str_lit": { "value": "hello" } }
  • ND_VAR: { "var": { "name": "x" } }
  • ND_LET:
    {
    "let": {
    "name": "x",
    "var_type": 2,
    "init": { "kind": "ND_INT_LIT", ... },
    "body": { "kind": "ND_BLOCK", ... }
    }
    }
  • ND_BINOP:
    {
    "binop": {
    "op": "+",
    "lhs": { "kind": "ND_VAR", ... },
    "rhs": { "kind": "ND_INT_LIT", ... }
    }
    }
  • ND_UNOP:
    {
    "unop": {
    "op": "-",
    "operand": { "kind": "ND_VAR", ... }
    }
    }
  • ND_CALL:
    {
    "call": {
    "callee": "add",
    "module_alias": "math",
    "args": [
    { "kind": "ND_VAR", ... }
    ]
    }
    }
  • ND_IF:
    {
    "if_expr": {
    "cond": { ... },
    "then_br": { ... },
    "else_br": { ... } // null if none
    }
    }
  • ND_WHILE:
    {
    "while_loop": {
    "cond": { ... },
    "body": { ... }
    }
    }
  • ND_BLOCK:
    {
    "block": {
    "stmts": [
    { ... }
    ]
    }
    }
  • ND_FN:
    {
    "fn": {
    "name": "add",
    "params": [
    { "name": "x", "type": 2 },
    { "name": "y", "type": 2 }
    ],
    "return_type": 2,
    "body": { ... }
    }
    }