zuzu-js 0.1.1 → 0.2.0

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.
@@ -985,14 +985,17 @@
985
985
  return 1;
986
986
  }
987
987
  slice(start, end) {
988
- return new _ZuzuBinary(this.bytes.slice(start, end));
988
+ return new this.constructor(this.bytes.slice(start, end));
989
989
  }
990
990
  at(index) {
991
- const idx = Number(index);
991
+ let idx = Number(index);
992
+ if (idx < 0) {
993
+ idx = this.bytes.length + idx;
994
+ }
992
995
  if (idx < 0 || idx >= this.bytes.length) {
993
996
  return null;
994
997
  }
995
- return new _ZuzuBinary([this.bytes[idx]]);
998
+ return new this.constructor([this.bytes[idx]]);
996
999
  }
997
1000
  to_String() {
998
1001
  let out = "";
@@ -1164,6 +1167,8 @@
1164
1167
  let inSingle = false;
1165
1168
  let inDouble = false;
1166
1169
  let inBacktick = false;
1170
+ let inRegex = false;
1171
+ let inRegexClass = false;
1167
1172
  let inLineComment = false;
1168
1173
  let inBlockComment = false;
1169
1174
  let escape = false;
@@ -1190,6 +1195,35 @@
1190
1195
  masked.push(ch === "\n" ? "\n" : " ");
1191
1196
  continue;
1192
1197
  }
1198
+ if (inRegex) {
1199
+ if (escape) {
1200
+ escape = false;
1201
+ masked.push(" ");
1202
+ continue;
1203
+ }
1204
+ if (ch === "\\") {
1205
+ escape = true;
1206
+ masked.push(" ");
1207
+ continue;
1208
+ }
1209
+ if (ch === "[") {
1210
+ inRegexClass = true;
1211
+ masked.push(" ");
1212
+ continue;
1213
+ }
1214
+ if (ch === "]") {
1215
+ inRegexClass = false;
1216
+ masked.push(" ");
1217
+ continue;
1218
+ }
1219
+ if (ch === "/" && !inRegexClass) {
1220
+ inRegex = false;
1221
+ masked.push(" ");
1222
+ continue;
1223
+ }
1224
+ masked.push(ch === "\n" ? "\n" : " ");
1225
+ continue;
1226
+ }
1193
1227
  if (inSingle || inDouble || inBacktick) {
1194
1228
  if (escape) {
1195
1229
  escape = false;
@@ -1225,6 +1259,12 @@
1225
1259
  masked.push(" ");
1226
1260
  continue;
1227
1261
  }
1262
+ if (ch === "/" && looksLikeRegexLiteralStart(stripped, i)) {
1263
+ inRegex = true;
1264
+ inRegexClass = false;
1265
+ masked.push(" ");
1266
+ continue;
1267
+ }
1228
1268
  if (ch === "'") {
1229
1269
  inSingle = true;
1230
1270
  masked.push(" ");
@@ -1295,6 +1335,17 @@
1295
1335
  }
1296
1336
  return [...names];
1297
1337
  }
