Code

Homoiconicity

Lisp languages established homoiconicity as the principle that program structure uses the same forms as ordinary data. The Clojure reader is one modern example of code represented by persistent data structures. A program can therefore be examined and changed with ordinary data operations. Code is literally data rather than an isolated source format.

This principle extends to JSON-compatible values. Patches, schemas, queries, mutations, Expressions, and Macros all exist as data. Expressions and Macros are stored as definition entities with the same value model as other project knowledge. Every client can use the same stored behavior without implementing it in one preferred programming language.

Expressions are values

An Expression is an ordinary value that Stardust can validate, keep, and compile. An array represents a call, with the operator first and its arguments after it. A symbol that starts with ? reads an input variable supplied at evaluation time. The program holds neither its inputs nor its results.

dust13B
[* ?value 2]

A stored Expression lives under definitions/expressions and is exactly this bare program. A write is rejected unless the program compiles. Parameter and result types are inferred from usage: an operator's signature pins its operands, and Stardust does not autocast between types. Evaluation returns one value and its measured CPU time.

Expression and Macro values closely resemble a generic abstract syntax tree (AST). Each call array identifies an operation and contains its argument subtrees. This structure could become a compilation target for another authoring language. Stardust does not currently supply those language front ends.

No hidden source format sits behind this definition. DUST makes the value readable for people, while its JSON form keeps the same structure for clients.

Macros reshape code

An Expression receives values and calculates a value. A Macro receives unevaluated forms and produces new expression data before compilation. There is no operation that runs a Macro on its own. It expands in place wherever a query or Expression calls it.

A Macro stored under definitions/macros names its parameters and an expansion template:

dust58B
params  [value]
expands [+ {insert value} {insert value}]

The call is still data:

dust12B
[double ?x]

Expansion produces more expression data:

dust10B
[+ ?x ?x]

A {spread value} marker splices a list of forms into the surrounding call. Each Macro definition has revisions, and a query can pin a Macro title to one revision with a definitions entry. A query without a pin uses the current revision. Expansion rejects a cycle when the same Macro revision appears again in its own active expansion chain. Generated code then receives the same compiler and cost limits as directly authored code.

LuaJIT bytecode

Each Expression targets LuaJIT bytecode. Expressions used by queries and mutations have the same execution target, operators, and cost model as standalone Expressions. Predicates, derived values, projections, and window arguments therefore share one language boundary.

The language provides a fixed set of special forms, such as if, cond, let, and fn, and a fixed set of operators. It has no loop form and no named recursion. A function created with fn accepts at most eight parameters. Only higher-order operators such as map, filter, reduce, and sortBy evaluate these functions over supplied values.

Stored Expression or Macro data

Macro expansion with cycle check

Validated expression data

LuaJIT bytecode

Cost-counted execution

Result value

Operation cost

Termination is not acceptable cost

A program can halt and still perform too much work. It can examine many facts, produce many rows, or calculate across large values.

A thread CPU clock measures the runtime cost of Expression evaluation and query execution in nanoseconds. It includes native helpers and result construction. This cost does not count LuaJIT bytecode or machine instructions. When a caller sets a cost ceiling, execution checks the clock at work checkpoints and after the result is built.

A standalone evaluation with no ceiling is unlimited. A query always has a bound: an explicit maxCost, or a dynamic CPU time budget derived from the query's structure and rows handled. Queries can also set maxResults, maxBytes, and maxWorkRows. Each result reports its measured CPU time.

The expression language calculates values from supplied inputs and selected facts. It has no input, output, network, or system access. Eventbus covers coordination with connected systems.