Skip to content

Compiler Pipeline

Understanding LOVE-LANG's three-phase OPERA → MATERIALIZE → SIMULATE compilation workflow.

LOVE-LANG uses a three-phase compilation pipeline inspired by the SATOR/SOLID synthesis. Every programme passes through these three stages before execution.


Natural Language Intent
┌────────────────────┐
│ OPERA (Draft) │ .love pseudo-lisp
│ /logic/*.love │ Focus on causality
└────────────────────┘
┌────────────────────┐
│ MATERIALIZE (AST) │ .edn strict AST
│ /programmes/*.edn │ Correct bracket depths
└────────────────────┘
┌────────────────────┐
│ SIMULATE (Verify) │ .ini invariants
│ /simulations/*.ini│ LSP verification
└────────────────────┘
Execution

Principle: DIP — High-level policy must not depend on low-level syntax.

The OPERA phase is where you draft the intent of your programme in a relaxed pseudo-lisp format. The goal is to capture logical causality — what causes what — without worrying about bracket depths or type annotations yet.

/logic/user-auth.love
;; Draft: User Authentication Flow
(def raw_pass "password123")
(def user_name "alice")
;; 1. Hash the password
;; 2. Validate as UserPayload
;; 3. Write to database
(let [hashed_pass (@hash_pass raw_pass)
payload (UserPayload user_name hashed_pass)]
(@sql_write "users" payload))
  • Is the logical flow clear?
  • Have you identified all inputs and outputs?
  • Have you listed all effectors you need?
  • Does the logic make sense without any bracket depths?

Principle: OCP — Materialize the draft into strict execution nodes.

The MATERIALIZE phase translates your .love draft into a strict LOVE-LANG EDN AST with correct bracket depths applied.

/programmes/user-auth.edn
{:type :programme
:id :user-auth
:body
[raw_pass . "password123"
user_name . "alice"
; L2: Pure operation — hashing
hashed_pass . [[ @hash_pass [raw_pass] ]]
; L3: Type contract — validate structure
; L5: Side effect — write to database
[[[[[ @sql_write
"users"
[[[ UserPayload ]]] { :name [user_name] :hash [hashed_pass] }
]]]]]]}

When materializing, apply the correct level:

What you’re doingLevel
Referencing a variableL1 [ ]
Calling a pure functionL2 [[ ]]
Applying a type contractL3 [[[ ]]]
Setting a compiler directiveL4 [[[[ ]]]]
Calling an I/O effectorL5 [[[[[ ]]]]]

Principle: LSP — Define the invariant truths the programme must conserve.

The SIMULATE phase defines a .ini file of expected state changes that the programme must produce. This is verified by the @TENET checker at runtime.

/simulations/user-auth.ini
[expected_state]
db_table = "users"
inserted_keys = "name, hash"
hash_matches_raw = false
output_count = 1
error_state = false

Before using any L5 effector, you must declare it in your model.edn:

model.edn
{:system::state :listening
:effectors [{:id :sql_write :desc "Writes to DB" :arity 2}
{:id :hash_pass :desc "Hashes a string" :arity 1}
{:id :http_post :desc "HTTP POST request" :arity 2}
{:id :print :desc "Print to stdout" :arity 1}]}

  1. Draft the intent (OPERA)

    /logic/fibonacci.love
    ;; Compute the nth fibonacci number
    (def n 10)
    (let [result (@fibonacci n)]
    (@print result))
  2. Materialize the AST (MATERIALIZE)

    /programmes/fibonacci.edn
    {:type :programme
    :id :fibonacci
    :body
    [n . 10
    result . [[ @fibonacci [n] ]]
    [[[[[ @print [result] ]]]]]]}
  3. Define the invariants (SIMULATE)

    /simulations/fibonacci.ini
    [expected_state]
    output_value = 55
    error_state = false