runtime + compiler: dharma, match, cgi blocks, VBD, agentic LLM

Two parallel agent sweeps closing the remaining structural gaps.

== Compiler completions ==

- match codegen: lowers Match into GCC/Clang statement-expression
  ({ ... }). Patterns: Wildcard, Binding, LitInt (==), LitStr
  (str_eq), LitBool. Per-match unique label via state counter.
  Verified: classify(0)→"zero", classify(1)→"one", classify(7)→"other".

- cgi block parsing: `cgi "name" { dharma_id, principal, network,
  engram }` → CgiBlock AST node → el_cgi_init() emitted as the first
  call in main() after el_runtime_init_args. Multiple cgi blocks per
  program emit a #error directive. Missing optional fields → EL_NULL.

- VBD compile-time enforcement: parser attaches `decorator: <name>`
  to FnDef. Codegen recursively walks fn bodies (Call/BinOp/Not/Neg/
  Field/Index/Try/Array/Map/If/For/Match plus Let/Return/Expr/While/
  For). If a non-@manager function calls dharma_emit or dharma_field,
  emit `#error "VBD violation: ... fn '<name>'"` before the function
  body. Verified: @engine fn calling dharma_emit → cc fails with the
  message. @manager fn calling dharma_emit → compiles clean.

Three-stage closure: stage1.c == stage2.c == stage3.c (2791 lines
each, byte-identical). dist/platform/elc rebuilt at 165 KB; .prev5
preserved.

== Runtime completions ==

- Real dharma_* primitives, no more stubs. Channel registry,
  request/response over HTTP, network-wide spreading activation,
  fire-and-forget event emission, blocking dharma_field with
  pthread_cond_timedwait (30s default), Hebbian relationship
  weights stored as Engram edges between dharma:self and
  dharma:peer:<id>, sorted-by-weight peer list. URL/ID arrays
  snapshotted before network I/O so mutexes never block on socket.

- New public C contract: el_runtime_dharma_event_arrive(type, payload,
  source) — application HTTP handler calls this when /dharma/event
  arrives, runtime broadcasts on _dharma_event_cv. Keeps the HTTP
  server generic; events flow through the application's router.

- llm_call_agentic real multi-turn loop. Tool registry (mutex-
  protected, dlsym-resolved, mirroring http_set_handler). Loop:
  build request with tools+messages → POST → dispatch on stop_reason.
  end_turn → return text. max_tokens → text + "[truncated]". tool_use
  → walk content[], call registered handler per block, build
  tool_result message, append to conversation, loop. Iteration cap
  10. Tools not registered return {"error":"tool not registered: X"}
  with is_error: true.

- New builtin: llm_register_tool(name, handler_fn_name).

Compile clean: cc -std=c11 -Wall -Wextra -c → zero warnings, zero
errors. Smoke test exercises every new dharma_* primitive +
llm_register_tool round-trip.

Runtime grew 3309→4079 lines (.c, ~155 KB), 312→342 lines (.h).

== Integration ==

Engram rebuilt against the new runtime: 130 KB binary, daemon
swapped on :8742 cleanly, /health and /api/stats both returning
correctly under launchd. No regressions.

== Status of "planned" items in language.md ==

- match codegen → IMPLEMENTED
- cgi block parsing → IMPLEMENTED
- VBD enforcement → IMPLEMENTED
- % operator → IMPLEMENTED (earlier today)
- vessel keyword → lexed (codegen uses package compatible)
- activate construct → still planned (low priority; engram_activate
  builtin covers the use case for now)
- sealed block → still planned
- dharma_emit fanout parallelization → potential future work, current
  serial behavior matches spec
