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:
+346
-3
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user