feat(reflect): generic value tree + Reflect.serialize/apply + JSON bridge (#44)
All checks were successful
bootstrap / cfree-fixpoint (push) Successful in 17s
ci / build-and-test (push) Successful in 1m15s
commit-lint / conventional-commits (push) Successful in 3s
docs / build-and-deploy (push) Successful in 19s

A self-describing Value node (null/int/fixed/bool/str/list/object) with
constructors, builders (Value.add/put) and accessors (get/at/count/kind/
as_int/as_str/…). Reflect.serialize(entity) walks an entity's whole component
set into a value tree — one member per component, each a sub-object of its
fields — and Reflect.apply(entity, value) writes one back; a fixed field
becomes a fixed node, everything else an int node, so the round-trip is
bit-exact, with the model id under "@kind". Json.encode/parse bridge the tree
to and from compact, stable, diffable text, with fixed written as an exact
terminating decimal that parses back bit-for-bit (verified across the raw
Q16.16 range). Together: a one-call, bit-exact save/load for entities.

Written in Ludic and spliced on demand (runtime/native/value.ludic +
reflect_io.ludic, like Query/Light), so a program that doesn't touch
Value.*/Json.*/Reflect.serialize compiles byte-identically and the C-free
bootstrap fixpoint holds (verified). The general tagged-union/any language type
stays tracked in #1; this ships the concrete value tree the serializer needs.

Adds 21 namespace-method docs pages + Value/Json sections,
examples/library/serialize.ludic, and a regression case. Whole CI set green:
x test 72/72, x test-tools 30/30, check-impl/vocabulary/docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-08-31 15:19:21 +03:00
parent 0d1b09e4f0
commit 9452557f3c
31 changed files with 16753 additions and 14898 deletions

View file

@ -295,6 +295,36 @@ function emit_ns_call(ns: pointer, meth: pointer, e: Node) -> Val {
if (meth == "has") { bare = "world_has"; push(labels, "entity"); push(labels, "prop") }
if (meth == "kind") { bare = "world_kind"; push(labels, "entity") }
if (meth == "model") { bare = "world_model_id"; push(labels, "name") }
# #44 — serialize an entity to a value tree and apply one back (reflect_io.ludic)
if (meth == "serialize") { bare = "reflect_serialize"; push(labels, "entity") }
if (meth == "apply") { bare = "reflect_apply"; push(labels, "entity"); push(labels, "value") }
}
# Value.* — the generic value tree (runtime/native/value.ludic, spliced on
# demand). Nodes are int/fixed/bool/str/list/object; the methods map straight
# to the spliced value_* functions, whose signatures carry the return types.
if (ns == "Value") {
if (meth == "null") { bare = "value_null" }
if (meth == "int") { bare = "value_int"; push(labels, "n") }
if (meth == "fixed") { bare = "value_fixed"; push(labels, "f") }
if (meth == "bool") { bare = "value_bool"; push(labels, "b") }
if (meth == "str") { bare = "value_str"; push(labels, "s") }
if (meth == "list") { bare = "value_list" }
if (meth == "object") { bare = "value_object" }
if (meth == "add") { bare = "value_add"; push(labels, "list"); push(labels, "item") }
if (meth == "put") { bare = "value_put"; push(labels, "obj"); push(labels, "key"); push(labels, "item") }
if (meth == "get") { bare = "value_get"; push(labels, "obj"); push(labels, "key") }
if (meth == "has") { bare = "value_has"; push(labels, "obj"); push(labels, "key") }
if (meth == "at") { bare = "value_at"; push(labels, "list"); push(labels, "index") }
if (meth == "key_at") { bare = "value_key_at"; push(labels, "obj"); push(labels, "index") }
if (meth == "count") { bare = "value_count"; push(labels, "value") }
if (meth == "kind") { bare = "value_kind"; push(labels, "value") }
if (meth == "as_int") { bare = "value_as_int"; push(labels, "value") }
if (meth == "as_str") { bare = "value_as_str"; push(labels, "value") }
}
# Json.* — the text bridge over the value tree (runtime/native/value.ludic).
if (ns == "Json") {
if (meth == "encode") { bare = "json_encode"; push(labels, "value") }
if (meth == "parse") { bare = "json_parse"; push(labels, "text") }
}
if (bare == null) { perr(`unknown builtin {ns}.{meth}`) }
reorder_named(e, labels)