tova 0.3.4 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,52 @@
2
2
  // Single source of truth for all inline stdlib code used in code generation.
3
3
  // Used by: base-codegen.js, client-codegen.js, bin/tova.js
4
4
 
5
- export const RESULT_OPTION = `function Ok(value) { return Object.freeze({ __tag: "Ok", value, map(fn) { return Ok(fn(value)); }, flatMap(fn) { const r = fn(value); if (r && r.__tag) return r; throw new Error("flatMap callback must return Ok/Err"); }, andThen(fn) { return this.flatMap(fn); }, unwrap() { return value; }, unwrapOr(_) { return value; }, expect(_) { return value; }, isOk() { return true; }, isErr() { return false; }, mapErr(_) { return this; }, unwrapErr() { throw new Error("Called unwrapErr on Ok"); }, or(_) { return this; }, and(other) { return other; }, context(_) { return this; } }); }
6
- function Err(error) { return Object.freeze({ __tag: "Err", error, map(_) { return this; }, flatMap(_) { return this; }, andThen(_) { return this; }, unwrap() { throw new Error("Called unwrap on Err: " + (typeof error === "object" ? JSON.stringify(error) : error)); }, unwrapOr(def) { return def; }, expect(msg) { throw new Error(msg); }, isOk() { return false; }, isErr() { return true; }, mapErr(fn) { return Err(fn(error)); }, unwrapErr() { return error; }, or(other) { return other; }, and(_) { return this; }, context(msg) { const inner = typeof error === "object" ? JSON.stringify(error) : String(error); return Err(msg + " \\u2192 caused by: " + inner); } }); }
7
- function Some(value) { return Object.freeze({ __tag: "Some", value, map(fn) { return Some(fn(value)); }, flatMap(fn) { const r = fn(value); if (r && r.__tag) return r; throw new Error("flatMap callback must return Some/None"); }, andThen(fn) { return this.flatMap(fn); }, unwrap() { return value; }, unwrapOr(_) { return value; }, expect(_) { return value; }, isSome() { return true; }, isNone() { return false; }, or(_) { return this; }, and(other) { return other; }, filter(pred) { return pred(value) ? this : None; } }); }
5
+ export const RESULT_OPTION = `class _Ok { constructor(value) { this.value = value; } }
6
+ _Ok.prototype.__tag = "Ok";
7
+ _Ok.prototype.map = function(fn) { return new _Ok(fn(this.value)); };
8
+ _Ok.prototype.flatMap = function(fn) { const r = fn(this.value); if (r && r.__tag) return r; throw new Error("flatMap callback must return Ok/Err"); };
9
+ _Ok.prototype.andThen = _Ok.prototype.flatMap;
10
+ _Ok.prototype.unwrap = function() { return this.value; };
11
+ _Ok.prototype.unwrapOr = function(_) { return this.value; };
12
+ _Ok.prototype.expect = function(_) { return this.value; };
13
+ _Ok.prototype.isOk = function() { return true; };
14
+ _Ok.prototype.isErr = function() { return false; };
15
+ _Ok.prototype.mapErr = function(_) { return this; };
16
+ _Ok.prototype.unwrapErr = function() { throw new Error("Called unwrapErr on Ok"); };
17
+ _Ok.prototype.or = function(_) { return this; };
18
+ _Ok.prototype.and = function(other) { return other; };
19
+ _Ok.prototype.context = function(_) { return this; };
20
+ function Ok(value) { return new _Ok(value); }
21
+ class _Err { constructor(error) { this.error = error; } }
22
+ _Err.prototype.__tag = "Err";
23
+ _Err.prototype.map = function(_) { return this; };
24
+ _Err.prototype.flatMap = function(_) { return this; };
25
+ _Err.prototype.andThen = _Err.prototype.flatMap;
26
+ _Err.prototype.unwrap = function() { throw new Error("Called unwrap on Err: " + (typeof this.error === "object" ? JSON.stringify(this.error) : this.error)); };
27
+ _Err.prototype.unwrapOr = function(def) { return def; };
28
+ _Err.prototype.expect = function(msg) { throw new Error(msg); };
29
+ _Err.prototype.isOk = function() { return false; };
30
+ _Err.prototype.isErr = function() { return true; };
31
+ _Err.prototype.mapErr = function(fn) { return new _Err(fn(this.error)); };
32
+ _Err.prototype.unwrapErr = function() { return this.error; };
33
+ _Err.prototype.or = function(other) { return other; };
34
+ _Err.prototype.and = function(_) { return this; };
35
+ _Err.prototype.context = function(msg) { const inner = typeof this.error === "object" ? JSON.stringify(this.error) : String(this.error); return new _Err(msg + " \\u2192 caused by: " + inner); };
36
+ function Err(error) { return new _Err(error); }
37
+ class _Some { constructor(value) { this.value = value; } }
38
+ _Some.prototype.__tag = "Some";
39
+ _Some.prototype.map = function(fn) { return new _Some(fn(this.value)); };
40
+ _Some.prototype.flatMap = function(fn) { const r = fn(this.value); if (r && r.__tag) return r; throw new Error("flatMap callback must return Some/None"); };
41
+ _Some.prototype.andThen = _Some.prototype.flatMap;
42
+ _Some.prototype.unwrap = function() { return this.value; };
43
+ _Some.prototype.unwrapOr = function(_) { return this.value; };
44
+ _Some.prototype.expect = function(_) { return this.value; };
45
+ _Some.prototype.isSome = function() { return true; };
46
+ _Some.prototype.isNone = function() { return false; };
47
+ _Some.prototype.or = function(_) { return this; };
48
+ _Some.prototype.and = function(other) { return other; };
49
+ _Some.prototype.filter = function(pred) { return pred(this.value) ? this : None; };
50
+ function Some(value) { return new _Some(value); }
8
51
  const None = Object.freeze({ __tag: "None", map(_) { return None; }, flatMap(_) { return None; }, andThen(_) { return None; }, unwrap() { throw new Error("Called unwrap on None"); }, unwrapOr(def) { return def; }, expect(msg) { throw new Error(msg); }, isSome() { return false; }, isNone() { return true; }, or(other) { return other; }, and(_) { return None; }, filter(_) { return None; } });`;
9
52
 
10
53
  export const PROPAGATE = `function __propagate(val) {
@@ -18,11 +61,11 @@ export const PROPAGATE = `function __propagate(val) {
18
61
  // Individual builtin functions for tree-shaking
19
62
  export const BUILTIN_FUNCTIONS = {
20
63
  print: `function print(...args) { console.log(...args); }`,
21
- len: `function len(v) { if (v == null) return 0; if (typeof v === 'string' || Array.isArray(v)) return v.length; if (typeof v === 'object') return Object.keys(v).length; return 0; }`,
64
+ len: `function len(v) { if (v == null) return 0; if (typeof v === 'string' || Array.isArray(v) || ArrayBuffer.isView(v)) return v.length; if (typeof v === 'object') return Object.keys(v).length; return 0; }`,
22
65
  range: `function range(s, e, st) { if (e === undefined) { e = s; s = 0; } if (st === undefined) st = s < e ? 1 : -1; if (st === 0) return []; const r = []; if (st > 0) { for (let i = s; i < e; i += st) r.push(i); } else { for (let i = s; i > e; i += st) r.push(i); } return r; }`,
23
66
  enumerate: `function enumerate(a) { return a.map((v, i) => [i, v]); }`,
24
67
  sum: `function sum(a) { return a.reduce((x, y) => x + y, 0); }`,
25
- sorted: `function sorted(a, k) { const c = [...a]; if (k) c.sort((x, y) => { const kx = k(x), ky = k(y); return kx < ky ? -1 : kx > ky ? 1 : 0; }); else c.sort((x, y) => x < y ? -1 : x > y ? 1 : 0); return c; }`,
68
+ sorted: `function sorted(a, k) { const c = [...a]; if (k) c.sort((x, y) => { const kx = k(x), ky = k(y); return kx < ky ? -1 : kx > ky ? 1 : 0; }); else if (c.length > 0 && typeof c[0] === 'number') { if (typeof __tova_native !== 'undefined' && __tova_native && c.length > 128) { const f = new Float64Array(c); __tova_native.tova_sort_f64(f, f.length); for (let i = 0; i < c.length; i++) c[i] = f[i]; } else if (c.length > 128) { const f = new Float64Array(c); f.sort(); for (let i = 0; i < c.length; i++) c[i] = f[i]; } else { c.sort((a, b) => a - b); } } else c.sort((x, y) => x < y ? -1 : x > y ? 1 : 0); return c; }`,
26
69
  reversed: `function reversed(a) { return [...a].reverse(); }`,
27
70
  zip: `function zip(...as) { if (as.length === 0) return []; const m = Math.min(...as.map(a => a.length)); const r = []; for (let i = 0; i < m; i++) r.push(as.map(a => a[i])); return r; }`,
28
71
  min: `function min(a) { if (a.length === 0) return null; let m = a[0]; for (let i = 1; i < a.length; i++) if (a[i] < m) m = a[i]; return m; }`,
@@ -65,6 +108,34 @@ export const BUILTIN_FUNCTIONS = {
65
108
  freeze: `function freeze(obj) { return Object.freeze(obj); }`,
66
109
  clone: `function clone(obj) { return structuredClone(obj); }`,
67
110
  sleep: `function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }`,
111
+ parallel_map: `async function parallel_map(arr, fn, numWorkers) {
112
+ if (!arr || arr.length === 0) return [];
113
+ const cores = numWorkers || (typeof navigator !== 'undefined' ? navigator.hardwareConcurrency : 4) || 4;
114
+ const n = Math.min(cores, arr.length);
115
+ if (n <= 1 || arr.length < 4) return arr.map(fn);
116
+ if (!parallel_map._pool) {
117
+ const { Worker } = await import("worker_threads");
118
+ const wc = 'const{parentPort}=require("worker_threads");parentPort.on("message",m=>{const fn=(0,eval)("("+m.f+")");try{const r=m.c.map(fn);parentPort.postMessage({i:m.i,r})}catch(e){parentPort.postMessage({i:m.i,e:e.message})}})';
119
+ parallel_map._pool = Array.from({length: n}, () => { const w = new Worker(wc, {eval: true}); w.unref(); return w; });
120
+ parallel_map._cid = 0;
121
+ }
122
+ const pool = parallel_map._pool;
123
+ const cs = Math.ceil(arr.length / pool.length);
124
+ const fnStr = fn.toString();
125
+ const cid = ++parallel_map._cid;
126
+ const promises = [];
127
+ for (let ci = 0; ci < pool.length && ci * cs < arr.length; ci++) {
128
+ const chunk = arr.slice(ci * cs, (ci + 1) * cs);
129
+ const mid = cid * 1000 + ci;
130
+ promises.push(new Promise((resolve, reject) => {
131
+ const w = pool[ci];
132
+ const h = (msg) => { if (msg.i === mid) { w.removeListener("message", h); if (msg.e) reject(new Error(msg.e)); else resolve(msg.r); } };
133
+ w.on("message", h);
134
+ w.postMessage({i: mid, c: chunk, f: fnStr});
135
+ }));
136
+ }
137
+ return (await Promise.all(promises)).flat();
138
+ }`,
68
139
  upper: `function upper(s) { return s.toUpperCase(); }`,
69
140
  lower: `function lower(s) { return s.toLowerCase(); }`,
70
141
  contains: `function contains(s, sub) { return s.includes(sub); }`,
@@ -173,6 +244,102 @@ Table.prototype = { get rows() { return this._rows.length; }, get columns() { re
173
244
  table_drop_duplicates: `function table_drop_duplicates(table, opts) { const by = opts && opts.by; const seen = new Set(); const rows = []; for (const row of table._rows) { const k = by ? (typeof by === 'function' ? String(by(row)) : String(row[by])) : JSON.stringify(row); if (!seen.has(k)) { seen.add(k); rows.push(row); } } return Table(rows, table._columns); }`,
174
245
  table_rename: `function table_rename(table, oldName, newName) { const cols = table._columns.map(c => c === oldName ? newName : c); const rows = table._rows.map(r => { const row = {}; for (const c of table._columns) row[c === oldName ? newName : c] = r[c]; return row; }); return Table(rows, cols); }`,
175
246
 
247
+ // ── Lazy Table Query Builder ────────────────────────
248
+ lazy: `function lazy(table) { return new LazyTable(table); }`,
249
+ collect: `function collect(v) { if (v instanceof LazyTable) return v.collect(); if (v && v._gen) return v.collect(); return v; }`,
250
+ LazyTable: `class LazyTable {
251
+ constructor(source) {
252
+ this._source = source;
253
+ this._steps = [];
254
+ }
255
+ _push(step) { const lt = new LazyTable(this._source); lt._steps = [...this._steps, step]; return lt; }
256
+ where(pred) { return this._push({ op: 'where', fn: pred }); }
257
+ select(...args) {
258
+ let cols;
259
+ if (args.length === 1 && args[0] && args[0].__exclude) {
260
+ cols = { exclude: new Set(Array.isArray(args[0].__exclude) ? args[0].__exclude : [args[0].__exclude]) };
261
+ } else { cols = args.filter(a => typeof a === 'string'); }
262
+ return this._push({ op: 'select', cols });
263
+ }
264
+ derive(derivations) { return this._push({ op: 'derive', derivations }); }
265
+ limit(n) { return this._push({ op: 'limit', n }); }
266
+ drop_duplicates(opts) { return this._push({ op: 'dedup', by: opts && opts.by }); }
267
+ rename(oldName, newName) { return this._push({ op: 'rename', oldName, newName }); }
268
+ sort_by(keyFn, opts) { return this._push({ op: 'sort', keyFn, desc: opts && opts.desc }); }
269
+ group_by(keyFn) {
270
+ const rows = this.collect()._rows;
271
+ const src = Table(rows, this._resolveColumns());
272
+ return table_group_by(src, keyFn);
273
+ }
274
+ _resolveColumns() {
275
+ let cols = [...this._source._columns];
276
+ for (const s of this._steps) {
277
+ if (s.op === 'select') {
278
+ cols = s.cols.exclude ? cols.filter(c => !s.cols.exclude.has(c)) : [...s.cols];
279
+ } else if (s.op === 'derive') {
280
+ for (const k of Object.keys(s.derivations)) { if (!cols.includes(k)) cols.push(k); }
281
+ } else if (s.op === 'rename') {
282
+ cols = cols.map(c => c === s.oldName ? s.newName : c);
283
+ }
284
+ }
285
+ return cols;
286
+ }
287
+ collect() {
288
+ let rows = this._source._rows;
289
+ let cols = [...this._source._columns];
290
+ for (const step of this._steps) {
291
+ switch (step.op) {
292
+ case 'where': rows = rows.filter(step.fn); break;
293
+ case 'select': {
294
+ const sc = step.cols.exclude ? cols.filter(c => !step.cols.exclude.has(c)) : step.cols;
295
+ rows = rows.map(r => { const row = {}; for (const c of sc) row[c] = r[c]; return row; });
296
+ cols = [...sc];
297
+ break;
298
+ }
299
+ case 'derive': {
300
+ for (const k of Object.keys(step.derivations)) { if (!cols.includes(k)) cols.push(k); }
301
+ rows = rows.map(r => { const row = { ...r }; for (const [k, fn] of Object.entries(step.derivations)) { row[k] = typeof fn === 'function' ? fn(r) : fn; } return row; });
302
+ break;
303
+ }
304
+ case 'limit': rows = rows.slice(0, step.n); break;
305
+ case 'dedup': {
306
+ const seen = new Set();
307
+ const filtered = [];
308
+ for (const row of rows) {
309
+ const k = step.by ? (typeof step.by === 'function' ? String(step.by(row)) : String(row[step.by])) : JSON.stringify(row);
310
+ if (!seen.has(k)) { seen.add(k); filtered.push(row); }
311
+ }
312
+ rows = filtered;
313
+ break;
314
+ }
315
+ case 'rename': {
316
+ cols = cols.map(c => c === step.oldName ? step.newName : c);
317
+ rows = rows.map(r => { const row = {}; for (const c of cols) row[c === step.newName ? step.newName : c] = r[c === step.newName ? step.oldName : c]; return row; });
318
+ break;
319
+ }
320
+ case 'sort': {
321
+ rows = [...rows].sort((a, b) => {
322
+ const ka = typeof step.keyFn === 'function' ? step.keyFn(a) : a[step.keyFn];
323
+ const kb = typeof step.keyFn === 'function' ? step.keyFn(b) : b[step.keyFn];
324
+ let c = ka < kb ? -1 : ka > kb ? 1 : 0;
325
+ return step.desc ? -c : c;
326
+ });
327
+ break;
328
+ }
329
+ }
330
+ }
331
+ return Table(rows, cols);
332
+ }
333
+ toArray() { return this.collect()._rows; }
334
+ toJSON() { return this.toArray(); }
335
+ get rows() { return this.collect()._rows.length; }
336
+ get columns() { return this._resolveColumns(); }
337
+ get shape() { const t = this.collect(); return [t._rows.length, t._columns.length]; }
338
+ toString() { return this.collect().toString(); }
339
+ _format(maxRows, title) { return this.collect()._format(maxRows, title); }
340
+ [Symbol.iterator]() { return this.collect()._rows[Symbol.iterator](); }
341
+ }`,
342
+
176
343
  // ── Aggregation helpers ─────────────────────────────
177
344
  agg_sum: `function agg_sum(fn) { return (rows) => rows.reduce((a, r) => a + (typeof fn === 'function' ? fn(r) : r[fn]), 0); }`,
178
345
  agg_count: `function agg_count(fn) { if (!fn) return (rows) => rows.length; return (rows) => rows.filter(fn).length; }`,
@@ -236,25 +403,26 @@ Table.prototype = { get rows() { return this._rows.length; }, get columns() { re
236
403
  const lines = text.split('\\n').filter(l => l.trim());
237
404
  if (lines.length === 0) return Table([]);
238
405
  const parseLine = (line) => { const fields = []; let cur = ''; let inQ = false; for (let i = 0; i < line.length; i++) { const ch = line[i]; if (inQ) { if (ch === '"' && line[i+1] === '"') { cur += '"'; i++; } else if (ch === '"') { inQ = false; } else { cur += ch; } } else { if (ch === '"') inQ = true; else if (ch === delim) { fields.push(cur.trim()); cur = ''; } else { cur += ch; } } } fields.push(cur.trim()); return fields; };
406
+ const _reInt = /^-?\\d+$/; const _reFloat = /^-?\\d*\\.\\d+$/;
239
407
  let headers, ds; if (hasHeader) { headers = parseLine(lines[0]); ds = 1; } else { const fr = parseLine(lines[0]); headers = fr.map((_, i) => 'col_' + i); ds = 0; }
240
- const rows = []; for (let i = ds; i < lines.length; i++) { const f = parseLine(lines[i]); const row = {}; for (let j = 0; j < headers.length; j++) { let v = f[j] ?? null; if (v !== null && v !== '') { if (/^-?\\d+$/.test(v)) v = parseInt(v, 10); else if (/^-?\\d*\\.\\d+$/.test(v)) v = parseFloat(v); else if (v === 'true') v = true; else if (v === 'false') v = false; else if (v === 'null' || v === 'nil') v = null; } else if (v === '') v = null; row[headers[j]] = v; } rows.push(row); }
408
+ const rows = []; for (let i = ds; i < lines.length; i++) { const f = parseLine(lines[i]); const row = {}; for (let j = 0; j < headers.length; j++) { let v = f[j] ?? null; if (v !== null && v !== '') { if (_reInt.test(v)) v = parseInt(v, 10); else if (_reFloat.test(v)) v = parseFloat(v); else if (v === 'true') v = true; else if (v === 'false') v = false; else if (v === 'null' || v === 'nil') v = null; } else if (v === '') v = null; row[headers[j]] = v; } rows.push(row); }
241
409
  return Table(rows, headers);
242
410
  }`,
