numbl 0.0.13 → 0.0.15

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/README.md CHANGED
@@ -8,6 +8,10 @@ Numbl is an open-source numerical computing environment that aims to be compatib
8
8
 
9
9
  You can try numbl directly in the browser at <https://numbl.org> — no installation required. All execution happens locally in your browser. Note that the browser version has limited functionality and is slower than the desktop/command-line version.
10
10
 
11
+ ## Embedding in web pages
12
+
13
+ Numbl scripts can be embedded in HTML and Markdown pages (including GitHub Pages). See the [numbl-embed-example](https://magland.github.io/numbl-embed-example/) for usage info and a [live demo](https://magland.github.io/numbl-embed-example/example1).
14
+
11
15
  ## Installation
12
16
 
13
17
  ```bash
@@ -50,4 +54,4 @@ Apache 2.0.
50
54
 
51
55
  ## Acknowledgements
52
56
 
53
- See [ACKNOWLEDGEMENTS.md](ACKNOWLEDGEMENTS.md).
57
+ See [acknowledgements.md](docs/acknowledgements.md).
package/dist-cli/cli.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  writeFileSync as writeFileSync3,
10
10
  appendFileSync as appendFileSync2
11
11
  } from "fs";
12
- import { delimiter, dirname as dirname3, join as join5, relative, resolve } from "path";
12
+ import { delimiter, dirname as dirname3, join as join6, relative, resolve } from "path";
13
13
  import { fileURLToPath as fileURLToPath2 } from "url";
14
14
  import { createRequire } from "module";
15
15
  import { execSync as execSync2 } from "child_process";
@@ -21437,7 +21437,11 @@ var ExpressionParser = class extends ParserBase {
21437
21437
  const nameSpan = this.spanFrom(nameToken.position, nameToken.end);
21438
21438
  this.pos++;
21439
21439
  this.pos++;
21440
- args.push({ type: "Char", value: `'${nameToken.lexeme}'`, span: nameSpan });
21440
+ args.push({
21441
+ type: "Char",
21442
+ value: `'${nameToken.lexeme}'`,
21443
+ span: nameSpan
21444
+ });
21441
21445
  args.push(this.parseExpr());
21442
21446
  } else {
21443
21447
  args.push(this.parseExpr());
@@ -25093,6 +25097,125 @@ function registerMathFunctions() {
25093
25097
  return buildResult(snArr, cnArr, dnArr, uT.shape);
25094
25098
  })
25095
25099
  );
25100
+ register(
25101
+ "legendre",
25102
+ builtinSingle((args) => {
25103
+ if (args.length < 2 || args.length > 3) {
25104
+ throw new RuntimeError("legendre requires 2 or 3 arguments");
25105
+ }
25106
+ const n = Math.round(toNumber(args[0]));
25107
+ if (n < 0 || !isFinite(n)) {
25108
+ throw new RuntimeError("Degree n must be a non-negative integer");
25109
+ }
25110
+ let normalization = "unnorm";
25111
+ if (args.length === 3) {
25112
+ const normArg = args[2];
25113
+ if (isRuntimeChar(normArg)) {
25114
+ normalization = normArg.value;
25115
+ } else if (typeof normArg === "string") {
25116
+ normalization = normArg;
25117
+ } else {
25118
+ throw new RuntimeError(
25119
+ "Third argument must be a normalization string"
25120
+ );
25121
+ }
25122
+ if (normalization !== "unnorm" && normalization !== "sch" && normalization !== "norm") {
25123
+ throw new RuntimeError(
25124
+ "Normalization must be 'unnorm', 'sch', or 'norm'"
25125
+ );
25126
+ }
25127
+ }
25128
+ const xArg = args[1];
25129
+ let xValues;
25130
+ let xShape;
25131
+ if (isRuntimeNumber(xArg)) {
25132
+ xValues = [xArg];
25133
+ xShape = [1, 1];
25134
+ } else if (isRuntimeLogical(xArg)) {
25135
+ xValues = [xArg ? 1 : 0];
25136
+ xShape = [1, 1];
25137
+ } else if (isRuntimeTensor(xArg)) {
25138
+ const t = xArg;
25139
+ xValues = Array.from(t.data);
25140
+ xShape = t.shape;
25141
+ } else {
25142
+ throw new RuntimeError("X must be a numeric value");
25143
+ }
25144
+ const numX = xValues.length;
25145
+ const numOrders = n + 1;
25146
+ const outSize = numOrders * numX;
25147
+ const result = new FloatXArray(outSize);
25148
+ for (let xi = 0; xi < numX; xi++) {
25149
+ const x = xValues[xi];
25150
+ const pmn = legendreAllOrders(n, x);
25151
+ for (let m = 0; m <= n; m++) {
25152
+ let val = pmn[m];
25153
+ if (normalization === "sch") {
25154
+ if (m > 0) {
25155
+ const scale = Math.pow(-1, m) * Math.sqrt(2 * factorialVal(n - m) / factorialVal(n + m));
25156
+ val = scale * val;
25157
+ }
25158
+ } else if (normalization === "norm") {
25159
+ const scale = Math.pow(-1, m) * Math.sqrt(
25160
+ (n + 0.5) * factorialVal(n - m) / factorialVal(n + m)
25161
+ );
25162
+ val = scale * val;
25163
+ }
25164
+ result[xi * numOrders + m] = val;
25165
+ }
25166
+ }
25167
+ if (isRuntimeNumber(xArg) || isRuntimeLogical(xArg)) {
25168
+ return RTV.tensor(result, [numOrders, 1]);
25169
+ }
25170
+ if (xShape.length === 2 && (xShape[0] === 1 || xShape[1] === 1)) {
25171
+ return RTV.tensor(result, [numOrders, numX]);
25172
+ }
25173
+ return RTV.tensor(result, [numOrders, ...xShape]);
25174
+ })
25175
+ );
25176
+ }
25177
+ function factorialVal(n) {
25178
+ if (n <= 1) return 1;
25179
+ let r = 1;
25180
+ for (let i = 2; i <= n; i++) r *= i;
25181
+ return r;
25182
+ }
25183
+ function legendreAllOrders(n, x) {
25184
+ const result = new Array(n + 1);
25185
+ if (n === 0) {
25186
+ result[0] = 1;
25187
+ return result;
25188
+ }
25189
+ const sqrtFactor = Math.sqrt(1 - x * x);
25190
+ for (let m = 0; m <= n; m++) {
25191
+ let pmm = 1;
25192
+ if (m > 0) {
25193
+ let dblFact = 1;
25194
+ for (let i = 1; i <= m; i++) {
25195
+ dblFact *= 2 * i - 1;
25196
+ }
25197
+ pmm = Math.pow(-1, m) * dblFact * Math.pow(sqrtFactor, m);
25198
+ }
25199
+ if (m === n) {
25200
+ result[m] = pmm;
25201
+ continue;
25202
+ }
25203
+ const pmm1 = x * (2 * m + 1) * pmm;
25204
+ if (m + 1 === n) {
25205
+ result[m] = pmm1;
25206
+ continue;
25207
+ }
25208
+ let pPrev2 = pmm;
25209
+ let pPrev1 = pmm1;
25210
+ let pCurr = 0;
25211
+ for (let l = m + 2; l <= n; l++) {
25212
+ pCurr = (x * (2 * l - 1) * pPrev1 - (l + m - 1) * pPrev2) / (l - m);
25213
+ pPrev2 = pPrev1;
25214
+ pPrev1 = pCurr;
25215
+ }
25216
+ result[m] = pCurr;
25217
+ }
25218
+ return result;
25096
25219
  }
25097
25220
 
25098
25221
  // src/numbl-core/builtins/shape-utils.ts
@@ -34118,6 +34241,22 @@ function registerGraphicsFunctions() {
34118
34241
  "close",
34119
34242
  builtinSingle(() => RTV.num(0))
34120
34243
  );
34244
+ register(
34245
+ "title",
34246
+ builtinSingle(() => RTV.num(0))
34247
+ );
34248
+ register(
34249
+ "xlabel",
34250
+ builtinSingle(() => RTV.num(0))
34251
+ );
34252
+ register(
34253
+ "ylabel",
34254
+ builtinSingle(() => RTV.num(0))
34255
+ );
34256
+ register(
34257
+ "shading",
34258
+ builtinSingle(() => RTV.num(0))
34259
+ );
34121
34260
  register(
34122
34261
  "drawnow",
34123
34262
  builtinSingle(() => RTV.num(0))
@@ -38191,6 +38330,19 @@ function plotInstr(plotInstructions, instr) {
38191
38330
  on = toString(mv) === "on";
38192
38331
  }
38193
38332
  plotInstructions.push({ type: "set_hold", value: on });
38333
+ } else if (instr.type === "set_title") {
38334
+ const text = typeof instr.text === "string" ? instr.text : toString(ensureRuntimeValue(instr.text));
38335
+ plotInstructions.push({ type: "set_title", text });
38336
+ } else if (instr.type === "set_xlabel") {
38337
+ const text = typeof instr.text === "string" ? instr.text : toString(ensureRuntimeValue(instr.text));
38338
+ plotInstructions.push({ type: "set_xlabel", text });
38339
+ } else if (instr.type === "set_ylabel") {
38340
+ const text = typeof instr.text === "string" ? instr.text : toString(ensureRuntimeValue(instr.text));
38341
+ plotInstructions.push({ type: "set_ylabel", text });
38342
+ } else if (instr.type === "set_shading") {
38343
+ const raw = typeof instr.shading === "string" ? instr.shading : toString(ensureRuntimeValue(instr.shading));
38344
+ const shading = raw.replace(/^'|'$/g, "");
38345
+ plotInstructions.push({ type: "set_shading", shading });
38194
38346
  } else if (instr.type === "close") {
38195
38347
  plotInstructions.push({ type: "close" });
38196
38348
  } else if (instr.type === "close_all") {
@@ -39147,6 +39299,61 @@ function getEffectiveDir(fileName, searchPaths) {
39147
39299
  }
39148
39300
  return parts.length > 0 ? parts.join("/") + "/" : "";
39149
39301
  }
39302
+ function resolveViaImports(name, argTypes, callSite, index2, wildcardOnly) {
39303
+ const imports = index2.fileImports.get(callSite.file);
39304
+ if (!imports) return null;
39305
+ for (const imp of imports) {
39306
+ if (imp.wildcard !== wildcardOnly) continue;
39307
+ if (imp.wildcard) {
39308
+ const candidateName = `${imp.namespace}.${name}`;
39309
+ if (index2.workspaceFunctions.has(candidateName)) {
39310
+ return { kind: "workspaceFunction", name: candidateName, argTypes };
39311
+ }
39312
+ if (index2.workspaceClasses.has(candidateName)) {
39313
+ return {
39314
+ kind: "workspaceClassConstructor",
39315
+ className: candidateName,
39316
+ argTypes
39317
+ };
39318
+ }
39319
+ if (index2.classStaticMethods.get(imp.namespace)?.has(name)) {
39320
+ return {
39321
+ kind: "classMethod",
39322
+ className: imp.namespace,
39323
+ methodName: name,
39324
+ compileArgTypes: argTypes,
39325
+ stripInstance: false
39326
+ };
39327
+ }
39328
+ } else {
39329
+ if (name !== imp.shortName) continue;
39330
+ if (imp.staticMethod) {
39331
+ return {
39332
+ kind: "classMethod",
39333
+ className: imp.staticMethod.className,
39334
+ methodName: imp.staticMethod.methodName,
39335
+ compileArgTypes: argTypes,
39336
+ stripInstance: false
39337
+ };
39338
+ }
39339
+ if (index2.workspaceFunctions.has(imp.qualifiedName)) {
39340
+ return {
39341
+ kind: "workspaceFunction",
39342
+ name: imp.qualifiedName,
39343
+ argTypes
39344
+ };
39345
+ }
39346
+ if (index2.workspaceClasses.has(imp.qualifiedName)) {
39347
+ return {
39348
+ kind: "workspaceClassConstructor",
39349
+ className: imp.qualifiedName,
39350
+ argTypes
39351
+ };
39352
+ }
39353
+ }
39354
+ }
39355
+ return null;
39356
+ }
39150
39357
  function resolveFunction(name, argTypes, callSite, index2) {
39151
39358
  if (callSite.targetClassName) {
39152
39359
  const className = callSite.targetClassName;
@@ -39161,6 +39368,10 @@ function resolveFunction(name, argTypes, callSite, index2) {
39161
39368
  stripInstance
39162
39369
  };
39163
39370
  }
39371
+ {
39372
+ const imported = resolveViaImports(name, argTypes, callSite, index2, false);
39373
+ if (imported) return imported;
39374
+ }
39164
39375
  if (callSite.className) {
39165
39376
  const className = callSite.className;
39166
39377
  const isClassMethod = index2.classInstanceMethods.get(className)?.has(name) || index2.classConstructors.get(className) === name || index2.classStaticMethods.get(className)?.has(name);
@@ -39209,6 +39420,10 @@ function resolveFunction(name, argTypes, callSite, index2) {
39209
39420
  }
39210
39421
  }
39211
39422
  }
39423
+ {
39424
+ const imported = resolveViaImports(name, argTypes, callSite, index2, true);
39425
+ if (imported) return imported;
39426
+ }
39212
39427
  if (callSite.file) {
39213
39428
  const dir = getEffectiveDir(callSite.file, index2.searchPaths);
39214
39429
  if (index2.privateFunctions.get(dir)?.has(name)) {
@@ -39263,6 +39478,9 @@ function resolveFunction(name, argTypes, callSite, index2) {
39263
39478
  argTypes
39264
39479
  };
39265
39480
  }
39481
+ if (index2.jsUserFunctions.has(name)) {
39482
+ return { kind: "builtin", name };
39483
+ }
39266
39484
  if (index2.workspaceClasses.has(name)) {
39267
39485
  return {
39268
39486
  kind: "workspaceClassConstructor",
@@ -39328,12 +39546,17 @@ function resolveFuncCall(ctx, name, args, nargout, span) {
39328
39546
  { type: "userFunction", functionId: `private:${name}` }
39329
39547
  ]);
39330
39548
  case "workspaceFunction":
39331
- return makeFuncCallExpr(ctx, name, args, nargout, span, [
39332
- { type: "userFunction", functionId: name }
39549
+ return makeFuncCallExpr(ctx, target.name, args, nargout, span, [
39550
+ { type: "userFunction", functionId: target.name }
39333
39551
  ]);
39334
39552
  case "workspaceClassConstructor":
39335
39553
  return {
39336
- kind: { type: "ClassInstantiation", className: name, args, nargout },
39554
+ kind: {
39555
+ type: "ClassInstantiation",
39556
+ className: target.className,
39557
+ args,
39558
+ nargout
39559
+ },
39337
39560
  span
39338
39561
  };
39339
39562
  case "builtin":
@@ -40720,8 +40943,17 @@ var LoweringContext = class _LoweringContext {
40720
40943
  * Should be called once after registerWorkspaceFiles() and registerLocalFunctionAST().
40721
40944
  * Parses all workspace files eagerly to discover subfunctions.
40722
40945
  */
40723
- buildFunctionIndex() {
40946
+ buildFunctionIndex(jsUserFunctionNames) {
40724
40947
  const builtins2 = new Set(getAllBuiltinNames());
40948
+ const jsUserFunctions = /* @__PURE__ */ new Set();
40949
+ if (jsUserFunctionNames) {
40950
+ for (const name of jsUserFunctionNames) {
40951
+ if (builtins2.has(name)) {
40952
+ builtins2.delete(name);
40953
+ jsUserFunctions.add(name);
40954
+ }
40955
+ }
40956
+ }
40725
40957
  const mainLocalFunctions = new Set(this.localFunctionASTs.keys());
40726
40958
  const workspaceFunctions = new Set(this.registry.filesByFuncName.keys());
40727
40959
  const workspaceClasses = /* @__PURE__ */ new Set([
@@ -40832,11 +41064,55 @@ var LoweringContext = class _LoweringContext {
40832
41064
  classInferiorClasses.set(className, new Set(info.inferiorClasses));
40833
41065
  }
40834
41066
  }
41067
+ const fileImports = /* @__PURE__ */ new Map();
41068
+ const collectImportsFromBody = (body, fileName) => {
41069
+ const entries = [];
41070
+ for (const stmt of body) {
41071
+ if (stmt.type !== "Import") continue;
41072
+ if (stmt.wildcard) {
41073
+ entries.push({ wildcard: true, namespace: stmt.path.join(".") });
41074
+ } else {
41075
+ const qualifiedName = stmt.path.join(".");
41076
+ const shortName = stmt.path[stmt.path.length - 1];
41077
+ const prefix = stmt.path.slice(0, -1).join(".");
41078
+ const isStaticMethod = prefix.length > 0 && classStaticMethods.get(prefix)?.has(shortName);
41079
+ entries.push({
41080
+ wildcard: false,
41081
+ qualifiedName,
41082
+ shortName,
41083
+ ...isStaticMethod ? { staticMethod: { className: prefix, methodName: shortName } } : {}
41084
+ });
41085
+ }
41086
+ }
41087
+ if (entries.length > 0) {
41088
+ fileImports.set(fileName, entries);
41089
+ }
41090
+ };
41091
+ try {
41092
+ const mainAst = this.getCachedAST(this.mainFileName);
41093
+ collectImportsFromBody(mainAst.body, this.mainFileName);
41094
+ } catch {
41095
+ }
41096
+ for (const [, entry] of this.registry.filesByFuncName) {
41097
+ const ast = this.getCachedAST(entry.fileName);
41098
+ collectImportsFromBody(ast.body, entry.fileName);
41099
+ }
41100
+ for (const [, info] of this.registry.classesByName) {
41101
+ const ast = this.getCachedAST(info.fileName);
41102
+ collectImportsFromBody(ast.body, info.fileName);
41103
+ }
41104
+ for (const [, entries] of this.registry.privateFilesByDir) {
41105
+ for (const [, entry] of entries) {
41106
+ const ast = this.getCachedAST(entry.fileName);
41107
+ collectImportsFromBody(ast.body, entry.fileName);
41108
+ }
41109
+ }
40835
41110
  const index2 = {
40836
41111
  builtins: builtins2,
40837
41112
  mainFileName: this.mainFileName,
40838
41113
  mainLocalFunctions,
40839
41114
  workspaceFunctions,
41115
+ jsUserFunctions,
40840
41116
  workspaceClasses,
40841
41117
  workspaceFileSubfunctions,
40842
41118
  classFileSubfunctions,
@@ -40847,7 +41123,8 @@ var LoweringContext = class _LoweringContext {
40847
41123
  privateFileSubfunctions,
40848
41124
  classInferiorClasses,
40849
41125
  searchPaths: this.registry.searchPaths,
40850
- fileToFuncName: this.registry.fileToFuncName
41126
+ fileToFuncName: this.registry.fileToFuncName,
41127
+ fileImports
40851
41128
  };
40852
41129
  this.registry.functionIndex = index2;
40853
41130
  return index2;
@@ -41604,6 +41881,14 @@ function genFuncCall(cg, kind) {
41604
41881
  return `$rt.plot_instr({type: "set_hold", value: ${args[0]}})`;
41605
41882
  if (kind.name === "ishold") return `$rt.ishold()`;
41606
41883
  if (kind.name === "clf") return `$rt.plot_instr({type: "clf"})`;
41884
+ if (kind.name === "title")
41885
+ return `$rt.plot_instr({type: "set_title", text: ${args[0] ?? '""'}})`;
41886
+ if (kind.name === "xlabel")
41887
+ return `$rt.plot_instr({type: "set_xlabel", text: ${args[0] ?? '""'}})`;
41888
+ if (kind.name === "ylabel")
41889
+ return `$rt.plot_instr({type: "set_ylabel", text: ${args[0] ?? '""'}})`;
41890
+ if (kind.name === "shading")
41891
+ return `$rt.plot_instr({type: "set_shading", shading: ${args[0] ?? '"faceted"'}})`;
41607
41892
  if (kind.name === "close") {
41608
41893
  if (args.length > 0) {
41609
41894
  return `$rt.plot_instr({type: "close_all"})`;
@@ -41715,13 +42000,18 @@ function genFuncCall(cg, kind) {
41715
42000
  break;
41716
42001
  }
41717
42002
  case "builtin": {
41718
- const nativeMath = tryNativeMathCodegen(
41719
- kind.name,
41720
- kind.nargout,
41721
- kind.args,
41722
- args
42003
+ const isJsUserFunc = cg.loweringCtx.functionIndex.jsUserFunctions.has(
42004
+ kind.name
41723
42005
  );
41724
- if (nativeMath !== null) return nativeMath;
42006
+ if (!isJsUserFunc) {
42007
+ const nativeMath = tryNativeMathCodegen(
42008
+ kind.name,
42009
+ kind.nargout,
42010
+ kind.args,
42011
+ args
42012
+ );
42013
+ if (nativeMath !== null) return nativeMath;
42014
+ }
41725
42015
  if (hasUnknown) {
41726
42016
  return emitDispatchUnknown(
41727
42017
  cg,
@@ -43327,8 +43617,61 @@ var defaultCheck = (_argTypes, nargout) => ({
43327
43617
  kind: "Unknown"
43328
43618
  })
43329
43619
  });
43330
- function loadJsUserFunctions(jsFiles) {
43620
+ function buildWasmMap(wasmFiles) {
43621
+ const map = /* @__PURE__ */ new Map();
43622
+ for (const f of wasmFiles) {
43623
+ if (!f.data) continue;
43624
+ const base = f.name.split("/").pop().replace(/\.wasm$/, "");
43625
+ map.set(base, f.data);
43626
+ }
43627
+ return map;
43628
+ }
43629
+ function parseWasmDirective(source) {
43630
+ const match = source.match(/^\s*\/\/\s*wasm:\s*(\S+)/);
43631
+ return match ? match[1] : null;
43632
+ }
43633
+ function instantiateWasm(wasmData) {
43634
+ const wasmModule = new WebAssembly.Module(wasmData);
43635
+ const moduleImports = WebAssembly.Module.imports(wasmModule);
43636
+ const importObject = {};
43637
+ const neededModules = new Set(moduleImports.map((i) => i.module));
43638
+ if (neededModules.has("wasi_snapshot_preview1")) {
43639
+ importObject.wasi_snapshot_preview1 = {
43640
+ fd_write: () => 0,
43641
+ fd_read: () => 0,
43642
+ fd_close: () => 0,
43643
+ fd_seek: () => 0,
43644
+ fd_fdstat_get: () => 0,
43645
+ proc_exit: () => {
43646
+ },
43647
+ environ_sizes_get: () => 0,
43648
+ environ_get: () => 0,
43649
+ clock_time_get: () => 0,
43650
+ args_sizes_get: () => 0,
43651
+ args_get: () => 0
43652
+ };
43653
+ }
43654
+ if (neededModules.has("env")) {
43655
+ importObject.env = {
43656
+ emscripten_notify_memory_growth: () => {
43657
+ }
43658
+ };
43659
+ }
43660
+ return new WebAssembly.Instance(wasmModule, importObject);
43661
+ }
43662
+ function loadJsUserFunctions(jsFiles, wasmFiles) {
43331
43663
  const result = /* @__PURE__ */ new Map();
43664
+ const wasmMap = wasmFiles ? buildWasmMap(wasmFiles) : /* @__PURE__ */ new Map();
43665
+ const wasmInstanceCache = /* @__PURE__ */ new Map();
43666
+ function getWasmInstance(name) {
43667
+ const cached = wasmInstanceCache.get(name);
43668
+ if (cached) return cached;
43669
+ const data = wasmMap.get(name);
43670
+ if (!data) return null;
43671
+ const instance = instantiateWasm(data);
43672
+ wasmInstanceCache.set(name, instance);
43673
+ return instance;
43674
+ }
43332
43675
  for (const file of jsFiles) {
43333
43676
  const funcName = funcNameFromFile(file.name);
43334
43677
  try {
@@ -43342,14 +43685,18 @@ function loadJsUserFunctions(jsFiles) {
43342
43685
  apply: branch.apply
43343
43686
  });
43344
43687
  };
43688
+ const wasmName = parseWasmDirective(file.source) ?? funcName;
43689
+ const wasmInstance = getWasmInstance(wasmName);
43345
43690
  const factory = new Function(
43346
43691
  "RTV",
43347
43692
  "RuntimeError",
43348
43693
  "FloatXArray",
43694
+ "IType",
43349
43695
  "register",
43696
+ "wasm",
43350
43697
  file.source
43351
43698
  );
43352
- factory(RTV, RuntimeError, FloatXArray, registerFn);
43699
+ factory(RTV, RuntimeError, FloatXArray, IType, registerFn, wasmInstance);
43353
43700
  if (branches.length === 0) {
43354
43701
  throw new Error(
43355
43702
  `JS user function '${funcName}' (${file.name}) must call register() at least once`
@@ -43445,10 +43792,13 @@ function generateMainScriptCode(source, mainFileName = "script.m", workspaceFile
43445
43792
  const ctx = new LoweringContext(source, mainFileName);
43446
43793
  const mWorkspaceFiles = [];
43447
43794
  const jsWorkspaceFiles = [];
43795
+ const wasmWorkspaceFiles = [];
43448
43796
  if (workspaceFiles) {
43449
43797
  for (const f of workspaceFiles) {
43450
43798
  if (f.name.endsWith(".js")) {
43451
43799
  jsWorkspaceFiles.push(f);
43800
+ } else if (f.name.endsWith(".wasm")) {
43801
+ wasmWorkspaceFiles.push(f);
43452
43802
  } else {
43453
43803
  mWorkspaceFiles.push(f);
43454
43804
  }
@@ -43465,7 +43815,10 @@ function generateMainScriptCode(source, mainFileName = "script.m", workspaceFile
43465
43815
  }
43466
43816
  ctx.registry.searchPaths = [...searchPaths ?? [], SHIM_SEARCH_PATH];
43467
43817
  _t0 = performance.now();
43468
- const jsUserFunctions = loadJsUserFunctions(jsWorkspaceFiles);
43818
+ const jsUserFunctions = loadJsUserFunctions(
43819
+ jsWorkspaceFiles,
43820
+ wasmWorkspaceFiles
43821
+ );
43469
43822
  const _loadJsUserFunctionsMs = performance.now() - _t0;
43470
43823
  _t0 = performance.now();
43471
43824
  ctx.fileASTCache.set(mainFileName, ast);
@@ -43491,12 +43844,15 @@ function generateMainScriptCode(source, mainFileName = "script.m", workspaceFile
43491
43844
  ctx.registerWorkspaceFiles(mWorkspaceFiles);
43492
43845
  }
43493
43846
  const jsUserFuncNames = [...jsUserFunctions.keys()];
43847
+ const savedBuiltins = /* @__PURE__ */ new Map();
43494
43848
  for (const name of jsUserFuncNames) {
43849
+ const existing = getBuiltin(name);
43850
+ if (existing) savedBuiltins.set(name, existing);
43495
43851
  register(name, jsUserFunctions.get(name));
43496
43852
  }
43497
43853
  const _registrationMs = performance.now() - _t0;
43498
43854
  _t0 = performance.now();
43499
- const functionIndex = ctx.buildFunctionIndex();
43855
+ const functionIndex = ctx.buildFunctionIndex(jsUserFuncNames);
43500
43856
  const _buildFunctionIndexMs = performance.now() - _t0;
43501
43857
  ctx.isTopLevel = true;
43502
43858
  const initialVarIRVars = [];
@@ -43575,7 +43931,12 @@ function generateMainScriptCode(source, mainFileName = "script.m", workspaceFile
43575
43931
  codegen.emit(`return $ret;`);
43576
43932
  const _codegenMs = performance.now() - _t0;
43577
43933
  for (const name of jsUserFuncNames) {
43578
- unregister(name);
43934
+ const original = savedBuiltins.get(name);
43935
+ if (original) {
43936
+ register(name, original);
43937
+ } else {
43938
+ unregister(name);
43939
+ }
43579
43940
  }
43580
43941
  return {
43581
43942
  jsCode: codegen.getCode(),
@@ -44455,6 +44816,14 @@ async function runRepl(initialWorkspaceFiles, onDrawnow, initialSearchPaths) {
44455
44816
 
44456
44817
  // src/cli-fileio.ts
44457
44818
  import { openSync, closeSync, readSync, writeSync, readFileSync as readFileSync5 } from "fs";
44819
+ import { homedir as homedir4 } from "os";
44820
+ import { join as join5 } from "path";
44821
+ function expandTilde(filepath) {
44822
+ if (filepath.startsWith("~/")) {
44823
+ return join5(homedir4(), filepath.slice(2));
44824
+ }
44825
+ return filepath;
44826
+ }
44458
44827
  function permissionToFlags(permission) {
44459
44828
  switch (permission) {
44460
44829
  case "r":
@@ -44481,7 +44850,7 @@ var NodeFileIOAdapter = class {
44481
44850
  fopen(filename, permission) {
44482
44851
  try {
44483
44852
  const flags = permissionToFlags(permission);
44484
- const fd = openSync(filename, flags);
44853
+ const fd = openSync(expandTilde(filename), flags);
44485
44854
  const fid = this.nextFid++;
44486
44855
  this.openFiles.set(fid, {
44487
44856
  fd,
@@ -44527,7 +44896,7 @@ var NodeFileIOAdapter = class {
44527
44896
  return this.readLine(entry, true);
44528
44897
  }
44529
44898
  fileread(filename) {
44530
- return readFileSync5(filename, "utf-8");
44899
+ return readFileSync5(expandTilde(filename), "utf-8");
44531
44900
  }
44532
44901
  feof(fid) {
44533
44902
  const entry = this.getEntry(fid);
@@ -44609,8 +44978,8 @@ var NodeFileIOAdapter = class {
44609
44978
  // src/cli.ts
44610
44979
  var __filename = fileURLToPath2(import.meta.url);
44611
44980
  var __dirname = dirname3(__filename);
44612
- var packageDir2 = join5(__dirname, "..");
44613
- var addonPath = join5(packageDir2, "build", "Release", "lapack_addon.node");
44981
+ var packageDir2 = join6(__dirname, "..");
44982
+ var addonPath = join6(packageDir2, "build", "Release", "lapack_addon.node");
44614
44983
  var nativeAddonLoaded = false;
44615
44984
  try {
44616
44985
  const req = createRequire(import.meta.url);
@@ -44625,7 +44994,7 @@ function scanMFiles(dirPath, excludeFile) {
44625
44994
  try {
44626
44995
  const entries = readdirSync2(dirPath);
44627
44996
  for (const entry of entries) {
44628
- const fullPath = join5(dirPath, entry);
44997
+ const fullPath = join6(dirPath, entry);
44629
44998
  if (excludeFile && fullPath === excludeFile) {
44630
44999
  continue;
44631
45000
  }
@@ -44640,6 +45009,13 @@ function scanMFiles(dirPath, excludeFile) {
44640
45009
  name: fullPath,
44641
45010
  source
44642
45011
  });
45012
+ } else if (stat.isFile() && entry.endsWith(".wasm")) {
45013
+ const data = readFileSync6(fullPath);
45014
+ files.push({
45015
+ name: fullPath,
45016
+ source: "",
45017
+ data: new Uint8Array(data)
45018
+ });
44643
45019
  }
44644
45020
  }
44645
45021
  } catch {
@@ -44657,7 +45033,7 @@ function findTestFiles(dir) {
44657
45033
  }
44658
45034
  entries.sort();
44659
45035
  for (const entry of entries) {
44660
- const fullPath = join5(current, entry);
45036
+ const fullPath = join6(current, entry);
44661
45037
  let stat;
44662
45038
  try {
44663
45039
  stat = statSync2(fullPath);
@@ -44665,7 +45041,7 @@ function findTestFiles(dir) {
44665
45041
  continue;
44666
45042
  }
44667
45043
  if (stat.isDirectory()) {
44668
- if (!entry.startsWith("@") && !entry.startsWith("+")) {
45044
+ if (!entry.startsWith("@") && !entry.startsWith("+") && entry !== "wasm") {
44669
45045
  walk(fullPath);
44670
45046
  }
44671
45047
  } else if (stat.isFile() && entry.endsWith(".m")) {
@@ -45110,7 +45486,7 @@ function finalizeDumpFile(dumpFile, mainFileName, jsCode) {
45110
45486
  writeFileSync3(dumpFile, header + jsCode + "\n" + jitContent);
45111
45487
  }
45112
45488
  async function cmdBuildAddon() {
45113
- const bindingGyp = join5(packageDir2, "binding.gyp");
45489
+ const bindingGyp = join6(packageDir2, "binding.gyp");
45114
45490
  if (!existsSync4(bindingGyp)) {
45115
45491
  console.error(
45116
45492
  "Error: binding.gyp not found in package directory: " + packageDir2
@@ -45317,7 +45693,7 @@ async function main() {
45317
45693
  await cmdEval(rest);
45318
45694
  break;
45319
45695
  case "run-tests": {
45320
- const dir = rest.length > 0 ? rest[0] : join5(packageDir2, "numbl_test_scripts");
45696
+ const dir = rest.length > 0 ? rest[0] : join6(packageDir2, "numbl_test_scripts");
45321
45697
  await runTests(dir);
45322
45698
  break;
45323
45699
  }