@mojir/dvala 0.0.17 → 0.0.19

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.
Files changed (2) hide show
  1. package/dist/cli/cli.js +289 -194
  2. package/package.json +7 -3
package/dist/cli/cli.js CHANGED
@@ -478,7 +478,7 @@ function findAllOccurrences(input, pattern) {
478
478
  }
479
479
  //#endregion
480
480
  //#region package.json
481
- var version = "0.0.17";
481
+ var version = "0.0.19";
482
482
  //#endregion
483
483
  //#region src/typeGuards/string.ts
484
484
  function isString(value, options = {}) {
@@ -5232,7 +5232,7 @@ function generateDocString(reference) {
5232
5232
  ${argStrings(reference).join("\n ")}
5233
5233
 
5234
5234
  Examples:
5235
- ${reference.examples.map((example) => smartTrim(example, 4)).join("\n\n")}`);
5235
+ ${reference.examples.map((example) => smartTrim(typeof example === "string" ? example : example.code, 4)).join("\n\n")}`);
5236
5236
  }
5237
5237
  function isEffectRef(ref) {
5238
5238
  return "effect" in ref;
@@ -24391,7 +24391,13 @@ const standardEffects = {
24391
24391
  description: "Value to print."
24392
24392
  } },
24393
24393
  variants: [{ argumentNames: ["value"] }],
24394
- examples: ["perform(effect(dvala.io.print), \"hello\")", "perform(effect(dvala.io.print), 42)"],
24394
+ examples: [{
24395
+ code: "perform(effect(dvala.io.print), \"hello\")",
24396
+ noRun: true
24397
+ }, {
24398
+ code: "perform(effect(dvala.io.print), 42)",
24399
+ noRun: true
24400
+ }],
24395
24401
  seeAlso: [
24396
24402
  "-effect-dvala.io.println",
24397
24403
  "-effect-dvala.io.error",
@@ -24413,7 +24419,13 @@ const standardEffects = {
24413
24419
  description: "Value to print."
24414
24420
  } },
24415
24421
  variants: [{ argumentNames: ["value"] }],
24416
- examples: ["perform(effect(dvala.io.println), \"hello\")", "perform(effect(dvala.io.println), [1, 2, 3])"],
24422
+ examples: [{
24423
+ code: "perform(effect(dvala.io.println), \"hello\")",
24424
+ noRun: true
24425
+ }, {
24426
+ code: "perform(effect(dvala.io.println), [1, 2, 3])",
24427
+ noRun: true
24428
+ }],
24417
24429
  seeAlso: [
24418
24430
  "-effect-dvala.io.print",
24419
24431
  "-effect-dvala.io.error",
@@ -24445,7 +24457,10 @@ const standardEffects = {
24445
24457
  description: "Value to write to stderr."
24446
24458
  } },
24447
24459
  variants: [{ argumentNames: ["value"] }],