243
411
  __parseJSONL: `function __parseJSONL(text) { return Table(text.split('\\n').filter(l => l.trim()).map(l => JSON.parse(l))); }`,
244
412
 
245
413
  // ── Table operation aliases (short names for pipe-friendly use) ──
246
- where: `function where(tableOrArr, pred) { if (tableOrArr && tableOrArr._rows) return table_where(tableOrArr, pred); return tableOrArr.filter(pred); }`,
247
- select: `function select(table, ...args) { return table_select(table, ...args); }`,
248
- derive: `function derive(table, derivations) { return table_derive(table, derivations); }`,
414
+ where: `function where(tableOrArr, pred) { if (tableOrArr instanceof LazyTable) return tableOrArr.where(pred); if (tableOrArr && tableOrArr._rows) return table_where(tableOrArr, pred); return tableOrArr.filter(pred); }`,
415
+ select: `function select(table, ...args) { if (table instanceof LazyTable) return table.select(...args); return table_select(table, ...args); }`,
416
+ derive: `function derive(table, derivations) { if (table instanceof LazyTable) return table.derive(derivations); return table_derive(table, derivations); }`,
249
417
  agg: `function agg(grouped, aggregations) { return table_agg(grouped, aggregations); }`,
250
- sort_by: `function sort_by(table, keyFn, opts) { return table_sort_by(table, keyFn, opts); }`,
251
- limit: `function limit(table, n) { return table_limit(table, n); }`,
418
+ sort_by: `function sort_by(table, keyFn, opts) { if (table instanceof LazyTable) return table.sort_by(keyFn, opts); return table_sort_by(table, keyFn, opts); }`,
419
+ limit: `function limit(table, n) { if (table instanceof LazyTable) return table.limit(n); return table_limit(table, n); }`,
252
420
  pivot: `function pivot(table, opts) { return table_pivot(table, opts); }`,
