runtime: engram_*_json accessors, http_set_handler dlsym, codegen int-call
Three changes that turned the runtime into something Engram-the-server
can actually run on top of.
1. engram_*_json accessors. The runtime's engram_get_node/search/scan/
neighbors/activate return ElList/ElMap; passing those through
json_stringify hit the type-erasure wall (an ElList* has no header
that distinguishes it from a string pointer). Added pre-serialized
sibling builtins:
engram_get_node_json(id) -> JSON object
engram_search_json(query, limit) -> JSON array of node objects
engram_scan_nodes_json(limit, offset)
engram_neighbors_json(node_id, max_depth, direction)
engram_activate_json(query, depth)
engram_stats_json()
Each walks the typed C structures and serializes directly, reusing
the existing engram_emit_node_json / engram_emit_edge_json helpers
from the snapshot path.
2. http_set_handler now falls back to dlsym(RTLD_DEFAULT, name) when
the named handler isn't already in the C-level registry. El programs
that define `fn handle_request(method, path, body) -> String` can
register themselves just by calling http_set_handler("handle_request").
No C glue required. Verified live on a real El server.
3. Codegen: extended int-typed dispatch on `+` to handle Calls. New
helper is_int_call recognizes a known-int-returning builtin set:
str_len, str_index_of, str_to_int, str_char_code, native_list_len,
el_list_len, len, json_get_int, json_array_len, engram_node_count,
engram_edge_count, time_now, time_now_utc, time_diff, time_add,
time_from_parts, el_abs/max/min, float_to_int. With this,
`pos + str_len(needle)` compiles to integer arithmetic instead of
string concat. The earlier limitation noted in the previous commit
(Ident + Call returning Int) is now closed.
Also: el_to_float / el_from_float moved to el_runtime.h as static
inlines so generated programs can use them. Eliminates the unused
inline definitions that were duplicating in the .c file.
Closure verified: stage1 vs stage2 byte-identical against the new
runtime. dist/platform/elc rebuilt; .prev4 preserved.
Engram server (engram/src/server.el) end-to-end:
POST /api/nodes ×3 → 3 UUIDs returned
POST /api/edges ×2 → linkage made
GET /api/stats → {"node_count":3,"edge_count":2}
GET /api/search?q=spreading&limit=5 → 1 hit, full node JSON
POST /api/activate {"query":"Hebbian","depth":3}
→ seed node @ hop 0, strength 0.8
→ 1-hop neighbor @ strength 0.392 (= 0.8 × 0.7 weight × 0.7 decay)
GET /api/neighbors/<id>?depth=2 → {node, edge, hops} triple
POST /api/save → {"ok":true,"path":"..."}
Server stays alive across all routes.
Snapshot save/load on restart still TODO — server starts with 0 nodes
even when a snapshot exists; investigation pending.
This commit is contained in:
+65
-3
@@ -1,5 +1,4 @@
|
||||
// elc-combined.el — El self-hosting compiler, single-file bootstrap edition
|
||||
|
||||
// elc-combined.el
|
||||
// lexer.el — el self-hosting lexer
|
||||
//
|
||||
// Tokenises an el source string into a list of token maps.
|
||||
@@ -1511,7 +1510,40 @@ fn cg_expr(expr: Map<String, Any>) -> String {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Same dispatch for Ident-Int + Call-to-known-Int-builtin (and the
|
||||
// mirror). Without this, expressions like `pos + str_len(s)` get
|
||||
// string-concatenated. is_int_call walks a known-builtin list.
|
||||
if left_kind == "Ident" {
|
||||
if right_kind == "Call" {
|
||||
let lname: String = left["name"]
|
||||
if is_int_name(lname) {
|
||||
if is_int_call(right) {
|
||||
let op_c: String = binop_to_c(op)
|
||||
return "(" + left_c + " " + op_c + " " + right_c + ")"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if right_kind == "Ident" {
|
||||
if left_kind == "Call" {
|
||||
let rname: String = right["name"]
|
||||
if is_int_name(rname) {
|
||||
if is_int_call(left) {
|
||||
let op_c: String = binop_to_c(op)
|
||||
return "(" + left_c + " " + op_c + " " + right_c + ")"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if left_kind == "Call" {
|
||||
if right_kind == "Call" {
|
||||
if is_int_call(left) {
|
||||
if is_int_call(right) {
|
||||
let op_c: String = binop_to_c(op)
|
||||
return "(" + left_c + " " + op_c + " " + right_c + ")"
|
||||
}
|
||||
}
|
||||
}
|
||||
return "el_str_concat(" + left_c + ", " + right_c + ")"
|
||||
}
|
||||
if right_kind == "Call" {
|
||||
@@ -1989,6 +2021,37 @@ fn is_int_name(name: String) -> Bool {
|
||||
return str_contains(csv, "," + name + ",")
|
||||
}
|
||||
|
||||
// Known runtime builtins that return Int. Used to dispatch arithmetic vs
|
||||
// string-concat on `+` when one side is a Call. New builtins must be added
|
||||
// here when they return Int and may participate in arithmetic.
|
||||
fn is_int_call(call_expr: Map<String, Any>) -> Bool {
|
||||
let func = call_expr["func"]
|
||||
let fk: String = func["expr"]
|
||||
if !str_eq(fk, "Ident") { return false }
|
||||
let name: String = func["name"]
|
||||
if str_eq(name, "str_len") { return true }
|
||||
if str_eq(name, "str_index_of") { return true }
|
||||
if str_eq(name, "str_to_int") { return true }
|
||||
if str_eq(name, "str_char_code") { return true }
|
||||
if str_eq(name, "native_list_len") { return true }
|
||||
if str_eq(name, "el_list_len") { return true }
|
||||
if str_eq(name, "len") { return true }
|
||||
if str_eq(name, "json_get_int") { return true }
|
||||
if str_eq(name, "json_array_len") { return true }
|
||||
if str_eq(name, "engram_node_count") { return true }
|
||||
if str_eq(name, "engram_edge_count") { return true }
|
||||
if str_eq(name, "time_now") { return true }
|
||||
if str_eq(name, "time_now_utc") { return true }
|
||||
if str_eq(name, "time_diff") { return true }
|
||||
if str_eq(name, "time_add") { return true }
|
||||
if str_eq(name, "time_from_parts") { return true }
|
||||
if str_eq(name, "el_abs") { return true }
|
||||
if str_eq(name, "el_max") { return true }
|
||||
if str_eq(name, "el_min") { return true }
|
||||
if str_eq(name, "float_to_int") { return true }
|
||||
return false
|
||||
}
|
||||
|
||||
fn add_int_name(name: String) -> Bool {
|
||||
let csv: String = state_get("__int_names")
|
||||
if str_eq(csv, "") { csv = "," }
|
||||
@@ -2154,7 +2217,6 @@ fn compile(source: String) -> String {
|
||||
// result to args()[1]. Then run:
|
||||
// cc -o <prog> <output.c> el_runtime.c
|
||||
|
||||
// CLI driver
|
||||
let _argv: [String] = args()
|
||||
let _src_path: String = native_list_get(_argv, 0)
|
||||
let _source: String = fs_read(_src_path)
|
||||
|
||||
Reference in New Issue
Block a user