code wiki / (root) / nx_iterator.nx

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}