nx_iterator.nx source
↩ module page · 375 lines · 12276 B
1// nx_iterator.nx -- composable iteration; kills the nested-loops bug class.
2//
3// User cardinal 2026-05-15: "how many loops within loops type bullshit
4// do we create that then caused memory leaks and infinite bloat etc,
5// isnt it better just to build the system so its bulletproof there
6// where a loop can call another loop like a function but with better
7// structure i feel like javascript c etc have all created a shit
8// paradigm not a usability paradagim."
9//
10// THE STRUCTURAL FIX: first-class iterators. A pipeline of N
11// transformations expresses as ONE while loop at the consumption end
12// instead of N nested loops. Zero raw nesting; zero per-iteration
13// allocations; clear ownership.
14//
15// Example transformation -- 5 stages, 1 loop:
16//
17// let it: *Iterator = nx_iter_range(0, 100)
18// it = nx_iter_filter(it, NX_FILTER_EVEN, 0)
19// it = nx_iter_map(it, NX_MAP_SQUARE, 0)
20// it = nx_iter_take(it, 5)
21// let v: *nx_int = (sys_mmap(NX_SIZEOF_NX_INT)) as *nx_int
22// while nx_iter_next(it, v) == 1 {
23// // v[0] = next squared even number, up to 5 of them
24// }
25//
26// Same logic in nested-loop style would be 4 deep with per-level
27// state, easy to leak. Iterator style: linear, audit-able, the
28// substrate's Captain Moroni doctrine applied to iteration.
29//
30// NO-CLOSURE-NEEDED DESIGN: filter / map use the same sealed-enum
31// predicate/op kinds as nx_array_ops.nx (NX_FILTER_* and NX_MAP_*).
32// Substrate dispatches internally. ~80% coverage; the remaining
33// 20% custom predicates wait for closure support (queued nxc2 work).
34//
35// CONSUMERS use NX_REDUCE_* op kinds from nx_array_ops.nx (SUM /
36// PRODUCT / MIN / MAX / COUNT / AND / OR / XOR) for fold semantics.
37//
38// MEMORY: each iterator is a fixed-size struct (8 nx_int fields +
39// 1 array pointer + 2 inner pointers = ~88 bytes). Per-pipeline
40// allocation is O(stages) and bounded; no per-iteration allocations.
41// Substrate refuses the "growing-context leak" pattern by design.
42//
43// genealogy_id: clu_liskov_1977_iterators + lisp_loop_steele_1990 +
44// python_pep_234_iterators + rust_iterator_trait_2015 +
45// haskell_lazy_lists_1990
46// lineage_id: composable_iterator_no_nested_loops
47
48// nx_safety_envelope:
49// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
50// sil_target: SIL1
51// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
52// verdict: NOT_YET_EVALUATED
53
54import "nx_syscalls.nx"
55import "nx_tier.nx"
56import "nx_array_ops.nx"
57const NX_MAGIC_1024: i64 = 1024
58
59// ===== Sealed-enum iterator kinds ====================================
60
61const NX_ITER_KIND_RANGE: nx_int = 0
62const NX_ITER_KIND_ARRAY: nx_int = 1
63const NX_ITER_KIND_FILTERED: nx_int = 2
64const NX_ITER_KIND_MAPPED: nx_int = 3
65const NX_ITER_KIND_CHAINED: nx_int = 4
66const NX_ITER_KIND_TAKE: nx_int = 5
67const NX_ITER_KIND_SKIP: nx_int = 6
68const NX_ITER_N_KINDS: nx_int = 7
69
70// Iterator struct. Fixed-size; substrate allocates one per pipeline
71// stage. state_* fields are general-purpose slots whose meaning
72// depends on `kind`:
73//
74// RANGE state_a = current value; state_b = end (exclusive)
75// ARRAY state_a = current index; state_b = n_elements;
76// array_buf = backing storage
77// FILTERED state_c = predicate (NX_FILTER_*); state_d = param;
78// inner_a = source iterator
79// MAPPED state_c = op (NX_MAP_*); state_d = param;
80// inner_a = source iterator
81// CHAINED state_c = phase (0 = drawing from inner_a, 1 = inner_b);
82// inner_a / inner_b = the two sources
83// TAKE state_c = remaining count; inner_a = source iterator
84// SKIP state_c = remaining to skip; inner_a = source iterator
85
86struct Iterator {
87 kind: nx_int,
88 state_a: nx_int,
89 state_b: nx_int,
90 state_c: nx_int,
91 state_d: nx_int,
92 array_buf: *nx_int,
93 inner_a: *Iterator,
94 inner_b: *Iterator,
95}
96
97// ===== Internal allocator =============================================
98
99func _iter_alloc() -> *Iterator {
100 let raw: *u8 = sys_mmap(8 * NX_SIZEOF_NX_INT)
101 let it: *Iterator = raw as *Iterator
102 return it
103}
104
105// ===== Constructors ===================================================
106
107func nx_iter_range(lo: nx_int, hi: nx_int) -> *Iterator {
108 let it: *Iterator = _iter_alloc()
109 it.kind = NX_ITER_KIND_RANGE
110 it.state_a = lo
111 it.state_b = hi
112 return it
113}
114
115func nx_iter_array(arr: *nx_int, n: nx_int) -> *Iterator {
116 let it: *Iterator = _iter_alloc()
117 it.kind = NX_ITER_KIND_ARRAY
118 it.state_a = 0
119 it.state_b = n
120 it.array_buf = arr
121 return it
122}
123
124// ===== Combinators ====================================================
125
126func nx_iter_filter(source: *Iterator, pred: nx_int, param: nx_int) -> *Iterator {
127 let it: *Iterator = _iter_alloc()
128 it.kind = NX_ITER_KIND_FILTERED
129 it.state_c = pred
130 it.state_d = param
131 it.inner_a = source
132 return it
133}
134
135func nx_iter_map(source: *Iterator, op: nx_int, param: nx_int) -> *Iterator {
136 let it: *Iterator = _iter_alloc()
137 it.kind = NX_ITER_KIND_MAPPED
138 it.state_c = op
139 it.state_d = param
140 it.inner_a = source
141 return it
142}
143
144func nx_iter_chain(a: *Iterator, b: *Iterator) -> *Iterator {
145 let it: *Iterator = _iter_alloc()
146 it.kind = NX_ITER_KIND_CHAINED
147 it.state_c = 0 // phase 0 = a
148 it.inner_a = a
149 it.inner_b = b
150 return it
151}
152
153func nx_iter_take(source: *Iterator, n: nx_int) -> *Iterator {
154 let it: *Iterator = _iter_alloc()
155 it.kind = NX_ITER_KIND_TAKE
156 it.state_c = n
157 it.inner_a = source
158 return it
159}
160
161func nx_iter_skip(source: *Iterator, n: nx_int) -> *Iterator {
162 let it: *Iterator = _iter_alloc()
163 it.kind = NX_ITER_KIND_SKIP
164 it.state_c = n
165 it.inner_a = source
166 return it
167}
168
169// ===== Pull-next dispatcher ==========================================
170//
171// Returns 1 if a value was written to out[0]; 0 if the iterator is done.
172// All combinator kinds recursively pull from inner_a (and inner_b for
173// CHAINED). No raw nesting; every stage pulls one item at a time.
174
175func _iter_filter_match(v: nx_int, pred: nx_int, param: nx_int) -> nx_int {
176 if pred == NX_FILTER_GT_ZERO { if v > 0 { return 1 } }
177 if pred == NX_FILTER_LT_ZERO { if v < 0 { return 1 } }
178 if pred == NX_FILTER_GTE_ZERO { if v >= 0 { return 1 } }
179 if pred == NX_FILTER_LTE_ZERO { if v <= 0 { return 1 } }
180 if pred == NX_FILTER_NON_ZERO { if v != 0 { return 1 } }
181 if pred == NX_FILTER_ZERO { if v == 0 { return 1 } }
182 if pred == NX_FILTER_EVEN {
183 let r: nx_int = v - (v / 2) * 2
184 if r == 0 { return 1 }
185 }
186 if pred == NX_FILTER_ODD {
187 let r: nx_int = v - (v / 2) * 2
188 if r != 0 { return 1 }
189 }
190 if pred == NX_FILTER_GT_PARAM { if v > param { return 1 } }
191 if pred == NX_FILTER_LT_PARAM { if v < param { return 1 } }
192 if pred == NX_FILTER_EQ_PARAM { if v == param { return 1 } }
193 return 0
194}
195
196func _iter_map_apply(v: nx_int, op: nx_int, param: nx_int) -> nx_int {
197 if op == NX_MAP_ABS {
198 if v < 0 { return -v }
199 return v
200 }
201 if op == NX_MAP_NEGATE { return -v }
202 if op == NX_MAP_SQUARE { return v * v }
203 if op == NX_MAP_DOUBLE { return v * 2 }
204 if op == NX_MAP_HALVE { return v / 2 }
205 if op == NX_MAP_ADD_PARAM { return v + param }
206 if op == NX_MAP_SUB_PARAM { return v - param }
207 if op == NX_MAP_MUL_PARAM { return v * param }
208 if op == NX_MAP_DIV_PARAM {
209 if param == 0 { return 0 }
210 return v / param
211 }
212 if op == NX_MAP_CLAMP_Q10 {
213 if v < 0 { return 0 }
214 if v > NX_MAGIC_1024 { return NX_MAGIC_1024 }
215 return v
216 }
217 if op == NX_MAP_CLAMP_SIGNED_Q10 {
218 if v < -NX_MAGIC_1024 { return -NX_MAGIC_1024 }
219 if v > NX_MAGIC_1024 { return NX_MAGIC_1024 }
220 return v
221 }
222 if op == NX_MAP_SIGN {
223 if v > 0 { return 1 }
224 if v < 0 { return -1 }
225 return 0
226 }
227 return v
228}
229
230func nx_iter_next(it: *Iterator, out: *nx_int) -> nx_int {
231 let kind: nx_int = it.kind
232
233 if kind == NX_ITER_KIND_RANGE {
234 if it.state_a < it.state_b {
235 out[0] = it.state_a
236 it.state_a = it.state_a + 1
237 return 1
238 }
239 return 0
240 }
241
242 if kind == NX_ITER_KIND_ARRAY {
243 if it.state_a < it.state_b {
244 out[0] = it.array_buf[it.state_a]
245 it.state_a = it.state_a + 1
246 return 1
247 }
248 return 0
249 }
250
251 if kind == NX_ITER_KIND_FILTERED {
252 var done: nx_int = 0
253 while done == 0 {
254 let got: nx_int = nx_iter_next(it.inner_a, out)
255 if got == 0 { return 0 }
256 if _iter_filter_match(out[0], it.state_c, it.state_d) == 1 {
257 return 1
258 }
259 }
260 return 0
261 }
262
263 if kind == NX_ITER_KIND_MAPPED {
264 let got: nx_int = nx_iter_next(it.inner_a, out)
265 if got == 0 { return 0 }
266 out[0] = _iter_map_apply(out[0], it.state_c, it.state_d)
267 return 1
268 }
269
270 if kind == NX_ITER_KIND_CHAINED {
271 if it.state_c == 0 {
272 let got_a: nx_int = nx_iter_next(it.inner_a, out)
273 if got_a == 1 { return 1 }
274 it.state_c = 1
275 }
276 return nx_iter_next(it.inner_b, out)
277 }
278
279 if kind == NX_ITER_KIND_TAKE {
280 if it.state_c <= 0 { return 0 }
281 let got: nx_int = nx_iter_next(it.inner_a, out)
282 if got == 0 { return 0 }
283 it.state_c = it.state_c - 1
284 return 1
285 }
286
287 if kind == NX_ITER_KIND_SKIP {
288 while it.state_c > 0 {
289 let g: nx_int = nx_iter_next(it.inner_a, out)
290 if g == 0 { return 0 }
291 it.state_c = it.state_c - 1
292 }
293 return nx_iter_next(it.inner_a, out)
294 }
295
296 return 0
297}
298
299// ===== Consumers (run the pipeline to completion) ===================
300
301// Count items emitted by the pipeline.
302func nx_iter_count(it: *Iterator) -> nx_int {
303 let v_buf: *nx_int = (sys_mmap(NX_SIZEOF_NX_INT)) as *nx_int
304 var n: nx_int = 0
305 while nx_iter_next(it, v_buf) == 1 {
306 n = n + 1
307 }
308 return n
309}
310
311// Reduce via a sealed-enum op (reuses NX_REDUCE_* from nx_array_ops).
312func nx_iter_reduce(it: *Iterator, op: nx_int) -> nx_int {
313 let v_buf: *nx_int = (sys_mmap(NX_SIZEOF_NX_INT)) as *nx_int
314 var acc: nx_int = 0
315 if op == NX_REDUCE_PRODUCT { acc = 1 }
316 if op == NX_REDUCE_AND { acc = -1 } // all-ones for AND identity
317 var first: nx_int = 1
318 while nx_iter_next(it, v_buf) == 1 {
319 let v: nx_int = v_buf[0]
320 if op == NX_REDUCE_SUM { acc = acc + v }
321 if op == NX_REDUCE_PRODUCT { acc = acc * v }
322 if op == NX_REDUCE_MIN {
323 if first == 1 { acc = v }
324 if first == 0 {
325 if v < acc { acc = v }
326 }
327 }
328 if op == NX_REDUCE_MAX {
329 if first == 1 { acc = v }
330 if first == 0 {
331 if v > acc { acc = v }
332 }
333 }
334 if op == NX_REDUCE_COUNT { acc = acc + 1 }
335 if op == NX_REDUCE_AND { acc = acc & v }
336 if op == NX_REDUCE_OR { acc = acc | v }
337 if op == NX_REDUCE_XOR { acc = acc ^ v }
338 first = 0
339 }
340 return acc
341}
342
343// Convenience consumers.
344func nx_iter_sum(it: *Iterator) -> nx_int {
345 return nx_iter_reduce(it, NX_REDUCE_SUM)
346}
347
348func nx_iter_min(it: *Iterator) -> nx_int {
349 return nx_iter_reduce(it, NX_REDUCE_MIN)
350}
351
352func nx_iter_max(it: *Iterator) -> nx_int {
353 return nx_iter_reduce(it, NX_REDUCE_MAX)
354}
355
356// Collect into a caller-provided array (up to max_out). Returns
357// the count written.
358func nx_iter_collect(it: *Iterator, out: *nx_int, max_out: nx_int) -> nx_int {
359 let v_buf: *nx_int = (sys_mmap(NX_SIZEOF_NX_INT)) as *nx_int
360 var n: nx_int = 0
361 while nx_iter_next(it, v_buf) == 1 {
362 if n >= max_out { return n }
363 out[n] = v_buf[0]
364 n = n + 1
365 }
366 return n
367}
368
369// ===== Sealed-enum validity =========================================
370
371func nx_iter_kind_is_valid(k: nx_int) -> nx_int {
372 if k < 0 { return 0 }
373 if k >= NX_ITER_N_KINDS { return 0 }
374 return 1
375}