253
421
  unpivot: `function unpivot(table, opts) { return table_unpivot(table, opts); }`,
254
422
  explode: `function explode(table, colFn) { return table_explode(table, colFn); }`,
255
423
  union: `function union(a, b) { if (a && a._rows) return table_union(a, b); return [...new Set([...a, ...b])]; }`,
256
- drop_duplicates: `function drop_duplicates(table, opts) { return table_drop_duplicates(table, opts); }`,
257
- rename: `function rename(table, oldName, newName) { return table_rename(table, oldName, newName); }`,
424
+ drop_duplicates: `function drop_duplicates(table, opts) { if (table instanceof LazyTable) return table.drop_duplicates(opts); return table_drop_duplicates(table, opts); }`,
425
+ rename: `function rename(table, oldName, newName) { if (table instanceof LazyTable) return table.rename(oldName, newName); return table_rename(table, oldName, newName); }`,
258
426
  mean: `function mean(v) { if (Array.isArray(v)) { return v.length === 0 ? 0 : v.reduce((a, b) => a + b, 0) / v.length; } return agg_mean(v); }`,
259
427
  median: `function median(v) { if (Array.isArray(v)) { if (v.length === 0) return null; const s = [...v].sort((a, b) => a - b); const m = Math.floor(s.length / 2); return s.length % 2 === 0 ? (s[m - 1] + s[m]) / 2 : s[m]; } return agg_median(v); }`,
