@mojir/dvala 0.0.15 → 0.0.17
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 +436 -14
- package/package.json +3 -1
package/dist/cli/cli.js
CHANGED
|
@@ -118,7 +118,8 @@ const NodeTypes = {
|
|
|
118
118
|
SpecialBuiltinSymbol: 7,
|
|
119
119
|
ReservedSymbol: 8,
|
|
120
120
|
Binding: 9,
|
|
121
|
-
Spread: 10
|
|
121
|
+
Spread: 10,
|
|
122
|
+
TemplateString: 11
|
|
122
123
|
};
|
|
123
124
|
const NodeTypesSet = new Set(Object.values(NodeTypes));
|
|
124
125
|
function getNodeTypeName(type) {
|
|
@@ -477,7 +478,7 @@ function findAllOccurrences(input, pattern) {
|
|
|
477
478
|
}
|
|
478
479
|
//#endregion
|
|
479
480
|
//#region package.json
|
|
480
|
-
var version = "0.0.
|
|
481
|
+
var version = "0.0.17";
|
|
481
482
|
//#endregion
|
|
482
483
|
//#region src/typeGuards/string.ts
|
|
483
484
|
function isString(value, options = {}) {
|
|
@@ -24481,6 +24482,128 @@ const standardEffects = {
|
|
|
24481
24482
|
"-effect-dvala.io.read-stdin",
|
|
24482
24483
|
"-effect-dvala.io.print",
|
|
24483
24484
|
"-effect-dvala.io.println",
|
|
24485
|
+
"-effect-dvala.io.pick",
|
|
24486
|
+
"-effect-dvala.io.confirm",
|
|
24487
|
+
"perform",
|
|
24488
|
+
"effect"
|
|
24489
|
+
]
|
|
24490
|
+
}
|
|
24491
|
+
},
|
|
24492
|
+
"dvala.io.pick": {
|
|
24493
|
+
handler: (args, k, sourceCodeInfo) => {
|
|
24494
|
+
const items = args[0];
|
|
24495
|
+
const options = args[1];
|
|
24496
|
+
if (!Array.isArray(items)) throw new DvalaError(`dvala.io.pick: first argument must be an array, got ${typeof items}`, sourceCodeInfo);
|
|
24497
|
+
if (items.length === 0) throw new DvalaError("dvala.io.pick: items array must not be empty", sourceCodeInfo);
|
|
24498
|
+
for (let i = 0; i < items.length; i++) if (typeof items[i] !== "string") throw new DvalaError(`dvala.io.pick: items[${i}] must be a string, got ${typeof items[i]}`, sourceCodeInfo);
|
|
24499
|
+
let promptMessage;
|
|
24500
|
+
let defaultIndex;
|
|
24501
|
+
if (options !== void 0) {
|
|
24502
|
+
if (typeof options !== "object" || options === null || Array.isArray(options)) throw new DvalaError(`dvala.io.pick: second argument must be an object, got ${typeof options}`, sourceCodeInfo);
|
|
24503
|
+
const opts = options;
|
|
24504
|
+
if (opts["prompt"] !== void 0) {
|
|
24505
|
+
if (typeof opts["prompt"] !== "string") throw new DvalaError("dvala.io.pick: options.prompt must be a string", sourceCodeInfo);
|
|
24506
|
+
promptMessage = opts["prompt"];
|
|
24507
|
+
}
|
|
24508
|
+
if (opts["default"] !== void 0) {
|
|
24509
|
+
if (typeof opts["default"] !== "number" || !Number.isInteger(opts["default"])) throw new DvalaError("dvala.io.pick: options.default must be an integer", sourceCodeInfo);
|
|
24510
|
+
defaultIndex = opts["default"];
|
|
24511
|
+
if (defaultIndex < 0 || defaultIndex >= items.length) throw new DvalaError(`dvala.io.pick: options.default (${defaultIndex}) is out of bounds for array of length ${items.length}`, sourceCodeInfo);
|
|
24512
|
+
}
|
|
24513
|
+
}
|
|
24514
|
+
if (typeof globalThis.prompt === "function") {
|
|
24515
|
+
const listLines = items.map((item, i) => `${i}: ${item}`).join("\n");
|
|
24516
|
+
const message = `${promptMessage ?? "Choose an item:"}${defaultIndex !== void 0 ? ` [default: ${defaultIndex}]` : ""}\n${listLines}`;
|
|
24517
|
+
const result = globalThis.prompt(message);
|
|
24518
|
+
if (result === null) return {
|
|
24519
|
+
type: "Value",
|
|
24520
|
+
value: null,
|
|
24521
|
+
k
|
|
24522
|
+
};
|
|
24523
|
+
const trimmed = result.trim();
|
|
24524
|
+
if (trimmed === "") return {
|
|
24525
|
+
type: "Value",
|
|
24526
|
+
value: defaultIndex !== void 0 ? defaultIndex : null,
|
|
24527
|
+
k
|
|
24528
|
+
};
|
|
24529
|
+
const parsed = Number(trimmed);
|
|
24530
|
+
if (!Number.isInteger(parsed) || parsed < 0 || parsed >= items.length) throw new DvalaError(`dvala.io.pick: invalid selection "${trimmed}"`, sourceCodeInfo);
|
|
24531
|
+
return {
|
|
24532
|
+
type: "Value",
|
|
24533
|
+
value: parsed,
|
|
24534
|
+
k
|
|
24535
|
+
};
|
|
24536
|
+
}
|
|
24537
|
+
throw new DvalaError("dvala.io.pick is not supported in this environment. In Node.js, register a \"dvala.io.pick\" host handler.", sourceCodeInfo);
|
|
24538
|
+
},
|
|
24539
|
+
arity: {
|
|
24540
|
+
min: 1,
|
|
24541
|
+
max: 2
|
|
24542
|
+
},
|
|
24543
|
+
docs: {
|
|
24544
|
+
category: "effect",
|
|
24545
|
+
description: "Presents a numbered list of items and asks the user to choose one. In browsers uses `window.prompt()`. In Node.js, register a host handler. Resumes with the index of the chosen item, or `null` if the user cancels.",
|
|
24546
|
+
returns: { type: ["integer", "null"] },
|
|
24547
|
+
args: {
|
|
24548
|
+
items: {
|
|
24549
|
+
type: "array",
|
|
24550
|
+
description: "Non-empty array of strings to display."
|
|
24551
|
+
},
|
|
24552
|
+
options: {
|
|
24553
|
+
type: "object",
|
|
24554
|
+
description: "Optional settings: `prompt` (string label) and `default` (integer index to use when the user submits an empty input)."
|
|
24555
|
+
}
|
|
24556
|
+
},
|
|
24557
|
+
variants: [{ argumentNames: ["items"] }, { argumentNames: ["items", "options"] }],
|
|
24558
|
+
examples: ["effect(dvala.io.pick)"],
|
|
24559
|
+
seeAlso: [
|
|
24560
|
+
"-effect-dvala.io.read-line",
|
|
24561
|
+
"-effect-dvala.io.confirm",
|
|
24562
|
+
"perform",
|
|
24563
|
+
"effect"
|
|
24564
|
+
]
|
|
24565
|
+
}
|
|
24566
|
+
},
|
|
24567
|
+
"dvala.io.confirm": {
|
|
24568
|
+
handler: (args, k, sourceCodeInfo) => {
|
|
24569
|
+
const question = args[0];
|
|
24570
|
+
const options = args[1];
|
|
24571
|
+
if (typeof question !== "string") throw new DvalaError(`dvala.io.confirm: first argument must be a string, got ${typeof question}`, sourceCodeInfo);
|
|
24572
|
+
if (options !== void 0) {
|
|
24573
|
+
if (typeof options !== "object" || options === null || Array.isArray(options)) throw new DvalaError(`dvala.io.confirm: second argument must be an object, got ${typeof options}`, sourceCodeInfo);
|
|
24574
|
+
const opts = options;
|
|
24575
|
+
if (opts["default"] !== void 0 && typeof opts["default"] !== "boolean") throw new DvalaError("dvala.io.confirm: options.default must be a boolean", sourceCodeInfo);
|
|
24576
|
+
}
|
|
24577
|
+
if (typeof globalThis.confirm === "function") return {
|
|
24578
|
+
type: "Value",
|
|
24579
|
+
value: globalThis.confirm(question),
|
|
24580
|
+
k
|
|
24581
|
+
};
|
|
24582
|
+
throw new DvalaError("dvala.io.confirm is not supported in this environment. In Node.js, register a \"dvala.io.confirm\" host handler.", sourceCodeInfo);
|
|
24583
|
+
},
|
|
24584
|
+
arity: {
|
|
24585
|
+
min: 1,
|
|
24586
|
+
max: 2
|
|
24587
|
+
},
|
|
24588
|
+
docs: {
|
|
24589
|
+
category: "effect",
|
|
24590
|
+
description: "Asks the user a yes/no question. In browsers uses `window.confirm()` and returns `true` (OK) or `false` (Cancel). In Node.js, register a host handler. The optional `default` hints the preferred answer to host handlers (e.g. for rendering `[Y/n]` in a CLI), but has no effect on the browser implementation.",
|
|
24591
|
+
returns: { type: "boolean" },
|
|
24592
|
+
args: {
|
|
24593
|
+
question: {
|
|
24594
|
+
type: "string",
|
|
24595
|
+
description: "The yes/no question to present."
|
|
24596
|
+
},
|
|
24597
|
+
options: {
|
|
24598
|
+
type: "object",
|
|
24599
|
+
description: "Optional settings: `default` (boolean, hints the preferred answer for host handlers)."
|
|
24600
|
+
}
|
|
24601
|
+
},
|
|
24602
|
+
variants: [{ argumentNames: ["question"] }, { argumentNames: ["question", "options"] }],
|
|
24603
|
+
examples: ["effect(dvala.io.confirm)"],
|
|
24604
|
+
seeAlso: [
|
|
24605
|
+
"-effect-dvala.io.read-line",
|
|
24606
|
+
"-effect-dvala.io.pick",
|
|
24484
24607
|
"perform",
|
|
24485
24608
|
"effect"
|
|
24486
24609
|
]
|
|
@@ -25688,6 +25811,94 @@ const tokenizeShebang = (input, position) => {
|
|
|
25688
25811
|
}
|
|
25689
25812
|
return NO_MATCH;
|
|
25690
25813
|
};
|
|
25814
|
+
const tokenizeTemplateString = (input, position) => {
|
|
25815
|
+
if (input[position] !== "`") return NO_MATCH;
|
|
25816
|
+
let value = "`";
|
|
25817
|
+
let length = 1;
|
|
25818
|
+
while (position + length < input.length) {
|
|
25819
|
+
const char = input[position + length];
|
|
25820
|
+
if (char === "`") {
|
|
25821
|
+
value += "`";
|
|
25822
|
+
length += 1;
|
|
25823
|
+
return [length, ["TemplateString", value]];
|
|
25824
|
+
}
|
|
25825
|
+
if (char === "$" && input[position + length + 1] === "{") {
|
|
25826
|
+
value += "${";
|
|
25827
|
+
length += 2;
|
|
25828
|
+
let braceDepth = 1;
|
|
25829
|
+
while (position + length < input.length && braceDepth > 0) {
|
|
25830
|
+
const c = input[position + length];
|
|
25831
|
+
if (c === "{") {
|
|
25832
|
+
braceDepth += 1;
|
|
25833
|
+
value += c;
|
|
25834
|
+
length += 1;
|
|
25835
|
+
} else if (c === "}") {
|
|
25836
|
+
braceDepth -= 1;
|
|
25837
|
+
value += c;
|
|
25838
|
+
length += 1;
|
|
25839
|
+
} else if (c === "\"") {
|
|
25840
|
+
value += c;
|
|
25841
|
+
length += 1;
|
|
25842
|
+
let escaping = false;
|
|
25843
|
+
while (position + length < input.length) {
|
|
25844
|
+
const sc = input[position + length];
|
|
25845
|
+
value += sc;
|
|
25846
|
+
length += 1;
|
|
25847
|
+
if (escaping) escaping = false;
|
|
25848
|
+
else if (sc === "\\") escaping = true;
|
|
25849
|
+
else if (sc === "\"") break;
|
|
25850
|
+
}
|
|
25851
|
+
} else if (c === "'") {
|
|
25852
|
+
value += c;
|
|
25853
|
+
length += 1;
|
|
25854
|
+
let escaping = false;
|
|
25855
|
+
while (position + length < input.length) {
|
|
25856
|
+
const sc = input[position + length];
|
|
25857
|
+
value += sc;
|
|
25858
|
+
length += 1;
|
|
25859
|
+
if (escaping) escaping = false;
|
|
25860
|
+
else if (sc === "\\") escaping = true;
|
|
25861
|
+
else if (sc === "'") break;
|
|
25862
|
+
}
|
|
25863
|
+
} else if (c === "`") {
|
|
25864
|
+
const [nestedLength, nestedToken] = tokenizeTemplateString(input, position + length);
|
|
25865
|
+
if (nestedLength === 0 || !nestedToken) return [length, [
|
|
25866
|
+
"Error",
|
|
25867
|
+
value,
|
|
25868
|
+
void 0,
|
|
25869
|
+
`Unclosed nested template string at position ${position + length}`
|
|
25870
|
+
]];
|
|
25871
|
+
if (nestedToken[0] === "Error") return [length + nestedLength, [
|
|
25872
|
+
"Error",
|
|
25873
|
+
value + nestedToken[1],
|
|
25874
|
+
void 0,
|
|
25875
|
+
nestedToken[3]
|
|
25876
|
+
]];
|
|
25877
|
+
value += nestedToken[1];
|
|
25878
|
+
length += nestedLength;
|
|
25879
|
+
} else {
|
|
25880
|
+
value += c;
|
|
25881
|
+
length += 1;
|
|
25882
|
+
}
|
|
25883
|
+
}
|
|
25884
|
+
if (braceDepth > 0) return [length, [
|
|
25885
|
+
"Error",
|
|
25886
|
+
value,
|
|
25887
|
+
void 0,
|
|
25888
|
+
`Unclosed interpolation in template string at position ${position}`
|
|
25889
|
+
]];
|
|
25890
|
+
} else {
|
|
25891
|
+
value += char;
|
|
25892
|
+
length += 1;
|
|
25893
|
+
}
|
|
25894
|
+
}
|
|
25895
|
+
return [length, [
|
|
25896
|
+
"Error",
|
|
25897
|
+
value,
|
|
25898
|
+
void 0,
|
|
25899
|
+
`Unclosed template string at position ${position}`
|
|
25900
|
+
]];
|
|
25901
|
+
};
|
|
25691
25902
|
const tokenizeSingleLineComment = (input, position) => {
|
|
25692
25903
|
if (input[position] === "/" && input[position + 1] === "/") {
|
|
25693
25904
|
let length = 2;
|
|
@@ -25712,6 +25923,7 @@ const tokenizers = [
|
|
|
25712
25923
|
tokenizeLBrace,
|
|
25713
25924
|
tokenizeRBrace,
|
|
25714
25925
|
tokenizeString,
|
|
25926
|
+
tokenizeTemplateString,
|
|
25715
25927
|
tokenizeRegexpShorthand,
|
|
25716
25928
|
tokenizeBasePrefixedNumber,
|
|
25717
25929
|
tokenizeNumber,
|
|
@@ -26506,6 +26718,11 @@ function findUnresolvedSymbolsInNode(node, contextStack, builtin) {
|
|
|
26506
26718
|
});
|
|
26507
26719
|
}
|
|
26508
26720
|
case NodeTypes.Spread: return findUnresolvedSymbolsInNode(node[1], contextStack, builtin);
|
|
26721
|
+
case NodeTypes.TemplateString: {
|
|
26722
|
+
const unresolvedSymbols = /* @__PURE__ */ new Set();
|
|
26723
|
+
for (const segment of node[1]) findUnresolvedSymbolsInNode(segment, contextStack, builtin)?.forEach((symbol) => unresolvedSymbols.add(symbol));
|
|
26724
|
+
return unresolvedSymbols;
|
|
26725
|
+
}
|
|
26509
26726
|
default: throw new DvalaError(`Unhandled node type: ${nodeType}`, node[2]);
|
|
26510
26727
|
}
|
|
26511
26728
|
}
|
|
@@ -27440,6 +27657,176 @@ function parseObject(ctx) {
|
|
|
27440
27657
|
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.object, params]], firstToken[2]);
|
|
27441
27658
|
}
|
|
27442
27659
|
//#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
|
|
27443
27830
|
//#region src/parser/subParsers/parseOperand.ts
|
|
27444
27831
|
function parseOperand(ctx) {
|
|
27445
27832
|
let operand = parseOperandPart(ctx);
|
|
@@ -27494,6 +27881,7 @@ function parseOperandPart(ctx) {
|
|
|
27494
27881
|
case "Number":
|
|
27495
27882
|
case "BasePrefixedNumber": return parseNumber(ctx);
|
|
27496
27883
|
case "string": return parseString(ctx, token);
|
|
27884
|
+
case "TemplateString": return parseTemplateString(ctx, token);
|
|
27497
27885
|
case "Symbol": {
|
|
27498
27886
|
ctx.storePosition();
|
|
27499
27887
|
const lamdaFunction = parseLambdaFunction(ctx);
|
|
@@ -27625,18 +28013,6 @@ function parse(tokenStream) {
|
|
|
27625
28013
|
return nodes;
|
|
27626
28014
|
}
|
|
27627
28015
|
//#endregion
|
|
27628
|
-
//#region src/tokenizer/minifyTokenStream.ts
|
|
27629
|
-
function minifyTokenStream(tokenStream, { removeWhiteSpace }) {
|
|
27630
|
-
const tokens = tokenStream.tokens.filter((token) => {
|
|
27631
|
-
if (isSingleLineCommentToken(token) || isMultiLineCommentToken(token) || isShebangToken(token) || removeWhiteSpace && isWhitespaceToken(token)) return false;
|
|
27632
|
-
return true;
|
|
27633
|
-
});
|
|
27634
|
-
return {
|
|
27635
|
-
...tokenStream,
|
|
27636
|
-
tokens
|
|
27637
|
-
};
|
|
27638
|
-
}
|
|
27639
|
-
//#endregion
|
|
27640
28016
|
//#region src/evaluator/effectTypes.ts
|
|
27641
28017
|
const SUSPENDED_MESSAGE = "Program suspended";
|
|
27642
28018
|
/**
|
|
@@ -28317,9 +28693,33 @@ function stepNode(node, env, k) {
|
|
|
28317
28693
|
};
|
|
28318
28694
|
case NodeTypes.NormalExpression: return stepNormalExpression(node, env, k);
|
|
28319
28695
|
case NodeTypes.SpecialExpression: return stepSpecialExpression(node, env, k);
|
|
28696
|
+
case NodeTypes.TemplateString: return stepTemplateString(node, env, k);
|
|
28320
28697
|
default: throw new DvalaError(`${getNodeTypeName(node[0])}-node cannot be evaluated`, node[2]);
|
|
28321
28698
|
}
|
|
28322
28699
|
}
|
|
28700
|
+
function stepTemplateString(node, env, k) {
|
|
28701
|
+
const segments = node[1];
|
|
28702
|
+
const sourceCodeInfo = node[2];
|
|
28703
|
+
if (segments.length === 0) return {
|
|
28704
|
+
type: "Value",
|
|
28705
|
+
value: "",
|
|
28706
|
+
k
|
|
28707
|
+
};
|
|
28708
|
+
const frame = {
|
|
28709
|
+
type: "TemplateStringBuild",
|
|
28710
|
+
segments,
|
|
28711
|
+
index: 0,
|
|
28712
|
+
result: "",
|
|
28713
|
+
env,
|
|
28714
|
+
sourceCodeInfo
|
|
28715
|
+
};
|
|
28716
|
+
return {
|
|
28717
|
+
type: "Eval",
|
|
28718
|
+
node: segments[0],
|
|
28719
|
+
env,
|
|
28720
|
+
k: [frame, ...k]
|
|
28721
|
+
};
|
|
28722
|
+
}
|
|
28323
28723
|
/**
|
|
28324
28724
|
* Normal expressions: evaluate arguments left-to-right, then dispatch.
|
|
28325
28725
|
* Push EvalArgsFrame + NanCheckFrame, then start evaluating the first arg.
|
|
@@ -29249,6 +29649,7 @@ function applyFrame(frame, value, k) {
|
|
|
29249
29649
|
case "And": return applyAnd(frame, value, k);
|
|
29250
29650
|
case "Or": return applyOr(frame, value, k);
|
|
29251
29651
|
case "Qq": return applyQq(frame, value, k);
|
|
29652
|
+
case "TemplateStringBuild": return applyTemplateStringBuild(frame, value, k);
|
|
29252
29653
|
case "ArrayBuild": return applyArrayBuild(frame, value, k);
|
|
29253
29654
|
case "ObjectBuild": return applyObjectBuild(frame, value, k);
|
|
29254
29655
|
case "LetBind": return applyLetBind(frame, value, k);
|
|
@@ -29535,6 +29936,27 @@ function advanceQq(frame, k) {
|
|
|
29535
29936
|
function skipUndefinedQq(frame, k) {
|
|
29536
29937
|
return advanceQq(frame, k);
|
|
29537
29938
|
}
|
|
29939
|
+
function applyTemplateStringBuild(frame, value, k) {
|
|
29940
|
+
const { segments, env } = frame;
|
|
29941
|
+
const result = frame.result + String(value);
|
|
29942
|
+
const nextIndex = frame.index + 1;
|
|
29943
|
+
if (nextIndex >= segments.length) return {
|
|
29944
|
+
type: "Value",
|
|
29945
|
+
value: result,
|
|
29946
|
+
k
|
|
29947
|
+
};
|
|
29948
|
+
const newFrame = {
|
|
29949
|
+
...frame,
|
|
29950
|
+
index: nextIndex,
|
|
29951
|
+
result
|
|
29952
|
+
};
|
|
29953
|
+
return {
|
|
29954
|
+
type: "Eval",
|
|
29955
|
+
node: segments[nextIndex],
|
|
29956
|
+
env,
|
|
29957
|
+
k: [newFrame, ...k]
|
|
29958
|
+
};
|
|
29959
|
+
}
|
|
29538
29960
|
function applyArrayBuild(frame, value, k) {
|
|
29539
29961
|
const { nodes, result, env, sourceCodeInfo } = frame;
|
|
29540
29962
|
if (frame.isSpread) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mojir/dvala",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"description": "dvala",
|
|
5
5
|
"author": "Albert Mojir",
|
|
6
6
|
"license": "MIT",
|
|
@@ -138,7 +138,9 @@
|
|
|
138
138
|
"dvala": "node ./dist/cli/cli.js",
|
|
139
139
|
"dev": "npx serve docs -p 9901",
|
|
140
140
|
"test:e2e": "npx playwright test",
|
|
141
|
+
"test:e2e:prod": "E2E_BASE_URL=https://mojir.github.io/dvala npx playwright test",
|
|
141
142
|
"test:e2e:headed": "npx playwright test --headed",
|
|
143
|
+
"test:e2e:prod:headed": "E2E_BASE_URL=https://mojir.github.io/dvala npx playwright test --headed",
|
|
142
144
|
"lcov": "open-cli ./coverage/index.html",
|
|
143
145
|
"mcp:inspect": "npx @modelcontextprotocol/inspector node ./dist/mcp-server/server.js"
|
|
144
146
|
},
|