aboutsummaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorGravatar Moon Flower <moonflower@elhenney.local> 2026-10-14 08:29:46 +0330
committerGravatar Moon Flower <moonflower@elhenney.local> 2026-10-14 08:29:46 +0330
commit1c53304d03414890af0da7432d1aea87e754a14a (patch)
tree97190d86cf548c9bc63f68c86a4d2a4f09809713 /src/lib
parenta (diff)
feat: grow memory limits, add icon/actor libraries, overhaul LSP diagnostics
Compiler and VM - make every memory map limit dynamic and adjustable at both build time and runtime, including compiler tables, stacks, and every execution region - reserve and grow tables, operand and return stacks, frame segments, and image-building output without relocating live code or globals - rename i/o and string helpers consistently (puts -> put_s, putn -> put_n, scat -> cat_s) across compiler, runtime, examples, tests, and docs - recast and consume values assigned to globals from inside functions - allow fixed and const together, add typed closure returns (fn:t(...)), multi-include syntax (include rt, run, core;), and recurse type inference - never infer string literals as word, anywhere, as argument, variable, or return type - draft docs/BORROW.md: a borrow checker less intrusive than Rust's but more intelligent LSP and editor integration - require explicit includes and give every toolchain area its own namespace (rt, lsp, run, core) - hover for built-in types, inferred types, values known at hover time, array and pointer lifetime/size, library names in include statements, and inferred lifetimes with explanations - honor /* */ comments as documentation for variables and arguments, and stop misattributing section headers or trailing comments as variable hints - stream diagnostics and cancel stale requests on change - jump to definition and references, including cross-file - quick fixes with automatic library import, and namespace completion Hints and diagnostics - render hints as blue squigglies by default, with hints on/small/off per file - suppress magic-number hints via a preceding /* */ comment, and say so - warn on unused arguments; allow multiple _ discards in parameters, pattern matching, and tagged enums, warning if a _ is actually used - rewrite hint and comment prose in professional compiler-doc register Libraries - add icon.zc: run-time self-modification, find/replace syntax, type and block edits, and step-through inspection of locals and globals - add actor.zc: async actors with Actor<I,O>, implicit this, spawn/recv/send, injected into the compiler machinery at include time rather than built in Tooling and docs - reorganize sources, tests, and designs into subdirectories - unify windows entry points in tinyvm.bat and add a matching tinyvm.sh - add --help across the toolchain - polish the learning guide, stdlib docs, and design notes Tests - add accept, reject, and regression coverage across the compiler, LSP, - runtime, icon, actors, threads, formatting, and the toolchain contracts, plus fuzzers and property tests
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/actor.zc280
-rw-r--r--src/lib/icon.zc623
-rw-r--r--src/lib/std.zc327
3 files changed, 1141 insertions, 89 deletions
diff --git a/src/lib/actor.zc b/src/lib/actor.zc
new file mode 100644
index 0000000..29fd600
--- /dev/null
+++ b/src/lib/actor.zc
@@ -0,0 +1,280 @@
+namespace actor;
+include rt, run;
+
+// stream entries are raw words: integer payloads never acquire heap ownership.
+unchecked word vector() { return rt.dnew(1, 0, 0); }
+unchecked int append(word vector, word value) {
+ rt.dgrow(vector, vector[0] + 1); vector[2][vector[0]] = value; vector[0]++; return 0;
+}
+unchecked int find(word refs, word value) {
+ int i = 0; while (i < refs[0]) { if (refs[2][i] == value) return i; i++; } return -1;
+}
+unchecked int size(word type) { return type[1]; }
+unchecked int packed(word type) { if (type[0] == 1 || type[0] == 12) return type[5]; return 0; }
+
+// encode pointer graphs with identity records; decoding creates fresh objects.
+// typed fields determine references, so an integer equal to a heap address
+// remains an integer. repeated references and cycles preserve identity.
+unchecked int encode(word data, word refs, word address, word type) {
+ int kind = type[0], i = 0, offset, bytes, n, id; word pointer, element;
+ if (kind == 8) {
+ while (i < type[3]) {
+ element = type[4][i * 3 + 1]; offset = type[4][i * 3 + 2];
+ if (offset < 0) { bytes = packed(element); n = 0; int k = 0; while (k < bytes) { n |= rt.ld8(rt.addr_b(address) - offset - 1 + k) << (k * 8); k++; } append(data, n); }
+ else encode(data, refs, address + offset, element);
+ i++;
+ }
+ return 0;
+ }
+ if (kind == 6) {
+ element = type[4]; bytes = packed(element);
+ while (i < type[3]) {
+ if (bytes) { n = 0; int k = 0; while (k < bytes) { n |= rt.ld8(rt.addr_b(address) + i * bytes + k) << (k * 8); k++; } append(data, n); }
+ else encode(data, refs, address + i * size(element), element);
+ i++;
+ }
+ return 0;
+ }
+ if (kind != 5 && kind != 11 && kind != 13 && kind != 7) { append(data, *address); return 0; }
+ pointer = *address;
+ if (!pointer) { append(data, 0); if (kind == 7) append(data, 0); return 0; }
+ if (kind == 7 && rt.callable_code(pointer)) { append(data, 0); append(data, pointer); return 0; }
+ id = find(refs, pointer);
+ if (id >= 0) { append(data, id + 1); return 0; }
+ id = refs[0]; append(refs, pointer); append(data, 0 - id - 1);
+ if (kind == 11) {
+ n = pointer[0]; append(data, n); element = type[4]; bytes = packed(element);
+ while (i < n) {
+ if (bytes) { int k = 0; int value = 0; while (k < bytes) { value |= rt.ld8(rt.addr_b(pointer[2]) + i * bytes + k) << (k * 8); k++; } append(data, value); }
+ else encode(data, refs, pointer[2] + i * size(element), element);
+ i++;
+ }
+ return 0;
+ }
+ if (kind == 13) { n = rt.len_s(pointer); append(data, n); while (i < n) { append(data, rt.at_s(pointer, i)); i++; } return 0; }
+ if (kind == 7) {
+ element = pointer[-1]; n = pointer[-5] - rt.HDR;
+ append(data, n); append(data, element); encode(data, refs, pointer, element); return 0;
+ }
+ element = type[4]; bytes = packed(element);
+ if (rt.isobj(pointer)) n = pointer[-5] - rt.HDR;
+ else if (element[0] == 2) n = rt.len_w(pointer) + 1;
+ else n = size(element);
+ append(data, n);
+ if (bytes || size(element) <= 0) { while (i < n) { append(data, pointer[i]); i++; } }
+ else { while (i + size(element) <= n) { encode(data, refs, pointer + i, element); i += size(element); } }
+ return 0;
+}
+unchecked word take(word frame) { word result = frame[0][2][frame[1]]; frame[1]++; return result; }
+unchecked int decode(word frame, word refs, word address, word type) {
+ int kind = type[0], i = 0, offset, bytes, n, id; word pointer, element;
+ if (kind == 8) {
+ while (i < type[3]) {
+ element = type[4][i * 3 + 1]; offset = type[4][i * 3 + 2];
+ if (offset < 0) { bytes = packed(element); word value = take(frame); int k = 0; while (k < bytes) { rt.st8(rt.addr_b(address) - offset - 1 + k, value >> (k * 8)); k++; } }
+ else decode(frame, refs, address + offset, element);
+ i++;
+ }
+ return 0;
+ }
+ if (kind == 6) {
+ element = type[4]; bytes = packed(element);
+ while (i < type[3]) {
+ if (bytes) { word value = take(frame); int k = 0; while (k < bytes) { rt.st8(rt.addr_b(address) + i * bytes + k, value >> (k * 8)); k++; } }
+ else decode(frame, refs, address + i * size(element), element);
+ i++;
+ }
+ return 0;
+ }
+ if (kind != 5 && kind != 11 && kind != 13 && kind != 7) { *address = take(frame); return 0; }
+ id = take(frame);
+ if (!id) { if (kind == 7) *address = take(frame); else *address = 0; return 0; }
+ if (id > 0) { *address = refs[2][id - 1]; return 0; }
+ n = take(frame);
+ if (kind == 11) {
+ element = type[4]; bytes = packed(element);
+ if (bytes) pointer = rt.dnew(0 - bytes, n, type); else pointer = rt.dnew(size(element), n, type);
+ append(refs, pointer); *address = pointer;
+ while (i < n) {
+ if (bytes) { word value = take(frame); int k = 0; while (k < bytes) { rt.st8(rt.addr_b(pointer[2]) + i * bytes + k, value >> (k * 8)); k++; } }
+ else decode(frame, refs, pointer[2] + i * size(element), element);
+ i++;
+ }
+ return 0;
+ }
+ if (kind == 13) {
+ pointer = rt.new((n + 7) / 8 + 1); pointer[0] = n; append(refs, pointer); *address = pointer;
+ while (i < n) { rt.st8(rt.addr_b(pointer + 1) + i, take(frame)); i++; } return 0;
+ }
+ if (kind == 7) { element = take(frame); pointer = rt.new_t(n, element); append(refs, pointer); *address = pointer; decode(frame, refs, pointer, element); return 0; }
+ pointer = rt.new_t(n, type[4]); append(refs, pointer); *address = pointer;
+ element = type[4]; bytes = packed(element);
+ if (bytes || size(element) <= 0) { while (i < n) { pointer[i] = take(frame); i++; } }
+ else { while (i + size(element) <= n) { decode(frame, refs, pointer + i, element); i += size(element); } }
+ return 0;
+}
+unchecked int write_message(int fd, word address, word type) {
+ word data = vector(), refs = vector(); int count;
+ encode(data, refs, address, type); count = data[0];
+ int result = rt.syscall(44, fd, &count, 1);
+ if (result >= 0) result = rt.syscall(44, fd, data[2], count);
+ rt.free(refs); rt.free(data); return result;
+}
+unchecked int read_message(int fd, word address, word type) {
+ int count = 0; word data, refs, frame[2];
+ if (rt.syscall(45, fd, &count, 1) < 0 || count < 0 || count > 1048576) return -1;
+ data = rt.dnew(1, count, 0); refs = vector(); frame[0] = data; frame[1] = 0;
+ if (rt.syscall(45, fd, data[2], count) < 0) { rt.free(refs); rt.free(data); return -1; }
+ decode(frame, refs, address, type); rt.free(refs); rt.free(data); return 0;
+}
+
+int busy, worker;
+type Dispatcher = fn();
+Dispatcher[?] dispatchers;
+unchecked int dispatch() {
+ if (busy || worker || !dispatchers) return 0;
+ busy = 1; int i = 0;
+ while (i < len(dispatchers)) { if (dispatchers[i]) dispatchers[i](); i++; }
+ busy = 0; return 0;
+}
+
+// declared actors expose a typed handle method and receive their own endpoint.
+// lowering lives in this imported source; the compiler only dispatches callbacks.
+unchecked int declaration_parameters(word type) {
+ if (type[0] != run.TY_GEN) return 0;
+ rt.put_c('<'); int i = 0;
+ while (i < type[3]) { if (i) rt.put_c(','); rt.put_s(type[6][i]); i++; }
+ rt.put_c('>'); return 0;
+}
+unchecked int declaration_type(word type) { rt.put_s(type[2]); declaration_parameters(type); return 0; }
+unchecked int declaration_method(word type, char* method) {
+ rt.put_s("unchecked fn "); rt.put_s(type[2]); rt.put_c('.'); rt.put_s(method); declaration_parameters(type); rt.put_c('('); declaration_type(type); rt.put_s("* this"); return 0;
+}
+unchecked int parse_actor() {
+ int symbol = run.kw_struct(); word type = run.sval[symbol], signature, fields, oldfields;
+ int handler, count, mode, length, capacity; word output, previous_grow; char* buffer;
+ if (type[0] == run.TY_GEN && rt.eq_w(type[2], "actor.Actor")) return 0;
+ handler = run.methof(type, run.intern("handle"));
+ if (handler < 0) { run.serr("an actor declaration requires handle(input) with a typed result"); return 0; }
+ signature = run.stype[handler];
+ if (!signature || signature[3] != 2) { run.serr("an actor handle method requires one input parameter"); return 0; }
+ int generic = type[0] == run.TY_GEN, stride = 3 - generic;
+ count = type[3]; if (generic) count = type[5]; int field_index = 0;
+ while (field_index < count) { if (type[4][field_index * stride] == run.intern("__mailbox")) { run.serr("__mailbox is reserved for actor state"); return 0; } field_index++; }
+ oldfields = type[4]; fields = run.talloc((count + 1) * stride + 1);
+ rt.copy_w(fields, oldfields, count * stride); fields[count * stride] = run.intern("__mailbox");
+ fields[count * stride + 1] = run.tword;
+ if (generic) type[5]++; else { fields[count * stride + 2] = type[1]; type[3]++; type[1]++; }
+ type[4] = fields;
+ buffer = rt.new(16384); mode = rt.outmode; output = rt.outbuf; length = rt.outn; capacity = rt.outcap; previous_grow = rt.outgrow;
+ rt.outgrow = 0; rt.outmode = 2; rt.outbuf = buffer; rt.outn = 0; rt.outcap = 16383;
+ declaration_method(type, "spawn"); rt.put_s(") { if (this.__mailbox) return -1; actor.Actor<");
+ run.tprint(signature[8]); rt.put_s(","); run.tprint(signature[4]); rt.put_s(">* endpoint = rt.new(actor.Actor<");
+ run.tprint(signature[8]); rt.put_s(","); run.tprint(signature[4]); rt.put_s(">); this.__mailbox = endpoint; return endpoint.spawn(fn(");
+ run.tprint(signature[8]); rt.put_s(" message) { return this.handle(message); }); } ");
+ int index = 0;
+ while (index < 5) {
+ if (index == 0) declaration_method(type, "send"); else if (index == 1) declaration_method(type, "recv"); else if (index == 2) declaration_method(type, "await"); else if (index == 3) declaration_method(type, "close"); else declaration_method(type, "poll");
+ if (index == 0) { rt.put_s(","); run.tprint(signature[8]); rt.put_s(" message"); }
+ if (index == 1) { rt.put_s(",int("); run.tprint(signature[4]); rt.put_s(").d callback"); }
+ rt.put_s(") { actor.Actor<"); run.tprint(signature[8]); rt.put_s(","); run.tprint(signature[4]); rt.put_s(">* endpoint = this.__mailbox as actor.Actor<");
+ run.tprint(signature[8]); rt.put_s(","); run.tprint(signature[4]); rt.put_s(">*; if (!endpoint) return -1; return endpoint.");
+ if (index == 0) rt.put_s("send(message);"); else if (index == 1) rt.put_s("recv(callback);"); else if (index == 2) rt.put_s("await();"); else if (index == 3) rt.put_s("close();"); else rt.put_s("poll();");
+ rt.put_s(" } "); index++;
+ }
+ buffer[rt.outn] = 0; rt.outgrow = previous_grow; rt.outmode = mode; rt.outbuf = output; rt.outn = length; rt.outcap = capacity;
+ word tokens = run.tokens(buffer);
+ if (generic) {
+ int position = 0, variable;
+ while (position < tokens[0]) {
+ variable = 0;
+ while (variable < run.ntl) {
+ word descriptor = run.typelist[variable];
+ if (descriptor[0] == run.TY_VAR && descriptor[3] < type[3] && tokens[1 + position * 3] == run.T_ID && tokens[2 + position * 3] == descriptor[2]) tokens[2 + position * 3] = type[6][descriptor[3]];
+ variable++;
+ }
+ position++;
+ }
+ }
+ word lowered = rt.new(tokens[0] * 3 + 1); int read = 0, written = 0;
+ while (read < tokens[0]) {
+ if (read > 0 && tokens[2 + (read - 1) * 3] == run.s_fn && read + 2 < tokens[0] && tokens[2 + (read + 1) * 3] == run.s_dot) {
+ lowered[1 + written * 3] = run.T_ID; lowered[2 + written * 3] = run.methname(tokens[2 + read * 3], tokens[2 + (read + 2) * 3]); lowered[3 + written * 3] = tokens[3 + read * 3]; read += 3;
+ } else { rt.copy_w(lowered + 1 + written * 3, tokens + 1 + read * 3, 3); read++; }
+ written++;
+ }
+ lowered[0] = written; run.compile_list_context(lowered, 1, run.module_name, run.module_imports); rt.free(lowered); rt.free(tokens); rt.free(buffer); return 0;
+}
+run.syntax("actor", actor.parse_actor);
+
+actor Actor<I, O> {
+ O(I) handler;
+ int(O) receiver;
+ int fd, process, pending, closed, dispatcher_index;
+
+ // threads receive a private vm snapshot and an owned channel endpoint.
+ unchecked fn spawn(O(I).d callback) {
+ if (this.process || this.closed || !callback) return -1;
+ this.handler = callback;
+ worker = 1;
+ this.process = rt.syscall(51, fn() {
+ worker = 1;
+ while (1) {
+ I message; O result;
+ if (read_message(this.fd, &message, typeof(I)) < 0) break;
+ result = this.handler(message);
+ if (write_message(this.fd, &result, typeof(O)) < 0) break;
+ }
+ return 0;
+ } as word, &this.fd, 0);
+ if (this.process < 0) { worker = 0; return -1; }
+ worker = 0;
+ if (!dispatchers) dispatchers = rt.new(Dispatcher[?]);
+ this.dispatcher_index = len(dispatchers);
+ push(dispatchers, fn:word() { this.poll(); return 0; });
+ return 0;
+ }
+
+ // sends a snapshot; the worker executes it asynchronously on its own cpu.
+ unchecked fn send(I message) {
+ if (this.process <= 0 || this.closed) return -1;
+ if (write_message(this.fd, &message, typeof(I)) < 0) { this.close(); return -1; }
+ this.pending++; return 0;
+ }
+
+ // registration does not wait; statement callbacks deliver ready replies.
+ unchecked fn recv(int(O).d callback) {
+ if (this.closed || !callback) return -1;
+ this.receiver = callback; this.poll(); return 0;
+ }
+
+ unchecked fn poll() {
+ O result;
+ if (!this.receiver || this.closed || this.process <= 0) return 0;
+ int previous_busy = busy; busy = 1;
+ while (!this.closed && this.pending > 0 && rt.syscall(46, this.fd, 0, 0) > 0) {
+ if (read_message(this.fd, &result, typeof(O)) < 0) { busy = previous_busy; this.close(); return -1; }
+ this.pending--; this.receiver(result);
+ }
+ busy = previous_busy; return 0;
+ }
+
+ // waits until every submitted message has reached the registered receiver.
+ unchecked fn await() {
+ if (!this.receiver && this.pending) return -1;
+ while (this.pending > 0 && !this.closed) {
+ if (rt.syscall(46, this.fd, -1, 0) < 0 || this.poll() < 0) return -1;
+ }
+ return 0;
+ }
+
+ unchecked fn close() {
+ if (this.closed) return 0;
+ this.closed = 1;
+ if (this.process > 0 && dispatchers) dispatchers[this.dispatcher_index] = 0;
+ if (this.process > 0) rt.syscall(52, this.process, 1, 0);
+ this.handler = 0; this.receiver = 0; return 0;
+ }
+}
+
+run.statement_hook(actor.dispatch);
diff --git a/src/lib/icon.zc b/src/lib/icon.zc
new file mode 100644
index 0000000..646a832
--- /dev/null
+++ b/src/lib/icon.zc
@@ -0,0 +1,623 @@
+namespace icon;
+include rt, run;
+
+// structured runtime transformations of compiler token lists.
+// token storage follows run.tokens: count, then kind, value and source line.
+// edits create independent lists; publication recompiles a function only after
+// delimiter validation. captures match complete balanced token ranges.
+
+int icon_error;
+
+int icon_eq(word* a, int ai, word* b, int bi, int n) {
+ int i = 0;
+ while (i < n) {
+ if (a[1 + (ai + i) * 3] != b[1 + (bi + i) * 3] || a[2 + (ai + i) * 3] != b[2 + (bi + i) * 3]) return 0;
+ i++;
+ }
+ return 1;
+}
+int icon_value(word* q, int i) { if (i < 0 || i >= q[0]) return 0; return q[2 + i * 3]; }
+int icon_kind(word* q, int i) { if (i < 0 || i >= q[0]) return 0; return q[1 + i * 3]; }
+int icon_is(word* q, int i, char* s) {
+ int kind = run.T_OP; if (s[0] >= 'a' && s[0] <= 'z') kind = run.T_ID;
+ return icon_kind(q, i) == kind && icon_value(q, i) == run.intern(s);
+}
+int icon_open(int v) { return v == run.intern("(") || v == run.intern("[") || v == run.intern("{"); }
+int icon_close(int v) { return v == run.intern(")") || v == run.intern("]") || v == run.intern("}"); }
+int icon_pair(int a, int b) {
+ return (a == run.intern("(") && b == run.intern(")")) || (a == run.intern("[") && b == run.intern("]")) || (a == run.intern("{") && b == run.intern("}"));
+}
+int icon_declaration(word* q, int start, int binding) {
+ int first = icon_value(q, start), i = start;
+ if (icon_kind(q, start) != run.T_ID || (first != run.intern("let") && first != run.intern("const") && !run.istypename(first))) return 0;
+ while (i < binding) { if (icon_is(q, i, "(") || icon_is(q, i, "=")) return 0; i++; }
+ return binding > start;
+}
+// locate a simple declaration at a statement boundary, including nested blocks.
+// result records [type start, binding position, semicolon, statement start].
+int icon_locate_declaration(word* q, int id, word* result) {
+ int start = 0, i, depth, v, binding, comma, type_start;
+ while (start < q[0]) {
+ if (start && !icon_is(q, start - 1, ";") && !icon_is(q, start - 1, "{") && !icon_is(q, start - 1, "}")) { start++; continue; }
+ type_start = start; if (icon_is(q, type_start, "const")) type_start++;
+ if (icon_kind(q, type_start) != run.T_ID || (!icon_is(q, type_start, "let") && !run.istypename(icon_value(q, type_start)))) { start++; continue; }
+ i = type_start; depth = 0; binding = -1; comma = 0;
+ while (i < q[0]) {
+ v = icon_value(q, i);
+ if (!depth && v == id && icon_kind(q, i) == run.T_ID && binding < 0 && icon_declaration(q, type_start, i) && (icon_is(q, i + 1, "=") || icon_is(q, i + 1, ";") || icon_is(q, i + 1, ",") || icon_is(q, i + 1, "("))) binding = i;
+ if (icon_kind(q, i) == run.T_OP) {
+ if (icon_open(v)) depth++;
+ else if (icon_close(v)) { if (!depth) break; depth--; }
+ if (!depth && v == run.intern(",")) comma = 1;
+ if (!depth && v == run.intern(";")) {
+ if (binding >= 0) {
+ if (comma || icon_is(q, binding + 1, "(")) return 0;
+ result[0] = type_start; result[1] = binding; result[2] = i; result[3] = start; return 1;
+ }
+ break;
+ }
+ }
+ i++;
+ }
+ start++;
+ }
+ return 0;
+}
+int icon_balanced(word* q, int a, int b) {
+ word* stack = rt.new(b - a + 1); int n = 0, i = a, v, valid = 1;
+ while (i < b) {
+ v = icon_value(q, i);
+ if (icon_kind(q, i) == run.T_OP) {
+ if (icon_open(v)) { stack[n] = v; n++; }
+ else if (icon_close(v)) { if (!n || !icon_pair(stack[n - 1], v)) { valid = 0; break; } n--; }
+ }
+ i++;
+ }
+ if (n) valid = 0;
+ rt.free(stack);
+ return valid;
+}
+word* .u icon_slice(word* q, int a, int b) {
+ word* r = rt.new(1 + (b - a) * 3); int i = 0; r[0] = b - a;
+ while (i < (b - a) * 3) { r[1 + i] = q[1 + a * 3 + i]; i++; }
+ return r;
+}
+word* .u icon_splice(word* q, int a, int b, word* insert) {
+ int n = q[0] - (b - a) + insert[0], i = 0, j = 0;
+ word* r = rt.new(1 + n * 3); r[0] = n;
+ while (i < a * 3) { r[1 + j] = q[1 + i]; i++; j++; }
+ i = 0; while (i < insert[0] * 3) { r[1 + j] = insert[1 + i]; i++; j++; }
+ i = b * 3; while (i < q[0] * 3) { r[1 + j] = q[1 + i]; i++; j++; }
+ return r;
+}
+// capture classes use integer literals, identifiers, expressions, statements
+// or blocks. a named capture records its exact token range for substitution.
+int icon_class(int name) {
+ if (name == run.intern("int")) return 1;
+ if (name == run.intern("name")) return 2;
+ if (name == run.intern("expr")) return 3;
+ if (name == run.intern("stmt")) return 4;
+ if (name == run.intern("block")) return 5;
+ return 0;
+}
+int icon_pattern(word* q) {
+ int i = 0;
+ if (!q || q[0] <= 0 || !icon_balanced(q, 0, q[0])) return 0;
+ while (i < q[0]) {
+ if (icon_is(q, i, "$")) {
+ if (icon_kind(q, i + 1) != run.T_ID || !icon_class(icon_value(q, i + 1))) return 0;
+ i += 2;
+ if (icon_is(q, i, ":")) { if (icon_kind(q, i + 1) != run.T_ID || icon_class(icon_value(q, i + 1))) return 0; i += 2; }
+ } else i++;
+ }
+ return 1;
+}
+int icon_precedence(int name) {
+ if (name == run.intern("||")) return 1;
+ if (name == run.intern("&&")) return 2;
+ if (name == run.intern("|")) return 3;
+ if (name == run.intern("^")) return 4;
+ if (name == run.intern("&")) return 5;
+ if (name == run.intern("==") || name == run.intern("!=")) return 6;
+ if (name == run.intern("<") || name == run.intern("<=") || name == run.intern(">") || name == run.intern(">=")) return 7;
+ if (name == run.intern("<<") || name == run.intern(">>")) return 8;
+ if (name == run.intern("+") || name == run.intern("-")) return 9;
+ if (name == run.intern("*") || name == run.intern("/") || name == run.intern("%")) return 10;
+ return 0;
+}
+// recognize expression structure before matching a variable-length capture.
+// this excludes statement keywords and incomplete operator sequences.
+int icon_expression(word* q, int start, int end, int minimum) {
+ int i = start, v, kind, priority, next;
+ if (i >= end) return -1;
+ v = icon_value(q, i); kind = icon_kind(q, i);
+ if (kind == run.T_OP && (v == run.intern("!") || v == run.intern("~") || v == run.intern("+") || v == run.intern("-") || v == run.intern("*") || v == run.intern("&"))) {
+ i = icon_expression(q, i + 1, end, 11); if (i < 0) return -1;
+ } else if (icon_is(q, i, "(")) {
+ i = icon_expression(q, i + 1, end, 1); if (i < 0 || !icon_is(q, i, ")")) return -1; i++;
+ } else if (kind == run.T_NUM || kind == run.T_FLT || kind == run.T_STR || kind == run.T_BSTR || kind == run.T_ID) {
+ if (kind == run.T_ID && (v == run.intern("return") || v == run.intern("let") || v == run.intern("if") || v == run.intern("while") || v == run.intern("for") || v == run.intern("else") || v == run.intern("break") || v == run.intern("continue") || v == run.intern("do") || v == run.intern("switch") || v == run.intern("const"))) return -1;
+ i++;
+ } else return -1;
+ while (i < end) {
+ if (icon_is(q, i, "(") || icon_is(q, i, "[")) {
+ v = icon_value(q, i); i++;
+ if (v != run.intern("(") || !icon_is(q, i, ")")) {
+ while (1) {
+ i = icon_expression(q, i, end, 1); if (i < 0) return -1;
+ if (v == run.intern("(") && icon_is(q, i, ",")) { i++; continue; } break;
+ }
+ }
+ if (i >= end || icon_kind(q, i) != run.T_OP || !icon_pair(v, icon_value(q, i))) return -1; i++; continue;
+ }
+ if (icon_is(q, i, ".")) { if (i + 1 >= end || icon_kind(q, i + 1) != run.T_ID) return -1; i += 2; continue; }
+ if (icon_is(q, i, "++") || icon_is(q, i, "--")) { i++; continue; }
+ if (minimum == 1 && icon_is(q, i, "?")) {
+ i = icon_expression(q, i + 1, end, 1); if (i < 0 || !icon_is(q, i, ":")) return -1;
+ i = icon_expression(q, i + 1, end, 1); if (i < 0) return -1; continue;
+ }
+ priority = 0; if (icon_kind(q, i) == run.T_OP) priority = icon_precedence(icon_value(q, i)); if (!priority || priority < minimum) break;
+ next = icon_expression(q, i + 1, end, priority + 1); if (next < 0) return -1; i = next;
+ }
+ return i;
+}
+int icon_range(word* q, int a, int b, int kind) {
+ if (a < 0 || a >= b || b > q[0]) return 0;
+ if (kind == 1) return b == a + 1 && icon_kind(q, a) == run.T_NUM;
+ if (kind == 2) return b == a + 1 && icon_kind(q, a) == run.T_ID;
+ if (kind == 3) return icon_expression(q, a, b, 1) == b;
+ if (kind == 5) return icon_is(q, a, "{") && icon_mate(q, a) == b - 1;
+ if (kind == 4) return icon_statement_end(q, a) == b;
+ return icon_expression(q, a, b, 1) == b;
+}
+// recursively match a pattern with backtracking at balanced capture boundaries.
+// captures contain count followed by [name, start, end, class] records.
+int icon_match(word* q, int at, word* p, int pi, word* caps) {
+ int kind, name, next, end, old, i;
+ if (pi == p[0]) return at + 1;
+ if (at >= q[0]) return 0;
+ if (!icon_is(p, pi, "$")) {
+ if (!icon_eq(q, at, p, pi, 1)) return 0;
+ return icon_match(q, at + 1, p, pi + 1, caps);
+ }
+ if (pi + 1 >= p[0]) return 0;
+ name = icon_value(p, pi + 1); kind = icon_class(name); next = pi + 2;
+ if (!kind) return 0;
+ if (icon_is(p, next, ":")) { name = icon_value(p, next + 1); next += 2; if (!name) return 0; }
+ else name = 0;
+ old = caps[0]; end = at + 1;
+ if (kind == 4) end = icon_statement_end(q, at);
+ if (kind == 5) { if (!icon_is(q, at, "{")) return 0; end = icon_mate(q, at) + 1; }
+ if (end <= at) return 0;
+ while (end <= q[0]) {
+ if (icon_range(q, at, end, kind)) {
+ i = 0;
+ while (i < old) {
+ if (name && caps[1 + i * 4] == name) {
+ if (end - at != caps[3 + i * 4] - caps[2 + i * 4] || !icon_eq(q, at, q, caps[2 + i * 4], end - at)) break;
+ }
+ i++;
+ }
+ if (i == old) {
+ caps[0] = old + 1; caps[1 + old * 4] = name; caps[2 + old * 4] = at; caps[3 + old * 4] = end; caps[4 + old * 4] = kind;
+ i = icon_match(q, end, p, next, caps);
+ if (i) return i;
+ caps[0] = old;
+ }
+ }
+ if (kind != 3) break;
+ end++;
+ }
+ return 0;
+}
+int icon_capture(word* caps, int name, int ordinal) {
+ int c = 0, kind = icon_class(name), k = 0;
+ while (c < caps[0]) {
+ if (!kind) { if (caps[1 + c * 4] == name) return c + 1; }
+ else if (caps[4 + c * 4] == kind) { if (k == ordinal) return c + 1; k++; }
+ c++;
+ }
+ return 0;
+}
+word* .u icon_expand(word* q, word* replacement, word* caps) {
+ int n = 0, i = 0, j = 0, c, name, kind, k, seen[6], a, b;
+ k = 0; while (k < 6) { seen[k] = 0; k++; }
+ while (i < replacement[0]) {
+ if (icon_is(replacement, i, "$")) {
+ if (icon_kind(replacement, i + 1) != run.T_ID) { icon_error = 3; return 0; }
+ name = icon_value(replacement, i + 1); kind = icon_class(name); c = icon_capture(caps, name, seen[kind]);
+ if (!c) { icon_error = 3; return 0; } c--;
+ n += caps[3 + c * 4] - caps[2 + c * 4]; if (kind) seen[kind]++; i += 2;
+ } else { n++; i++; }
+ }
+ word* r = rt.new(1 + n * 3); i = 0;
+ k = 0; while (k < 6) { seen[k] = 0; k++; }
+ while (i < replacement[0]) {
+ if (icon_is(replacement, i, "$")) {
+ name = icon_value(replacement, i + 1); kind = icon_class(name); c = icon_capture(caps, name, seen[kind]) - 1;
+ if (kind) seen[kind]++;
+ a = caps[2 + c * 4] * 3; b = caps[3 + c * 4] * 3;
+ while (a < b) { r[1 + j] = q[1 + a]; a++; j++; }
+ i += 2;
+ } else {
+ k = 0; while (k < 3) { r[1 + j] = replacement[1 + i * 3 + k]; j++; k++; } i++;
+ }
+ }
+ r[0] = j / 3;
+ if (!icon_balanced(r, 0, r[0])) { rt.free(r); icon_error = 1; return 0; }
+ return r;
+}
+
+// resumable frames retain their own token list and explicit binding maps.
+// [tokens, position, state, result, error, locals, globals, blocks, steps].
+
+// locate a binding in a frame map by its interned name.
+int icon_binding(word map, int name) {
+ while (map) { if (map[0] == name) return map; map = map[2]; }
+ return 0;
+}
+int icon_read(word binding) { if (binding[3]) return *binding[3]; return binding[1]; }
+int icon_store(word*.m frame, int column, int name, int value) {
+ word entry = icon_binding(frame[column], name);
+ if (!entry) {
+ word* node = rt.new(4); node[0] = name; node[1] = value; node[2] = frame[column]; node[3] = 0; frame[column] = node; return value;
+ }
+ entry[1] = value; if (entry[3]) *entry[3] = value; return value;
+}
+int icon_declare(word*.m frame, int name, int value) {
+ word* node = rt.new(4); node[0] = name; node[1] = value; node[2] = frame[5]; node[3] = 0; frame[5] = node; return value;
+}
+int icon_fail(word frame) { frame[2] = 2; frame[4] = 5; icon_error = 5; return 0; }
+int icon_end(word* q, int start) {
+ int depth = 0, i = start, v;
+ while (i < q[0]) {
+ v = icon_value(q, i);
+ if (icon_kind(q, i) == run.T_OP) {
+ if (icon_open(v)) depth++;
+ else if (icon_close(v)) { if (!depth) return i; depth--; }
+ if (!depth && v == run.intern(";")) return i;
+ }
+ i++;
+ }
+ return i;
+}
+int icon_mate(word* q, int start) {
+ int depth = 1, i = start + 1, v;
+ while (i < q[0]) {
+ v = icon_value(q, i);
+ if (icon_kind(q, i) == run.T_OP) {
+ if (icon_open(v)) depth++; else if (icon_close(v)) depth--;
+ if (!depth) return i;
+ }
+ i++;
+ }
+ return -1;
+}
+// find one statement boundary; control-flow captures require braced bodies.
+int icon_statement_end(word* q, int start) {
+ int close, end;
+ if (icon_is(q, start, "{")) { close = icon_mate(q, start); if (close < 0) return -1; return close + 1; }
+ if (icon_is(q, start, "if") || icon_is(q, start, "while")) {
+ if (!icon_is(q, start + 1, "(")) return -1;
+ close = icon_mate(q, start + 1);
+ if (close < 0 || !icon_is(q, close + 1, "{")) return -1;
+ end = icon_mate(q, close + 1); if (end < 0) return -1; end++;
+ if (icon_is(q, start, "if") && icon_is(q, end, "else")) {
+ if (!icon_is(q, end + 1, "{")) return -1;
+ close = icon_mate(q, end + 1); if (close < 0) return -1; return close + 1;
+ }
+ return end;
+ }
+ end = icon_end(q, start);
+ if (!icon_is(q, end, ";")) return -1;
+ return end + 1;
+}
+// construct a compiler return body in one allocation, retaining source lines.
+word* .u icon_eval_tokens(word* q, int start, int end) {
+ int count = end - start;
+ word* expr = rt.new(7 + count * 3); expr[0] = count + 2;
+ expr[1] = run.T_ID; expr[2] = run.intern("return"); expr[3] = 1;
+ if (count) rt.copy_w(expr + 4, q + 1 + start * 3, count * 3);
+ expr[4 + count * 3] = run.T_OP; expr[5 + count * 3] = run.intern(";"); expr[6 + count * 3] = 1;
+ return expr;
+}
+// scalar frame substitution cannot preserve lvalue operations or implicit
+// program-global reads. reject these before compiling or executing a range.
+int icon_frame_expression(word frame, int start, int end) {
+ word* q = frame[0]; int i = start, s; word binding;
+ if (!icon_range(q, start, end, 3)) return 0;
+ while (i < end) {
+ if (icon_is(q, i, "++") || icon_is(q, i, "--")) return 0;
+ if (icon_kind(q, i) == run.T_ID && !icon_is(q, i - 1, ".")) {
+ binding = icon_binding(frame[5], icon_value(q, i));
+ if (!binding) binding = icon_binding(frame[6], icon_value(q, i));
+ if (!binding) {
+ s = run.lookups(icon_value(q, i));
+ if (s < 0 || (run.skind[s] != run.K_FN && run.skind[s] != run.K_CONST)) return 0;
+ }
+ }
+ i++;
+ }
+ return 1;
+}
+int icon_frame_eval(word frame, int start, int end) {
+ word* expr = icon_eval_tokens(frame[0], start, end);
+ int i = 1; word binding;
+ while (i < expr[0] - 1) {
+ if (icon_kind(expr, i) == run.T_ID && !icon_is(expr, i - 1, ".")) {
+ binding = icon_binding(frame[5], icon_value(expr, i));
+ if (!binding) binding = icon_binding(frame[6], icon_value(expr, i));
+ if (binding) { expr[1 + i * 3] = run.T_NUM; expr[2 + i * 3] = icon_read(binding); }
+ }
+ i++;
+ }
+ int result = run.eval(expr); rt.free(expr); return result;
+}
+int icon_block(word*.m frame, int open, int resume) {
+ int close = icon_mate(frame[0], open);
+ if (close < 0) return icon_fail(frame);
+ word block = rt.new(4); block[0] = close; block[1] = resume; block[2] = frame[7]; block[3] = frame[5]; frame[7] = block; frame[1] = open + 1; return 1;
+}
+
+struct icon {
+ // the last transformation status: 0 success, 1 invalid delimiters,
+ // 2 invalid range or declaration, 3 unresolved replacement capture,
+ // 4 unavailable function metadata, 5 unsupported stepping syntax,
+ // 6 rejected function publication.
+ static int error() { return icon_error; }
+ // check token delimiters without compiling or executing the list.
+ static int validate(word* q) { icon_error = 0; if (!q || q[0] < 0 || !icon_balanced(q, 0, q[0])) { icon_error = 1; return 0; } return 1; }
+ // return an independent token list with [start, end) replaced by insert.
+ static word* .u splice(word* q, int start, int end, word* insert) {
+ icon_error = 0;
+ if (!q || !insert || q[0] < 0 || insert[0] < 0 || start < 0 || end < start || end > q[0]) { icon_error = 2; return 0; }
+ word* r = icon_splice(q, start, end, insert);
+ if (!icon_balanced(r, 0, r[0])) { rt.free(r); icon_error = 1; return 0; }
+ return r;
+ }
+ // replace each nonoverlapping pattern match. inserted tokens are not
+ // searched again. the input and templates remain unchanged.
+ static word* .u rewrite(word* q, word* pattern, word* replacement) {
+ icon_error = 0;
+ if (!q || q[0] < 0 || !replacement || replacement[0] < 0 || !icon_pattern(pattern) || !icon_balanced(q, 0, q[0])) { icon_error = 1; return 0; }
+ word* result = icon_slice(q, 0, q[0]); word* caps = rt.new(1 + pattern[0] * 4); word* insert, next;
+ int at = 0, end;
+ while (at < result[0]) {
+ caps[0] = 0; end = icon_match(result, at, pattern, 0, caps);
+ if (!end) { at++; continue; }
+ insert = icon_expand(result, replacement, caps);
+ if (!insert) { rt.free(result); rt.free(caps); return 0; }
+ next = icon_splice(result, at, end - 1, insert); at += insert[0];
+ rt.free(insert); rt.free(result); result = next;
+ }
+ rt.free(caps); return result;
+ }
+ // transform retained function tokens and publish the compiled replacement.
+ // references to the original entry continue to use the published version.
+ static int find_replace(word function, word* pattern, word* replacement) {
+ icon_error = 0;
+ if (!replacement || replacement[0] < 0 || !icon_pattern(pattern)) { icon_error = 1; return 0; }
+ word* q = run.function_tokens(function);
+ if (!q) { icon_error = 4; return 0; }
+ word* caps = rt.new(1 + pattern[0] * 4); int i = 0, end, count = 0;
+ while (i < q[0]) { caps[0] = 0; end = icon_match(q, i, pattern, 0, caps); if (end) { count++; i = end - 1; } else i++; }
+ rt.free(caps);
+ if (!count) { rt.free(q); return 0; }
+ word* changed = icon.rewrite(q, pattern, replacement); rt.free(q);
+ if (!changed) return 0;
+ int published = run.replace_function(function, changed); rt.free(changed);
+ if (!published) { icon_error = 6; return 0; }
+ return count;
+ }
+ // remove a complete declaration statement containing the requested name.
+ // declarations containing multiple bindings are rejected to preserve them.
+ static word* .u undeclare(word* q, char* name) {
+ icon_error = 0;
+ int declaration[4];
+ if (!q || q[0] < 0 || !name || !icon_locate_declaration(q, run.intern(name), declaration)) { icon_error = 2; return 0; }
+ word* empty = rt.new(1); empty[0] = 0;
+ word* result = icon.splice(q, declaration[3], declaration[2] + 1, empty); rt.free(empty); return result;
+ }
+ // change the type of the first simple declaration of name.
+ static word* .u change_type(word* q, char* name, word* type) {
+ icon_error = 0;
+ int declaration[4];
+ if (!q || q[0] < 0 || !name || !icon_locate_declaration(q, run.intern(name), declaration)) { icon_error = 2; return 0; }
+ return icon.splice(q, declaration[0], declaration[1], type);
+ }
+ // insert a scoped statement block at the given token boundary.
+ static word* .u insert_block(word* q, int at, word* body) {
+ icon_error = 0;
+ if (!q || !body || body[0] < 0 || at < 0 || at > q[0]) { icon_error = 2; return 0; }
+ word* block = rt.new(7 + body[0] * 3); int i = 0; block[0] = body[0] + 2;
+ block[1] = run.T_OP; block[2] = run.intern("{"); block[3] = 1;
+ while (i < body[0] * 3) { block[4 + i] = body[1 + i]; i++; }
+ block[4 + i] = run.T_OP; block[5 + i] = run.intern("}"); block[6 + i] = 1;
+ word* result = icon.splice(q, at, at, block); rt.free(block); return result;
+ }
+ // evaluate a selected expression using the compiler rather than a text
+ // evaluator. the range must contain one complete expression.
+ static int calculate(word* q, int start, int end) {
+ icon_error = 0;
+ if (!q || start < 0 || end > q[0] || !icon_range(q, start, end, 3)) { icon_error = 2; return 0; }
+ word* expr = icon_eval_tokens(q, start, end);
+ int value = run.eval(expr); rt.free(expr); return value;
+ }
+ // start a resumable evaluation with independent tokens and binding storage.
+ // stepping executes statements through the compiler; no native frame is
+ // suspended or inspected. scalar bindings are visible between statements.
+ static word* .u begin(word* q) {
+ if (!icon.validate(q)) { icon_error = 1; return 0; }
+ word* frame = rt.new(9); int i = 0; while (i < 9) { frame[i] = 0; i++; } frame[0] = icon_slice(q, 0, q[0]); return frame;
+ }
+ // create an explicit frame from a function's retained body. argument values
+ // are supplied through set_local before stepping starts.
+ static word* .u begin_function(int function) {
+ word* q = run.function_tokens(function);
+ if (!q) { icon_error = 4; return 0; }
+ word* frame = icon.begin(q); rt.free(q); return frame;
+ }
+ // set an isolated global binding, or update storage explicitly bound to it.
+ static int set_global(word frame, char* name, int value) { icon_error = 0; return icon_store(frame, 6, run.intern(name), value); }
+ // update the innermost local binding, creating a binding when absent.
+ static int set_local(word frame, char* name, int value) { icon_error = 0; return icon_store(frame, 5, run.intern(name), value); }
+ // bind one existing scalar global explicitly. reads observe its current
+ // value, and subsequent frame assignments update its program storage.
+ static int bind_global(word frame, char* name) {
+ int s = run.lookups(name); icon_error = 0;
+ if (s < 0 || run.skind[s] != run.K_GVAR) { icon_error = 2; return 0; }
+ word type = run.stype[s];
+ if (type && type != run.tint && type != run.tword) { icon_error = 2; return 0; }
+ icon_store(frame, 6, run.intern(name), *run.sval[s]);
+ word binding = icon_binding(frame[6], run.intern(name)); binding[3] = run.sval[s]; return 1;
+ }
+ // read the innermost local binding; a missing binding sets status 2.
+ static int local(word frame, char* name) { icon_error = 0; word b = icon_binding(frame[5], run.intern(name)); if (!b) { icon_error = 2; return 0; } return icon_read(b); }
+ // read an isolated or explicitly connected global binding.
+ static int global(word frame, char* name) { icon_error = 0; word b = icon_binding(frame[6], run.intern(name)); if (!b) { icon_error = 2; return 0; } return icon_read(b); }
+ // frame state: 0 resumable, 1 complete, 2 unsupported statement.
+ static int state(word frame) { return frame[2]; }
+ // the returned value after completion; zero before a return is executed.
+ static int result(word frame) { return frame[3]; }
+ // token index of the next statement or control-flow decision.
+ static int position(word frame) { return frame[1]; }
+ // number of executed statements and control-flow decisions.
+ static int steps(word frame) { return frame[8]; }
+ // number of retained local binding records, including active shadowed records.
+ static int local_count(word frame) { word b = frame[5]; int n = 0; while (b) { n++; b = b[2]; } return n; }
+ // interned name of a local record; zero when index is outside the map.
+ static char* local_name(word frame, int index) { word b = frame[5]; while (b && index > 0) { index--; b = b[2]; } if (!b || index < 0) return 0; return b[0]; }
+ // execute one statement or control-flow decision. returns 1 after progress,
+ // 0 after completion or an unsupported statement. blocks, if and while
+ // require braces; declarations contain one scalar binding.
+ static int step(word frame) {
+ if (!frame || frame[2]) return 0;
+ icon_error = 0;
+ word* q = frame[0]; int at = frame[1], end, name, value, column = 5, v, close, open, alternate;
+ word block, binding;
+ while (frame[7] && at == frame[7][0]) {
+ block = frame[7];
+ while (frame[5] != block[3]) { binding = frame[5]; frame[5] = binding[2]; rt.free(binding); }
+ at = block[1]; frame[7] = block[2]; rt.free(block); frame[1] = at;
+ }
+ if (at >= q[0]) { frame[2] = 1; return 0; }
+ frame[8]++;
+ if (icon_is(q, at, ";")) { frame[1] = at + 1; return 1; }
+ if (icon_is(q, at, "{")) { close = icon_mate(q, at); return icon_block(frame, at, close + 1); }
+ if (icon_is(q, at, "if") || icon_is(q, at, "while")) {
+ if (!icon_is(q, at + 1, "(")) return icon_fail(frame);
+ close = icon_mate(q, at + 1); open = close + 1;
+ if (close < 0 || !icon_is(q, open, "{")) return icon_fail(frame);
+ end = icon_mate(q, open); if (end < 0) return icon_fail(frame);
+ alternate = end + 1;
+ if (icon_is(q, at, "if") && icon_is(q, alternate, "else")) {
+ if (!icon_is(q, alternate + 1, "{")) return icon_fail(frame);
+ }
+ if (!icon_frame_expression(frame, at + 2, close)) return icon_fail(frame);
+ value = icon_frame_eval(frame, at + 2, close);
+ if (icon_is(q, at, "while")) { if (value) return icon_block(frame, open, at); frame[1] = end + 1; return 1; }
+ if (icon_is(q, alternate, "else")) {
+ close = icon_mate(q, alternate + 1);
+ if (value) return icon_block(frame, open, close + 1);
+ return icon_block(frame, alternate + 1, close + 1);
+ }
+ if (value) return icon_block(frame, open, end + 1);
+ frame[1] = end + 1; return 1;
+ }
+ end = icon_end(q, at);
+ if (end >= q[0] || !icon_is(q, end, ";")) return icon_fail(frame);
+ if (icon_is(q, at, "return")) {
+ frame[3] = 0;
+ if (end > at + 1) { if (!icon_frame_expression(frame, at + 1, end)) return icon_fail(frame); frame[3] = icon_frame_eval(frame, at + 1, end); }
+ frame[1] = end + 1; frame[2] = 1; return 1;
+ }
+ if (icon_is(q, at, "let") || icon_is(q, at, "int") || icon_is(q, at, "word")) {
+ if (icon_kind(q, at + 1) != run.T_ID) return icon_fail(frame);
+ name = icon_value(q, at + 1); value = 0;
+ if (end > at + 2) { if (!icon_is(q, at + 2, "=") || !icon_frame_expression(frame, at + 3, end)) return icon_fail(frame); value = icon_frame_eval(frame, at + 3, end); }
+ icon_declare(frame, name, value); frame[1] = end + 1; return 1;
+ }
+ name = icon_value(q, at); binding = icon_binding(frame[5], name);
+ if (!binding) { binding = icon_binding(frame[6], name); column = 6; }
+ if (binding && icon_kind(q, at) == run.T_ID) {
+ v = icon_value(q, at + 1);
+ if (end == at + 2 && (v == run.intern("++") || v == run.intern("--"))) {
+ value = icon_read(binding) + 1; if (v == run.intern("--")) value = icon_read(binding) - 1;
+ } else {
+ if (v != run.intern("=") && v != run.intern("+=") && v != run.intern("-=") && v != run.intern("*=") && v != run.intern("/=")) return icon_fail(frame);
+ if (!icon_frame_expression(frame, at + 2, end)) return icon_fail(frame);
+ value = icon_frame_eval(frame, at + 2, end);
+ if (v == run.intern("+=")) value = icon_read(binding) + value;
+ if (v == run.intern("-=")) value = icon_read(binding) - value;
+ if (v == run.intern("*=")) value = icon_read(binding) * value;
+ if (v == run.intern("/=")) value = icon_read(binding) / value;
+ }
+ icon_store(frame, column, name, value); frame[1] = end + 1; return 1;
+ }
+ return icon_fail(frame);
+ }
+ // execute at most budget statements. returning 0 leaves the frame resumable;
+ // callers can inspect bindings, edit them, then continue with another budget.
+ static int resume(word frame, int budget) { while (budget > 0 && !frame[2]) { icon.step(frame); budget--; } return frame[2]; }
+ // release a frame and all storage it owns.
+ static int close(.d frame) {
+ word b, next; int column = 5;
+ while (column <= 7) { b = frame[column]; while (b) { next = b[2]; rt.free(b); b = next; } column++; }
+ rt.free(frame[0]); rt.free(frame); return 0;
+ }
+}
+
+// public entry points for the icon namespace.
+
+// status of the most recent transformation or binding operation; see docs/ICON.md.
+int error() { return icon.icon.error(); }
+// check delimiter structure only; does not compile or execute tokens.
+int validate(word* q) { return icon.icon.validate(q); }
+// return a new token list with [start, end) replaced; caller owns the result.
+word* .u splice(word* q, int start, int end, word* insert) { return icon.icon.splice(q, start, end, insert); }
+// return a new list with nonoverlapping matches replaced; inputs remain unchanged.
+word* .u rewrite(word* q, word* pattern, word* replacement) { return icon.icon.rewrite(q, pattern, replacement); }
+// publish a transformed function body; returns matches, or 0 on no match or failure.
+int find_replace(word function, word* pattern, word* replacement) { return icon.icon.find_replace(function, pattern, replacement); }
+// remove the first complete single-binding declaration named name; caller owns the result.
+word* .u undeclare(word* q, char* name) { return icon.icon.undeclare(q, name); }
+// replace the type prefix of the first simple declaration named name; caller owns the result.
+word* .u change_type(word* q, char* name, word* type) { return icon.icon.change_type(q, name, type); }
+// insert a scoped block containing body at token boundary at; caller owns the result.
+word* .u insert_block(word* q, int at, word* body) { return icon.icon.insert_block(q, at, body); }
+// compile and evaluate the expression in [start, end); expression calls retain their effects.
+int calculate(word* q, int start, int end) { return icon.icon.calculate(q, start, end); }
+// create a frame with copied tokens and isolated bindings; caller closes the frame.
+word* .u begin(word* q) { return icon.icon.begin(q); }
+// create a frame from retained function tokens; set arguments as locals before stepping.
+word* .u begin_function(int function) { return icon.icon.begin_function(function); }
+// set an isolated global binding, creating it when absent; returns the stored value.
+int set_global(word frame, char* name, int value) { return icon.icon.set_global(frame, name, value); }
+// set the innermost local binding, creating it when absent; returns the stored value.
+int set_local(word frame, char* name, int value) { return icon.icon.set_local(frame, name, value); }
+// connect an int or word program global to the frame; later frame writes update it.
+int bind_global(word frame, char* name) { return icon.icon.bind_global(frame, name); }
+// read a local binding; missing names return 0 and set error status 2.
+int local(word frame, char* name) { return icon.icon.local(frame, name); }
+// read a frame global binding; missing names return 0 and set error status 2.
+int global(word frame, char* name) { return icon.icon.global(frame, name); }
+// return frame state: 0 resumable, 1 complete, or 2 unsupported statement.
+int state(word frame) { return icon.icon.state(frame); }
+// return the value produced by return, or 0 before a return is executed.
+int result(word frame) { return icon.icon.result(frame); }
+// return the token index of the next statement or control-flow decision.
+int position(word frame) { return icon.icon.position(frame); }
+// return the number of statements and control-flow decisions executed.
+int steps(word frame) { return icon.icon.steps(frame); }
+// count retained local records, including shadowed records in active blocks.
+int local_count(word frame) { return icon.icon.local_count(frame); }
+// return the zero-based local record name, or 0 when index is out of range.
+char* local_name(word frame, int index) { return icon.icon.local_name(frame, index); }
+// execute scalar declarations and assignments, standalone ++/--, return, blocks,
+// or braced if/else/while; direct program globals need bind_global first.
+// unsupported syntax sets error status 5.
+int step(word frame) { return icon.icon.step(frame); }
+// execute at most budget steps and return the frame state; frame bindings are scalar int/word values.
+int resume(word frame, int budget) { return icon.icon.resume(frame, budget); }
+// release the frame and owned storage; consumes frame and returns 0.
+int close(.d frame) { return icon.icon.close(d*frame); }
diff --git a/src/lib/std.zc b/src/lib/std.zc
index 375f403..f232422 100644
--- a/src/lib/std.zc
+++ b/src/lib/std.zc
@@ -1,35 +1,40 @@
+namespace std;
+include rt, run;
+
// small helpers for strings, files, the clock and byte buffers. not part of the
-// genome; include it with: include "src/lib/std.zc";
+// genome as declarations; its source is embedded. include it with: include std;
// syscall numbers (SYS_OPEN and the rest) live in the runtime.
-// a string here is a word string, one character per word and a 0 at the end,
-// which is char*. the Bytes group takes packed byte strings (b"..."), whose
-// type is a plain word.
+// a character string has type char*, one character per word and a 0 at the end.
+// the Bytes group takes packed byte strings (b"..."), whose
+// type is bstr.
// the helpers are grouped in structs, and each is a static function, so it is
-// called as Group.name(args): Text.itoa(n, buf), Host.now(), Buf.get(b, 0).
+// called as std.Group.name(args): std.Text.itoa(n, buf), std.Host.now(),
+// std.Buf.get(b, 0).
// the constructors arena(n) and bnew(n) and the argv helpers are plain
// functions, because an owned result has to carry its .u tag.
-// ---- the command line and the environment ----
+//: ---- the command line and the environment ----
+
+//: argc and argv, envc and envp, as passed to main by c. a zc string is
+//: one character per word, so an array of them is char*[?]. libinit() fills
+//: these and the runtime calls it before main, if the program has one (an
+//: image built by build_image, or a check-mode document, does not).
-// argc and argv, envc and envp, the way c hands them to main. a zc string is
-// one character per word, so an array of them is []char*. libinit() fills
-// these and the runtime calls it before main, if the program has one (an
-// image built by build_image, or a check-mode document, does not).
int argc;
-[]char* argv;
+char*[?] argv;
int envc;
-[]char* envp;
+char*[?] envp;
// one entry of a vector syscall, as a fresh string, or 0 past the end.
// op: the syscall that fetches an entry (SYS_ARG or SYS_ENV)
// i: which entry
// returns: the entry, owned by the caller; 0 when there is no entry i
-unchecked char*.u argstr(int op, int i) {
- int n = syscall(op, i, 0, 0);
+char*.u argstr(int op, int i) {
+ int n = rt.syscall(op, i, 0, 0);
char* s;
if (n < 0) return 0;
- s = new(n + 1);
- syscall(op, i, s, n + 1);
+ s = rt.new(n + 1);
+ rt.syscall(op, i, s, n + 1);
return s;
}
@@ -37,9 +42,9 @@ unchecked char*.u argstr(int op, int i) {
// cntop: the syscall that counts the entries (SYS_ARGC or SYS_ENVC)
// stop: the syscall that fetches one (SYS_ARG or SYS_ENV)
// returns: a new array of the strings, owned by the caller
-unchecked []char*.u argsvec(int cntop, int stop) {
- int n = syscall(cntop, 0, 0, 0), i = 0;
- []char* a = new([]char*, 0);
+char*[?].u argsvec(int cntop, int stop) {
+ int n = rt.syscall(cntop, 0, 0, 0), i = 0;
+ char*[?] a = rt.new(char*[?], 0);
while (i < n) { push(a, argstr(stop, i)); i++; }
return a;
}
@@ -48,43 +53,44 @@ unchecked []char*.u argsvec(int cntop, int stop) {
// it stays a plain function: the compiler looks it up by name.
// returns: 0
int libinit() {
- argv = argsvec(SYS_ARGC, SYS_ARG);
- envp = argsvec(SYS_ENVC, SYS_ENV);
+ argv = argsvec(rt.SYS_ARGC, rt.SYS_ARG);
+ envp = argsvec(rt.SYS_ENVC, rt.SYS_ENV);
argc = len(argv); envc = len(envp);
return 0;
}
-// ---- capturing output ----
+//: ---- capturing output ----
// capture.start() redirects everything the program writes into a string and
-// hands it back. writes go to the buffer instead of the terminal until end()
+// returns it. writes go to the buffer instead of the terminal until end()
// stops it, so the buffer is a string once end() has run:
// let buf = capture.start();
// put_s("hello world!"); // nothing on the terminal
-// buf.end();
+// capture.end(buf);
// put_s(buf); // hello world
// one capture at a time, and the buffer belongs to whoever holds it.
+
struct capture {
- // starts a capture of the output.
- // returns: the buffer, a word on purpose: the method end() is looked up on
- // any receiver the compiler cannot type, and a typed one has none
- static word start() {
+ // redirects program output into a collector-managed character buffer.
+ // returns the buffer; call end(buffer) before reading its terminator.
+ // returns: the buffer used for this capture
+ static char* start() {
int cap = 256;
- outmode = 2; outn = 0; outcap = cap;
- outbuf = new(cap);
- return outbuf;
+ rt.outmode = 2; rt.outn = 0; rt.outcap = cap;
+ rt.outbuf = rt.new(cap);
+ return rt.outbuf;
}
-
- // stops the capture and terminates the buffer.
- // returns: the string it collected (this is the buffer)
- word end() {
- outbuf[outn] = 0;
- outmode = 0; outbuf = 0; outn = 0; outcap = 0;
- return this;
+ // stops output capture, terminates the buffer, and returns the collected text.
+ // buffer: the buffer returned by start
+ // returns: the same buffer, now terminated
+ static char* end(char* buffer) {
+ rt.outbuf[rt.outn] = 0;
+ rt.outmode = 0; rt.outbuf = 0; rt.outn = 0; rt.outcap = 0;
+ return buffer;
}
}
-// ---- word strings ----
+//: ---- character strings ----
struct Text {
// the number written in decimal at the start of s. a leading '-' makes it
@@ -106,7 +112,7 @@ struct Text {
int b[24], i = 0, j = 0, neg = 0;
if (!n) { dst[0] = '0'; dst[1] = 0; return 1; }
if (n < 0) { neg = 1; n = 0 - n; }
- if (n < 0) { copy_w(dst, "-9223372036854775808", 21); return 20; }
+ if (n < 0) { rt.copy_w(dst, "-9223372036854775808", 21); return 20; }
while (n) { b[i] = '0' + n % 10; i++; n = n / 10; }
if (neg) { dst[0] = '-'; j = 1; }
while (i) { i--; dst[j] = b[i]; j++; }
@@ -138,6 +144,19 @@ struct Text {
return 1;
}
+ // does s end with suffix? an empty suffix always matches.
+ // s: the string to test
+ // suffix: the ending to look for
+ // returns: 1 when it matches, 0 otherwise
+ static int ends(char* s, char* suffix) {
+ int n = 0, m = 0, i = 0;
+ while (s[n]) n++;
+ while (suffix[m]) m++;
+ if (m > n) return 0;
+ while (i < m) { if (s[n - m + i] != suffix[i]) return 0; i++; }
+ return 1;
+ }
+
// how many characters come before the 0.
// s: the string
// returns: the length, not counting the 0
@@ -227,23 +246,32 @@ struct Text {
}
}
-// ---- numbers ----
+//: ---- numbers ----
+
// plain integer helpers. none of them can fail: an out of range result wraps
// the same way the arithmetic does.
+
struct Num {
- // the absolute value of n.
+ // the absolute value of n, using wrapping word arithmetic. the minimum
+ // word remains negative because its positive value cannot fit in a word.
+ // n: the value
+ // returns: its absolute value, or the minimum word
static int abs(int n) {
if (n < 0) return 0 - n;
return n;
}
// the smaller of a and b.
+ // a, b: the values to compare
+ // returns: the smaller value (a when they are equal)
static int min(int a, int b) {
if (a < b) return a;
return b;
}
// the larger of a and b.
+ // a, b: the values to compare
+ // returns: the larger value (a when they are equal)
static int max(int a, int b) {
if (a > b) return a;
return b;
@@ -251,14 +279,17 @@ struct Num {
// v, kept between lo and hi.
// v: the value
- // lo, hi: the bounds, lo first
+ // lo, hi: the bounds, lo first; lo must not exceed hi
+ // returns: v clamped to the bounds
static int clamp(int v, int lo, int hi) {
if (v < lo) return lo;
if (v > hi) return hi;
return v;
}
- // the greatest common divisor of two numbers, always 0 or more.
+ // the greatest common divisor of a and b, computed with word arithmetic.
+ // a, b: the values
+ // returns: their greatest common divisor
static int gcd(int a, int b) {
if (a < 0) a = 0 - a;
if (b < 0) b = 0 - b;
@@ -267,7 +298,10 @@ struct Num {
return a;
}
- // b raised to the power e, for e of 0 or more. e below 0 gives 1.
+ // b raised to the power e. e below 0 gives 1; the result wraps as a word.
+ // b: the base
+ // e: the exponent
+ // returns: b to the power e, or 1 when e is negative
static int pow(int b, int e) {
int r = 1;
while (e > 0) { r = r * b; e--; }
@@ -275,6 +309,8 @@ struct Num {
}
// the whole square root of n, rounded down. n below 0 gives 0.
+ // n: the value
+ // returns: the greatest integer whose square is at most n, or 0 for n <= 0
static int isqrt(int n) {
int lo = 0, hi = 3037000499, mid;
if (n <= 0) return 0;
@@ -285,7 +321,9 @@ struct Num {
return lo;
}
- // -1, 0 or 1 by the sign of n.
+ // -1, 0 or 1 according to the sign of n.
+ // n: the value
+ // returns: -1 when n is negative, 0 when it is zero, and 1 when it is positive
static int sign(int n) {
if (n < 0) return 0 - 1;
if (n > 0) return 1;
@@ -293,9 +331,9 @@ struct Num {
}
}
-// ---- the host ----
+//: ---- the host ----
-// the argument block for Host.raw: the number, then its six arguments.
+//: the argument block for Host.raw: the number, then its six arguments.
int rawblk[7];
struct Host {
@@ -309,115 +347,160 @@ struct Host {
// is wanted.
// nr: the syscall number (x86-64 and arm64 numbers differ, so check the host)
// a..f: the six arguments, unused ones 0
- // returns: the kernel's result, or -errno when the call fails
+ // returns: the kernel's result, -errno on failure, or -1 on unsupported hosts
static int raw(int nr, int a, int b, int c, int d, int e, int f) {
rawblk[0] = nr; rawblk[1] = a; rawblk[2] = b; rawblk[3] = c;
rawblk[4] = d; rawblk[5] = e; rawblk[6] = f;
- return syscall(SYS_RAW, rawblk, 0, 0);
+ return rt.syscall(rt.SYS_RAW, rawblk, 0, 0);
}
// the value of an environment variable.
// name: the variable's name
- // dst: where the value goes, a word string
+ // dst: where the value goes, a zero-terminated character string
// n: how many words dst has
// returns: the length of the value, -1 if the variable is not set
- static int env(char* name, char* dst, int n) { return syscall(SYS_GETENV, name, dst, n); }
+ static int env(char* name, char* dst, int n) { return rt.syscall(rt.SYS_GETENV, name, dst, n); }
// the clock.
// returns: the time in seconds since 1970
- static int now() { return syscall(SYS_TIME, 0, 0, 0); }
+ static int now() { return rt.syscall(rt.SYS_TIME, 0, 0, 0); }
// writes a string to a file, replacing what was there.
// path: the file's name
// s: the text to write
// returns: how many bytes were written, -1 if the file could not be opened
static int writefile(char* path, char* s) {
- int fd = syscall(SYS_OPEN, path, 1, 0), n;
+ int fd = rt.syscall(rt.SYS_OPEN, path, 1, 0), n;
if (fd < 0) return -1;
- n = syscall(SYS_WRITE, fd, s, len_w(s));
- syscall(SYS_CLOSE, fd, 0, 0);
+ n = rt.syscall(rt.SYS_WRITE, fd, s, rt.len_w(s));
+ rt.syscall(rt.SYS_CLOSE, fd, 0, 0);
return n;
}
}
-// ---- byte strings ----
-// the same helpers for packed byte strings. word strings above are unchanged.
-// a byte string is a word whose first word is its length (see len_s).
+//: ---- byte strings ----
+
+// the same helpers for packed byte strings. character-string helpers above are unchanged.
+// a packed byte string has type bstr; len_s reads its stored byte length.
struct Bytes {
- // like Text.atoi, for a byte string.
+ // reads a decimal number at the start of s; a leading '-' makes it
+ // negative, and the first non-digit ends the number.
// s: the byte string
- // returns: the number
- static int atoi(word s) { return s_to_n(s); }
+ // returns: the number, or 0 when s starts with no digit
+ static int atoi(bstr s) { return rt.s_to_n(s); }
// like Text.itoa, but the text is made fresh.
// n: the number
// returns: a new byte string
- static word itoa(int n) { return n_to_s(n); }
+ static bstr itoa(int n) { return rt.n_to_s(n); }
// like Text.starts, for byte strings.
// s: the byte string to test
// pfx: the beginning to look for
// returns: 1 if s begins with pfx, 0 if not
- static int starts(word s, word pfx) {
+ static int starts(bstr s, bstr pfx) {
+ int i = 0, n = rt.len_s(s), m = rt.len_s(pfx);
+ if (m > n) return 0;
+ while (i < m) { if (rt.at_s(s, i) != rt.at_s(pfx, i)) return 0; i++; }
+ return 1;
+ }
+
+ // the number of bytes in a packed byte string.
+ // s: the byte string
+ // returns: its stored length
+ static int len(bstr s) { return rt.len_s(s); }
+
+ // are two packed byte strings equal, byte for byte.
+ // a, b: the byte strings to compare
+ // returns: 1 if equal, 0 otherwise
+ static int eq(bstr a, bstr b) {
int i = 0;
- if (len_s(pfx) > len_s(s)) return 0;
- while (i < len_s(pfx)) { if (at_s(s, i) != at_s(pfx, i)) return 0; i++; }
+ if (rt.len_s(a) != rt.len_s(b)) return 0;
+ while (i < rt.len_s(a)) { if (rt.at_s(a, i) != rt.at_s(b, i)) return 0; i++; }
+ return 1;
+ }
+
+ // where pat first appears in s.
+ // s: the byte string to search
+ // pat: the byte string to find; an empty pat is found at 0
+ // returns: its byte index, or -1 when absent
+ static int find(bstr s, bstr pat) {
+ int i = 0, j, n = rt.len_s(s), m = rt.len_s(pat);
+ if (!m) return 0;
+ while (i + m <= n) {
+ j = 0;
+ while (j < m && rt.at_s(s, i + j) == rt.at_s(pat, j)) j++;
+ if (j == m) return i;
+ i++;
+ }
+ return 0 - 1;
+ }
+
+ // does s end with suffix? an empty suffix always matches.
+ // s: the byte string to test
+ // suffix: the ending to look for
+ // returns: 1 when it matches, 0 otherwise
+ static int ends(bstr s, bstr suffix) {
+ int n = rt.len_s(s), m = rt.len_s(suffix), i = 0;
+ if (m > n) return 0;
+ while (i < m) { if (rt.at_s(s, n - m + i) != rt.at_s(suffix, i)) return 0; i++; }
return 1;
}
// like Host.env, for byte strings.
// name: the variable's name, a byte string
- // dst: where the value goes, a word buffer
- // n: how many bytes dst can take
+ // dst: the packed byte string whose payload receives the value
+ // n: the maximum number of bytes to write
// returns: the length of the value, -1 if the variable is not set
- static int env(word name, word dst, int n) { return bgetenv(name, dst, n); }
+ static int env(bstr name, bstr dst, int n) { return rt.bgetenv(name, dst, n); }
// like Host.writefile, for a byte string.
// path: the file's name, a byte string
// s: the bytes to write
// returns: how many bytes were written, -1 if the file could not be opened
- static int writefile(word path, word s) {
- int fd = bopen(path, 1), n;
+ static int writefile(bstr path, bstr s) {
+ int fd = rt.bopen(path, 1), n;
if (fd < 0) return -1;
- n = bwrite(fd, s, len_s(s));
- syscall(SYS_CLOSE, fd, 0, 0);
+ n = rt.bwrite(fd, s, rt.len_s(s));
+ rt.syscall(rt.SYS_CLOSE, fd, 0, 0);
return n;
}
}
-// ---- arenas ----
+//: ---- arenas ----
// a region that is freed all at once.
-// n: its size in words
+// n: its capacity in words; values below 1 allocate one word
// returns: the arena, owned by the caller
-int .u arena(int n) { return arena_new(n); }
+int .u arena(int n) { return rt.arena_new(n); }
struct Arena {
- // takes n words from an arena.
+ // takes n zeroed words from an arena.
// a: the arena
// n: how many words
- // returns: the address of the first, 0 if the arena is full
- static int alloc(word a, int n) { return arena_alloc(a, n); }
+ // returns: the first word, or 0 when n words do not fit
+ static int alloc(word a, int n) { return rt.arena_alloc(a, n); }
- // gives every block of an arena back at once.
+ // gives every block of an arena back at once. pointers returned by alloc
+ // before the reset are no longer valid.
// a: the arena
// returns: 0
- static int reset(word a) { return arena_reset(a); }
+ static int reset(word a) { return rt.arena_reset(a); }
}
-// ---- growable byte buffers ----
+//: ---- growable byte buffers ----
-// a byte buffer that knows its length and its capacity: two words, then the
+// a byte buffer stores its length and capacity in two words, followed by the
// bytes, packed.
-// n: the capacity in bytes
-// returns: the buffer, owned by the caller, empty
+// n: the capacity in bytes; a negative value is treated as 0
+// returns: an empty buffer with that capacity, owned by the caller
word .u bnew(int n) {
int words;
word p;
if (n < 0) n = 0;
words = (n + 7) >> 3;
- p = new(2 + words);
+ p = rt.new(2 + words);
p[0] = 0;
p[1] = n;
return u*p;
@@ -438,9 +521,9 @@ struct Buf {
// b: the buffer
// i: the position
// returns: the byte, 0 when i is outside the buffer
- static int get(word b, int i) { if (i < 0 || i >= b[0]) return 0; return ld8(addr_b(b + 2) + i); }
+ static int get(word b, int i) { if (i < 0 || i >= b[0]) return 0; return rt.ld8(rt.addr_b(b + 2) + i); }
- // sets one byte of a buffer. setting the byte just past the end makes the
+ // sets one byte of a buffer. setting the byte at the current length makes the
// buffer one longer.
// b: the buffer
// i: the position, below the capacity
@@ -448,8 +531,74 @@ struct Buf {
// returns: the byte stored, 0 when i is outside the capacity
static int set(word b, int i, int v) {
if (i < 0 || i >= b[1]) return 0;
- st8(addr_b(b + 2) + i, v & 255);
+ rt.st8(rt.addr_b(b + 2) + i, v & 255);
if (i >= b[0]) b[0] = i + 1;
return v & 255;
}
}
+
+//: array algorithms borrow their input and visit elements in index order.
+//: map and filter return independent handles. reference elements are shared
+//: with the collector, so freeing the input does not invalidate the result.
+
+// the first index equal to value, or -1 when no element matches.
+int array_find<T>(fixed T[?].m source, T value) {
+ int length = len(source);
+ for (int i = 0; i < length; i++) if (source[i] == value) return i;
+ return -1;
+}
+// true when an element equals value.
+int array_contains<T>(fixed T[?].m source, T value) { return array_find(source, value) >= 0; }
+// the first index whose predicate is true, or -1 when none matches.
+int array_find_if<T>(fixed T[?].m source, int(T).m predicate) {
+ int length = len(source);
+ for (int i = 0; i < length; i++) if (predicate(source[i])) return i;
+ return -1;
+}
+// true when any element satisfies predicate; false for an empty array.
+int array_any<T>(fixed T[?].m source, int(T).m predicate) { return array_find_if(source, predicate) >= 0; }
+// true when every element satisfies predicate, including an empty array.
+int array_all<T>(fixed T[?].m source, int(T).m predicate) {
+ int length = len(source);
+ for (int i = 0; i < length; i++) if (!predicate(source[i])) return 0;
+ return 1;
+}
+// the number of elements satisfying predicate.
+int array_count<T>(fixed T[?].m source, int(T).m predicate) {
+ int length = len(source);
+ int count = 0;
+ for (int i = 0; i < length; i++) if (predicate(source[i])) count++;
+ return count;
+}
+// apply transform once to each element and return the transformed array.
+// the result element type follows the callback's return type.
+U[?].u array_map<T,U>(fixed T[?].m source, U(T).m transform) {
+ int length = len(source);
+ U[?] result = rt.new(U[?]);
+ for (int i = 0; i < length; i++) push(result, run.share_value(transform(source[i])));
+ return result;
+}
+// copy elements satisfying predicate into a new array, preserving order.
+T[?].u array_filter<T>(fixed T[?].m source, int(T).m predicate) {
+ int length = len(source);
+ T[?] result = rt.new(T[?]);
+ for (int i = 0; i < length; i++) if (predicate(source[i])) push(result, run.share_value(source[i]));
+ return result;
+}
+// fold elements from left to right, starting with initial; an empty array
+// returns initial without calling combine.
+A array_reduce<T,A>(fixed T[?].m source, A initial, A(A,T).m combine) {
+ int length = len(source);
+ for (int i = 0; i < length; i++) initial = combine(initial, source[i]);
+ return initial;
+}
+
+run.method("find", array_find);
+run.method("contains", array_contains);
+run.method("find_if", array_find_if);
+run.method("any", array_any);
+run.method("all", array_all);
+run.method("count", array_count);
+run.method("map", array_map);
+run.method("filter", array_filter);
+run.method("reduce", array_reduce);