summaryrefslogtreecommitdiff
path: root/vere
diff options
context:
space:
mode:
Diffstat (limited to 'vere')
-rw-r--r--vere/C_INSIGHTS.md51
-rw-r--r--vere/build.zig9
-rw-r--r--vere/pkg/noun/nock.c83
-rw-r--r--vere/pkg/vere/ivory_boot_test.c72
-rw-r--r--vere/pkg/vere/solid_boot_test.c19
5 files changed, 197 insertions, 37 deletions
diff --git a/vere/C_INSIGHTS.md b/vere/C_INSIGHTS.md
new file mode 100644
index 0000000..016ee83
--- /dev/null
+++ b/vere/C_INSIGHTS.md
@@ -0,0 +1,51 @@
+## on nock.c
+CLAUDE:
+The key is that call_count is static (shared across ALL function calls), while my_call is local (each function call gets its own copy on the stack).
+
+ Here's what happens without my_call:
+
+ 1. Call #0 ENTERS → logs "ENTER call #0", call_count becomes 1
+ 2. Call #1 ENTERS (recursive) → logs "ENTER call #1", call_count becomes 2
+ 3. Call #2 ENTERS (recursive) → logs "ENTER call #2", call_count becomes 3
+ 4. Call #2 EXITS → logs "EXIT call #2" using (call_count-1 = 2) ✓ Correct!
+ 5. Call #1 EXITS → logs "EXIT call #1" using (call_count-1 = 2) ✗ Wrong! Should be 1
+ 6. Call #0 EXITS → logs "EXIT call #0" using (call_count-1 = 2) ✗ Wrong! Should be 0
+
+ The problem: by the time call #0 exits, call_count has already been incremented by all the recursive calls.
+
+ With my_call (a local variable):
+
+ 1. Call #0: my_call=0 (saved on stack), call_count++
+ 2. Call #1: my_call=1 (saved on stack), call_count++
+ 3. Call #2: my_call=2 (saved on stack), call_count++
+ 4. Call #2 EXITS → uses its own my_call=2 ✓
+ 5. Call #1 EXITS → uses its own my_call=1 ✓
+ 6. Call #0 EXITS → uses its own my_call=0 ✓
+
+ Each recursive call preserves its own my_call value on the stack, so the EXIT log always matches the ENTER log!
+
+ This is a classic recursion pattern in C: use static variables for things you want to share across calls (like a counter), but capture their values in local variables if you need them to survive recursion.
+
+
+ ## after first round on vere
+ CLAUDE:
+ Current Logging Features
+
+ The C Nock interpreter now logs:
+ - Call number: Unique ID for each Nock evaluation (call #0, #1, etc.)
+ - Recursion depth: Indented to show call hierarchy
+ - Opcode: The Nock rule being executed (0-12) or CELL for distribution
+ - Subject type: Whether the subject (bus) is a cell or atom
+ - Mug: Hash of the subject/result for tracking identical nouns
+ - Return value type: Cell or atom with its mug
+
+ Example Pattern from the Log
+
+ >>> ENTER call #8 depth=5 opcode=8 bus=cell[mug=0x5cc2e3ff]
+ >>> ENTER call #9 depth=6 opcode=8 bus=cell[mug=0x528e9b65]
+ <<< EXIT call #9 depth=6 returns=cell[mug=0x6d2bc01f]
+ >>> ENTER call #10 depth=6 opcode=8 bus=cell[mug=0x479b42af]
+ <<< EXIT call #10 depth=6 returns=cell[mug=0x6d2bc01f]
+ <<< EXIT call #8 depth=5 returns=cell[mug=0x6d2bc01f]
+
+ This shows: opcode 8 (Nock Eight = extend subject) calling itself recursively twice, both returning the same noun (mug 0x6d2bc01f).
diff --git a/vere/build.zig b/vere/build.zig
index 707ca2b..72baf8e 100644
--- a/vere/build.zig
+++ b/vere/build.zig
@@ -638,6 +638,11 @@ fn buildBinary(
.deps = vere_test_deps,
},
.{
+ .name = "ivory-boot-test",
+ .file = "pkg/vere/ivory_boot_test.c",
+ .deps = vere_test_deps,
+ },
+ .{
.name = "newt-test",
.file = "pkg/vere/newt_tests.c",
.deps = vere_test_deps,
@@ -700,7 +705,7 @@ fn buildBinary(
test_exe.addLibraryPath(.{
.cwd_relative = "/opt/homebrew/opt/llvm@18/lib/clang/18/lib/darwin",
});
- if (cfg.asan) test_exe.linkSystemLibrary("clang_rt.asan_osx_dynamic");
+ if (cfg.asan) test_exe.linkSystemLibrary("clang_rt.asan_osx_dynamic");
if (cfg.ubsan) test_exe.linkSystemLibrary("clang_rt.ubsan_osx_dynamic");
}
@@ -719,7 +724,7 @@ fn buildBinary(
});
const exe_install = b.addInstallArtifact(test_exe, .{});
const run_unit_tests = b.addRunArtifact(test_exe);
- if ( t.os.tag.isDarwin() and (cfg.asan or cfg.ubsan) ) {
+ if (t.os.tag.isDarwin() and (cfg.asan or cfg.ubsan)) {
// disable libmalloc warnings
run_unit_tests.setEnvironmentVariable("MallocNanoZone", "0");
}
diff --git a/vere/pkg/noun/nock.c b/vere/pkg/noun/nock.c
index 959ec96..eef211b 100644
--- a/vere/pkg/noun/nock.c
+++ b/vere/pkg/noun/nock.c
@@ -2919,28 +2919,6 @@ u3n_burn(u3p(u3n_prog) pog_p, u3_noun bus)
static u3_noun
_n_burn_on(u3_noun bus, u3_noun fol)
{
- static c3_w burn_count = 0;
- if ( burn_count < 500 ) { // Increased to 500 to capture solid pill boot
- u3_noun hib = u3h(fol);
- if ( c3y == u3du(hib) ) {
- u3l_log("[C-Burn:%u] cell-cell formula", burn_count);
- } else {
- u3l_log("[C-Burn:%u] opcode %u", burn_count, hib);
- // For call #1, dump more details
- if ( burn_count == 1 ) {
- u3_noun gal = u3t(fol);
- u3l_log("[C-Burn:1-DEBUG] gal is %s", (c3y == u3du(gal)) ? "cell" : "atom");
- if ( c3y == u3du(gal) ) {
- u3_noun gal_h = u3h(gal);
- u3l_log("[C-Burn:1-DEBUG] head(gal) is %s, val=%u",
- (c3y == u3du(gal_h)) ? "cell" : "atom",
- (c3y == u3du(gal_h)) ? u3h(gal_h) : gal_h);
- }
- }
- }
- burn_count++;
- }
-
u3n_prog* pog_u = _n_find(u3_nul, fol);
u3z(fol);
@@ -2954,12 +2932,46 @@ u3n_nock_on(u3_noun bus, u3_noun fol)
{
u3_noun pro;
- static c3_w call_count = 0;
- if ( call_count < 100 ) { // Increased to 100
- u3l_log(">>> u3n_nock_on call #%u <<<", call_count);
+ // Static variables persist across all calls; local variables are per-call
+ static c3_w call_count = 0; // Global counter incremented on each call
+ static c3_w depth = 0; // Current recursion depth
+
+ // Capture current call_count in a local variable so it survives recursion.
+ // Without this, when we log EXIT, call_count will have been incremented
+ // by all the recursive calls that happened in between, giving wrong numbers.
+ c3_w my_call = call_count;
+ c3_o should_log = (call_count < 100) ? c3y : c3n;
+
+ if ( c3y == should_log ) {
+ u3_noun hib = u3h(fol); // Get the opcode (head of formula)
+
+ // Build indentation string based on recursion depth for readability
+ const char* indent = "";
+ if ( depth == 0 ) indent = "";
+ else if ( depth == 1 ) indent = " ";
+ else if ( depth == 2 ) indent = " ";
+ else if ( depth == 3 ) indent = " ";
+ else if ( depth == 4 ) indent = " ";
+ else if ( depth >= 5 ) indent = " ";
+
+ // Log entry with opcode. CELL means distribution (implicit cons).
+ if ( c3y == u3du(hib) ) {
+ u3l_log("%s>>> ENTER call #%u depth=%u opcode=CELL bus=%s[mug=0x%x]",
+ indent, my_call, depth,
+ (c3y == u3du(bus)) ? "cell" : "atom",
+ u3r_mug(bus));
+ } else {
+ // Opcodes 0-12 are the Nock rules (0=slot, 1=const, 2=eval, etc.)
+ u3l_log("%s>>> ENTER call #%u depth=%u opcode=%u bus=%s[mug=0x%x]",
+ indent, my_call, depth, hib,
+ (c3y == u3du(bus)) ? "cell" : "atom",
+ u3r_mug(bus));
+ }
call_count++;
}
+ depth++;
+
u3t_on(noc_o);
#if 0
pro = _n_nock_on(bus, fol);
@@ -2968,6 +2980,27 @@ u3n_nock_on(u3_noun bus, u3_noun fol)
#endif
u3t_off(noc_o);
+ // Restore depth before logging exit (so depth matches the ENTER log)
+ depth--;
+
+ if ( c3y == should_log ) {
+ // Rebuild indentation to match the ENTER log
+ const char* indent = "";
+ if ( depth == 0 ) indent = "";
+ else if ( depth == 1 ) indent = " ";
+ else if ( depth == 2 ) indent = " ";
+ else if ( depth == 3 ) indent = " ";
+ else if ( depth == 4 ) indent = " ";
+ else if ( depth >= 5 ) indent = " ";
+
+ // Log exit with the result. Use my_call (not call_count) to match ENTER.
+ // The mug (hash) helps track if the same noun is returned multiple times.
+ u3l_log("%s<<< EXIT call #%u depth=%u returns=%s[mug=0x%x]",
+ indent, my_call, depth,
+ (c3y == u3du(pro)) ? "cell" : "atom",
+ u3r_mug(pro));
+ }
+
return pro;
}
diff --git a/vere/pkg/vere/ivory_boot_test.c b/vere/pkg/vere/ivory_boot_test.c
new file mode 100644
index 0000000..0bd968b
--- /dev/null
+++ b/vere/pkg/vere/ivory_boot_test.c
@@ -0,0 +1,72 @@
+/// @file
+
+// #include "ivory.h"
+#include "noun.h"
+#include "ur/ur.h"
+#include "vere.h"
+
+/* _setup(): prepare for tests.
+*/
+static void
+_setup(void)
+{
+ u3C.wag_w |= u3o_hashless;
+ u3m_boot_lite(1 << 28); // 256MB loom for solid pill
+
+ // Load OCaml's ivory.pill file instead of embedded
+ u3l_log("Loading ivory.pill from OCaml directory...");
+ u3_noun ivory_jammed = u3m_file("/home/y/code/urbit/vere/ocaml/ivory.pill");
+ u3l_log("Cuing ivory.pill...");
+ u3_noun pil = u3ke_cue(ivory_jammed);
+ // Don't free ivory_jammed - it's managed by u3m_file
+
+ u3l_log("ivory_pil is_atom: %u", u3a_is_atom(pil));
+
+ // Boot with ivory pill
+ u3l_log("Booting with ivory.pill from OCaml...");
+ if ( c3n == u3v_boot_lite(pil) ) {
+ printf("*** fail: ivory boot failed\n");
+ exit(1);
+ }
+ u3l_log("✓ Ivory boot completed!");
+}
+
+/* _test_lily(): test small noun parsing.
+*/
+static void
+_test_lily()
+{
+ c3_l lit_l;
+ c3_w big_w[] = {0, 0, 1};
+ u3_noun big = u3i_words(3, big_w);
+ u3_noun cod = u3dc("scot", c3__uv, big);
+
+ if ( c3y == u3v_lily(c3__uv, cod, &lit_l) ) {
+ printf("*** fail _test_lily-1\n");
+ exit(1);
+ }
+ cod = u3dc("scot", c3__ud, 0x7fffffff);
+ if ( (c3n == u3v_lily(c3__ud, cod, &lit_l)) ||
+ (0x7fffffff != lit_l) ) {
+ printf("*** fail _test_lily-2a\n");
+ exit(1);
+ }
+ cod = u3dc("scot", c3__ux, u3i_word(0x80000000));
+ if ( c3y == u3v_lily(c3__ux, cod, &lit_l) ) {
+ printf("*** fail _test_lily-2b\n");
+ exit(1);
+ }
+}
+
+/* main(): run all test cases.
+*/
+int
+main(int argc, char* argv[])
+{
+ _setup();
+
+ _test_lily();
+
+ fprintf(stderr, "test boot: ok\n");
+ return 0;
+}
diff --git a/vere/pkg/vere/solid_boot_test.c b/vere/pkg/vere/solid_boot_test.c
index 1d5c11d..26a0f07 100644
--- a/vere/pkg/vere/solid_boot_test.c
+++ b/vere/pkg/vere/solid_boot_test.c
@@ -258,22 +258,21 @@ _setup(void)
u3C.wag_w |= u3o_hashless;
u3m_boot_lite(1 << 28); // 256MB loom for solid pill
- // Load OCaml's ivory.pill file instead of embedded
- u3l_log("Loading ivory.pill from OCaml directory...");
- u3_noun ivory_jammed = u3m_file("/home/y/code/urbit/vere/ocaml/ivory.pill");
- u3l_log("Cuing ivory.pill...");
- u3_noun pil = u3ke_cue(ivory_jammed);
+ u3l_log("Loading solid.pill from OCaml directory...");
+ u3_noun solid_jammed = u3m_file("/home/y/code/urbit/vere/ocaml/solid.pill");
+ u3l_log("Cuing solid.pill...");
+ u3_noun pil = u3ke_cue(solid_jammed);
// Don't free ivory_jammed - it's managed by u3m_file
- u3l_log("ivory_pil is_atom: %u", u3a_is_atom(pil));
+ u3l_log("solid_pil is_atom: %u", u3a_is_atom(pil));
- // Boot with ivory pill
- u3l_log("Booting with ivory.pill from OCaml...");
+ // Boot with solid pill
+ u3l_log("Booting with solid.pill from OCaml...");
if ( c3n == u3v_boot_lite(pil) ) {
- printf("*** fail: ivory boot failed\n");
+ printf("*** fail: solid boot failed\n");
exit(1);
}
- u3l_log("✓ Ivory boot completed!");
+ u3l_log("✓ solid boot completed!");
}
/* _test_lily(): test small noun parsing.