Skip to main content

JSON That Thinks: How We Built a Turing-Complete Language Inside JSON

· 6 min read
Osamah Alghanmi
Co-Founder & Technical Lead

What if JSON could express logic, not just data? What if your configuration files could make decisions?

We built a Turing-complete programming language that's a strict subset of JSON. No new syntax. No custom parser. Every Almadar program is valid JSON.

Here's why — and how.

Entity(Matter)Page(Space)Trait(Energy)idleactivehas_traitrenderstransition
Orbital Unit = Entity + Traits + Pages

The Problem with Configuration Languages

The industry has been circling this problem for decades:

  • YAML — Great for data, terrible for logic. Leads to templating nightmares (looking at you, Helm charts).
  • HCL (Terraform) — Invented a new syntax. Now you need a parser, an LSP, editor plugins, and training.
  • Dhall — Principled but niche. Few developers know it exists.
  • Jsonnet/CUE — JSON-like but not JSON. Close, but they break tooling compatibility.

The pattern: every time someone needs logic in configuration, they invent a new language. New parser. New tooling. New learning curve.

We asked: what if we didn't?

The Insight: S-Expressions Are Already JSON

In 1958, John McCarthy invented S-expressions for Lisp:

(+ 1 2)
(if (> x 10) "big" "small")

S-expressions are just nested lists with a function in the first position.

JSON arrays are... nested lists.

["+", 1, 2]
["if", [">", "x", 10], "big", "small"]

S-expressions are JSON arrays. We didn't need to invent syntax. We needed to interpret what was already there.

How Almadar Works

An Almadar program is a JSON object containing entities, state machines, and logic expressed as S-expressions:

{
"name": "ApprovalWorkflow",
"orbitals": [{
"entity": {
"name": "Request",
"fields": [
{ "name": "amount", "type": "number" },
{ "name": "status", "type": "enum", "values": ["pending", "approved", "rejected"] }
]
},
"traits": [{
"name": "ApprovalTrait",
"linkedEntity": "Request",
"stateMachine": {
"states": [
{ "name": "Pending", "isInitial": true },
{ "name": "Approved" },
{ "name": "Rejected" }
],
"transitions": [{
"from": "Pending",
"to": "Approved",
"event": "APPROVE",
"guard": ["and",
[">=", "@user.roleLevel", 3],
["<", "@entity.amount", 10000]
],
"effects": [
["set", "@entity.status", "approved"],
["set", "@entity.approvedAt", "@now"],
["emit", "REQUEST_APPROVED"]
]
}]
}
}]
}]
}

The logic (guard, effects) is pure S-expressions. The structure (entities, states, transitions) is pure JSON. Together, they form a complete program.

Why This Matters: Homoiconicity

Homoiconicity means "code and data share the same representation." It's Lisp's superpower, and now it's JSON's.

Because Almadar programs are JSON:

1. Every JSON tool works on Almadar programs.

# Validate with any JSON validator
cat app.orb | python -m json.tool

# Query with jq
jq '.orbitals[].traits[].stateMachine.states' app.orb

# Diff two schemas
diff <(jq -S . v1.orb) <(jq -S . v2.orb)

2. AI can generate and modify programs.

LLMs are excellent at generating structured JSON. They're terrible at generating correct syntax in novel languages. By staying in JSON, Almadar programs are trivially generated by any model that can produce valid JSON — which is all of them.

3. Programs are data you can transform.

Want to add audit logging to every transition? Write a JSON transformation:

schema.orbitals.forEach(orbital =>
orbital.traits.forEach(trait =>
trait.stateMachine.transitions.forEach(t => {
t.effects.push(["log", "info", `Transition: ${t.from}${t.to}`]);
})
)
);

No AST parsing. No compiler plugins. Just data manipulation.

The Turing Completeness Argument

A language is Turing-complete if it can simulate any computation. Almadar achieves this through:

  1. State machines — Arbitrary state, transitions, and memory (entity fields)
  2. S-expression guards — Boolean logic with arbitrary nesting (and, or, not, comparison operators)
  3. S-expression effects — Side effects including set (memory writes), emit (communication), and if (branching)
  4. Cross-orbital events — Inter-process communication (emit/listen)
  5. Recursion via self-transitions — A state can transition to itself with modified entity fields

A self-loop with conditional effects and entity field updates is equivalent to a while loop with mutable state:

{
"from": "Computing",
"to": "Computing",
"event": "TICK",
"guard": [">", "@entity.counter", 0],
"effects": [
["set", "@entity.counter", ["-", "@entity.counter", 1]],
["set", "@entity.result", ["+", "@entity.result", "@entity.counter"]],
["emit", "TICK"]
]
}

This computes the sum of numbers from N to 0. The state machine is the loop. The entity is the memory. The guard is the termination condition.

Comparison: The Configuration Language Landscape

LanguageJSON CompatibleLogic SupportTuring CompleteTooling
JSONYesNoNoUniversal
YAMLNoNoNoWide
JsonnetPartialYesYesLimited
DhallNoYesNo (intentionally)Minimal
CUEPartialYesNo (intentionally)Growing
HCLNoLimitedNoTerraform
AlmadarYesYesYesUniversal (JSON)

Almadar is the only Turing-complete language that's a strict subset of JSON. This means every JSON tool, every JSON API, every JSON database, and every JSON-aware LLM works with Almadar programs out of the box.

How S-Expressions Execute

The Almadar compiler evaluates S-expressions recursively:

["and",
[">=", "@user.roleLevel", 3],
["<", "@entity.amount", 10000]
]

Evaluation steps:

  1. Resolve bindings: @user.roleLevel5, @entity.amount7500
  2. Evaluate inner expressions: [">=", 5, 3]true, ["<", 7500, 10000]true
  3. Evaluate outer expression: ["and", true, true]true

The compiler optimizes repeated guard evaluations internally — common in UIs where the same conditions are checked on every render — so evaluation is fast even for complex expressions.

Extending Without Versioning

Adding new operators to Almadar requires no schema version bump:

["geo-distance", "@entity.location", "@payload.target"]

If the evaluator knows geo-distance, it evaluates it. If not, it returns an error with a clear message. New operators are additive — they never break existing programs.

This is the same extensibility model that made Lisp survive for 65 years.

The Tradeoff: Verbosity

Let's be honest. S-expressions in JSON are more verbose than a custom syntax would be:

-- Hypothetical custom syntax
guard: user.roleLevel >= 3 and entity.amount < 10000

-- Almadar (actual)
"guard": ["and", [">=", "@user.roleLevel", 3], ["<", "@entity.amount", 10000]]

The custom syntax is 50 characters. The Almadar version is 75. That's ~50% more characters.

We believe this tradeoff is worth it because:

  • You never need a custom parser
  • You never need a custom LSP
  • You never need to teach your team a new syntax
  • AI generates it correctly on the first try
  • Every JSON tool in existence works on your programs

Verbosity is a one-time cost. Tooling compatibility is forever.

The Takeaway

Every decade, someone invents a new configuration language to solve the "logic in config" problem. Each one adds a parser, a learning curve, and an ecosystem to maintain.

Almadar takes a different path: no new syntax. Just JSON arrays interpreted as S-expressions, combined with state machines for control flow and entities for memory.

The result is a Turing-complete language that:

  • Every JSON tool understands
  • Every LLM can generate
  • Every developer can read (it's just arrays)
  • Compiles to TypeScript, Rust, and Python

Because the best syntax might be the one you already know.

Explore the S-expression standard library to see all available operators.

Recent Posts