nx_primitive_registry.nx source
↩ module page · 173 lines · 6419 B
1// nx_primitive_registry.nx -- callable-by-name primitive catalog.
2//
3// Per user 2026-05-15: "actually ingest and use all the algorithms
4// known as primitives as callable elements like functions like other
5// systems".
6//
7// THE PATTERN (NumPy / SciPy / Mathematica / MATLAB):
8// - Every primitive has a stable name (string)
9// - The catalog records (name, signature_id, domain, complexity,
10// provenance), enabling discovery and introspection
11// - Lookup-by-name returns a stable IntID; downstream code
12// dispatches by ID for performance
13// - Per-domain query lets the user enumerate "what physics
14// primitives are available?"
15//
16// NOTE: NishiLang doesn't yet have first-class function pointers in
17// the user-source surface, so the registry stores METADATA + a stable
18// dispatch_id. Caller chooses to invoke by direct symbol name (the
19// fast path -- L3 callable) OR via the dispatcher (the slow but
20// uniform path). Adding first-class fn pointers is a named blocker
21// for full Mathematica-style Symbolic[] dispatch and is on the
22// compiler-improvements queue.
23
24// nx_safety_envelope:
25// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
26// sil_target: SIL1
27// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
28// verdict: NOT_YET_EVALUATED
29
30import "nx_kernel_v2.nx"
31
32// ===== Domain enum (sealed) =========================================
33const NX_DOMAIN_LOGIC: nx_int = 1 // kernel rules + propositional engine
34const NX_DOMAIN_ARITH: nx_int = 2 // nat / int / rat / real Term substrate
35const NX_DOMAIN_LINALG: nx_int = 3 // vec / matrix
36const NX_DOMAIN_CALC: nx_int = 4 // symbolic derivative / integral
37const NX_DOMAIN_PHYSICS: nx_int = 5 // units + dim analysis + dynamics
38const NX_DOMAIN_CHEM: nx_int = 6 // atomic table + stoichiometry
39const NX_DOMAIN_PROB: nx_int = 7 // probability + statistics
40const NX_DOMAIN_PROOF: nx_int = 8 // proof construction (engine + tactics)
41const NX_DOMAIN_RENDER: nx_int = 9 // visual output (CLI + browser)
42const NX_DOMAIN_DATA: nx_int = 10 // sketches / hashes / encodings
43
44// ===== Complexity buckets (Q-format big-O hints) ====================
45const NX_COMPLEX_O1: nx_int = 1
46const NX_COMPLEX_O_LOG_N: nx_int = 2
47const NX_COMPLEX_O_N: nx_int = 3
48const NX_COMPLEX_O_N_LOG_N: nx_int = 4
49const NX_COMPLEX_O_N2: nx_int = 5
50const NX_COMPLEX_O_N3: nx_int = 6
51const NX_COMPLEX_O_2N: nx_int = 7
52const NX_COMPLEX_NP: nx_int = 8
53
54// ===== Provenance ====================================================
55// PROVED_NATIVE: implementation lives in NishiLang + has a kernel
56// chain or smoke-verified semantics. PENDING: registered name only,
57// no implementation yet. No third-party-trust status (cardinal).
58const NX_PRIM_PROVED_NATIVE: nx_int = 0
59const NX_PRIM_PENDING: nx_int = 1
60
61// ===== One catalog entry ============================================
62struct PrimEntry {
63 name: *u8,
64 name_len: nx_int,
65 sig_id: nx_int, // signature schema id (1=Term->Term, 2=Term*Term->Term, etc.)
66 domain: nx_int,
67 complexity: nx_int,
68 status: nx_int,
69 dispatch_id: nx_int, // stable 0-indexed id
70}
71const NX_PRIM_BYTES: nx_int = 56
72
73struct PrimRegistry {
74 entries: *PrimEntry,
75 n: nx_int,
76 cap: nx_int,
77}
78const NX_PRIM_REG_BYTES: nx_int = 24
79
80func nx_prim_registry_new(cap: nx_int) -> *PrimRegistry {
81 let r: *PrimRegistry = (sys_mmap(NX_PRIM_REG_BYTES as i64)) as *PrimRegistry
82 r.entries = (sys_mmap((cap * NX_PRIM_BYTES) as i64)) as *PrimEntry
83 r.n = 0
84 r.cap = cap
85 return r
86}
87
88func nx_prim_at(r: *PrimRegistry, i: nx_int) -> *PrimEntry {
89 return ((r.entries as nx_int) + (i * NX_PRIM_BYTES)) as *PrimEntry
90}
91
92// Register a primitive. Returns dispatch_id (>= 0) or -1 if full.
93func nx_prim_register(
94 r: *PrimRegistry,
95 name: *u8, name_len: nx_int,
96 sig_id: nx_int, domain: nx_int,
97 complexity: nx_int, status: nx_int
98) -> nx_int {
99 if r.n >= r.cap { return 0 - 1 }
100 let e: *PrimEntry = nx_prim_at(r, r.n)
101 e.name = name
102 e.name_len = name_len
103 e.sig_id = sig_id
104 e.domain = domain
105 e.complexity = complexity
106 e.status = status
107 e.dispatch_id = r.n
108 let id: nx_int = r.n
109 r.n = r.n + 1
110 return id
111}
112
113// Lookup: return dispatch_id of named primitive, or -1.
114// Linear scan; fine for catalogs under ~500 entries. For larger
115// catalogs use a hash index (named follow-up).
116func nx_prim_lookup(r: *PrimRegistry, name: *u8, name_len: nx_int) -> nx_int {
117 var i: nx_int = 0
118 while i < r.n {
119 let e: *PrimEntry = nx_prim_at(r, i)
120 if e.name_len == name_len {
121 var same: nx_int = 1
122 var j: nx_int = 0
123 while j < name_len {
124 let a: *u8 = ((name as nx_int) + j) as *u8
125 let b: *u8 = ((e.name as nx_int) + j) as *u8
126 if a[0] != b[0] { same = 0 }
127 j = j + 1
128 }
129 if same == 1 { return e.dispatch_id }
130 }
131 i = i + 1
132 }
133 return 0 - 1
134}
135
136// Count primitives in a given domain.
137func nx_prim_count_by_domain(r: *PrimRegistry, domain: nx_int) -> nx_int {
138 var n: nx_int = 0
139 var i: nx_int = 0
140 while i < r.n {
141 let e: *PrimEntry = nx_prim_at(r, i)
142 if e.domain == domain { n = n + 1 }
143 i = i + 1
144 }
145 return n
146}
147
148// Count by status.
149func nx_prim_count_by_status(r: *PrimRegistry, status: nx_int) -> nx_int {
150 var n: nx_int = 0
151 var i: nx_int = 0
152 while i < r.n {
153 let e: *PrimEntry = nx_prim_at(r, i)
154 if e.status == status { n = n + 1 }
155 i = i + 1
156 }
157 return n
158}
159
160// Domain name pretty-print.
161func nx_prim_domain_name(d: nx_int) -> *u8 {
162 if d == NX_DOMAIN_LOGIC { return "LOGIC " as *u8 }
163 if d == NX_DOMAIN_ARITH { return "ARITH " as *u8 }
164 if d == NX_DOMAIN_LINALG { return "LINALG " as *u8 }
165 if d == NX_DOMAIN_CALC { return "CALC " as *u8 }
166 if d == NX_DOMAIN_PHYSICS { return "PHYSICS " as *u8 }
167 if d == NX_DOMAIN_CHEM { return "CHEM " as *u8 }
168 if d == NX_DOMAIN_PROB { return "PROB " as *u8 }
169 if d == NX_DOMAIN_PROOF { return "PROOF " as *u8 }
170 if d == NX_DOMAIN_RENDER { return "RENDER " as *u8 }
171 if d == NX_DOMAIN_DATA { return "DATA " as *u8 }
172 return "UNKNOWN " as *u8
173}