260
428
 
@@ -332,13 +500,14 @@ Table.prototype = { get rows() { return this._rows.length; }, get columns() { re
332
500
  date_part: `function date_part(d, part) { if (typeof d === 'number') d = new Date(d); if (part === 'year') return d.getFullYear(); if (part === 'month') return d.getMonth() + 1; if (part === 'day') return d.getDate(); if (part === 'hour') return d.getHours(); if (part === 'minute') return d.getMinutes(); if (part === 'second') return d.getSeconds(); if (part === 'weekday') return d.getDay(); return null; }`,
333
501
  time_ago: `function time_ago(d) { if (typeof d === 'number') d = new Date(d); const s = Math.floor((Date.now() - d.getTime()) / 1000); if (s < 60) return s + ' seconds ago'; const m = Math.floor(s / 60); if (m < 60) return m + (m === 1 ? ' minute ago' : ' minutes ago'); const h = Math.floor(m / 60); if (h < 24) return h + (h === 1 ? ' hour ago' : ' hours ago'); const dy = Math.floor(h / 24); if (dy < 30) return dy + (dy === 1 ? ' day ago' : ' days ago'); const mo = Math.floor(dy / 30); if (mo < 12) return mo + (mo === 1 ? ' month ago' : ' months ago'); const yr = Math.floor(mo / 12); return yr + (yr === 1 ? ' year ago' : ' years ago'); }`,
334
502
 
335
- // ── Regex ──────────────────────────────────────────────
336
- regex_test: `function regex_test(s, pattern, flags) { return new RegExp(pattern, flags).test(s); }`,
337
- regex_match: `function regex_match(s, pattern, flags) { const m = s.match(new RegExp(pattern, flags)); if (!m) return Err('No match'); return Ok({ match: m[0], index: m.index, groups: m.slice(1) }); }`,
338
- regex_find_all: `function regex_find_all(s, pattern, flags) { const re = new RegExp(pattern, (flags || '') + (flags && flags.includes('g') ? '' : 'g')); const results = []; let m; while ((m = re.exec(s)) !== null) { results.push({ match: m[0], index: m.index, groups: m.slice(1) }); } return results; }`,
339
- regex_replace: `function regex_replace(s, pattern, replacement, flags) { return s.replace(new RegExp(pattern, flags || 'g'), replacement); }`,
340
- regex_split: `function regex_split(s, pattern, flags) { return s.split(new RegExp(pattern, flags)); }`,
341
- regex_capture: `function regex_capture(s, pattern, flags) { const m = s.match(new RegExp(pattern, flags)); if (!m) return Err('No match'); if (!m.groups) return Err('No named groups'); return Ok(m.groups); }`,
503
+ // ── Regex (with compiled regex cache) ─────────────────
504
+ __regex_cache: `const __reCache = new Map(); function __re(p, f) { const k = p + '\\0' + (f || ''); let r = __reCache.get(k); if (!r) { r = new RegExp(p, f); __reCache.set(k, r); if (__reCache.size > 1000) { const first = __reCache.keys().next().value; __reCache.delete(first); } } return r; }`,
505
+ regex_test: `function regex_test(s, pattern, flags) { return __re(pattern, flags).test(s); }`,
506
+ regex_match: `function regex_match(s, pattern, flags) { const m = s.match(__re(pattern, flags)); if (!m) return Err('No match'); return Ok({ match: m[0], index: m.index, groups: m.slice(1) }); }`,
507
+ regex_find_all: `function regex_find_all(s, pattern, flags) { const re = __re(pattern, (flags || '') + (flags && flags.includes('g') ? '' : 'g')); const results = []; let m; re.lastIndex = 0; while ((m = re.exec(s)) !== null) { results.push({ match: m[0], index: m.index, groups: m.slice(1) }); } return results; }`,
508
+ regex_replace: `function regex_replace(s, pattern, replacement, flags) { return s.replace(__re(pattern, flags || 'g'), replacement); }`,
509
+ regex_split: `function regex_split(s, pattern, flags) { return s.split(__re(pattern, flags)); }`,
510
+ regex_capture: `function regex_capture(s, pattern, flags) { const m = s.match(__re(pattern, flags)); if (!m) return Err('No match'); if (!m.groups) return Err('No named groups'); return Ok(m.groups); }`,
342
511
 
343
512
  // ── Validation ─────────────────────────────────────────
344
513
  is_email: `function is_email(s) { return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(s); }`,
@@ -857,6 +1026,102 @@ Table.prototype = { get rows() { return this._rows.length; }, get columns() { re
857
1026
  collections: `const collections = Object.freeze({
858
1027
  OrderedDict, DefaultDict, Counter, Deque
859
1028
  });`,
1029
+
1030
+ // ─── Typed numeric array functions for @fast mode ───────────────
1031
+
1032
+ typed_zeros: `function typed_zeros(n, Type) {
1033
+ return new (Type || Float64Array)(n);
1034
+ }`,
1035
+
1036
+ typed_ones: `function typed_ones(n, Type) {
1037
+ const out = new (Type || Float64Array)(n);
1038
+ out.fill(1);
1039
+ return out;
1040
+ }`,
1041
+
1042
+ typed_fill: `function typed_fill(arr, value) {
1043
+ const out = new arr.constructor(arr.length);
1044
+ out.fill(value);
1045
+ return out;
1046
+ }`,
1047
+
1048
+ typed_range: `function typed_range(start, end, step) {
1049
+ step = step || 1;
1050
+ const n = Math.ceil((end - start) / step);
1051
+ const arr = new Float64Array(n);
1052
+ for (let i = 0; i < n; i++) arr[i] = start + i * step;
1053
+ return arr;
1054
+ }`,
1055
+
1056
+ typed_linspace: `function typed_linspace(start, end, n) {
1057
+ const out = new Float64Array(n);
1058
+ if (n <= 1) { if (n === 1) out[0] = start; return out; }
1059
+ const step = (end - start) / (n - 1);
1060
+ for (let i = 0; i < n; i++) out[i] = start + i * step;
1061
+ return out;
1062
+ }`,
1063
+
1064
+ typed_sum: `function typed_sum(arr) {
1065
+ let s = 0, c = 0;
1066
+ for (let i = 0; i < arr.length; i++) {
1067
+ const y = arr[i] - c;
1068
+ const t = s + y;
1069
+ c = (t - s) - y;
1070
+ s = t;
1071
+ }
1072
+ return s;
1073
+ }`,
1074
+
1075
+ typed_dot: `function typed_dot(a, b) {
1076
+ const n = a.length;
1077
+ let s = 0;
1078
+ for (let i = 0; i < n; i++) s += a[i] * b[i];
1079
+ return s;
1080
+ }`,
1081
+
1082
+ typed_norm: `function typed_norm(arr) {
1083
+ let s = 0;
1084
+ for (let i = 0; i < arr.length; i++) s += arr[i] * arr[i];
1085
+ return Math.sqrt(s);
1086
+ }`,
1087
+
1088
+ typed_add: `function typed_add(a, b) {
1089
+ const n = a.length;
1090
+ const out = new a.constructor(n);
1091
+ for (let i = 0; i < n; i++) out[i] = a[i] + b[i];
1092
+ return out;
1093
+ }`,
1094
+
1095
+ typed_scale: `function typed_scale(arr, scalar) {
1096
+ const out = new arr.constructor(arr.length);
1097
+ for (let i = 0; i < arr.length; i++) out[i] = arr[i] * scalar;
1098
+ return out;
1099
+ }`,
1100
+
1101
+ typed_map: `function typed_map(arr, fn) {
1102
+ const out = new arr.constructor(arr.length);
1103
+ for (let i = 0; i < arr.length; i++) out[i] = fn(arr[i], i);
1104
+ return out;
1105
+ }`,
1106
+
1107
+ typed_reduce: `function typed_reduce(arr, fn, init) {
1108
+ let acc = init;
1109
+ for (let i = 0; i < arr.length; i++) acc = fn(acc, arr[i], i);
1110
+ return acc;
1111
+ }`,
1112
+
1113
+ typed_sort: `function typed_sort(arr) {
1114
+ if (arr instanceof Float64Array || arr instanceof Int32Array || arr instanceof Uint8Array ||
1115
+ arr instanceof Float32Array || arr instanceof Int16Array || arr instanceof Uint16Array ||
1116
+ arr instanceof Uint32Array || arr instanceof Int8Array) {
1117
+ const out = new arr.constructor(arr);
1118
+ out.sort();
1119
+ return out;
1120
+ }
1121
+ const out = [...arr];
1122
+ out.sort((a, b) => a - b);
1123
+ return out;
1124
+ }`,
860
1125
  };
861
1126
 
862
1127
  // All known builtin names for matching
@@ -881,13 +1146,21 @@ export const STDLIB_DEPS = {
881
1146
  fs: ['Ok', 'Err'],
882
1147
  url: ['Ok', 'Err'],
883
1148
  parse_url: ['Ok', 'Err'],
884
- regex_match: ['Ok', 'Err'],
885
- regex_capture: ['Ok', 'Err'],
1149
+ regex_test: ['__regex_cache'],
1150
+ regex_match: ['Ok', 'Err', '__regex_cache'],
1151
+ regex_find_all: ['__regex_cache'],
1152
+ regex_replace: ['__regex_cache'],
1153
+ regex_split: ['__regex_cache'],
1154
+ regex_capture: ['Ok', 'Err', '__regex_cache'],
886
1155
  json_parse: ['Ok', 'Err'],
887
1156
  date_parse: ['Ok', 'Err'],
888
1157
  read_text: ['Ok', 'Err'],
889
1158
  try_fn: ['Ok', 'Err'],
890
1159
  try_async: ['Ok', 'Err'],
1160
+ // LazyTable requires Table and table_* functions
1161
+ lazy: ['LazyTable', 'Table'],
1162
+ collect: ['LazyTable'],
1163
+ LazyTable: ['Table', 'table_where', 'table_group_by'],
891
1164
  // Seq uses Some/None
892
1165
  Seq: ['Some', 'None'],
893
1166
  // compare family
@@ -954,6 +1227,7 @@ const _LEGACY_NAMES = [
954
1227
  'lines', 'capitalize', 'title_case', 'snake_case', 'camel_case',
955
1228
  'assert_eq', 'assert_ne', 'assert', 'assert_throws',
956
1229
  'create_spy', 'create_mock',
1230
+ 'parallel_map',
957
1231
  ];
958
1232
  export const BUILTINS = _LEGACY_NAMES.map(n => BUILTIN_FUNCTIONS[n]).join('\n');
959
1233
 
@@ -971,9 +1245,44 @@ export function buildSelectiveStdlib(usedNames) {
971
1245
  return parts.join('\n');
972
1246
  }
973
1247
 
974
- // Full stdlib for runtime (REPL, run command)
1248
+ // Native FFI bridge initialization (server-side only, Bun runtime)
1249
+ // Lazily loads the Rust native library for high-performance stdlib operations
1250
+ // Async version for tova run (AsyncFunction context supports await)
1251
+ export const NATIVE_INIT = `var __tova_native = null;
1252
+ try {
1253
+ if (typeof Bun !== 'undefined') {
1254
+ const { dlopen: __dl, FFIType: __F } = await import('bun:ffi');
1255
+ const __path = await import('path');
1256
+ const __fs = await import('fs');
1257
+ const __searchDirs = [
1258
+ __path.join(__path.dirname(typeof __tova_filename !== 'undefined' ? __tova_filename : ''), 'native', 'target', 'release'),
1259
+ __path.join(process.cwd(), 'native', 'target', 'release'),
1260
+ __path.join(process.env.HOME || '', '.tova', 'lib'),
1261
+ ];
1262
+ const __libName = process.platform === 'darwin' ? 'libtova_native.dylib' : process.platform === 'win32' ? 'tova_native.dll' : 'libtova_native.so';
1263
+ for (const __d of __searchDirs) {
1264
+ const __p = __path.join(__d, __libName);
1265
+ if (__fs.existsSync(__p)) {
1266
+ const __lib = __dl(__p, {
1267
+ tova_sort_f64: { args: [__F.ptr, __F.u64], returns: __F.void },
1268
+ tova_sort_i64: { args: [__F.ptr, __F.u64], returns: __F.void },
1269
+ tova_sum_f64: { args: [__F.ptr, __F.u64], returns: __F.f64 },
1270
+ tova_min_f64: { args: [__F.ptr, __F.u64], returns: __F.f64 },
1271
+ tova_max_f64: { args: [__F.ptr, __F.u64], returns: __F.f64 },
1272
+ });
1273
+ __tova_native = __lib.symbols;
1274
+ break;
1275
+ }
1276
+ }
1277
+ }
1278
+ } catch (__e) {}`;
1279
+
1280
+ // Sync-safe version without await (for non-async contexts like tests, REPL eval)
1281
+ export const NATIVE_INIT_SYNC = `var __tova_native = null;`;
1282
+
1283
+ // Full stdlib for runtime (REPL, run command) — sync-safe (no await)
975
1284
  export function getFullStdlib() {
976
- return `${buildSelectiveStdlib(BUILTIN_NAMES)}\n${RESULT_OPTION}\n${PROPAGATE}`;
1285
+ return `${NATIVE_INIT_SYNC}\n${buildSelectiveStdlib(BUILTIN_NAMES)}\n${RESULT_OPTION}\n${PROPAGATE}`;
977
1286
  }
978
1287
 
979
1288
  // Stdlib for client codegen (includes builtins + result/option + propagate)
@@ -0,0 +1,150 @@
1
+ // Tova Native FFI Bridge
2
+ // Provides high-performance Rust-backed operations via Bun FFI
3
+ // Falls back gracefully to pure JS when native library is unavailable
4
+
5
+ let _lib = null;
6
+ let _available = false;
7
+
8
+ function _findLibrary() {
9
+ const { existsSync } = require('fs');
10
+ const { join, dirname } = require('path');
11
+
12
+ // Search paths for the native library
13
+ const names = process.platform === 'darwin'
14
+ ? ['libtova_native.dylib']
15
+ : process.platform === 'win32'
16
+ ? ['tova_native.dll']
17
+ : ['libtova_native.so'];
18
+
19
+ const searchDirs = [
20
+ // Relative to this file (src/stdlib/)
21
+ join(dirname(__filename), '..', '..', 'native', 'target', 'release'),
22
+ // Relative to bin/tova.js
23
+ join(dirname(__filename), '..', 'native', 'target', 'release'),
24
+ // System-wide install
25
+ join(process.env.HOME || '', '.tova', 'lib'),
26
+ // Next to the binary
27
+ dirname(process.argv[1] || ''),
28
+ ];
29
+
30
+ for (const dir of searchDirs) {
31
+ for (const name of names) {
32
+ const path = join(dir, name);
33
+ if (existsSync(path)) return path;
34
+ }
35
+ }
36
+ return null;
37
+ }
38
+
39
+ function _init() {
40
+ if (_lib !== null) return _available;
41
+
42
+ try {
43
+ const libPath = _findLibrary();
44
+ if (!libPath) {
45
+ _lib = false;
46
+ _available = false;
47
+ return false;
48
+ }
49
+
50
+ const { dlopen, FFIType } = require('bun:ffi');
51
+ _lib = dlopen(libPath, {
52
+ tova_sort_f64: {
53
+ args: [FFIType.ptr, FFIType.u64],
54
+ returns: FFIType.void,
55
+ },
56
+ tova_sort_i64: {
57
+ args: [FFIType.ptr, FFIType.u64],
58
+ returns: FFIType.void,
59
+ },
60
+ tova_unique_sorted_i64: {
61
+ args: [FFIType.ptr, FFIType.u64],
62
+ returns: FFIType.u64,
63
+ },
64
+ tova_unique_sorted_f64: {
65
+ args: [FFIType.ptr, FFIType.u64],
66
+ returns: FFIType.u64,
67
+ },
68
+ tova_sum_f64: {
69
+ args: [FFIType.ptr, FFIType.u64],
70
+ returns: FFIType.f64,
71
+ },
72
+ tova_min_f64: {
73
+ args: [FFIType.ptr, FFIType.u64],
74
+ returns: FFIType.f64,
75
+ },
76
+ tova_max_f64: {
77
+ args: [FFIType.ptr, FFIType.u64],
78
+ returns: FFIType.f64,
79
+ },
80
+ });
81
+ _available = true;
82
+ return true;
83
+ } catch (e) {
84
+ _lib = false;
85
+ _available = false;
86
+ return false;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Sort an array of numbers using Rust radix sort.
92
+ * Returns a new sorted array. Falls back to JS sort if native unavailable.
93
+ * Only used for arrays above the threshold (overhead of copying to/from TypedArray).
94
+ */
95
+ export function nativeSortNumbers(arr) {
96
+ if (!_init()) return null; // fallback signal
97
+
98
+ const len = arr.length;
99
+ const buf = new Float64Array(len);
100
+ for (let i = 0; i < len; i++) buf[i] = arr[i];
101
+
102
+ _lib.symbols.tova_sort_f64(buf, len);
103
+
104
+ const result = new Array(len);
105
+ for (let i = 0; i < len; i++) result[i] = buf[i];
106
+ return result;
107
+ }
108
+
109
+ /**
110
+ * Sort a Float64Array in-place using Rust radix sort.
111
+ */
112
+ export function nativeSortF64(buf) {
113
+ if (!_init()) return false;
114
+ _lib.symbols.tova_sort_f64(buf, buf.length);
115
+ return true;
116
+ }
117
+
118
+ /**
119
+ * Sum an array of numbers using Rust Kahan summation.
120
+ */
121
+ export function nativeSum(arr) {
122
+ if (!_init()) return null;
123
+ const buf = new Float64Array(arr);
124
+ return _lib.symbols.tova_sum_f64(buf, buf.length);
125
+ }
126
+
127
+ /**
128
+ * Find min of numeric array using Rust.
129
+ */
130
+ export function nativeMin(arr) {
131
+ if (!_init()) return null;
132
+ const buf = new Float64Array(arr);
133
+ return _lib.symbols.tova_min_f64(buf, buf.length);
134
+ }
135
+
136
+ /**
137
+ * Find max of numeric array using Rust.
138
+ */
139
+ export function nativeMax(arr) {
140
+ if (!_init()) return null;
141
+ const buf = new Float64Array(arr);
142
+ return _lib.symbols.tova_max_f64(buf, buf.length);
143
+ }
144
+
145
+ /**
146
+ * Check if native library is available.
147
+ */
148
+ export function isNativeAvailable() {
149
+ return _init();
150
+ }
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Auto-generated by scripts/embed-runtime.js — do not edit
2
- export const VERSION = "0.3.4";
2
+ export const VERSION = "0.3.6";