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"))) {
|
if (str_eq(kind, EL_STR("Float"))) {
|
||||||
el_val_t v = el_get_field(expr, EL_STR("value"));
|
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"))) {
|
if (str_eq(kind, EL_STR("Str"))) {
|
||||||
el_val_t v = el_get_field(expr, EL_STR("value"));
|
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_STR(s) cast string literal to el_val_t
|
||||||
* EL_CSTR(v) cast el_val_t back to const char*
|
* EL_CSTR(v) cast el_val_t back to const char*
|
||||||
* EL_INT(v) identity — el_val_t is already int64_t
|
* 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
|
#pragma once
|
||||||
@@ -36,6 +45,22 @@ typedef int64_t el_val_t;
|
|||||||
#define EL_INT(v) (v)
|
#define EL_INT(v) (v)
|
||||||
#define EL_NULL ((el_val_t)0)
|
#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
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#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_get(el_val_t url);
|
||||||
el_val_t http_post(el_val_t url, el_val_t body);
|
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_serve(el_val_t port, el_val_t handler);
|
||||||
|
void http_set_handler(el_val_t name);
|
||||||
|
|
||||||
/* ── Filesystem ──────────────────────────────────────────────────────────── */
|
/* ── Filesystem ──────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
el_val_t fs_read(el_val_t path);
|
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_write(el_val_t path, el_val_t content);
|
||||||
|
el_val_t fs_list(el_val_t path);
|
||||||
|
|
||||||
/* ── JSON ────────────────────────────────────────────────────────────────── */
|
/* ── JSON ────────────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
@@ -206,9 +237,33 @@ el_val_t dharma_peers(void);
|
|||||||
* network-wide across all connected CGI graphs. */
|
* 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_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);
|
el_val_t engram_node_full(el_val_t content, el_val_t node_type, el_val_t label,
|
||||||
void engram_connect(el_val_t from_id, el_val_t to_id, el_val_t weight, el_val_t relation);
|
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_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() ─────────────────────────────────────────────────────────────────
|
/* ── args() ─────────────────────────────────────────────────────────────────
|
||||||
* Provides access to command-line arguments passed to the program.
|
* 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" {
|
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"]
|
let v: String = expr["value"]
|
||||||
return v
|
return "el_from_float(" + v + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
if kind == "Str" {
|
if kind == "Str" {
|
||||||
|
|||||||
+5
-5
@@ -1,5 +1,4 @@
|
|||||||
// elc-combined.el — El self-hosting compiler, single-file bootstrap edition
|
// 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
|
// 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", ""))
|
let tokens = native_list_append(tokens, make_tok("Eof", ""))
|
||||||
tokens
|
tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
// parser.el — el self-hosting recursive descent parser
|
// parser.el — el self-hosting recursive descent parser
|
||||||
//
|
//
|
||||||
// Consumes the token list produced by lexer.el and builds a list of AST
|
// 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
|
stmts
|
||||||
}
|
}
|
||||||
|
|
||||||
// codegen.el — El compiler C source code generator
|
// codegen.el — El compiler C source code generator
|
||||||
//
|
//
|
||||||
// Input: list of AST statement maps (from parser.el)
|
// Input: list of AST statement maps (from parser.el)
|
||||||
@@ -1431,8 +1428,12 @@ fn cg_expr(expr: Map<String, Any>) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if kind == "Float" {
|
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"]
|
let v: String = expr["value"]
|
||||||
return v
|
return "el_from_float(" + v + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
if kind == "Str" {
|
if kind == "Str" {
|
||||||
@@ -2128,7 +2129,6 @@ fn codegen(stmts: [Map<String, Any>], source: String) -> String {
|
|||||||
// Return empty string — output was streamed via println
|
// Return empty string — output was streamed via println
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
|
|
||||||
// compiler.el — el self-hosting compiler pipeline
|
// compiler.el — el self-hosting compiler pipeline
|
||||||
//
|
//
|
||||||
// Wires lexer -> parser -> codegen into a single compile() function.
|
// Wires lexer -> parser -> codegen into a single compile() function.
|
||||||
|
|||||||
Reference in New Issue
Block a user