24448
- examples: ["perform(effect(dvala.io.error), \"something went wrong\")"],
24460
+ examples: [{
24461
+ code: "perform(effect(dvala.io.error), \"something went wrong\")",
24462
+ noRun: true
24463
+ }],
24449
24464
  seeAlso: [
24450
24465
  "-effect-dvala.io.print",
24451
24466
  "-effect-dvala.io.println",
@@ -24999,8 +25014,13 @@ const datatype = {
24999
25014
  datatype: true,
25000
25015
  title: "string",
25001
25016
  category: "datatype",
25002
- description: "A `string`",
25003
- examples: ["\"hello\"", "\"\""]
25017
+ description: "A `string`. Strings are written with double quotes. Template strings use backticks and support `${...}` interpolation — any expression can appear inside the braces.",
25018
+ examples: [
25019
+ "\"hello\"",
25020
+ "\"\"",
25021
+ "`Hello, World!`",
25022
+ "`${2} * ${3} = ${2 * 3}`"
25023
+ ]
25004
25024
  },
25005
25025
  "-type-object": {
25006
25026
  datatype: true,
@@ -25326,14 +25346,11 @@ function deriveEffectReference() {
25326
25346
  return result;
25327
25347
  }
25328
25348
  const effectReference = deriveEffectReference();
25329
- const allReference = sortByCategory({
25349
+ sortByCategory({
25330
25350
  ...apiReference,
25331
25351
  ...moduleReference,
25332
25352
  ...effectReference
25333
25353
  });
25334
- Object.values(allReference).forEach((ref) => {
25335
- ref.title = ref.title.replace(/"/g, """);
25336
- });
25337
25354
  function sortByCategory(ref) {
25338
25355
  return Object.fromEntries(Object.entries(ref).sort(([keyA, refA], [keyB, refB]) => {
25339
25356
  const catA = refA.category === "special-expression" ? "" : refA.category;
@@ -26828,6 +26845,9 @@ function isStringToken(token) {
26828
26845
  function isA_BinaryOperatorToken(token) {
26829
26846
  return token?.[0] === "Operator" && isBinaryOperator(token[1]);
26830
26847
  }
26848
+ function isTemplateStringToken(token) {
26849
+ return token?.[0] === "TemplateString";
26850
+ }
26831
26851
  function throwUnexpectedToken(expected, expectedValue, actual) {
26832
26852
  throw new DvalaError(`Unexpected token: ${actual ? `${actual[0]} '${actual[1]}'` : "end of input"}, expected ${expected}${expectedValue ? ` '${expectedValue}'` : ""}`, actual?.[2]);
26833
26853
  }
@@ -27122,6 +27142,176 @@ function parseNumber(ctx) {
27122
27142
  return withSourceCodeInfo([NodeTypes.Number, negative ? -Number(numberString) : Number(numberString)], token[2]);
27123
27143
  }
27124
27144
  //#endregion
27145
+ //#region src/tokenizer/minifyTokenStream.ts
27146
+ function minifyTokenStream(tokenStream, { removeWhiteSpace }) {
27147
+ const tokens = tokenStream.tokens.filter((token) => {
27148
+ if (isSingleLineCommentToken(token) || isMultiLineCommentToken(token) || isShebangToken(token) || removeWhiteSpace && isWhitespaceToken(token)) return false;
27149
+ return true;
27150
+ });
27151
+ return {
27152
+ ...tokenStream,
27153
+ tokens
27154
+ };
27155
+ }
27156
+ //#endregion
27157
+ //#region src/parser/subParsers/parseTemplateString.ts
27158
+ /**
27159
+ * Scan from `start` inside a `${...}` interpolation until the matching `}`.
27160
+ * Returns the expression source text (without the outer `${` and `}`) and
27161
+ * the number of characters consumed (including the closing `}`).
27162
+ */
27163
+ function scanExpression(raw, start) {
27164
+ let i = start;
27165
+ let expr = "";
27166
+ let depth = 1;
27167
+ while (i < raw.length && depth > 0) {
27168
+ const c = raw[i];
27169
+ if (c === "{") {
27170
+ depth++;
27171
+ expr += c;
27172
+ i++;
27173
+ } else if (c === "}") {
27174
+ depth--;
27175
+ if (depth > 0) expr += c;
27176
+ i++;
27177
+ } else if (c === "\"") {
27178
+ const { str, consumed } = scanString(raw, i);
27179
+ expr += str;
27180
+ i += consumed;
27181
+ } else if (c === "'") {
27182
+ const { str, consumed } = scanQuotedSymbol(raw, i);
27183
+ expr += str;
27184
+ i += consumed;
27185
+ } else if (c === "`") {
27186
+ const { str, consumed } = scanNestedTemplate(raw, i);
27187
+ expr += str;
27188
+ i += consumed;
27189
+ } else {
27190
+ expr += c;
27191
+ i++;
27192
+ }
27193
+ }
27194
+ return {
27195
+ expr,
27196
+ consumed: i - start
27197
+ };
27198
+ }
27199
+ function scanString(raw, start) {
27200
+ let i = start + 1;
27201
+ let str = "\"";
27202
+ let escaping = false;
27203
+ while (i < raw.length) {
27204
+ const c = raw[i];
27205
+ str += c;
27206
+ i++;
27207
+ if (escaping) escaping = false;
27208
+ else if (c === "\\") escaping = true;
27209
+ else if (c === "\"") break;
27210
+ }
27211
+ return {
27212
+ str,
27213
+ consumed: i - start
27214
+ };
27215
+ }
27216
+ function scanQuotedSymbol(raw, start) {
27217
+ let i = start + 1;
27218
+ let str = "'";
27219
+ let escaping = false;
27220
+ while (i < raw.length) {
27221
+ const c = raw[i];
27222
+ str += c;
27223
+ i++;
27224
+ if (escaping) escaping = false;
27225
+ else if (c === "\\") escaping = true;
27226
+ else if (c === "'") break;
27227
+ }
27228
+ return {
27229
+ str,
27230
+ consumed: i - start
27231
+ };
27232
+ }
27233
+ /**
27234
+ * Scan a full nested template string starting at `start` (pointing at the opening backtick).
27235
+ * Handles ${...} spans inside the template recursively.
27236
+ */
27237
+ function scanNestedTemplate(raw, start) {
27238
+ let i = start + 1;
27239
+ let str = "`";
27240
+ while (i < raw.length) {
27241
+ const c = raw[i];
27242
+ if (c === "`") {
27243
+ str += c;
27244
+ i++;
27245
+ break;
27246
+ } else if (c === "$" && raw[i + 1] === "{") {
27247
+ str += "${";
27248
+ i += 2;
27249
+ const { expr, consumed } = scanExpression(raw, i);
27250
+ str += `${expr}}`;
27251
+ i += consumed;
27252
+ } else {
27253
+ str += c;
27254
+ i++;
27255
+ }
27256
+ }
27257
+ return {
27258
+ str,
27259
+ consumed: i - start
27260
+ };
27261
+ }
27262
+ /**
27263
+ * Split the raw content of a template string (between the surrounding backticks)
27264
+ * into alternating literal and expression segments.
27265
+ */
27266
+ function splitSegments(raw) {
27267
+ const segments = [];
27268
+ let i = 0;
27269
+ let literal = "";
27270
+ while (i < raw.length) if (raw[i] === "$" && raw[i + 1] === "{") {
27271
+ if (literal.length > 0) {
27272
+ segments.push({
27273
+ type: "literal",
27274
+ value: literal
27275
+ });
27276
+ literal = "";
27277
+ }
27278
+ i += 2;
27279
+ const { expr, consumed } = scanExpression(raw, i);
27280
+ i += consumed;
27281
+ segments.push({
27282
+ type: "expression",
27283
+ value: expr
27284
+ });
27285
+ } else {
27286
+ literal += raw[i];
27287
+ i++;
27288
+ }
27289
+ if (literal.length > 0) segments.push({
27290
+ type: "literal",
27291
+ value: literal
27292
+ });
27293
+ return segments;
27294
+ }
27295
+ function parseTemplateString(ctx, token) {
27296
+ ctx.advance();
27297
+ const sourceCodeInfo = token[2];
27298
+ const segments = splitSegments(token[1].slice(1, -1));
27299
+ if (segments.length === 0) return withSourceCodeInfo([NodeTypes.String, ""], sourceCodeInfo);
27300
+ if (segments.length === 1 && segments[0].type === "literal") return withSourceCodeInfo([NodeTypes.String, segments[0].value], sourceCodeInfo);
27301
+ const segmentNodes = [];
27302
+ for (const segment of segments) if (segment.type === "literal") {
27303
+ if (segment.value.length === 0) continue;
27304
+ segmentNodes.push(withSourceCodeInfo([NodeTypes.String, segment.value], sourceCodeInfo));
27305
+ } else {
27306
+ if (segment.value.trim().length === 0) throw new DvalaError("Empty interpolation in template string", sourceCodeInfo);
27307
+ const minified = minifyTokenStream(tokenize(segment.value, false, sourceCodeInfo?.filePath), { removeWhiteSpace: true });
27308
+ for (const t of minified.tokens) if (t[0] === "Error") throw new DvalaError(`Template string interpolation error: ${t[3]}`, sourceCodeInfo);
27309
+ const expr = parseExpression(createParserContext(minified), 0);
27310
+ segmentNodes.push(expr);
27311
+ }
27312
+ return withSourceCodeInfo([NodeTypes.TemplateString, segmentNodes], sourceCodeInfo);
27313
+ }
27314
+ //#endregion
27125
27315
  //#region src/parser/subParsers/parseBindingTarget.ts
27126
27316
  function parseBindingTarget(ctx, { requireDefaultValue, noRest, allowLiteralPatterns } = {}) {
27127
27317
  const firstToken = ctx.tryPeek();
@@ -27134,6 +27324,10 @@ function parseBindingTarget(ctx, { requireDefaultValue, noRest, allowLiteralPatt
27134
27324
  const node = parseNumber(ctx);
27135
27325
  return withSourceCodeInfo([bindingTargetTypes.literal, [node]], firstToken[2]);
27136
27326
  }
27327
+ if (isTemplateStringToken(firstToken)) {
27328
+ const node = parseTemplateString(ctx, firstToken);
27329
+ return withSourceCodeInfo([bindingTargetTypes.literal, [node]], firstToken[2]);
27330
+ }
27137
27331
  if (isStringToken(firstToken)) {
27138
27332
  const node = parseString(ctx, firstToken);
27139
27333
  return withSourceCodeInfo([bindingTargetTypes.literal, [node]], firstToken[2]);
@@ -27251,7 +27445,7 @@ function parseOptionalDefaulValue(ctx) {
27251
27445
  }
27252
27446
  }
27253
27447
  function isLiteralToken(token) {
27254
- return isNumberToken(token) || isBasePrefixedNumberToken(token) || isStringToken(token) || isReservedSymbolToken(token, "true") || isReservedSymbolToken(token, "false") || isReservedSymbolToken(token, "null");
27448
+ return isNumberToken(token) || isBasePrefixedNumberToken(token) || isStringToken(token) || isTemplateStringToken(token) || isReservedSymbolToken(token, "true") || isReservedSymbolToken(token, "false") || isReservedSymbolToken(token, "null");
27255
27449
  }
27256
27450
  //#endregion
27257
27451
  //#region src/parser/subParsers/parseLet.ts
@@ -27631,7 +27825,8 @@ function parseObject(ctx) {
27631
27825
  params.push(withSourceCodeInfo([NodeTypes.Spread, ctx.parseExpression()], ctx.peekSourceCodeInfo()));
27632
27826
  } else {
27633
27827
  const token = ctx.tryPeek();
27634
- if (isStringToken(token)) {
27828
+ if (isTemplateStringToken(token)) params.push(parseTemplateString(ctx, token));
27829
+ else if (isStringToken(token)) {
27635
27830
  const stringNode = parseString(ctx, token);
27636
27831
  params.push(withSourceCodeInfo([NodeTypes.String, stringNode[1]], token[2]));
27637
27832
  } else if (isSymbolToken(token)) {
@@ -27657,176 +27852,6 @@ function parseObject(ctx) {
27657
27852
  return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.object, params]], firstToken[2]);
27658
27853
  }
27659
27854
  //#endregion
27660
- //#region src/tokenizer/minifyTokenStream.ts
27661
- function minifyTokenStream(tokenStream, { removeWhiteSpace }) {
27662
- const tokens = tokenStream.tokens.filter((token) => {
27663
- if (isSingleLineCommentToken(token) || isMultiLineCommentToken(token) || isShebangToken(token) || removeWhiteSpace && isWhitespaceToken(token)) return false;
27664
- return true;
27665
- });
27666
- return {
27667
- ...tokenStream,
27668
- tokens
27669
- };
27670
- }
27671
- //#endregion
27672
- //#region src/parser/subParsers/parseTemplateString.ts
27673
- /**
27674
- * Scan from `start` inside a `${...}` interpolation until the matching `}`.
27675
- * Returns the expression source text (without the outer `${` and `}`) and
27676
- * the number of characters consumed (including the closing `}`).
27677
- */
27678
- function scanExpression(raw, start) {
27679
- let i = start;
27680
- let expr = "";
27681
- let depth = 1;
27682
- while (i < raw.length && depth > 0) {
27683
- const c = raw[i];
27684
- if (c === "{") {
27685
- depth++;
27686
- expr += c;
27687
- i++;
27688
- } else if (c === "}") {
27689
- depth--;
27690
- if (depth > 0) expr += c;
27691
- i++;
27692
- } else if (c === "\"") {
27693
- const { str, consumed } = scanString(raw, i);
27694
- expr += str;
27695
- i += consumed;
27696
- } else if (c === "'") {
27697
- const { str, consumed } = scanQuotedSymbol(raw, i);
27698
- expr += str;
27699
- i += consumed;
27700
- } else if (c === "`") {
27701
- const { str, consumed } = scanNestedTemplate(raw, i);
27702
- expr += str;
27703
- i += consumed;
27704
- } else {
27705
- expr += c;
27706
- i++;
27707
- }
27708
- }
27709
- return {
27710
- expr,
27711
- consumed: i - start
27712
- };
27713
- }
27714
- function scanString(raw, start) {
27715
- let i = start + 1;
27716
- let str = "\"";
27717
- let escaping = false;
27718
- while (i < raw.length) {
27719
- const c = raw[i];
27720
- str += c;
27721
- i++;
27722
- if (escaping) escaping = false;
27723
- else if (c === "\\") escaping = true;
27724
- else if (c === "\"") break;
27725
- }
27726
- return {
27727
- str,
27728
- consumed: i - start
27729
- };
27730
- }
27731
- function scanQuotedSymbol(raw, start) {
27732
- let i = start + 1;
27733
- let str = "'";
27734
- let escaping = false;
27735
- while (i < raw.length) {
27736
- const c = raw[i];
27737
- str += c;
27738
- i++;
27739
- if (escaping) escaping = false;
27740
- else if (c === "\\") escaping = true;
27741
- else if (c === "'") break;
27742
- }
27743
- return {
27744
- str,
27745
- consumed: i - start
27746
- };
27747
- }
27748
- /**
27749
- * Scan a full nested template string starting at `start` (pointing at the opening backtick).
27750
- * Handles ${...} spans inside the template recursively.
27751
- */
27752
- function scanNestedTemplate(raw, start) {
27753
- let i = start + 1;
27754
- let str = "`";
27755
- while (i < raw.length) {
27756
- const c = raw[i];
27757
- if (c === "`") {
27758
- str += c;
27759
- i++;
27760
- break;
27761
- } else if (c === "$" && raw[i + 1] === "{") {
27762
- str += "${";
27763
- i += 2;
27764
- const { expr, consumed } = scanExpression(raw, i);
27765
- str += `${expr}}`;
27766
- i += consumed;
27767
- } else {
27768
- str += c;
27769
- i++;
27770
- }
27771
- }
27772
- return {
27773
- str,
27774
- consumed: i - start
27775
- };
27776
- }
27777
- /**
27778
- * Split the raw content of a template string (between the surrounding backticks)
27779
- * into alternating literal and expression segments.
27780
- */
27781
- function splitSegments(raw) {
27782
- const segments = [];
27783
- let i = 0;
27784
- let literal = "";
27785
- while (i < raw.length) if (raw[i] === "$" && raw[i + 1] === "{") {
27786
- if (literal.length > 0) {
27787
- segments.push({
27788
- type: "literal",
27789
- value: literal
27790
- });
27791
- literal = "";
27792
- }
27793
- i += 2;
27794
- const { expr, consumed } = scanExpression(raw, i);
27795
- i += consumed;
27796
- segments.push({
27797
- type: "expression",
27798
- value: expr
27799
- });
27800
- } else {
27801
- literal += raw[i];
27802
- i++;
27803
- }
27804
- if (literal.length > 0) segments.push({
27805
- type: "literal",
27806
- value: literal
27807
- });
27808
- return segments;
27809
- }
27810
- function parseTemplateString(ctx, token) {
27811
- ctx.advance();
27812
- const sourceCodeInfo = token[2];
27813
- const segments = splitSegments(token[1].slice(1, -1));
27814
- if (segments.length === 0) return withSourceCodeInfo([NodeTypes.String, ""], sourceCodeInfo);
27815
- if (segments.length === 1 && segments[0].type === "literal") return withSourceCodeInfo([NodeTypes.String, segments[0].value], sourceCodeInfo);
27816
- const segmentNodes = [];
27817
- for (const segment of segments) if (segment.type === "literal") {
27818
- if (segment.value.length === 0) continue;
27819
- segmentNodes.push(withSourceCodeInfo([NodeTypes.String, segment.value], sourceCodeInfo));
27820
- } else {
27821
- if (segment.value.trim().length === 0) throw new DvalaError("Empty interpolation in template string", sourceCodeInfo);
27822
- const minified = minifyTokenStream(tokenize(segment.value, false, sourceCodeInfo?.filePath), { removeWhiteSpace: true });
27823
- for (const t of minified.tokens) if (t[0] === "Error") throw new DvalaError(`Template string interpolation error: ${t[3]}`, sourceCodeInfo);
27824
- const expr = parseExpression(createParserContext(minified), 0);
27825
- segmentNodes.push(expr);
27826
- }
27827
- return withSourceCodeInfo([NodeTypes.TemplateString, segmentNodes], sourceCodeInfo);
27828
- }
27829
- //#endregion
27830
27855
  //#region src/parser/subParsers/parseOperand.ts
27831
27856
  function parseOperand(ctx) {
27832
27857
  let operand = parseOperandPart(ctx);
@@ -28097,6 +28122,22 @@ var ResumeFromSignal = class {
28097
28122
  function isResumeFromSignal(value) {
28098
28123
  return value instanceof ResumeFromSignal;
28099
28124
  }
28125
+ /**
28126
+ * Thrown (as a promise rejection) by `halt()` inside a host handler.
28127
+ * Caught by the effect trampoline loop — NOT by Dvala-level try/catch.
28128
+ * Terminates execution immediately and returns a halted result.
28129
+ */
28130
+ var HaltSignal = class {
28131
+ _brand = "HaltSignal";
28132
+ constructor(value, snapshots, nextSnapshotIndex) {
28133
+ this.value = value;
28134
+ this.snapshots = snapshots;
28135
+ this.nextSnapshotIndex = nextSnapshotIndex;
28136
+ }
28137
+ };
28138
+ function isHaltSignal(value) {
28139
+ return value instanceof HaltSignal;
28140
+ }
28100
28141
  //#endregion
28101
28142
  //#region src/evaluator/effectRef.ts
28102
28143
  const internMap = /* @__PURE__ */ new Map();
@@ -30658,6 +30699,8 @@ function dispatchHostHandler(effectName, matchingHandlers, args, k, signal, sour
30658
30699
  }
30659
30700
  function tryHandler(index) {
30660
30701
  if (index >= matchingHandlers.length) {
30702
+ const standardHandler = getStandardEffectHandler(effectName);
30703
+ if (standardHandler) return standardHandler(args, k, sourceCodeInfo);
30661
30704
  if (effectName === "dvala.error") throw new UserDefinedError(typeof argsArray[0] === "string" ? argsArray[0] : String(argsArray[0] ?? "Unknown error"), sourceCodeInfo);
30662
30705
  if (effectName === "dvala.checkpoint") return {
30663
30706
  type: "Value",
@@ -30666,11 +30709,7 @@ function dispatchHostHandler(effectName, matchingHandlers, args, k, signal, sour
30666
30709
  };
30667
30710
  throw new DvalaError(`Unhandled effect: '${effectName}'`, sourceCodeInfo);
30668
30711
  }
30669
- const [pattern, handler] = matchingHandlers[index];
30670
- if (pattern === "*") {
30671
- const standardHandler = getStandardEffectHandler(effectName);
30672
- if (standardHandler) return standardHandler(args, k, sourceCodeInfo);
30673
- }
30712
+ const [_pattern, handler] = matchingHandlers[index];
30674
30713
  let outcome;
30675
30714
  let settled = false;
30676
30715
  function assertNotSettled(operation) {
@@ -30745,10 +30784,17 @@ function dispatchHostHandler(effectName, matchingHandlers, args, k, signal, sour
30745
30784
  kind: "throw",
30746
30785
  error: new ResumeFromSignal(found.continuation, value, found.index)
30747
30786
  };
30787
+ },
30788
+ halt: (value = null) => {
30789
+ assertNotSettled("halt");
30790
+ outcome = {
30791
+ kind: "throw",
30792
+ error: new HaltSignal(value, snapshotState ? snapshotState.snapshots : [], snapshotState ? snapshotState.nextSnapshotIndex : 0)
30793
+ };
30748
30794
  }
30749
30795
  });
30750
30796
  if (!(handlerResult instanceof Promise)) {
30751
- if (!outcome) throw new DvalaError(`Effect handler for '${effectName}' did not call resume(), fail(), suspend(), or next()`, sourceCodeInfo);
30797
+ if (!outcome) throw new DvalaError(`Effect handler for '${effectName}' did not call resume(), fail(), suspend(), halt(), or next()`, sourceCodeInfo);
30752
30798
  return resolveOutcome(outcome, index + 1);
30753
30799
  }
30754
30800
  if (outcome) {
@@ -30756,11 +30802,11 @@ function dispatchHostHandler(effectName, matchingHandlers, args, k, signal, sour
30756
30802
  return resolveOutcome(outcome, index + 1);
30757
30803
  }
30758
30804
  return handlerResult.then(() => {
30759
- if (!outcome) throw new DvalaError(`Effect handler for '${effectName}' did not call resume(), fail(), suspend(), or next()`, sourceCodeInfo);
30805
+ if (!outcome) throw new DvalaError(`Effect handler for '${effectName}' did not call resume(), fail(), suspend(), halt(), or next()`, sourceCodeInfo);
30760
30806
  return resolveOutcome(outcome, index + 1);
30761
30807
  }, (e) => {
30762
30808
  if (outcome) return resolveOutcome(outcome, index + 1);
30763
- if (isSuspensionSignal(e) || isResumeFromSignal(e)) throw e;
30809
+ if (isSuspensionSignal(e) || isResumeFromSignal(e) || isHaltSignal(e)) throw e;
30764
30810
  return {
30765
30811
  type: "Error",
30766
30812
  error: e instanceof DvalaError ? e : new DvalaError(e instanceof Error ? e : `${e}`, sourceCodeInfo),
@@ -31684,7 +31730,7 @@ function tick(step, handlers, signal, snapshotState) {
31684
31730
  }
31685
31731
  }
31686
31732
  } catch (error) {
31687
- if (isSuspensionSignal(error)) throw error;
31733
+ if (isSuspensionSignal(error) || isHaltSignal(error)) throw error;
31688
31734
  if (error instanceof DvalaError) {
31689
31735
  const effectStep = tryDispatchDvalaError(error, step.type === "Value" ? step.k.slice(1) : step.k, handlers, signal, snapshotState);
31690
31736
  if (effectStep !== null) return effectStep;
@@ -31820,8 +31866,9 @@ async function runEffectLoop(initial, handlers, signal, initialSnapshotState, ma
31820
31866
  const continuation = serializeTerminalSnapshot(snapshotState.snapshots, snapshotState.nextSnapshotIndex);
31821
31867
  const meta = {};
31822
31868
  if (options?.error) meta.error = options.error.toJSON();
31869
+ if (options?.halted) meta.halted = true;
31823
31870
  if (options?.result !== void 0) meta.result = options.result;
31824
- const message = options?.error ? "Run failed with error" : "Run completed successfully";
31871
+ const message = options?.error ? "Run failed with error" : options?.halted ? "Program halted" : "Run completed successfully";
31825
31872
  return createSnapshot({
31826
31873
  continuation,
31827
31874
  timestamp: Date.now(),
@@ -31888,6 +31935,20 @@ async function runEffectLoop(initial, handlers, signal, initialSnapshotState, ma
31888
31935
  effectArgs: error.effectArgs
31889
31936
  })
31890
31937
  };
31938
+ if (isHaltSignal(error)) {
31939
+ const snapshot = createTerminalSnapshot({
31940
+ result: error.value,
31941
+ halted: true
31942
+ });
31943
+ return snapshot ? {
31944
+ type: "halted",
31945
+ value: error.value,
31946
+ snapshot
31947
+ } : {
31948
+ type: "halted",
31949
+ value: error.value
31950
+ };
31951
+ }
31891
31952
  if (error instanceof DvalaError) {
31892
31953
  const snapshot = createTerminalSnapshot({ error });
31893
31954
  return snapshot ? {
@@ -32002,6 +32063,13 @@ var Cache = class {
32002
32063
  * a Dvala instance.
32003
32064
  */
32004
32065
  /**
32066
+ * Tokenize a Dvala source string into a token stream.
32067
+ * Pass `debug: true` to capture source positions (needed for the debugger).
32068
+ */
32069
+ function tokenizeSource(source, debug = false, filePath) {
32070
+ return tokenize(source, debug, filePath);
32071
+ }
32072
+ /**
32005
32073
  * Get all undefined symbols in a Dvala program.
32006
32074
  *
32007
32075
  * @param source - Dvala source code
@@ -33741,11 +33809,38 @@ const getInlineCodeRule = (fmt) => (text, index) => {
33741
33809
  formattedText: ""
33742
33810
  };
33743
33811
  };
33812
+ function getTemplateStringRule(fmt) {
33813
+ return (text, index) => {
33814
+ if (text[index] !== "`") return noMatch;
33815
+ try {
33816
+ const { tokens } = tokenizeSource(text.slice(index));
33817
+ const first = tokens[0];
33818
+ if (!first || first[0] !== "TemplateString") return noMatch;
33819
+ const count = first[1].length;
33820
+ const segments = splitSegments(first[1].slice(1, -1));
33821
+ let formattedText = `${Colors.FgRed}\`${Colors.Reset}`;
33822
+ for (const seg of segments) if (seg.type === "literal") formattedText += Colors.FgRed + seg.value + Colors.Reset;
33823
+ else {
33824
+ formattedText += `${Colors.Bright}${Colors.FgWhite}\${${Colors.Reset}`;
33825
+ formattedText += getDvalaFormatter(fmt)(seg.value);
33826
+ formattedText += `${Colors.Bright}${Colors.FgWhite}}${Colors.Reset}`;
33827
+ }
33828
+ formattedText += `${Colors.FgRed}\`${Colors.Reset}`;
33829
+ return {
33830
+ count,
33831
+ formattedText
33832
+ };
33833
+ } catch {
33834
+ return noMatch;
33835
+ }
33836
+ };
33837
+ }
33744
33838
  function getDvalaExpressionRules(cli) {
33745
33839
  return [
33746
33840
  commentRule,
33747
33841
  stringRule,
33748
33842
  shortcutStringRule,
33843
+ getTemplateStringRule(cli),
33749
33844
  functionNameRule,
33750
33845
  getNumberRule(cli),
33751
33846
  dvalaKeywordRule,
@@ -33863,7 +33958,7 @@ function getArgumentInfo(fmt, reference) {
33863
33958
  const dvala = createDvala({ debug: false });
33864
33959
  function getCliFunctionExamples(fmt, reference) {
33865
33960
  const { examples } = reference;
33866
- return examples.map((example) => example.trim()).map((example) => {
33961
+ return examples.map((example) => (typeof example === "string" ? example : example.code).trim()).map((example) => {
33867
33962
  const oldLog = console.log;
33868
33963
  console.log = function() {};
33869
33964
  let result;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mojir/dvala",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "description": "dvala",
5
5
  "author": "Albert Mojir",
6
6
  "license": "MIT",
@@ -136,7 +136,7 @@
136
136
  "build-playground": "wireit",
137
137
  "build-vscode-ext": "wireit",
138
138
  "dvala": "node ./dist/cli/cli.js",
139
- "dev": "npx serve docs -p 9901",
139
+ "dev": "npx serve docs -p 9901 --single",
140
140
  "test:e2e": "npx playwright test",
141
141
  "test:e2e:prod": "E2E_BASE_URL=https://mojir.github.io/dvala npx playwright test",
142
142
  "test:e2e:headed": "npx playwright test --headed",
@@ -157,7 +157,7 @@
157
157
  "@vscode/vsce": "^3.7.1",
158
158
  "esbuild": "^0.27.3",
159
159
  "eslint": "^10.0.3",
160
- "open-cli": "8.0.0",
160
+ "open-cli": "^5.0.0",
161
161
  "rolldown": "^1.0.0-rc.8",
162
162
  "serve": "^14.2.6",
163
163
  "tslib": "^2.8.1",
@@ -298,6 +298,7 @@
298
298
  "src/**",
299
299
  "common/**",
300
300
  "reference/**/*.ts",
301
+ "tutorials/**",
301
302
  "rolldown.config.playground-builder.mjs",
302
303
  "rolldown.config.playground-www.mjs",
303
304
  "rolldown.plugins.mjs",
@@ -326,5 +327,8 @@
326
327
  ],
327
328
  "clean": "if-file-deleted"
328
329
  }
330
+ },
331
+ "dependencies": {
332
+ "marked": "^17.0.4"
329
333
  }
330
334
  }