Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 866c75e5e2 |
+12
-24
@@ -31,46 +31,34 @@ This is where almost all work belongs. El programs are source files that get com
|
||||
|
||||
This is the self-contained C OS-boundary layer. It provides the `__`-prefixed primitives that compiled El programs call: libcurl HTTP, pthreads, filesystem I/O, arena allocation, etc. It is **not generated** — it is maintained by hand.
|
||||
|
||||
The runtime is native El (`runtime/*.el`) over a C OS-boundary. **Status (verified 2026-08-15):** the migration to a seed-only boundary is *in progress, not done*. Two files exist:
|
||||
- `el-compiler/runtime/el_runtime.c` (~516 KB) — **LIVE**. Holds the engram store (`EngramStore engram_global`) plus the `http_*`/`json_*`/`state_*`/`engram_*` impls. It is the authoritative single-file link target for the compiler, and `tools/install.sh` compiles it into `libel.a`. This is where a new C builtin's *implementation* must currently live to be linkable.
|
||||
- `el-compiler/runtime/el_seed.c` — the intended hand-maintained `__`-prefixed seed (thin wrappers over the above). It is compiled alongside `el_runtime.c` by `tools/install.sh`, but does **not** compile standalone yet (see the build-path caveat under "Rebuilding the Compiler").
|
||||
- `el-compiler/runtime/legacy/el_runtime.c` (~419 KB) — **DEAD**. Archived duplicate; no build script references it.
|
||||
The old `el_runtime.c` has been archived to `el-compiler/runtime/legacy/`. The runtime is now native El (`runtime/*.el`). `el_seed.c` replaces `el_runtime.c` as the sole C compilation dependency.
|
||||
|
||||
**Only edit these when you genuinely need OS-level access** (raw sockets, GPU calls, new libcurl features, a new engram store op). For everything else, write El.
|
||||
**Only edit `el_seed.c` when you genuinely need OS-level access** (raw sockets, GPU calls, new libcurl features). For everything else, write El.
|
||||
|
||||
When you add a C builtin (verbatim-emit recipe — the El name is emitted as the exact C symbol; `builtin_arity` is an arity guard only, not a dispatch table):
|
||||
1. Implement the C function in `el_runtime.c` (and declare it in `el_runtime.h`).
|
||||
2. Add a `__`-prefixed thin wrapper in `el_seed.c` and declare it in `el_seed.h`.
|
||||
3. Add the name to `builtin_arity` in `el-compiler/src/codegen.el` — add **both** the plain and `__`-prefixed spellings.
|
||||
4. Rebuild the elc binary (see below) and confirm the self-host fixpoint is byte-identical.
|
||||
|
||||
Worked example: the `engram_assert_json` (op_assert seam) and `engram_node_full_in`/`engram_connect_in` (purview write-side) primitives added 2026-08-15 follow exactly this recipe.
|
||||
When you do add a C builtin:
|
||||
1. Add the C function to `el_seed.c`
|
||||
2. Declare it in `el_seed.h`
|
||||
3. Add it to the `builtin_arity` table in `el-compiler/src/codegen.el` (so the compiler knows the arg count)
|
||||
4. Rebuild the elc binary (see below)
|
||||
|
||||
---
|
||||
|
||||
## Rebuilding the Compiler
|
||||
|
||||
After changing any `.el` source in `el-compiler/src/` (run from the `lang/` dir):
|
||||
After changing any `.el` source in `el-compiler/src/`:
|
||||
|
||||
```bash
|
||||
# 1. Stage2: current elc compiles the (modified) compiler to C
|
||||
cd /Users/will/Development/neuron-technologies/foundation/el
|
||||
./dist/platform/elc elc-cli.el > elc-new.c
|
||||
# 2. Build the new compiler. The C link target is el_runtime.c — it holds the
|
||||
# engram store + http/json/state impls the compiler output calls. el_runtime.c
|
||||
# self-hosts elc on its own; el_seed.c is the (aspirational) seed layer and does
|
||||
# NOT compile standalone under clang (missing prototypes for the el_runtime.c
|
||||
# symbols it wraps — see caveat below), so link el_runtime.c here.
|
||||
cc -std=c11 -I el-compiler/runtime -lcurl -lpthread \
|
||||
-o dist/platform/elc-new \
|
||||
elc-new.c el-compiler/runtime/el_runtime.c
|
||||
# 3. Verify self-hosting FIXPOINT (stage3 == stage2 output, byte-identical):
|
||||
elc-new.c el-compiler/runtime/el_seed.c
|
||||
# Verify self-hosting:
|
||||
./dist/platform/elc-new elc-cli.el > elc-verify.c
|
||||
diff elc-new.c elc-verify.c # must be identical
|
||||
diff elc-new.c elc-verify.c # should be identical
|
||||
mv dist/platform/elc-new dist/platform/elc
|
||||
```
|
||||
|
||||
> **Build-path caveat (verified 2026-08-15).** `el_seed.c` is the intended hand-maintained OS-boundary seed, but it does **not** compile standalone under modern clang: it wraps ~16 unprefixed `el_runtime.c` symbols (`http_serve`, `json_*`, `state_*`, `http_response`) without prototypes, and clang treats implicit declarations as errors (C99+). The productionised install (`tools/install.sh`) builds `libel.a` from **both** `el_seed.o` + `el_runtime.o` together, which is why linking succeeds there. To make `el_seed.c` build on its own, add prototypes for those symbols (or `#include "el_runtime.h"`, reconciling the `__http_serve` return-type mismatch first). Until then, `el_runtime.c` is the authoritative single-file link target for the compiler.
|
||||
|
||||
After changing `el_seed.c` only (no El source changes), rebuild downstream programs but do NOT need to rebuild the compiler binary itself — the seed is linked at the application level, not the compiler level.
|
||||
|
||||
---
|
||||
|
||||
@@ -8410,59 +8410,6 @@ el_val_t engram_activate_json(el_val_t query, el_val_t depth) {
|
||||
return el_wrap_str(jb_finish(&b));
|
||||
}
|
||||
|
||||
/* op_assert seam (realizer promotion, bl-53/#57).
|
||||
* Gathers the grounded ASSERTION ENVELOPE for a subject node —
|
||||
* { "subject": <node|null>, "grounding": [ {node,edge,hops}... ] }
|
||||
* i.e. the self-geometry a realizer renders as faithful first-person text.
|
||||
* Read-only: realization (geometry->text) stays in the faculty/realizer;
|
||||
* this native primitive produces its structured input from proven paths
|
||||
* (engram_emit_node_json + engram_neighbors_json). arity 2 (node_id, depth). */
|
||||
el_val_t engram_assert_json(el_val_t node_id, el_val_t depth) {
|
||||
const char* sid = EL_CSTR(node_id);
|
||||
JsonBuf b; jb_init(&b);
|
||||
jb_puts(&b, "{\"subject\":");
|
||||
EngramNode* n = (sid && *sid) ? engram_find_node(sid) : NULL;
|
||||
if (n) engram_emit_node_json(&b, n); else jb_puts(&b, "null");
|
||||
jb_puts(&b, ",\"grounding\":");
|
||||
el_val_t nb = engram_neighbors_json(node_id, depth, EL_STR("both"));
|
||||
const char* nbs = EL_CSTR(nb);
|
||||
jb_puts(&b, (nbs && *nbs) ? nbs : "[]");
|
||||
jb_putc(&b, '}');
|
||||
return el_wrap_str(jb_finish(&b));
|
||||
}
|
||||
|
||||
/* Parametric mutation (purview write-side bounding, keystone 56ecbec6).
|
||||
* The mutation verbs travel with a TARGET MANIFOLD (purview) instead of the
|
||||
* implicit global singleton. purview==0 (EL_NULL) is the DEGENERATE/DEFAULT
|
||||
* case: G = live, behaviour identical to the base op. A non-zero purview is a
|
||||
* bounded target that the engine cannot yet resolve (multi-manifold store is a
|
||||
* promotion item), so we REFUSE rather than silently mutate the live set —
|
||||
* write-side bounding must never leak into G=live. */
|
||||
el_val_t engram_node_full_in(el_val_t purview,
|
||||
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) {
|
||||
if (purview == 0) {
|
||||
return engram_node_full(content, node_type, label, salience, importance,
|
||||
confidence, tier, tags);
|
||||
}
|
||||
fprintf(stderr, "[engram] purview write-side not yet resolvable (G != live); "
|
||||
"refusing to append to live store (purview=%lld)\n",
|
||||
(long long)purview);
|
||||
return EL_STR("");
|
||||
}
|
||||
|
||||
void engram_connect_in(el_val_t purview,
|
||||
el_val_t from_id, el_val_t to_id, el_val_t weight, el_val_t relation) {
|
||||
if (purview == 0) {
|
||||
engram_connect(from_id, to_id, weight, relation);
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "[engram] purview write-side not yet resolvable (G != live); "
|
||||
"refusing to connect in live store (purview=%lld)\n",
|
||||
(long long)purview);
|
||||
}
|
||||
|
||||
el_val_t engram_stats_json(void) {
|
||||
EngramStore* g = engram_get();
|
||||
char buf[128];
|
||||
|
||||
@@ -639,14 +639,6 @@ el_val_t engram_scan_nodes_by_type_json(el_val_t node_type, el_val_t limit, el_
|
||||
el_val_t engram_neighbors_json(el_val_t node_id, el_val_t max_depth, el_val_t direction);
|
||||
el_val_t engram_activate_json(el_val_t query, el_val_t depth);
|
||||
el_val_t engram_stats_json(void);
|
||||
/* op_assert seam: grounded assertion envelope {subject,grounding} for the realizer. */
|
||||
el_val_t engram_assert_json(el_val_t node_id, el_val_t depth);
|
||||
/* Parametric mutation (purview write-side): purview==0 => G=live (default), else refuse. */
|
||||
el_val_t engram_node_full_in(el_val_t purview, 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);
|
||||
void engram_connect_in(el_val_t purview, el_val_t from_id, el_val_t to_id,
|
||||
el_val_t weight, el_val_t relation);
|
||||
el_val_t engram_list_layers_json(void);
|
||||
/* engram_compile_layered_json — produce a prompt-ready text block split
|
||||
* into "[LAYER 0 — STRUCTURAL]" (non-suppressible layers, sacred fire)
|
||||
|
||||
@@ -1095,15 +1095,6 @@ el_val_t __engram_activate_json(el_val_t query, el_val_t depth) {
|
||||
}
|
||||
|
||||
el_val_t __engram_stats_json(void) { return engram_stats_json(); }
|
||||
el_val_t __engram_assert_json(el_val_t node_id, el_val_t depth) { return engram_assert_json(node_id, depth); }
|
||||
el_val_t __engram_node_full_in(el_val_t purview, 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) {
|
||||
return engram_node_full_in(purview, content, node_type, label, salience, importance, confidence, tier, tags);
|
||||
}
|
||||
void __engram_connect_in(el_val_t purview, el_val_t from_id, el_val_t to_id, el_val_t weight, el_val_t relation) {
|
||||
engram_connect_in(purview, from_id, to_id, weight, relation);
|
||||
}
|
||||
el_val_t __engram_list_layers_json(void) { return engram_list_layers_json(); }
|
||||
|
||||
el_val_t __engram_compile_layered_json(el_val_t intent, el_val_t depth) {
|
||||
|
||||
@@ -233,12 +233,6 @@ el_val_t __engram_scan_nodes_by_type_json(el_val_t node_type, el_val_t limit, e
|
||||
el_val_t __engram_neighbors_json(el_val_t node_id, el_val_t max_depth, el_val_t direction);
|
||||
el_val_t __engram_activate_json(el_val_t query, el_val_t depth);
|
||||
el_val_t __engram_stats_json(void);
|
||||
el_val_t __engram_assert_json(el_val_t node_id, el_val_t depth);
|
||||
el_val_t __engram_node_full_in(el_val_t purview, 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);
|
||||
void __engram_connect_in(el_val_t purview, el_val_t from_id, el_val_t to_id,
|
||||
el_val_t weight, el_val_t relation);
|
||||
el_val_t __engram_list_layers_json(void);
|
||||
el_val_t __engram_compile_layered_json(el_val_t intent, el_val_t depth);
|
||||
|
||||
|
||||
@@ -2579,9 +2579,6 @@ fn builtin_arity(name: String) -> Int {
|
||||
if str_eq(name, "__engram_neighbors_filtered") { return 3 }
|
||||
if str_eq(name, "__engram_activate") { return 2 }
|
||||
if str_eq(name, "__engram_activate_json") { return 2 }
|
||||
if str_eq(name, "__engram_assert_json") { return 2 }
|
||||
if str_eq(name, "__engram_node_full_in") { return 9 }
|
||||
if str_eq(name, "__engram_connect_in") { return 5 }
|
||||
if str_eq(name, "__engram_scan_nodes_json") { return 2 }
|
||||
if str_eq(name, "__generate") { return 1 }
|
||||
// Filesystem
|
||||
@@ -2679,9 +2676,6 @@ fn builtin_arity(name: String) -> Int {
|
||||
if str_eq(name, "engram_neighbors_json") { return 3 }
|
||||
if str_eq(name, "engram_activate_json") { return 2 }
|
||||
if str_eq(name, "engram_stats_json") { return 0 }
|
||||
if str_eq(name, "engram_assert_json") { return 2 }
|
||||
if str_eq(name, "engram_node_full_in") { return 9 }
|
||||
if str_eq(name, "engram_connect_in") { return 5 }
|
||||
// LLM
|
||||
if str_eq(name, "llm_call") { return 2 }
|
||||
if str_eq(name, "llm_call_system") { return 3 }
|
||||
@@ -3633,6 +3627,24 @@ fn codegen_streaming(tokens: [Any], sigs: [Map<String, Any>], source: String) ->
|
||||
let pos: Int = 0
|
||||
let el_main_body: [Map<String, Any>] = native_list_empty()
|
||||
let toplevel_exec_stmts: [Map<String, Any>] = native_list_empty()
|
||||
// CGI IDENTITY CAPTURE (2026-08-09). A cgi block is a top-level DECLARATION, so
|
||||
// the classifier below correctly excludes it from toplevel_exec_stmts and calls
|
||||
// el_release on it. The identity emission further down then searched
|
||||
// toplevel_exec_stmts for it — a list that structurally can never contain it —
|
||||
// found nothing, and emitted nothing, silently. Measured: that search sees only
|
||||
// [Let, Expr] for a program whose first statement is a cgi block.
|
||||
// Fix: copy the values out BEFORE the release (strings, so no dangling reference)
|
||||
// and emit from these. No search, so the failure mode is removed rather than moved.
|
||||
let cgi_have: Bool = false
|
||||
let cgi_name_v: String = ""
|
||||
let cgi_did_v: String = ""
|
||||
let cgi_prin_v: String = ""
|
||||
let cgi_net_v: String = ""
|
||||
let cgi_eng_v: String = ""
|
||||
let cgi_has_did: Bool = false
|
||||
let cgi_has_prin: Bool = false
|
||||
let cgi_has_net: Bool = false
|
||||
let cgi_has_eng: Bool = false
|
||||
let has_toplevel_exec: Bool = false
|
||||
|
||||
let stream_running: Bool = true
|
||||
@@ -3743,6 +3755,20 @@ fn codegen_streaming(tokens: [Any], sigs: [Map<String, Any>], source: String) ->
|
||||
if is_top_level_decl(stmt) {
|
||||
// Import, TypeDef, EnumDef, CgiBlock, ServiceBlock, ExternFn
|
||||
// These are no-ops in codegen (forward decls already emitted)
|
||||
// — except a CgiBlock, whose declared identity must survive
|
||||
// this release to be emitted as a compiled constant.
|
||||
if str_eq(sk, "CgiBlock") {
|
||||
let cgi_have = true
|
||||
let cgi_name_v = stmt["name"]
|
||||
let cgi_did_v = stmt["dharma_id"]
|
||||
let cgi_prin_v = stmt["principal"]
|
||||
let cgi_net_v = stmt["network"]
|
||||
let cgi_eng_v = stmt["engram"]
|
||||
let cgi_has_did = stmt["has_dharma_id"]
|
||||
let cgi_has_prin = stmt["has_principal"]
|
||||
let cgi_has_net = stmt["has_network"]
|
||||
let cgi_has_eng = stmt["has_engram"]
|
||||
}
|
||||
el_release(stmt)
|
||||
} else {
|
||||
if str_eq(sk, "Let") {
|
||||
@@ -3821,33 +3847,17 @@ fn codegen_streaming(tokens: [Any], sigs: [Map<String, Any>], source: String) ->
|
||||
let sig2 = native_list_get(sigs, si2)
|
||||
let sk3: String = sig2["kind"]
|
||||
if str_eq(sk3, "cgi_block") {
|
||||
// We need the full cgi_block data — it was parsed by scan_fn_sigs
|
||||
// but scan only stored the name. For cgi_init we need dharma_id etc.
|
||||
// Since cgi blocks are rare and small, they end up in toplevel_exec_stmts.
|
||||
// Find the CgiBlock in toplevel_exec_stmts.
|
||||
let tes_n: Int = native_list_len(toplevel_exec_stmts)
|
||||
let tes_i: Int = 0
|
||||
while tes_i < tes_n {
|
||||
let tes = native_list_get(toplevel_exec_stmts, tes_i)
|
||||
let tes_k: String = tes["stmt"]
|
||||
if str_eq(tes_k, "CgiBlock") {
|
||||
let cname2: String = tes["name"]
|
||||
let cdid2: String = tes["dharma_id"]
|
||||
let cprin2: String = tes["principal"]
|
||||
let cnet2: String = tes["network"]
|
||||
let ceng2: String = tes["engram"]
|
||||
let has_did2: Bool = tes["has_dharma_id"]
|
||||
let has_prin2: Bool = tes["has_principal"]
|
||||
let has_net2: Bool = tes["has_network"]
|
||||
let has_eng2: Bool = tes["has_engram"]
|
||||
let arg_name2: String = "EL_STR(" + c_str_lit(cname2) + ")"
|
||||
let arg_did2: String = cgi_arg(cdid2, has_did2)
|
||||
let arg_prin2: String = cgi_arg(cprin2, has_prin2)
|
||||
let arg_net2: String = cgi_arg(cnet2, has_net2)
|
||||
let arg_eng2: String = cgi_arg(ceng2, has_eng2)
|
||||
emit_line(" el_cgi_init(" + arg_name2 + ", " + arg_did2 + ", " + arg_prin2 + ", " + arg_net2 + ", " + arg_eng2 + ");")
|
||||
}
|
||||
let tes_i = tes_i + 1
|
||||
// Emit from the values captured before the declaration was released.
|
||||
// The previous implementation searched toplevel_exec_stmts, which by
|
||||
// construction never contains a declaration — so it emitted nothing and
|
||||
// said nothing. See the capture block near toplevel_exec_stmts init.
|
||||
if cgi_have {
|
||||
let arg_name2: String = "EL_STR(" + c_str_lit(cgi_name_v) + ")"
|
||||
let arg_did2: String = cgi_arg(cgi_did_v, cgi_has_did)
|
||||
let arg_prin2: String = cgi_arg(cgi_prin_v, cgi_has_prin)
|
||||
let arg_net2: String = cgi_arg(cgi_net_v, cgi_has_net)
|
||||
let arg_eng2: String = cgi_arg(cgi_eng_v, cgi_has_eng)
|
||||
emit_line(" el_cgi_init(" + arg_name2 + ", " + arg_did2 + ", " + arg_prin2 + ", " + arg_net2 + ", " + arg_eng2 + ");")
|
||||
}
|
||||
}
|
||||
let si2 = si2 + 1
|
||||
|
||||
Reference in New Issue
Block a user