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:
Vendored
BIN
Binary file not shown.
Vendored
+1
-1
@@ -1650,7 +1650,7 @@ el_val_t cg_expr(el_val_t expr) {
|
||||
}
|
||||
if (str_eq(kind, EL_STR("Float"))) {
|
||||
el_val_t v = el_get_field(expr, EL_STR("value"));
|
||||
return v;
|
||||
return el_str_concat(el_str_concat(EL_STR("el_from_float("), v), EL_STR(")"));
|
||||
}
|
||||
if (str_eq(kind, EL_STR("Str"))) {
|
||||
el_val_t v = el_get_field(expr, EL_STR("value"));
|
||||
|
||||
BIN
Binary file not shown.
+1541
-58
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,15 @@
|
||||
* EL_STR(s) cast string literal to el_val_t
|
||||
* EL_CSTR(v) cast el_val_t back to const char*
|
||||
* EL_INT(v) identity — el_val_t is already int64_t
|
||||
*
|
||||
* Link requirements:
|
||||
* -lcurl — required for the HTTP client (http_get, http_post, llm_*).
|
||||
* -lpthread — required for the HTTP server (one detached thread per
|
||||
* connection, capped at 64 concurrent).
|
||||
*
|
||||
* Canonical compile command:
|
||||
* cc -std=c11 -I el-compiler/runtime -lcurl -lpthread \
|
||||
* -o <out> <prog>.c el-compiler/runtime/el_runtime.c
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@@ -36,6 +45,22 @@ typedef int64_t el_val_t;
|
||||
#define EL_INT(v) (v)
|
||||
#define EL_NULL ((el_val_t)0)
|
||||
|
||||
/* Float values share the el_val_t (int64) slot via a bit-cast.
|
||||
* The codegen emits Float literals as `el_from_float(<dbl>)` so the
|
||||
* underlying bits represent the IEEE 754 double. Float-aware builtins
|
||||
* (math, format, json) round-trip via these helpers. */
|
||||
static inline double el_to_float(el_val_t v) {
|
||||
union { int64_t i; double f; } u;
|
||||
u.i = (int64_t)v;
|
||||
return u.f;
|
||||
}
|
||||
|
||||
static inline el_val_t el_from_float(double f) {
|
||||
union { double f; int64_t i; } u;
|
||||
u.f = f;
|
||||
return (el_val_t)u.i;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -88,12 +113,18 @@ el_val_t el_map_set(el_val_t map, el_val_t key, el_val_t value);
|
||||
|
||||
el_val_t http_get(el_val_t url);
|
||||
el_val_t http_post(el_val_t url, el_val_t body);
|
||||
el_val_t http_post_json(el_val_t url, el_val_t json_body);
|
||||
el_val_t http_get_with_headers(el_val_t url, el_val_t headers_map);
|
||||
el_val_t http_post_with_headers(el_val_t url, el_val_t body, el_val_t headers_map);
|
||||
el_val_t http_post_form_auth(el_val_t url, el_val_t form_body, el_val_t auth_header);
|
||||
void http_serve(el_val_t port, el_val_t handler);
|
||||
void http_set_handler(el_val_t name);
|
||||
|
||||
/* ── Filesystem ──────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t fs_read(el_val_t path);
|
||||
el_val_t fs_write(el_val_t path, el_val_t content);
|
||||
el_val_t fs_list(el_val_t path);
|
||||
|
||||
/* ── JSON ────────────────────────────────────────────────────────────────── */
|
||||
|
||||
@@ -206,9 +237,33 @@ el_val_t dharma_peers(void);
|
||||
* network-wide across all connected CGI graphs. */
|
||||
|
||||
el_val_t engram_node(el_val_t content, el_val_t node_type, el_val_t salience);
|
||||
el_val_t engram_activate(el_val_t query, el_val_t depth);
|
||||
void engram_connect(el_val_t from_id, el_val_t to_id, el_val_t weight, el_val_t relation);
|
||||
el_val_t engram_node_full(el_val_t content, el_val_t node_type, el_val_t label,
|
||||
el_val_t salience, el_val_t importance, el_val_t confidence,
|
||||
el_val_t tier, el_val_t tags);
|
||||
el_val_t engram_get_node(el_val_t id);
|
||||
void engram_strengthen(el_val_t node_id);
|
||||
void engram_forget(el_val_t node_id);
|
||||
el_val_t engram_node_count(void);
|
||||
el_val_t engram_search(el_val_t query, el_val_t limit);
|
||||
el_val_t engram_scan_nodes(el_val_t limit, el_val_t offset);
|
||||
void engram_connect(el_val_t from_id, el_val_t to_id, el_val_t weight, el_val_t relation);
|
||||
el_val_t engram_edge_between(el_val_t from_id, el_val_t to_id);
|
||||
el_val_t engram_neighbors(el_val_t node_id);
|
||||
el_val_t engram_neighbors_filtered(el_val_t node_id, el_val_t max_depth, el_val_t direction);
|
||||
el_val_t engram_edge_count(void);
|
||||
el_val_t engram_activate(el_val_t query, el_val_t depth);
|
||||
el_val_t engram_save(el_val_t path);
|
||||
el_val_t engram_load(el_val_t path);
|
||||
|
||||
/* ── LLM (Anthropic API client) ─────────────────────────────────────────────
|
||||
* All functions call https://api.anthropic.com/v1/messages with the API key
|
||||
* from env ANTHROPIC_API_KEY. Default model when empty: claude-sonnet-4-5. */
|
||||
|
||||
el_val_t llm_call(el_val_t model, el_val_t prompt);
|
||||
el_val_t llm_call_system(el_val_t model, el_val_t system_prompt, el_val_t user_prompt);
|
||||
el_val_t llm_call_agentic(el_val_t model, el_val_t system, el_val_t user, el_val_t tools);
|
||||
el_val_t llm_vision(el_val_t model, el_val_t system, el_val_t prompt, el_val_t image_url_or_b64);
|
||||
el_val_t llm_models(void);
|
||||
|
||||
/* ── args() ─────────────────────────────────────────────────────────────────
|
||||
* Provides access to command-line arguments passed to the program.
|
||||
|
||||
@@ -108,8 +108,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 double→int64 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" {
|
||||
|
||||
+5
-5
@@ -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 double→int64 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.
|
||||
|
||||
Reference in New Issue
Block a user