numbl 0.1.3 → 0.1.5

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.
package/dist-lib/lib.js CHANGED
@@ -18789,6 +18789,258 @@ function coerceToTensor(v, name) {
18789
18789
  throw new RuntimeError(`${name}: argument must be numeric`);
18790
18790
  }
18791
18791
 
18792
+ // src/numbl-core/helpers/string.ts
18793
+ function numStr(n) {
18794
+ if (n === Infinity) return "Inf";
18795
+ if (n === -Infinity) return "-Inf";
18796
+ if (isNaN(n)) return "NaN";
18797
+ if (n === 0) return "0";
18798
+ const prec = 5;
18799
+ const exp = Math.floor(Math.log10(Math.abs(n)));
18800
+ let s;
18801
+ if (exp < -4 || exp >= prec) {
18802
+ s = n.toExponential(prec - 1);
18803
+ const ePos = s.indexOf("e");
18804
+ let mantissa = s.slice(0, ePos);
18805
+ const expPart0 = s.slice(ePos);
18806
+ if (mantissa.includes(".")) mantissa = mantissa.replace(/\.?0+$/, "");
18807
+ const expPart = expPart0.replace(/([eE][+-])(\d)$/, "$10$2");
18808
+ s = mantissa + expPart;
18809
+ } else {
18810
+ if (Number.isInteger(n)) return String(n);
18811
+ s = n.toPrecision(prec);
18812
+ if (s.includes(".")) s = s.replace(/\.?0+$/, "");
18813
+ }
18814
+ return s;
18815
+ }
18816
+ function num2strScalar(n) {
18817
+ if (n === Infinity) return "Inf";
18818
+ if (n === -Infinity) return "-Inf";
18819
+ if (isNaN(n)) return "NaN";
18820
+ if (n === 0) return "0";
18821
+ if (Number.isInteger(n)) return String(n);
18822
+ const prec = 5;
18823
+ const exp = Math.floor(Math.log10(Math.abs(n)));
18824
+ let s;
18825
+ if (exp < -4 || exp >= prec) {
18826
+ s = n.toExponential(prec - 1);
18827
+ const ePos = s.indexOf("e");
18828
+ let mantissa = s.slice(0, ePos);
18829
+ const expPart0 = s.slice(ePos);
18830
+ if (mantissa.includes(".")) mantissa = mantissa.replace(/\.?0+$/, "");
18831
+ const expPart = expPart0.replace(/([eE][+-])(\d)$/, "$10$2");
18832
+ s = mantissa + expPart;
18833
+ } else {
18834
+ s = n.toPrecision(prec);
18835
+ if (s.includes(".")) s = s.replace(/\.?0+$/, "");
18836
+ }
18837
+ return s;
18838
+ }
18839
+ function applyWidth(spec, str) {
18840
+ const m = spec.match(/^%([-+ #]*)0?(\d+)?/);
18841
+ if (!m) return str;
18842
+ const explicitFlags = m[1] || "";
18843
+ const leftAlign = explicitFlags.includes("-");
18844
+ const afterPercent = spec.slice(1);
18845
+ const flagAndWidth = afterPercent.match(/^([-+ #]*)(0?)(\d+)?/);
18846
+ const zeroFlag = flagAndWidth ? flagAndWidth[2] === "0" : false;
18847
+ const width = flagAndWidth && flagAndWidth[3] ? parseInt(flagAndWidth[3]) : 0;
18848
+ if (width <= str.length) return str;
18849
+ const zeroPad = !leftAlign && zeroFlag;
18850
+ const padLen = width - str.length;
18851
+ if (leftAlign) return str + " ".repeat(padLen);
18852
+ if (zeroPad) {
18853
+ if (str[0] === "-" || str[0] === "+") {
18854
+ return str[0] + "0".repeat(padLen) + str.slice(1);
18855
+ }
18856
+ return "0".repeat(padLen) + str;
18857
+ }
18858
+ return " ".repeat(padLen) + str;
18859
+ }
18860
+ function sprintfFormat(fmt, args) {
18861
+ const flatArgs = [];
18862
+ for (const arg of args) {
18863
+ if (isRuntimeTensor(arg)) {
18864
+ for (let k = 0; k < arg.data.length; k++) {
18865
+ flatArgs.push(arg.data[k]);
18866
+ }
18867
+ } else {
18868
+ flatArgs.push(arg);
18869
+ }
18870
+ }
18871
+ let result = "";
18872
+ let argIdx = 0;
18873
+ do {
18874
+ const startArgIdx = argIdx;
18875
+ let outOfArgs = false;
18876
+ let i = 0;
18877
+ while (i < fmt.length && !outOfArgs) {
18878
+ if (fmt[i] === "%" && i + 1 < fmt.length) {
18879
+ i++;
18880
+ let spec = "%";
18881
+ while (i < fmt.length && !"dfigeEsoxXuc%".includes(fmt[i])) {
18882
+ if (fmt[i] === "*") {
18883
+ if (argIdx >= flatArgs.length) {
18884
+ outOfArgs = true;
18885
+ break;
18886
+ }
18887
+ spec += String(Math.round(toNumber(flatArgs[argIdx++])));
18888
+ i++;
18889
+ } else {
18890
+ spec += fmt[i];
18891
+ i++;
18892
+ }
18893
+ }
18894
+ if (outOfArgs) break;
18895
+ if (i < fmt.length) {
18896
+ const ch = fmt[i];
18897
+ i++;
18898
+ if (ch === "%") {
18899
+ result += "%";
18900
+ } else if (argIdx >= flatArgs.length) {
18901
+ outOfArgs = true;
18902
+ } else if (ch === "d" || ch === "i" || ch === "u") {
18903
+ const raw = toNumber(flatArgs[argIdx++]);
18904
+ const isInt = Number.isInteger(raw);
18905
+ const canPrintAsInt = ch === "u" ? isInt && raw >= 0 : isInt;
18906
+ if (!canPrintAsInt) {
18907
+ let eStr = raw.toExponential(6);
18908
+ eStr = eStr.replace(/e([+-])(\d)$/, "e$10$2");
18909
+ result += applyWidth(spec, eStr);
18910
+ } else {
18911
+ const n = raw;
18912
+ const flags = spec.slice(1);
18913
+ const hasPlus = flags.includes("+");
18914
+ const leftAlign = flags.includes("-");
18915
+ const widthMatch = spec.match(/^%[^0-9]*(\d+)/);
18916
+ const width = widthMatch ? parseInt(widthMatch[1]) : 0;
18917
+ const zeroPad = !leftAlign && /^[-+ ]*0/.test(spec.slice(1));
18918
+ const s = String(Math.abs(n));
18919
+ const sign = n < 0 ? "-" : hasPlus ? "+" : "";
18920
+ if (width > 0) {
18921
+ const padChar = zeroPad ? "0" : " ";
18922
+ const padLen = Math.max(0, width - sign.length - s.length);
18923
+ const pad = padChar.repeat(padLen);
18924
+ result += leftAlign ? sign + s + " ".repeat(padLen) : zeroPad ? sign + pad + s : pad + sign + s;
18925
+ } else {
18926
+ result += sign + s;
18927
+ }
18928
+ }
18929
+ } else if (ch === "f") {
18930
+ const n = toNumber(flatArgs[argIdx++]);
18931
+ if (!isFinite(n) || isNaN(n)) {
18932
+ result += applyWidth(spec, numStr(n));
18933
+ } else {
18934
+ const fFlags = spec.slice(1);
18935
+ const fHasPlus = fFlags.includes("+");
18936
+ const precMatch = spec.match(/\.(\d+)/);
18937
+ const prec = precMatch ? parseInt(precMatch[1]) : 6;
18938
+ const formatted = n.toFixed(prec);
18939
+ const fSign = n < 0 ? "" : fHasPlus ? "+" : "";
18940
+ result += applyWidth(spec, fSign + formatted);
18941
+ }
18942
+ } else if (ch === "e" || ch === "E") {
18943
+ const n = toNumber(flatArgs[argIdx++]);
18944
+ if (!isFinite(n) || isNaN(n)) {
18945
+ result += applyWidth(spec, numStr(n));
18946
+ } else {
18947
+ const precMatch = spec.match(/\.(\d+)/);
18948
+ const prec = precMatch ? parseInt(precMatch[1]) : 6;
18949
+ let eStr = n.toExponential(prec);
18950
+ eStr = eStr.replace(/e([+-])(\d)$/, "e$10$2");
18951
+ if (ch === "E") eStr = eStr.toUpperCase();
18952
+ result += applyWidth(spec, eStr);
18953
+ }
18954
+ } else if (ch === "x" || ch === "X") {
18955
+ const n = Math.round(toNumber(flatArgs[argIdx++]));
18956
+ let s = Math.abs(n).toString(16);
18957
+ if (ch === "X") s = s.toUpperCase();
18958
+ result += applyWidth(spec, s);
18959
+ } else if (ch === "o") {
18960
+ const n = Math.round(toNumber(flatArgs[argIdx++]));
18961
+ result += applyWidth(spec, Math.abs(n).toString(8));
18962
+ } else if (ch === "g" || ch === "G") {
18963
+ const gVal = toNumber(flatArgs[argIdx++]);
18964
+ if (!isFinite(gVal) || isNaN(gVal)) {
18965
+ result += applyWidth(spec, numStr(gVal));
18966
+ } else {
18967
+ const precMatch = spec.match(/\.(\d+)/);
18968
+ const gPrec = precMatch ? parseInt(precMatch[1]) : 6;
18969
+ let gStr;
18970
+ if (gVal === 0) {
18971
+ gStr = "0";
18972
+ } else {
18973
+ const exp = Math.floor(Math.log10(Math.abs(gVal)));
18974
+ if (exp < -4 || exp >= gPrec) {
18975
+ gStr = gVal.toExponential(gPrec - 1);
18976
+ const ePos = gStr.indexOf("e");
18977
+ let mantissa = gStr.slice(0, ePos);
18978
+ let expPart = gStr.slice(ePos);
18979
+ if (mantissa.includes(".")) {
18980
+ mantissa = mantissa.replace(/\.?0+$/, "");
18981
+ }
18982
+ expPart = expPart.replace(/e([+-])(\d)$/, "e$10$2");
18983
+ gStr = mantissa + expPart;
18984
+ } else {
18985
+ gStr = gVal.toPrecision(gPrec);
18986
+ if (gStr.includes(".")) {
18987
+ gStr = gStr.replace(/\.?0+$/, "");
18988
+ }
18989
+ if (gStr.includes("e")) {
18990
+ gStr = String(parseFloat(gStr));
18991
+ }
18992
+ }
18993
+ }
18994
+ if (ch === "G") gStr = gStr.toUpperCase();
18995
+ result += applyWidth(spec, gStr);
18996
+ }
18997
+ } else if (ch === "s") {
18998
+ const sVal = toString(flatArgs[argIdx++]);
18999
+ const sFlags = spec.slice(1);
19000
+ const sLeftAlign = sFlags.includes("-");
19001
+ const sWidthMatch = spec.match(/^%[^0-9]*(\d+)/);
19002
+ const sWidth = sWidthMatch ? parseInt(sWidthMatch[1]) : 0;
19003
+ if (sWidth > sVal.length) {
19004
+ const sPad = " ".repeat(sWidth - sVal.length);
19005
+ result += sLeftAlign ? sVal + sPad : sPad + sVal;
19006
+ } else {
19007
+ result += sVal;
19008
+ }
19009
+ } else if (ch === "c") {
19010
+ result += String.fromCharCode(
19011
+ Math.round(toNumber(flatArgs[argIdx++]))
19012
+ );
19013
+ } else {
19014
+ result += spec + ch;
19015
+ argIdx++;
19016
+ }
19017
+ }
19018
+ } else if (fmt[i] === "\\" && i + 1 < fmt.length) {
19019
+ i++;
19020
+ switch (fmt[i]) {
19021
+ case "n":
19022
+ result += "\n";
19023
+ break;
19024
+ case "t":
19025
+ result += " ";
19026
+ break;
19027
+ case "\\":
19028
+ result += "\\";
19029
+ break;
19030
+ default:
19031
+ result += "\\" + fmt[i];
19032
+ }
19033
+ i++;
19034
+ } else {
19035
+ result += fmt[i];
19036
+ i++;
19037
+ }
19038
+ }
19039
+ if (argIdx === startArgIdx) break;
19040
+ } while (argIdx < flatArgs.length);
19041
+ return result;
19042
+ }
19043
+
18792
19044
  // src/numbl-core/helpers/arithmetic.ts
18793
19045
  function toComplex(v) {
18794
19046
  if (isRuntimeComplexNumber(v)) return { re: v.re, im: v.im };
@@ -19016,7 +19268,26 @@ function tensorElemwiseComplex(at, bt, opCode, jsOp) {
19016
19268
  const isReal = resultIm.every((x) => x === 0);
19017
19269
  return RTV.tensor(resultRe, at.shape, isReal ? void 0 : resultIm);
19018
19270
  }
19271
+ function coerceToConcatString(v) {
19272
+ if (isRuntimeString(v)) return v;
19273
+ if (isRuntimeChar(v)) return v.value;
19274
+ if (isRuntimeLogical(v)) return v ? "true" : "false";
19275
+ if (isRuntimeNumber(v)) return num2strScalar(v);
19276
+ if (isRuntimeTensor(v) && v.data.length === 1 && !v.imag) {
19277
+ const x = v.data[0];
19278
+ if (v._isLogical === true) return x ? "true" : "false";
19279
+ return num2strScalar(x);
19280
+ }
19281
+ return null;
19282
+ }
19019
19283
  function mAdd(a, b) {
19284
+ if (isRuntimeString(a) || isRuntimeString(b)) {
19285
+ const aStr = coerceToConcatString(a);
19286
+ const bStr = coerceToConcatString(b);
19287
+ if (aStr !== null && bStr !== null) {
19288
+ return RTV.string(aStr + bStr);
19289
+ }
19290
+ }
19020
19291
  const m = matchSameShapeTensors(a, b);
19021
19292
  if (m) {
19022
19293
  const [at, bt] = m;
@@ -19911,6 +20182,7 @@ function extractTensorElement(base, i) {
19911
20182
  const im2 = base.imag[i];
19912
20183
  return im2 === 0 ? RTV.num(base.data[i]) : RTV.complex(base.data[i], im2);
19913
20184
  }
20185
+ if (base._isLogical === true) return base.data[i] !== 0;
19914
20186
  return RTV.num(base.data[i]);
19915
20187
  }
19916
20188
  function resolveIndex(idx, dimSize, boundsLimit = dimSize) {
@@ -19964,17 +20236,35 @@ function growTensor2D(base, newRows, newCols) {
19964
20236
  }
19965
20237
  function assignSlice(base, rowIndices, colIndices, rhs, curRows) {
19966
20238
  if (isRuntimeTensor(rhs)) {
20239
+ const nR = rowIndices.length;
20240
+ const nC = colIndices.length;
19967
20241
  const [rhsRows, rhsCols] = tensorSize2D(rhs);
19968
- if (rhsRows !== rowIndices.length || rhsCols !== colIndices.length) {
20242
+ const shapeMatches = rhsRows === nR && rhsCols === nC;
20243
+ const sliceIsVector = nR === 1 || nC === 1;
20244
+ const rhsIsVector = rhsRows === 1 || rhsCols === 1;
20245
+ const countMatches = rhs.data.length === nR * nC;
20246
+ if (!shapeMatches && !(sliceIsVector && rhsIsVector && countMatches)) {
19969
20247
  throw new RuntimeError("Subscripted assignment dimension mismatch");
19970
20248
  }
19971
20249
  if (rhs.imag || base.imag) ensureImag(base);
19972
- for (let ri = 0; ri < rowIndices.length; ri++) {
19973
- for (let ci = 0; ci < colIndices.length; ci++) {
19974
- const dstLi = colMajorIndex(rowIndices[ri], colIndices[ci], curRows);
19975
- const srcLi = colMajorIndex(ri, ci, rhsRows);
19976
- base.data[dstLi] = rhs.data[srcLi];
19977
- if (base.imag) base.imag[dstLi] = rhs.imag ? rhs.imag[srcLi] : 0;
20250
+ if (shapeMatches) {
20251
+ for (let ri = 0; ri < nR; ri++) {
20252
+ for (let ci = 0; ci < nC; ci++) {
20253
+ const dstLi = colMajorIndex(rowIndices[ri], colIndices[ci], curRows);
20254
+ const srcLi = colMajorIndex(ri, ci, rhsRows);
20255
+ base.data[dstLi] = rhs.data[srcLi];
20256
+ if (base.imag) base.imag[dstLi] = rhs.imag ? rhs.imag[srcLi] : 0;
20257
+ }
20258
+ }
20259
+ } else {
20260
+ let k = 0;
20261
+ for (let ci = 0; ci < nC; ci++) {
20262
+ for (let ri = 0; ri < nR; ri++) {
20263
+ const dstLi = colMajorIndex(rowIndices[ri], colIndices[ci], curRows);
20264
+ base.data[dstLi] = rhs.data[k];
20265
+ if (base.imag) base.imag[dstLi] = rhs.imag ? rhs.imag[k] : 0;
20266
+ k++;
20267
+ }
19978
20268
  }
19979
20269
  }
19980
20270
  } else {
@@ -20311,6 +20601,23 @@ function indexIntoScalar(base, indices) {
20311
20601
  return RTV.tensor(out, [1, count]);
20312
20602
  }
20313
20603
  }
20604
+ if (indices.length === 1 && isRuntimeTensor(indices[0])) {
20605
+ const idx = indices[0];
20606
+ for (let i = 0; i < idx.data.length; i++) {
20607
+ const k = Math.round(idx.data[i]);
20608
+ if (k !== 1) throw new RuntimeError("Index exceeds array bounds");
20609
+ }
20610
+ const n = idx.data.length;
20611
+ const scalarRe = isRuntimeNumber(base) ? base : base.re;
20612
+ const data = new FloatXArray(n);
20613
+ data.fill(scalarRe);
20614
+ if (isRuntimeComplexNumber(base) && base.im !== 0) {
20615
+ const im2 = new FloatXArray(n);
20616
+ im2.fill(base.im);
20617
+ return RTV.tensor(data, [...idx.shape], im2);
20618
+ }
20619
+ return RTV.tensor(data, [...idx.shape]);
20620
+ }
20314
20621
  for (const idx of indices) {
20315
20622
  if (isColonIndex(idx)) continue;
20316
20623
  const i = toNumber(idx);
@@ -20322,6 +20629,22 @@ function indexIntoTensor(base, indices) {
20322
20629
  if (indices.length === 1) {
20323
20630
  return indexIntoTensor1D(base, indices[0]);
20324
20631
  }
20632
+ if (base.shape.length > indices.length) {
20633
+ const collapsedShape = [];
20634
+ for (let d = 0; d < indices.length - 1; d++) {
20635
+ collapsedShape.push(base.shape[d]);
20636
+ }
20637
+ let tail = 1;
20638
+ for (let d = indices.length - 1; d < base.shape.length; d++) {
20639
+ tail *= base.shape[d];
20640
+ }
20641
+ collapsedShape.push(tail);
20642
+ const view = { ...base, shape: collapsedShape };
20643
+ if (indices.length === 2) {
20644
+ return indexIntoTensor2D(view, indices[0], indices[1]);
20645
+ }
20646
+ return indexIntoTensorND(view, indices);
20647
+ }
20325
20648
  if (indices.length === 2) {
20326
20649
  return indexIntoTensor2D(base, indices[0], indices[1]);
20327
20650
  }
@@ -20350,6 +20673,11 @@ function indexIntoTensor1D(base, idx) {
20350
20673
  }
20351
20674
  function indexIntoTensor2D(base, rowIdx, colIdx) {
20352
20675
  const [rows, cols] = tensorSize2D(base);
20676
+ const baseLogical = base._isLogical === true;
20677
+ const markLogical = (t) => {
20678
+ if (baseLogical && isRuntimeTensor(t)) t._isLogical = true;
20679
+ return t;
20680
+ };
20353
20681
  if (isRuntimeNumber(rowIdx) && isColonIndex(colIdx)) {
20354
20682
  const r = Math.round(rowIdx) - 1;
20355
20683
  if (r < 0 || r >= rows)
@@ -20360,7 +20688,7 @@ function indexIntoTensor2D(base, rowIdx, colIdx) {
20360
20688
  resultData2[ci] = base.data[r + ci * rows];
20361
20689
  if (resultImag2 && base.imag) resultImag2[ci] = base.imag[r + ci * rows];
20362
20690
  }
20363
- return RTV.tensor(resultData2, [1, cols], resultImag2);
20691
+ return markLogical(RTV.tensor(resultData2, [1, cols], resultImag2));
20364
20692
  }
20365
20693
  if (isColonIndex(rowIdx) && isRuntimeNumber(colIdx)) {
20366
20694
  const c = Math.round(colIdx) - 1;
@@ -20372,7 +20700,7 @@ function indexIntoTensor2D(base, rowIdx, colIdx) {
20372
20700
  const resultImag2 = base.imag ? new FloatXArray(rows) : void 0;
20373
20701
  if (resultImag2 && base.imag)
20374
20702
  for (let ri = 0; ri < rows; ri++) resultImag2[ri] = base.imag[offset + ri];
20375
- return RTV.tensor(resultData2, [rows, 1], resultImag2);
20703
+ return markLogical(RTV.tensor(resultData2, [rows, 1], resultImag2));
20376
20704
  }
20377
20705
  const rowIdxArr = resolveIndex(rowIdx, rows);
20378
20706
  const colIdxArr = resolveIndex(colIdx, cols);
@@ -20394,7 +20722,7 @@ function indexIntoTensor2D(base, rowIdx, colIdx) {
20394
20722
  }
20395
20723
  }
20396
20724
  }
20397
- return RTV.tensor(resultData, [numR, numC], resultImag);
20725
+ return markLogical(RTV.tensor(resultData, [numR, numC], resultImag));
20398
20726
  }
20399
20727
  function indexIntoTensorND(base, indices) {
20400
20728
  const shape = base.shape;
@@ -20442,7 +20770,13 @@ function indexIntoTensorND(base, indices) {
20442
20770
  subs[d] = 0;
20443
20771
  }
20444
20772
  }
20445
- return RTV.tensor(resultData, resultShape, resultImag);
20773
+ const result = RTV.tensor(
20774
+ resultData,
20775
+ resultShape,
20776
+ resultImag
20777
+ );
20778
+ if (base._isLogical === true) result._isLogical = true;
20779
+ return result;
20446
20780
  }
20447
20781
  function indexIntoCell(base, indices) {
20448
20782
  if (indices.length === 1) {
@@ -20607,6 +20941,7 @@ function indexIntoLogical(base, indices) {
20607
20941
  return base;
20608
20942
  }
20609
20943
  function indexIntoTensorWithTensor(base, idx) {
20944
+ const baseLogical = base._isLogical === true;
20610
20945
  if (idx._isLogical) {
20611
20946
  const selected = [];
20612
20947
  const selectedIm = [];
@@ -20620,12 +20955,19 @@ function indexIntoTensorWithTensor(base, idx) {
20620
20955
  if (selected.length === 1) {
20621
20956
  if (hasImag2 && selectedIm[0] !== 0)
20622
20957
  return RTV.complex(selected[0], selectedIm[0]);
20958
+ if (baseLogical) return selected[0] !== 0;
20623
20959
  return RTV.num(selected[0]);
20624
20960
  }
20625
20961
  const imOut2 = hasImag2 && selectedIm.some((x) => x !== 0) ? new FloatXArray(selectedIm) : void 0;
20626
20962
  const isRow = base.shape.length === 2 && base.shape[0] === 1;
20627
20963
  const outShape2 = isRow ? [1, selected.length] : [selected.length, 1];
20628
- return RTV.tensor(new FloatXArray(selected), outShape2, imOut2);
20964
+ const result2 = RTV.tensor(
20965
+ new FloatXArray(selected),
20966
+ outShape2,
20967
+ imOut2
20968
+ );
20969
+ if (baseLogical) result2._isLogical = true;
20970
+ return result2;
20629
20971
  }
20630
20972
  const resultData = [];
20631
20973
  const hasImag = base.imag !== void 0;
@@ -20641,7 +20983,13 @@ function indexIntoTensorWithTensor(base, idx) {
20641
20983
  const baseIsVector = base.shape.length <= 2 && (base.shape[0] === 1 || base.shape[1] === 1 || base.shape.length === 1);
20642
20984
  const outShape = idxIs0x0 ? [0, 0] : baseIsVector ? base.shape[0] === 1 ? [1, resultData.length] : [resultData.length, 1] : idx.shape;
20643
20985
  const imOut = hasImag && imIndices.some((x) => x !== 0) ? new FloatXArray(imIndices) : void 0;
20644
- return RTV.tensor(new FloatXArray(resultData), outShape, imOut);
20986
+ const result = RTV.tensor(
20987
+ new FloatXArray(resultData),
20988
+ outShape,
20989
+ imOut
20990
+ );
20991
+ if (baseLogical) result._isLogical = true;
20992
+ return result;
20645
20993
  }
20646
20994
  function indexIntoRTValue(base, indices) {
20647
20995
  if (isRuntimeNumber(base) || isRuntimeComplexNumber(base)) {
@@ -20991,10 +21339,18 @@ function storeIntoTensor2D(base, indices, rhs) {
20991
21339
  return storeIntoTensorColonCol(base, indices[0], rhs, rows, cols);
20992
21340
  }
20993
21341
  if (rowIsTensor || colIsTensor) {
20994
- const rowIndices = resolveIndex(indices[0], rows, 0);
20995
- const colIndices = resolveIndex(indices[1], cols, 0);
20996
- const maxRow = rowIndices.length > 0 ? Math.max(...rowIndices) + 1 : rows;
20997
- const maxCol = colIndices.length > 0 ? Math.max(...colIndices) + 1 : cols;
21342
+ const isEmpty = base.data.length === 0;
21343
+ let effectiveCols = cols;
21344
+ let effectiveRows = rows;
21345
+ if (isEmpty && isRuntimeTensor(rhs)) {
21346
+ const [rhsRowsG, rhsColsG] = tensorSize2D(rhs);
21347
+ if (rowIsColon && !colIsColon) effectiveRows = rhsRowsG;
21348
+ if (colIsColon && !rowIsColon) effectiveCols = rhsColsG;
21349
+ }
21350
+ const rowIndices = rowIsColon ? Array.from({ length: effectiveRows }, (_, i) => i) : resolveIndex(indices[0], rows, 0);
21351
+ const colIndices = colIsColon ? Array.from({ length: effectiveCols }, (_, i) => i) : resolveIndex(indices[1], cols, 0);
21352
+ const maxRow = rowIndices.length > 0 ? Math.max(...rowIndices) + 1 : effectiveRows;
21353
+ const maxCol = colIndices.length > 0 ? Math.max(...colIndices) + 1 : effectiveCols;
20998
21354
  base = growTensor2D(base, maxRow, maxCol);
20999
21355
  const [curRows] = tensorSize2D(base);
21000
21356
  assignSlice(base, rowIndices, colIndices, rhs, curRows);
@@ -21422,6 +21778,9 @@ function horzcat(...values) {
21422
21778
  if (values.some((v) => isRuntimeCell(v))) {
21423
21779
  return cellCatAlongDim(values, 1);
21424
21780
  }
21781
+ if (values.some((v) => isRuntimeStruct(v) || isRuntimeStructArray(v))) {
21782
+ return structCat(values);
21783
+ }
21425
21784
  return catAlongDim(values, 1);
21426
21785
  }
21427
21786
  function vertcat(...values) {
@@ -21433,8 +21792,47 @@ function vertcat(...values) {
21433
21792
  if (values.some((v) => isRuntimeCell(v))) {
21434
21793
  return cellCatAlongDim(values, 0);
21435
21794
  }
21795
+ if (values.some((v) => isRuntimeStruct(v) || isRuntimeStructArray(v))) {
21796
+ return structCat(values);
21797
+ }
21436
21798
  return catAlongDim(values, 0);
21437
21799
  }
21800
+ function structCat(values) {
21801
+ const elements = [];
21802
+ let fieldNames = null;
21803
+ for (const v of values) {
21804
+ if (isRuntimeStruct(v)) {
21805
+ const keys = Array.from(v.fields.keys());
21806
+ if (fieldNames === null) fieldNames = keys;
21807
+ else if (!arraysEqual(fieldNames, keys)) {
21808
+ throw new RuntimeError(
21809
+ "Cannot concatenate structs with different field names"
21810
+ );
21811
+ }
21812
+ elements.push(v);
21813
+ } else if (isRuntimeStructArray(v)) {
21814
+ if (fieldNames === null) fieldNames = [...v.fieldNames];
21815
+ else if (!arraysEqual(fieldNames, v.fieldNames)) {
21816
+ throw new RuntimeError(
21817
+ "Cannot concatenate struct arrays with different field names"
21818
+ );
21819
+ }
21820
+ for (const e of v.elements) elements.push(e);
21821
+ } else {
21822
+ if (isRuntimeTensor(v) && v.data.length === 0 && v.shape.every((d) => d === 0)) {
21823
+ continue;
21824
+ }
21825
+ throw new RuntimeError(`Cannot concatenate ${kstr(v)} into struct`);
21826
+ }
21827
+ }
21828
+ if (fieldNames === null) fieldNames = [];
21829
+ return RTV.structArray(fieldNames, elements);
21830
+ }
21831
+ function arraysEqual(a, b) {
21832
+ if (a.length !== b.length) return false;
21833
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
21834
+ return true;
21835
+ }
21438
21836
  function toSparseForCat(v) {
21439
21837
  if (isRuntimeSparseMatrix(v)) return v;
21440
21838
  if (isRuntimeNumber(v)) {
@@ -22042,7 +22440,7 @@ var token_config_default = {
22042
22440
  stripUnderscores: true,
22043
22441
  integerToken: "Integer",
22044
22442
  floatToken: "Float",
22045
- exponentChars: ["e", "E"],
22443
+ exponentChars: ["e", "E", "d", "D"],
22046
22444
  decimalPoint: ".",
22047
22445
  dotOperatorPrefixes: ["*", "/", "\\", "^"]
22048
22446
  },
@@ -22347,6 +22745,9 @@ function tokenizeDetailed(input) {
22347
22745
  if (numCfg.stripUnderscores) {
22348
22746
  lexeme = lexeme.replace(/_/g, "");
22349
22747
  }
22748
+ if (isFloat) {
22749
+ lexeme = lexeme.replace(/[dD]/g, "e");
22750
+ }
22350
22751
  const tok = isFloat ? Token[numCfg.floatToken] : Token[numCfg.integerToken];
22351
22752
  lineStart = false;
22352
22753
  out.push({ token: tok, lexeme, start, end: pos });
@@ -27507,224 +27908,88 @@ defineBuiltin({
27507
27908
  }
27508
27909
  });
27509
27910
 
27510
- // src/numbl-core/helpers/string.ts
27511
- function numStr(n) {
27512
- if (n === Infinity) return "Inf";
27513
- if (n === -Infinity) return "-Inf";
27514
- if (isNaN(n)) return "NaN";
27515
- if (n === 0) return "0";
27516
- const prec = 5;
27517
- const exp = Math.floor(Math.log10(Math.abs(n)));
27518
- let s;
27519
- if (exp < -4 || exp >= prec) {
27520
- s = n.toExponential(prec - 1);
27521
- const ePos = s.indexOf("e");
27522
- let mantissa = s.slice(0, ePos);
27523
- const expPart0 = s.slice(ePos);
27524
- if (mantissa.includes(".")) mantissa = mantissa.replace(/\.?0+$/, "");
27525
- const expPart = expPart0.replace(/([eE][+-])(\d)$/, "$10$2");
27526
- s = mantissa + expPart;
27527
- } else {
27528
- if (Number.isInteger(n)) return String(n);
27529
- s = n.toPrecision(prec);
27530
- if (s.includes(".")) s = s.replace(/\.?0+$/, "");
27911
+ // src/numbl-core/interpreter/builtins/logical.ts
27912
+ var LOGICAL_KINDS = /* @__PURE__ */ new Set([
27913
+ "number",
27914
+ "boolean",
27915
+ "tensor",
27916
+ "sparse_matrix"
27917
+ ]);
27918
+ function binaryLogicalMatch(argTypes) {
27919
+ if (argTypes.length !== 2) return null;
27920
+ if (!LOGICAL_KINDS.has(argTypes[0].kind)) return null;
27921
+ if (!LOGICAL_KINDS.has(argTypes[1].kind)) return null;
27922
+ if (argTypes[0].kind === "tensor" || argTypes[1].kind === "tensor") {
27923
+ return [{ kind: "tensor", isComplex: false, isLogical: true }];
27531
27924
  }
27532
- return s;
27533
- }
27534
- function applyWidth(spec, str) {
27535
- const m = spec.match(/^%([-+ #]*)0?(\d+)?/);
27536
- if (!m) return str;
27537
- const explicitFlags = m[1] || "";
27538
- const leftAlign = explicitFlags.includes("-");
27539
- const afterPercent = spec.slice(1);
27540
- const flagAndWidth = afterPercent.match(/^([-+ #]*)(0?)(\d+)?/);
27541
- const zeroFlag = flagAndWidth ? flagAndWidth[2] === "0" : false;
27542
- const width = flagAndWidth && flagAndWidth[3] ? parseInt(flagAndWidth[3]) : 0;
27543
- if (width <= str.length) return str;
27544
- const zeroPad = !leftAlign && zeroFlag;
27545
- const padLen = width - str.length;
27546
- if (leftAlign) return str + " ".repeat(padLen);
27547
- if (zeroPad) {
27548
- if (str[0] === "-" || str[0] === "+") {
27549
- return str[0] + "0".repeat(padLen) + str.slice(1);
27550
- }
27551
- return "0".repeat(padLen) + str;
27925
+ if (argTypes[0].kind === "sparse_matrix" || argTypes[1].kind === "sparse_matrix") {
27926
+ return [{ kind: "tensor", isComplex: false, isLogical: true }];
27552
27927
  }
27553
- return " ".repeat(padLen) + str;
27928
+ return [{ kind: "boolean" }];
27554
27929
  }
27555
- function sprintfFormat(fmt, args) {
27556
- const flatArgs = [];
27557
- for (const arg of args) {
27558
- if (isRuntimeTensor(arg)) {
27559
- for (let k = 0; k < arg.data.length; k++) {
27560
- flatArgs.push(arg.data[k]);
27561
- }
27562
- } else {
27563
- flatArgs.push(arg);
27564
- }
27930
+ function isTensorLike(v) {
27931
+ return isRuntimeTensor(v) || isRuntimeSparseMatrix(v);
27932
+ }
27933
+ function applyBinaryLogical(args, op, scalarFn) {
27934
+ const a = args[0];
27935
+ const b = args[1];
27936
+ if (isTensorLike(a) || isTensorLike(b)) {
27937
+ return elementWiseLogicalOp(a, b, op);
27565
27938
  }
27566
- let result = "";
27567
- let argIdx = 0;
27568
- do {
27569
- const startArgIdx = argIdx;
27570
- let outOfArgs = false;
27571
- let i = 0;
27572
- while (i < fmt.length && !outOfArgs) {
27573
- if (fmt[i] === "%" && i + 1 < fmt.length) {
27574
- i++;
27575
- let spec = "%";
27576
- while (i < fmt.length && !"dfigeEsoxXuc%".includes(fmt[i])) {
27577
- spec += fmt[i];
27578
- i++;
27579
- }
27580
- if (i < fmt.length) {
27581
- const ch = fmt[i];
27582
- i++;
27583
- if (ch === "%") {
27584
- result += "%";
27585
- } else if (argIdx >= flatArgs.length) {
27586
- outOfArgs = true;
27587
- } else if (ch === "d" || ch === "i" || ch === "u") {
27588
- const raw = toNumber(flatArgs[argIdx++]);
27589
- const isInt = Number.isInteger(raw);
27590
- const canPrintAsInt = ch === "u" ? isInt && raw >= 0 : isInt;
27591
- if (!canPrintAsInt) {
27592
- let eStr = raw.toExponential(6);
27593
- eStr = eStr.replace(/e([+-])(\d)$/, "e$10$2");
27594
- result += applyWidth(spec, eStr);
27595
- } else {
27596
- const n = raw;
27597
- const flags = spec.slice(1);
27598
- const hasPlus = flags.includes("+");
27599
- const leftAlign = flags.includes("-");
27600
- const widthMatch = spec.match(/^%[^0-9]*(\d+)/);
27601
- const width = widthMatch ? parseInt(widthMatch[1]) : 0;
27602
- const zeroPad = !leftAlign && /^[-+ ]*0/.test(spec.slice(1));
27603
- const s = String(Math.abs(n));
27604
- const sign = n < 0 ? "-" : hasPlus ? "+" : "";
27605
- if (width > 0) {
27606
- const padChar = zeroPad ? "0" : " ";
27607
- const padLen = Math.max(0, width - sign.length - s.length);
27608
- const pad = padChar.repeat(padLen);
27609
- result += leftAlign ? sign + s + " ".repeat(padLen) : zeroPad ? sign + pad + s : pad + sign + s;
27610
- } else {
27611
- result += sign + s;
27612
- }
27613
- }
27614
- } else if (ch === "f") {
27615
- const n = toNumber(flatArgs[argIdx++]);
27616
- if (!isFinite(n) || isNaN(n)) {
27617
- result += applyWidth(spec, numStr(n));
27618
- } else {
27619
- const fFlags = spec.slice(1);
27620
- const fHasPlus = fFlags.includes("+");
27621
- const precMatch = spec.match(/\.(\d+)/);
27622
- const prec = precMatch ? parseInt(precMatch[1]) : 6;
27623
- const formatted = n.toFixed(prec);
27624
- const fSign = n < 0 ? "" : fHasPlus ? "+" : "";
27625
- result += applyWidth(spec, fSign + formatted);
27626
- }
27627
- } else if (ch === "e" || ch === "E") {
27628
- const n = toNumber(flatArgs[argIdx++]);
27629
- if (!isFinite(n) || isNaN(n)) {
27630
- result += applyWidth(spec, numStr(n));
27631
- } else {
27632
- const precMatch = spec.match(/\.(\d+)/);
27633
- const prec = precMatch ? parseInt(precMatch[1]) : 6;
27634
- let eStr = n.toExponential(prec);
27635
- eStr = eStr.replace(/e([+-])(\d)$/, "e$10$2");
27636
- if (ch === "E") eStr = eStr.toUpperCase();
27637
- result += applyWidth(spec, eStr);
27638
- }
27639
- } else if (ch === "x" || ch === "X") {
27640
- const n = Math.round(toNumber(flatArgs[argIdx++]));
27641
- let s = Math.abs(n).toString(16);
27642
- if (ch === "X") s = s.toUpperCase();
27643
- result += applyWidth(spec, s);
27644
- } else if (ch === "o") {
27645
- const n = Math.round(toNumber(flatArgs[argIdx++]));
27646
- result += applyWidth(spec, Math.abs(n).toString(8));
27647
- } else if (ch === "g" || ch === "G") {
27648
- const gVal = toNumber(flatArgs[argIdx++]);
27649
- if (!isFinite(gVal) || isNaN(gVal)) {
27650
- result += applyWidth(spec, numStr(gVal));
27651
- } else {
27652
- const precMatch = spec.match(/\.(\d+)/);
27653
- const gPrec = precMatch ? parseInt(precMatch[1]) : 6;
27654
- let gStr;
27655
- if (gVal === 0) {
27656
- gStr = "0";
27657
- } else {
27658
- const exp = Math.floor(Math.log10(Math.abs(gVal)));
27659
- if (exp < -4 || exp >= gPrec) {
27660
- gStr = gVal.toExponential(gPrec - 1);
27661
- const ePos = gStr.indexOf("e");
27662
- let mantissa = gStr.slice(0, ePos);
27663
- let expPart = gStr.slice(ePos);
27664
- if (mantissa.includes(".")) {
27665
- mantissa = mantissa.replace(/\.?0+$/, "");
27666
- }
27667
- expPart = expPart.replace(/e([+-])(\d)$/, "e$10$2");
27668
- gStr = mantissa + expPart;
27669
- } else {
27670
- gStr = gVal.toPrecision(gPrec);
27671
- if (gStr.includes(".")) {
27672
- gStr = gStr.replace(/\.?0+$/, "");
27673
- }
27674
- if (gStr.includes("e")) {
27675
- gStr = String(parseFloat(gStr));
27676
- }
27677
- }
27678
- }
27679
- if (ch === "G") gStr = gStr.toUpperCase();
27680
- result += applyWidth(spec, gStr);
27681
- }
27682
- } else if (ch === "s") {
27683
- const sVal = toString(flatArgs[argIdx++]);
27684
- const sFlags = spec.slice(1);
27685
- const sLeftAlign = sFlags.includes("-");
27686
- const sWidthMatch = spec.match(/^%[^0-9]*(\d+)/);
27687
- const sWidth = sWidthMatch ? parseInt(sWidthMatch[1]) : 0;
27688
- if (sWidth > sVal.length) {
27689
- const sPad = " ".repeat(sWidth - sVal.length);
27690
- result += sLeftAlign ? sVal + sPad : sPad + sVal;
27691
- } else {
27692
- result += sVal;
27693
- }
27694
- } else if (ch === "c") {
27695
- result += String.fromCharCode(
27696
- Math.round(toNumber(flatArgs[argIdx++]))
27697
- );
27698
- } else {
27699
- result += spec + ch;
27700
- argIdx++;
27701
- }
27702
- }
27703
- } else if (fmt[i] === "\\" && i + 1 < fmt.length) {
27704
- i++;
27705
- switch (fmt[i]) {
27706
- case "n":
27707
- result += "\n";
27708
- break;
27709
- case "t":
27710
- result += " ";
27711
- break;
27712
- case "\\":
27713
- result += "\\";
27714
- break;
27715
- default:
27716
- result += "\\" + fmt[i];
27939
+ return RTV.logical(scalarFn(toBool(a), toBool(b)));
27940
+ }
27941
+ var orCase = {
27942
+ match: binaryLogicalMatch,
27943
+ apply: (args) => applyBinaryLogical(
27944
+ args,
27945
+ (x, y) => x !== 0 || y !== 0 ? 1 : 0,
27946
+ (a, b) => a || b
27947
+ )
27948
+ };
27949
+ defineBuiltin({
27950
+ name: "or",
27951
+ help: {
27952
+ signatures: ["TF = or(A, B)"],
27953
+ description: "Logical OR. Functional form of A | B; returns a logical scalar or tensor."
27954
+ },
27955
+ cases: [orCase]
27956
+ });
27957
+ var andCase = {
27958
+ match: binaryLogicalMatch,
27959
+ apply: (args) => applyBinaryLogical(
27960
+ args,
27961
+ (x, y) => x !== 0 && y !== 0 ? 1 : 0,
27962
+ (a, b) => a && b
27963
+ )
27964
+ };
27965
+ defineBuiltin({
27966
+ name: "and",
27967
+ help: {
27968
+ signatures: ["TF = and(A, B)"],
27969
+ description: "Logical AND. Functional form of A & B; returns a logical scalar or tensor."
27970
+ },
27971
+ cases: [andCase]
27972
+ });
27973
+ defineBuiltin({
27974
+ name: "not",
27975
+ help: {
27976
+ signatures: ["TF = not(A)"],
27977
+ description: "Logical negation. Functional form of ~A; returns a logical scalar or tensor."
27978
+ },
27979
+ cases: [
27980
+ {
27981
+ match: (argTypes) => {
27982
+ if (argTypes.length !== 1) return null;
27983
+ if (!LOGICAL_KINDS.has(argTypes[0].kind)) return null;
27984
+ if (argTypes[0].kind === "tensor" || argTypes[0].kind === "sparse_matrix") {
27985
+ return [{ kind: "tensor", isComplex: false, isLogical: true }];
27717
27986
  }
27718
- i++;
27719
- } else {
27720
- result += fmt[i];
27721
- i++;
27722
- }
27987
+ return [{ kind: "boolean" }];
27988
+ },
27989
+ apply: (args) => not(args[0])
27723
27990
  }
27724
- if (argIdx === startArgIdx) break;
27725
- } while (argIdx < flatArgs.length);
27726
- return result;
27727
- }
27991
+ ]
27992
+ });
27728
27993
 
27729
27994
  // src/numbl-core/interpreter/builtins/utility.ts
27730
27995
  function sparseToDense2(S) {
@@ -27834,7 +28099,7 @@ defineBuiltin({
27834
28099
  {
27835
28100
  match: (argTypes) => {
27836
28101
  if (argTypes.length < 1) return null;
27837
- return [{ kind: "number" }];
28102
+ return [];
27838
28103
  },
27839
28104
  apply: (args) => {
27840
28105
  const v = args[0];
@@ -27855,7 +28120,7 @@ defineBuiltin({
27855
28120
  const msg = args.length > 1 ? textValue(args[1]) ?? String(args[1]) : "Assertion failed";
27856
28121
  throw new Error(msg);
27857
28122
  }
27858
- return 0;
28123
+ return void 0;
27859
28124
  }
27860
28125
  }
27861
28126
  ]
@@ -27866,7 +28131,7 @@ defineBuiltin({
27866
28131
  {
27867
28132
  match: (argTypes) => {
27868
28133
  if (argTypes.length === 0) return null;
27869
- return [{ kind: "number" }];
28134
+ return [];
27870
28135
  },
27871
28136
  apply: (args) => {
27872
28137
  const first = textValue(args[0]) ?? String(args[0]);
@@ -28538,6 +28803,19 @@ defineBuiltin({
28538
28803
  name: "NaN",
28539
28804
  cases: arrayConstructorCases(nanFill, NaN)
28540
28805
  });
28806
+ function infFill(shape) {
28807
+ const data = new FloatXArray(numel(shape));
28808
+ data.fill(Infinity);
28809
+ return makeTensor(data, void 0, shape);
28810
+ }
28811
+ defineBuiltin({
28812
+ name: "inf",
28813
+ cases: arrayConstructorCases(infFill, Infinity)
28814
+ });
28815
+ defineBuiltin({
28816
+ name: "Inf",
28817
+ cases: arrayConstructorCases(infFill, Infinity)
28818
+ });
28541
28819
  defineBuiltin({
28542
28820
  name: "eye",
28543
28821
  cases: arrayConstructorCases(
@@ -29375,19 +29653,33 @@ function preserveTextType(t) {
29375
29653
  if (t.kind === "string") return { kind: "string" };
29376
29654
  return null;
29377
29655
  }
29656
+ function applyTextFn(v, fn) {
29657
+ if (isRuntimeCell(v)) {
29658
+ const out = new Array(v.data.length);
29659
+ for (let i = 0; i < v.data.length; i++) {
29660
+ out[i] = applyTextFn(v.data[i], fn);
29661
+ }
29662
+ return RTV.cell(out, [...v.shape]);
29663
+ }
29664
+ if (isRuntimeChar(v)) return RTV.char(fn(v.value));
29665
+ if (isRuntimeString(v)) return RTV.string(fn(toString(v)));
29666
+ return v;
29667
+ }
29378
29668
  function textPreserveResolve(fn) {
29379
29669
  return (argTypes) => {
29380
29670
  if (argTypes.length !== 1) return null;
29381
- const out = preserveTextType(argTypes[0]);
29671
+ const t = argTypes[0];
29672
+ if (t.kind === "cell") {
29673
+ return {
29674
+ outputTypes: [{ kind: "cell" }],
29675
+ apply: (args) => applyTextFn(args[0], fn)
29676
+ };
29677
+ }
29678
+ const out = preserveTextType(t);
29382
29679
  if (!out) return null;
29383
29680
  return {
29384
29681
  outputTypes: [out],
29385
- apply: (args) => {
29386
- const v = args[0];
29387
- const s = toString(v);
29388
- const result = fn(s);
29389
- return isRuntimeChar(v) ? RTV.char(result) : RTV.string(result);
29390
- }
29682
+ apply: (args) => applyTextFn(args[0], fn)
29391
29683
  };
29392
29684
  };
29393
29685
  }
@@ -29765,6 +30057,150 @@ registerIBuiltin({
29765
30057
  };
29766
30058
  }
29767
30059
  });
30060
+ var RE_ALPHA = /\p{L}/u;
30061
+ var RE_ALPHANUM = /[\p{L}\p{N}]/u;
30062
+ var RE_CNTRL = /\p{Cc}/u;
30063
+ var RE_GRAPHIC = /[\p{L}\p{N}\p{P}\p{S}\p{M}]/u;
30064
+ var RE_LOWER = /\p{Ll}/u;
30065
+ var RE_PUNCT = /\p{P}/u;
30066
+ var RE_UPPER = /\p{Lu}/u;
30067
+ var RE_WSPACE = /\s/u;
30068
+ function isstrpropPredicate(category) {
30069
+ switch (category) {
30070
+ case "alpha":
30071
+ return (cp) => RE_ALPHA.test(String.fromCodePoint(cp));
30072
+ case "alphanum":
30073
+ return (cp) => RE_ALPHANUM.test(String.fromCodePoint(cp));
30074
+ case "cntrl":
30075
+ return (cp) => RE_CNTRL.test(String.fromCodePoint(cp));
30076
+ case "digit":
30077
+ return (cp) => cp >= 48 && cp <= 57;
30078
+ case "graphic":
30079
+ return (cp) => RE_GRAPHIC.test(String.fromCodePoint(cp));
30080
+ case "lower":
30081
+ return (cp) => RE_LOWER.test(String.fromCodePoint(cp));
30082
+ case "print":
30083
+ return (cp) => cp === 32 || RE_GRAPHIC.test(String.fromCodePoint(cp));
30084
+ case "punct":
30085
+ return (cp) => RE_PUNCT.test(String.fromCodePoint(cp));
30086
+ case "wspace":
30087
+ return (cp) => RE_WSPACE.test(String.fromCodePoint(cp));
30088
+ case "upper":
30089
+ return (cp) => RE_UPPER.test(String.fromCodePoint(cp));
30090
+ case "xdigit":
30091
+ return (cp) => cp >= 48 && cp <= 57 || cp >= 65 && cp <= 70 || cp >= 97 && cp <= 102;
30092
+ default:
30093
+ return null;
30094
+ }
30095
+ }
30096
+ function stringCodePoints(s) {
30097
+ const out = [];
30098
+ for (const ch of s) out.push(ch.codePointAt(0));
30099
+ return out;
30100
+ }
30101
+ function logicalRowFromString(s, pred, shape) {
30102
+ const cps = stringCodePoints(s);
30103
+ const data = new FloatXArray(cps.length);
30104
+ for (let i = 0; i < cps.length; i++) data[i] = pred(cps[i]) ? 1 : 0;
30105
+ return {
30106
+ kind: "tensor",
30107
+ data,
30108
+ shape: shape ?? [1, cps.length],
30109
+ _isLogical: true,
30110
+ _rc: 1
30111
+ };
30112
+ }
30113
+ function logicalFromNumericTensor(data, shape, pred) {
30114
+ const out = new FloatXArray(data.length);
30115
+ for (let i = 0; i < data.length; i++) {
30116
+ out[i] = pred(Math.round(data[i])) ? 1 : 0;
30117
+ }
30118
+ return {
30119
+ kind: "tensor",
30120
+ data: out,
30121
+ shape: [...shape],
30122
+ _isLogical: true,
30123
+ _rc: 1
30124
+ };
30125
+ }
30126
+ function isstrpropApply(args) {
30127
+ const v = args[0];
30128
+ const category = toString(args[1]);
30129
+ const pred = isstrpropPredicate(category);
30130
+ if (!pred) {
30131
+ throw new RuntimeError(`isstrprop: unknown category '${category}'`);
30132
+ }
30133
+ let forceCell = false;
30134
+ if (args.length >= 3) {
30135
+ const flagName = toString(args[2]).toLowerCase();
30136
+ if (flagName !== "forcecelloutput") {
30137
+ throw new RuntimeError(`isstrprop: unknown name '${toString(args[2])}'`);
30138
+ }
30139
+ if (args.length < 4) {
30140
+ throw new RuntimeError("isstrprop: 'ForceCellOutput' requires a value");
30141
+ }
30142
+ forceCell = toNumber(args[3]) !== 0;
30143
+ }
30144
+ if (isRuntimeCell(v)) {
30145
+ const out = new Array(v.data.length);
30146
+ for (let i = 0; i < v.data.length; i++) {
30147
+ const elem = v.data[i];
30148
+ const s = isText(elem) ? toString(elem) : "";
30149
+ out[i] = logicalRowFromString(s, pred);
30150
+ }
30151
+ return RTV.cell(out, [...v.shape]);
30152
+ }
30153
+ let result;
30154
+ if (isRuntimeString(v)) {
30155
+ result = logicalRowFromString(toString(v), pred);
30156
+ } else if (isRuntimeChar(v)) {
30157
+ const c = v;
30158
+ result = logicalRowFromString(
30159
+ c.value,
30160
+ pred,
30161
+ c.shape ? [...c.shape] : void 0
30162
+ );
30163
+ } else if (isRuntimeTensor(v)) {
30164
+ result = logicalFromNumericTensor(v.data, v.shape, pred);
30165
+ } else if (isRuntimeNumber(v) || isRuntimeLogical(v)) {
30166
+ const cp = Math.round(toNumber(v));
30167
+ const data = new FloatXArray(1);
30168
+ data[0] = pred(cp) ? 1 : 0;
30169
+ result = { kind: "tensor", data, shape: [1, 1], _isLogical: true, _rc: 1 };
30170
+ } else {
30171
+ throw new RuntimeError("isstrprop: unsupported input type");
30172
+ }
30173
+ if (forceCell) return RTV.cell([result], [1, 1]);
30174
+ return result;
30175
+ }
30176
+ registerIBuiltin({
30177
+ name: "isstrprop",
30178
+ help: {
30179
+ signatures: [
30180
+ "TF = isstrprop(str, category)",
30181
+ "TF = isstrprop(str, category, 'ForceCellOutput', tf)"
30182
+ ],
30183
+ description: "Test which characters in str belong to a category (alpha, alphanum, cntrl, digit, graphic, lower, print, punct, upper, wspace, xdigit). Returns a logical array, or a cell of logical vectors when str is a cell array or ForceCellOutput is true. Numeric input is treated as Unicode code points."
30184
+ },
30185
+ resolve: (argTypes) => {
30186
+ if (argTypes.length < 2 || argTypes.length > 4) return null;
30187
+ if (!isTextType(argTypes[1])) return null;
30188
+ if (argTypes.length >= 3 && !isTextType(argTypes[2])) return null;
30189
+ if (argTypes.length === 4) {
30190
+ const k = argTypes[3].kind;
30191
+ if (k !== "number" && k !== "boolean") return null;
30192
+ }
30193
+ const t0 = argTypes[0];
30194
+ if (t0.kind !== "char" && t0.kind !== "string" && t0.kind !== "number" && t0.kind !== "boolean" && t0.kind !== "tensor" && t0.kind !== "cell") {
30195
+ return null;
30196
+ }
30197
+ const isCell = t0.kind === "cell";
30198
+ return {
30199
+ outputTypes: isCell ? [{ kind: "cell" }] : [{ kind: "tensor", isComplex: false, isLogical: true }],
30200
+ apply: (args) => isstrpropApply(args)
30201
+ };
30202
+ }
30203
+ });
29768
30204
  registerIBuiltin({
29769
30205
  name: "contains",
29770
30206
  resolve: (argTypes) => {
@@ -33150,11 +33586,15 @@ defineBuiltin({
33150
33586
  apply: (args) => {
33151
33587
  if (args.length !== 2)
33152
33588
  throw new RuntimeError("kron requires 2 arguments");
33153
- const a = args[0], b = args[1];
33154
- const A = isRuntimeNumber(a) ? RTV.tensor(new FloatXArray([a]), [1, 1]) : a;
33155
- const B = isRuntimeNumber(b) ? RTV.tensor(new FloatXArray([b]), [1, 1]) : b;
33156
- if (!isRuntimeTensor(A) || !isRuntimeTensor(B))
33589
+ const coerce = (v) => {
33590
+ if (isRuntimeNumber(v))
33591
+ return RTV.tensor(new FloatXArray([v]), [1, 1]);
33592
+ if (isRuntimeSparseMatrix(v)) return sparseToDense(v);
33593
+ if (isRuntimeTensor(v)) return v;
33157
33594
  throw new RuntimeError("kron: arguments must be numeric");
33595
+ };
33596
+ const A = coerce(args[0]);
33597
+ const B = coerce(args[1]);
33158
33598
  const [m, n] = tensorSize2D(A);
33159
33599
  const [p2, q] = tensorSize2D(B);
33160
33600
  const rows = m * p2, cols = n * q;
@@ -35472,6 +35912,65 @@ defineBuiltin({
35472
35912
  }
35473
35913
  ]
35474
35914
  });
35915
+ defineBuiltin({
35916
+ name: "bitget",
35917
+ cases: [
35918
+ {
35919
+ match: (argTypes) => {
35920
+ if (argTypes.length !== 2) return null;
35921
+ return [{ kind: "unknown" }];
35922
+ },
35923
+ // bitget(A, bit) returns bit number `bit` (1-based, 1 = LSB) of each
35924
+ // element of A. MATLAB supports vector `bit` with broadcasting; we
35925
+ // match the scalar-or-same-shape rules used by the other bitwise ops.
35926
+ apply: (args) => bitwiseOp(
35927
+ args[0],
35928
+ args[1],
35929
+ (a, bit) => {
35930
+ const b = Math.round(bit);
35931
+ if (b < 1) return 0;
35932
+ if (b <= 31) return a >>> b - 1 & 1;
35933
+ const bi = BigInt(a);
35934
+ return Number(bi >> BigInt(b - 1) & 1n);
35935
+ },
35936
+ "bitget"
35937
+ )
35938
+ }
35939
+ ]
35940
+ });
35941
+ defineBuiltin({
35942
+ name: "bitset",
35943
+ cases: [
35944
+ {
35945
+ match: (argTypes) => {
35946
+ if (argTypes.length < 2 || argTypes.length > 3) return null;
35947
+ return [{ kind: "unknown" }];
35948
+ },
35949
+ // bitset(A, bit) → set bit to 1
35950
+ // bitset(A, bit, v) → set bit to v (0 or 1)
35951
+ apply: (args) => {
35952
+ const v = args.length >= 3 ? Math.round(toNumber(args[2])) : 1;
35953
+ return bitwiseOp(
35954
+ args[0],
35955
+ args[1],
35956
+ (a, bit) => {
35957
+ const b = Math.round(bit);
35958
+ if (b < 1) return a;
35959
+ if (b <= 31) {
35960
+ const mask2 = 1 << b - 1;
35961
+ return v ? a | mask2 : a & ~mask2;
35962
+ }
35963
+ const bi = BigInt(a);
35964
+ const mask = 1n << BigInt(b - 1);
35965
+ const out = v ? bi | mask : bi & ~mask;
35966
+ return Number(out);
35967
+ },
35968
+ "bitset"
35969
+ );
35970
+ }
35971
+ }
35972
+ ]
35973
+ });
35475
35974
  function coordTransform(name, nArgs, nOut, fn) {
35476
35975
  defineBuiltin({
35477
35976
  name,
@@ -36601,6 +37100,131 @@ defineBuiltin({
36601
37100
  }
36602
37101
  ]
36603
37102
  });
37103
+ var INT_RANGES = [
37104
+ { name: "int8", min: -128, max: 127 },
37105
+ { name: "int16", min: -32768, max: 32767 },
37106
+ { name: "int32", min: -2147483648, max: 2147483647 },
37107
+ // int64/uint64 can't represent their full native range as doubles;
37108
+ // clamp at Number.MAX_SAFE_INTEGER to avoid silent precision loss.
37109
+ {
37110
+ name: "int64",
37111
+ min: -Number.MAX_SAFE_INTEGER,
37112
+ max: Number.MAX_SAFE_INTEGER
37113
+ },
37114
+ { name: "uint8", min: 0, max: 255 },
37115
+ { name: "uint16", min: 0, max: 65535 },
37116
+ { name: "uint32", min: 0, max: 4294967295 },
37117
+ { name: "uint64", min: 0, max: Number.MAX_SAFE_INTEGER }
37118
+ ];
37119
+ function saturateRoundToward(x, min, max) {
37120
+ if (isNaN(x)) return 0;
37121
+ const r = x >= 0 ? Math.floor(x + 0.5) : -Math.floor(-x + 0.5);
37122
+ if (r < min) return min;
37123
+ if (r > max) return max;
37124
+ return r;
37125
+ }
37126
+ for (const { name, min, max } of INT_RANGES) {
37127
+ defineBuiltin({
37128
+ name,
37129
+ cases: [
37130
+ {
37131
+ match: (argTypes) => {
37132
+ if (argTypes.length !== 1) return null;
37133
+ const a = argTypes[0];
37134
+ if (a.kind === "number" || a.kind === "boolean" || a.kind === "char" || a.kind === "complex_or_number")
37135
+ return [{ kind: "number" }];
37136
+ if (a.kind === "tensor")
37137
+ return [
37138
+ {
37139
+ kind: "tensor",
37140
+ isComplex: false,
37141
+ shape: a.shape,
37142
+ ndim: a.ndim
37143
+ }
37144
+ ];
37145
+ return null;
37146
+ },
37147
+ apply: (args) => {
37148
+ const v = args[0];
37149
+ if (isRuntimeNumber(v))
37150
+ return RTV.num(saturateRoundToward(v, min, max));
37151
+ if (isRuntimeLogical(v)) return RTV.num(v ? 1 : 0);
37152
+ if (isRuntimeComplexNumber(v))
37153
+ return RTV.num(saturateRoundToward(v.re, min, max));
37154
+ if (isRuntimeChar(v)) {
37155
+ if (v.value.length === 0)
37156
+ return RTV.tensor(new FloatXArray(0), [0, 0]);
37157
+ if (v.value.length === 1)
37158
+ return RTV.num(
37159
+ saturateRoundToward(v.value.charCodeAt(0), min, max)
37160
+ );
37161
+ const out = new FloatXArray(v.value.length);
37162
+ for (let i = 0; i < v.value.length; i++) {
37163
+ out[i] = saturateRoundToward(v.value.charCodeAt(i), min, max);
37164
+ }
37165
+ return RTV.row(Array.from(out));
37166
+ }
37167
+ if (isRuntimeTensor(v)) {
37168
+ const data = new FloatXArray(v.data.length);
37169
+ for (let i = 0; i < v.data.length; i++) {
37170
+ data[i] = saturateRoundToward(v.data[i], min, max);
37171
+ }
37172
+ return RTV.tensor(data, [...v.shape]);
37173
+ }
37174
+ return RTV.num(saturateRoundToward(toNumber(v), min, max));
37175
+ }
37176
+ }
37177
+ ]
37178
+ });
37179
+ }
37180
+ defineBuiltin({
37181
+ name: "idivide",
37182
+ cases: [
37183
+ {
37184
+ match: (argTypes) => {
37185
+ if (argTypes.length < 2 || argTypes.length > 3) return null;
37186
+ return [{ kind: "unknown" }];
37187
+ },
37188
+ apply: (args) => {
37189
+ const divFix = (a2, b2) => {
37190
+ if (b2 === 0) {
37191
+ return 0;
37192
+ }
37193
+ const q = a2 / b2;
37194
+ return q >= 0 ? Math.floor(q) : -Math.floor(-q);
37195
+ };
37196
+ const a = args[0];
37197
+ const b = args[1];
37198
+ if (isRuntimeNumber(a) && isRuntimeNumber(b)) {
37199
+ return RTV.num(divFix(a, b));
37200
+ }
37201
+ if (isRuntimeTensor(a) && isRuntimeNumber(b)) {
37202
+ const bv = b;
37203
+ const data = new FloatXArray(a.data.length);
37204
+ for (let i = 0; i < a.data.length; i++)
37205
+ data[i] = divFix(a.data[i], bv);
37206
+ return RTV.tensor(data, [...a.shape]);
37207
+ }
37208
+ if (isRuntimeNumber(a) && isRuntimeTensor(b)) {
37209
+ const av = a;
37210
+ const data = new FloatXArray(b.data.length);
37211
+ for (let i = 0; i < b.data.length; i++)
37212
+ data[i] = divFix(av, b.data[i]);
37213
+ return RTV.tensor(data, [...b.shape]);
37214
+ }
37215
+ if (isRuntimeTensor(a) && isRuntimeTensor(b)) {
37216
+ if (a.data.length !== b.data.length)
37217
+ throw new RuntimeError("idivide: arrays must be the same size");
37218
+ const data = new FloatXArray(a.data.length);
37219
+ for (let i = 0; i < a.data.length; i++)
37220
+ data[i] = divFix(a.data[i], b.data[i]);
37221
+ return RTV.tensor(data, [...a.shape]);
37222
+ }
37223
+ throw new RuntimeError("idivide: arguments must be numeric");
37224
+ }
37225
+ }
37226
+ ]
37227
+ });
36604
37228
  defineBuiltin({
36605
37229
  name: "logical",
36606
37230
  cases: [
@@ -37983,6 +38607,24 @@ registerIBuiltin({
37983
38607
  }
37984
38608
  })
37985
38609
  });
38610
+ function getMexExt() {
38611
+ if (typeof process === "undefined") return "";
38612
+ const platform = process.platform;
38613
+ const cpuArch = process.arch;
38614
+ if (platform === "win32") return "mexw64";
38615
+ if (platform === "darwin")
38616
+ return cpuArch === "arm64" ? "mexmaca64" : "mexmaci64";
38617
+ return "mexa64";
38618
+ }
38619
+ defineBuiltin({
38620
+ name: "mexext",
38621
+ cases: [
38622
+ {
38623
+ match: (argTypes) => argTypes.length === 0 ? [{ kind: "char" }] : null,
38624
+ apply: () => RTV.char(getMexExt())
38625
+ }
38626
+ ]
38627
+ });
37986
38628
  var _platform = typeof process !== "undefined" ? process.platform : "linux";
37987
38629
  for (const [name, val] of [
37988
38630
  ["ismac", _platform === "darwin"],
@@ -39028,7 +39670,7 @@ for (const name of ["clear", "clc", "clf"]) {
39028
39670
  name,
39029
39671
  resolve: () => ({
39030
39672
  outputTypes: [],
39031
- apply: () => 0
39673
+ apply: () => void 0
39032
39674
  })
39033
39675
  });
39034
39676
  }
@@ -39162,7 +39804,7 @@ registerIBuiltin({
39162
39804
  name: "set",
39163
39805
  resolve: () => ({
39164
39806
  outputTypes: [],
39165
- apply: () => 0
39807
+ apply: () => void 0
39166
39808
  })
39167
39809
  });
39168
39810
  for (const name of [
@@ -39184,7 +39826,7 @@ for (const name of [
39184
39826
  name,
39185
39827
  resolve: () => ({
39186
39828
  outputTypes: [],
39187
- apply: () => 0
39829
+ apply: () => void 0
39188
39830
  })
39189
39831
  });
39190
39832
  }
@@ -40992,6 +41634,15 @@ var H = {
40992
41634
  signatures: ["TF = isequal(A, B, ...)"],
40993
41635
  description: "True if all inputs are equal (NaN ~= NaN)."
40994
41636
  },
41637
+ // ── Dynamic evaluation ────────────────────────────────────────────────
41638
+ evalin: {
41639
+ signatures: ["V = evalin(WS, EXPR)", "V = evalin(WS, EXPR, DEFAULT)"],
41640
+ description: "Evaluate EXPR in workspace WS ('caller' or 'base'/'workspace').\n\nnumbl-specific note: variables read by evalin must be declared in the\nfunction that owns them with a `% external-access:` comment, e.g.\n function out = f()\n % external-access: x y\n x = 1; y = 2;\n ...\n end\nVariables not listed in `% external-access` are stored in a separate\ndynamic map and are only reachable through evalin/assignin. The\ndirective is a comment, so MATLAB ignores it."
41641
+ },
41642
+ assignin: {
41643
+ signatures: ["assignin(WS, NAME, VALUE)"],
41644
+ description: "Assign VALUE to variable NAME in workspace WS ('caller' or 'base'/'workspace').\n\nnumbl-specific note: variables written by assignin must be declared in\nthe function that owns them with a `% external-access:` comment, e.g.\n function out = f()\n % external-access: x y\n x = 1; y = 2;\n ...\n end\nVariables not listed in `% external-access` are stored in a separate\ndynamic map and are only reachable through evalin/assignin. The\ndirective is a comment, so MATLAB ignores it."
41645
+ },
40995
41646
  // ── Misc ──────────────────────────────────────────────────────────────
40996
41647
  disp: {
40997
41648
  signatures: ["disp(X)"],
@@ -43904,6 +44555,114 @@ function bisectEvent(eventIdx, step, events) {
43904
44555
  return [tOld + xFinal * h, denseOutputEval(yOld, Q, h, xFinal)];
43905
44556
  }
43906
44557
 
44558
+ // src/numbl-core/helpers/quadgk.ts
44559
+ var XK = [
44560
+ -0.9914553711208126,
44561
+ -0.9491079123427585,
44562
+ -0.8648644233597691,
44563
+ -0.7415311855993945,
44564
+ -0.586087235467691,
44565
+ -0.4058451513773972,
44566
+ -0.2077849550078985,
44567
+ 0,
44568
+ 0.2077849550078985,
44569
+ 0.4058451513773972,
44570
+ 0.586087235467691,
44571
+ 0.7415311855993945,
44572
+ 0.8648644233597691,
44573
+ 0.9491079123427585,
44574
+ 0.9914553711208126
44575
+ ];
44576
+ var WK = [
44577
+ 0.0229353220105292,
44578
+ 0.0630920926299786,
44579
+ 0.1047900103222502,
44580
+ 0.1406532597155259,
44581
+ 0.1690047266392679,
44582
+ 0.1903505780647854,
44583
+ 0.2044329400752989,
44584
+ 0.2094821410847278,
44585
+ 0.2044329400752989,
44586
+ 0.1903505780647854,
44587
+ 0.1690047266392679,
44588
+ 0.1406532597155259,
44589
+ 0.1047900103222502,
44590
+ 0.0630920926299786,
44591
+ 0.0229353220105292
44592
+ ];
44593
+ var WG = [
44594
+ 0.1294849661688697,
44595
+ 0.2797053914892767,
44596
+ 0.3818300505051189,
44597
+ 0.4179591836734694,
44598
+ 0.3818300505051189,
44599
+ 0.2797053914892767,
44600
+ 0.1294849661688697
44601
+ ];
44602
+ function kronrodNodes(lo, hi) {
44603
+ const m = (lo + hi) / 2;
44604
+ const h = (hi - lo) / 2;
44605
+ const out = new Array(15);
44606
+ for (let i = 0; i < 15; i++) out[i] = m + h * XK[i];
44607
+ return out;
44608
+ }
44609
+ function segmentEstimate(lo, hi, fv) {
44610
+ const h = (hi - lo) / 2;
44611
+ let K = 0;
44612
+ let G = 0;
44613
+ for (let i = 0; i < 15; i++) K += WK[i] * fv[i];
44614
+ for (let i = 0; i < 7; i++) G += WG[i] * fv[2 * i + 1];
44615
+ K *= h;
44616
+ G *= h;
44617
+ return { K, err: Math.abs(K - G) };
44618
+ }
44619
+ function quadgkAdaptive(integrand, a, b, opts = {}) {
44620
+ const relTol = opts.relTol ?? 1e-6;
44621
+ const absTol = opts.absTol ?? 1e-10;
44622
+ const maxIntervals = opts.maxIntervalCount ?? 650;
44623
+ if (a === b) return { value: 0, errbnd: 0, intervalsUsed: 0 };
44624
+ const sign = a < b ? 1 : -1;
44625
+ const lo0 = Math.min(a, b);
44626
+ const hi0 = Math.max(a, b);
44627
+ const segmentOn = (lo, hi) => {
44628
+ const pts = kronrodNodes(lo, hi);
44629
+ const fv = integrand(pts);
44630
+ if (fv.length !== 15) {
44631
+ throw new Error(
44632
+ `quadgk: integrand must return 15 values for 15 nodes, got ${fv.length}`
44633
+ );
44634
+ }
44635
+ return segmentEstimate(lo, hi, fv);
44636
+ };
44637
+ const initial = segmentOn(lo0, hi0);
44638
+ let totalK = initial.K;
44639
+ let totalErr = initial.err;
44640
+ const worklist = [{ lo: lo0, hi: hi0, ...initial }];
44641
+ const converged = () => totalErr <= Math.max(absTol, relTol * Math.abs(totalK));
44642
+ let iters = 0;
44643
+ while (!converged() && worklist.length < maxIntervals && iters < 1e4) {
44644
+ iters++;
44645
+ let worstIdx = 0;
44646
+ for (let i = 1; i < worklist.length; i++) {
44647
+ if (worklist[i].err > worklist[worstIdx].err) worstIdx = i;
44648
+ }
44649
+ const worst = worklist[worstIdx];
44650
+ if (worst.hi - worst.lo <= 1e-15 * (hi0 - lo0)) break;
44651
+ const mid = (worst.lo + worst.hi) / 2;
44652
+ const s1 = segmentOn(worst.lo, mid);
44653
+ const s2 = segmentOn(mid, worst.hi);
44654
+ totalK += s1.K + s2.K - worst.K;
44655
+ totalErr += s1.err + s2.err - worst.err;
44656
+ worklist[worstIdx] = { lo: worst.lo, hi: mid, ...s1 };
44657
+ worklist.push({ lo: mid, hi: worst.hi, ...s2 });
44658
+ }
44659
+ return {
44660
+ value: sign * totalK,
44661
+ errbnd: totalErr,
44662
+ intervalsUsed: worklist.length
44663
+ };
44664
+ }
44665
+
43907
44666
  // src/numbl-core/runtime/specialBuiltinNames.ts
43908
44667
  var SPECIAL_BUILTIN_NAMES = [
43909
44668
  "help",
@@ -43992,6 +44751,7 @@ var SPECIAL_BUILTIN_NAMES = [
43992
44751
  "webread",
43993
44752
  "delete",
43994
44753
  "rmdir",
44754
+ "movefile",
43995
44755
  "unzip",
43996
44756
  "dir",
43997
44757
  "warning",
@@ -44005,7 +44765,8 @@ var SPECIAL_BUILTIN_NAMES = [
44005
44765
  "ode45",
44006
44766
  "ode23",
44007
44767
  "deval",
44008
- "toc"
44768
+ "toc",
44769
+ "quadgk"
44009
44770
  ];
44010
44771
 
44011
44772
  // src/numbl-core/runtime/specialBuiltins.ts
@@ -44019,42 +44780,58 @@ function registerSpecial(name, fn) {
44019
44780
  })
44020
44781
  });
44021
44782
  }
44783
+ function registerSpecialVoid(name, fn) {
44784
+ registerDynamicIBuiltin({
44785
+ name,
44786
+ resolve: () => ({
44787
+ outputTypes: [],
44788
+ apply: (args) => {
44789
+ fn(args);
44790
+ return void 0;
44791
+ }
44792
+ })
44793
+ });
44794
+ }
44022
44795
  function registerSpecialBuiltins(rt) {
44023
- registerSpecial("help", (_nargout, args) => {
44796
+ registerSpecial("help", (nargout, args) => {
44797
+ let text = "";
44798
+ const emit = (s) => {
44799
+ text += s;
44800
+ if (nargout === 0) rt.output(s);
44801
+ };
44024
44802
  if (args.length === 0) {
44025
44803
  const names = getAllBuiltinNames().sort();
44026
- rt.output("Available builtins:\n");
44027
- rt.output(" " + names.join(", ") + "\n");
44028
- rt.output("\nType 'help <name>' for help on a specific builtin.\n");
44029
- return 0;
44030
- }
44031
- const name = toString(args[0]);
44032
- const h = getIBuiltinHelp(name);
44033
- if (!h) {
44034
- const allNames = getAllBuiltinNames();
44035
- if (allNames.includes(name)) {
44036
- rt.output(`No help available for '${name}'.
44804
+ emit("Available builtins:\n");
44805
+ emit(" " + names.join(", ") + "\n");
44806
+ emit("\nType 'help <name>' for help on a specific builtin.\n");
44807
+ } else {
44808
+ const name = toString(args[0]);
44809
+ const h = getIBuiltinHelp(name);
44810
+ if (!h) {
44811
+ const allNames = getAllBuiltinNames();
44812
+ if (allNames.includes(name)) {
44813
+ emit(`No help available for '${name}'.
44037
44814
  `);
44038
- } else {
44039
- rt.output(`Unknown function '${name}'.
44815
+ } else {
44816
+ emit(`Unknown function '${name}'.
44040
44817
  `);
44041
- }
44042
- return 0;
44043
- }
44044
- rt.output(` ${h.signatures.join("\n ")}
44818
+ }
44819
+ } else {
44820
+ emit(` ${h.signatures.join("\n ")}
44045
44821
 
44046
44822
  `);
44047
- rt.output(`${h.description}
44823
+ emit(`${h.description}
44048
44824
  `);
44049
- return 0;
44825
+ }
44826
+ }
44827
+ return nargout >= 1 ? RTV.char(text) : void 0;
44050
44828
  });
44051
- registerSpecial("disp", (_nargout, args) => {
44829
+ registerSpecialVoid("disp", (args) => {
44052
44830
  if (args.length >= 1) {
44053
44831
  const mv = ensureRuntimeValue(args[0]);
44054
- if (isRuntimeTensor(mv) && mv.data.length === 0) return 0;
44832
+ if (isRuntimeTensor(mv) && mv.data.length === 0) return;
44055
44833
  rt.output(displayValue(mv) + "\n");
44056
44834
  }
44057
- return 0;
44058
44835
  });
44059
44836
  registerSpecial("toc", (nargout) => {
44060
44837
  const elapsed = (performance.now() - getTicTime()) / 1e3;
@@ -44064,12 +44841,13 @@ function registerSpecialBuiltins(rt) {
44064
44841
  }
44065
44842
  return RTV.num(elapsed);
44066
44843
  });
44067
- registerSpecial("warning", (_nargout, args) => {
44068
- if (args.length === 0) return RTV.num(0);
44844
+ registerSpecial("warning", (nargout, args) => {
44845
+ if (args.length === 0) return nargout >= 1 ? RTV.num(0) : void 0;
44069
44846
  const margs = args.map((a) => ensureRuntimeValue(a));
44070
44847
  if (margs.length === 2 && isRuntimeChar(margs[0]) && isRuntimeChar(margs[1])) {
44071
44848
  const state = toString(margs[0]);
44072
44849
  if (state === "on" || state === "off") {
44850
+ if (nargout === 0) return void 0;
44073
44851
  return RTV.struct(
44074
44852
  /* @__PURE__ */ new Map([
44075
44853
  ["state", RTV.char("on")],
@@ -44098,9 +44876,9 @@ function registerSpecialBuiltins(rt) {
44098
44876
  } else {
44099
44877
  rt.output("Warning: " + sprintfFormat(fmt, fmtArgs) + "\n");
44100
44878
  }
44101
- return RTV.num(0);
44879
+ return nargout >= 1 ? RTV.num(0) : void 0;
44102
44880
  });
44103
- registerSpecial("fprintf", (_nargout, args) => {
44881
+ registerSpecial("fprintf", (nargout, args) => {
44104
44882
  let output = "";
44105
44883
  if (args.length >= 1) {
44106
44884
  const margs = args.map((a) => ensureRuntimeValue(a));
@@ -44111,27 +44889,7 @@ function registerSpecialBuiltins(rt) {
44111
44889
  fmtIdx = 1;
44112
44890
  }
44113
44891
  const fmt = toString(margs[fmtIdx]);
44114
- const scalarArgs = [];
44115
- for (let i = fmtIdx + 1; i < margs.length; i++) {
44116
- const a = margs[i];
44117
- if (isRuntimeTensor(a)) {
44118
- for (let j = 0; j < a.data.length; j++)
44119
- scalarArgs.push(RTV.num(a.data[j]));
44120
- } else {
44121
- scalarArgs.push(a);
44122
- }
44123
- }
44124
- const specCount = (fmt.match(/%[^%]/g) || []).length;
44125
- if (specCount === 0 || scalarArgs.length === 0) {
44126
- output = sprintfFormat(fmt, scalarArgs);
44127
- } else {
44128
- let idx = 0;
44129
- while (idx < scalarArgs.length) {
44130
- const batch = scalarArgs.slice(idx, idx + specCount);
44131
- output += sprintfFormat(fmt, batch);
44132
- idx += specCount;
44133
- }
44134
- }
44892
+ output = sprintfFormat(fmt, margs.slice(fmtIdx + 1));
44135
44893
  if (fid === 1 || fid === 2) {
44136
44894
  rt.output(output);
44137
44895
  } else {
@@ -44142,7 +44900,7 @@ function registerSpecialBuiltins(rt) {
44142
44900
  rt.fileIO.fwrite(fid, output);
44143
44901
  }
44144
44902
  }
44145
- return output.length;
44903
+ return nargout >= 1 ? output.length : void 0;
44146
44904
  });
44147
44905
  registerSpecial("arrayfun", (nargout, args) => {
44148
44906
  return arrayfunImpl(rt, nargout, args);
@@ -44174,6 +44932,55 @@ function registerSpecialBuiltins(rt) {
44174
44932
  registerSpecial("bsxfun", (nargout, args) => {
44175
44933
  return bsxfunImpl(rt, nargout, args);
44176
44934
  });
44935
+ registerSpecial("quadgk", (nargout, args) => {
44936
+ if (args.length < 3)
44937
+ throw new RuntimeError(
44938
+ "quadgk: requires at least 3 arguments (fun, a, b)"
44939
+ );
44940
+ const fnArg = ensureRuntimeValue(args[0]);
44941
+ if (!isRuntimeFunction(fnArg))
44942
+ throw new RuntimeError("quadgk: first argument must be a function");
44943
+ const a = toNumber(ensureRuntimeValue(args[1]));
44944
+ const b = toNumber(ensureRuntimeValue(args[2]));
44945
+ let relTol;
44946
+ let absTol;
44947
+ let maxIntervalCount;
44948
+ for (let i = 3; i + 1 < args.length; i += 2) {
44949
+ const keyRv = ensureRuntimeValue(args[i]);
44950
+ const key = isRuntimeChar(keyRv) ? keyRv.value : isRuntimeString(keyRv) ? keyRv : "";
44951
+ const lowerKey = key.toLowerCase();
44952
+ const valRv = ensureRuntimeValue(args[i + 1]);
44953
+ if (lowerKey === "reltol") relTol = toNumber(valRv);
44954
+ else if (lowerKey === "abstol") absTol = toNumber(valRv);
44955
+ else if (lowerKey === "maxintervalcount")
44956
+ maxIntervalCount = toNumber(valRv);
44957
+ }
44958
+ const integrand = (pts) => {
44959
+ const vecData = new FloatXArray(pts);
44960
+ const vec = RTV.tensor(vecData, [1, pts.length]);
44961
+ const resultRaw = rt.index(fnArg, [vec], 1);
44962
+ const rv = ensureRuntimeValue(resultRaw);
44963
+ if (isRuntimeNumber(rv)) {
44964
+ return new Array(pts.length).fill(rv);
44965
+ }
44966
+ if (isRuntimeTensor(rv)) {
44967
+ if (rv.data.length !== pts.length) {
44968
+ throw new RuntimeError(
44969
+ `quadgk: integrand returned ${rv.data.length} values for ${pts.length} nodes`
44970
+ );
44971
+ }
44972
+ return Array.from(rv.data);
44973
+ }
44974
+ throw new RuntimeError("quadgk: integrand must return a numeric vector");
44975
+ };
44976
+ const result = quadgkAdaptive(integrand, a, b, {
44977
+ relTol,
44978
+ absTol,
44979
+ maxIntervalCount
44980
+ });
44981
+ if (nargout >= 2) return [result.value, result.errbnd];
44982
+ return result.value;
44983
+ });
44177
44984
  registerSpecial("subsref", (nargout, args) => {
44178
44985
  return subsrefBuiltin(rt, nargout, args);
44179
44986
  });
@@ -44796,7 +45603,7 @@ function registerSpecialBuiltins(rt) {
44796
45603
  return RTV.char(text);
44797
45604
  }
44798
45605
  });
44799
- registerSpecial("delete", (_nargout, args) => {
45606
+ registerSpecialVoid("delete", (args) => {
44800
45607
  const io = requireFileIO();
44801
45608
  if (!io.deleteFile)
44802
45609
  throw new RuntimeError("delete is not available in this environment");
@@ -44806,7 +45613,6 @@ function registerSpecialBuiltins(rt) {
44806
45613
  for (const arg of margs) {
44807
45614
  io.deleteFile(toString(arg));
44808
45615
  }
44809
- return 0;
44810
45616
  });
44811
45617
  registerSpecial("rmdir", (nargout, args) => {
44812
45618
  const io = requireFileIO();
@@ -44829,6 +45635,34 @@ function registerSpecialBuiltins(rt) {
44829
45635
  RTV.char("")
44830
45636
  ];
44831
45637
  });
45638
+ registerSpecial("movefile", (nargout, args) => {
45639
+ const io = requireFileIO();
45640
+ if (!io.movefile)
45641
+ throw new RuntimeError("movefile is not available in this environment");
45642
+ const margs = args.map((a) => ensureRuntimeValue(a));
45643
+ if (margs.length < 1)
45644
+ throw new RuntimeError("movefile requires at least 1 argument");
45645
+ const source = toString(margs[0]);
45646
+ const destination = margs.length >= 2 ? toString(margs[1]) : rt.system?.cwd() ?? ".";
45647
+ let force = false;
45648
+ if (margs.length >= 3) {
45649
+ const third = toString(margs[2]);
45650
+ if (third.toLowerCase() === "f") force = true;
45651
+ }
45652
+ const ok = io.movefile(source, destination, force);
45653
+ if (nargout === 0) {
45654
+ if (!ok)
45655
+ throw new RuntimeError(
45656
+ `movefile: cannot move '${source}' to '${destination}'`
45657
+ );
45658
+ return void 0;
45659
+ }
45660
+ return nargout <= 1 ? RTV.num(ok ? 1 : 0) : [
45661
+ RTV.num(ok ? 1 : 0),
45662
+ RTV.char(ok ? "" : `Cannot move '${source}' to '${destination}'`),
45663
+ RTV.char("")
45664
+ ];
45665
+ });
44832
45666
  registerSpecial("unzip", (nargout, args) => {
44833
45667
  const io = requireFileIO();
44834
45668
  if (!io.unzip)
@@ -44880,7 +45714,7 @@ function registerSpecialBuiltins(rt) {
44880
45714
  const cellData = extracted.map((f) => RTV.char(f));
44881
45715
  return RTV.cell(cellData, [1, cellData.length]);
44882
45716
  }
44883
- return 0;
45717
+ return void 0;
44884
45718
  });
44885
45719
  registerSpecial("dir", (nargout, args) => {
44886
45720
  const io = requireFileIO();
@@ -44977,7 +45811,7 @@ function registerSpecialBuiltins(rt) {
44977
45811
  const parts = margs.map((a) => toString(a));
44978
45812
  return RTV.char(parts.join("/"));
44979
45813
  });
44980
- registerSpecial("assignin", (_nargout, args) => {
45814
+ registerSpecialVoid("assignin", (args) => {
44981
45815
  if (args.length < 3)
44982
45816
  throw new RuntimeError("assignin requires 3 arguments");
44983
45817
  const margs = args.map((a) => ensureRuntimeValue(a));
@@ -44992,7 +45826,6 @@ function registerSpecialBuiltins(rt) {
44992
45826
  } else {
44993
45827
  rt.setWorkspaceVariable(varName, args[2]);
44994
45828
  }
44995
- return 0;
44996
45829
  });
44997
45830
  registerSpecial("evalin", (_nargout, args) => {
44998
45831
  if (args.length < 2)
@@ -45013,13 +45846,12 @@ function registerSpecialBuiltins(rt) {
45013
45846
  }
45014
45847
  return val;
45015
45848
  });
45016
- registerSpecial("drawnow", () => {
45849
+ registerSpecialVoid("drawnow", () => {
45017
45850
  rt.drawnow();
45018
- return 0;
45019
45851
  });
45020
- registerSpecial("pause", (_nargout, args) => {
45852
+ registerSpecial("pause", (nargout, args) => {
45021
45853
  rt.pause(args[0] ?? 0);
45022
- return 0;
45854
+ return nargout >= 1 ? RTV.char("on") : void 0;
45023
45855
  });
45024
45856
  registerSpecial("mfilename", (_nargout, args) => {
45025
45857
  const file = rt.$file ?? "";
@@ -45063,7 +45895,7 @@ function registerSpecialBuiltins(rt) {
45063
45895
  }
45064
45896
  }
45065
45897
  if (nargout >= 1) return RTV.char(rt.searchPaths.join(";"));
45066
- return 0;
45898
+ return void 0;
45067
45899
  });
45068
45900
  registerSpecial("rmpath", (nargout, args) => {
45069
45901
  if (!rt.onPathChange) {
@@ -45083,11 +45915,11 @@ function registerSpecialBuiltins(rt) {
45083
45915
  }
45084
45916
  }
45085
45917
  if (nargout >= 1) return RTV.char(rt.searchPaths.join(";"));
45086
- return 0;
45918
+ return void 0;
45087
45919
  });
45088
- registerSpecial("savepath", () => {
45920
+ registerSpecial("savepath", (nargout) => {
45089
45921
  rt.output("Warning: savepath is a no-op in numbl\n");
45090
- return 0;
45922
+ return nargout >= 1 ? RTV.num(0) : void 0;
45091
45923
  });
45092
45924
  registerSpecial("input", (_nargout, args) => {
45093
45925
  const margs = args.map((a) => ensureRuntimeValue(a));
@@ -45150,11 +45982,11 @@ function registerSpecialBuiltins(rt) {
45150
45982
  }
45151
45983
  return RTV.char(sys?.getEnv(toString(args[0])) ?? "");
45152
45984
  });
45153
- registerSpecial("setenv", (_nargout, args) => {
45985
+ registerSpecialVoid("setenv", (args) => {
45154
45986
  const sys = rt.system;
45155
45987
  if (args.length === 2) {
45156
45988
  sys?.setEnv(toString(args[0]), toString(args[1]));
45157
- return 0;
45989
+ return;
45158
45990
  }
45159
45991
  if (args.length === 1) {
45160
45992
  const d = args[0];
@@ -45162,20 +45994,26 @@ function registerSpecialBuiltins(rt) {
45162
45994
  for (const { key, value } of d.entries.values()) {
45163
45995
  sys?.setEnv(toString(key), toString(value));
45164
45996
  }
45165
- return 0;
45997
+ return;
45166
45998
  }
45167
45999
  sys?.setEnv(toString(d), "");
45168
- return 0;
46000
+ return;
45169
46001
  }
45170
46002
  throw new RuntimeError("setenv: invalid arguments");
45171
46003
  });
45172
46004
  registerSpecial("pwd", () => {
45173
46005
  return RTV.char(rt.system?.cwd() ?? "/");
45174
46006
  });
45175
- registerSpecial("cd", (_nargout, args) => {
46007
+ registerSpecial("cd", (nargout, args) => {
45176
46008
  const sys = rt.system;
45177
46009
  const curDir = sys?.cwd() ?? "/";
45178
- if (args.length === 0) return RTV.char(curDir);
46010
+ if (args.length === 0) {
46011
+ if (nargout === 0) {
46012
+ rt.output(curDir + "\n");
46013
+ return void 0;
46014
+ }
46015
+ return RTV.char(curDir);
46016
+ }
45179
46017
  const target = toString(args[0]);
45180
46018
  if (sys) {
45181
46019
  try {
@@ -45183,15 +46021,18 @@ function registerSpecialBuiltins(rt) {
45183
46021
  } catch {
45184
46022
  throw new RuntimeError(`Cannot change directory to '${target}'`);
45185
46023
  }
46024
+ if (rt.onCwdChange) {
46025
+ rt.onCwdChange(sys.cwd());
46026
+ }
45186
46027
  }
45187
- return RTV.char(curDir);
46028
+ return nargout >= 1 ? RTV.char(curDir) : void 0;
45188
46029
  });
45189
- registerSpecial("figure", (_nargout, args) => {
46030
+ registerSpecial("figure", (nargout, args) => {
45190
46031
  const handle = args.length > 0 ? args[0] : 1;
45191
46032
  plotInstr(rt.plotInstructions, { type: "set_figure_handle", handle });
45192
- return 0;
46033
+ return nargout >= 1 ? RTV.num(toNumber(ensureRuntimeValue(handle))) : void 0;
45193
46034
  });
45194
- registerSpecial("subplot", (_nargout, args) => {
46035
+ registerSpecial("subplot", (nargout, args) => {
45195
46036
  if (args.length >= 3) {
45196
46037
  plotInstr(rt.plotInstructions, {
45197
46038
  type: "set_subplot",
@@ -45200,43 +46041,41 @@ function registerSpecialBuiltins(rt) {
45200
46041
  index: args[2]
45201
46042
  });
45202
46043
  }
45203
- return 0;
46044
+ return nargout >= 1 ? RTV.num(0) : void 0;
45204
46045
  });
45205
- registerSpecial("title", (_nargout, args) => {
46046
+ registerSpecial("title", (nargout, args) => {
45206
46047
  if (args.length > 0) {
45207
46048
  plotInstr(rt.plotInstructions, { type: "set_title", text: args[0] });
45208
46049
  }
45209
- return 0;
46050
+ return nargout >= 1 ? RTV.num(0) : void 0;
45210
46051
  });
45211
- registerSpecial("xlabel", (_nargout, args) => {
46052
+ registerSpecial("xlabel", (nargout, args) => {
45212
46053
  if (args.length > 0) {
45213
46054
  plotInstr(rt.plotInstructions, { type: "set_xlabel", text: args[0] });
45214
46055
  }
45215
- return 0;
46056
+ return nargout >= 1 ? RTV.num(0) : void 0;
45216
46057
  });
45217
- registerSpecial("ylabel", (_nargout, args) => {
46058
+ registerSpecial("ylabel", (nargout, args) => {
45218
46059
  if (args.length > 0) {
45219
46060
  plotInstr(rt.plotInstructions, { type: "set_ylabel", text: args[0] });
45220
46061
  }
45221
- return 0;
46062
+ return nargout >= 1 ? RTV.num(0) : void 0;
45222
46063
  });
45223
- registerSpecial("hold", (_nargout, args) => {
46064
+ registerSpecialVoid("hold", (args) => {
45224
46065
  if (args.length > 0) {
45225
46066
  plotInstr(rt.plotInstructions, { type: "set_hold", value: args[0] });
45226
46067
  }
45227
- return 0;
45228
46068
  });
45229
- registerSpecial("grid", (_nargout, args) => {
46069
+ registerSpecialVoid("grid", (args) => {
45230
46070
  if (args.length > 0) {
45231
46071
  plotInstr(rt.plotInstructions, { type: "set_grid", value: args[0] });
45232
46072
  }
45233
- return 0;
45234
46073
  });
45235
- registerSpecial("legend", (_nargout, args) => {
46074
+ registerSpecial("legend", (nargout, args) => {
45236
46075
  legendCall(rt.plotInstructions, args);
45237
- return 0;
46076
+ return nargout >= 1 ? RTV.num(0) : void 0;
45238
46077
  });
45239
- registerSpecial("close", (_nargout, args) => {
46078
+ registerSpecial("close", (nargout, args) => {
45240
46079
  if (args.length > 0) {
45241
46080
  const val = toString(args[0]);
45242
46081
  if (val === "all") {
@@ -45247,26 +46086,25 @@ function registerSpecialBuiltins(rt) {
45247
46086
  } else {
45248
46087
  plotInstr(rt.plotInstructions, { type: "close" });
45249
46088
  }
45250
- return 0;
46089
+ return nargout >= 1 ? RTV.num(1) : void 0;
45251
46090
  });
45252
- registerSpecial("sgtitle", (_nargout, args) => {
46091
+ registerSpecial("sgtitle", (nargout, args) => {
45253
46092
  if (args.length > 0) {
45254
46093
  plotInstr(rt.plotInstructions, { type: "set_sgtitle", text: args[0] });
45255
46094
  }
45256
- return 0;
46095
+ return nargout >= 1 ? RTV.num(0) : void 0;
45257
46096
  });
45258
- registerSpecial("shading", (_nargout, args) => {
46097
+ registerSpecialVoid("shading", (args) => {
45259
46098
  if (args.length > 0) {
45260
46099
  plotInstr(rt.plotInstructions, {
45261
46100
  type: "set_shading",
45262
46101
  shading: args[0]
45263
46102
  });
45264
46103
  }
45265
- return 0;
45266
46104
  });
45267
- registerSpecial("clf", () => {
46105
+ registerSpecial("clf", (nargout) => {
45268
46106
  plotInstr(rt.plotInstructions, { type: "clf" });
45269
- return 0;
46107
+ return nargout >= 1 ? RTV.num(1) : void 0;
45270
46108
  });
45271
46109
  registerSpecial("ode45", (nargout, args) => {
45272
46110
  return _ode45Impl(rt, nargout, args, dormandPrince45);
@@ -45811,6 +46649,29 @@ function methodDispatch(rt, name, nargout, args) {
45811
46649
  }
45812
46650
  return fieldVal;
45813
46651
  }
46652
+ {
46653
+ const accessorKey = `${firstRV.className}.get.${name}`;
46654
+ if (!rt.activeAccessors.has(accessorKey)) {
46655
+ const getter = rt.cachedResolveClassMethod(
46656
+ firstRV.className,
46657
+ `get.${name}`
46658
+ );
46659
+ if (getter) {
46660
+ rt.activeAccessors.add(accessorKey);
46661
+ let gotVal;
46662
+ try {
46663
+ gotVal = getter(1, first);
46664
+ } finally {
46665
+ rt.activeAccessors.delete(accessorKey);
46666
+ }
46667
+ const remaining = args.slice(1);
46668
+ if (remaining.length > 0) {
46669
+ return rt.index(gotVal, remaining, nargout);
46670
+ }
46671
+ return gotVal;
46672
+ }
46673
+ }
46674
+ }
45814
46675
  try {
45815
46676
  return callClassMethod(rt, firstRV.className, name, nargout, args);
45816
46677
  } catch (e) {
@@ -46379,6 +47240,7 @@ function index(rt, base, indices, nargout = 1, skipSubsref = false) {
46379
47240
  const im2 = t.imag[i];
46380
47241
  return im2 === 0 ? t.data[i] : RTV.complex(t.data[i], im2);
46381
47242
  }
47243
+ if (t._isLogical === true) return t.data[i] !== 0;
46382
47244
  return t.data[i];
46383
47245
  }
46384
47246
  } else if (nIdx === 2) {
@@ -46387,7 +47249,14 @@ function index(rt, base, indices, nargout = 1, skipSubsref = false) {
46387
47249
  if (typeof ri === "number" && typeof ci === "number") {
46388
47250
  const s = t.shape;
46389
47251
  const rows = s.length === 0 ? 1 : s.length === 1 ? 1 : s[0];
46390
- const cols = s.length === 0 ? 1 : s.length === 1 ? s[0] : s[1];
47252
+ let cols;
47253
+ if (s.length === 0) cols = 1;
47254
+ else if (s.length === 1) cols = s[0];
47255
+ else if (s.length === 2) cols = s[1];
47256
+ else {
47257
+ cols = 1;
47258
+ for (let k = 1; k < s.length; k++) cols *= s[k];
47259
+ }
46391
47260
  const r = Math.round(ri) - 1;
46392
47261
  const c = Math.round(ci) - 1;
46393
47262
  if (r < 0 || r >= rows || c < 0 || c >= cols)
@@ -46397,6 +47266,7 @@ function index(rt, base, indices, nargout = 1, skipSubsref = false) {
46397
47266
  const im2 = t.imag[lin];
46398
47267
  return im2 === 0 ? t.data[lin] : RTV.complex(t.data[lin], im2);
46399
47268
  }
47269
+ if (t._isLogical === true) return t.data[lin] !== 0;
46400
47270
  return t.data[lin];
46401
47271
  }
46402
47272
  } else if (nIdx >= 3) {
@@ -46787,14 +47657,20 @@ function multiOutputCellAssign(base, indices, results) {
46787
47657
  if (base === void 0 || base === null) base = RTV.cell([], [0, 0]);
46788
47658
  let mv = ensureRuntimeValue(base);
46789
47659
  if (!isRuntimeCell(mv)) mv = RTV.cell([], [0, 0]);
46790
- const idxMv = ensureRuntimeValue(indices);
46791
47660
  let idxArray;
46792
- if (isRuntimeTensor(idxMv)) {
46793
- idxArray = Array.from(idxMv.data);
46794
- } else if (isRuntimeNumber(idxMv)) {
46795
- idxArray = [idxMv];
47661
+ if (indices === COLON_SENTINEL) {
47662
+ const n = mv.data.length;
47663
+ idxArray = [];
47664
+ for (let i = 1; i <= n; i++) idxArray.push(i);
46796
47665
  } else {
46797
- idxArray = [1];
47666
+ const idxMv = ensureRuntimeValue(indices);
47667
+ if (isRuntimeTensor(idxMv)) {
47668
+ idxArray = Array.from(idxMv.data);
47669
+ } else if (isRuntimeNumber(idxMv)) {
47670
+ idxArray = [idxMv];
47671
+ } else {
47672
+ idxArray = [1];
47673
+ }
46798
47674
  }
46799
47675
  for (let i = 0; i < idxArray.length && i < results.length; i++) {
46800
47676
  const idx = idxArray[i];
@@ -47021,6 +47897,8 @@ var Runtime = class _Runtime {
47021
47897
  classMethodCache = /* @__PURE__ */ new Map();
47022
47898
  /** Callback for addpath/rmpath — mutates search paths and rebuilds function index. */
47023
47899
  onPathChange = null;
47900
+ /** Callback invoked after cd() to update the implicit cwd search path. */
47901
+ onCwdChange = null;
47024
47902
  /** Reference to the active search paths (set by executeCode). */
47025
47903
  searchPaths = [];
47026
47904
  // Workspace accessors: varName → { get, set } closures over script-level vars
@@ -47300,23 +48178,26 @@ var Runtime = class _Runtime {
47300
48178
  if (e instanceof RuntimeError) {
47301
48179
  return RTV.struct(
47302
48180
  /* @__PURE__ */ new Map([
47303
- ["message", RTV.string(e.message)],
47304
- ["identifier", RTV.string(e.identifier)]
48181
+ ["message", RTV.char(e.message)],
48182
+ ["identifier", RTV.char(e.identifier)],
48183
+ ["stack", buildStackField(e)]
47305
48184
  ])
47306
48185
  );
47307
48186
  }
47308
48187
  if (e instanceof Error) {
47309
48188
  return RTV.struct(
47310
48189
  /* @__PURE__ */ new Map([
47311
- ["message", RTV.string(e.message)],
47312
- ["identifier", RTV.string("")]
48190
+ ["message", RTV.char(e.message)],
48191
+ ["identifier", RTV.char("")],
48192
+ ["stack", emptyStackField()]
47313
48193
  ])
47314
48194
  );
47315
48195
  }
47316
48196
  return RTV.struct(
47317
48197
  /* @__PURE__ */ new Map([
47318
- ["message", RTV.string(String(e))],
47319
- ["identifier", RTV.string("")]
48198
+ ["message", RTV.char(String(e))],
48199
+ ["identifier", RTV.char("")],
48200
+ ["stack", emptyStackField()]
47320
48201
  ])
47321
48202
  );
47322
48203
  }
@@ -48148,6 +49029,63 @@ var rstr = (s) => {
48148
49029
  if (isRuntimeChar(s)) return s.value;
48149
49030
  throw new RuntimeError(`Expected string or char, got ${kstr(s)}`);
48150
49031
  };
49032
+ function emptyStackField() {
49033
+ return RTV.structArray(["file", "name", "line"], []);
49034
+ }
49035
+ function buildStackField(e) {
49036
+ const fieldNames = ["file", "name", "line"];
49037
+ const frames = e.callStack;
49038
+ if (!frames || frames.length === 0) {
49039
+ if (e.file || e.line !== null) {
49040
+ return RTV.structArray(fieldNames, [
49041
+ RTV.struct(
49042
+ /* @__PURE__ */ new Map([
49043
+ ["file", RTV.char(e.file ?? "")],
49044
+ ["name", RTV.char("")],
49045
+ ["line", e.line ?? 0]
49046
+ ])
49047
+ )
49048
+ ]);
49049
+ }
49050
+ return emptyStackField();
49051
+ }
49052
+ const elements = [];
49053
+ const N = frames.length;
49054
+ for (let i = N - 1; i >= 0; i--) {
49055
+ let frameFile;
49056
+ let frameLine;
49057
+ if (i === N - 1) {
49058
+ frameFile = e.file;
49059
+ frameLine = e.line ?? 0;
49060
+ } else {
49061
+ const callerFrame = frames[i + 1];
49062
+ frameFile = callerFrame.callerFile;
49063
+ frameLine = callerFrame.callerLine;
49064
+ }
49065
+ elements.push(
49066
+ RTV.struct(
49067
+ /* @__PURE__ */ new Map([
49068
+ ["file", RTV.char(frameFile ?? "")],
49069
+ ["name", RTV.char(frames[i].name)],
49070
+ ["line", frameLine]
49071
+ ])
49072
+ )
49073
+ );
49074
+ }
49075
+ const outermost = frames[0];
49076
+ if (outermost.callerFile && outermost.callerLine > 0) {
49077
+ elements.push(
49078
+ RTV.struct(
49079
+ /* @__PURE__ */ new Map([
49080
+ ["file", RTV.char(outermost.callerFile)],
49081
+ ["name", RTV.char("")],
49082
+ ["line", outermost.callerLine]
49083
+ ])
49084
+ )
49085
+ );
49086
+ }
49087
+ return RTV.structArray(fieldNames, elements);
49088
+ }
48151
49089
 
48152
49090
  // src/numbl-core/jsUserFunctions.ts
48153
49091
  function funcNameFromFile(fileName) {
@@ -50541,6 +51479,9 @@ function execStmt(stmt) {
50541
51479
  case "ExprStmt": {
50542
51480
  const val = this.evalExprNargout(stmt.expr, 0);
50543
51481
  const singleVal = Array.isArray(val) ? val[0] : val;
51482
+ if (singleVal === void 0) {
51483
+ return null;
51484
+ }
50544
51485
  const rv = ensureRuntimeValue(singleVal);
50545
51486
  this.ans = rv;
50546
51487
  this.env.set("ans", rv);
@@ -50564,19 +51505,25 @@ function execStmt(stmt) {
50564
51505
  if (stmt.lvalues.length === 1 && stmt.lvalues[0].type === "IndexCell") {
50565
51506
  const lv = stmt.lvalues[0];
50566
51507
  const cellBase = lv.base.type === "Ident" ? this.env.get(lv.base.name) ?? RTV.cell([], [0, 0]) : this.evalExpr(lv.base);
50567
- const indices = lv.indices.map((idx) => this.evalExpr(idx));
50568
- const idxVal = ensureRuntimeValue(indices[0]);
51508
+ const indices = this.evalIndicesWithEnd(cellBase, lv.indices);
50569
51509
  let expandedCount = 1;
50570
- if (isRuntimeTensor(idxVal)) {
50571
- expandedCount = idxVal.data.length;
50572
- } else if (typeof idxVal === "number") {
50573
- expandedCount = 1;
51510
+ const idx0 = indices[0];
51511
+ if (idx0 === COLON_SENTINEL) {
51512
+ const baseRv = ensureRuntimeValue(cellBase);
51513
+ expandedCount = isRuntimeCell(baseRv) ? baseRv.data.length : 0;
51514
+ } else {
51515
+ const idxVal = ensureRuntimeValue(idx0);
51516
+ if (isRuntimeTensor(idxVal)) {
51517
+ expandedCount = idxVal.data.length;
51518
+ } else if (typeof idxVal === "number") {
51519
+ expandedCount = 1;
51520
+ }
50574
51521
  }
50575
51522
  const val2 = this.evalExprNargout(stmt.expr, expandedCount);
50576
51523
  const values2 = Array.isArray(val2) ? val2 : [val2];
50577
51524
  const result = this.rt.multiOutputCellAssign(
50578
51525
  cellBase,
50579
- indices[0],
51526
+ idx0,
50580
51527
  values2.map((v) => ensureRuntimeValue(v))
50581
51528
  );
50582
51529
  if (lv.base.type === "Ident") {
@@ -51138,7 +52085,11 @@ function evalAnonFunc(expr) {
51138
52085
  capturedMethodName,
51139
52086
  () => {
51140
52087
  try {
51141
- return this.evalExprNargout(bodyExpr, narg);
52088
+ const result = this.evalExprNargout(bodyExpr, narg);
52089
+ if (narg > 1 && !(Array.isArray(result) && result.length >= narg)) {
52090
+ throw new RuntimeError("Too many output arguments.");
52091
+ }
52092
+ return result;
51142
52093
  } finally {
51143
52094
  this.env = savedEnv;
51144
52095
  }
@@ -51618,6 +52569,17 @@ register("exist", (ctx, args) => {
51618
52569
  }
51619
52570
  return FALL_THROUGH;
51620
52571
  });
52572
+ register("which", (ctx, args) => {
52573
+ if (args.length < 1) return FALL_THROUGH;
52574
+ const nameArg = toString(ensureRuntimeValue(args[0]));
52575
+ if (ctx.env.has(nameArg)) return RTV.char("variable");
52576
+ const filePath = ctx.lookupWorkspaceFile(nameArg);
52577
+ if (filePath) return RTV.char(filePath);
52578
+ if (ctx.rt.builtins[nameArg] || getIBuiltin(nameArg)) {
52579
+ return RTV.char("built-in");
52580
+ }
52581
+ return RTV.char("");
52582
+ });
51621
52583
  register("isfolder", (ctx, args) => {
51622
52584
  if (args.length < 1) return FALL_THROUGH;
51623
52585
  const fio = ctx.rt.fileIO;
@@ -51720,6 +52682,9 @@ register("run", (ctx, args) => {
51720
52682
  } catch {
51721
52683
  throw new RuntimeError(`Cannot change directory to '${scriptDir}'`);
51722
52684
  }
52685
+ if (ctx.rt.onCwdChange) {
52686
+ ctx.rt.onCwdChange(sys.cwd());
52687
+ }
51723
52688
  }
51724
52689
  const cwdAfterCd = sys?.cwd() ?? "/";
51725
52690
  try {
@@ -51731,6 +52696,9 @@ register("run", (ctx, args) => {
51731
52696
  sys.chdir(oldCwd);
51732
52697
  } catch {
51733
52698
  }
52699
+ if (ctx.rt.onCwdChange) {
52700
+ ctx.rt.onCwdChange(sys.cwd());
52701
+ }
51734
52702
  }
51735
52703
  }
51736
52704
  return void 0;
@@ -51750,7 +52718,14 @@ function callFunction(name, args, nargout) {
51750
52718
  workspaceEnv: this.workspaceEnv,
51751
52719
  evalInLocalScope: (codeArg, fileName) => this.evalInLocalScope(codeArg, fileName),
51752
52720
  callFunction: (n, a, no) => this.callFunction(n, a, no),
51753
- rt: this.rt
52721
+ rt: this.rt,
52722
+ lookupWorkspaceFile: (n) => {
52723
+ const entry = this.ctx.registry.filesByFuncName.get(n);
52724
+ if (entry) return entry.fileName;
52725
+ const classInfo = this.ctx.getClassInfo(n);
52726
+ if (classInfo) return classInfo.fileName;
52727
+ return void 0;
52728
+ }
51754
52729
  };
51755
52730
  const result = specialHandler(ctx, args, nargout);
51756
52731
  if (result !== FALL_THROUGH) return result;
@@ -51784,13 +52759,18 @@ function interpretTarget(target, args, nargout) {
51784
52759
  const argTypes = margs.map(inferJitType);
51785
52760
  const resolution = ib.resolve(argTypes, nargout);
51786
52761
  if (resolution) {
52762
+ const isVoid = resolution.outputTypes.length === 0;
52763
+ if (isVoid && nargout > 0) {
52764
+ throw new RuntimeError("Too many output arguments.");
52765
+ }
51787
52766
  if (this.rt.profilingEnabled) {
51788
52767
  this.rt.profileEnter("builtin:interp:" + target.name);
51789
- const result = resolution.apply(margs, nargout);
52768
+ const result2 = resolution.apply(margs, nargout);
51790
52769
  this.rt.profileLeave();
51791
- return result;
52770
+ return isVoid ? void 0 : result2;
51792
52771
  }
51793
- return resolution.apply(margs, nargout);
52772
+ const result = resolution.apply(margs, nargout);
52773
+ return isVoid ? void 0 : result;
51794
52774
  }
51795
52775
  }
51796
52776
  const builtin = this.rt.builtins[target.name];
@@ -51882,7 +52862,19 @@ function interpretLocalFunction(target, args, nargout) {
51882
52862
  function interpretWorkspaceFunction(target, args, nargout) {
51883
52863
  const dotIdx = target.name.lastIndexOf(".");
51884
52864
  const primaryName = dotIdx >= 0 ? target.name.slice(dotIdx + 1) : target.name;
51885
- const fn = this.findFunctionInWorkspaceFile(target.name, primaryName);
52865
+ let fn = this.findFunctionInWorkspaceFile(target.name, primaryName);
52866
+ if (!fn) {
52867
+ const entry = this.ctx.registry.filesByFuncName.get(target.name);
52868
+ if (entry) {
52869
+ const ast = this.ctx.getCachedAST(entry.fileName);
52870
+ for (const stmt of ast.body) {
52871
+ if (stmt.type === "Function") {
52872
+ fn = funcDefFromStmt(stmt);
52873
+ break;
52874
+ }
52875
+ }
52876
+ }
52877
+ }
51886
52878
  if (!fn) {
51887
52879
  const entry = this.ctx.registry.filesByFuncName.get(target.name);
51888
52880
  if (entry) {
@@ -52026,6 +53018,11 @@ function interpretConstructor(classInfo, args, nargout) {
52026
53018
  return args[0];
52027
53019
  }
52028
53020
  function callUserFunction(fn, args, nargout, narginOverride) {
53021
+ const hasVarargoutDecl = fn.outputs.length > 0 && fn.outputs[fn.outputs.length - 1] === "varargout";
53022
+ const declaredRegularOutputs = hasVarargoutDecl ? fn.outputs.length - 1 : fn.outputs.length;
53023
+ if (!hasVarargoutDecl && nargout > declaredRegularOutputs) {
53024
+ throw new RuntimeError("Too many output arguments.");
53025
+ }
52029
53026
  if (this.optimization >= 1 && narginOverride === void 0) {
52030
53027
  const jitResult = tryJitCall(this, fn, args, nargout);
52031
53028
  if (jitResult !== JIT_SKIP) return jitResult;
@@ -52075,7 +53072,7 @@ function callUserFunction(fn, args, nargout, narginOverride) {
52075
53072
  }
52076
53073
  }
52077
53074
  }
52078
- const hasVarargout = fn.outputs.length > 0 && fn.outputs[fn.outputs.length - 1] === "varargout";
53075
+ const hasVarargout = hasVarargoutDecl;
52079
53076
  const regularOutputs = hasVarargout ? fn.outputs.slice(0, -1) : fn.outputs;
52080
53077
  const collectCount = nargout === 0 && regularOutputs.length > 0 ? 1 : Math.min(regularOutputs.length, nargout);
52081
53078
  const outputs = [];
@@ -53680,6 +54677,31 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
53680
54677
  }
53681
54678
  }
53682
54679
  }
54680
+ let implicitCwdPath = null;
54681
+ if (options.implicitCwdPath !== null && options.system && options.fileIO?.scanDirectory) {
54682
+ try {
54683
+ const cwd = options.implicitCwdPath ?? options.system.cwd();
54684
+ const absCwd = options.fileIO.resolvePath?.(cwd) ?? cwd;
54685
+ const explicitPaths = searchPaths ?? [];
54686
+ if (!explicitPaths.includes(absCwd)) {
54687
+ implicitCwdPath = absCwd;
54688
+ const prefix = absCwd.endsWith("/") ? absCwd : absCwd + "/";
54689
+ const alreadyHave = mWorkspaceFiles.some(
54690
+ (f) => f.name === absCwd || f.name.startsWith(prefix)
54691
+ );
54692
+ if (!alreadyHave) {
54693
+ const cwdFiles = options.fileIO.scanDirectory(absCwd);
54694
+ for (const f of cwdFiles) {
54695
+ if (f.name.endsWith(".m")) {
54696
+ mWorkspaceFiles.push(f);
54697
+ }
54698
+ }
54699
+ }
54700
+ }
54701
+ } catch {
54702
+ implicitCwdPath = null;
54703
+ }
54704
+ }
53683
54705
  const jsUserFunctions = loadJsUserFunctions(
53684
54706
  jsWorkspaceFiles,
53685
54707
  wasmWorkspaceFiles,
@@ -53697,6 +54719,9 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
53697
54719
  stdlibShimNames.add(shimName);
53698
54720
  }
53699
54721
  ctx.registry.searchPaths = [...searchPaths ?? [], SHIM_SEARCH_PATH];
54722
+ if (implicitCwdPath !== null) {
54723
+ ctx.registry.searchPaths.unshift(implicitCwdPath);
54724
+ }
53700
54725
  ctx.fileASTCache.set(mainFileName, ast);
53701
54726
  const skippedFiles = /* @__PURE__ */ new Set();
53702
54727
  for (const f of mWorkspaceFiles) {
@@ -53730,6 +54755,11 @@ function executeCode(source, options = {}, workspaceFiles, mainFileName = "scrip
53730
54755
  ctx.registerWorkspaceFiles(mWorkspaceFiles);
53731
54756
  }
53732
54757
  const functionIndex = ctx.buildFunctionIndex(jsUserFunctionNames);
54758
+ const savedSpecialBuiltins = /* @__PURE__ */ new Map();
54759
+ for (const name of SPECIAL_BUILTIN_NAMES) {
54760
+ const existing = getIBuiltin(name);
54761
+ if (existing) savedSpecialBuiltins.set(name, existing);
54762
+ }
53733
54763
  const rt = new Runtime(options, options.initialVariableValues);
53734
54764
  const savedIBuiltins = /* @__PURE__ */ new Map();
53735
54765
  for (const ib of jsUserFunctions) {
@@ -53771,13 +54801,39 @@ ${jsCode}`
53771
54801
  interpreter.installRuntimeCallbacks();
53772
54802
  rt.searchPaths = ctx.registry.searchPaths;
53773
54803
  let pathsModified = false;
54804
+ const rebuildWorkspace = () => {
54805
+ const paths = ctx.registry.searchPaths;
54806
+ const priorityOf = (name) => {
54807
+ let bestIdx = paths.length;
54808
+ let bestLen = -1;
54809
+ for (let i = 0; i < paths.length; i++) {
54810
+ const p2 = paths[i];
54811
+ const withSep = p2.endsWith("/") ? p2 : p2 + "/";
54812
+ if (name === p2 || name.startsWith(withSep)) {
54813
+ if (p2.length > bestLen) {
54814
+ bestLen = p2.length;
54815
+ bestIdx = i;
54816
+ }
54817
+ }
54818
+ }
54819
+ return bestIdx;
54820
+ };
54821
+ mWorkspaceFiles.sort((a, b) => priorityOf(a.name) - priorityOf(b.name));
54822
+ ctx.clearWorkspaceRegistrations();
54823
+ ctx.registerWorkspaceFiles(mWorkspaceFiles);
54824
+ const newIndex = ctx.buildFunctionIndex(jsUserFunctionNames);
54825
+ interpreter.functionIndex = newIndex;
54826
+ interpreter.clearAllCaches();
54827
+ pathsModified = true;
54828
+ };
53774
54829
  rt.onPathChange = (action, dir, position) => {
53775
54830
  const fileIO = options.fileIO;
53776
54831
  const absDir = fileIO?.resolvePath?.(dir) ?? dir;
53777
54832
  if (action === "add") {
53778
54833
  if (ctx.registry.searchPaths.includes(absDir)) return;
53779
54834
  if (position === "begin") {
53780
- ctx.registry.searchPaths.unshift(absDir);
54835
+ const cwdIsFirst = implicitCwdPath !== null && ctx.registry.searchPaths[0] === implicitCwdPath;
54836
+ ctx.registry.searchPaths.splice(cwdIsFirst ? 1 : 0, 0, absDir);
53781
54837
  } else {
53782
54838
  const shimIdx = ctx.registry.searchPaths.indexOf(SHIM_SEARCH_PATH);
53783
54839
  if (shimIdx >= 0) {
@@ -53795,10 +54851,14 @@ ${jsCode}`
53795
54851
  try {
53796
54852
  ctx.fileASTCache.set(f.name, parseMFile(f.source, f.name));
53797
54853
  } catch (e) {
53798
- if (e instanceof SyntaxError && e.file === null) {
53799
- e.file = f.name;
54854
+ if (e instanceof SyntaxError) {
54855
+ console.warn(
54856
+ `Warning: skipping ${f.name} (syntax error at line ${e.line ?? "?"})`
54857
+ );
54858
+ } else {
54859
+ console.warn(`Warning: skipping ${f.name} (parse error)`);
53800
54860
  }
53801
- throw e;
54861
+ continue;
53802
54862
  }
53803
54863
  interpreter.fileSources.set(f.name, f.source);
53804
54864
  mWorkspaceFiles.push(f);
@@ -53838,26 +54898,76 @@ ${jsCode}`
53838
54898
  }
53839
54899
  }
53840
54900
  }
53841
- const paths = ctx.registry.searchPaths;
53842
- mWorkspaceFiles.sort((a, b) => {
53843
- const ai = paths.findIndex(
53844
- (p2) => a.name.startsWith(p2.endsWith("/") ? p2 : p2 + "/") || a.name === p2
53845
- );
53846
- const bi = paths.findIndex(
53847
- (p2) => b.name.startsWith(p2.endsWith("/") ? p2 : p2 + "/") || b.name === p2
54901
+ rebuildWorkspace();
54902
+ };
54903
+ rt.onCwdChange = (newCwd) => {
54904
+ const fileIO = options.fileIO;
54905
+ const absNewCwd = fileIO?.resolvePath?.(newCwd) ?? newCwd;
54906
+ if (implicitCwdPath === absNewCwd) return;
54907
+ if (implicitCwdPath !== null) {
54908
+ const oldPrefix = implicitCwdPath.endsWith("/") ? implicitCwdPath : implicitCwdPath + "/";
54909
+ const deeperPaths = ctx.registry.searchPaths.filter(
54910
+ (p2) => p2 !== implicitCwdPath && p2 !== SHIM_SEARCH_PATH && (p2 === implicitCwdPath || p2.startsWith(oldPrefix))
53848
54911
  );
53849
- const aPri = ai >= 0 ? ai : paths.length;
53850
- const bPri = bi >= 0 ? bi : paths.length;
53851
- return aPri - bPri;
53852
- });
53853
- ctx.clearWorkspaceRegistrations();
53854
- ctx.registerWorkspaceFiles(mWorkspaceFiles);
53855
- const newIndex = ctx.buildFunctionIndex(jsUserFunctionNames);
53856
- interpreter.functionIndex = newIndex;
53857
- interpreter.clearAllCaches();
53858
- pathsModified = true;
54912
+ const fileBelongsToDeeperPath = (fname) => {
54913
+ for (const p2 of deeperPaths) {
54914
+ const withSep = p2.endsWith("/") ? p2 : p2 + "/";
54915
+ if (fname === p2 || fname.startsWith(withSep)) return true;
54916
+ }
54917
+ return false;
54918
+ };
54919
+ for (let i = mWorkspaceFiles.length - 1; i >= 0; i--) {
54920
+ const fname = mWorkspaceFiles[i].name;
54921
+ if ((fname === implicitCwdPath || fname.startsWith(oldPrefix)) && !fileBelongsToDeeperPath(fname)) {
54922
+ ctx.fileASTCache.delete(fname);
54923
+ interpreter.fileSources.delete(fname);
54924
+ mWorkspaceFiles.splice(i, 1);
54925
+ }
54926
+ }
54927
+ const oldIdx = ctx.registry.searchPaths.indexOf(implicitCwdPath);
54928
+ if (oldIdx >= 0) ctx.registry.searchPaths.splice(oldIdx, 1);
54929
+ implicitCwdPath = null;
54930
+ }
54931
+ if (ctx.registry.searchPaths.includes(absNewCwd)) {
54932
+ rebuildWorkspace();
54933
+ return;
54934
+ }
54935
+ ctx.registry.searchPaths.unshift(absNewCwd);
54936
+ implicitCwdPath = absNewCwd;
54937
+ if (fileIO?.scanDirectory) {
54938
+ let newFiles = [];
54939
+ try {
54940
+ newFiles = fileIO.scanDirectory(absNewCwd);
54941
+ } catch {
54942
+ }
54943
+ for (const f of newFiles) {
54944
+ if (f.name.endsWith(".m") && !ctx.fileASTCache.has(f.name)) {
54945
+ try {
54946
+ ctx.fileASTCache.set(f.name, parseMFile(f.source, f.name));
54947
+ } catch (e) {
54948
+ if (e instanceof SyntaxError) {
54949
+ console.warn(
54950
+ `Warning: skipping ${f.name} (syntax error at line ${e.line ?? "?"})`
54951
+ );
54952
+ } else {
54953
+ console.warn(`Warning: skipping ${f.name} (parse error)`);
54954
+ }
54955
+ continue;
54956
+ }
54957
+ interpreter.fileSources.set(f.name, f.source);
54958
+ mWorkspaceFiles.push(f);
54959
+ }
54960
+ }
54961
+ }
54962
+ rebuildWorkspace();
53859
54963
  };
53860
54964
  rt.evalLocalCallback = (code, initialVars, onOutput, fileName) => {
54965
+ const nestedSearchPaths = ctx.registry.searchPaths.filter(
54966
+ (p2) => p2 !== SHIM_SEARCH_PATH && p2 !== implicitCwdPath
54967
+ );
54968
+ const nestedWorkspaceFiles = mWorkspaceFiles.filter(
54969
+ (f) => !stdlibShimNames.has(f.name)
54970
+ );
53861
54971
  const evalResult = executeCode(
53862
54972
  code,
53863
54973
  {
@@ -53865,11 +54975,18 @@ ${jsCode}`
53865
54975
  displayResults: false,
53866
54976
  initialVariableValues: initialVars,
53867
54977
  fileIO: options.fileIO,
53868
- onInput: options.onInput
54978
+ system: options.system,
54979
+ onInput: options.onInput,
54980
+ implicitCwdPath
53869
54981
  },
53870
- void 0,
53871
- fileName
54982
+ nestedWorkspaceFiles,
54983
+ fileName,
54984
+ nestedSearchPaths,
54985
+ nativeBridge
53872
54986
  );
54987
+ if (evalResult.implicitCwdPath !== void 0) {
54988
+ implicitCwdPath = evalResult.implicitCwdPath;
54989
+ }
53873
54990
  return {
53874
54991
  returnValue: evalResult.returnValue,
53875
54992
  variableValues: evalResult.variableValues,
@@ -53901,12 +55018,13 @@ ${jitSections.join("\n\n")}` : "// No JS generated",
53901
55018
  }
53902
55019
  if (pathsModified) {
53903
55020
  result.searchPaths = ctx.registry.searchPaths.filter(
53904
- (p2) => p2 !== SHIM_SEARCH_PATH
55021
+ (p2) => p2 !== SHIM_SEARCH_PATH && p2 !== implicitCwdPath
53905
55022
  );
53906
55023
  result.workspaceFiles = mWorkspaceFiles.filter(
53907
55024
  (f) => !stdlibShimNames.has(f.name)
53908
55025
  );
53909
55026
  }
55027
+ result.implicitCwdPath = implicitCwdPath;
53910
55028
  return result;
53911
55029
  } catch (e) {
53912
55030
  const generatedJS = jitSections.length > 0 ? `// Interpreter mode \u2014 JIT compiled sections:
@@ -53942,6 +55060,9 @@ ${jitSections.join("\n\n")}` : "// No JS generated";
53942
55060
  unregisterIBuiltin(ib.name);
53943
55061
  }
53944
55062
  }
55063
+ for (const [, ib] of savedSpecialBuiltins) {
55064
+ registerDynamicIBuiltin(ib);
55065
+ }
53945
55066
  }
53946
55067
  }
53947
55068
  export {