runtime: HTTP, in-process graph store, LLM, fs_list

Batches 2/3/4 of the runtime extension. The runtime grew from 1620
to 3112 lines (.c) and 247 to 286 lines (.h) — adding 27 new or
real-implementation builtins and replacing every batch-1 stub.

Batch 2 — HTTP / fs (8 builtins)
- http_get, http_post: replaced stubs with real libcurl client.
  Network errors return JSON {"error":"..."} so callers can detect.
- http_post_json: sets Content-Type: application/json.
- http_get_with_headers, http_post_with_headers: ElMap → headers.
- http_post_form_auth: form-urlencoded + Authorization header
  (Stripe-style API calls).
- http_serve: replaced stub with real POSIX-socket server, threaded,
  capped at 64 concurrent connections. Auto-detects content type
  (HTML / JSON / plain). Handler dispatch via named registry.
- fs_list: directory listing via opendir/readdir.

Batch 3 — In-process graph store (14 builtins)
- engram_node, engram_node_full: create node, returns UUID.
- engram_get_node, engram_forget, engram_node_count.
- engram_strengthen: Hebbian potentiation (+0.05, clamp 1.0,
  bumps last_activated).
- engram_search, engram_scan_nodes: text search, paginated scan.
- engram_connect, engram_edge_between, engram_neighbors,
  engram_neighbors_filtered, engram_edge_count.
- engram_activate: real spreading-activation algorithm.
  BFS to depth, max-activation merge across paths, decay 0.7/hop,
  multiplied by node confidence, filtered by epistemic_confidence
  ≥ 0.2 (refresh threshold), sorted desc.
- engram_save, engram_load: JSON snapshot persistence.

Batch 4 — LLM (5 builtins)
- llm_call, llm_call_system: Anthropic /v1/messages via libcurl.
  ANTHROPIC_API_KEY from env. Default model claude-sonnet-4-5.
- llm_vision: adds image content block. URL / base64 / file path
  detected by prefix.
- llm_models: returns the available model list.
- llm_call_agentic: stubbed with TODO (single-turn fallback to
  llm_call_system); full tool_use loop is the next iteration.

Codegen fix: emit Float literals as `el_from_float(<v>)`. Without
the wrapper, C implicit conversion truncates 0.8 to 0 when passed to
a builtin expecting el_val_t. Float helpers moved to el_runtime.h
so generated programs can call them.

Compile-time
- cc -std=c11 -Wall -Wextra -c el_runtime.c → no errors, no warnings.
- Link requires -lcurl -lpthread (documented in header comment).

Verified end-to-end
- engram_node × 2, engram_connect, engram_activate("Hebbian", 2)
  returns 2 activated nodes with correct epistemic confidence.
- http_get("https://httpbin.org/get") returns 259-byte JSON live.
- Self-host closure: stage1 vs stage2 byte-identical against the
  new runtime.
- engram_save → engram_load round-trip preserves graph.

dist/platform/elc rebuilt against the new runtime (147 KB, up from
94 KB due to libcurl link). .prev2 preserves the prior binary.
This commit is contained in:
Will Anderson
2026-04-30 13:29:31 -05:00
parent 9fca4dc3ce
commit 951b8d574b
7 changed files with 1609 additions and 67 deletions
+5 -5
View File
@@ -1,5 +1,4 @@
// elc-combined.el El self-hosting compiler, single-file bootstrap edition
// Inlines lexer + parser + codegen CLI entry at end.
// lexer.el el self-hosting lexer
//
@@ -536,7 +535,6 @@ fn lex(source: String) -> [Map<String, Any>] {
let tokens = native_list_append(tokens, make_tok("Eof", ""))
tokens
}
// parser.el el self-hosting recursive descent parser
//
// Consumes the token list produced by lexer.el and builds a list of AST
@@ -1320,7 +1318,6 @@ fn parse(tokens: [Map<String, Any>]) -> [Map<String, Any>] {
}
stmts
}
// codegen.el El compiler C source code generator
//
// Input: list of AST statement maps (from parser.el)
@@ -1431,8 +1428,12 @@ fn cg_expr(expr: Map<String, Any>) -> String {
}
if kind == "Float" {
// Wrap Float literals in el_from_float() so the bit pattern is
// preserved through the el_val_t (int64) slot. Without this,
// implicit doubleint64 conversion in C truncates `0.8` to `0`
// when passed to a builtin that expects el_val_t.
let v: String = expr["value"]
return v
return "el_from_float(" + v + ")"
}
if kind == "Str" {
@@ -2128,7 +2129,6 @@ fn codegen(stmts: [Map<String, Any>], source: String) -> String {
// Return empty string output was streamed via println
""
}
// compiler.el el self-hosting compiler pipeline
//
// Wires lexer -> parser -> codegen into a single compile() function.