This commit is contained in:
Will Anderson
2026-04-30 14:06:19 -05:00
parent fac24435ce
commit 12d5e7777e
8 changed files with 1955 additions and 97 deletions
+346 -3
View File
@@ -1272,12 +1272,93 @@ fn parse_stmt(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
return make_result({ "stmt": "For", "item": item_name, "list": list_expr, "body": body }, p)
}
// @decorator skip and parse next stmt
// @decorator capture decorator name and attach to following stmt
if k == "At" {
let p = pos + 1
// skip decorator name
let dec_name = tok_value(tokens, p)
let p = p + 1
return parse_stmt(tokens, p)
let r = parse_stmt(tokens, p)
let inner = r["node"]
let p2 = r["pos"]
let inner_kind: String = inner["stmt"]
if str_eq(inner_kind, "FnDef") {
let with_dec = {
"stmt": "FnDef",
"name": inner["name"],
"params": inner["params"],
"body": inner["body"],
"ret_type": inner["ret_type"],
"decorator": dec_name
}
return make_result(with_dec, p2)
}
return r
}
// cgi block: cgi "name" { field: "val", ... }
if k == "Cgi" {
let p = pos + 1
let name = tok_value(tokens, p)
let p = p + 1
let p = expect(tokens, p, "LBrace")
let dharma_id = ""
let principal = ""
let network = ""
let engram = ""
let has_dharma_id = false
let has_principal = false
let has_network = false
let has_engram = false
let running = true
while running {
let k2 = tok_kind(tokens, p)
if k2 == "RBrace" {
let running = false
} else {
if k2 == "Eof" {
let running = false
} else {
let fname = tok_value(tokens, p)
let p = p + 1
let p = expect(tokens, p, "Colon")
let fval = tok_value(tokens, p)
let p = p + 1
if str_eq(fname, "dharma_id") {
let dharma_id = fval
let has_dharma_id = true
}
if str_eq(fname, "principal") {
let principal = fval
let has_principal = true
}
if str_eq(fname, "network") {
let network = fval
let has_network = true
}
if str_eq(fname, "engram") {
let engram = fval
let has_engram = true
}
let k3 = tok_kind(tokens, p)
if k3 == "Comma" {
let p = p + 1
}
}
}
}
let p = expect(tokens, p, "RBrace")
return make_result({
"stmt": "CgiBlock",
"name": name,
"dharma_id": dharma_id,
"principal": principal,
"network": network,
"engram": engram,
"has_dharma_id": has_dharma_id,
"has_principal": has_principal,
"has_network": has_network,
"has_engram": has_engram
}, p)
}
// bare expression or if/match statement
@@ -1752,9 +1833,82 @@ fn cg_expr(expr: Map<String, Any>) -> String {
return "/* if-expr */ ((" + cond_c + ") ? (el_val_t)1 : (el_val_t)0)"
}
if kind == "Match" {
return cg_match(expr)
}
"EL_NULL"
}
// Match codegen
//
// Lower a match expression to a GCC/Clang statement-expression.
// A unique label suffix is allocated per match via state_set("__match_counter").
fn next_match_id() -> String {
let csv: String = state_get("__match_counter")
let n = 0
if !str_eq(csv, "") {
let n = str_to_int(csv)
}
let n = n + 1
state_set("__match_counter", native_int_to_str(n))
native_int_to_str(n)
}
fn cg_match(expr: Map<String, Any>) -> String {
let subject = expr["subject"]
let arms = expr["arms"]
let subj_c: String = cg_expr(subject)
let id: String = next_match_id()
let subj_var: String = "_match_subj_" + id
let result_var: String = "_match_result_" + id
let done_label: String = "_match_done_" + id
let out: String = "({ el_val_t " + subj_var + " = " + subj_c + "; el_val_t " + result_var + " = 0; "
let n: Int = native_list_len(arms)
let i = 0
while i < n {
let arm = native_list_get(arms, i)
let pat = arm["pattern"]
let body = arm["body"]
let pkind: String = pat["pattern"]
let body_c: String = cg_expr(body)
if str_eq(pkind, "Wildcard") {
let out = out + "{ " + result_var + " = (" + body_c + "); goto " + done_label + "; } "
} else {
if str_eq(pkind, "Binding") {
let bname: String = pat["name"]
let out = out + "{ el_val_t " + bname + " = " + subj_var + "; " + result_var + " = (" + body_c + "); goto " + done_label + "; } "
} else {
if str_eq(pkind, "LitInt") {
let v: String = pat["value"]
let out = out + "if (" + subj_var + " == " + v + ") { " + result_var + " = (" + body_c + "); goto " + done_label + "; } "
} else {
if str_eq(pkind, "LitStr") {
let v: String = pat["value"]
let out = out + "if (str_eq(" + subj_var + ", EL_STR(" + c_str_lit(v) + "))) { " + result_var + " = (" + body_c + "); goto " + done_label + "; } "
} else {
if str_eq(pkind, "LitBool") {
let v: String = pat["value"]
let bv = "0"
if str_eq(v, "true") {
let bv = "1"
}
let out = out + "if (" + subj_var + " == " + bv + ") { " + result_var + " = (" + body_c + "); goto " + done_label + "; } "
} else {
// unknown pattern wildcard
let out = out + "{ " + result_var + " = (" + body_c + "); goto " + done_label + "; } "
}
}
}
}
}
let i = i + 1
}
let out = out + done_label + ":; " + result_var + "; })"
out
}
// Variable scope tracking
//
// El allows `let x = expr` to both declare and reassign x in the same scope.
@@ -1857,6 +2011,7 @@ fn cg_stmt(stmt: Map<String, Any>, indent: String, declared: [String]) -> [Strin
if kind == "TypeDef" { return declared }
if kind == "EnumDef" { return declared }
if kind == "Import" { return declared }
if kind == "CgiBlock" { return declared }
declared
}
@@ -2086,6 +2241,15 @@ fn cg_fn(stmt: Map<String, Any>) -> Void {
let body = stmt["body"]
let ret_type: String = stmt["ret_type"]
let params_c: String = params_to_c(params)
// VBD role enforcement: dharma_emit / dharma_field may only be called
// from @manager-decorated functions. Surface violations to the C compiler
// via #error directives emitted before the function definition.
let decorator: String = stmt["decorator"]
if vbd_has_restricted_call(body) {
if !str_eq(decorator, "manager") {
emit_line("#error \"VBD violation: dharma_emit/dharma_field called from non-@manager fn '" + fn_name + "'\"")
}
}
// Seed the per-function int-name set so the `+` codegen can dispatch
// arithmetic vs concat on type-annotated identifiers.
build_int_names_for_params(params)
@@ -2127,12 +2291,174 @@ fn is_top_level_decl(stmt: Map<String, Any>) -> Bool {
if kind == "TypeDef" { return true }
if kind == "EnumDef" { return true }
if kind == "Import" { return true }
if kind == "CgiBlock" { return true }
false
}
// Format a string-or-EL_NULL argument for el_cgi_init.
fn cgi_arg(value: String, has_value: Bool) -> String {
if has_value {
return "EL_STR(" + c_str_lit(value) + ")"
}
return "EL_NULL"
}
// VBD role enforcement
//
// Scan a function body for direct calls to DHARMA-restricted builtins
// (dharma_emit, dharma_field). These may only appear inside @manager fns.
fn vbd_is_restricted_name(name: String) -> Bool {
if str_eq(name, "dharma_emit") { return true }
if str_eq(name, "dharma_field") { return true }
false
}
fn vbd_expr_has_restricted_call(expr: Map<String, Any>) -> Bool {
let kind: String = expr["expr"]
if str_eq(kind, "Call") {
let func = expr["func"]
let fk: String = func["expr"]
if str_eq(fk, "Ident") {
let fname: String = func["name"]
if vbd_is_restricted_name(fname) { return true }
}
if vbd_expr_has_restricted_call(func) { return true }
let args = expr["args"]
let an: Int = native_list_len(args)
let ai = 0
while ai < an {
let a = native_list_get(args, ai)
if vbd_expr_has_restricted_call(a) { return true }
let ai = ai + 1
}
return false
}
if str_eq(kind, "BinOp") {
let l = expr["left"]
let r = expr["right"]
if vbd_expr_has_restricted_call(l) { return true }
if vbd_expr_has_restricted_call(r) { return true }
return false
}
if str_eq(kind, "Not") {
return vbd_expr_has_restricted_call(expr["inner"])
}
if str_eq(kind, "Neg") {
return vbd_expr_has_restricted_call(expr["inner"])
}
if str_eq(kind, "Field") {
return vbd_expr_has_restricted_call(expr["object"])
}
if str_eq(kind, "Index") {
if vbd_expr_has_restricted_call(expr["object"]) { return true }
if vbd_expr_has_restricted_call(expr["index"]) { return true }
return false
}
if str_eq(kind, "Try") {
return vbd_expr_has_restricted_call(expr["inner"])
}
if str_eq(kind, "Array") {
let elems = expr["elems"]
let n: Int = native_list_len(elems)
let i = 0
while i < n {
let e = native_list_get(elems, i)
if vbd_expr_has_restricted_call(e) { return true }
let i = i + 1
}
return false
}
if str_eq(kind, "Map") {
let pairs = expr["pairs"]
let n: Int = native_list_len(pairs)
let i = 0
while i < n {
let pair = native_list_get(pairs, i)
let v = pair["value"]
if vbd_expr_has_restricted_call(v) { return true }
let i = i + 1
}
return false
}
if str_eq(kind, "If") {
if vbd_expr_has_restricted_call(expr["cond"]) { return true }
if vbd_has_restricted_call(expr["then"]) { return true }
if vbd_has_restricted_call(expr["else"]) { return true }
return false
}
if str_eq(kind, "For") {
if vbd_expr_has_restricted_call(expr["list"]) { return true }
if vbd_has_restricted_call(expr["body"]) { return true }
return false
}
if str_eq(kind, "Match") {
if vbd_expr_has_restricted_call(expr["subject"]) { return true }
let arms = expr["arms"]
let n: Int = native_list_len(arms)
let i = 0
while i < n {
let arm = native_list_get(arms, i)
let body = arm["body"]
if vbd_expr_has_restricted_call(body) { return true }
let i = i + 1
}
return false
}
false
}
fn vbd_has_restricted_call(stmts: [Map<String, Any>]) -> Bool {
let n: Int = native_list_len(stmts)
let i = 0
while i < n {
let s = native_list_get(stmts, i)
let sk: String = s["stmt"]
if str_eq(sk, "Let") {
if vbd_expr_has_restricted_call(s["value"]) { return true }
}
if str_eq(sk, "Return") {
if vbd_expr_has_restricted_call(s["value"]) { return true }
}
if str_eq(sk, "Expr") {
if vbd_expr_has_restricted_call(s["value"]) { return true }
}
if str_eq(sk, "While") {
if vbd_expr_has_restricted_call(s["cond"]) { return true }
if vbd_has_restricted_call(s["body"]) { return true }
}
if str_eq(sk, "For") {
if vbd_expr_has_restricted_call(s["list"]) { return true }
if vbd_has_restricted_call(s["body"]) { return true }
}
let i = i + 1
}
false
}
// Entry point
fn codegen(stmts: [Map<String, Any>], source: String) -> String {
// Detect cgi blocks: at most one allowed. Emit a #error if more than one.
let n_top: Int = native_list_len(stmts)
let cgi_count = 0
let cgi_block: Map<String, Any> = { "stmt": "None" }
let ti = 0
while ti < n_top {
let s = native_list_get(stmts, ti)
let sk: String = s["stmt"]
if str_eq(sk, "CgiBlock") {
let cgi_count = cgi_count + 1
if cgi_count == 1 {
let cgi_block = s
}
}
let ti = ti + 1
}
if cgi_count > 1 {
emit_line("#error \"El: multiple cgi blocks in program (only one allowed)\"")
}
// Preamble
emit_line("#include <stdint.h>")
emit_line("#include <stdlib.h>")
@@ -2170,6 +2496,23 @@ fn codegen(stmts: [Map<String, Any>], source: String) -> String {
// main()
emit_line("int main(int argc, char** argv) {")
emit_line(" el_runtime_init_args(argc, argv);")
if cgi_count >= 1 {
let cname: String = cgi_block["name"]
let cdid: String = cgi_block["dharma_id"]
let cprin: String = cgi_block["principal"]
let cnet: String = cgi_block["network"]
let ceng: String = cgi_block["engram"]
let has_did: Bool = cgi_block["has_dharma_id"]
let has_prin: Bool = cgi_block["has_principal"]
let has_net: Bool = cgi_block["has_network"]
let has_eng: Bool = cgi_block["has_engram"]
let arg_name: String = "EL_STR(" + c_str_lit(cname) + ")"
let arg_did: String = cgi_arg(cdid, has_did)
let arg_prin: String = cgi_arg(cprin, has_prin)
let arg_net: String = cgi_arg(cnet, has_net)
let arg_eng: String = cgi_arg(ceng, has_eng)
emit_line(" el_cgi_init(" + arg_name + ", " + arg_did + ", " + arg_prin + ", " + arg_net + ", " + arg_eng + ");")
}
let main_decl = native_list_empty()
let i = 0
while i < n {