1338
+ function looksLikeRegexLiteralStart(source, index) {
1339
+ let pos = index - 1;
1340
+ while (pos >= 0 && /\s/u.test(source[pos])) {
1341
+ pos--;
1342
+ }
1343
+ if (pos < 0) {
1344
+ return true;
1345
+ }
1346
+ const previous = source[pos];
1347
+ return /[({[=,:;!~?&|+\-*%^<>]/u.test(previous);
1348
+ }
1298
1349
  function collectTopLevelUnpackDeclarationNames(source, topLevelMask) {
1299
1350
  const names = [];
1300
1351
  const rx = /(?:^|[;\n])\s*(?:export\s+)?(?:let|const)\b/gm;
@@ -5240,25 +5291,21 @@
5240
5291
  function previous() {
5241
5292
  return tokens[index - 1];
5242
5293
  }
5243
- function canTerminateStatement() {
5294
+ function canTerminateSimpleStatement() {
5244
5295
  const token = current();
5245
- const prev = previous();
5246
- if (!prev) {
5247
- return false;
5248
- }
5249
5296
  if (token.type === "punctuation" && token.value === "}") {
5250
5297
  return true;
5251
5298
  }
5252
5299
  if (token.type === "eof") {
5253
5300
  return true;
5254
5301
  }
5255
- return token.line > prev.endLine;
5302
+ return false;
5256
5303
  }
5257
5304
  function consumeStatementTerminator(message) {
5258
5305
  if (match("punctuation", ";")) {
5259
5306
  return;
5260
5307
  }
5261
- if (canTerminateStatement()) {
5308
+ if (canTerminateSimpleStatement()) {
5262
5309
  return;
5263
5310
  }
5264
5311
  throw new TranspilerSyntaxError(message || "Expected statement terminator", current());
@@ -6389,7 +6436,7 @@
6389
6436
  prefix: true
6390
6437
  }, keyword, endLocFromNode(test));
6391
6438
  }
6392
- expect("punctuation", ";", semicolonMessage);
6439
+ consumeStatementTerminator(semicolonMessage);
6393
6440
  const consequent = withLoc({
6394
6441
  type: "BlockStatement",
6395
6442
  body: [stmt]
@@ -6406,7 +6453,7 @@
6406
6453
  const keyword = current();
6407
6454
  index++;
6408
6455
  const iterable = parseExpression();
6409
- expect("punctuation", ";", semicolonMessage);
6456
+ consumeStatementTerminator(semicolonMessage);
6410
6457
  const body = withLoc({
6411
6458
  type: "BlockStatement",
6412
6459
  body: [stmt]
@@ -7338,6 +7385,7 @@
7338
7385
  ...options,
7339
7386
  asyncContext: needsAsyncWrapper && !syncEval,
7340
7387
  asyncNames: new Set(collectAsyncFunctionNames(ast.body)),
7388
+ evalNames: /* @__PURE__ */ new Set(["eval", ...collectEvalImportNames(ast.body)]),
7341
7389
  weakStorageNames: new Set(collectWeakDeclaredNames(ast.body)),
7342
7390
  scopeNames: /* @__PURE__ */ new Set([
7343
7391
  ...expressionDeclaredNames
@@ -7390,7 +7438,8 @@ ${source}
7390
7438
  "__argc__",
7391
7439
  "__file__",
7392
7440
  "__global__",
7393
- "__system__"
7441
+ "__system__",
7442
+ ...options.evalNames || []
7394
7443
  ].filter(Boolean));
7395
7444
  const localKinds = /* @__PURE__ */ new Set([
7396
7445
  "FunctionDeclaration",
@@ -7417,6 +7466,21 @@ ${source}
7417
7466
  }
7418
7467
  return;
7419
7468
  }
7469
+ if (value.type === "TryStatement") {
7470
+ visit(value.block, value);
7471
+ for (const handler of value.handlers || []) {
7472
+ const paramName = handler.paramName;
7473
+ const hadParam = paramName ? declared.has(paramName) : false;
7474
+ if (paramName) {
7475
+ declared.add(paramName);
7476
+ }
7477
+ visit(handler.body, handler);
7478
+ if (paramName && !hadParam) {
7479
+ declared.delete(paramName);
7480
+ }
7481
+ }
7482
+ return;
7483
+ }
7420
7484
  if (value.type === "VariableUnpackDeclaration") {
7421
7485
  visit(value.init, value);
7422
7486
  for (const entry of bindingEntriesForPattern(value.pattern)) {
@@ -7490,6 +7554,7 @@ ${source}
7490
7554
  syncEval: options.syncEval,
7491
7555
  asyncContext: options.asyncContext,
7492
7556
  asyncNames: options.asyncNames,
7557
+ evalNames: options.evalNames,
7493
7558
  weakStorageNames: options.weakStorageNames,
7494
7559
  scopeNames: options.scopeNames,
7495
7560
  bodylessFunctionNames: options.bodylessFunctionNames,
@@ -7908,7 +7973,7 @@ globalThis[${JSON.stringify(node.id.name)}] = ${name2};` : declaration;
7908
7973
  for (const param of params) {
7909
7974
  if (param.type === "SpecialParameter") {
7910
7975
  if (param.special === "lead_rest") {
7911
- lines.push(`let ${param.leadName} = arguments[${argIndex}];`);
7976
+ lines.push(`const ${param.leadName} = arguments[${argIndex}];`);
7912
7977
  argIndex++;
7913
7978
  lines.push(`const ${param.name} = Array.prototype.slice.call( arguments, ${argIndex} );`);
7914
7979
  continue;
@@ -7935,11 +8000,11 @@ globalThis[${JSON.stringify(node.id.name)}] = ${name2};` : declaration;
7935
8000
  continue;
7936
8001
  }
7937
8002
  if (param.defaultValue) {
7938
- lines.push(`let ${paramName} = __argc__ > ${argIndex} ? arguments[${argIndex}] : ${emitExpression(param.defaultValue)};`);
8003
+ lines.push(`const ${paramName} = __argc__ > ${argIndex} ? arguments[${argIndex}] : ${emitExpression(param.defaultValue)};`);
7939
8004
  } else if (param.optional) {
7940
- lines.push(`let ${paramName} = __argc__ > ${argIndex} ? arguments[${argIndex}] : null;`);
8005
+ lines.push(`const ${paramName} = __argc__ > ${argIndex} ? arguments[${argIndex}] : null;`);
7941
8006
  } else {
7942
- lines.push(`let ${paramName} = arguments[${argIndex}];`);
8007
+ lines.push(`const ${paramName} = arguments[${argIndex}];`);
7943
8008
  }
7944
8009
  if (param.typeName) {
7945
8010
  lines.push(
@@ -7955,62 +8020,71 @@ globalThis[${JSON.stringify(node.id.name)}] = ${name2};` : declaration;
7955
8020
  case "pairlist_only":
7956
8021
  return [
7957
8022
  `const __zuzu_call_args = Array.prototype.slice.call( arguments );`,
7958
- `let ${signature.namedName} = __zuzu_pairlist_literal( [] );`,
8023
+ "let __zuzu_named_args = __zuzu_pairlist_literal( [] );",
7959
8024
  "for ( const __a of __zuzu_call_args ) {",
7960
- `if ( __zuzu_is_pairlist( __a ) ) { ${signature.namedName} = __a; } else { throw new Exception( "named PairList parameter only accepts named arguments" ); }`,
7961
- "}"
8025
+ 'if ( __zuzu_is_pairlist( __a ) ) { __zuzu_named_args = __a; } else { throw new Exception( "named PairList parameter only accepts named arguments" ); }',
8026
+ "}",
8027
+ `const ${signature.namedName} = __zuzu_named_args;`
7962
8028
  ].join("\n");
7963
8029
  case "pairlist_rest_array":
7964
8030
  case "rest_array_pairlist":
7965
8031
  return [
7966
8032
  `const __zuzu_call_args = Array.prototype.slice.call( arguments );`,
7967
- `let ${signature.namedName} = __zuzu_pairlist_literal( [] );`,
7968
- `let ${signature.restName} = [];`,
8033
+ "let __zuzu_named_args = __zuzu_pairlist_literal( [] );",
8034
+ "const __zuzu_rest_args = [];",
7969
8035
  "for ( const __a of __zuzu_call_args ) {",
7970
- `if ( __zuzu_is_pairlist( __a ) ) { ${signature.namedName} = __a; } else { ${signature.restName}.push( __a ); }`,
7971
- "}"
8036
+ "if ( __zuzu_is_pairlist( __a ) ) { __zuzu_named_args = __a; } else { __zuzu_rest_args.push( __a ); }",
8037
+ "}",
8038
+ `const ${signature.namedName} = __zuzu_named_args;`,
8039
+ `const ${signature.restName} = __zuzu_rest_args;`
7972
8040
  ].join("\n");
7973
8041
  case "lead_pairlist":
7974
8042
  return [
7975
8043
  "const __zuzu_call_args = Array.prototype.slice.call( arguments );",
7976
- `let ${signature.headName} = __zuzu_call_args[0];`,
7977
- `let ${signature.namedName} = __zuzu_pairlist_literal( [] );`,
7978
- `let ${signature.restName} = [];`,
8044
+ `const ${signature.headName} = __zuzu_call_args[0];`,
8045
+ "let __zuzu_named_args = __zuzu_pairlist_literal( [] );",
8046
+ "const __zuzu_rest_args = [];",
7979
8047
  "for ( let __i = 1; __i < __zuzu_call_args.length; __i++ ) {",
7980
8048
  "const __a = __zuzu_call_args[__i];",
7981
- `if ( __zuzu_is_pairlist( __a ) ) { ${signature.namedName} = __a; } else { ${signature.restName}.push( __a ); }`,
7982
- "}"
8049
+ "if ( __zuzu_is_pairlist( __a ) ) { __zuzu_named_args = __a; } else { __zuzu_rest_args.push( __a ); }",
8050
+ "}",
8051
+ `const ${signature.namedName} = __zuzu_named_args;`,
8052
+ `const ${signature.restName} = __zuzu_rest_args;`
7983
8053
  ].join("\n");
7984
8054
  case "trail_pairlist":
7985
8055
  return [
7986
8056
  "const __zuzu_call_args = Array.prototype.slice.call( arguments );",
7987
- `let ${signature.headName} = __zuzu_call_args[0];`,
7988
- `let ${signature.restName} = [];`,
7989
- `let ${signature.namedName} = __zuzu_pairlist_literal( [] );`,
8057
+ `const ${signature.headName} = __zuzu_call_args[0];`,
8058
+ "const __zuzu_rest_args = [];",
8059
+ "let __zuzu_named_args = __zuzu_pairlist_literal( [] );",
7990
8060
  "for ( let __i = 1; __i < __zuzu_call_args.length; __i++ ) {",
7991
8061
  "const __a = __zuzu_call_args[__i];",
7992
- `if ( __zuzu_is_pairlist( __a ) ) { ${signature.namedName} = __a; } else { ${signature.restName}.push( __a ); }`,
7993
- "}"
8062
+ "if ( __zuzu_is_pairlist( __a ) ) { __zuzu_named_args = __a; } else { __zuzu_rest_args.push( __a ); }",
8063
+ "}",
8064
+ `const ${signature.restName} = __zuzu_rest_args;`,
8065
+ `const ${signature.namedName} = __zuzu_named_args;`
7994
8066
  ].join("\n");
7995
8067
  case "scalar_pairlist":
7996
8068
  return [
7997
8069
  "const __zuzu_call_args = Array.prototype.slice.call( arguments );",
7998
- `let ${signature.headName} = __zuzu_call_args[0];`,
7999
- `let ${signature.namedName} = __zuzu_pairlist_literal( [] );`,
8070
+ `const ${signature.headName} = __zuzu_call_args[0];`,
8071
+ "let __zuzu_named_args = __zuzu_pairlist_literal( [] );",
8000
8072
  "for ( let __i = 1; __i < __zuzu_call_args.length; __i++ ) {",
8001
8073
  "const __a = __zuzu_call_args[__i];",
8002
- `if ( __zuzu_is_pairlist( __a ) ) { ${signature.namedName} = __a; } else { throw new Exception( "named arguments not allowed for this function" ); }`,
8003
- "}"
8074
+ 'if ( __zuzu_is_pairlist( __a ) ) { __zuzu_named_args = __a; } else { throw new Exception( "named arguments not allowed for this function" ); }',
8075
+ "}",
8076
+ `const ${signature.namedName} = __zuzu_named_args;`
8004
8077
  ].join("\n");
8005
8078
  case "variadic":
8006
8079
  return [
8007
8080
  "const __zuzu_call_args = Array.prototype.slice.call( arguments );",
8008
- `let ${signature.headName} = __zuzu_call_args[0];`,
8009
- `let ${signature.restName} = [];`,
8081
+ `const ${signature.headName} = __zuzu_call_args[0];`,
8082
+ "const __zuzu_rest_args = [];",
8010
8083
  "for ( let __i = 1; __i < __zuzu_call_args.length; __i++ ) {",
8011
8084
  "const __a = __zuzu_call_args[__i];",
8012
- `if ( __zuzu_is_pairlist( __a ) ) { throw new Exception( "named arguments not allowed for this function" ); } ${signature.restName}.push( __a );`,
8013
- "}"
8085
+ 'if ( __zuzu_is_pairlist( __a ) ) { throw new Exception( "named arguments not allowed for this function" ); } __zuzu_rest_args.push( __a );',
8086
+ "}",
8087
+ `const ${signature.restName} = __zuzu_rest_args;`
8014
8088
  ].join("\n");
8015
8089
  default:
8016
8090
  return "";
@@ -8077,7 +8151,7 @@ globalThis[${JSON.stringify(node.id.name)}] = ${name2};` : declaration;
8077
8151
  const moduleName = `__zuzu_imported_${Math.abs(hashString(node.source))}_${node.specifiers.length}_${options.syncEval ? loopCounter++ : 0}`;
8078
8152
  const defineImports = node.specifiers.map((specifier) => {
8079
8153
  const importedName = normalizeImportedName(node.source, specifier.imported);
8080
- if (node.source === "std/eval" && importedName === "eval" && specifier.local === "eval") {
8154
+ if (node.source === "std/eval" && importedName === "eval") {
8081
8155
  return "";
8082
8156
  }
8083
8157
  return defineLiveImport(specifier.local, moduleName, importedName, options);
@@ -8263,6 +8337,21 @@ ${cleanup}
8263
8337
  }
8264
8338
  return names;
8265
8339
  }
8340
+ function collectEvalImportNames(statements = []) {
8341
+ const names = [];
8342
+ for (const stmt of statements) {
8343
+ if (stmt.type !== "ImportDeclaration" || stmt.source !== "std/eval") {
8344
+ continue;
8345
+ }
8346
+ for (const specifier of stmt.specifiers || []) {
8347
+ const importedName = normalizeImportedName(stmt.source, specifier.imported);
8348
+ if (importedName === "eval" && specifier.local) {
8349
+ names.push(specifier.local);
8350
+ }
8351
+ }
8352
+ }
8353
+ return names;
8354
+ }
8266
8355
  function collectDeclaredNames(statements) {
8267
8356
  const names = [];
8268
8357
  for (const stmt of statements || []) {
@@ -8741,7 +8830,7 @@ ${cleanup}
8741
8830
  if (node.callee.type === "Super") {
8742
8831
  const argArray = emitCallArgumentArray(node.arguments);
8743
8832
  source = `__zuzu_super_dispatch( __zuzu_super_static__, self, __zuzu_super_class__, __zuzu_super_method__, ${argArray} )`;
8744
- } else if (node.callee.type === "Identifier" && node.callee.name === "eval" && node.arguments.length > 0) {
8833
+ } else if (node.callee.type === "Identifier" && (options.evalNames || /* @__PURE__ */ new Set(["eval"])).has(node.callee.name) && node.arguments.length > 0) {
8745
8834
  source = emitEvalCall(node.arguments);
8746
8835
  } else if (node.callee.type === "MemberExpression" && !node.callee.computed && node.callee.property.type === "Identifier" && node.callee.property.name === "length" && node.arguments.length === 0) {
8747
8836
  source = `__zuzu_length( ${emitExpression(node.callee.object)} )`;
@@ -8972,7 +9061,7 @@ ${cleanup}
8972
9061
  return `__zuzu_ne( ${left}, ${right} )`;
8973
9062
  case "!=":
8974
9063
  case "\u2260":
8975
- return `__zuzu_num_ne( ${left}, ${right} )`;
9064
+ return `__zuzu_ne( ${left}, ${right} )`;
8976
9065
  case "~":
8977
9066
  return `__zuzu_match( ${left}, ${right} )`;
8978
9067
  case "_":
@@ -9150,12 +9239,8 @@ ${cleanup}
9150
9239
  function emitSliceExpression(node) {
9151
9240
  const object = emitExpression(node.object);
9152
9241
  const start = node.start ? emitExpression(node.start) : "0";
9153
- if (node.length == null) {
9154
- return `${object}.slice( ${start} )`;
9155
- }
9156
- const length = emitExpression(node.length);
9157
- const end = isNegativeNumericLiteral(node.length) ? length : `__zuzu_add( ${start}, ${length} )`;
9158
- return `${object}.slice( ${start}, ${end} )`;
9242
+ const length = node.length == null ? "null" : emitExpression(node.length);
9243
+ return `__zuzu_get_slice( ${object}, ${start}, ${length} )`;
9159
9244
  }
9160
9245
  function unwrapGroupedExpression(node) {
9161
9246
  let current = node;
@@ -9164,9 +9249,6 @@ ${cleanup}
9164
9249
  }
9165
9250
  return current;
9166
9251
  }
9167
- function isNegativeNumericLiteral(node) {
9168
- return !!(node && node.type === "UnaryExpression" && node.operator === "-" && node.argument && node.argument.type === "NumericLiteral");
9169
- }
9170
9252
  function emitRegexReplaceExpression(node) {
9171
9253
  const leftTarget = unwrapGroupedExpression(node.left);
9172
9254
  if (leftTarget && leftTarget.type === "BinaryExpression" && ["@", "@@", "@?"].includes(leftTarget.operator)) {
@@ -10973,6 +11055,14 @@ ${cleanup}
10973
11055
  slurp_utf8_async() {
10974
11056
  return new Task(async () => fs.promises.readFile(this.value, "utf8"));
10975
11057
  }
11058
+ append(value) {
11059
+ traceBlockingOperation("std/io Path.append");
11060
+ if (!(value && value.bytes instanceof Uint8Array)) {
11061
+ throw new Error(`TypeException: Path.append expects BinaryString, got ${typeName2(value)}`);
11062
+ }
11063
+ fs.appendFileSync(this.value, Buffer.from(value.bytes));
11064
+ return this;
11065
+ }
10976
11066
  append_async(value) {
10977
11067
  return new Task(async () => {
10978
11068
  if (!(value && value.bytes instanceof Uint8Array)) {
@@ -10982,6 +11072,14 @@ ${cleanup}
10982
11072
  return this;
10983
11073
  });
10984
11074
  }
11075
+ append_utf8(text) {
11076
+ traceBlockingOperation("std/io Path.append_utf8");
11077
+ if (typeof text !== "string") {
11078
+ throw new Error(`TypeException: Path.append_utf8 expects String, got ${typeName2(text)}`);
11079
+ }
11080
+ fs.appendFileSync(this.value, text, "utf8");
11081
+ return this;
11082
+ }
10985
11083
  append_utf8_async(text) {
10986
11084
  return new Task(async () => {
10987
11085
  if (typeof text !== "string") {
@@ -12492,10 +12590,11 @@ ${cleanup}
12492
12590
  if (typeof target === "string" || Array.isArray(target)) {
12493
12591
  let resolved = Number(index);
12494
12592
  if (Number.isFinite(resolved)) {
12593
+ const indexed = typeof target === "string" ? [...target] : target;
12495
12594
  if (resolved < 0) {
12496
- resolved = target.length + resolved;
12595
+ resolved = indexed.length + resolved;
12497
12596
  }
12498
- return resolveWeakValue(target[resolved]);
12597
+ return resolveWeakValue(indexed[resolved] ?? null);
12499
12598
  }
12500
12599
  }
12501
12600
  if (target && typeof target === "object" && target.constructor && typeof target.constructor.__zuzu_class_name === "string" && !Object.prototype.hasOwnProperty.call(target, key) && typeof target[key] === "function") {
@@ -12503,22 +12602,58 @@ ${cleanup}
12503
12602
  }
12504
12603
  return resolveWeakValue(target[index]);
12505
12604
  }
12605
+ function zuzuSliceBounds(size, from, length) {
12606
+ let start = Number(from ?? 0);
12607
+ if (!Number.isFinite(start)) {
12608
+ start = 0;
12609
+ }
12610
+ if (start < 0) {
12611
+ start = size + start;
12612
+ }
12613
+ start = Math.max(0, Math.min(size, start));
12614
+ if (length == null) {
12615
+ return [start, size];
12616
+ }
12617
+ const span = Number(length);
12618
+ if (!Number.isFinite(span)) {
12619
+ return [start, start];
12620
+ }
12621
+ const end = span < 0 ? Math.max(0, Math.min(size, size + span)) : Math.max(0, Math.min(size, start + span));
12622
+ return [Math.min(start, end), Math.max(start, end)];
12623
+ }
12624
+ function zuzuGetSlice(target, from, length) {
12625
+ target = resolveWeakValue(target);
12626
+ if (target == null) {
12627
+ return null;
12628
+ }
12629
+ if (typeof target === "string") {
12630
+ const chars = [...target];
12631
+ const [start, end] = zuzuSliceBounds(chars.length, from, length);
12632
+ return chars.slice(start, end).join("");
12633
+ }
12634
+ if (target instanceof ZuzuBinary) {
12635
+ const [start, end] = zuzuSliceBounds(target.length, from, length);
12636
+ return target.slice(start, end);
12637
+ }
12638
+ if (Array.isArray(target)) {
12639
+ const [start, end] = zuzuSliceBounds(target.length, from, length);
12640
+ return target.slice(start, end);
12641
+ }
12642
+ return target.slice(from, length == null ? void 0 : Number(from) + Number(length));
12643
+ }
12506
12644
  function zuzuAssignSlice(target, from, length, value) {
12507
12645
  const hasLength = length != null;
12508
- const span = hasLength ? Number(length) : null;
12509
12646
  const replacement = value == null ? "" : value;
12510
12647
  if (typeof target === "string") {
12511
- let start2 = Number(from);
12512
- if (start2 < 0) {
12513
- start2 = target.length + start2;
12514
- }
12515
- start2 = Math.max(0, Math.min(target.length, start2));
12516
- const end = hasLength ? Math.max(0, Math.min(target.length, span >= 0 ? start2 + span : target.length + span)) : target.length;
12517
- return target.slice(0, start2) + String(replacement) + target.slice(end);
12648
+ const chars = [...target];
12649
+ const [start2, end] = zuzuSliceBounds(chars.length, from, length);
12650
+ chars.splice(start2, end - start2, ...[...String(replacement)]);
12651
+ return chars.join("");
12518
12652
  }
12519
12653
  const start = Number(from);
12520
12654
  if (Array.isArray(target)) {
12521
12655
  const items = Array.isArray(replacement) ? replacement : [replacement];
12656
+ const span = hasLength ? Number(length) : null;
12522
12657
  if (hasLength) {
12523
12658
  target.splice(start, span, ...items);
12524
12659
  } else {
@@ -12527,7 +12662,16 @@ ${cleanup}
12527
12662
  return target;
12528
12663
  }
12529
12664
  if (target instanceof ZuzuBinary) {
12530
- throw new Error("Exception: BinaryString slice assignment is not supported");
12665
+ if (!(value instanceof ZuzuBinary)) {
12666
+ throw new Error(`TypeException: BinaryString slice assignment expects BinaryString, got ${zuzuTypeof(value)}`);
12667
+ }
12668
+ const [byteStart, byteEnd] = zuzuSliceBounds(target.length, from, length);
12669
+ const next = new Uint8Array(target.length - (byteEnd - byteStart) + value.bytes.length);
12670
+ next.set(target.bytes.slice(0, byteStart), 0);
12671
+ next.set(value.bytes, byteStart);
12672
+ next.set(target.bytes.slice(byteEnd), byteStart + value.bytes.length);
12673
+ target.bytes = next;
12674
+ return target;
12531
12675
  }
12532
12676
  throw new Error(`TypeException: slice assignment expects String or Array, got ${zuzuTypeof(target)}`);
12533
12677
  }
@@ -12547,18 +12691,27 @@ ${cleanup}
12547
12691
  throw new Error("TypeException: cannot assign to null");
12548
12692
  }
12549
12693
  if (target instanceof ZuzuBinary) {
12550
- throw new Error("Exception: BinaryString index assignment is not supported");
12694
+ if (!(value instanceof ZuzuBinary)) {
12695
+ throw new Error(`TypeException: BinaryString index assignment expects BinaryString, got ${zuzuTypeof(value)}`);
12696
+ }
12697
+ zuzuAssignSlice(target, index, 1, value);
12698
+ if (typeof assignTarget === "function") {
12699
+ assignTarget(target);
12700
+ }
12701
+ return target;
12551
12702
  }
12552
12703
  if (typeof target === "string") {
12553
12704
  let resolved = Number(index);
12554
12705
  if (!Number.isFinite(resolved)) {
12555
12706
  throw new Error("TypeException: String index assignment expects numeric index");
12556
12707
  }
12708
+ const chars = [...target];
12557
12709
  if (resolved < 0) {
12558
- resolved = target.length + resolved;
12710
+ resolved = chars.length + resolved;
12559
12711
  }
12560
- resolved = Math.max(0, Math.min(target.length, resolved));
12561
- const updated = target.slice(0, resolved) + String(value ?? "") + target.slice(resolved + 1);
12712
+ resolved = Math.max(0, Math.min(chars.length, resolved));
12713
+ chars.splice(resolved, resolved < chars.length ? 1 : 0, ...[...String(value ?? "")]);
12714
+ const updated = chars.join("");
12562
12715
  return typeof assignTarget === "function" ? assignTarget(updated) : updated;
12563
12716
  }
12564
12717
  if (isPairListLike(target)) {
@@ -12578,14 +12731,10 @@ ${cleanup}
12578
12731
  const hasLength = length != null;
12579
12732
  const span = hasLength ? Number(length) : null;
12580
12733
  if (arguments.length === 0) {
12581
- const end = hasLength ? span >= 0 ? start + span : span : void 0;
12582
- if (target instanceof ZuzuBinary) {
12583
- return target.slice(start, end);
12584
- }
12585
- return target.slice(start, end);
12734
+ return zuzuGetSlice(target, start, hasLength ? span : null);
12586
12735
  }
12587
- if (target instanceof ZuzuBinary) {
12588
- throw new Error("Exception: BinaryString slice assignment is not supported");
12736
+ if (target instanceof ZuzuBinary || typeof target === "string") {
12737
+ return zuzuAssignSlice(target, start, hasLength ? span : null, maybeValue);
12589
12738
  }
12590
12739
  if (!hasLength) {
12591
12740
  target.splice(start, target.length - start, ...maybeValue);
@@ -12714,6 +12863,13 @@ ${cleanup}
12714
12863
  if (typeName2 === "PairList") {
12715
12864
  return isPairListLike(value) ? 1 : 0;
12716
12865
  }
12866
+ if (typeName2 === "Exception") {
12867
+ const globalType2 = globalThis.Exception;
12868
+ if (globalType2 != null && zuzuInstanceof(value, globalType2)) {
12869
+ return 1;
12870
+ }
12871
+ return value instanceof Error ? 1 : 0;
12872
+ }
12717
12873
  return zuzuTypeof(value) === typeName2 ? 1 : 0;
12718
12874
  }
12719
12875
  if (typeName2 === "BinaryString") {
@@ -13948,6 +14104,9 @@ ${cleanup}
13948
14104
  __zuzu_ref_key(target, key) {
13949
14105
  return refKey(target, key);
13950
14106
  },
14107
+ __zuzu_get_slice(target, from, length) {
14108
+ return zuzuGetSlice(target, from, length);
14109
+ },
13951
14110
  __zuzu_ref_slice(target, from, length) {
13952
14111
  return refSlice(target, from, length);
13953
14112
  },
@@ -39328,9 +39487,9 @@ ${lines.join("\n")}
39328
39487
  }
39329
39488
  });
39330
39489
 
39331
- // ../../../../../tmp/zuzu-browser-build.yNRBlw/browser-stdlib.generated.js
39490
+ // ../../../../../tmp/zuzu-browser-build.WePoiO/browser-stdlib.generated.js
39332
39491
  var require_browser_stdlib_generated = __commonJS({
39333
- "../../../../../tmp/zuzu-browser-build.yNRBlw/browser-stdlib.generated.js"(exports2, module2) {
39492
+ "../../../../../tmp/zuzu-browser-build.WePoiO/browser-stdlib.generated.js"(exports2, module2) {
39334
39493
  "use strict";
39335
39494
  function createBrowserStdlib2() {
39336
39495
  const jsModules = /* @__PURE__ */ Object.create(null);
@@ -45188,9 +45347,10 @@ class ZZTemplate extends ZTemplate {
45188
45347
  }
45189
45348
  }
45190
45349
  `;
45350
+ virtualFiles["/modules/test/more.zzm"] = '=encoding utf8\n\n=head1 NAME\n\ntest/more - Write unit tests and integration tests in ZuzuScript.\n\n=head1 SYNOPSIS\n\n from test/more import *;\n from my/project import frobnicate;\n\n is(frobnicate(21), 42, "frobinated 21 correctly");\n is(frobnicate(null), null, "frobinate on null input");\n\n done_testing();\n\n=cut\n\n\nlet Number _COUNT := 0;\nlet Number _PASSED := 0;\nlet Number _FAILED := 0;\nlet Number _LEVEL := 0;\nlet Number _PLAN := -1;\nlet Number _TODO_FAILED := 0;\nlet Number _TODO_PASSED := 0;\n\nlet TODO;\n\nfunction _directive () {\n if ( TODO ) {\n if ( TODO instanceof String ) {\n return ` # TODO ${TODO}`;\n }\n else {\n return " # TODO";\n }\n }\n return "";\n}\n\nfunction _indent () {\n let i := _LEVEL;\n let str := "";\n while ( i > 0 ) {\n str _= " ";\n i--;\n }\n\n return str;\n}\n\nclass BailOutException extends Exception;\nclass SkipAllException extends Exception;\n\nfunction _module_name_is_valid ( String module ) {\n return module ~ /^[A-Za-z_][A-Za-z0-9_]*(\\/[A-Za-z_][A-Za-z0-9_]*)*$/;\n}\n\nfunction _module_is_available ( String module ) {\n from std/eval import eval;\n\n try {\n eval( `do { from ${module} import *; }; true;` );\n return true;\n }\n catch {\n return false;\n }\n}\n\nfunction _capability_is_available ( String capability ) {\n if ( not( capability ~ /^[A-Za-z_][A-Za-z0-9_]*$/ ) ) {\n die `Invalid capability name: ${capability}`;\n }\n\n let deny_key := `deny_${capability}`;\n if ( deny_key in __system__ ) {\n return not __system__.get(deny_key);\n }\n\n return false;\n}\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nC<< test/more >> is a module for test-driven development. It can be used\nfor writing unit tests and integration tests. It generates output in TAP\nL<https://testanything.org/>.\n\nThis module should feel familiar to anybody who has used C<< Test::More >>\nfor Perl, though there are some minor differences.\n\n=head2 Functions\n\n=over\n\n=item C<< pass(String name?) >>\n\nIndicates the named test has passed.\n\n=cut\n\nfunction pass ( String name? ) {\n ++_PASSED;\n ++_COUNT;\n ++_TODO_PASSED if TODO;\n if ( name \u2261 null ) {\n say `${_indent()}ok ${_COUNT}${_directive()}`;\n }\n else {\n say `${_indent()}ok ${_COUNT} - ${name}${_directive()}`;\n }\n\n return true;\n}\n\n=item C<< fail(String name?) >>\n\nIndicates the named test has failed.\n\n=cut\n\nfunction fail ( String name ) {\n ++_FAILED;\n ++_COUNT;\n ++_TODO_FAILED if TODO;\n if ( name \u2261 null ) {\n say `${_indent()}not ok ${_COUNT}${_directive()}`;\n }\n else {\n say `${_indent()}not ok ${_COUNT} - ${name}${_directive()}`;\n }\n\n return false;\n}\n\n=item C<< ok(expr, String name?) >>\n\nPasses the named test only if C<expr> is truthy.\n\n=cut\n\nfunction ok ( expr, String name? ) {\n if (expr) {\n return pass(name);\n }\n else {\n return fail(name);\n }\n}\n\nfunction _coerced_equal ( got, expected ) {\n if ( got \u2261 expected ) {\n return true;\n }\n\n if ( got instanceof Boolean and expected instanceof Number ) {\n return ( got ? 1: 0 ) = expected;\n }\n if ( got instanceof Number and expected instanceof Boolean ) {\n return got = ( expected ? 1: 0 );\n }\n\n return false;\n}\n\n=item C<< is(got, expected, String name?) >>\n\nPasses the named test only if C<< got \u2261 expected >>.\n\n=cut\n\nfunction is ( got, expected, String name? ) {\n\n if ( not ok( _coerced_equal( got, expected ), name ) ) {\n from std/dump import Dumper;\n say `${_indent()}# expected: ${Dumper.dump(expected)}`;\n say `${_indent()}# got: ${Dumper.dump(got)}`;\n return false;\n }\n\n return true;\n}\n\n=item C<< isnt(got, unexpected, String name?) >>\n\nPasses the named test only if C<< got \u2262 expected >>.\n\n=cut\n\nfunction isnt ( got, unexpected, String name? ) {\n return ok( not _coerced_equal( got, unexpected ), name );\n}\n\n=item C<< like(got, expected_re, String name?) >>\n\nPasses the named test only if C<< got ~ expected >>.\n\n=cut\n\nfunction like ( got, Regexp expected_re, String name? ) {\n\n if ( not ok( got ~ expected_re, name ) ) {\n say `${_indent()}# expected (regexp): ${expected_re}`;\n say `${_indent()}# got: ${got}`;\n return false;\n }\n\n return true;\n}\n\n=item C<< unlike(got, unexpected_re, String name?) >>\n\nFails the named test only if C<< got ~ expected >>.\n\n=cut\n\nfunction unlike ( got, Regexp unexpected_re, String name? ) {\n\n if ( not ok( not( got ~ unexpected_re ), name ) ) {\n say `${_indent()}# unexpected (regexp): ${unexpected_re}`;\n say `${_indent()}# got: ${got}`;\n return false;\n }\n\n return true;\n}\n\n=item C<< diag(diagnostic) >>\n\nOutputs the diagnostic.\n\n=cut\n\nfunction diag (diagnostic) {\n say `${_indent()}# ${diagnostic}`;\n return true;\n}\n\n=item C<< explain(value) >>\n\nCombines diag with Dumper.dump();\n\n=cut\n\nfunction explain (value) {\n from std/dump import Dumper;\n return diag( Dumper.dump(value) );\n}\n\n=item C<< bail_out(String message?) >>\n\nBail out of running further tests.\n\n=cut\n\nfunction bail_out ( String message? ) {\n let e;\n if ( message \u2261 null ) {\n e := new BailOutException( message: "Bail out!" );\n }\n else {\n e := new BailOutException( message: `Bail out! ${message}` );\n }\n throw e;\n}\n\n=item C<< skip_all(String reason) >>\n\nSkips the whole test file by emitting a TAP skip-all plan and exiting\nsuccessfully when the runtime provides C<std/proc>.\n\nThis is intended for guards at the beginning of a test script, before\nany tests have run. Browser-like hosts without C<std/proc> use a\nspecial exception fallback that the browser ztest runner treats as a\nskip.\n\n=cut\n\nfunction skip_all ( String reason ) {\n die "Cannot skip all tests after tests have already run" if _COUNT > 0;\n die "Cannot skip all tests in a subtest" if _LEVEL > 0;\n _PLAN := 0;\n say `1..${_PLAN} # SKIP: ${reason}`;\n\n from std/proc try import Proc;\n Proc.exit(0) if Proc \u2262 null;\n throw new SkipAllException( message: reason );\n}\n\n=item C<< requires_module(String module) >>\n\nSkips the whole test file if the named module cannot be loaded.\n\n=cut\n\nfunction requires_module ( String module ) {\n if ( not _module_name_is_valid(module) ) {\n die `Invalid module name: ${module}`;\n }\n\n if ( not _module_is_available(module) ) {\n skip_all( `module ${module} is unavailable` );\n }\n\n return true;\n}\n\n=item C<< requires_capability(String capability) >>\n\nSkips the whole test file if the named runtime capability is not\navailable.\n\n=cut\n\nfunction requires_capability ( String capability ) {\n if ( not _capability_is_available(capability) ) {\n skip_all( `capability ${capability} is unavailable` );\n }\n\n return true;\n}\n\n=item C<< author_testing() >>\n\nSkips the whole test file unless the C<AUTHOR_TESTING> environment\nvariable is set to a true value.\n\n=cut\n\nfunction author_testing () {\n requires_capability( "proc" );\n from std/proc try import Env;\n if ( Env and Env.get( "AUTHOR_TESTING", false ) ) {\n return true;\n }\n skip_all( "requires AUTHOR_TESTING=1" );\n}\n\n=item C<< extended_testing() >>\n\nSkips the whole test file unless the C<EXTENDED_TESTING> environment\nvariable is set to a true value.\n\n=cut\n\nfunction extended_testing () {\n requires_capability( "proc" );\n from std/proc try import Env;\n if ( Env and Env.get( "EXTENDED_TESTING", false ) ) {\n return true;\n }\n skip_all( "requires EXTENDED_TESTING=1" );\n}\n\n=item C<< plan(Number n) >>\n\nIndicate the number of tests you intend on running, before running any\ntests.\n\ndone_testing() can later check the number.\n\n=cut\n\nfunction plan ( Number n ) {\n die "Must plan() before running any tests" if _COUNT > 0;\n die "Cannot plan() in a subtest" if _LEVEL > 0;\n die "Must plan() at least one test" if n < 1;\n _PLAN := n;\n say `1..${_PLAN}`;\n}\n\n=item C<< done_testing(Number n?) >>\n\nIndicates that testing is finished.\n\nYou may optionally provide the expected total number of tests, and the\nfunction will throw an exception if that doesn\'t match the number of\ntests which were actually run.\n\nWill also throw an error if you called plan() earlier and the actual\nnumber of tests run differs from your plan.\n\n=cut\n\nfunction done_testing ( Number n? ) {\n die "Unexpected done_testing() in subtest" if _LEVEL > 0;\n die `Expected ${n} tests, but ran ${_COUNT}` if n \u2262 null and n \u2260 _COUNT;\n\n function maybe_fail_count ( should_die ) {\n if ( _TODO_PASSED > 0 ) {\n warn `Passed ${_TODO_PASSED} TODO tests!`;\n }\n if ( _FAILED > 0 ) {\n let msg := `Failed ${_FAILED} tests`;\n msg _= ` (${_TODO_FAILED} todo, ${_FAILED - _TODO_FAILED} true fails)` if _TODO_FAILED > 0;\n die msg if should_die and ( _FAILED > _TODO_FAILED );\n warn msg;\n }\n }\n\n if ( _PLAN < 0 ) {\n say `1..${_COUNT}`;\n }\n else if ( _PLAN \u2260 _COUNT ) {\n maybe_fail_count(false);\n die `Planned ${_PLAN} tests, but ran ${_COUNT}`;\n }\n\n maybe_fail_count(true);\n return true;\n}\n\n=item C<< subtest(String name, Function f) >>\n\nCalls f() as a subtest.\n\nDon\'t use done_testing() within the function.\n\n=cut\n\nfunction subtest ( String name, Function f ) {\n let orig_context := {\n count: _COUNT,\n passed: _PASSED,\n failed: _FAILED,\n level: _LEVEL,\n todo_passed: _TODO_PASSED,\n todo_failed: _TODO_FAILED,\n };\n\n function restore_context () {\n _COUNT := orig_context{count};\n _PASSED := orig_context{passed};\n _FAILED := orig_context{failed};\n _LEVEL := orig_context{level};\n _TODO_PASSED := orig_context{todo_passed};\n _TODO_FAILED := orig_context{todo_failed};\n }\n\n _COUNT := 0;\n _PASSED := 0;\n _FAILED := 0;\n _TODO_PASSED := 0;\n _TODO_FAILED := 0;\n _LEVEL++;\n let is_ok := true;\n\n try {\n diag( `Subtest: ${name}` );\n f();\n say `${_indent()}1..${_COUNT}`;\n is_ok := false if _FAILED > _TODO_FAILED;\n restore_context();\n }\n catch ( BailOutException e ) {\n throw e;\n }\n catch ( Exception e ) {\n is_ok := false;\n restore_context();\n }\n\n return ok( is_ok, name );\n}\n\n=back\n\n=item C<< todo(Boolean c, String reason, Function f) >>\n\nRuns the tests in the given function, but if condition c is true then\nfirst sets TODO to true.\n\nYou can manually set and unset the TODO variable instead.\n\n=cut\n\nfunction todo ( Boolean c, String reason, Function f ) {\n let orig := TODO;\n TODO := reason if c;\n try {\n f();\n }\n catch {\n }\n TODO := orig;\n return not c;\n}\n\n=back\n\n=item C<< exception(Function f) >>\n\nCalls f() and returns any exception thrown.\n\nReturns null if no exception was thrown.\n\n=cut\n\nfunction exception ( Function f ) {\n try {\n f();\n return null;\n }\n catch ( BailOutException e ) {\n throw e;\n }\n catch ( Exception e ) {\n return e;\n }\n}\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< test/more >> is copyright Toby Inkster.\n\nIt is free software; you may redistribute it and/or modify it under\nthe terms of either the Artistic License 1.0 or the GNU General Public\nLicense version 2.\n\n=cut\n';
45191
45351
  return {
45192
45352
  repoRoot: "/",
45193
- includePaths: [],
45353
+ includePaths: ["/__zuzu_browser_modules__/0"],
45194
45354
  jsModules,
45195
45355
  virtualFiles
45196
45356
  };
@@ -45566,7 +45726,7 @@ class ZZTemplate extends ZTemplate {
45566
45726
  }
45567
45727
  });
45568
45728
 
45569
- // ../../../../../tmp/zuzu-browser-build.yNRBlw/browser-worker-entry.generated.js
45729
+ // ../../../../../tmp/zuzu-browser-build.WePoiO/browser-worker-entry.generated.js
45570
45730
  var { createBrowserStdlib } = require_browser_stdlib_generated();
45571
45731
  globalThis.__ZUZU_BROWSER_DEFAULT_RUNTIME_OPTIONS__ = createBrowserStdlib();
45572
45732
  var { installBrowserWorker } = require_browser_worker_entry();