@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.
- package/dist/cli/cli.js +289 -194
- 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.
|
|
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: [
|
|
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: [
|
|
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: [
|
|
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: [
|
|
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
|
-
|
|
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 (
|
|
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 [
|
|
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.
|
|
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": "
|
|
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
|
}
|