zuzu-js 0.5.0 → 0.6.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.
- package/dist/zuzu-browser-worker.js +99 -31
- package/dist/zuzu-browser.js +103 -35
- package/lib/cli.js +1 -1
- package/lib/runtime-helpers.js +20 -2
- package/lib/runtime.js +15 -8
- package/lib/transpiler-new/codegen.js +19 -6
- package/modules/std/io.js +49 -4
- package/package.json +1 -1
- package/stdlib/modules/std/config.zzm +5 -5
- package/stdlib/modules/std/data/kdl/xml.zzm +1 -1
- package/stdlib/modules/std/gui/dialogue.zzm +1 -0
- package/stdlib/modules/std/gui.zzm +3 -1
- package/stdlib/modules/std/io.zzm +7 -0
- package/stdlib/modules/std/path/z/context.zzm +1 -0
- package/stdlib/modules/std/path/z/evaluate.zzm +4 -1
- package/stdlib/modules/std/path/z/functions.zzm +1 -0
- package/stdlib/modules/std/path/z/node.zzm +3 -2
- package/stdlib/modules/std/path/zz/functions.zzm +2 -0
|
@@ -840,6 +840,14 @@
|
|
|
840
840
|
}
|
|
841
841
|
return resolved;
|
|
842
842
|
}
|
|
843
|
+
function strongReferenceCount(value) {
|
|
844
|
+
const resolved = resolveWeakValue(value);
|
|
845
|
+
if (!isWeakableValue(resolved)) {
|
|
846
|
+
return 0;
|
|
847
|
+
}
|
|
848
|
+
const state = weakStates.get(resolved);
|
|
849
|
+
return state ? state.strong : 0;
|
|
850
|
+
}
|
|
843
851
|
function retainedValuesForCollection(collection, create = false) {
|
|
844
852
|
const resolved = resolveWeakValue(collection);
|
|
845
853
|
if (!isWeakableValue(resolved)) {
|
|
@@ -891,7 +899,7 @@
|
|
|
891
899
|
}
|
|
892
900
|
return null;
|
|
893
901
|
}
|
|
894
|
-
function releaseValue(value) {
|
|
902
|
+
function releaseValue(value, options = {}) {
|
|
895
903
|
if (isWeakCell(value)) {
|
|
896
904
|
return null;
|
|
897
905
|
}
|
|
@@ -902,7 +910,12 @@
|
|
|
902
910
|
const wasStrong = state.strong;
|
|
903
911
|
state.strong = Math.max(0, state.strong - 1);
|
|
904
912
|
if (wasStrong > 0 && state.strong === 0) {
|
|
905
|
-
|
|
913
|
+
if (!options.preserveCollectionValues) {
|
|
914
|
+
releaseCollectionValues(resolved);
|
|
915
|
+
}
|
|
916
|
+
if (!options.suppressDemolish && typeof resolved.__demolish__ === "function") {
|
|
917
|
+
resolved.__demolish__();
|
|
918
|
+
}
|
|
906
919
|
}
|
|
907
920
|
}
|
|
908
921
|
}
|
|
@@ -1607,6 +1620,7 @@
|
|
|
1607
1620
|
resolveWeakValue,
|
|
1608
1621
|
retainValue,
|
|
1609
1622
|
releaseValue,
|
|
1623
|
+
strongReferenceCount,
|
|
1610
1624
|
retainCollectionValue,
|
|
1611
1625
|
releaseCollectionValue,
|
|
1612
1626
|
releaseCollectionValues,
|
|
@@ -8312,6 +8326,9 @@ globalThis[${JSON.stringify(node.id.name)}] = ${name2};` : declaration;
|
|
|
8312
8326
|
if (options.inSwitchSection) {
|
|
8313
8327
|
return `return { __zuzu_return: true, value: ${value} };`;
|
|
8314
8328
|
}
|
|
8329
|
+
if (options.returnSlotName) {
|
|
8330
|
+
return `return (${options.returnSlotName} = ${value});`;
|
|
8331
|
+
}
|
|
8315
8332
|
return node.argument ? `return ${value};` : `return ${value};`;
|
|
8316
8333
|
}
|
|
8317
8334
|
case "IfStatement":
|
|
@@ -8396,18 +8413,23 @@ globalThis[${JSON.stringify(node.id.name)}] = ${name2};` : declaration;
|
|
|
8396
8413
|
}
|
|
8397
8414
|
function emitFunctionLike(node, header, options = {}) {
|
|
8398
8415
|
const signature = analyzeSpecialSignature(node.params);
|
|
8399
|
-
const functionOptions = createFunctionOptions(node, options);
|
|
8400
8416
|
const cleanupNames = collectCleanupNames(node.body);
|
|
8417
|
+
const returnSlotName = cleanupNames.length > 0 ? "__zuzu_returning" : null;
|
|
8418
|
+
const functionOptions = {
|
|
8419
|
+
...createFunctionOptions(node, options),
|
|
8420
|
+
returnSlotName
|
|
8421
|
+
};
|
|
8401
8422
|
const bodySource = withEmitContext(
|
|
8402
8423
|
functionOptions,
|
|
8403
8424
|
() => emitFunctionBlock(node.body, cleanupNames, functionOptions)
|
|
8404
8425
|
);
|
|
8405
|
-
const cleanup = cleanupNames.map((name2) => `__zuzu_maybe_demolish( ${name2} );`).join("\n");
|
|
8426
|
+
const cleanup = cleanupNames.map((name2) => `__zuzu_maybe_demolish( ${name2}, ${returnSlotName} );`).join("\n");
|
|
8406
8427
|
const bodyWrapper = [
|
|
8428
|
+
returnSlotName ? `let ${returnSlotName} = null;` : "",
|
|
8407
8429
|
"try {",
|
|
8408
8430
|
bodySource,
|
|
8409
8431
|
"} catch ( __zuzu_nonlocal ) {",
|
|
8410
|
-
"if ( __zuzu_nonlocal && __zuzu_nonlocal.__zuzu_nonlocal_return ) { return __zuzu_nonlocal.value; }",
|
|
8432
|
+
returnSlotName ? `if ( __zuzu_nonlocal && __zuzu_nonlocal.__zuzu_nonlocal_return ) { ${returnSlotName} = __zuzu_nonlocal.value; return ${returnSlotName}; }` : "if ( __zuzu_nonlocal && __zuzu_nonlocal.__zuzu_nonlocal_return ) { return __zuzu_nonlocal.value; }",
|
|
8411
8433
|
"throw __zuzu_nonlocal;",
|
|
8412
8434
|
"}",
|
|
8413
8435
|
cleanupNames.length > 0 ? "finally {" : "",
|
|
@@ -8557,7 +8579,8 @@ globalThis[${JSON.stringify(node.id.name)}] = ${name2};` : declaration;
|
|
|
8557
8579
|
...expressionDeclaredNames
|
|
8558
8580
|
]),
|
|
8559
8581
|
bodylessFunctionNames: /* @__PURE__ */ new Set(),
|
|
8560
|
-
chainHereName: hereParamName || options.chainHereName
|
|
8582
|
+
chainHereName: hereParamName || options.chainHereName,
|
|
8583
|
+
returnSlotName: null
|
|
8561
8584
|
};
|
|
8562
8585
|
}
|
|
8563
8586
|
function extractDeclaredParamNames(params) {
|
|
@@ -9026,7 +9049,7 @@ globalThis[${JSON.stringify(node.id.name)}] = ${name2};` : declaration;
|
|
|
9026
9049
|
${body}
|
|
9027
9050
|
}`;
|
|
9028
9051
|
}
|
|
9029
|
-
const cleanup = cleanupNames.map((name2) => `__zuzu_maybe_demolish( ${name2} );`).join("\n");
|
|
9052
|
+
const cleanup = cleanupNames.map((name2) => options.returnSlotName ? `__zuzu_maybe_demolish( ${name2}, ${options.returnSlotName} );` : `__zuzu_maybe_demolish( ${name2} );`).join("\n");
|
|
9030
9053
|
return `{
|
|
9031
9054
|
${cleanupNames.map((name2) => `let ${name2} = null;`).join("\n")}
|
|
9032
9055
|
try {
|
|
@@ -9509,7 +9532,7 @@ ${cleanup}
|
|
|
9509
9532
|
case "ConditionalExpression":
|
|
9510
9533
|
return `( __zuzu_truthy( ${emitExpression(node.test)} ) ? ${emitExpression(node.consequent)} : ${emitExpression(node.alternate)} )`;
|
|
9511
9534
|
case "ShortTernaryExpression":
|
|
9512
|
-
return `(
|
|
9535
|
+
return `( () => { const __zuzu_left = ${emitExpression(node.test)}; return __zuzu_resolve_weak_value( __zuzu_left ) != null ? __zuzu_left : ${emitExpression(node.alternate)}; } )()`;
|
|
9513
9536
|
case "TryExpression":
|
|
9514
9537
|
return emitTryExpression(node);
|
|
9515
9538
|
case "DoExpression":
|
|
@@ -11540,7 +11563,7 @@ ${cleanup}
|
|
|
11540
11563
|
"package.json"(exports2, module2) {
|
|
11541
11564
|
module2.exports = {
|
|
11542
11565
|
name: "zuzu-js",
|
|
11543
|
-
version: "0.
|
|
11566
|
+
version: "0.6.0",
|
|
11544
11567
|
description: "JavaScript runtime, compiler, and browser bundle for ZuzuScript.",
|
|
11545
11568
|
main: "lib/zuzu.js",
|
|
11546
11569
|
bin: {
|
|
@@ -11619,6 +11642,27 @@ ${cleanup}
|
|
|
11619
11642
|
var { BinaryString: BinaryString2 } = require_runtime_helpers();
|
|
11620
11643
|
var { Task, traceBlockingOperation } = require_task();
|
|
11621
11644
|
var runtimePolicy2 = {};
|
|
11645
|
+
function makeTempFilePath() {
|
|
11646
|
+
for (let attempt = 0; attempt < 1e3; attempt++) {
|
|
11647
|
+
const nonce = `${process.pid}-${Date.now()}-${attempt}-${Math.random().toString(36).slice(2)}`;
|
|
11648
|
+
const file = path.join(os.tmpdir(), `zuzu-js-${nonce}.tmp`);
|
|
11649
|
+
let fd = null;
|
|
11650
|
+
try {
|
|
11651
|
+
fd = fs.openSync(file, "wx");
|
|
11652
|
+
return file;
|
|
11653
|
+
} catch (err) {
|
|
11654
|
+
if (err && err.code === "EEXIST") {
|
|
11655
|
+
continue;
|
|
11656
|
+
}
|
|
11657
|
+
throw err;
|
|
11658
|
+
} finally {
|
|
11659
|
+
if (fd !== null) {
|
|
11660
|
+
fs.closeSync(fd);
|
|
11661
|
+
}
|
|
11662
|
+
}
|
|
11663
|
+
}
|
|
11664
|
+
throw new Error("IOError: could not create temporary file");
|
|
11665
|
+
}
|
|
11622
11666
|
function typeName2(value) {
|
|
11623
11667
|
if (value == null) {
|
|
11624
11668
|
return "Null";
|
|
@@ -11793,11 +11837,28 @@ ${cleanup}
|
|
|
11793
11837
|
return out;
|
|
11794
11838
|
}
|
|
11795
11839
|
var Path = class _Path {
|
|
11796
|
-
constructor(rawPath) {
|
|
11840
|
+
constructor(rawPath, options = {}) {
|
|
11797
11841
|
const pathValue = rawPath && typeof rawPath === "object" && !Array.isArray(rawPath) && (Object.prototype.hasOwnProperty.call(rawPath, "path") || typeof rawPath.get === "function" && typeof rawPath.has === "function" && rawPath.has("path")) ? typeof rawPath.get === "function" ? rawPath.get("path") : rawPath.path : rawPath;
|
|
11798
11842
|
this.value = String(pathValue ?? "");
|
|
11799
11843
|
this._linePos = 0;
|
|
11800
11844
|
this._lineMode = "text";
|
|
11845
|
+
this._tempCleanup = options.tempCleanup || null;
|
|
11846
|
+
}
|
|
11847
|
+
__demolish__() {
|
|
11848
|
+
const cleanup = this._tempCleanup;
|
|
11849
|
+
this._tempCleanup = null;
|
|
11850
|
+
if (cleanup === "dir") {
|
|
11851
|
+
try {
|
|
11852
|
+
fs.rmSync(this.value, { recursive: true, force: true });
|
|
11853
|
+
} catch (_err) {
|
|
11854
|
+
}
|
|
11855
|
+
} else if (cleanup === "file") {
|
|
11856
|
+
try {
|
|
11857
|
+
fs.rmSync(this.value, { force: true });
|
|
11858
|
+
} catch (_err) {
|
|
11859
|
+
}
|
|
11860
|
+
}
|
|
11861
|
+
return null;
|
|
11801
11862
|
}
|
|
11802
11863
|
to_String() {
|
|
11803
11864
|
return this.value;
|
|
@@ -12034,11 +12095,13 @@ ${cleanup}
|
|
|
12034
12095
|
return new _Path(process.cwd());
|
|
12035
12096
|
}
|
|
12036
12097
|
static tempfile() {
|
|
12037
|
-
|
|
12038
|
-
return new _Path(path.join(dir, "tmp.txt"));
|
|
12098
|
+
return new _Path(makeTempFilePath(), { tempCleanup: "file" });
|
|
12039
12099
|
}
|
|
12040
12100
|
static tempdir() {
|
|
12041
|
-
return new _Path(
|
|
12101
|
+
return new _Path(
|
|
12102
|
+
fs.mkdtempSync(path.join(os.tmpdir(), "zuzu-js-dir-")),
|
|
12103
|
+
{ tempCleanup: "dir" }
|
|
12104
|
+
);
|
|
12042
12105
|
}
|
|
12043
12106
|
static join(parts) {
|
|
12044
12107
|
return new _Path(path.join(...parts.map((p) => String(p))));
|
|
@@ -12118,6 +12181,7 @@ ${cleanup}
|
|
|
12118
12181
|
resolveWeakValue,
|
|
12119
12182
|
retainValue,
|
|
12120
12183
|
releaseValue,
|
|
12184
|
+
strongReferenceCount,
|
|
12121
12185
|
retainCollectionValue,
|
|
12122
12186
|
releaseCollectionValue,
|
|
12123
12187
|
releaseCollectionValues,
|
|
@@ -13861,15 +13925,18 @@ ${cleanup}
|
|
|
13861
13925
|
}
|
|
13862
13926
|
throw new TypeError(`${String(property)} is not a function`);
|
|
13863
13927
|
}
|
|
13864
|
-
function zuzuMaybeDemolish(value) {
|
|
13928
|
+
function zuzuMaybeDemolish(value, preserved = null) {
|
|
13865
13929
|
const original = value;
|
|
13866
13930
|
value = resolveWeakValue(value);
|
|
13867
|
-
|
|
13868
|
-
|
|
13869
|
-
|
|
13931
|
+
const shouldDemolish = value && value !== resolveWeakValue(preserved) && typeof value.__demolish__ === "function";
|
|
13932
|
+
releaseValue(original, {
|
|
13933
|
+
preserveCollectionValues: value === resolveWeakValue(preserved),
|
|
13934
|
+
suppressDemolish: true
|
|
13935
|
+
});
|
|
13936
|
+
if (shouldDemolish && strongReferenceCount(value) === 0) {
|
|
13937
|
+
return value.__demolish__();
|
|
13870
13938
|
}
|
|
13871
|
-
|
|
13872
|
-
return result;
|
|
13939
|
+
return null;
|
|
13873
13940
|
}
|
|
13874
13941
|
function zuzuCan(value, methodName) {
|
|
13875
13942
|
value = resolveWeakValue(value);
|
|
@@ -15436,8 +15503,8 @@ ${cleanup}
|
|
|
15436
15503
|
__zuzu_resolve_brace_key(object, literalProperty, getDynamicProperty) {
|
|
15437
15504
|
return zuzuResolveBraceKey(object, literalProperty, getDynamicProperty);
|
|
15438
15505
|
},
|
|
15439
|
-
__zuzu_maybe_demolish(value) {
|
|
15440
|
-
return zuzuMaybeDemolish(value);
|
|
15506
|
+
__zuzu_maybe_demolish(value, preserved = null) {
|
|
15507
|
+
return zuzuMaybeDemolish(value, preserved);
|
|
15441
15508
|
},
|
|
15442
15509
|
__zuzu_xor(left, right) {
|
|
15443
15510
|
return zuzuTruthy(left) !== zuzuTruthy(right);
|
|
@@ -41546,9 +41613,9 @@ ${lines.join("\n")}
|
|
|
41546
41613
|
}
|
|
41547
41614
|
});
|
|
41548
41615
|
|
|
41549
|
-
// ../../../../../tmp/zuzu-browser-build.
|
|
41616
|
+
// ../../../../../tmp/zuzu-browser-build.UGYrnC/browser-stdlib.generated.js
|
|
41550
41617
|
var require_browser_stdlib_generated = __commonJS({
|
|
41551
|
-
"../../../../../tmp/zuzu-browser-build.
|
|
41618
|
+
"../../../../../tmp/zuzu-browser-build.UGYrnC/browser-stdlib.generated.js"(exports2, module2) {
|
|
41552
41619
|
"use strict";
|
|
41553
41620
|
function createBrowserStdlib2() {
|
|
41554
41621
|
const jsModules = /* @__PURE__ */ Object.create(null);
|
|
@@ -41579,8 +41646,8 @@ ${lines.join("\n")}
|
|
|
41579
41646
|
virtualFiles["/modules/std/colour.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/colour - Colour parsing helpers.\n\n=head1 SYNOPSIS\n\n from std/colour import parse_colour;\n\n say( parse_colour("red") ); # #ff0000\n say( parse_colour("#abc") ); # #aabbcc\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nC<parse_colour> accepts three- and six-digit hexadecimal colours and the\nCSS3 extended colour keywords. It returns a lowercase six-digit\nhexadecimal colour string.\n\n=head1 EXPORTS\n\n=head2 Functions\n\n=over\n\n=item C<< parse_colour(String x) >>\n\nParameters: C<x> is a CSS colour keyword or a three- or six-digit\nhexadecimal colour string. Returns: C<String>. Returns the normalized\nlowercase C<#rrggbb> colour string, or throws if C<x> is not recognised.\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/colour >> 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\nfrom std/string import trim;\n\nconst _COLOUR_KEYWORDS := {\n aliceblue: "#f0f8ff",\n antiquewhite: "#faebd7",\n aqua: "#00ffff",\n aquamarine: "#7fffd4",\n azure: "#f0ffff",\n beige: "#f5f5dc",\n bisque: "#ffe4c4",\n black: "#000000",\n blanchedalmond: "#ffebcd",\n blue: "#0000ff",\n blueviolet: "#8a2be2",\n brown: "#a52a2a",\n burlywood: "#deb887",\n cadetblue: "#5f9ea0",\n chartreuse: "#7fff00",\n chocolate: "#d2691e",\n coral: "#ff7f50",\n cornflowerblue: "#6495ed",\n cornsilk: "#fff8dc",\n crimson: "#dc143c",\n cyan: "#00ffff",\n darkblue: "#00008b",\n darkcyan: "#008b8b",\n darkgoldenrod: "#b8860b",\n darkgray: "#a9a9a9",\n darkgreen: "#006400",\n darkgrey: "#a9a9a9",\n darkkhaki: "#bdb76b",\n darkmagenta: "#8b008b",\n darkolivegreen: "#556b2f",\n darkorange: "#ff8c00",\n darkorchid: "#9932cc",\n darkred: "#8b0000",\n darksalmon: "#e9967a",\n darkseagreen: "#8fbc8f",\n darkslateblue: "#483d8b",\n darkslategray: "#2f4f4f",\n darkslategrey: "#2f4f4f",\n darkturquoise: "#00ced1",\n darkviolet: "#9400d3",\n deeppink: "#ff1493",\n deepskyblue: "#00bfff",\n dimgray: "#696969",\n dimgrey: "#696969",\n dodgerblue: "#1e90ff",\n firebrick: "#b22222",\n floralwhite: "#fffaf0",\n forestgreen: "#228b22",\n fuchsia: "#ff00ff",\n gainsboro: "#dcdcdc",\n ghostwhite: "#f8f8ff",\n gold: "#ffd700",\n goldenrod: "#daa520",\n gray: "#808080",\n green: "#008000",\n greenyellow: "#adff2f",\n grey: "#808080",\n honeydew: "#f0fff0",\n hotpink: "#ff69b4",\n indianred: "#cd5c5c",\n indigo: "#4b0082",\n ivory: "#fffff0",\n khaki: "#f0e68c",\n lavender: "#e6e6fa",\n lavenderblush: "#fff0f5",\n lawngreen: "#7cfc00",\n lemonchiffon: "#fffacd",\n lightblue: "#add8e6",\n lightcoral: "#f08080",\n lightcyan: "#e0ffff",\n lightgoldenrodyellow: "#fafad2",\n lightgray: "#d3d3d3",\n lightgreen: "#90ee90",\n lightgrey: "#d3d3d3",\n lightpink: "#ffb6c1",\n lightsalmon: "#ffa07a",\n lightseagreen: "#20b2aa",\n lightskyblue: "#87cefa",\n lightslategray: "#778899",\n lightslategrey: "#778899",\n lightsteelblue: "#b0c4de",\n lightyellow: "#ffffe0",\n lime: "#00ff00",\n limegreen: "#32cd32",\n linen: "#faf0e6",\n magenta: "#ff00ff",\n maroon: "#800000",\n mediumaquamarine: "#66cdaa",\n mediumblue: "#0000cd",\n mediumorchid: "#ba55d3",\n mediumpurple: "#9370db",\n mediumseagreen: "#3cb371",\n mediumslateblue: "#7b68ee",\n mediumspringgreen: "#00fa9a",\n mediumturquoise: "#48d1cc",\n mediumvioletred: "#c71585",\n midnightblue: "#191970",\n mintcream: "#f5fffa",\n mistyrose: "#ffe4e1",\n moccasin: "#ffe4b5",\n navajowhite: "#ffdead",\n navy: "#000080",\n oldlace: "#fdf5e6",\n olive: "#808000",\n olivedrab: "#6b8e23",\n orange: "#ffa500",\n orangered: "#ff4500",\n orchid: "#da70d6",\n palegoldenrod: "#eee8aa",\n palegreen: "#98fb98",\n paleturquoise: "#afeeee",\n palevioletred: "#db7093",\n papayawhip: "#ffefd5",\n peachpuff: "#ffdab9",\n peru: "#cd853f",\n pink: "#ffc0cb",\n plum: "#dda0dd",\n powderblue: "#b0e0e6",\n purple: "#800080",\n rebeccapurple: "#663399",\n red: "#ff0000",\n rosybrown: "#bc8f8f",\n royalblue: "#4169e1",\n saddlebrown: "#8b4513",\n salmon: "#fa8072",\n sandybrown: "#f4a460",\n seagreen: "#2e8b57",\n seashell: "#fff5ee",\n sienna: "#a0522d",\n silver: "#c0c0c0",\n skyblue: "#87ceeb",\n slateblue: "#6a5acd",\n slategray: "#708090",\n slategrey: "#708090",\n snow: "#fffafa",\n springgreen: "#00ff7f",\n steelblue: "#4682b4",\n tan: "#d2b48c",\n teal: "#008080",\n thistle: "#d8bfd8",\n tomato: "#ff6347",\n turquoise: "#40e0d0",\n violet: "#ee82ee",\n wheat: "#f5deb3",\n white: "#ffffff",\n whitesmoke: "#f5f5f5",\n yellow: "#ffff00",\n yellowgreen: "#9acd32",\n};\n\nfunction parse_colour ( String x ) {\n let text := lc( trim(x) );\n\n if ( text ~ /^#[0-9a-f]{6}$/ ) {\n return text;\n }\n\n let short := text ~ /^#([0-9a-f])([0-9a-f])([0-9a-f])$/;\n if ( short ) {\n return "#"\n _ short[1] _ short[1]\n _ short[2] _ short[2]\n _ short[3] _ short[3];\n }\n\n if ( _COLOUR_KEYWORDS.exists(text) ) {\n return _COLOUR_KEYWORDS.get(text);\n }\n\n die `Invalid colour: ${x}`;\n}\n';
|
|
41580
41647
|
virtualFiles["/modules/std/result.zzm"] = "=encoding utf8\n\n=head1 NAME\n\nstd/result - A simple Result object for explicit success and failure values.\n\n=head1 SYNOPSIS\n\n from std/result import Result;\n\n let r := Result.ok(42);\n\n if ( r.is_ok() ) {\n say r.unwrap();\n }\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nC<Result> is a small, subclassable object modelled on a simplified version\nof Rust's C<Result> concept. It is useful when a function, task, or worker\nwants to return an explicit success or failure value without throwing an\nexception.\n\nIt is a normal ZuzuScript class. Workers do not require C<Result>; any value\nsupported by C<std/marshal> may be returned.\n\n=head1 EXPORTS\n\n=head2 Classes\n\n=over\n\n=item C<Result>\n\nContainer for either an ok value or an error value.\n\n=over\n\n=item C<< Result.ok(value) >>\n\nParameters: C<value> is any success value. Returns: C<Result>. Creates an\nok result wrapping C<value>.\n\n=item C<< Result.err(error) >>\n\nParameters: C<error> is any error value. Returns: C<Result>. Creates an\nerror result wrapping C<error>.\n\n=item C<< result.is_ok() >>\n\nParameters: none. Returns: C<Boolean>. Returns true when the result is an\nok value.\n\n=item C<< result.is_err() >>\n\nParameters: none. Returns: C<Boolean>. Returns true when the result is an\nerror value.\n\n=item C<< result.value() >>\n\nParameters: none. Returns: value. Returns the stored ok value, or\nC<null> for an error result.\n\n=item C<< result.error() >>\n\nParameters: none. Returns: value. Returns the stored error value, or\nC<null> for an ok result.\n\n=item C<< result.unwrap() >>\n\nParameters: none. Returns: value. Returns the ok value, or throws if the\nresult is an error.\n\n=item C<< result.unwrap_err() >>\n\nParameters: none. Returns: value. Returns the error value, or throws if\nthe result is ok.\n\n=back\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/result >> 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\nclass Result {\n let Boolean _is_ok := true;\n let _value := null;\n let _error := null;\n\n static method ok ( value ) {\n return new self(\n _is_ok: true,\n _value: value,\n _error: null,\n );\n }\n\n static method err ( error ) {\n return new self(\n _is_ok: false,\n _value: null,\n _error: error,\n );\n }\n\n method is_ok () {\n return _is_ok;\n }\n\n method is_err () {\n return not _is_ok;\n }\n\n method value () {\n return _value;\n }\n\n method error () {\n return _error;\n }\n\n method unwrap () {\n if ( _is_ok ) {\n return _value;\n }\n\n die `called Result.unwrap() on an err value: ${_error}`;\n }\n\n method unwrap_err () {\n if ( not _is_ok ) {\n return _error;\n }\n\n die `called Result.unwrap_err() on an ok value: ${_value}`;\n }\n}\n";
|
|
41581
41648
|
virtualFiles["/modules/std/string/quoted_printable.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/string/quoted_printable - Quoted-printable encoders and decoders.\n\n=head1 SYNOPSIS\n\n from std/string/quoted_printable import encode, decode;\n\n let raw := to_binary( "Hello, world!\\r\\n" );\n\n let text := encode(raw);\n let bytes := decode(text);\n\n let binary_text := encode(raw, binary: true);\n let short_lines := encode(raw, line_length: 40, newline: "\\n");\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nThis module provides quoted-printable encoding and decoding helpers for\nRFC 2045-style byte transport. Encoding returns ASCII C<String> text.\nDecoding returns a C<BinaryString>, because quoted-printable is a byte\ntransfer encoding rather than a Unicode text format.\n\nThe C<binary> option controls how input line break bytes are encoded.\nIn the default non-binary mode, CRLF, CR, and LF bytes are normalized to\nthe configured C<newline> string. In binary mode, CR and LF bytes are\nencoded as C<=0D> and C<=0A>.\n\n=head1 EXPORTS\n\n=head2 Functions\n\n=over\n\n=item * C<encode(BinaryString bytes, ... PairList options)>\n\nParameters: C<bytes> is binary input data and C<options> controls\nencoding. Returns: C<String>. Encodes C<bytes> as quoted-printable ASCII\ntext.\n\n=item * C<decode(String text, ... PairList options)>\n\nParameters: C<text> is quoted-printable text and C<options> controls\nstrictness. Returns: C<BinaryString>. Decodes quoted-printable text into\nbytes.\n\n=back\n\n=head1 OPTIONS\n\n=over\n\n=item * C<line_length>\n\nMaximum encoded line length. Defaults to C<76> and must be at least\nC<4>.\n\n=item * C<newline>\n\nOutput newline for hard line breaks and encoded soft breaks. Defaults\nto CRLF.\n\n=item * C<binary>\n\nWhen true, encode CR and LF bytes as C<=0D> and C<=0A>. Defaults to\nfalse.\n\n=item * C<strict>\n\nWhen true, malformed quoted-printable escape sequences throw during\ndecoding. Non-strict decoding preserves malformed escape text\nliterally.\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/string/quoted_printable >> 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\n\nlet encode := null;\nlet decode := null;\n\n{\n from std/string import chr, index, ord, substr;\n from std/string/base64 import\n encode as _base64_encode,\n decode as _base64_decode;\n\n let _B64_ALPHABET := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"\n _ "abcdefghijklmnopqrstuvwxyz0123456789+/";\n let _HEX := "0123456789ABCDEF";\n\n function _div_floor ( Number n, Number d ) {\n return floor( n / d );\n }\n\n function _mod ( Number n, Number d ) {\n return n - _div_floor( n, d ) * d;\n }\n\n function _bytes_to_binary ( Array bytes ) {\n let out := "";\n let i := 0;\n let n := bytes.length();\n\n while ( i < n ) {\n let b0 := bytes[i];\n let b1 := null;\n let b2 := null;\n if ( i + 1 < n ) {\n b1 := bytes[i + 1];\n }\n if ( i + 2 < n ) {\n b2 := bytes[i + 2];\n }\n\n let c0 := _div_floor( b0, 4 );\n let c1 := _mod( b0, 4 ) * 16;\n let c2 := 64;\n let c3 := 64;\n\n if ( not( b1 == null ) ) {\n c1 += _div_floor( b1, 16 );\n c2 := _mod( b1, 16 ) * 4;\n if ( not( b2 == null ) ) {\n c2 += _div_floor( b2, 64 );\n c3 := _mod( b2, 64 );\n }\n }\n\n out _= substr( _B64_ALPHABET, c0, 1 );\n out _= substr( _B64_ALPHABET, c1, 1 );\n out _= c2 == 64 ? "=" : substr( _B64_ALPHABET, c2, 1 );\n out _= c3 == 64 ? "=" : substr( _B64_ALPHABET, c3, 1 );\n i += 3;\n }\n\n return _base64_decode(out);\n }\n\n function _binary_to_bytes ( BinaryString raw ) {\n let b64 := _base64_encode(raw);\n let out := [];\n let i := 0;\n let n := length b64;\n\n while ( i < n ) {\n let c0 := index( _B64_ALPHABET, substr( b64, i, 1 ) );\n let c1 := index( _B64_ALPHABET, substr( b64, i + 1, 1 ) );\n let ch2 := substr( b64, i + 2, 1 );\n let ch3 := substr( b64, i + 3, 1 );\n let c2 := -1;\n let c3 := -1;\n if ( ch2 ne "=" ) {\n c2 := index( _B64_ALPHABET, ch2 );\n }\n if ( ch3 ne "=" ) {\n c3 := index( _B64_ALPHABET, ch3 );\n }\n\n out.push( c0 * 4 + _div_floor( c1, 16 ) );\n if ( c2 >= 0 ) {\n out.push( _mod( c1, 16 ) * 16 + _div_floor( c2, 4 ) );\n }\n if ( c3 >= 0 ) {\n out.push( _mod( c2, 4 ) * 64 + c3 );\n }\n\n i += 4;\n }\n\n return out;\n }\n\n function _parse_options ( PairList options ) {\n let line_length := 76;\n let newline := "\\r\\n";\n let binary := false;\n let strict := false;\n\n for ( let option in options.enumerate() ) {\n let key := option.key;\n let value := option.value;\n\n if ( key eq "line_length" ) {\n line_length := value;\n }\n else if ( key eq "newline" ) {\n newline := value;\n }\n else if ( key eq "binary" ) {\n binary := value;\n }\n else if ( key eq "strict" ) {\n strict := value;\n }\n else {\n die `quoted_printable option \'${key}\' is not supported`;\n }\n }\n\n if ( not( line_length instanceof Number ) ) {\n die "quoted_printable line_length option expects Number";\n }\n if ( line_length < 4 ) {\n die "quoted_printable line_length option must be at least 4";\n }\n if ( not( newline instanceof String ) ) {\n die "quoted_printable newline option expects String";\n }\n if ( not( binary instanceof Boolean ) ) {\n die "quoted_printable binary option expects Boolean";\n }\n if ( not( strict instanceof Boolean ) ) {\n die "quoted_printable strict option expects Boolean";\n }\n\n return {\n line_length: int(line_length),\n newline: newline,\n binary: binary,\n strict: strict,\n };\n }\n\n function _is_safe_literal ( Number b ) {\n return ( b >= 33 and b <= 60 ) or ( b >= 62 and b <= 126 );\n }\n\n function _byte_to_hex_token ( Number b ) {\n return "="\n _ substr( _HEX, _div_floor( b, 16 ), 1 )\n _ substr( _HEX, _mod( b, 16 ), 1 );\n }\n\n function _simple_token_length ( Number b, Boolean final_byte ) {\n if ( ( b == 9 or b == 32 ) and not final_byte ) {\n return 1;\n }\n if ( _is_safe_literal(b) ) {\n return 1;\n }\n return 3;\n }\n\n function _space_tab_needs_escape (\n Array bytes,\n Number i,\n Number column,\n Number line_length,\n ) {\n let n := bytes.length();\n if ( i + 1 >= n ) {\n return true;\n }\n\n let j := i + 1;\n while ( j < n and ( bytes[j] == 9 or bytes[j] == 32 ) ) {\n j++;\n }\n if ( j >= n ) {\n return true;\n }\n\n let next_is_final := i + 2 >= n;\n let next_len := _simple_token_length( bytes[i + 1], next_is_final );\n let max := next_is_final ? line_length : line_length - 1;\n return column + 1 + next_len > max;\n }\n\n function _token_for_byte (\n Array bytes,\n Number i,\n Number column,\n Number line_length,\n ) {\n let b := bytes[i];\n\n if ( b == 9 or b == 32 ) {\n if ( _space_tab_needs_escape( bytes, i, column, line_length ) ) {\n return _byte_to_hex_token(b);\n }\n return chr(b);\n }\n\n if ( _is_safe_literal(b) ) {\n return chr(b);\n }\n\n return _byte_to_hex_token(b);\n }\n\n function _emit_token (\n String out,\n Number column,\n String token,\n Boolean more_after,\n Number line_length,\n String newline,\n ) {\n let max := more_after ? line_length - 1 : line_length;\n let updated_out := out;\n let updated_column := column;\n\n if ( updated_column > 0 and updated_column + ( length token ) > max ) {\n updated_out _= "=" _ newline;\n updated_column := 0;\n }\n\n updated_out _= token;\n updated_column += length token;\n\n return [ updated_out, updated_column ];\n }\n\n function _encode_segment (\n Array bytes,\n Number line_length,\n String newline,\n ) {\n let out := "";\n let column := 0;\n let i := 0;\n let n := bytes.length();\n\n while ( i < n ) {\n let token := _token_for_byte( bytes, i, column, line_length );\n let emitted := _emit_token(\n out,\n column,\n token,\n i + 1 < n,\n line_length,\n newline,\n );\n out := emitted[0];\n column := emitted[1];\n i++;\n }\n\n return out;\n }\n\n function _encode_text_bytes (\n Array bytes,\n Number line_length,\n String newline,\n ) {\n let out := "";\n let line := [];\n let i := 0;\n let n := bytes.length();\n\n while ( i < n ) {\n let b := bytes[i];\n if ( b == 13 or b == 10 ) {\n out _= _encode_segment( line, line_length, newline );\n out _= newline;\n line := [];\n\n if ( b == 13 and i + 1 < n and bytes[i + 1] == 10 ) {\n i++;\n }\n }\n else {\n line.push(b);\n }\n\n i++;\n }\n\n out _= _encode_segment( line, line_length, newline );\n return out;\n }\n\n function _hex_value ( String ch ) {\n let cp := ord(ch);\n if ( cp >= 48 and cp <= 57 ) {\n return cp - 48;\n }\n if ( cp >= 65 and cp <= 70 ) {\n return cp - 55;\n }\n if ( cp >= 97 and cp <= 102 ) {\n return cp - 87;\n }\n return -1;\n }\n\n function _decode_text_bytes ( String text, Boolean strict ) {\n let out := [];\n let i := 0;\n let n := length text;\n\n while ( i < n ) {\n let ch := substr( text, i, 1 );\n let cp := ord( text, i );\n\n if ( cp > 127 ) {\n die "quoted_printable.decode rejects non-ASCII input";\n }\n\n if ( ch eq "=" ) {\n if ( i + 1 >= n ) {\n die "malformed quoted-printable escape" if strict;\n out.push(61);\n i++;\n next;\n }\n\n let ch1 := substr( text, i + 1, 1 );\n if ( ch1 eq "\\r" ) {\n if ( i + 2 < n and substr( text, i + 2, 1 ) eq "\\n" ) {\n i += 3;\n }\n else {\n i += 2;\n }\n next;\n }\n if ( ch1 eq "\\n" ) {\n i += 2;\n next;\n }\n\n if ( i + 2 < n ) {\n let hi := _hex_value(ch1);\n let lo := _hex_value( substr( text, i + 2, 1 ) );\n if ( hi >= 0 and lo >= 0 ) {\n out.push( hi * 16 + lo );\n i += 3;\n next;\n }\n }\n\n die "malformed quoted-printable escape" if strict;\n out.push(61);\n i++;\n next;\n }\n\n out.push(cp);\n i++;\n }\n\n return out;\n }\n\n encode := function ( BinaryString bytes, ... PairList options ) {\n let opts := _parse_options(options);\n let raw := _binary_to_bytes(bytes);\n\n if ( opts{binary} ) {\n return _encode_segment(\n raw,\n opts{line_length},\n opts{newline},\n );\n }\n\n return _encode_text_bytes(\n raw,\n opts{line_length},\n opts{newline},\n );\n };\n\n decode := function ( String text, ... PairList options ) {\n let opts := _parse_options(options);\n return _bytes_to_binary( _decode_text_bytes( text, opts{strict} ) );\n };\n}\n';
|
|
41582
|
-
virtualFiles["/modules/std/gui.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/gui - User-facing GUI constructor helpers.\n\n=head1 SYNOPSIS\n\n from std/gui import *;\n\n let w := Window(\n title: "Demo",\n VBox(\n Label( text: "Name:" ),\n Input( id: "name" ),\n Button( text: "OK", id: "submit" ),\n ),\n );\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by zuzu.pl, zuzu-rust, and zuzu-js on Electron.\nIt is not supported by zuzu-js on Node. It is partially supported by\nzuzu-js in the browser: core GUI and widget lifecycle coverage passes,\nbut filesystem-backed file and directory dialogue coverage is unsupported.\n\n=head1 DESCRIPTION\n\nThis module provides thin constructor helpers over C<std/gui/objects>.\nHost runtimes expose the shared widget, object tree, event, and window\nlifecycle APIs when GUI support is available. It also re-exports\nbackend-native file and directory dialogue hooks for C<std/gui/dialogue>.\n\nThe module exports C<EM>, the standard UI font height in logical\npixels, derived from C<std/gui/objects> metadata.\n\n=head1 EXPORTS\n\n=head2 Constants\n\n=over\n\n=item C<EM>\n\nType: C<Number>. Standard UI font height in logical pixels.\n\n=item C<GUI_XML_NS>\n\nType: C<String>. XML namespace URI used by GUI XML serialization and\nparsing.\n\n=back\n\n=head2 Functions\n\n=over\n\n=item C<< Window(... PairList p, Array c) >>, C<< VBox(... PairList p, Array c) >>, C<< HBox(... PairList p, Array c) >>, C<< Frame(... PairList p, Array c) >>\n\nParameters: C<p> are widget properties and C<c> contains child widgets.\nReturns: widget object. Constructs window and layout widgets.\n\n=item C<< Label(... PairList p, Array c) >>, C<< Text(... PairList p, Array c) >>, C<< RichText(... PairList p, Array c) >>, C<< Image(... PairList p, Array c) >>\n\nParameters: C<p> are widget properties and C<c> contains child widgets.\nReturns: widget object. Constructs content widgets.\n\n=item C<< Input(... PairList p, Array c) >>, C<< DatePicker(... PairList p, Array c) >>, C<< Checkbox(... PairList p, Array c) >>, C<< Radio(... PairList p, Array c) >>, C<< RadioGroup(... PairList p, Array c) >>, C<< Select(... PairList p, Array c) >>\n\nParameters: C<p> are widget properties and C<c> contains child widgets.\nReturns: widget object. Constructs input widgets.\n\n=item C<< Menu(... PairList p, Array c) >>, C<< MenuItem(... PairList p, Array c) >>, C<< Button(... PairList p, Array c) >>, C<< Separator(... PairList p, Array c) >>, C<< Slider(... PairList p, Array c) >>, C<< Progress(... PairList p, Array c) >>\n\nParameters: C<p> are widget properties and C<c> contains child widgets.\nReturns: widget object. Constructs command and control widgets.\n\n=item C<< Tabs(... PairList p, Array c) >>, C<< Tab(... PairList p, Array c) >>, C<< ListView(... PairList p, Array c) >>, C<< TreeView(... PairList p, Array c) >>\n\nParameters: C<p> are widget properties and C<c> contains child widgets.\nReturns: widget object. Constructs tabular and collection widgets.\n\n=item C<< unbind(BindingToken token) >>\n\nParameters: C<token> is a binding token. Returns: C<null>. Removes a GUI\nbinding.\n\n=item C<< gui_from_xml(String xml) >>, C<< gui_from_xml_file(path) >>\n\nParameters: C<xml> is GUI XML text and C<path> is a file path. Returns:\nwidget object. Builds a GUI object tree from XML.\n\n=item C<< gui_to_xml(Widget root) >>\n\nParameters: C<root> is a GUI widget. Returns: C<String>. Serializes a\nGUI object tree to XML.\n\n=back\n\n=head2 Classes\n\n=over\n\n=item C<BindingToken>\n\nRepresents a model/widget binding.\n\n=over\n\n=item C<< token.is_active() >>\n\nParameters: none. Returns: C<Boolean>. Returns true while the binding is\nactive.\n\n=item C<< token.set_listener(token) >>\n\nParameters: C<token> is a listener token. Returns: C<BindingToken>.\nStores the listener token associated with the binding.\n\n=item C<< token.sync_model_to_widget() >>, C<< token.sync_widget_to_model() >>\n\nParameters: none. Returns: C<null>. Synchronizes the bound values.\n\n=item C<< token.unbind() >>\n\nParameters: none. Returns: C<null>. Removes the binding.\n\n=back\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/gui >> 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\nfrom std/gui/objects import\n Window as _WindowClass,\n VBox as _VBoxClass,\n HBox as _HBoxClass,\n Frame as _FrameClass,\n Label as _LabelClass,\n Text as _TextClass,\n RichText as _RichTextClass,\n Image as _ImageClass,\n Input as _InputClass,\n DatePicker as _DatePickerClass,\n Checkbox as _CheckboxClass,\n Radio as _RadioClass,\n RadioGroup as _RadioGroupClass,\n Select as _SelectClass,\n Menu as _MenuClass,\n MenuItem as _MenuItemClass,\n Button as _ButtonClass,\n Separator as _SeparatorClass,\n Slider as _SliderClass,\n Progress as _ProgressClass,\n Tabs as _TabsClass,\n Tab as _TabClass,\n ListView as _ListViewClass,\n TreeView as _TreeViewClass,\n Widget,\n Event,\n ListenerToken,\n native_file_open,\n native_file_save,\n native_directory_open,\n native_directory_save,\n native_colour_picker,\n meta as _gui_meta;\n\nfrom std/gui/objects try import\n native_alert,\n native_confirm,\n native_prompt;\n\nfrom std/data/xml import XML;\nfrom std/data/xml/escape import escape_xml;\nfrom std/internals import getupperprop;\nfrom std/path/zz import ZZPath;\nfrom std/string import join, split, substr, trim;\n\n\nconst Number EM := _gui_meta{font_size_pixels};\n\nfunction _assert_known_props ( String ctor, PairList props, Array allowed ) {\n let geometry := [\n "width",\n "height",\n "minwidth",\n "minheight",\n "maxwidth",\n "maxheight",\n ];\n for ( let key in props.keys() ) {\n if ( key \u2209 allowed and key \u2209 geometry ) {\n die `GUI_PROP_UNKNOWN: ${ctor} does not accept property \'${key}\'`;\n }\n }\n}\n\nfunction _assert_prop_value ( String ctor, String prop, value, Array allowed ) {\n if ( value \u2209 allowed ) {\n die `GUI_PROP_TYPE: ${ctor} property \'${prop}\' has invalid value`;\n }\n}\n\nfunction _assert_prop_array ( String ctor, String prop, value ) {\n if ( not( value instanceof Array ) ) {\n die `GUI_PROP_TYPE: ${ctor} property \'${prop}\' expects Array`;\n }\n}\n\nfunction _with_geometry_props ( Array allowed ) {\n for ( let key in [\n "width",\n "height",\n "minwidth",\n "minheight",\n "maxwidth",\n "maxheight",\n ] ) {\n allowed.push(key) unless key \u2208 allowed;\n }\n return allowed;\n}\n\nfunction _apply_geometry ( Widget widget, PairList p ) {\n for ( let key in [\n "width",\n "height",\n "minwidth",\n "minheight",\n "maxwidth",\n "maxheight",\n ] ) {\n if ( p.has(key) ) {\n widget.(key)( p.get(key) );\n }\n }\n return widget;\n}\n\nfunction Window ( ... PairList p, Array c ) {\n _assert_known_props(\n "Window",\n p,\n _with_geometry_props( [\n "id",\n "title",\n "width",\n "height",\n "resizable",\n "modal",\n "visible",\n "enabled",\n "disabled",\n ] ),\n );\n\n return new _WindowClass(\n id: p.get( "id", null ),\n title: p.get( "title", "" ),\n width: p.get( "width", 800 ),\n height: p.get( "height", 600 ),\n minwidth: p.get( "minwidth", null ),\n minheight: p.get( "minheight", null ),\n maxwidth: p.get( "maxwidth", null ),\n maxheight: p.get( "maxheight", null ),\n resizable: p.get( "resizable", true ),\n modal: p.get( "modal", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n );\n}\n\nfunction VBox ( ... PairList p, Array c ) {\n _assert_known_props(\n "VBox",\n p,\n [\n "id",\n "align",\n "gap",\n "padding",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n _assert_prop_value(\n "VBox",\n "align",\n p.get( "align", "top" ),\n [ "top", "centre", "bottom", "stretch" ],\n );\n\n return _apply_geometry( new _VBoxClass(\n id: p.get( "id", null ),\n align: p.get( "align", "top" ),\n gap: p.get( "gap", 0 ),\n padding: p.get( "padding", 0 ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction HBox ( ... PairList p, Array c ) {\n _assert_known_props(\n "HBox",\n p,\n _with_geometry_props( [\n "id",\n "align",\n "gap",\n "padding",\n "visible",\n "enabled",\n "disabled",\n ] ),\n );\n\n _assert_prop_value(\n "HBox",\n "align",\n p.get( "align", "left" ),\n [ "left", "centre", "right", "stretch" ],\n );\n\n return new _HBoxClass(\n id: p.get( "id", null ),\n align: p.get( "align", "left" ),\n gap: p.get( "gap", 0 ),\n padding: p.get( "padding", 0 ),\n width: p.get( "width", null ),\n height: p.get( "height", null ),\n minwidth: p.get( "minwidth", null ),\n minheight: p.get( "minheight", null ),\n maxwidth: p.get( "maxwidth", null ),\n maxheight: p.get( "maxheight", null ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n );\n}\n\nfunction Frame ( ... PairList p, Array c ) {\n _assert_known_props(\n "Frame",\n p,\n [\n "id",\n "label",\n "collapsible",\n "collapsed",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _FrameClass(\n id: p.get( "id", null ),\n label: p.get( "label", "" ),\n collapsible: p.get( "collapsible", false ),\n collapsed: p.get( "collapsed", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Label ( ... PairList p, Array c ) {\n _assert_known_props(\n "Label",\n p,\n [ "id", "text", "for", "visible", "enabled", "disabled" ],\n );\n\n let label := new _LabelClass(\n id: p.get( "id", null ),\n text: p.get( "text", "" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n );\n if ( p.has("for") ) {\n label.set_for_id( p.get( "for", null ) );\n }\n\n return _apply_geometry( label, p );\n}\n\nfunction Text ( ... PairList p, Array c ) {\n _assert_known_props(\n "Text",\n p,\n [\n "id",\n "value",\n "multiline",\n "readonly",\n "wrap",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _TextClass(\n id: p.get( "id", null ),\n value: p.get( "value", "" ),\n multiline: p.get( "multiline", false ),\n readonly: p.get( "readonly", false ),\n wrap: p.get( "wrap", true ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction RichText ( ... PairList p, Array c ) {\n _assert_known_props(\n "RichText",\n p,\n [\n "id",\n "value",\n "multiline",\n "readonly",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _RichTextClass(\n id: p.get( "id", null ),\n value: p.get( "value", "" ),\n multiline: p.get( "multiline", true ),\n readonly: p.get( "readonly", true ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Image ( ... PairList p, Array c ) {\n _assert_known_props(\n "Image",\n p,\n [ "id", "src", "alt", "fit", "visible", "enabled", "disabled" ],\n );\n\n _assert_prop_value(\n "Image",\n "fit",\n p.get( "fit", "none" ),\n [ "none", "contain", "cover", "stretch" ],\n );\n\n return _apply_geometry( new _ImageClass(\n id: p.get( "id", null ),\n src: p.get( "src", "" ),\n alt: p.get( "alt", "" ),\n fit: p.get( "fit", "none" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Input ( ... PairList p, Array c ) {\n _assert_known_props(\n "Input",\n p,\n [\n "id",\n "value",\n "placeholder",\n "multiline",\n "readonly",\n "password",\n "required",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _InputClass(\n id: p.get( "id", null ),\n value: p.get( "value", "" ),\n placeholder: p.get( "placeholder", "" ),\n multiline: p.get( "multiline", false ),\n readonly: p.get( "readonly", false ),\n password: p.get( "password", false ),\n required: p.get( "required", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction DatePicker ( ... PairList p, Array c ) {\n _assert_known_props(\n "DatePicker",\n p,\n [\n "id",\n "value",\n "min",\n "max",\n "first_day_of_week",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _DatePickerClass(\n id: p.get( "id", null ),\n value: p.get( "value", null ),\n min: p.get( "min", null ),\n max: p.get( "max", null ),\n first_day_of_week: p.get( "first_day_of_week", 0 ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Checkbox ( ... PairList p, Array c ) {\n _assert_known_props(\n "Checkbox",\n p,\n [\n "id",\n "label",\n "checked",\n "indeterminate",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _CheckboxClass(\n id: p.get( "id", null ),\n label: p.get( "label", "" ),\n checked: p.get( "checked", false ),\n indeterminate: p.get( "indeterminate", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Radio ( ... PairList p, Array c ) {\n _assert_known_props(\n "Radio",\n p,\n [\n "id",\n "label",\n "value",\n "group",\n "checked",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _RadioClass(\n id: p.get( "id", null ),\n label: p.get( "label", "" ),\n value: p.get( "value", "" ),\n group: p.get( "group", null ),\n checked: p.get( "checked", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction RadioGroup ( ... PairList p, Array c ) {\n _assert_known_props(\n "RadioGroup",\n p,\n [\n "id",\n "name",\n "value",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _RadioGroupClass(\n id: p.get( "id", null ),\n name: p.get( "name", "" ),\n value: p.get( "value", null ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Select ( ... PairList p, Array c ) {\n _assert_known_props(\n "Select",\n p,\n [\n "id",\n "value",\n "options",\n "multiple",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n if ( p.has("options") ) {\n _assert_prop_array( "Select", "options", p.get("options") );\n }\n\n return _apply_geometry( new _SelectClass(\n id: p.get( "id", null ),\n value: p.get( "value", null ),\n options: p.get( "options", [] ),\n multiple: p.get( "multiple", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Menu ( ... PairList p, Array c ) {\n _assert_known_props(\n "Menu",\n p,\n [ "id", "text", "visible", "enabled", "disabled" ],\n );\n\n return _apply_geometry( new _MenuClass(\n id: p.get( "id", null ),\n text: p.get( "text", "" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction MenuItem ( ... PairList p, Array c ) {\n _assert_known_props(\n "MenuItem",\n p,\n [ "id", "text", "disabled", "visible", "enabled" ],\n );\n\n return _apply_geometry( new _MenuItemClass(\n id: p.get( "id", null ),\n text: p.get( "text", "" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Button ( ... PairList p, Array c ) {\n _assert_known_props(\n "Button",\n p,\n _with_geometry_props( [\n "id",\n "text",\n "variant",\n "visible",\n "enabled",\n "disabled",\n ] ),\n );\n\n _assert_prop_value(\n "Button",\n "variant",\n p.get( "variant", "default" ),\n [ "default", "primary", "danger" ],\n );\n\n return new _ButtonClass(\n id: p.get( "id", null ),\n text: p.get( "text", "" ),\n variant: p.get( "variant", "default" ),\n width: p.get( "width", null ),\n height: p.get( "height", null ),\n minwidth: p.get( "minwidth", null ),\n minheight: p.get( "minheight", null ),\n maxwidth: p.get( "maxwidth", null ),\n maxheight: p.get( "maxheight", null ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n );\n}\n\nfunction Separator ( ... PairList p, Array c ) {\n _assert_known_props(\n "Separator",\n p,\n [ "id", "orientation", "visible", "enabled", "disabled" ],\n );\n\n _assert_prop_value(\n "Separator",\n "orientation",\n p.get( "orientation", "horizontal" ),\n [ "horizontal", "vertical" ],\n );\n\n return _apply_geometry( new _SeparatorClass(\n id: p.get( "id", null ),\n orientation: p.get( "orientation", "horizontal" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Slider ( ... PairList p, Array c ) {\n _assert_known_props(\n "Slider",\n p,\n [\n "id",\n "value",\n "min",\n "max",\n "step",\n "orientation",\n "readonly",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n _assert_prop_value(\n "Slider",\n "orientation",\n p.get( "orientation", "horizontal" ),\n [ "horizontal", "vertical" ],\n );\n\n return _apply_geometry( new _SliderClass(\n id: p.get( "id", null ),\n value: p.get( "value", 0 ),\n min: p.get( "min", 0 ),\n max: p.get( "max", 100 ),\n step: p.get( "step", 1 ),\n orientation: p.get( "orientation", "horizontal" ),\n readonly: p.get( "readonly", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Progress ( ... PairList p, Array c ) {\n _assert_known_props(\n "Progress",\n p,\n [\n "id",\n "value",\n "min",\n "max",\n "indeterminate",\n "show_text",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _ProgressClass(\n id: p.get( "id", null ),\n value: p.get( "value", 0 ),\n min: p.get( "min", 0 ),\n max: p.get( "max", 100 ),\n indeterminate: p.get( "indeterminate", false ),\n show_text: p.get( "show_text", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Tabs ( ... PairList p, Array c ) {\n _assert_known_props(\n "Tabs",\n p,\n [\n "id",\n "selected",\n "placement",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n _assert_prop_value(\n "Tabs",\n "placement",\n p.get( "placement", "top" ),\n [ "top", "bottom", "left", "right" ],\n );\n\n return _apply_geometry( new _TabsClass(\n id: p.get( "id", null ),\n selected: p.get( "selected", null ),\n placement: p.get( "placement", "top" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Tab ( ... PairList p, Array c ) {\n _assert_known_props(\n "Tab",\n p,\n [\n "id",\n "title",\n "value",\n "selected",\n "closable",\n "icon",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _TabClass(\n id: p.get( "id", null ),\n title: p.get( "title", "" ),\n value: p.get( "value", "" ),\n selected: p.get( "selected", false ),\n closable: p.get( "closable", false ),\n icon: p.get( "icon", null ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction ListView ( ... PairList p, Array c ) {\n _assert_known_props(\n "ListView",\n p,\n [\n "id",\n "items",\n "selected_index",\n "multiple",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n if ( p.has("items") ) {\n _assert_prop_array( "ListView", "items", p.get("items") );\n }\n\n return _apply_geometry( new _ListViewClass(\n id: p.get( "id", null ),\n items: p.get( "items", [] ),\n selected_index: p.get( "selected_index", null ),\n multiple: p.get( "multiple", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction TreeView ( ... PairList p, Array c ) {\n _assert_known_props(\n "TreeView",\n p,\n [\n "id",\n "items",\n "selected_path",\n "multiple",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n if ( p.has("items") ) {\n _assert_prop_array( "TreeView", "items", p.get("items") );\n }\n if ( p.has("selected_path") ) {\n _assert_prop_array( "TreeView", "selected_path", p.get("selected_path") );\n }\n\n return _apply_geometry( new _TreeViewClass(\n id: p.get( "id", null ),\n items: p.get( "items", [] ),\n selected_path: p.get( "selected_path", [] ),\n multiple: p.get( "multiple", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction _binding_path_class ( path_class ) {\n if ( path_class \u2261 null ) {\n return ZZPath;\n }\n if ( not( path_class instanceof Class ) ) {\n die "GUI_BIND_PATH: paths special property must be Class or null";\n }\n return path_class;\n}\n\nfunction _binding_compile_path ( String path_text, path_class ) {\n if ( path_class \u2261 null ) {\n try {\n return new ZZPath( path: path_text );\n }\n catch ( Exception e ) {\n die `GUI_BIND_PATH: ${e{message}}`;\n }\n }\n\n let path_type := _binding_path_class(path_class);\n try {\n return new path_type( path: path_text );\n }\n catch ( Exception e ) {\n die `GUI_BIND_PATH: ${e{message}}`;\n }\n}\n\nfunction _binding_path ( pathish, path_class ) {\n if ( pathish instanceof String ) {\n return _binding_compile_path( pathish, path_class );\n }\n if (\n pathish can first\n and ( pathish can assign_first or pathish can ref_first )\n ) {\n return pathish;\n }\n\n die "GUI_BIND_PATH: binding path must be String or path-like Object";\n}\n\nfunction _binding_get ( model, path ) {\n return path.first( model, null );\n}\n\nfunction _binding_set ( model, path, value ) {\n if ( path can assign_first ) {\n return path.assign_first( model, value );\n }\n\n let ref := path.ref_first(model);\n return ref(value);\n}\n\nclass BindingToken {\n let Widget widget but weak;\n let String widget_prop;\n let model;\n let model_path;\n let String event := "change";\n let listener := null;\n let Boolean _active := true;\n\n method is_active () {\n return _active;\n }\n\n method set_listener ( token ) {\n listener := token;\n return self;\n }\n\n method sync_model_to_widget () {\n widget.(widget_prop)( _binding_get( model, model_path ) );\n return self;\n }\n\n method sync_widget_to_model () {\n _binding_set( model, model_path, widget.(widget_prop)() );\n return self;\n }\n\n method unbind () {\n if ( _active and listener \u2262 null ) {\n widget.off(listener);\n }\n _active := false;\n return self;\n }\n}\n\nfunction bind (\n Widget widget,\n String widget_prop,\n model,\n model_path,\n ... PairList p\n) {\n let path := _binding_path( model_path, getupperprop( 1, "paths" ) );\n\n let token := new BindingToken(\n widget: widget,\n widget_prop: widget_prop,\n model: model,\n model_path: path,\n event: p.get( "event", "change" ),\n );\n let event_name := p.get( "event", "change" );\n\n switch ( p.get( "initial", "model" ): eq ) {\n case "model":\n token.sync_model_to_widget();\n case "widget":\n token.sync_widget_to_model();\n case "none":\n // No initial sync.\n default:\n die "GUI_BIND_INITIAL: initial must be model, widget, or none";\n }\n\n token.set_listener(\n widget.on( event_name, function () {\n if ( token.is_active() ) {\n token.sync_widget_to_model();\n }\n } ),\n );\n return token;\n}\n\nfunction unbind ( BindingToken token ) {\n return token.unbind();\n}\n\nlet GUI_XML_NS := "https://zuzulang.org/ns/std/gui";\n\nfunction _xml_error ( String code, String message ) {\n die `${code}: ${message}`;\n}\n\nfunction _xml_tag_name ( node ) {\n return node.localName() ?: node.nodeName();\n}\n\nfunction _xml_assert_namespace ( node ) {\n let ns := node.namespaceURI();\n if ( ns \u2262 null and ns \u2262 "" and ns \u2262 GUI_XML_NS ) {\n _xml_error(\n "GUI_XML_STRUCTURE",\n `unsupported GUI XML namespace \'${ns}\'`,\n );\n }\n}\n\nfunction _xml_common_allowed ( Array extra ) {\n let out := [\n "id",\n "visible",\n "enabled",\n "disabled",\n "width",\n "height",\n "minwidth",\n "minheight",\n "maxwidth",\n "maxheight",\n ];\n for ( let attr in extra ) {\n out.push(attr);\n }\n return out;\n}\n\nfunction _xml_allowed_attrs ( String tag ) {\n switch ( tag: eq ) {\n case "Window":\n return _xml_common_allowed(\n [ "title", "width", "height", "resizable", "modal" ],\n );\n case "VBox", "HBox":\n return _xml_common_allowed( [ "align", "gap", "padding" ] );\n case "Frame":\n return _xml_common_allowed( [ "label", "collapsible", "collapsed" ] );\n case "Label":\n return _xml_common_allowed( [ "text", "for" ] );\n case "Text":\n return _xml_common_allowed(\n [ "value", "multiline", "readonly", "wrap" ],\n );\n case "RichText":\n return _xml_common_allowed(\n [ "value", "multiline", "readonly" ],\n );\n case "Image":\n return _xml_common_allowed( [ "src", "alt", "fit" ] );\n case "Input":\n return _xml_common_allowed( [\n "value",\n "placeholder",\n "multiline",\n "readonly",\n "password",\n "required",\n ] );\n case "DatePicker":\n return _xml_common_allowed(\n [ "value", "min", "max", "first_day_of_week" ],\n );\n case "Checkbox":\n return _xml_common_allowed( [ "label", "checked", "indeterminate" ] );\n case "Radio":\n return _xml_common_allowed( [ "label", "value", "group", "checked" ] );\n case "RadioGroup":\n return _xml_common_allowed( [ "name", "value" ] );\n case "Select":\n return _xml_common_allowed( [ "value", "multiple" ] );\n case "Menu":\n return _xml_common_allowed( [ "text" ] );\n case "MenuItem":\n return _xml_common_allowed( [ "text" ] );\n case "Button":\n return _xml_common_allowed( [ "text", "variant" ] );\n case "Separator":\n return _xml_common_allowed( [ "orientation" ] );\n case "Slider":\n return _xml_common_allowed( [\n "value",\n "min",\n "max",\n "step",\n "orientation",\n "readonly",\n ] );\n case "Progress":\n return _xml_common_allowed( [\n "value",\n "min",\n "max",\n "indeterminate",\n "show_text",\n ] );\n case "Tabs":\n return _xml_common_allowed( [ "selected", "placement" ] );\n case "Tab":\n return _xml_common_allowed(\n [ "title", "value", "selected", "closable", "icon" ],\n );\n case "ListView":\n return _xml_common_allowed( [ "selected_index", "multiple" ] );\n case "TreeView":\n return _xml_common_allowed( [ "selected_path", "multiple" ] );\n }\n\n _xml_error( "GUI_XML_STRUCTURE", `unsupported GUI XML element \'${tag}\'` );\n}\n\nfunction _xml_bool_attrs ( String tag ) {\n return [\n "visible",\n "enabled",\n "disabled",\n "resizable",\n "modal",\n "collapsible",\n "collapsed",\n "multiline",\n "readonly",\n "wrap",\n "password",\n "required",\n "checked",\n "indeterminate",\n "multiple",\n "show_text",\n "selected",\n "closable",\n ];\n}\n\nfunction _xml_number_attrs ( String tag ) {\n return [\n "width",\n "height",\n "minwidth",\n "minheight",\n "maxwidth",\n "maxheight",\n "gap",\n "padding",\n "first_day_of_week",\n "selected_index",\n "value",\n "min",\n "max",\n "step",\n ];\n}\n\nfunction _xml_bool ( String tag, String attr, String raw ) {\n switch ( lc(raw): eq ) {\n case "true", "1", "yes", "on": return true;\n case "false", "0", "no", "off": return false;\n }\n _xml_error(\n "GUI_XML_ATTR_TYPE",\n `${tag}.${attr} expects a boolean XML attribute`,\n );\n}\n\nfunction _xml_number ( String tag, String attr, String raw ) {\n if ( not ( raw ~ /^-?[0-9]+(\\.[0-9]+)?$/ ) ) {\n _xml_error(\n "GUI_XML_ATTR_TYPE",\n `${tag}.${attr} expects a numeric XML attribute`,\n );\n }\n return 0 + raw;\n}\n\nfunction _xml_int_list ( String tag, String attr, String raw ) {\n let out := [];\n return out if raw eq "";\n for ( let part in split( raw, "," ) ) {\n let item := trim(part);\n if ( not ( item ~ /^-?[0-9]+$/ ) ) {\n _xml_error(\n "GUI_XML_ATTR_TYPE",\n `${tag}.${attr} expects comma-separated integers`,\n );\n }\n out.push( 0 + item );\n }\n return out;\n}\n\nfunction _xml_coerce_attr ( String tag, String attr, String raw ) {\n if ( attr eq "selected" and tag eq "Tabs" ) {\n return raw;\n }\n if ( attr eq "value" and tag \u2209 [ "Slider", "Progress" ] ) {\n return raw;\n }\n if ( attr eq "min" and tag eq "DatePicker" ) {\n return raw;\n }\n if ( attr eq "max" and tag eq "DatePicker" ) {\n return raw;\n }\n if ( attr eq "selected_path" ) {\n return _xml_int_list( tag, attr, raw );\n }\n if ( attr \u2208 _xml_bool_attrs(tag) ) {\n return _xml_bool( tag, attr, raw );\n }\n if ( attr \u2208 _xml_number_attrs(tag) ) {\n return _xml_number( tag, attr, raw );\n }\n return raw;\n}\n\nfunction _xml_attrs ( node ) {\n let tag := _xml_tag_name(node);\n let props := {};\n let meta := {};\n let meta_keys := [];\n let style := {};\n let style_keys := [];\n let allowed := _xml_allowed_attrs(tag);\n\n for ( let attr in node.attributeNames() ) {\n next if attr eq "xmlns";\n next if substr( attr, 0, 6 ) eq "xmlns:";\n let value := node.getAttribute(attr);\n if ( substr( attr, 0, 5 ) eq "meta." ) {\n let key := substr( attr, 5 );\n _xml_error( "GUI_XML_ATTR_UNKNOWN", "empty meta attribute name" )\n if key eq "";\n meta{(key)} := value;\n meta_keys.push(key);\n next;\n }\n if ( substr( attr, 0, 6 ) eq "style." ) {\n let key := substr( attr, 6 );\n _xml_error( "GUI_XML_ATTR_UNKNOWN", "empty style attribute name" )\n if key eq "";\n style{(key)} := value;\n style_keys.push(key);\n next;\n }\n if ( attr \u2209 allowed ) {\n _xml_error(\n "GUI_XML_ATTR_UNKNOWN",\n `${tag} does not accept XML attribute \'${attr}\'`,\n );\n }\n props{(attr)} := _xml_coerce_attr( tag, attr, value );\n }\n\n return {\n props: props,\n meta: meta,\n meta_keys: meta_keys,\n style: style,\n style_keys: style_keys,\n };\n}\n\nfunction _xml_apply_meta ( Widget widget, Dict meta, Array keys ) {\n for ( let key in keys ) {\n widget.meta( key, meta{(key)} );\n }\n if ( keys.length() > 0 ) {\n widget.meta( "__xml_meta_keys", keys );\n }\n return widget;\n}\n\nfunction _xml_apply_style ( Widget widget, Dict style, Array keys ) {\n for ( let key in keys ) {\n widget.style( key, style{(key)} );\n }\n if ( keys.length() > 0 ) {\n widget.meta( "__xml_style_keys", keys );\n }\n return widget;\n}\n\nfunction _xml_widget_from_node ( node ) {\n _xml_assert_namespace(node);\n\n let tag := _xml_tag_name(node);\n let child_widgets := [];\n for ( let child in node.children() ) {\n child_widgets.push( _xml_widget_from_node(child) );\n }\n let parsed := _xml_attrs(node);\n let p := parsed{props};\n let widget;\n\n switch ( tag: eq ) {\n case "Window":\n widget := new _WindowClass(\n id: p.get( "id", null ),\n title: p.get( "title", "" ),\n width: p.get( "width", 800 ),\n height: p.get( "height", 600 ),\n resizable: p.get( "resizable", true ),\n modal: p.get( "modal", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: child_widgets,\n );\n case "VBox":\n widget := new _VBoxClass(\n id: p.get( "id", null ),\n align: p.get( "align", "top" ),\n gap: p.get( "gap", 0 ),\n padding: p.get( "padding", 0 ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: child_widgets,\n );\n case "HBox":\n widget := new _HBoxClass(\n id: p.get( "id", null ),\n align: p.get( "align", "left" ),\n gap: p.get( "gap", 0 ),\n padding: p.get( "padding", 0 ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: child_widgets,\n );\n case "Frame":\n widget := new _FrameClass(\n id: p.get( "id", null ),\n label: p.get( "label", "" ),\n collapsible: p.get( "collapsible", false ),\n collapsed: p.get( "collapsed", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: child_widgets,\n );\n case "Label":\n widget := Label(\n id: p.get( "id", null ),\n text: p.get( "text", "" ),\n ("for"): p.get( "for", null ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Text":\n widget := Text(\n id: p.get( "id", null ),\n value: p.get( "value", "" ),\n multiline: p.get( "multiline", false ),\n readonly: p.get( "readonly", false ),\n wrap: p.get( "wrap", true ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "RichText":\n widget := RichText(\n id: p.get( "id", null ),\n value: p.get( "value", "" ),\n multiline: p.get( "multiline", true ),\n readonly: p.get( "readonly", true ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Image":\n widget := Image(\n id: p.get( "id", null ),\n src: p.get( "src", "" ),\n alt: p.get( "alt", "" ),\n fit: p.get( "fit", "none" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Input":\n widget := Input(\n id: p.get( "id", null ),\n value: p.get( "value", "" ),\n placeholder: p.get( "placeholder", "" ),\n multiline: p.get( "multiline", false ),\n readonly: p.get( "readonly", false ),\n password: p.get( "password", false ),\n required: p.get( "required", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "DatePicker":\n widget := DatePicker(\n id: p.get( "id", null ),\n value: p.get( "value", null ),\n min: p.get( "min", null ),\n max: p.get( "max", null ),\n first_day_of_week: p.get( "first_day_of_week", 0 ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Checkbox":\n widget := Checkbox(\n id: p.get( "id", null ),\n label: p.get( "label", "" ),\n checked: p.get( "checked", false ),\n indeterminate: p.get( "indeterminate", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Radio":\n widget := Radio(\n id: p.get( "id", null ),\n label: p.get( "label", "" ),\n value: p.get( "value", "" ),\n group: p.get( "group", null ),\n checked: p.get( "checked", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "RadioGroup":\n widget := new _RadioGroupClass(\n id: p.get( "id", null ),\n name: p.get( "name", "" ),\n value: p.get( "value", null ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: child_widgets,\n );\n case "Select":\n widget := Select(\n id: p.get( "id", null ),\n value: p.get( "value", null ),\n multiple: p.get( "multiple", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Menu":\n widget := new _MenuClass(\n id: p.get( "id", null ),\n text: p.get( "text", "" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: child_widgets,\n );\n case "MenuItem":\n widget := MenuItem(\n id: p.get( "id", null ),\n text: p.get( "text", "" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Button":\n widget := Button(\n id: p.get( "id", null ),\n text: p.get( "text", "" ),\n variant: p.get( "variant", "default" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Separator":\n widget := Separator(\n id: p.get( "id", null ),\n orientation: p.get( "orientation", "horizontal" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Slider":\n widget := Slider(\n id: p.get( "id", null ),\n value: p.get( "value", 0 ),\n min: p.get( "min", 0 ),\n max: p.get( "max", 100 ),\n step: p.get( "step", 1 ),\n orientation: p.get( "orientation", "horizontal" ),\n readonly: p.get( "readonly", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Progress":\n widget := Progress(\n id: p.get( "id", null ),\n value: p.get( "value", 0 ),\n min: p.get( "min", 0 ),\n max: p.get( "max", 100 ),\n indeterminate: p.get( "indeterminate", false ),\n show_text: p.get( "show_text", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Tabs":\n widget := new _TabsClass(\n id: p.get( "id", null ),\n selected: p.get( "selected", null ),\n placement: p.get( "placement", "top" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: child_widgets,\n );\n case "Tab":\n widget := new _TabClass(\n id: p.get( "id", null ),\n title: p.get( "title", "" ),\n value: p.get( "value", "" ),\n selected: p.get( "selected", false ),\n closable: p.get( "closable", false ),\n icon: p.get( "icon", null ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: child_widgets,\n );\n case "ListView":\n widget := ListView(\n id: p.get( "id", null ),\n selected_index: p.get( "selected_index", null ),\n multiple: p.get( "multiple", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "TreeView":\n widget := TreeView(\n id: p.get( "id", null ),\n selected_path: p.get( "selected_path", [] ),\n multiple: p.get( "multiple", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n default:\n _xml_error(\n "GUI_XML_STRUCTURE",\n `unsupported GUI XML element \'${tag}\'`,\n );\n }\n\n _xml_apply_meta( widget, parsed{meta}, parsed{meta_keys} );\n return _xml_apply_style( widget, parsed{style}, parsed{style_keys} );\n}\n\nfunction gui_from_xml ( String xml ) {\n return _xml_widget_from_node( XML.parse(xml).documentElement() );\n}\n\nfunction gui_from_xml_file ( path ) {\n die "XML.load is denied by runtime policy" if __system__{deny_fs};\n from std/io import Path;\n let file := path instanceof Path ? path : new Path(path);\n return _xml_widget_from_node( XML.load(file).documentElement() );\n}\n\nfunction _xml_tag_for_widget ( Widget widget ) {\n if ( widget instanceof _WindowClass ) { return "Window"; }\n if ( widget instanceof _VBoxClass ) { return "VBox"; }\n if ( widget instanceof _HBoxClass ) { return "HBox"; }\n if ( widget instanceof _FrameClass ) { return "Frame"; }\n if ( widget instanceof _LabelClass ) { return "Label"; }\n if ( widget instanceof _TextClass ) { return "Text"; }\n if ( widget instanceof _RichTextClass ) { return "RichText"; }\n if ( widget instanceof _ImageClass ) { return "Image"; }\n if ( widget instanceof _InputClass ) { return "Input"; }\n if ( widget instanceof _DatePickerClass ) { return "DatePicker"; }\n if ( widget instanceof _CheckboxClass ) { return "Checkbox"; }\n if ( widget instanceof _RadioClass ) { return "Radio"; }\n if ( widget instanceof _RadioGroupClass ) { return "RadioGroup"; }\n if ( widget instanceof _SelectClass ) { return "Select"; }\n if ( widget instanceof _MenuClass ) { return "Menu"; }\n if ( widget instanceof _MenuItemClass ) { return "MenuItem"; }\n if ( widget instanceof _ButtonClass ) { return "Button"; }\n if ( widget instanceof _SeparatorClass ) { return "Separator"; }\n if ( widget instanceof _SliderClass ) { return "Slider"; }\n if ( widget instanceof _ProgressClass ) { return "Progress"; }\n if ( widget instanceof _TabsClass ) { return "Tabs"; }\n if ( widget instanceof _TabClass ) { return "Tab"; }\n if ( widget instanceof _ListViewClass ) { return "ListView"; }\n if ( widget instanceof _TreeViewClass ) { return "TreeView"; }\n _xml_error( "GUI_XML_STRUCTURE", "unsupported widget for gui_to_xml" );\n}\n\nfunction _xml_attr ( String name, value ) {\n return "" if value \u2261 null;\n return ` ${name}="${escape_xml(value)}"`;\n}\n\nfunction _xml_attr_if ( String name, value, fallback ) {\n return "" if value \u2261 fallback;\n return _xml_attr( name, value );\n}\n\nfunction _xml_bool_attr_if ( String name, value, fallback ) {\n let normalized := value ? true : false;\n let normalized_fallback := fallback ? true : false;\n return "" if normalized \u2261 normalized_fallback;\n return _xml_attr( name, normalized ? "true" : "false" );\n}\n\nfunction _xml_attr_nonempty ( String name, value ) {\n return "" if value \u2261 null or value eq "";\n return _xml_attr( name, value );\n}\n\nfunction _xml_join_ints ( Array values ) {\n let out := [];\n for ( let value in values ) {\n out.push( "" _ value );\n }\n return join( ",", out );\n}\n\nfunction _xml_meta_attrs ( Widget widget ) {\n let keys := widget.meta( "__xml_meta_keys" ) ?: [];\n let out := "";\n for ( let key in keys ) {\n out _= _xml_attr( "meta." _ key, widget.meta(key) );\n }\n return out;\n}\n\nfunction _xml_style_attrs ( Widget widget ) {\n let keys := widget.meta( "__xml_style_keys" ) ?: [];\n let out := "";\n for ( let key in keys ) {\n out _= _xml_attr( "style." _ key, widget.style(key) );\n }\n return out;\n}\n\nfunction _xml_common_attrs ( Widget widget ) {\n let out := "";\n out _= _xml_attr( "id", widget.id() ) if widget.id() \u2262 null;\n out _= _xml_bool_attr_if( "visible", widget.visible(), true );\n out _= _xml_bool_attr_if( "disabled", not widget.enabled(), false );\n out _= _xml_meta_attrs(widget);\n out _= _xml_style_attrs(widget);\n return out;\n}\n\nfunction _xml_widget_attrs ( Widget widget ) {\n let out := _xml_common_attrs(widget);\n if ( widget instanceof _WindowClass ) {\n out _= _xml_attr_if( "title", widget.title(), "" );\n }\n else if ( widget instanceof _VBoxClass or widget instanceof _HBoxClass ) {\n let default_align := widget instanceof _VBoxClass ? "top" : "left";\n out _= _xml_attr_if( "align", widget.align(), default_align );\n out _= _xml_attr_if( "gap", widget.gap(), 0 );\n out _= _xml_attr_if( "padding", widget.padding(), 0 )\n unless widget.padding() instanceof Array;\n }\n else if ( widget instanceof _FrameClass ) {\n out _= _xml_attr_if( "label", widget.label(), "" );\n out _= _xml_bool_attr_if( "collapsible", widget.collapsible(), false );\n out _= _xml_bool_attr_if( "collapsed", widget.collapsed(), false );\n }\n else if ( widget instanceof _LabelClass ) {\n out _= _xml_attr_if( "text", widget.text(), "" );\n out _= _xml_attr_nonempty( "for", widget.for_id() );\n }\n else if ( widget instanceof _TextClass ) {\n out _= _xml_attr_if( "value", widget.value(), "" );\n out _= _xml_bool_attr_if( "multiline", widget.multiline(), false );\n out _= _xml_bool_attr_if( "readonly", widget.readonly(), false );\n out _= _xml_bool_attr_if( "wrap", widget.wrap(), true );\n }\n else if ( widget instanceof _RichTextClass ) {\n out _= _xml_attr_if( "value", widget.value(), "" );\n out _= _xml_bool_attr_if( "multiline", widget.multiline(), true );\n out _= _xml_bool_attr_if( "readonly", widget.readonly(), true );\n }\n else if ( widget instanceof _ImageClass ) {\n out _= _xml_attr_if( "src", widget.src(), "" );\n out _= _xml_attr_if( "alt", widget.alt(), "" );\n out _= _xml_attr_if( "fit", widget.fit(), "none" );\n }\n else if ( widget instanceof _InputClass ) {\n out _= _xml_attr_if( "value", widget.value(), "" );\n out _= _xml_attr_if( "placeholder", widget.placeholder(), "" );\n out _= _xml_bool_attr_if( "multiline", widget.multiline(), false );\n out _= _xml_bool_attr_if( "readonly", widget.readonly(), false );\n out _= _xml_bool_attr_if( "password", widget.password(), false );\n out _= _xml_bool_attr_if( "required", widget.required(), false );\n }\n else if ( widget instanceof _DatePickerClass ) {\n out _= _xml_attr( "value", widget.value() );\n out _= _xml_attr( "min", widget.min() ) if widget.min() \u2262 null;\n out _= _xml_attr( "max", widget.max() ) if widget.max() \u2262 null;\n out _= _xml_attr_if(\n "first_day_of_week",\n widget.first_day_of_week(),\n 0,\n );\n }\n else if ( widget instanceof _CheckboxClass ) {\n out _= _xml_attr_if( "label", widget.label(), "" );\n out _= _xml_bool_attr_if( "checked", widget.checked(), false );\n out _= _xml_bool_attr_if( "indeterminate", widget.indeterminate(), false );\n }\n else if ( widget instanceof _RadioClass ) {\n out _= _xml_attr_if( "label", widget.label(), "" );\n out _= _xml_attr_if( "value", widget.value(), "" );\n out _= _xml_attr_nonempty( "group", widget.group() );\n out _= _xml_bool_attr_if( "checked", widget.checked(), false );\n }\n else if ( widget instanceof _RadioGroupClass ) {\n out _= _xml_attr_if( "name", widget.name(), "" );\n out _= _xml_attr( "value", widget.value() ) if widget.value() \u2262 null;\n }\n else if ( widget instanceof _SelectClass ) {\n out _= _xml_attr( "value", widget.value() ) if widget.value() \u2262 null;\n out _= _xml_bool_attr_if( "multiple", widget.multiple(), false );\n }\n else if ( widget instanceof _MenuClass ) {\n out _= _xml_attr_if( "text", widget.text(), "" );\n }\n else if ( widget instanceof _MenuItemClass ) {\n out _= _xml_attr_if( "text", widget.text(), "" );\n }\n else if ( widget instanceof _ButtonClass ) {\n out _= _xml_attr_if( "text", widget.text(), "" );\n out _= _xml_attr_if( "variant", widget.variant(), "default" );\n }\n else if ( widget instanceof _SeparatorClass ) {\n out _= _xml_attr_if( "orientation", widget.orientation(), "horizontal" );\n }\n else if ( widget instanceof _SliderClass ) {\n out _= _xml_attr_if( "value", widget.value(), 0 );\n out _= _xml_attr_if( "min", widget.min(), 0 );\n out _= _xml_attr_if( "max", widget.max(), 100 );\n out _= _xml_attr_if( "step", widget.step(), 1 );\n out _= _xml_attr_if( "orientation", widget.orientation(), "horizontal" );\n out _= _xml_bool_attr_if( "readonly", widget.readonly(), false );\n }\n else if ( widget instanceof _ProgressClass ) {\n out _= _xml_attr_if( "value", widget.value(), 0 );\n out _= _xml_attr_if( "min", widget.min(), 0 );\n out _= _xml_attr_if( "max", widget.max(), 100 );\n out _= _xml_bool_attr_if( "indeterminate", widget.indeterminate(), false );\n out _= _xml_bool_attr_if( "show_text", widget.show_text(), false );\n }\n else if ( widget instanceof _TabsClass ) {\n out _= _xml_attr( "selected", widget.selected() )\n if widget.selected() \u2262 null;\n out _= _xml_attr_if( "placement", widget.placement(), "top" );\n }\n else if ( widget instanceof _TabClass ) {\n out _= _xml_attr_if( "title", widget.title(), "" );\n out _= _xml_attr_if( "value", widget.value(), "" );\n out _= _xml_attr( "icon", widget.icon() ) if widget.icon() \u2262 null;\n out _= _xml_bool_attr_if( "selected", widget.selected(), false );\n out _= _xml_bool_attr_if( "closable", widget.closable(), false );\n }\n else if ( widget instanceof _ListViewClass ) {\n out _= _xml_attr( "selected_index", widget.selected_index() )\n if widget.selected_index() \u2262 null;\n out _= _xml_bool_attr_if( "multiple", widget.multiple(), false );\n }\n else if ( widget instanceof _TreeViewClass ) {\n let selected_path := widget.selected_path();\n out _= _xml_attr( "selected_path", _xml_join_ints(selected_path) )\n if selected_path.length() > 0;\n out _= _xml_bool_attr_if( "multiple", widget.multiple(), false );\n }\n return out;\n}\n\nfunction _xml_serialize_widget ( Widget widget, Number depth ) {\n let indent := "";\n let i := 0;\n while ( i < depth ) {\n indent _= "\\t";\n i++;\n }\n\n let tag := _xml_tag_for_widget(widget);\n let attrs := _xml_widget_attrs(widget);\n attrs _= _xml_attr( "xmlns", GUI_XML_NS ) if depth = 0;\n let children := widget.children();\n if ( children.length() = 0 ) {\n return indent _ "<" _ tag _ attrs _ " />";\n }\n\n let parts := [];\n for ( let child in children ) {\n parts.push( _xml_serialize_widget( child, depth + 1 ) );\n }\n\n return indent _ "<" _ tag _ attrs _ ">\\n"\n _ join( "\\n", parts )\n _ "\\n" _ indent _ "</" _ tag _ ">";\n}\n\nfunction gui_to_xml ( Widget root ) {\n return _xml_serialize_widget( root, 0 ) _ "\\n";\n}\n';
|
|
41583
|
-
virtualFiles["/modules/std/gui/dialogue.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/gui/dialogue - Dialogue helpers.\n\n=head1 SYNOPSIS\n\n from std/gui/dialogue import *;\n\n alert("Saved");\n let ok := confirm("Continue?");\n let name := prompt("Name:", value: "Ada");\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by zuzu.pl, zuzu-rust, and zuzu-js on Electron\nand Browser. It is not supported by zuzu-js on Node.\n\n=head1 DESCRIPTION\n\nThis module provides convenience dialogue helpers layered on top of the\nregular C<std/gui> widgets.\n\n=head1 EXPORTS\n\n=head2 Functions\n\n=over\n\n=item C<< alert(String message, ... PairList p) >>, C<< alert_window(String message, ... PairList p) >>\n\nParameters: C<message> is display text and C<p> contains options.\nReturns: C<null> for C<alert> or C<Window> for C<alert_window>. Shows or\nbuilds an alert dialogue.\n\n=item C<< confirm(String message, ... PairList p) >>, C<< confirm_window(String message, ... PairList p) >>\n\nParameters: C<message> is display text and C<p> contains options.\nReturns: C<Boolean> for C<confirm> or C<Window> for\nC<confirm_window>. Shows or builds a confirmation dialogue.\n\n=item C<< prompt(String message, ... PairList p) >>, C<< prompt_window(String message, ... PairList p) >>\n\nParameters: C<message> is prompt text and C<p> contains options such as\nC<value>. Returns: C<String> or C<null> for C<prompt>, or C<Window> for\nC<prompt_window>. Shows or builds a text prompt.\n\n=item C<< file_open(... PairList p) >>, C<< file_save(... PairList p) >>\n\nParameters: C<p> contains dialogue options. Returns: path value or\nC<null>. Opens a file selection dialogue.\n\n=item C<< directory_open(... PairList p) >>, C<< directory_save(... PairList p) >>\n\nParameters: C<p> contains dialogue options. Returns: path value or\nC<null>. Opens a directory selection dialogue.\n\n=item C<< colour_picker(... PairList p) >>\n\nParameters: C<p> contains dialogue options. Returns: C<String> or\nC<null>. Opens a colour picker and returns a normalized colour string.\n\n=item C<< file_open_window(... PairList p) >>, C<< file_save_window(... PairList p) >>, C<< directory_open_window(... PairList p) >>, C<< directory_save_window(... PairList p) >>, C<< colour_picker_window(... PairList p) >>\n\nParameters: C<p> contains dialogue options. Returns: C<Window>. Builds\nthe corresponding GUI dialogue window.\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/gui/dialogue >> 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\nfrom std/gui try import\n EM,\n Window,\n VBox,\n HBox,\n Label,\n Text,\n Input,\n Button,\n Widget,\n native_file_open,\n native_file_save,\n native_directory_open,\n native_directory_save,\n native_colour_picker;\nfrom std/gui/objects try import\n meta as _objects_meta,\n native_alert,\n native_confirm,\n native_prompt;\nfrom std/colour import parse_colour;\nfrom std/string import split;\nfrom std/tui import\n colour_text,\n directory_completions,\n filename_completions,\n readline;\n\nfunction _tui_string ( value ) {\n return value \u2261 null ? "" : "" _ value;\n}\n\nfunction _gui_backend () {\n if ( _objects_meta \u2262 null ) {\n return _objects_meta{backend};\n }\n return "";\n}\n\nfunction _path_dialogue_unsupported () {\n if ( __system__{deny_fs} ) {\n die "GUI_DIALOGUE_FS_DENIED: file and directory dialogues require filesystem capability";\n }\n if ( _gui_backend() eq "browser-dom" ) {\n die "GUI_DIALOGUE_UNSUPPORTED: file and directory dialogues are unsupported in JS/Browser";\n }\n}\n\nfunction _tui_prompt ( String message, PairList p, String default_value ) {\n return readline(\n colour_text( message _ " ", "cyan" ),\n default_value,\n null,\n );\n}\n\nfunction _tui_path_dialog (\n String label,\n String default_value,\n completion,\n PairList p\n) {\n let answer := readline(\n colour_text( p.get( "label", label ) _ " ", "cyan" ),\n _tui_string( p.get( "value", default_value ) ),\n completion,\n );\n if ( p.get( "multiple", false ) ) {\n return split( answer, p.get( "separator", "\\n" ) );\n }\n return answer;\n}\n\nfunction _parse_colour_or_null ( value ) {\n return try {\n parse_colour( _tui_string(value) );\n }\n catch {\n null;\n };\n}\n\nfunction _colour_initial_value ( PairList p ) {\n let raw := _tui_string( p.get( "value", "#000000" ) );\n let parsed := _parse_colour_or_null(raw);\n return parsed \u2262 null ? parsed : raw;\n}\n\nfunction _colour_default_value ( PairList p ) {\n return _parse_colour_or_null( p.get( "value", "#000000" ) ) ?: "#000000";\n}\n\nfunction _tui_colour_dialog ( PairList p ) {\n let default_value := _colour_default_value(p);\n while ( true ) {\n let answer := readline(\n colour_text( p.get( "label", "Colour:" ) _ " ", "cyan" ),\n default_value,\n null,\n );\n let parsed := _parse_colour_or_null(answer);\n if ( parsed \u2262 null ) {\n return parsed;\n }\n say( colour_text( "Invalid colour; try #336699 or red.", "yellow" ) );\n }\n}\n\nfunction _dialogue_window (\n String kind,\n String title,\n Widget body,\n Array buttons,\n PairList p\n) {\n let button_row := HBox(\n id: "buttons",\n align: "right",\n gap: 6,\n height: p.get( "button_row_height", 2.125 \xD7 EM ),\n maxheight: p.get( "button_row_height", 2.125 \xD7 EM ),\n );\n for ( let button in buttons ) {\n button_row.add_child(button);\n }\n let w := Window(\n title: title,\n width: p.get( "width", 360 ),\n height: p.get( "height", 180 ),\n resizable: p.get( "resizable", false ),\n modal: true,\n VBox(\n id: p.get( "id", null ),\n gap: p.get( "gap", 8 ),\n padding: p.get( "padding", 12 ),\n body,\n button_row,\n ),\n );\n w.meta( "dialogue.kind", kind );\n return w;\n}\n\nfunction _message_body ( String message, PairList p ) {\n return Text(\n id: p.get( "message_id", "message" ),\n value: message,\n readonly: true,\n wrap: true,\n );\n}\n\nfunction _primary_button ( PairList p, String fallback ) {\n return Button(\n id: p.get( "ok_id", "ok" ),\n text: p.get( "ok_text", fallback ),\n variant: "primary",\n width: p.get( "button_width", 5.5 \xD7 EM ),\n height: p.get( "button_height", 1.75 \xD7 EM ),\n maxheight: p.get( "button_height", 1.75 \xD7 EM ),\n );\n}\n\nfunction _cancel_button ( PairList p ) {\n return Button(\n id: p.get( "cancel_id", "cancel" ),\n text: p.get( "cancel_text", "Cancel" ),\n width: p.get( "button_width", 5.5 \xD7 EM ),\n height: p.get( "button_height", 1.75 \xD7 EM ),\n maxheight: p.get( "button_height", 1.75 \xD7 EM ),\n );\n}\n\nfunction _alert_window ( String message, PairList p ) {\n let ok := _primary_button( p, "OK" );\n let w := _dialogue_window(\n "alert",\n p.get( "title", "Alert" ),\n _message_body( message, p ),\n [ ok ],\n p,\n );\n ok.click( function () {\n w.close(null);\n } );\n return w;\n}\n\nfunction alert_window ( String message, ... PairList p ) {\n return _alert_window( message, p );\n}\n\nfunction alert ( String message, ... PairList p ) {\n if ( p.has("auto_result") ) {\n return null;\n }\n if ( __system__{deny_gui} ) {\n say( colour_text( message, "yellow" ) );\n return null;\n }\n if ( native_alert \u2262 null ) {\n let native_result := native_alert( message, p );\n if ( native_result ) {\n return null;\n }\n }\n return _alert_window( message, p ).call();\n}\n\nfunction _confirm_window ( String message, PairList p ) {\n let ok := _primary_button( p, p.get( "ok_text", "OK" ) );\n let cancel := _cancel_button(p);\n let w := _dialogue_window(\n "confirm",\n p.get( "title", "Confirm" ),\n _message_body( message, p ),\n [ cancel, ok ],\n p,\n );\n ok.click( function () {\n w.close(true);\n } );\n cancel.click( function () {\n w.close(false);\n } );\n return w;\n}\n\nfunction confirm_window ( String message, ... PairList p ) {\n return _confirm_window( message, p );\n}\n\nfunction confirm ( String message, ... PairList p ) {\n if ( p.has("auto_result") ) {\n return p.get("auto_result") ? true : false;\n }\n if ( __system__{deny_gui} ) {\n let yes_default := p.get( "value", p.get( "default", false ) );\n let suffix := yes_default ? " [Y/n] " : " [y/N] ";\n let answer := readline(\n colour_text( message _ suffix, "cyan" ),\n yes_default ? "y" : "n",\n null,\n );\n return ( answer ~ /^(?:y|yes|1|true)$/i ) ? true : false;\n }\n if ( native_confirm \u2262 null ) {\n let native_result := native_confirm( message, p );\n if ( native_result \u2262 null ) {\n return native_result ? true : false;\n }\n }\n return _confirm_window( message, p ).call() ? true : false;\n}\n\nfunction _prompt_window ( String message, PairList p ) {\n let input := Input(\n id: p.get( "input_id", "value" ),\n value: p.get( "value", "" ),\n placeholder: p.get( "placeholder", "" ),\n );\n let ok := _primary_button( p, p.get( "ok_text", "OK" ) );\n let cancel := _cancel_button(p);\n let w := _dialogue_window(\n "prompt",\n p.get( "title", "Prompt" ),\n VBox(\n gap: 6,\n _message_body( message, p ),\n input,\n ),\n [ cancel, ok ],\n p,\n );\n ok.click( function () {\n w.close( input.value() );\n } );\n cancel.click( function () {\n w.close(null);\n } );\n return w;\n}\n\nfunction prompt_window ( String message, ... PairList p ) {\n return _prompt_window( message, p );\n}\n\nfunction prompt ( String message, ... PairList p ) {\n if ( p.has("auto_result") ) {\n return p.get("auto_result");\n }\n if ( __system__{deny_gui} ) {\n return _tui_prompt(\n message,\n p,\n _tui_string( p.get( "value", "" ) ),\n );\n }\n if ( native_prompt \u2262 null ) {\n let native_result := native_prompt( message, p );\n if ( _gui_backend() eq "browser-dom" ) {\n return native_result;\n }\n if ( native_result \u2262 null ) {\n return native_result;\n }\n }\n return _prompt_window( message, p ).call();\n}\n\nfunction _path_dialog_window (\n String kind,\n String title,\n String label,\n String default_value,\n PairList p\n) {\n let input := Input(\n id: p.get( "input_id", "path" ),\n value: p.get( "value", default_value ),\n placeholder: p.get( "placeholder", "" ),\n multiline: p.get( "multiple", false ),\n );\n let ok := _primary_button( p, p.get( "ok_text", "OK" ) );\n let cancel := _cancel_button(p);\n let w := _dialogue_window(\n kind,\n p.get( "title", title ),\n VBox(\n gap: 6,\n Label( text: p.get( "label", label ), ("for"): input.id() ),\n input,\n ),\n [ cancel, ok ],\n p,\n );\n ok.click( function () {\n if ( p.get( "multiple", false ) ) {\n w.close( split( input.value(), p.get( "separator", "\\n" ) ) );\n }\n else {\n w.close( input.value() );\n }\n } );\n cancel.click( function () {\n w.close(null);\n } );\n return w;\n}\n\nfunction file_open_window ( ... PairList p ) {\n return _path_dialog_window( "file_open", "Open File", "File:", "", p );\n}\n\nfunction file_save_window ( ... PairList p ) {\n return _path_dialog_window( "file_save", "Save File", "File:", "", p );\n}\n\nfunction directory_open_window ( ... PairList p ) {\n return _path_dialog_window(\n "directory_open",\n "Open Directory",\n "Directory:",\n "",\n p,\n );\n}\n\nfunction directory_save_window ( ... PairList p ) {\n return _path_dialog_window(\n "directory_save",\n "Save Directory",\n "Directory:",\n "",\n p,\n );\n}\n\nfunction _colour_picker_window ( PairList p ) {\n let input := Input(\n id: p.get( "input_id", "path" ),\n value: _colour_initial_value(p),\n placeholder: p.get( "placeholder", "#336699" ),\n );\n let ok := _primary_button( p, p.get( "ok_text", "OK" ) );\n let cancel := _cancel_button(p);\n let default_value := _colour_default_value(p);\n let sync_ok := function () {\n ok.set_enabled( _parse_colour_or_null( input.value() ) \u2262 null );\n };\n let w := _dialogue_window(\n "colour_picker",\n p.get( "title", "Choose Colour" ),\n VBox(\n gap: 6,\n Label(\n text: p.get( "label", "Colour:" ),\n ("for"): input.id(),\n ),\n input,\n ),\n [ cancel, ok ],\n p,\n );\n sync_ok();\n input.on( "change", sync_ok );\n ok.click( function () {\n let parsed := _parse_colour_or_null( input.value() );\n if ( parsed \u2262 null ) {\n w.close(parsed);\n }\n } );\n cancel.click( function () {\n w.close(default_value);\n } );\n return w;\n}\n\nfunction colour_picker_window ( ... PairList p ) {\n return _colour_picker_window(p);\n}\n\nfunction file_open ( ... PairList p ) {\n _path_dialogue_unsupported();\n if ( p.has("auto_result") ) {\n return p.get("auto_result");\n }\n if ( __system__{deny_gui} ) {\n return _tui_path_dialog( "File:", "", filename_completions, p );\n }\n return native_file_open(p);\n}\n\nfunction file_save ( ... PairList p ) {\n _path_dialogue_unsupported();\n if ( p.has("auto_result") ) {\n return p.get("auto_result");\n }\n if ( __system__{deny_gui} ) {\n return _tui_path_dialog( "File:", "", filename_completions, p );\n }\n return native_file_save(p);\n}\n\nfunction directory_open ( ... PairList p ) {\n _path_dialogue_unsupported();\n if ( p.has("auto_result") ) {\n return p.get("auto_result");\n }\n if ( __system__{deny_gui} ) {\n return _tui_path_dialog(\n "Directory:",\n "",\n directory_completions,\n p,\n );\n }\n return native_directory_open(p);\n}\n\nfunction directory_save ( ... PairList p ) {\n _path_dialogue_unsupported();\n if ( p.has("auto_result") ) {\n return p.get("auto_result");\n }\n if ( __system__{deny_gui} ) {\n return _tui_path_dialog(\n "Directory:",\n "",\n directory_completions,\n p,\n );\n }\n if ( native_directory_save \u2262 null ) {\n let native_result := native_directory_save(p);\n if ( native_result \u2262 null ) {\n return native_result;\n }\n if ( _gui_backend() eq "electron-dom" ) {\n return null;\n }\n }\n return _path_dialog_window(\n "directory_save",\n "Save Directory",\n "Directory:",\n "",\n p,\n ).call();\n}\n\nfunction colour_picker ( ... PairList p ) {\n if ( p.has("auto_result") ) {\n return parse_colour( _tui_string( p.get("auto_result") ) );\n }\n if ( __system__{deny_gui} ) {\n return _tui_colour_dialog(p);\n }\n if ( native_colour_picker \u2262 null ) {\n let native_result := native_colour_picker(p);\n if ( native_result \u2262 null ) {\n return parse_colour(native_result);\n }\n if ( _gui_backend() eq "browser-dom" ) {\n return null;\n }\n }\n if ( _gui_backend() eq "electron-dom" ) {\n return _colour_picker_window(p).call();\n }\n return parse_colour( _colour_picker_window(p).call() );\n}\n';
|
|
41649
|
+
virtualFiles["/modules/std/gui.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/gui - User-facing GUI constructor helpers.\n\n=head1 SYNOPSIS\n\n from std/gui import *;\n\n let w := Window(\n title: "Demo",\n VBox(\n Label( text: "Name:" ),\n Input( id: "name" ),\n Button( text: "OK", id: "submit" ),\n ),\n );\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by zuzu.pl, zuzu-rust, and zuzu-js on Electron.\nIt is not supported by zuzu-js on Node. It is partially supported by\nzuzu-js in the browser: core GUI and widget lifecycle coverage passes,\nbut filesystem-backed file and directory dialogue coverage is unsupported.\n\n=head1 DESCRIPTION\n\nThis module provides thin constructor helpers over C<std/gui/objects>.\nHost runtimes expose the shared widget, object tree, event, and window\nlifecycle APIs when GUI support is available. It also re-exports\nbackend-native file and directory dialogue hooks for C<std/gui/dialogue>.\n\nThe module exports C<EM>, the standard UI font height in logical\npixels, derived from C<std/gui/objects> metadata.\n\n=head1 EXPORTS\n\n=head2 Constants\n\n=over\n\n=item C<EM>\n\nType: C<Number>. Standard UI font height in logical pixels.\n\n=item C<GUI_XML_NS>\n\nType: C<String>. XML namespace URI used by GUI XML serialization and\nparsing.\n\n=back\n\n=head2 Functions\n\n=over\n\n=item C<< Window(... PairList p, Array c) >>, C<< VBox(... PairList p, Array c) >>, C<< HBox(... PairList p, Array c) >>, C<< Frame(... PairList p, Array c) >>\n\nParameters: C<p> are widget properties and C<c> contains child widgets.\nReturns: widget object. Constructs window and layout widgets.\n\n=item C<< Label(... PairList p, Array c) >>, C<< Text(... PairList p, Array c) >>, C<< RichText(... PairList p, Array c) >>, C<< Image(... PairList p, Array c) >>\n\nParameters: C<p> are widget properties and C<c> contains child widgets.\nReturns: widget object. Constructs content widgets.\n\n=item C<< Input(... PairList p, Array c) >>, C<< DatePicker(... PairList p, Array c) >>, C<< Checkbox(... PairList p, Array c) >>, C<< Radio(... PairList p, Array c) >>, C<< RadioGroup(... PairList p, Array c) >>, C<< Select(... PairList p, Array c) >>\n\nParameters: C<p> are widget properties and C<c> contains child widgets.\nReturns: widget object. Constructs input widgets.\n\n=item C<< Menu(... PairList p, Array c) >>, C<< MenuItem(... PairList p, Array c) >>, C<< Button(... PairList p, Array c) >>, C<< Separator(... PairList p, Array c) >>, C<< Slider(... PairList p, Array c) >>, C<< Progress(... PairList p, Array c) >>\n\nParameters: C<p> are widget properties and C<c> contains child widgets.\nReturns: widget object. Constructs command and control widgets.\n\n=item C<< Tabs(... PairList p, Array c) >>, C<< Tab(... PairList p, Array c) >>, C<< ListView(... PairList p, Array c) >>, C<< TreeView(... PairList p, Array c) >>\n\nParameters: C<p> are widget properties and C<c> contains child widgets.\nReturns: widget object. Constructs tabular and collection widgets.\n\n=item C<< unbind(BindingToken token) >>\n\nParameters: C<token> is a binding token. Returns: C<null>. Removes a GUI\nbinding.\n\n=item C<< gui_from_xml(String xml) >>, C<< gui_from_xml_file(path) >>\n\nParameters: C<xml> is GUI XML text and C<path> is a file path. Returns:\nwidget object. Builds a GUI object tree from XML.\n\n=item C<< gui_to_xml(Widget root) >>\n\nParameters: C<root> is a GUI widget. Returns: C<String>. Serializes a\nGUI object tree to XML.\n\n=back\n\n=head2 Classes\n\n=over\n\n=item C<BindingToken>\n\nRepresents a model/widget binding.\n\n=over\n\n=item C<< token.is_active() >>\n\nParameters: none. Returns: C<Boolean>. Returns true while the binding is\nactive.\n\n=item C<< token.set_listener(token) >>\n\nParameters: C<token> is a listener token. Returns: C<BindingToken>.\nStores the listener token associated with the binding.\n\n=item C<< token.sync_model_to_widget() >>, C<< token.sync_widget_to_model() >>\n\nParameters: none. Returns: C<null>. Synchronizes the bound values.\n\n=item C<< token.unbind() >>\n\nParameters: none. Returns: C<null>. Removes the binding.\n\n=back\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/gui >> 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\nfrom std/gui/objects import\n Window as _WindowClass,\n VBox as _VBoxClass,\n HBox as _HBoxClass,\n Frame as _FrameClass,\n Label as _LabelClass,\n Text as _TextClass,\n RichText as _RichTextClass,\n Image as _ImageClass,\n Input as _InputClass,\n DatePicker as _DatePickerClass,\n Checkbox as _CheckboxClass,\n Radio as _RadioClass,\n RadioGroup as _RadioGroupClass,\n Select as _SelectClass,\n Menu as _MenuClass,\n MenuItem as _MenuItemClass,\n Button as _ButtonClass,\n Separator as _SeparatorClass,\n Slider as _SliderClass,\n Progress as _ProgressClass,\n Tabs as _TabsClass,\n Tab as _TabClass,\n ListView as _ListViewClass,\n TreeView as _TreeViewClass,\n Widget,\n Event,\n ListenerToken,\n native_file_open,\n native_file_save,\n native_directory_open,\n native_directory_save,\n native_colour_picker,\n meta as _gui_meta;\n\nfrom std/gui/objects try import\n native_alert,\n native_confirm,\n native_prompt;\n\nfrom std/data/xml import XML;\nfrom std/data/xml/escape import escape_xml;\nfrom std/internals import getupperprop;\nfrom std/path/zz import ZZPath;\nfrom std/string import join, split, substr, trim;\n\n\nconst Number EM := _gui_meta{font_size_pixels};\n\nfunction _assert_known_props ( String ctor, PairList props, Array allowed ) {\n let geometry := [\n "width",\n "height",\n "minwidth",\n "minheight",\n "maxwidth",\n "maxheight",\n ];\n for ( let key in props.keys() ) {\n if ( key \u2209 allowed and key \u2209 geometry ) {\n die `GUI_PROP_UNKNOWN: ${ctor} does not accept property \'${key}\'`;\n }\n }\n}\n\nfunction _assert_prop_value ( String ctor, String prop, value, Array allowed ) {\n if ( value \u2209 allowed ) {\n die `GUI_PROP_TYPE: ${ctor} property \'${prop}\' has invalid value`;\n }\n}\n\nfunction _assert_prop_array ( String ctor, String prop, value ) {\n if ( not( value instanceof Array ) ) {\n die `GUI_PROP_TYPE: ${ctor} property \'${prop}\' expects Array`;\n }\n}\n\nfunction _with_geometry_props ( Array allowed ) {\n for ( let key in [\n "width",\n "height",\n "minwidth",\n "minheight",\n "maxwidth",\n "maxheight",\n ] ) {\n allowed.push(key) unless key \u2208 allowed;\n }\n return allowed;\n}\n\nfunction _apply_geometry ( Widget widget, PairList p ) {\n for ( let key in [\n "width",\n "height",\n "minwidth",\n "minheight",\n "maxwidth",\n "maxheight",\n ] ) {\n if ( p.has(key) ) {\n widget.(key)( p.get(key) );\n }\n }\n return widget;\n}\n\nfunction Window ( ... PairList p, Array c ) {\n _assert_known_props(\n "Window",\n p,\n _with_geometry_props( [\n "id",\n "title",\n "width",\n "height",\n "resizable",\n "modal",\n "visible",\n "enabled",\n "disabled",\n ] ),\n );\n\n return new _WindowClass(\n id: p.get( "id", null ),\n title: p.get( "title", "" ),\n width: p.get( "width", 800 ),\n height: p.get( "height", 600 ),\n minwidth: p.get( "minwidth", null ),\n minheight: p.get( "minheight", null ),\n maxwidth: p.get( "maxwidth", null ),\n maxheight: p.get( "maxheight", null ),\n resizable: p.get( "resizable", true ),\n modal: p.get( "modal", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n );\n}\n\nfunction VBox ( ... PairList p, Array c ) {\n _assert_known_props(\n "VBox",\n p,\n [\n "id",\n "align",\n "gap",\n "padding",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n _assert_prop_value(\n "VBox",\n "align",\n p.get( "align", "top" ),\n [ "top", "centre", "bottom", "stretch" ],\n );\n\n return _apply_geometry( new _VBoxClass(\n id: p.get( "id", null ),\n align: p.get( "align", "top" ),\n gap: p.get( "gap", 0 ),\n padding: p.get( "padding", 0 ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction HBox ( ... PairList p, Array c ) {\n _assert_known_props(\n "HBox",\n p,\n _with_geometry_props( [\n "id",\n "align",\n "gap",\n "padding",\n "visible",\n "enabled",\n "disabled",\n ] ),\n );\n\n _assert_prop_value(\n "HBox",\n "align",\n p.get( "align", "left" ),\n [ "left", "centre", "right", "stretch" ],\n );\n\n return new _HBoxClass(\n id: p.get( "id", null ),\n align: p.get( "align", "left" ),\n gap: p.get( "gap", 0 ),\n padding: p.get( "padding", 0 ),\n width: p.get( "width", null ),\n height: p.get( "height", null ),\n minwidth: p.get( "minwidth", null ),\n minheight: p.get( "minheight", null ),\n maxwidth: p.get( "maxwidth", null ),\n maxheight: p.get( "maxheight", null ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n );\n}\n\nfunction Frame ( ... PairList p, Array c ) {\n _assert_known_props(\n "Frame",\n p,\n [\n "id",\n "label",\n "collapsible",\n "collapsed",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _FrameClass(\n id: p.get( "id", null ),\n label: p.get( "label", "" ),\n collapsible: p.get( "collapsible", false ),\n collapsed: p.get( "collapsed", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Label ( ... PairList p, Array c ) {\n _assert_known_props(\n "Label",\n p,\n [ "id", "text", "for", "visible", "enabled", "disabled" ],\n );\n\n let label := new _LabelClass(\n id: p.get( "id", null ),\n text: p.get( "text", "" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n );\n if ( p.has("for") ) {\n label.set_for_id( p.get( "for", null ) );\n }\n\n return _apply_geometry( label, p );\n}\n\nfunction Text ( ... PairList p, Array c ) {\n _assert_known_props(\n "Text",\n p,\n [\n "id",\n "value",\n "multiline",\n "readonly",\n "wrap",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _TextClass(\n id: p.get( "id", null ),\n value: p.get( "value", "" ),\n multiline: p.get( "multiline", false ),\n readonly: p.get( "readonly", false ),\n wrap: p.get( "wrap", true ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction RichText ( ... PairList p, Array c ) {\n _assert_known_props(\n "RichText",\n p,\n [\n "id",\n "value",\n "multiline",\n "readonly",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _RichTextClass(\n id: p.get( "id", null ),\n value: p.get( "value", "" ),\n multiline: p.get( "multiline", true ),\n readonly: p.get( "readonly", true ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Image ( ... PairList p, Array c ) {\n _assert_known_props(\n "Image",\n p,\n [ "id", "src", "alt", "fit", "visible", "enabled", "disabled" ],\n );\n\n _assert_prop_value(\n "Image",\n "fit",\n p.get( "fit", "none" ),\n [ "none", "contain", "cover", "stretch" ],\n );\n\n return _apply_geometry( new _ImageClass(\n id: p.get( "id", null ),\n src: p.get( "src", "" ),\n alt: p.get( "alt", "" ),\n fit: p.get( "fit", "none" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Input ( ... PairList p, Array c ) {\n _assert_known_props(\n "Input",\n p,\n [\n "id",\n "value",\n "placeholder",\n "multiline",\n "readonly",\n "password",\n "required",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _InputClass(\n id: p.get( "id", null ),\n value: p.get( "value", "" ),\n placeholder: p.get( "placeholder", "" ),\n multiline: p.get( "multiline", false ),\n readonly: p.get( "readonly", false ),\n password: p.get( "password", false ),\n required: p.get( "required", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction DatePicker ( ... PairList p, Array c ) {\n _assert_known_props(\n "DatePicker",\n p,\n [\n "id",\n "value",\n "min",\n "max",\n "first_day_of_week",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _DatePickerClass(\n id: p.get( "id", null ),\n value: p.get( "value", null ),\n min: p.get( "min", null ),\n max: p.get( "max", null ),\n first_day_of_week: p.get( "first_day_of_week", 0 ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Checkbox ( ... PairList p, Array c ) {\n _assert_known_props(\n "Checkbox",\n p,\n [\n "id",\n "label",\n "checked",\n "indeterminate",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _CheckboxClass(\n id: p.get( "id", null ),\n label: p.get( "label", "" ),\n checked: p.get( "checked", false ),\n indeterminate: p.get( "indeterminate", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Radio ( ... PairList p, Array c ) {\n _assert_known_props(\n "Radio",\n p,\n [\n "id",\n "label",\n "value",\n "group",\n "checked",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _RadioClass(\n id: p.get( "id", null ),\n label: p.get( "label", "" ),\n value: p.get( "value", "" ),\n group: p.get( "group", null ),\n checked: p.get( "checked", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction RadioGroup ( ... PairList p, Array c ) {\n _assert_known_props(\n "RadioGroup",\n p,\n [\n "id",\n "name",\n "value",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _RadioGroupClass(\n id: p.get( "id", null ),\n name: p.get( "name", "" ),\n value: p.get( "value", null ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Select ( ... PairList p, Array c ) {\n _assert_known_props(\n "Select",\n p,\n [\n "id",\n "value",\n "options",\n "multiple",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n if ( p.has("options") ) {\n _assert_prop_array( "Select", "options", p.get("options") );\n }\n\n return _apply_geometry( new _SelectClass(\n id: p.get( "id", null ),\n value: p.get( "value", null ),\n options: p.get( "options", [] ),\n multiple: p.get( "multiple", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Menu ( ... PairList p, Array c ) {\n _assert_known_props(\n "Menu",\n p,\n [ "id", "text", "visible", "enabled", "disabled" ],\n );\n\n return _apply_geometry( new _MenuClass(\n id: p.get( "id", null ),\n text: p.get( "text", "" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction MenuItem ( ... PairList p, Array c ) {\n _assert_known_props(\n "MenuItem",\n p,\n [ "id", "text", "disabled", "visible", "enabled" ],\n );\n\n return _apply_geometry( new _MenuItemClass(\n id: p.get( "id", null ),\n text: p.get( "text", "" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Button ( ... PairList p, Array c ) {\n _assert_known_props(\n "Button",\n p,\n _with_geometry_props( [\n "id",\n "text",\n "variant",\n "visible",\n "enabled",\n "disabled",\n ] ),\n );\n\n _assert_prop_value(\n "Button",\n "variant",\n p.get( "variant", "default" ),\n [ "default", "primary", "danger" ],\n );\n\n return new _ButtonClass(\n id: p.get( "id", null ),\n text: p.get( "text", "" ),\n variant: p.get( "variant", "default" ),\n width: p.get( "width", null ),\n height: p.get( "height", null ),\n minwidth: p.get( "minwidth", null ),\n minheight: p.get( "minheight", null ),\n maxwidth: p.get( "maxwidth", null ),\n maxheight: p.get( "maxheight", null ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n );\n}\n\nfunction Separator ( ... PairList p, Array c ) {\n _assert_known_props(\n "Separator",\n p,\n [ "id", "orientation", "visible", "enabled", "disabled" ],\n );\n\n _assert_prop_value(\n "Separator",\n "orientation",\n p.get( "orientation", "horizontal" ),\n [ "horizontal", "vertical" ],\n );\n\n return _apply_geometry( new _SeparatorClass(\n id: p.get( "id", null ),\n orientation: p.get( "orientation", "horizontal" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Slider ( ... PairList p, Array c ) {\n _assert_known_props(\n "Slider",\n p,\n [\n "id",\n "value",\n "min",\n "max",\n "step",\n "orientation",\n "readonly",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n _assert_prop_value(\n "Slider",\n "orientation",\n p.get( "orientation", "horizontal" ),\n [ "horizontal", "vertical" ],\n );\n\n return _apply_geometry( new _SliderClass(\n id: p.get( "id", null ),\n value: p.get( "value", 0 ),\n min: p.get( "min", 0 ),\n max: p.get( "max", 100 ),\n step: p.get( "step", 1 ),\n orientation: p.get( "orientation", "horizontal" ),\n readonly: p.get( "readonly", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Progress ( ... PairList p, Array c ) {\n _assert_known_props(\n "Progress",\n p,\n [\n "id",\n "value",\n "min",\n "max",\n "indeterminate",\n "show_text",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _ProgressClass(\n id: p.get( "id", null ),\n value: p.get( "value", 0 ),\n min: p.get( "min", 0 ),\n max: p.get( "max", 100 ),\n indeterminate: p.get( "indeterminate", false ),\n show_text: p.get( "show_text", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Tabs ( ... PairList p, Array c ) {\n _assert_known_props(\n "Tabs",\n p,\n [\n "id",\n "selected",\n "placement",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n _assert_prop_value(\n "Tabs",\n "placement",\n p.get( "placement", "top" ),\n [ "top", "bottom", "left", "right" ],\n );\n\n return _apply_geometry( new _TabsClass(\n id: p.get( "id", null ),\n selected: p.get( "selected", null ),\n placement: p.get( "placement", "top" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction Tab ( ... PairList p, Array c ) {\n _assert_known_props(\n "Tab",\n p,\n [\n "id",\n "title",\n "value",\n "selected",\n "closable",\n "icon",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n return _apply_geometry( new _TabClass(\n id: p.get( "id", null ),\n title: p.get( "title", "" ),\n value: p.get( "value", "" ),\n selected: p.get( "selected", false ),\n closable: p.get( "closable", false ),\n icon: p.get( "icon", null ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction ListView ( ... PairList p, Array c ) {\n _assert_known_props(\n "ListView",\n p,\n [\n "id",\n "items",\n "selected_index",\n "multiple",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n if ( p.has("items") ) {\n _assert_prop_array( "ListView", "items", p.get("items") );\n }\n\n return _apply_geometry( new _ListViewClass(\n id: p.get( "id", null ),\n items: p.get( "items", [] ),\n selected_index: p.get( "selected_index", null ),\n multiple: p.get( "multiple", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction TreeView ( ... PairList p, Array c ) {\n _assert_known_props(\n "TreeView",\n p,\n [\n "id",\n "items",\n "selected_path",\n "multiple",\n "visible",\n "enabled",\n "disabled",\n ],\n );\n\n if ( p.has("items") ) {\n _assert_prop_array( "TreeView", "items", p.get("items") );\n }\n if ( p.has("selected_path") ) {\n _assert_prop_array( "TreeView", "selected_path", p.get("selected_path") );\n }\n\n return _apply_geometry( new _TreeViewClass(\n id: p.get( "id", null ),\n items: p.get( "items", [] ),\n selected_path: p.get( "selected_path", [] ),\n multiple: p.get( "multiple", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: c,\n ), p );\n}\n\nfunction _binding_path_class ( path_class ) {\n if ( path_class \u2261 null ) {\n return ZZPath;\n }\n if ( not( path_class instanceof Class ) ) {\n die "GUI_BIND_PATH: paths special property must be Class or null";\n }\n return path_class;\n}\n\nfunction _binding_compile_path ( String path_text, path_class ) {\n if ( path_class \u2261 null ) {\n try {\n return new ZZPath( path: path_text );\n }\n catch ( Exception e ) {\n die `GUI_BIND_PATH: ${e{message}}`;\n }\n }\n\n let path_type := _binding_path_class(path_class);\n try {\n return new path_type( path: path_text );\n }\n catch ( Exception e ) {\n die `GUI_BIND_PATH: ${e{message}}`;\n }\n}\n\nfunction _binding_path ( pathish, path_class ) {\n if ( pathish instanceof String ) {\n return _binding_compile_path( pathish, path_class );\n }\n if (\n pathish can first\n and ( pathish can assign_first or pathish can ref_first )\n ) {\n return pathish;\n }\n\n die "GUI_BIND_PATH: binding path must be String or path-like Object";\n}\n\nfunction _binding_get ( model, path ) {\n return path.first( model, null );\n}\n\nfunction _binding_set ( model, path, value ) {\n if ( path can assign_first ) {\n return path.assign_first( model, value );\n }\n\n let ref := path.ref_first(model);\n return ref(value);\n}\n\nclass BindingToken {\n let Widget widget but weak;\n let String widget_prop;\n let model;\n let model_path;\n let String event := "change";\n let listener := null;\n let Boolean _active := true;\n\n method is_active () {\n return _active;\n }\n\n method set_listener ( token ) {\n listener := token;\n return self;\n }\n\n method sync_model_to_widget () {\n widget.(widget_prop)( _binding_get( model, model_path ) );\n return self;\n }\n\n method sync_widget_to_model () {\n _binding_set( model, model_path, widget.(widget_prop)() );\n return self;\n }\n\n method unbind () {\n if ( _active and listener \u2262 null ) {\n widget.off(listener);\n }\n _active := false;\n return self;\n }\n}\n\nfunction bind (\n Widget widget,\n String widget_prop,\n model,\n model_path,\n ... PairList p\n) {\n let path := _binding_path( model_path, getupperprop( 1, "paths" ) );\n\n let token := new BindingToken(\n widget: widget,\n widget_prop: widget_prop,\n model: model,\n model_path: path,\n event: p.get( "event", "change" ),\n );\n let event_name := p.get( "event", "change" );\n\n switch ( p.get( "initial", "model" ): eq ) {\n case "model":\n token.sync_model_to_widget();\n case "widget":\n token.sync_widget_to_model();\n case "none":\n // No initial sync.\n default:\n die "GUI_BIND_INITIAL: initial must be model, widget, or none";\n }\n\n token.set_listener(\n widget.on( event_name, function () {\n if ( token.is_active() ) {\n token.sync_widget_to_model();\n }\n } ),\n );\n return token;\n}\n\nfunction unbind ( BindingToken token ) {\n return token.unbind();\n}\n\nlet GUI_XML_NS := "https://zuzulang.org/ns/std/gui";\n\nfunction _xml_error ( String code, String message ) {\n die `${code}: ${message}`;\n}\n\nfunction _xml_tag_name ( node ) {\n return node.localName() or? node.nodeName();\n}\n\nfunction _xml_assert_namespace ( node ) {\n let ns := node.namespaceURI();\n if ( ns \u2262 null and ns \u2262 "" and ns \u2262 GUI_XML_NS ) {\n _xml_error(\n "GUI_XML_STRUCTURE",\n `unsupported GUI XML namespace \'${ns}\'`,\n );\n }\n}\n\nfunction _xml_common_allowed ( Array extra ) {\n let out := [\n "id",\n "visible",\n "enabled",\n "disabled",\n "width",\n "height",\n "minwidth",\n "minheight",\n "maxwidth",\n "maxheight",\n ];\n for ( let attr in extra ) {\n out.push(attr);\n }\n return out;\n}\n\nfunction _xml_allowed_attrs ( String tag ) {\n switch ( tag: eq ) {\n case "Window":\n return _xml_common_allowed(\n [ "title", "width", "height", "resizable", "modal" ],\n );\n case "VBox", "HBox":\n return _xml_common_allowed( [ "align", "gap", "padding" ] );\n case "Frame":\n return _xml_common_allowed( [ "label", "collapsible", "collapsed" ] );\n case "Label":\n return _xml_common_allowed( [ "text", "for" ] );\n case "Text":\n return _xml_common_allowed(\n [ "value", "multiline", "readonly", "wrap" ],\n );\n case "RichText":\n return _xml_common_allowed(\n [ "value", "multiline", "readonly" ],\n );\n case "Image":\n return _xml_common_allowed( [ "src", "alt", "fit" ] );\n case "Input":\n return _xml_common_allowed( [\n "value",\n "placeholder",\n "multiline",\n "readonly",\n "password",\n "required",\n ] );\n case "DatePicker":\n return _xml_common_allowed(\n [ "value", "min", "max", "first_day_of_week" ],\n );\n case "Checkbox":\n return _xml_common_allowed( [ "label", "checked", "indeterminate" ] );\n case "Radio":\n return _xml_common_allowed( [ "label", "value", "group", "checked" ] );\n case "RadioGroup":\n return _xml_common_allowed( [ "name", "value" ] );\n case "Select":\n return _xml_common_allowed( [ "value", "multiple" ] );\n case "Menu":\n return _xml_common_allowed( [ "text" ] );\n case "MenuItem":\n return _xml_common_allowed( [ "text" ] );\n case "Button":\n return _xml_common_allowed( [ "text", "variant" ] );\n case "Separator":\n return _xml_common_allowed( [ "orientation" ] );\n case "Slider":\n return _xml_common_allowed( [\n "value",\n "min",\n "max",\n "step",\n "orientation",\n "readonly",\n ] );\n case "Progress":\n return _xml_common_allowed( [\n "value",\n "min",\n "max",\n "indeterminate",\n "show_text",\n ] );\n case "Tabs":\n return _xml_common_allowed( [ "selected", "placement" ] );\n case "Tab":\n return _xml_common_allowed(\n [ "title", "value", "selected", "closable", "icon" ],\n );\n case "ListView":\n return _xml_common_allowed( [ "selected_index", "multiple" ] );\n case "TreeView":\n return _xml_common_allowed( [ "selected_path", "multiple" ] );\n }\n\n _xml_error( "GUI_XML_STRUCTURE", `unsupported GUI XML element \'${tag}\'` );\n}\n\nfunction _xml_bool_attrs ( String tag ) {\n return [\n "visible",\n "enabled",\n "disabled",\n "resizable",\n "modal",\n "collapsible",\n "collapsed",\n "multiline",\n "readonly",\n "wrap",\n "password",\n "required",\n "checked",\n "indeterminate",\n "multiple",\n "show_text",\n "selected",\n "closable",\n ];\n}\n\nfunction _xml_number_attrs ( String tag ) {\n return [\n "width",\n "height",\n "minwidth",\n "minheight",\n "maxwidth",\n "maxheight",\n "gap",\n "padding",\n "first_day_of_week",\n "selected_index",\n "value",\n "min",\n "max",\n "step",\n ];\n}\n\nfunction _xml_bool ( String tag, String attr, String raw ) {\n switch ( lc(raw): eq ) {\n case "true", "1", "yes", "on": return true;\n case "false", "0", "no", "off": return false;\n }\n _xml_error(\n "GUI_XML_ATTR_TYPE",\n `${tag}.${attr} expects a boolean XML attribute`,\n );\n}\n\nfunction _xml_number ( String tag, String attr, String raw ) {\n if ( not ( raw ~ /^-?[0-9]+(\\.[0-9]+)?$/ ) ) {\n _xml_error(\n "GUI_XML_ATTR_TYPE",\n `${tag}.${attr} expects a numeric XML attribute`,\n );\n }\n return 0 + raw;\n}\n\nfunction _xml_int_list ( String tag, String attr, String raw ) {\n let out := [];\n return out if raw eq "";\n for ( let part in split( raw, "," ) ) {\n let item := trim(part);\n if ( not ( item ~ /^-?[0-9]+$/ ) ) {\n _xml_error(\n "GUI_XML_ATTR_TYPE",\n `${tag}.${attr} expects comma-separated integers`,\n );\n }\n out.push( 0 + item );\n }\n return out;\n}\n\nfunction _xml_coerce_attr ( String tag, String attr, String raw ) {\n if ( attr eq "selected" and tag eq "Tabs" ) {\n return raw;\n }\n if ( attr eq "value" and tag \u2209 [ "Slider", "Progress" ] ) {\n return raw;\n }\n if ( attr eq "min" and tag eq "DatePicker" ) {\n return raw;\n }\n if ( attr eq "max" and tag eq "DatePicker" ) {\n return raw;\n }\n if ( attr eq "selected_path" ) {\n return _xml_int_list( tag, attr, raw );\n }\n if ( attr \u2208 _xml_bool_attrs(tag) ) {\n return _xml_bool( tag, attr, raw );\n }\n if ( attr \u2208 _xml_number_attrs(tag) ) {\n return _xml_number( tag, attr, raw );\n }\n return raw;\n}\n\nfunction _xml_attrs ( node ) {\n let tag := _xml_tag_name(node);\n let props := {};\n let meta := {};\n let meta_keys := [];\n let style := {};\n let style_keys := [];\n let allowed := _xml_allowed_attrs(tag);\n\n for ( let attr in node.attributeNames() ) {\n next if attr eq "xmlns";\n next if substr( attr, 0, 6 ) eq "xmlns:";\n let value := node.getAttribute(attr);\n if ( substr( attr, 0, 5 ) eq "meta." ) {\n let key := substr( attr, 5 );\n _xml_error( "GUI_XML_ATTR_UNKNOWN", "empty meta attribute name" )\n if key eq "";\n meta{(key)} := value;\n meta_keys.push(key);\n next;\n }\n if ( substr( attr, 0, 6 ) eq "style." ) {\n let key := substr( attr, 6 );\n _xml_error( "GUI_XML_ATTR_UNKNOWN", "empty style attribute name" )\n if key eq "";\n style{(key)} := value;\n style_keys.push(key);\n next;\n }\n if ( attr \u2209 allowed ) {\n _xml_error(\n "GUI_XML_ATTR_UNKNOWN",\n `${tag} does not accept XML attribute \'${attr}\'`,\n );\n }\n props{(attr)} := _xml_coerce_attr( tag, attr, value );\n }\n\n return {\n props: props,\n meta: meta,\n meta_keys: meta_keys,\n style: style,\n style_keys: style_keys,\n };\n}\n\nfunction _xml_apply_meta ( Widget widget, Dict meta, Array keys ) {\n for ( let key in keys ) {\n widget.meta( key, meta{(key)} );\n }\n if ( keys.length() > 0 ) {\n widget.meta( "__xml_meta_keys", keys );\n }\n return widget;\n}\n\nfunction _xml_apply_style ( Widget widget, Dict style, Array keys ) {\n for ( let key in keys ) {\n widget.style( key, style{(key)} );\n }\n if ( keys.length() > 0 ) {\n widget.meta( "__xml_style_keys", keys );\n }\n return widget;\n}\n\nfunction _xml_widget_from_node ( node ) {\n _xml_assert_namespace(node);\n\n let tag := _xml_tag_name(node);\n let child_widgets := [];\n for ( let child in node.children() ) {\n child_widgets.push( _xml_widget_from_node(child) );\n }\n let parsed := _xml_attrs(node);\n let p := parsed{props};\n let widget;\n\n switch ( tag: eq ) {\n case "Window":\n widget := new _WindowClass(\n id: p.get( "id", null ),\n title: p.get( "title", "" ),\n width: p.get( "width", 800 ),\n height: p.get( "height", 600 ),\n resizable: p.get( "resizable", true ),\n modal: p.get( "modal", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: child_widgets,\n );\n case "VBox":\n widget := new _VBoxClass(\n id: p.get( "id", null ),\n align: p.get( "align", "top" ),\n gap: p.get( "gap", 0 ),\n padding: p.get( "padding", 0 ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: child_widgets,\n );\n case "HBox":\n widget := new _HBoxClass(\n id: p.get( "id", null ),\n align: p.get( "align", "left" ),\n gap: p.get( "gap", 0 ),\n padding: p.get( "padding", 0 ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: child_widgets,\n );\n case "Frame":\n widget := new _FrameClass(\n id: p.get( "id", null ),\n label: p.get( "label", "" ),\n collapsible: p.get( "collapsible", false ),\n collapsed: p.get( "collapsed", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: child_widgets,\n );\n case "Label":\n widget := Label(\n id: p.get( "id", null ),\n text: p.get( "text", "" ),\n ("for"): p.get( "for", null ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Text":\n widget := Text(\n id: p.get( "id", null ),\n value: p.get( "value", "" ),\n multiline: p.get( "multiline", false ),\n readonly: p.get( "readonly", false ),\n wrap: p.get( "wrap", true ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "RichText":\n widget := RichText(\n id: p.get( "id", null ),\n value: p.get( "value", "" ),\n multiline: p.get( "multiline", true ),\n readonly: p.get( "readonly", true ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Image":\n widget := Image(\n id: p.get( "id", null ),\n src: p.get( "src", "" ),\n alt: p.get( "alt", "" ),\n fit: p.get( "fit", "none" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Input":\n widget := Input(\n id: p.get( "id", null ),\n value: p.get( "value", "" ),\n placeholder: p.get( "placeholder", "" ),\n multiline: p.get( "multiline", false ),\n readonly: p.get( "readonly", false ),\n password: p.get( "password", false ),\n required: p.get( "required", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "DatePicker":\n widget := DatePicker(\n id: p.get( "id", null ),\n value: p.get( "value", null ),\n min: p.get( "min", null ),\n max: p.get( "max", null ),\n first_day_of_week: p.get( "first_day_of_week", 0 ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Checkbox":\n widget := Checkbox(\n id: p.get( "id", null ),\n label: p.get( "label", "" ),\n checked: p.get( "checked", false ),\n indeterminate: p.get( "indeterminate", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Radio":\n widget := Radio(\n id: p.get( "id", null ),\n label: p.get( "label", "" ),\n value: p.get( "value", "" ),\n group: p.get( "group", null ),\n checked: p.get( "checked", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "RadioGroup":\n widget := new _RadioGroupClass(\n id: p.get( "id", null ),\n name: p.get( "name", "" ),\n value: p.get( "value", null ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: child_widgets,\n );\n case "Select":\n widget := Select(\n id: p.get( "id", null ),\n value: p.get( "value", null ),\n multiple: p.get( "multiple", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Menu":\n widget := new _MenuClass(\n id: p.get( "id", null ),\n text: p.get( "text", "" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: child_widgets,\n );\n case "MenuItem":\n widget := MenuItem(\n id: p.get( "id", null ),\n text: p.get( "text", "" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Button":\n widget := Button(\n id: p.get( "id", null ),\n text: p.get( "text", "" ),\n variant: p.get( "variant", "default" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Separator":\n widget := Separator(\n id: p.get( "id", null ),\n orientation: p.get( "orientation", "horizontal" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Slider":\n widget := Slider(\n id: p.get( "id", null ),\n value: p.get( "value", 0 ),\n min: p.get( "min", 0 ),\n max: p.get( "max", 100 ),\n step: p.get( "step", 1 ),\n orientation: p.get( "orientation", "horizontal" ),\n readonly: p.get( "readonly", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Progress":\n widget := Progress(\n id: p.get( "id", null ),\n value: p.get( "value", 0 ),\n min: p.get( "min", 0 ),\n max: p.get( "max", 100 ),\n indeterminate: p.get( "indeterminate", false ),\n show_text: p.get( "show_text", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "Tabs":\n widget := new _TabsClass(\n id: p.get( "id", null ),\n selected: p.get( "selected", null ),\n placement: p.get( "placement", "top" ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: child_widgets,\n );\n case "Tab":\n widget := new _TabClass(\n id: p.get( "id", null ),\n title: p.get( "title", "" ),\n value: p.get( "value", "" ),\n selected: p.get( "selected", false ),\n closable: p.get( "closable", false ),\n icon: p.get( "icon", null ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n children: child_widgets,\n );\n case "ListView":\n widget := ListView(\n id: p.get( "id", null ),\n selected_index: p.get( "selected_index", null ),\n multiple: p.get( "multiple", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n case "TreeView":\n widget := TreeView(\n id: p.get( "id", null ),\n selected_path: p.get( "selected_path", [] ),\n multiple: p.get( "multiple", false ),\n visible: p.get( "visible", true ),\n enabled: p.get( "enabled", true ),\n disabled: p.get( "disabled", false ),\n );\n default:\n _xml_error(\n "GUI_XML_STRUCTURE",\n `unsupported GUI XML element \'${tag}\'`,\n );\n }\n\n _xml_apply_meta( widget, parsed{meta}, parsed{meta_keys} );\n return _xml_apply_style( widget, parsed{style}, parsed{style_keys} );\n}\n\nfunction gui_from_xml ( String xml ) {\n return _xml_widget_from_node( XML.parse(xml).documentElement() );\n}\n\nfunction gui_from_xml_file ( path ) {\n die "XML.load is denied by runtime policy" if __system__{deny_fs};\n from std/io import Path;\n let file := path instanceof Path ? path : new Path(path);\n return _xml_widget_from_node( XML.load(file).documentElement() );\n}\n\nfunction _xml_tag_for_widget ( Widget widget ) {\n if ( widget instanceof _WindowClass ) { return "Window"; }\n if ( widget instanceof _VBoxClass ) { return "VBox"; }\n if ( widget instanceof _HBoxClass ) { return "HBox"; }\n if ( widget instanceof _FrameClass ) { return "Frame"; }\n if ( widget instanceof _LabelClass ) { return "Label"; }\n if ( widget instanceof _TextClass ) { return "Text"; }\n if ( widget instanceof _RichTextClass ) { return "RichText"; }\n if ( widget instanceof _ImageClass ) { return "Image"; }\n if ( widget instanceof _InputClass ) { return "Input"; }\n if ( widget instanceof _DatePickerClass ) { return "DatePicker"; }\n if ( widget instanceof _CheckboxClass ) { return "Checkbox"; }\n if ( widget instanceof _RadioClass ) { return "Radio"; }\n if ( widget instanceof _RadioGroupClass ) { return "RadioGroup"; }\n if ( widget instanceof _SelectClass ) { return "Select"; }\n if ( widget instanceof _MenuClass ) { return "Menu"; }\n if ( widget instanceof _MenuItemClass ) { return "MenuItem"; }\n if ( widget instanceof _ButtonClass ) { return "Button"; }\n if ( widget instanceof _SeparatorClass ) { return "Separator"; }\n if ( widget instanceof _SliderClass ) { return "Slider"; }\n if ( widget instanceof _ProgressClass ) { return "Progress"; }\n if ( widget instanceof _TabsClass ) { return "Tabs"; }\n if ( widget instanceof _TabClass ) { return "Tab"; }\n if ( widget instanceof _ListViewClass ) { return "ListView"; }\n if ( widget instanceof _TreeViewClass ) { return "TreeView"; }\n _xml_error( "GUI_XML_STRUCTURE", "unsupported widget for gui_to_xml" );\n}\n\nfunction _xml_attr ( String name, value ) {\n return "" if value \u2261 null;\n return ` ${name}="${escape_xml(value)}"`;\n}\n\nfunction _xml_attr_if ( String name, value, fallback ) {\n return "" if value \u2261 fallback;\n return _xml_attr( name, value );\n}\n\nfunction _xml_bool_attr_if ( String name, value, fallback ) {\n let normalized := value ? true : false;\n let normalized_fallback := fallback ? true : false;\n return "" if normalized \u2261 normalized_fallback;\n return _xml_attr( name, normalized ? "true" : "false" );\n}\n\nfunction _xml_attr_nonempty ( String name, value ) {\n return "" if value \u2261 null or value eq "";\n return _xml_attr( name, value );\n}\n\nfunction _xml_join_ints ( Array values ) {\n let out := [];\n for ( let value in values ) {\n out.push( "" _ value );\n }\n return join( ",", out );\n}\n\nfunction _xml_meta_attrs ( Widget widget ) {\n // These metadata slots are null or arrays; empty arrays are values.\n let keys := widget.meta( "__xml_meta_keys" ) ?: [];\n let out := "";\n for ( let key in keys ) {\n out _= _xml_attr( "meta." _ key, widget.meta(key) );\n }\n return out;\n}\n\nfunction _xml_style_attrs ( Widget widget ) {\n // These metadata slots are null or arrays; empty arrays are values.\n let keys := widget.meta( "__xml_style_keys" ) ?: [];\n let out := "";\n for ( let key in keys ) {\n out _= _xml_attr( "style." _ key, widget.style(key) );\n }\n return out;\n}\n\nfunction _xml_common_attrs ( Widget widget ) {\n let out := "";\n out _= _xml_attr( "id", widget.id() ) if widget.id() \u2262 null;\n out _= _xml_bool_attr_if( "visible", widget.visible(), true );\n out _= _xml_bool_attr_if( "disabled", not widget.enabled(), false );\n out _= _xml_meta_attrs(widget);\n out _= _xml_style_attrs(widget);\n return out;\n}\n\nfunction _xml_widget_attrs ( Widget widget ) {\n let out := _xml_common_attrs(widget);\n if ( widget instanceof _WindowClass ) {\n out _= _xml_attr_if( "title", widget.title(), "" );\n }\n else if ( widget instanceof _VBoxClass or widget instanceof _HBoxClass ) {\n let default_align := widget instanceof _VBoxClass ? "top" : "left";\n out _= _xml_attr_if( "align", widget.align(), default_align );\n out _= _xml_attr_if( "gap", widget.gap(), 0 );\n out _= _xml_attr_if( "padding", widget.padding(), 0 )\n unless widget.padding() instanceof Array;\n }\n else if ( widget instanceof _FrameClass ) {\n out _= _xml_attr_if( "label", widget.label(), "" );\n out _= _xml_bool_attr_if( "collapsible", widget.collapsible(), false );\n out _= _xml_bool_attr_if( "collapsed", widget.collapsed(), false );\n }\n else if ( widget instanceof _LabelClass ) {\n out _= _xml_attr_if( "text", widget.text(), "" );\n out _= _xml_attr_nonempty( "for", widget.for_id() );\n }\n else if ( widget instanceof _TextClass ) {\n out _= _xml_attr_if( "value", widget.value(), "" );\n out _= _xml_bool_attr_if( "multiline", widget.multiline(), false );\n out _= _xml_bool_attr_if( "readonly", widget.readonly(), false );\n out _= _xml_bool_attr_if( "wrap", widget.wrap(), true );\n }\n else if ( widget instanceof _RichTextClass ) {\n out _= _xml_attr_if( "value", widget.value(), "" );\n out _= _xml_bool_attr_if( "multiline", widget.multiline(), true );\n out _= _xml_bool_attr_if( "readonly", widget.readonly(), true );\n }\n else if ( widget instanceof _ImageClass ) {\n out _= _xml_attr_if( "src", widget.src(), "" );\n out _= _xml_attr_if( "alt", widget.alt(), "" );\n out _= _xml_attr_if( "fit", widget.fit(), "none" );\n }\n else if ( widget instanceof _InputClass ) {\n out _= _xml_attr_if( "value", widget.value(), "" );\n out _= _xml_attr_if( "placeholder", widget.placeholder(), "" );\n out _= _xml_bool_attr_if( "multiline", widget.multiline(), false );\n out _= _xml_bool_attr_if( "readonly", widget.readonly(), false );\n out _= _xml_bool_attr_if( "password", widget.password(), false );\n out _= _xml_bool_attr_if( "required", widget.required(), false );\n }\n else if ( widget instanceof _DatePickerClass ) {\n out _= _xml_attr( "value", widget.value() );\n out _= _xml_attr( "min", widget.min() ) if widget.min() \u2262 null;\n out _= _xml_attr( "max", widget.max() ) if widget.max() \u2262 null;\n out _= _xml_attr_if(\n "first_day_of_week",\n widget.first_day_of_week(),\n 0,\n );\n }\n else if ( widget instanceof _CheckboxClass ) {\n out _= _xml_attr_if( "label", widget.label(), "" );\n out _= _xml_bool_attr_if( "checked", widget.checked(), false );\n out _= _xml_bool_attr_if( "indeterminate", widget.indeterminate(), false );\n }\n else if ( widget instanceof _RadioClass ) {\n out _= _xml_attr_if( "label", widget.label(), "" );\n out _= _xml_attr_if( "value", widget.value(), "" );\n out _= _xml_attr_nonempty( "group", widget.group() );\n out _= _xml_bool_attr_if( "checked", widget.checked(), false );\n }\n else if ( widget instanceof _RadioGroupClass ) {\n out _= _xml_attr_if( "name", widget.name(), "" );\n out _= _xml_attr( "value", widget.value() ) if widget.value() \u2262 null;\n }\n else if ( widget instanceof _SelectClass ) {\n out _= _xml_attr( "value", widget.value() ) if widget.value() \u2262 null;\n out _= _xml_bool_attr_if( "multiple", widget.multiple(), false );\n }\n else if ( widget instanceof _MenuClass ) {\n out _= _xml_attr_if( "text", widget.text(), "" );\n }\n else if ( widget instanceof _MenuItemClass ) {\n out _= _xml_attr_if( "text", widget.text(), "" );\n }\n else if ( widget instanceof _ButtonClass ) {\n out _= _xml_attr_if( "text", widget.text(), "" );\n out _= _xml_attr_if( "variant", widget.variant(), "default" );\n }\n else if ( widget instanceof _SeparatorClass ) {\n out _= _xml_attr_if( "orientation", widget.orientation(), "horizontal" );\n }\n else if ( widget instanceof _SliderClass ) {\n out _= _xml_attr_if( "value", widget.value(), 0 );\n out _= _xml_attr_if( "min", widget.min(), 0 );\n out _= _xml_attr_if( "max", widget.max(), 100 );\n out _= _xml_attr_if( "step", widget.step(), 1 );\n out _= _xml_attr_if( "orientation", widget.orientation(), "horizontal" );\n out _= _xml_bool_attr_if( "readonly", widget.readonly(), false );\n }\n else if ( widget instanceof _ProgressClass ) {\n out _= _xml_attr_if( "value", widget.value(), 0 );\n out _= _xml_attr_if( "min", widget.min(), 0 );\n out _= _xml_attr_if( "max", widget.max(), 100 );\n out _= _xml_bool_attr_if( "indeterminate", widget.indeterminate(), false );\n out _= _xml_bool_attr_if( "show_text", widget.show_text(), false );\n }\n else if ( widget instanceof _TabsClass ) {\n out _= _xml_attr( "selected", widget.selected() )\n if widget.selected() \u2262 null;\n out _= _xml_attr_if( "placement", widget.placement(), "top" );\n }\n else if ( widget instanceof _TabClass ) {\n out _= _xml_attr_if( "title", widget.title(), "" );\n out _= _xml_attr_if( "value", widget.value(), "" );\n out _= _xml_attr( "icon", widget.icon() ) if widget.icon() \u2262 null;\n out _= _xml_bool_attr_if( "selected", widget.selected(), false );\n out _= _xml_bool_attr_if( "closable", widget.closable(), false );\n }\n else if ( widget instanceof _ListViewClass ) {\n out _= _xml_attr( "selected_index", widget.selected_index() )\n if widget.selected_index() \u2262 null;\n out _= _xml_bool_attr_if( "multiple", widget.multiple(), false );\n }\n else if ( widget instanceof _TreeViewClass ) {\n let selected_path := widget.selected_path();\n out _= _xml_attr( "selected_path", _xml_join_ints(selected_path) )\n if selected_path.length() > 0;\n out _= _xml_bool_attr_if( "multiple", widget.multiple(), false );\n }\n return out;\n}\n\nfunction _xml_serialize_widget ( Widget widget, Number depth ) {\n let indent := "";\n let i := 0;\n while ( i < depth ) {\n indent _= "\\t";\n i++;\n }\n\n let tag := _xml_tag_for_widget(widget);\n let attrs := _xml_widget_attrs(widget);\n attrs _= _xml_attr( "xmlns", GUI_XML_NS ) if depth = 0;\n let children := widget.children();\n if ( children.length() = 0 ) {\n return indent _ "<" _ tag _ attrs _ " />";\n }\n\n let parts := [];\n for ( let child in children ) {\n parts.push( _xml_serialize_widget( child, depth + 1 ) );\n }\n\n return indent _ "<" _ tag _ attrs _ ">\\n"\n _ join( "\\n", parts )\n _ "\\n" _ indent _ "</" _ tag _ ">";\n}\n\nfunction gui_to_xml ( Widget root ) {\n return _xml_serialize_widget( root, 0 ) _ "\\n";\n}\n';
|
|
41650
|
+
virtualFiles["/modules/std/gui/dialogue.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/gui/dialogue - Dialogue helpers.\n\n=head1 SYNOPSIS\n\n from std/gui/dialogue import *;\n\n alert("Saved");\n let ok := confirm("Continue?");\n let name := prompt("Name:", value: "Ada");\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by zuzu.pl, zuzu-rust, and zuzu-js on Electron\nand Browser. It is not supported by zuzu-js on Node.\n\n=head1 DESCRIPTION\n\nThis module provides convenience dialogue helpers layered on top of the\nregular C<std/gui> widgets.\n\n=head1 EXPORTS\n\n=head2 Functions\n\n=over\n\n=item C<< alert(String message, ... PairList p) >>, C<< alert_window(String message, ... PairList p) >>\n\nParameters: C<message> is display text and C<p> contains options.\nReturns: C<null> for C<alert> or C<Window> for C<alert_window>. Shows or\nbuilds an alert dialogue.\n\n=item C<< confirm(String message, ... PairList p) >>, C<< confirm_window(String message, ... PairList p) >>\n\nParameters: C<message> is display text and C<p> contains options.\nReturns: C<Boolean> for C<confirm> or C<Window> for\nC<confirm_window>. Shows or builds a confirmation dialogue.\n\n=item C<< prompt(String message, ... PairList p) >>, C<< prompt_window(String message, ... PairList p) >>\n\nParameters: C<message> is prompt text and C<p> contains options such as\nC<value>. Returns: C<String> or C<null> for C<prompt>, or C<Window> for\nC<prompt_window>. Shows or builds a text prompt.\n\n=item C<< file_open(... PairList p) >>, C<< file_save(... PairList p) >>\n\nParameters: C<p> contains dialogue options. Returns: path value or\nC<null>. Opens a file selection dialogue.\n\n=item C<< directory_open(... PairList p) >>, C<< directory_save(... PairList p) >>\n\nParameters: C<p> contains dialogue options. Returns: path value or\nC<null>. Opens a directory selection dialogue.\n\n=item C<< colour_picker(... PairList p) >>\n\nParameters: C<p> contains dialogue options. Returns: C<String> or\nC<null>. Opens a colour picker and returns a normalized colour string.\n\n=item C<< file_open_window(... PairList p) >>, C<< file_save_window(... PairList p) >>, C<< directory_open_window(... PairList p) >>, C<< directory_save_window(... PairList p) >>, C<< colour_picker_window(... PairList p) >>\n\nParameters: C<p> contains dialogue options. Returns: C<Window>. Builds\nthe corresponding GUI dialogue window.\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/gui/dialogue >> 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\nfrom std/gui try import\n EM,\n Window,\n VBox,\n HBox,\n Label,\n Text,\n Input,\n Button,\n Widget,\n native_file_open,\n native_file_save,\n native_directory_open,\n native_directory_save,\n native_colour_picker;\nfrom std/gui/objects try import\n meta as _objects_meta,\n native_alert,\n native_confirm,\n native_prompt;\nfrom std/colour import parse_colour;\nfrom std/string import split;\nfrom std/tui import\n colour_text,\n directory_completions,\n filename_completions,\n readline;\n\nfunction _tui_string ( value ) {\n return value \u2261 null ? "" : "" _ value;\n}\n\nfunction _gui_backend () {\n if ( _objects_meta \u2262 null ) {\n return _objects_meta{backend};\n }\n return "";\n}\n\nfunction _path_dialogue_unsupported () {\n if ( __system__{deny_fs} ) {\n die "GUI_DIALOGUE_FS_DENIED: file and directory dialogues require filesystem capability";\n }\n if ( _gui_backend() eq "browser-dom" ) {\n die "GUI_DIALOGUE_UNSUPPORTED: file and directory dialogues are unsupported in JS/Browser";\n }\n}\n\nfunction _tui_prompt ( String message, PairList p, String default_value ) {\n return readline(\n colour_text( message _ " ", "cyan" ),\n default_value,\n null,\n );\n}\n\nfunction _tui_path_dialog (\n String label,\n String default_value,\n completion,\n PairList p\n) {\n let answer := readline(\n colour_text( p.get( "label", label ) _ " ", "cyan" ),\n _tui_string( p.get( "value", default_value ) ),\n completion,\n );\n if ( p.get( "multiple", false ) ) {\n return split( answer, p.get( "separator", "\\n" ) );\n }\n return answer;\n}\n\nfunction _parse_colour_or_null ( value ) {\n return try {\n parse_colour( _tui_string(value) );\n }\n catch {\n null;\n };\n}\n\nfunction _colour_initial_value ( PairList p ) {\n let raw := _tui_string( p.get( "value", "#000000" ) );\n let parsed := _parse_colour_or_null(raw);\n return parsed \u2262 null ? parsed : raw;\n}\n\nfunction _colour_default_value ( PairList p ) {\n // The parser returns null for invalid input; valid colours are values.\n return _parse_colour_or_null( p.get( "value", "#000000" ) ) ?: "#000000";\n}\n\nfunction _tui_colour_dialog ( PairList p ) {\n let default_value := _colour_default_value(p);\n while ( true ) {\n let answer := readline(\n colour_text( p.get( "label", "Colour:" ) _ " ", "cyan" ),\n default_value,\n null,\n );\n let parsed := _parse_colour_or_null(answer);\n if ( parsed \u2262 null ) {\n return parsed;\n }\n say( colour_text( "Invalid colour; try #336699 or red.", "yellow" ) );\n }\n}\n\nfunction _dialogue_window (\n String kind,\n String title,\n Widget body,\n Array buttons,\n PairList p\n) {\n let button_row := HBox(\n id: "buttons",\n align: "right",\n gap: 6,\n height: p.get( "button_row_height", 2.125 \xD7 EM ),\n maxheight: p.get( "button_row_height", 2.125 \xD7 EM ),\n );\n for ( let button in buttons ) {\n button_row.add_child(button);\n }\n let w := Window(\n title: title,\n width: p.get( "width", 360 ),\n height: p.get( "height", 180 ),\n resizable: p.get( "resizable", false ),\n modal: true,\n VBox(\n id: p.get( "id", null ),\n gap: p.get( "gap", 8 ),\n padding: p.get( "padding", 12 ),\n body,\n button_row,\n ),\n );\n w.meta( "dialogue.kind", kind );\n return w;\n}\n\nfunction _message_body ( String message, PairList p ) {\n return Text(\n id: p.get( "message_id", "message" ),\n value: message,\n readonly: true,\n wrap: true,\n );\n}\n\nfunction _primary_button ( PairList p, String fallback ) {\n return Button(\n id: p.get( "ok_id", "ok" ),\n text: p.get( "ok_text", fallback ),\n variant: "primary",\n width: p.get( "button_width", 5.5 \xD7 EM ),\n height: p.get( "button_height", 1.75 \xD7 EM ),\n maxheight: p.get( "button_height", 1.75 \xD7 EM ),\n );\n}\n\nfunction _cancel_button ( PairList p ) {\n return Button(\n id: p.get( "cancel_id", "cancel" ),\n text: p.get( "cancel_text", "Cancel" ),\n width: p.get( "button_width", 5.5 \xD7 EM ),\n height: p.get( "button_height", 1.75 \xD7 EM ),\n maxheight: p.get( "button_height", 1.75 \xD7 EM ),\n );\n}\n\nfunction _alert_window ( String message, PairList p ) {\n let ok := _primary_button( p, "OK" );\n let w := _dialogue_window(\n "alert",\n p.get( "title", "Alert" ),\n _message_body( message, p ),\n [ ok ],\n p,\n );\n ok.click( function () {\n w.close(null);\n } );\n return w;\n}\n\nfunction alert_window ( String message, ... PairList p ) {\n return _alert_window( message, p );\n}\n\nfunction alert ( String message, ... PairList p ) {\n if ( p.has("auto_result") ) {\n return null;\n }\n if ( __system__{deny_gui} ) {\n say( colour_text( message, "yellow" ) );\n return null;\n }\n if ( native_alert \u2262 null ) {\n let native_result := native_alert( message, p );\n if ( native_result ) {\n return null;\n }\n }\n return _alert_window( message, p ).call();\n}\n\nfunction _confirm_window ( String message, PairList p ) {\n let ok := _primary_button( p, p.get( "ok_text", "OK" ) );\n let cancel := _cancel_button(p);\n let w := _dialogue_window(\n "confirm",\n p.get( "title", "Confirm" ),\n _message_body( message, p ),\n [ cancel, ok ],\n p,\n );\n ok.click( function () {\n w.close(true);\n } );\n cancel.click( function () {\n w.close(false);\n } );\n return w;\n}\n\nfunction confirm_window ( String message, ... PairList p ) {\n return _confirm_window( message, p );\n}\n\nfunction confirm ( String message, ... PairList p ) {\n if ( p.has("auto_result") ) {\n return p.get("auto_result") ? true : false;\n }\n if ( __system__{deny_gui} ) {\n let yes_default := p.get( "value", p.get( "default", false ) );\n let suffix := yes_default ? " [Y/n] " : " [y/N] ";\n let answer := readline(\n colour_text( message _ suffix, "cyan" ),\n yes_default ? "y" : "n",\n null,\n );\n return ( answer ~ /^(?:y|yes|1|true)$/i ) ? true : false;\n }\n if ( native_confirm \u2262 null ) {\n let native_result := native_confirm( message, p );\n if ( native_result \u2262 null ) {\n return native_result ? true : false;\n }\n }\n return _confirm_window( message, p ).call() ? true : false;\n}\n\nfunction _prompt_window ( String message, PairList p ) {\n let input := Input(\n id: p.get( "input_id", "value" ),\n value: p.get( "value", "" ),\n placeholder: p.get( "placeholder", "" ),\n );\n let ok := _primary_button( p, p.get( "ok_text", "OK" ) );\n let cancel := _cancel_button(p);\n let w := _dialogue_window(\n "prompt",\n p.get( "title", "Prompt" ),\n VBox(\n gap: 6,\n _message_body( message, p ),\n input,\n ),\n [ cancel, ok ],\n p,\n );\n ok.click( function () {\n w.close( input.value() );\n } );\n cancel.click( function () {\n w.close(null);\n } );\n return w;\n}\n\nfunction prompt_window ( String message, ... PairList p ) {\n return _prompt_window( message, p );\n}\n\nfunction prompt ( String message, ... PairList p ) {\n if ( p.has("auto_result") ) {\n return p.get("auto_result");\n }\n if ( __system__{deny_gui} ) {\n return _tui_prompt(\n message,\n p,\n _tui_string( p.get( "value", "" ) ),\n );\n }\n if ( native_prompt \u2262 null ) {\n let native_result := native_prompt( message, p );\n if ( _gui_backend() eq "browser-dom" ) {\n return native_result;\n }\n if ( native_result \u2262 null ) {\n return native_result;\n }\n }\n return _prompt_window( message, p ).call();\n}\n\nfunction _path_dialog_window (\n String kind,\n String title,\n String label,\n String default_value,\n PairList p\n) {\n let input := Input(\n id: p.get( "input_id", "path" ),\n value: p.get( "value", default_value ),\n placeholder: p.get( "placeholder", "" ),\n multiline: p.get( "multiple", false ),\n );\n let ok := _primary_button( p, p.get( "ok_text", "OK" ) );\n let cancel := _cancel_button(p);\n let w := _dialogue_window(\n kind,\n p.get( "title", title ),\n VBox(\n gap: 6,\n Label( text: p.get( "label", label ), ("for"): input.id() ),\n input,\n ),\n [ cancel, ok ],\n p,\n );\n ok.click( function () {\n if ( p.get( "multiple", false ) ) {\n w.close( split( input.value(), p.get( "separator", "\\n" ) ) );\n }\n else {\n w.close( input.value() );\n }\n } );\n cancel.click( function () {\n w.close(null);\n } );\n return w;\n}\n\nfunction file_open_window ( ... PairList p ) {\n return _path_dialog_window( "file_open", "Open File", "File:", "", p );\n}\n\nfunction file_save_window ( ... PairList p ) {\n return _path_dialog_window( "file_save", "Save File", "File:", "", p );\n}\n\nfunction directory_open_window ( ... PairList p ) {\n return _path_dialog_window(\n "directory_open",\n "Open Directory",\n "Directory:",\n "",\n p,\n );\n}\n\nfunction directory_save_window ( ... PairList p ) {\n return _path_dialog_window(\n "directory_save",\n "Save Directory",\n "Directory:",\n "",\n p,\n );\n}\n\nfunction _colour_picker_window ( PairList p ) {\n let input := Input(\n id: p.get( "input_id", "path" ),\n value: _colour_initial_value(p),\n placeholder: p.get( "placeholder", "#336699" ),\n );\n let ok := _primary_button( p, p.get( "ok_text", "OK" ) );\n let cancel := _cancel_button(p);\n let default_value := _colour_default_value(p);\n let sync_ok := function () {\n ok.set_enabled( _parse_colour_or_null( input.value() ) \u2262 null );\n };\n let w := _dialogue_window(\n "colour_picker",\n p.get( "title", "Choose Colour" ),\n VBox(\n gap: 6,\n Label(\n text: p.get( "label", "Colour:" ),\n ("for"): input.id(),\n ),\n input,\n ),\n [ cancel, ok ],\n p,\n );\n sync_ok();\n input.on( "change", sync_ok );\n ok.click( function () {\n let parsed := _parse_colour_or_null( input.value() );\n if ( parsed \u2262 null ) {\n w.close(parsed);\n }\n } );\n cancel.click( function () {\n w.close(default_value);\n } );\n return w;\n}\n\nfunction colour_picker_window ( ... PairList p ) {\n return _colour_picker_window(p);\n}\n\nfunction file_open ( ... PairList p ) {\n _path_dialogue_unsupported();\n if ( p.has("auto_result") ) {\n return p.get("auto_result");\n }\n if ( __system__{deny_gui} ) {\n return _tui_path_dialog( "File:", "", filename_completions, p );\n }\n return native_file_open(p);\n}\n\nfunction file_save ( ... PairList p ) {\n _path_dialogue_unsupported();\n if ( p.has("auto_result") ) {\n return p.get("auto_result");\n }\n if ( __system__{deny_gui} ) {\n return _tui_path_dialog( "File:", "", filename_completions, p );\n }\n return native_file_save(p);\n}\n\nfunction directory_open ( ... PairList p ) {\n _path_dialogue_unsupported();\n if ( p.has("auto_result") ) {\n return p.get("auto_result");\n }\n if ( __system__{deny_gui} ) {\n return _tui_path_dialog(\n "Directory:",\n "",\n directory_completions,\n p,\n );\n }\n return native_directory_open(p);\n}\n\nfunction directory_save ( ... PairList p ) {\n _path_dialogue_unsupported();\n if ( p.has("auto_result") ) {\n return p.get("auto_result");\n }\n if ( __system__{deny_gui} ) {\n return _tui_path_dialog(\n "Directory:",\n "",\n directory_completions,\n p,\n );\n }\n if ( native_directory_save \u2262 null ) {\n let native_result := native_directory_save(p);\n if ( native_result \u2262 null ) {\n return native_result;\n }\n if ( _gui_backend() eq "electron-dom" ) {\n return null;\n }\n }\n return _path_dialog_window(\n "directory_save",\n "Save Directory",\n "Directory:",\n "",\n p,\n ).call();\n}\n\nfunction colour_picker ( ... PairList p ) {\n if ( p.has("auto_result") ) {\n return parse_colour( _tui_string( p.get("auto_result") ) );\n }\n if ( __system__{deny_gui} ) {\n return _tui_colour_dialog(p);\n }\n if ( native_colour_picker \u2262 null ) {\n let native_result := native_colour_picker(p);\n if ( native_result \u2262 null ) {\n return parse_colour(native_result);\n }\n if ( _gui_backend() eq "browser-dom" ) {\n return null;\n }\n }\n if ( _gui_backend() eq "electron-dom" ) {\n return _colour_picker_window(p).call();\n }\n return parse_colour( _colour_picker_window(p).call() );\n}\n';
|
|
41584
41651
|
virtualFiles["/modules/std/uuid.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/uuid - Pure ZuzuScript UUID v1 generator.\n\n=head1 SYNOPSIS\n\n from std/uuid import create_uuid, create_uuid_binary;\n\n let text := create_uuid();\n let raw := create_uuid_binary();\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nThis module implements UUID version 1 generation using only\nZuzuScript code.\n\n=head1 EXPORTS\n\n=head2 Functions\n\n=over\n\n=item * C<create_uuid_binary()>\n\nParameters: none. Returns: C<BinaryString>. Returns a single UUID as 16\nraw bytes.\n\n=item * C<create_uuid()>\n\nParameters: none. Returns: C<String>. Returns a single UUID as\nlowercase hexadecimal text with hyphens in the usual C<8-4-4-4-12>\nlayout.\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/uuid >> 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\nfrom std/math import Math;\nfrom std/string import substr;\nfrom std/string/base64 import decode;\nfrom std/time import Time;\n\nlet _B64_ALPHABET := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";\nlet _HEX_ALPHABET := "0123456789abcdef";\n\nfunction _div_floor ( Number n, Number d ) {\n return floor( n / d );\n}\n\nfunction _mod ( Number n, Number d ) {\n return n - _div_floor( n, d ) * d;\n}\n\nfunction _rand_int ( Number max ) {\n return floor( Math.rand(max) );\n}\n\nfunction _bytes_to_binary ( Array bytes ) {\n let out := "";\n let i := 0;\n let n := bytes.length();\n\n while ( i < n ) {\n let b0 := bytes[i];\n let b1 := null;\n let b2 := null;\n if ( i + 1 < n ) {\n b1 := bytes[i + 1];\n }\n if ( i + 2 < n ) {\n b2 := bytes[i + 2];\n }\n\n let c0 := _div_floor( b0, 4 );\n let c1 := _mod( b0, 4 ) * 16;\n let c2 := 64;\n let c3 := 64;\n\n if ( b1 \u2262 null ) {\n c1 += _div_floor( b1, 16 );\n c2 := _mod( b1, 16 ) * 4;\n if ( b2 \u2262 null ) {\n c2 += _div_floor( b2, 64 );\n c3 := _mod( b2, 64 );\n }\n }\n\n out _= substr( _B64_ALPHABET, c0, 1 );\n out _= substr( _B64_ALPHABET, c1, 1 );\n if ( c2 \u2261 64 ) {\n out _= "=";\n }\n else {\n out _= substr( _B64_ALPHABET, c2, 1 );\n }\n if ( c3 \u2261 64 ) {\n out _= "=";\n }\n else {\n out _= substr( _B64_ALPHABET, c3, 1 );\n }\n\n i += 3;\n }\n\n return decode(out);\n}\n\nfunction _timestamp_words () {\n // Seconds between 1582-10-15 and 1970-01-01.\n let epoch_offset := 12219292800;\n let seconds := floor( new Time().epoch() ) + epoch_offset;\n let ticks := _rand_int(10000000);\n\n let words := [\n _mod( seconds, 65536 ),\n _mod( _div_floor( seconds, 65536 ), 65536 ),\n _mod( _div_floor( seconds, 4294967296 ), 65536 ),\n 0,\n 0,\n ];\n\n let scale := 10000000;\n let i := 0;\n let carry := 0;\n while ( i < words.length() ) {\n let product := words[i] * scale + carry;\n words[i] := _mod( product, 65536 );\n carry := _div_floor( product, 65536 );\n i++;\n }\n\n let add_i := 0;\n let add_carry := ticks;\n while ( add_carry > 0 and add_i < words.length() ) {\n let sum := words[add_i] + _mod( add_carry, 65536 );\n words[add_i] := _mod( sum, 65536 );\n add_carry := _div_floor( add_carry, 65536 ) + _div_floor( sum, 65536 );\n add_i++;\n }\n\n return words;\n}\n\nfunction _create_uuid_bytes () {\n let words := _timestamp_words();\n let time_low := words[0] + words[1] * 65536;\n let time_mid := words[2];\n let time_hi_and_version := _mod( words[3], 4096 ) + 4096;\n\n let clock_seq := _rand_int(16384);\n let clock_seq_hi_and_reserved := _mod( _div_floor( clock_seq, 256 ), 64 ) + 128;\n let clock_seq_low := _mod( clock_seq, 256 );\n\n let node := [];\n let j := 0;\n while ( j < 6 ) {\n node.push( _rand_int(256) );\n j++;\n }\n // Multicast bit set means this is not an IEEE MAC address.\n node[0] := node[0] | 1;\n\n return [\n _mod( _div_floor( time_low, 16777216 ), 256 ),\n _mod( _div_floor( time_low, 65536 ), 256 ),\n _mod( _div_floor( time_low, 256 ), 256 ),\n _mod( time_low, 256 ),\n _mod( _div_floor( time_mid, 256 ), 256 ),\n _mod( time_mid, 256 ),\n _mod( _div_floor( time_hi_and_version, 256 ), 256 ),\n _mod( time_hi_and_version, 256 ),\n clock_seq_hi_and_reserved,\n clock_seq_low,\n node[0],\n node[1],\n node[2],\n node[3],\n node[4],\n node[5],\n ];\n}\n\nfunction _byte_to_hex ( Number b ) {\n let hi := _div_floor( b, 16 );\n let lo := _mod( b, 16 );\n return substr( _HEX_ALPHABET, hi, 1 ) _ substr( _HEX_ALPHABET, lo, 1 );\n}\n\nfunction _bytes_to_uuid_text ( Array bytes ) {\n let out := "";\n let i := 0;\n while ( i < 16 ) {\n out _= _byte_to_hex( bytes[i] );\n if ( i \u2261 3 or i \u2261 5 or i \u2261 7 or i \u2261 9 ) {\n out _= "-";\n }\n i++;\n }\n return out;\n}\n\nfunction create_uuid_binary () {\n return _bytes_to_binary( _create_uuid_bytes() );\n}\n\nfunction create_uuid () {\n return _bytes_to_uuid_text( _create_uuid_bytes() );\n}\n';
|
|
41585
41652
|
virtualFiles["/modules/std/dump.zzm"] = `=encoding utf8
|
|
41586
41653
|
|
|
@@ -41903,7 +41970,7 @@ class Dumper {
|
|
|
41903
41970
|
virtualFiles["/modules/std/data/cbor.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/data/cbor - CBOR encoding and decoding for ZuzuScript.\n\n=head1 SYNOPSIS\n\n from std/data/cbor import CBOR, TaggedValue;\n\n let codec := new CBOR();\n let bytes := codec.encode({ answer: 42 });\n let value := codec.decode(bytes);\n\n // Decode CBOR maps to Zuzu PairLists instead of Dicts.\n // This preserves key order and allows duplicate keys.\n codec := new CBOR( pairlists: true );\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by zuzu.pl, zuzu-rust, and zuzu-js on Node and\nElectron. It is partially supported by zuzu-js in the browser: in-memory\nCBOR encode/decode coverage passes, but file-backed load/dump coverage is\nunsupported because browser filesystem capability is unavailable.\n\n=head1 DESCRIPTION\n\nPure-Zuzu implementation of CBOR (RFC 8949).\n\n=head1 EXPORTS\n\n=head2 Classes\n\n=over\n\n=item C<< TaggedValue({ tag: Number, value: value }) >>\n\nConstructs a tagged CBOR value. Returns: C<TaggedValue>. Stores the CBOR\ntag number and associated value.\n\n=item C<< CBOR({ pairlists?: Boolean }) >>\n\nConstructs a CBOR codec. Returns: C<CBOR>. The C<pairlists> option makes\ndecoded maps return as C<PairList> values instead of C<Dict> values.\n\n=over\n\n=item C<< codec.encode(value) >>\n\nParameters: C<value> is any CBOR-encodable ZuzuScript value. Returns:\nC<BinaryString>. Encodes C<value> as CBOR bytes.\n\n=item C<< codec.encode_binarystring(value) >>\n\nAlias for C<codec.encode(value)>.\n\n=item C<< codec.decode(BinaryString raw) >>\n\nParameters: C<raw> is CBOR data. Returns: value. Decodes CBOR bytes into\nthe equivalent ZuzuScript value.\n\n=item C<< codec.decode_binarystring(BinaryString raw) >>\n\nAlias for C<codec.decode(raw)>.\n\n=item C<< codec.load(Path path) >>\n\nParameters: C<path> is a C<std/io> C<Path>. Returns: value. Reads CBOR\nbytes from C<path> and decodes them.\n\n=item C<< codec.dump(Path path, value) >>\n\nParameters: C<path> is a C<std/io> C<Path> and C<value> is any\nCBOR-encodable value. Returns: C<null>. Encodes C<value> and writes CBOR\nbytes to C<path>.\n\n=back\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/data/cbor >> 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\nfrom std/string import substr, index;\nfrom std/string/base64 import encode, decode;\nfrom std/time import Time;\n\n\nlet _B64_ALPHABET := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";\n\n// Set to "PairList" to return CBOR objects as Zuzu PairLists.\nlet _DECODE_MAP_AS := "Dict";\n\nclass TaggedValue {\n let Number tag;\n let value;\n}\n\nfunction _div_floor ( Number n, Number d ) {\n return floor( n / d ); // fix syntax highlighting\n}\n\nfunction _mod ( Number n, Number d ) {\n return n - _div_floor( n, d ) * d;\n}\n\nfunction _bytes_to_binary ( Array bytes ) {\n let out := "";\n let i := 0;\n let n := bytes.length();\n\n while ( i < n ) {\n let b0 := bytes[i];\n let b1 := null;\n let b2 := null;\n if ( i + 1 < n ) {\n b1 := bytes[i + 1];\n }\n if ( i + 2 < n ) {\n b2 := bytes[i + 2];\n }\n\n let c0 := _div_floor( b0, 4 );\n let c1 := _mod( b0, 4 ) * 16;\n let c2 := 64;\n let c3 := 64;\n\n if ( b1 \u2262 null ) {\n c1 += _div_floor( b1, 16 );\n c2 := _mod( b1, 16 ) * 4;\n if ( b2 \u2262 null ) {\n c2 += _div_floor( b2, 64 );\n c3 := _mod( b2, 64 );\n }\n }\n\n out _= substr( _B64_ALPHABET, c0, 1 );\n out _= substr( _B64_ALPHABET, c1, 1 );\n if ( c2 \u2261 64 ) {\n out _= "=";\n }\n else {\n out _= substr( _B64_ALPHABET, c2, 1 );\n }\n if ( c3 \u2261 64 ) {\n out _= "=";\n }\n else {\n out _= substr( _B64_ALPHABET, c3, 1 );\n }\n i += 3;\n }\n\n return decode(out);\n}\n\nfunction _binary_to_bytes ( BinaryString raw ) {\n let b64 := encode(raw);\n let out := [];\n let i := 0;\n let n := length b64;\n\n while ( i < n ) {\n let c0 := index( _B64_ALPHABET, substr( b64, i, 1 ) );\n let c1 := index( _B64_ALPHABET, substr( b64, i + 1, 1 ) );\n let ch2 := substr( b64, i + 2, 1 );\n let ch3 := substr( b64, i + 3, 1 );\n let c2 := -1;\n let c3 := -1;\n if ( ch2 \u2262 "=" ) {\n c2 := index( _B64_ALPHABET, ch2 );\n }\n if ( ch3 \u2262 "=" ) {\n c3 := index( _B64_ALPHABET, ch3 );\n }\n\n out.push( c0 * 4 + _div_floor( c1, 16 ) );\n\n if ( c2 >= 0 ) {\n out.push( _mod( c1, 16 ) * 16 + _div_floor( c2, 4 ) );\n }\n if ( c3 >= 0 ) {\n out.push( _mod( c2, 4 ) * 64 + c3 );\n }\n\n i += 4;\n }\n\n return out;\n}\n\nfunction _is_int ( value ) {\n if ( not( value instanceof Number ) ) {\n return false;\n }\n\n return _mod( value, 1 ) \u2261 0;\n}\n\nfunction _emit_uint ( Array out, Number major, Number n ) {\n if ( n < 24 ) {\n out.push( major * 32 + n );\n return;\n }\n if ( n < 256 ) {\n out.push( major * 32 + 24 );\n out.push(n);\n return;\n }\n if ( n < 65536 ) {\n out.push( major * 32 + 25 );\n out.push( _mod( _div_floor( n, 256 ), 256 ) );\n out.push( _mod( n, 256 ) );\n return;\n }\n if ( n < 4294967296 ) {\n out.push( major * 32 + 26 );\n out.push( _mod( _div_floor( n, 16777216 ), 256 ) );\n out.push( _mod( _div_floor( n, 65536 ), 256 ) );\n out.push( _mod( _div_floor( n, 256 ), 256 ) );\n out.push( _mod( n, 256 ) );\n return;\n }\n\n let hi := _div_floor( n, 4294967296 );\n let lo := _mod( n, 4294967296 );\n out.push( major * 32 + 27 );\n out.push( _mod( _div_floor( hi, 16777216 ), 256 ) );\n out.push( _mod( _div_floor( hi, 65536 ), 256 ) );\n out.push( _mod( _div_floor( hi, 256 ), 256 ) );\n out.push( _mod( hi, 256 ) );\n out.push( _mod( _div_floor( lo, 16777216 ), 256 ) );\n out.push( _mod( _div_floor( lo, 65536 ), 256 ) );\n out.push( _mod( _div_floor( lo, 256 ), 256 ) );\n out.push( _mod( lo, 256 ) );\n}\n\nfunction _encode_value ( Array out, value ) {\n if ( value \u2261 null ) {\n out.push(246);\n return;\n }\n if ( value \u2261 true ) {\n out.push(245);\n return;\n }\n if ( value \u2261 false ) {\n out.push(244);\n return;\n }\n\n if ( value instanceof Number ) {\n die "CBOR.encode currently supports only integer Number values" if not _is_int(value);\n if ( value >= 0 ) {\n _emit_uint( out, 0, value );\n }\n else {\n _emit_uint( out, 1, -1 - value );\n }\n return;\n }\n\n if ( value instanceof BinaryString ) {\n let b := _binary_to_bytes(value);\n _emit_uint( out, 2, b.length() );\n let i := 0;\n while ( i < b.length() ) {\n out.push( b[i] );\n i++;\n }\n return;\n }\n\n if ( value instanceof String ) {\n let b := _binary_to_bytes( to_binary(value) );\n _emit_uint( out, 3, b.length() );\n let i := 0;\n while ( i < b.length() ) {\n out.push( b[i] );\n i++;\n }\n return;\n }\n\n if ( value instanceof Array ) {\n _emit_uint( out, 4, value.length() );\n let i := 0;\n while ( i < value.length() ) {\n _encode_value( out, value[i] );\n i++;\n }\n return;\n }\n\n if ( value instanceof Set ) {\n _emit_uint( out, 6, 258 );\n _encode_value( out, value.sortstr() );\n return;\n }\n\n if ( value instanceof Bag ) {\n _encode_value( out, value.sortstr() );\n return;\n }\n\n if ( value instanceof PairList ) {\n let pairs := value.to_Array();\n _emit_uint( out, 5, pairs.length() );\n let i := 0;\n while ( i < pairs.length() ) {\n let pair := pairs[i]{pair};\n _encode_value( out, pair[0] );\n _encode_value( out, pair[1] );\n i++;\n }\n return;\n }\n\n if ( value instanceof Dict ) {\n let keys := value.sorted_keys();\n _emit_uint( out, 5, keys.length() );\n let i := 0;\n while ( i < keys.length() ) {\n let k := keys[i];\n _encode_value( out, k );\n _encode_value( out, value.get(k) );\n i++;\n }\n return;\n }\n\n if ( value instanceof TaggedValue ) {\n _emit_uint( out, 6, value { tag } );\n _encode_value( out, value { value } );\n return;\n }\n if ( value instanceof Time ) {\n _emit_uint( out, 6, 0 );\n _encode_value( out, value.epoch() );\n return;\n }\n\n die `CBOR cannot encode value of type ${typeof value}`;\n}\n\nfunction _read_uint ( Array bytes, Number ai, Number pos ) {\n if ( ai < 24 ) {\n return [ ai, pos ];\n }\n if ( ai \u2261 24 ) {\n return [ bytes[pos], pos + 1 ];\n }\n if ( ai \u2261 25 ) {\n return [ bytes[pos] * 256 + bytes[pos + 1], pos + 2 ];\n }\n if ( ai \u2261 26 ) {\n return [ bytes[pos] * 16777216 + bytes[pos + 1] * 65536 + bytes[pos + 2] * 256 + bytes[pos + 3], pos + 4 ];\n }\n if ( ai \u2261 27 ) {\n let hi := bytes[pos] * 16777216 + bytes[pos + 1] * 65536 + bytes[pos + 2] * 256 + bytes[pos + 3];\n let lo := bytes[pos + 4] * 16777216 + bytes[pos + 5] * 65536 + bytes[pos + 6] * 256 + bytes[pos + 7];\n return [ hi * 4294967296 + lo, pos + 8 ];\n }\n\n die `Unsupported CBOR additional info: ${ai}`;\n}\n\nfunction _decode_at ( Array bytes, Number pos ) {\n let head := bytes[pos];\n let major := _div_floor( head, 32 );\n let ai := _mod( head, 32 );\n let parsed := _read_uint( bytes, ai, pos + 1 );\n let arg := parsed[0];\n let p := parsed[1];\n\n if ( major \u2261 0 ) {\n return [ arg, p ];\n }\n if ( major \u2261 1 ) {\n return [ -1 - arg, p ];\n }\n if ( major \u2261 2 ) {\n let chunk := [];\n let i := 0;\n while ( i < arg ) {\n chunk.push( bytes[p + i] );\n i++;\n }\n return [ _bytes_to_binary(chunk), p + arg ];\n }\n if ( major \u2261 3 ) {\n let chunk := [];\n let i := 0;\n while ( i < arg ) {\n chunk.push( bytes[p + i] );\n i++;\n }\n return [ to_string( _bytes_to_binary(chunk) ), p + arg ];\n }\n if ( major \u2261 4 ) {\n let arr := [];\n let i := 0;\n let q := p;\n while ( i < arg ) {\n let item := _decode_at( bytes, q );\n arr.push( item[0] );\n q := item[1];\n i++;\n }\n return [ arr, q ];\n }\n if ( major \u2261 5 ) {\n let d := _DECODE_MAP_AS eq "PairList" ? new PairList() : new Dict();\n let i := 0;\n let q := p;\n while ( i < arg ) {\n let k := _decode_at( bytes, q );\n let v := _decode_at( bytes, k[1] );\n d.add( k[0], v[0] ) unless _DECODE_MAP_AS eq "Dict" and d.exists( k[0] );\n q := v[1];\n i++;\n }\n return [ d, q ];\n }\n if ( major \u2261 6 ) {\n let tagged := _decode_at( bytes, p );\n if ( arg \u2261 0 and tagged[0] instanceof Number ) {\n return [ new Time( tagged[0] ), tagged[1] ];\n }\n if ( arg \u2261 258 and tagged[0] instanceof Array ) {\n return [ tagged[0].to_Set(), tagged[1] ];\n }\n return [ new TaggedValue( tag: arg, value: tagged[0] ), tagged[1] ];\n }\n if ( major \u2261 7 ) {\n if ( ai \u2261 20 ) {\n return [ false, pos + 1 ];\n }\n if ( ai \u2261 21 ) {\n return [ true, pos + 1 ];\n }\n if ( ai \u2261 22 ) {\n return [ null, pos + 1 ];\n }\n die `Unsupported CBOR simple/float value (ai=${ai})`;\n }\n\n die `Unsupported CBOR major type: ${major}`;\n}\n\nclass CBOR {\n let pairlists := false;\n\n method encode ( value ) {\n let out := [];\n _encode_value( out, value );\n return _bytes_to_binary(out);\n }\n\n method encode_binarystring ( value ) {\n return self.encode(value);\n }\n\n method decode ( BinaryString raw ) {\n _DECODE_MAP_AS := pairlists ? "PairList" : "Dict";\n let bytes := _binary_to_bytes(raw);\n let parsed := _decode_at( bytes, 0 );\n die "Trailing bytes after CBOR value" if parsed[1] < bytes.length();\n return parsed[0];\n }\n\n method decode_binarystring ( BinaryString raw ) {\n return self.decode(raw);\n }\n\n method load ( path ) {\n from std/io import Path;\n die "CBOR.load is denied by runtime policy" if __system__{deny_fs};\n die "CBOR.load expects a std/io Path object" if not( path instanceof Path );\n return self.decode( path.slurp() );\n }\n\n method dump ( path, value ) {\n from std/io import Path;\n die "CBOR.dump is denied by runtime policy" if __system__{deny_fs};\n die "CBOR.dump expects a std/io Path object" if not( path instanceof Path );\n path.spew( self.encode(value) );\n return path;\n }\n}\n';
|
|
41904
41971
|
virtualFiles["/modules/std/data/kdl.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/data/kdl - KDL parsing and serialization for ZuzuScript.\n\n=head1 SYNOPSIS\n\n from std/data/kdl import KDL;\n\n let kdl := new KDL();\n let doc := kdl.decode("""\n package name="zuzu" { version "0.1.0" }\n """);\n let text := kdl.encode(doc);\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by zuzu.pl, zuzu-rust, and zuzu-js on Node and\nElectron. It is partially supported by zuzu-js in the browser: in-memory\nKDL parsing and serialization coverage passes, but fixture and load/dump\ncoverage is unsupported because browser filesystem capability is\nunavailable.\n\n=head1 DESCRIPTION\n\nThis module provides a pure-Zuzu implementation of the KDL document\nmodel and parser, with a user-facing API modelled on C<std/data/json>.\n\n=head1 EXPORTS\n\nThe parser returns explicit KDL model objects rather than generic Dict\nand Array structures.\n\n=head2 C<KDLDocument>\n\nC<KDLDocument> represents a complete parsed KDL document.\n\nConstructor fields:\n\n=over\n\n=item * C<nodes>\n\nAn Array of top-level C<KDLNode> objects. Defaults to an empty Array.\n\n=back\n\nMethods:\n\n=over\n\n=item C<nodes()>\n\nParameters: none. Returns: C<Array>. Returns the Array of top-level\nC<KDLNode> objects.\n\n=item C<to_Iterator()>\n\nParameters: none. Returns: C<Function>. Returns an iterator over the\ndocument\'s top-level nodes.\n\n=back\n\n=head2 C<KDLNode>\n\nC<KDLNode> represents one KDL node.\n\nConstructor fields:\n\n=over\n\n=item * C<name>\n\nThe node name as a String.\n\n=item * C<type_annotation>\n\nThe node type annotation as a String, or C<null> when unannotated.\n\n=item * C<args>\n\nAn Array of unnamed argument C<KDLValue> objects. Defaults to an empty\nArray.\n\n=item * C<props>\n\nA C<PairList> mapping property names to C<KDLValue> objects. Defaults\nto an empty C<PairList>, and preserves duplicate property names.\n\n=item * C<children>\n\nAn Array of child C<KDLNode> objects. Defaults to an empty Array.\n\n=back\n\nMethods:\n\n=over\n\n=item C<name()>\n\nParameters: none. Returns: C<String>. Returns the node name.\n\n=item C<type_annotation()>\n\nParameters: none. Returns: C<String> or C<null>. Returns the node type\nannotation.\n\n=item C<args()>\n\nParameters: none. Returns: C<Array>. Returns the unnamed argument\nC<KDLValue> objects.\n\n=item C<props()>\n\nParameters: none. Returns: C<PairList>. Returns the named property\nC<KDLValue> objects.\n\n=item C<children()>\n\nParameters: none. Returns: C<Array>. Returns the child C<KDLNode>\nobjects.\n\n=item C<to_Iterator()>\n\nParameters: none. Returns: C<Function>. Returns an iterator over the\nnode\'s arguments followed by its child nodes.\n\n=back\n\n=head2 C<KDLValue>\n\nC<KDLValue> represents an unnamed argument or named property value.\n\nConstructor fields:\n\n=over\n\n=item * C<type>\n\nThe KDL value type: C<null>, C<boolean>, C<number>, or C<string>.\nSome conversion helpers may create opaque C<KDLValue> objects with\nother type names; those are not KDL primitives.\n\n=item * C<kind>\n\nFor numbers, one of C<integer>, C<float>, or C<string>. C<string> is\nused for KDL number keywords such as C<#inf> that do not have a native\nJSON-like numeric value.\n\n=item * C<value>\n\nThe raw native value.\n\n=item * C<type_annotation>\n\nThe value type annotation as a String, or C<null> when unannotated.\n\n=item * C<canonical_number>\n\nThe canonical numeric spelling used by canonical serialization, or\nC<null>.\n\n=back\n\nMethods:\n\n=over\n\n=item C<type()>\n\nParameters: none. Returns: C<String>. Returns the value type.\n\n=item C<kind()>\n\nParameters: none. Returns: C<String> or C<null>. Returns the numeric\nkind for number values.\n\n=item C<value()>\n\nParameters: none. Returns: value. Returns the raw native value.\n\n=item C<type_annotation()>\n\nParameters: none. Returns: C<String> or C<null>. Returns the value type\nannotation.\n\n=item C<canonical_number()>\n\nParameters: none. Returns: C<String> or C<null>. Returns the canonical\nnumeric spelling.\n\n=item C<is_null()>, C<is_boolean()>, C<is_number()>, C<is_string()>\n\nParameters: none. Returns: C<Boolean>. Predicate methods for the four\nKDL primitive value types.\n\n=item C<is_true()>, C<is_false()>\n\nParameters: none. Returns: C<Boolean>. Predicate methods for boolean\nC<#true> and C<#false> values.\n\n=item C<to_Number()>\n\nParameters: none. Returns: C<Number>. Returns the native number, or\nthrows when the value is not a finite native number.\n\n=item C<to_String()>\n\nParameters: none. Returns: C<String>. Coerces the value to a string.\n\n=item C<to_Boolean()>\n\nParameters: none. Returns: C<Boolean>. Coerces the value to a boolean.\n\n=item C<native_value()>\n\nParameters: none. Returns: value. Returns the raw native value, with KDL\nC<#null> represented as C<null>.\n\n=back\n\n=head2 C<KDL>\n\nC<KDL> is the codec class for KDL text.\n\n=over\n\n=item C<< codec.decode(String text) >>\n\nParameters: C<text> is KDL text. Returns: C<KDLDocument>. Parses KDL\ntext.\n\n=item C<< codec.decode_binarystring(BinaryString bytes) >>\n\nParameters: C<bytes> is UTF-8 KDL bytes. Returns: C<KDLDocument>.\nParses KDL bytes.\n\n=item C<< codec.encode(value) >>\n\nParameters: C<value> is a C<KDLDocument>, C<KDLNode>, or compatible\nvalue. Returns: C<String>. Serializes KDL data to text.\n\n=item C<< codec.encode_binarystring(value) >>\n\nParameters: C<value> is a C<KDLDocument>, C<KDLNode>, or compatible\nvalue. Returns: C<BinaryString>. Serializes KDL data to UTF-8 bytes.\n\n=item C<< codec.load(path) >>\n\nParameters: C<path> is a C<std/io> C<Path>. Returns: C<KDLDocument>.\nReads and parses a KDL file.\n\n=item C<< codec.dump(path, value) >>\n\nParameters: C<path> is a C<std/io> C<Path> and C<value> is KDL data.\nReturns: C<null>. Serializes and writes a KDL file.\n\n=back\n\n=head1 ZPATH\n\nC<KDLDocument>, C<KDLNode>, and C<KDLValue> objects can be queried with\nC<std/path/z>. A KDL document\'s top-level nodes are exposed as children.\nA KDL node exposes its arguments first, then its child nodes, as ZPath\nchildren. KDL properties are exposed as ZPath attributes.\n\n from std/data/kdl import KDL;\n from std/path/z import ZPath;\n\n let doc := ( new KDL() ).decode( """\n (pkg)package "zuzu" (email)"dev@example.test" version="0.1.0" {\n foo "first"\n bar "middle"\n foo "second"\n foo "third"\n }\n """ );\n\n // The package node\'s first child is its first argument.\n say( ( new ZPath( path: "/package/#0" ) ).first(doc) ); // zuzu\n\n // Properties are attributes.\n say( ( new ZPath( path: "/package/@version" ) ).first(doc) ); // 0.1.0\n\n // Child nodes can be selected by name and zero-based occurrence.\n say( ( new ZPath( path: "/package/foo#2/#0" ) ).first(doc) ); // third\n\n // tag() exposes type annotations on nodes and values.\n say( ( new ZPath( path: "tag(/package)" ) ).first(doc) ); // pkg\n say( ( new ZPath( path: "tag(/package/#1)" ) ).first(doc) ); // email\n\n // local-name() exposes the node name, like it does for XML elements.\n say( ( new ZPath( path: "local-name(/package)" ) ).first(doc) );\n say( ( new ZPath( path: "/package/foo#2/local-name()" ) ).first(doc) );\n\n=head1 COMPATIBILITY\n\nThe parser is recursive descent and covers the usual KDL 2.0 document\nmodel: nodes, arguments, properties, annotations, comments, slashdash,\nstrings, booleans, nulls, numbers, and children.\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/data/kdl >> 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\nfrom std/string import substr, index, chr, ord, sprint;\n\n\nfunction _kdl_starts_with ( String text, Number pos, String prefix ) {\n return substr( text, pos, length prefix ) \u2261 prefix;\n}\n\nfunction _kdl_is_newline ( String ch ) {\n return ch \u2261 "\\n" or ch \u2261 "\\r" or ch \u2261 chr(11)\n or ch \u2261 chr(133) or ch \u2261 chr(8232) or ch \u2261 chr(8233);\n}\n\nfunction _kdl_is_ws ( String ch ) {\n return false if ch \u2261 "";\n let code := ord(ch);\n return ch \u2261 " " or ch \u2261 "\\t" or ch \u2261 chr(12)\n or code \u2261 160 or code \u2261 5760 or code \u2261 8239\n or code \u2261 8287 or code \u2261 12288\n or ( code >= 8192 and code <= 8202 );\n}\n\nfunction _kdl_is_line_space ( String ch ) {\n return _kdl_is_ws(ch) or _kdl_is_newline(ch);\n}\n\nfunction _kdl_is_digit ( String ch ) {\n return ch ~ /^[0-9]$/;\n}\n\nfunction _kdl_is_hex_digit ( String ch ) {\n return ch ~ /^[0-9a-fA-F]$/;\n}\n\nfunction _kdl_hex_value ( String ch ) {\n if ( _kdl_is_digit(ch) ) {\n return ch + 0;\n }\n if ( index( "abcdef", ch ) >= 0 ) {\n return 10 + index( "abcdef", ch );\n }\n if ( index( "ABCDEF", ch ) >= 0 ) {\n return 10 + index( "ABCDEF", ch );\n }\n die `Invalid hexadecimal digit \'${ch}\'`;\n}\n\nfunction _kdl_from_hex ( String raw ) {\n let n := 0;\n let i := 0;\n while ( i < length raw ) {\n n := n * 16 + _kdl_hex_value( substr( raw, i, 1 ) );\n i++;\n }\n return n;\n}\n\nfunction _kdl_clean_number ( String token ) {\n let out := "";\n let i := 0;\n while ( i < length token ) {\n let ch := substr( token, i, 1 );\n out _= ch if ch \u2262 "_";\n i++;\n }\n return out;\n}\n\nfunction _kdl_parse_radix ( String token, Number radix ) {\n let sign := 1;\n let i := 0;\n if ( substr( token, 0, 1 ) \u2261 "-" ) {\n sign := -1;\n i := 1;\n }\n else if ( substr( token, 0, 1 ) \u2261 "+" ) {\n i := 1;\n }\n i += 2;\n\n let n := 0;\n while ( i < length token ) {\n let ch := substr( token, i, 1 );\n if ( ch \u2262 "_" ) {\n let v := radix \u2261 16 ? _kdl_hex_value(ch) : ch + 0;\n die `Invalid digit \'${ch}\' for radix ${radix}` if v >= radix;\n n := n * radix + v;\n }\n i++;\n }\n return sign * n;\n}\n\nfunction _kdl_strip_decimal_zeroes ( String raw ) {\n let i := 0;\n while ( i < length raw - 1 and substr( raw, i, 1 ) \u2261 "0" ) {\n i++;\n }\n return substr( raw, i, length raw - i );\n}\n\nfunction _kdl_decimal_mul_add ( String raw, Number factor, Number add ) {\n let out := "";\n let carry := add;\n let i := length raw - 1;\n while ( i >= 0 ) {\n let n := ( substr( raw, i, 1 ) + 0 ) * factor + carry;\n out := "" _ ( n mod 10 ) _ out;\n carry := floor( n / 10 );\n i--;\n }\n while ( carry > 0 ) {\n out := "" _ ( carry mod 10 ) _ out;\n carry := floor( carry / 10 );\n }\n return _kdl_strip_decimal_zeroes(out);\n}\n\nfunction _kdl_decimal_from_radix ( String token, Number radix ) {\n let sign := "";\n let i := 0;\n if ( substr( token, 0, 1 ) \u2261 "-" ) {\n sign := "-";\n i := 1;\n }\n else if ( substr( token, 0, 1 ) \u2261 "+" ) {\n i := 1;\n }\n i += 2;\n\n let out := "0";\n while ( i < length token ) {\n out := _kdl_decimal_mul_add(\n out,\n radix,\n _kdl_hex_value( substr( token, i, 1 ) ),\n );\n i++;\n }\n return sign _ out if sign \u2262 "" and out \u2262 "0";\n return out;\n}\n\nfunction _kdl_canonical_decimal_int ( String token ) {\n let sign := "";\n let i := 0;\n if ( substr( token, 0, 1 ) \u2261 "-" ) {\n sign := "-";\n i := 1;\n }\n else if ( substr( token, 0, 1 ) \u2261 "+" ) {\n i := 1;\n }\n let body := _kdl_strip_decimal_zeroes(\n substr( token, i, length token - i )\n );\n return sign _ body if sign \u2262 "" and body \u2262 "0";\n return body;\n}\n\nfunction _kdl_canonical_float ( String token ) {\n let epos := index( token, "e" );\n epos := index( token, "E" ) if epos < 0;\n return token if epos < 0;\n\n let mantissa := substr( token, 0, epos );\n let exponent := substr( token, epos + 1, length token - epos - 1 );\n if ( substr( exponent, 0, 1 ) \u2262 "+"\n and substr( exponent, 0, 1 ) \u2262 "-"\n ) {\n exponent := "+" _ exponent;\n }\n return mantissa _ "E" _ exponent;\n}\n\nfunction _kdl_find_multiline_close (\n String text,\n String close,\n Number start,\n Boolean raw,\n) {\n let found := index( text, close, start );\n while ( found >= 0 ) {\n if ( raw or found = 0 or substr( text, found - 1, 1 ) \u2262 "\\\\" ) {\n return found;\n }\n found := index( text, close, found + 1 );\n }\n return -1;\n}\n\nfunction _kdl_reserved_ident ( String text ) {\n return text \u2261 "true" or text \u2261 "false" or text \u2261 "null"\n or text \u2261 "inf" or text \u2261 "-inf" or text \u2261 "nan";\n}\n\nfunction _kdl_ident_break ( String ch ) {\n return ch \u2261 "" or _kdl_is_line_space(ch)\n or ch \u2261 "\\\\" or ch \u2261 "/" or ch \u2261 "(" or ch \u2261 ")"\n or ch \u2261 "{" or ch \u2261 "}" or ch \u2261 ";" or ch \u2261 "["\n or ch \u2261 "]" or ch \u2261 "\\"" or ch \u2261 "#" or ch \u2261 "=";\n}\n\nfunction _kdl_forbidden_codepoint ( Number code ) {\n return code < 32 or code \u2261 127 or code \u2261 65279\n or code \u2261 8206 or code \u2261 8207\n or ( code >= 8234 and code <= 8238 )\n or ( code >= 8294 and code <= 8297 );\n}\n\nfunction _kdl_is_plain_ident ( String text ) {\n if ( text \u2261 "" or _kdl_reserved_ident(text) ) {\n return false;\n }\n let first := substr( text, 0, 1 );\n let second := substr( text, 1, 1 );\n if ( _kdl_is_digit(first) ) {\n return false;\n }\n if ( ( first \u2261 "+" or first \u2261 "-" ) and _kdl_is_digit(second) ) {\n return false;\n }\n if ( first \u2261 "." and _kdl_is_digit(second) ) {\n return false;\n }\n let i := 0;\n while ( i < length text ) {\n return false if _kdl_ident_break( substr( text, i, 1 ) );\n i++;\n }\n return true;\n}\n\nfunction _kdl_escape_string ( String text ) {\n let out := "";\n let i := 0;\n while ( i < length text ) {\n let ch := substr( text, i, 1 );\n if ( ch \u2261 "\\\\" ) { out _= "\\\\\\\\"; }\n else if ( ch \u2261 "\\"" ) { out _= "\\\\\\""; }\n else if ( ch \u2261 chr(8) ) { out _= "\\\\b"; }\n else if ( ch \u2261 chr(12) ) { out _= "\\\\f"; }\n else if ( ch \u2261 "\\n" ) { out _= "\\\\n"; }\n else if ( ch \u2261 "\\r" ) { out _= "\\\\r"; }\n else if ( ch \u2261 "\\t" ) { out _= "\\\\t"; }\n else if ( ord(ch) < 32 ) {\n out _= "\\\\u{" _ sprint( "%x", ord(ch) ) _ "}";\n }\n else { out _= ch; }\n i++;\n }\n return out;\n}\n\nfunction _kdl_name ( String text ) {\n return _kdl_is_plain_ident(text) ? text : `"${_kdl_escape_string(text)}"`;\n}\n\nfunction _kdl_indent ( Number level, Boolean canonical := false ) {\n let out := "";\n let i := 0;\n let unit := canonical ? " " : "\\t";\n while ( i < level ) {\n out _= unit;\n i++;\n }\n return out;\n}\n\nfunction _kdl_canonical_props ( PairList props ) {\n let latest := {};\n for ( let pair in props.to_Array() ) {\n let kv := pair{pair};\n latest{(kv[0])} := kv[1];\n }\n\n let out := [];\n for ( let key in latest.sorted_keys() ) {\n out.push( [ key, latest{(key)} ] );\n }\n return out;\n}\n\nclass KDLValue {\n let String type := "null";\n let kind := null;\n let value := null;\n let type_annotation := null;\n let canonical_number := null;\n\n method type () { return type; }\n method kind () { return kind; }\n method value () { return value; }\n method type_annotation () { return type_annotation; }\n method canonical_number () { return canonical_number; }\n\n method is_null () { return type \u2261 "null"; }\n method is_boolean () { return type \u2261 "boolean"; }\n method is_number () { return type \u2261 "number"; }\n method is_string () { return type \u2261 "string"; }\n method is_true () { return type \u2261 "boolean" and value; }\n method is_false () { return type \u2261 "boolean" and not value; }\n\n method to_Number () {\n die "KDLValue is not a number" if type \u2262 "number";\n die "KDL keyword number has no native numeric value" if kind \u2261 "string";\n return value;\n }\n\n method to_String () {\n return "" if type \u2261 "null";\n return "" _ value;\n }\n\n method to_Boolean () {\n if ( type \u2261 "boolean" ) { return value; }\n if ( type \u2261 "null" ) { return false; }\n if ( type \u2261 "number" ) {\n return kind \u2262 "string" and value \u2262 0;\n }\n return value \u2262 "";\n }\n\n method native_value () {\n return null if type \u2261 "null";\n return value;\n }\n}\n\nclass KDLNode {\n let name := "";\n let type_annotation := null;\n let args := [];\n let props := null;\n let children := [];\n\n method __build__ () {\n props := new PairList() if props \u2261 null;\n args := [] if args \u2261 null;\n children := [] if children \u2261 null;\n }\n\n method name () { return name; }\n method type_annotation () { return type_annotation; }\n method args () { return args; }\n method props () { return props; }\n method children () { return children; }\n\n method to_Iterator () {\n let values := [];\n for ( let arg in args ) {\n values.push(arg);\n }\n for ( let child in children ) {\n values.push(child);\n }\n return values.to_Iterator();\n }\n}\n\nclass KDLDocument {\n let nodes := [];\n\n method __build__ () {\n nodes := [] if nodes \u2261 null;\n }\n\n method nodes () { return nodes; }\n\n method to_Iterator () {\n return nodes.to_Iterator();\n }\n}\n\nclass _KDLParser {\n let String text := "";\n let Number pos := 0;\n\n method _eof () {\n return pos >= length text;\n }\n\n method _peek () {\n return "" if self._eof();\n return substr( text, pos, 1 );\n }\n\n method _take () {\n let ch := self._peek();\n pos++;\n return ch;\n }\n\n method _error ( String message ) {\n die `KDL parse error at offset ${pos}: ${message}`;\n }\n\n method _skip_newline () {\n if ( _kdl_starts_with( text, pos, "\\r\\n" ) ) {\n pos += 2;\n return true;\n }\n if ( _kdl_is_newline( self._peek() ) ) {\n pos++;\n return true;\n }\n return false;\n }\n\n method _skip_line_comment () {\n return false if not _kdl_starts_with( text, pos, "//" );\n pos += 2;\n while ( not self._eof() and not _kdl_is_newline( self._peek() ) ) {\n pos++;\n }\n self._skip_newline();\n return true;\n }\n\n method _skip_block_comment () {\n return false if not _kdl_starts_with( text, pos, "/*" );\n pos += 2;\n let depth := 1;\n while ( depth > 0 ) {\n self._error("Unterminated block comment") if self._eof();\n if ( _kdl_starts_with( text, pos, "/*" ) ) {\n depth++;\n pos += 2;\n }\n else if ( _kdl_starts_with( text, pos, "*/" ) ) {\n depth--;\n pos += 2;\n }\n else {\n pos++;\n }\n }\n return true;\n }\n\n method _skip_line_space () {\n let moved := false;\n let keep := true;\n while ( keep and not self._eof() ) {\n keep := false;\n if ( _kdl_is_ws( self._peek() ) ) {\n pos++;\n moved := true;\n keep := true;\n }\n else if ( self._skip_newline() ) {\n moved := true;\n keep := true;\n }\n else if ( self._skip_line_comment() ) {\n moved := true;\n keep := true;\n }\n else if ( self._skip_block_comment() ) {\n moved := true;\n keep := true;\n }\n else if ( self._skip_escline() ) {\n moved := true;\n keep := true;\n }\n }\n return moved;\n }\n\n method _skip_node_space () {\n let moved := false;\n let keep := true;\n while ( keep and not self._eof() ) {\n keep := false;\n if ( _kdl_is_ws( self._peek() ) ) {\n pos++;\n moved := true;\n keep := true;\n }\n else if ( self._skip_block_comment() ) {\n moved := true;\n keep := true;\n }\n else if ( self._skip_escline() ) {\n moved := true;\n keep := true;\n }\n }\n return moved;\n }\n\n method _skip_escline () {\n return false if self._peek() \u2262 "\\\\";\n let save := pos;\n pos++;\n let keep := true;\n while ( keep and not self._eof() ) {\n keep := false;\n while ( _kdl_is_ws( self._peek() ) ) {\n pos++;\n keep := true;\n }\n if ( self._skip_block_comment() ) {\n keep := true;\n }\n }\n if ( self._skip_line_comment() or self._skip_newline() or self._eof() ) {\n return true;\n }\n pos := save;\n return false;\n }\n\n method _parse_document () {\n if ( _kdl_starts_with( text, pos, chr(65279) ) ) {\n pos++;\n }\n let nodes := self._parse_nodes(false);\n self._skip_line_space();\n self._error("Unexpected trailing input") if not self._eof();\n return new KDLDocument( nodes: nodes );\n }\n\n method _parse_nodes ( Boolean in_children ) {\n let nodes := [];\n while (true) {\n self._skip_line_space();\n if ( self._eof() ) {\n return nodes;\n }\n if ( in_children and self._peek() \u2261 "}" ) {\n return nodes;\n }\n if ( _kdl_starts_with( text, pos, "/-" ) ) {\n self._consume_slashdash();\n self._parse_node(true);\n }\n else {\n let node := self._parse_node(false);\n nodes.push(node) if node \u2262 null;\n }\n }\n return nodes;\n }\n\n method _consume_slashdash () {\n self._error("Expected slashdash") if not _kdl_starts_with( text, pos, "/-" );\n pos += 2;\n self._skip_line_space();\n }\n\n method _parse_type_annotation () {\n return null if self._peek() \u2262 "(";\n pos++;\n self._skip_node_space();\n let name := self._parse_string();\n self._skip_node_space();\n self._error("Expected \')\' after type annotation") if self._peek() \u2262 ")";\n pos++;\n self._skip_node_space();\n return name;\n }\n\n method _parse_node ( Boolean discard ) {\n let annotation := self._parse_type_annotation();\n self._skip_node_space();\n let name := self._parse_string();\n let args := [];\n let props := new PairList();\n let children := [];\n let slashdash_child_before_entry := false;\n\n while (true) {\n let had_space := self._skip_node_space();\n if ( self._eof() ) {\n return discard ? null : new KDLNode(\n name: name,\n type_annotation: annotation,\n args: args,\n props: props,\n children: children,\n );\n }\n\n let ch := self._peek();\n if ( ch \u2261 ";" ) {\n pos++;\n return discard ? null : new KDLNode(\n name: name,\n type_annotation: annotation,\n args: args,\n props: props,\n children: children,\n );\n }\n if ( ch \u2261 "}" ) {\n return discard ? null : new KDLNode(\n name: name,\n type_annotation: annotation,\n args: args,\n props: props,\n children: children,\n );\n }\n if ( _kdl_is_newline(ch) ) {\n self._skip_newline();\n return discard ? null : new KDLNode(\n name: name,\n type_annotation: annotation,\n args: args,\n props: props,\n children: children,\n );\n }\n if ( _kdl_starts_with( text, pos, "//" ) ) {\n self._skip_line_comment();\n return discard ? null : new KDLNode(\n name: name,\n type_annotation: annotation,\n args: args,\n props: props,\n children: children,\n );\n }\n if ( _kdl_starts_with( text, pos, "/-" ) ) {\n self._consume_slashdash();\n if ( self._peek() \u2261 "{" ) {\n self._parse_children();\n slashdash_child_before_entry := true;\n }\n else {\n self._parse_entry(true, args, props);\n }\n next;\n }\n if ( ch \u2261 "{" ) {\n self._error("Child block must be the final node field")\n if children.length() > 0;\n children := self._parse_children();\n next;\n }\n self._error("Expected whitespace before node field")\n if not had_space;\n self._error("Child block must be the final node field")\n if children.length() > 0;\n self._error("Discarded child block must not precede node fields")\n if slashdash_child_before_entry;\n self._parse_entry(discard, args, props);\n }\n }\n\n method _parse_children () {\n self._error("Expected \'{\'") if self._peek() \u2262 "{";\n pos++;\n let kids := self._parse_nodes(true);\n self._skip_line_space();\n self._error("Expected \'}\' after child block") if self._peek() \u2262 "}";\n pos++;\n return kids;\n }\n\n method _parse_entry ( Boolean discard, Array args, PairList props ) {\n if ( self._peek() \u2261 "(" ) {\n let value := self._parse_value();\n args.push(value) if not discard;\n return;\n }\n\n if ( self._peek() \u2261 "#" or self._starts_number() ) {\n let value := self._parse_value();\n args.push(value) if not discard;\n return;\n }\n\n let key := self._parse_string();\n let after_key := pos;\n self._skip_node_space();\n if ( self._peek() \u2261 "=" ) {\n pos++;\n self._skip_node_space();\n let value := self._parse_value();\n props.add( key, value ) if not discard;\n return;\n }\n pos := after_key;\n\n let value := new KDLValue( type: "string", value: key );\n args.push(value) if not discard;\n return;\n }\n\n method _parse_value () {\n let annotation := self._parse_type_annotation();\n self._skip_node_space();\n\n if ( self._peek() \u2261 "#" ) {\n return self._parse_hash_value(annotation);\n }\n if ( self._starts_number() ) {\n return self._parse_number(annotation);\n }\n return new KDLValue(\n type: "string",\n value: self._parse_string(),\n type_annotation: annotation,\n );\n }\n\n method _starts_number () {\n let ch := self._peek();\n let nxt := substr( text, pos + 1, 1 );\n return _kdl_is_digit(ch)\n or ( ( ch \u2261 "+" or ch \u2261 "-" ) and _kdl_is_digit(nxt) );\n }\n\n method _read_token () {\n let start := pos;\n while ( not self._eof() ) {\n let ch := self._peek();\n last if _kdl_ident_break(ch);\n pos++;\n }\n return substr( text, start, pos - start );\n }\n\n method _parse_hash_value ( annotation ) {\n if ( _kdl_starts_with( text, pos, "#true" ) ) {\n pos += 5;\n return new KDLValue(\n type: "boolean",\n value: true,\n type_annotation: annotation,\n );\n }\n if ( _kdl_starts_with( text, pos, "#false" ) ) {\n pos += 6;\n return new KDLValue(\n type: "boolean",\n value: false,\n type_annotation: annotation,\n );\n }\n if ( _kdl_starts_with( text, pos, "#null" ) ) {\n pos += 5;\n return new KDLValue(\n type: "null",\n value: null,\n type_annotation: annotation,\n );\n }\n if ( _kdl_starts_with( text, pos, "#inf" ) ) {\n pos += 4;\n return new KDLValue(\n type: "number",\n kind: "string",\n value: "#inf",\n type_annotation: annotation,\n );\n }\n if ( _kdl_starts_with( text, pos, "#-inf" ) ) {\n pos += 5;\n return new KDLValue(\n type: "number",\n kind: "string",\n value: "#-inf",\n type_annotation: annotation,\n );\n }\n if ( _kdl_starts_with( text, pos, "#nan" ) ) {\n pos += 4;\n return new KDLValue(\n type: "number",\n kind: "string",\n value: "#nan",\n type_annotation: annotation,\n );\n }\n return new KDLValue(\n type: "string",\n value: self._parse_raw_string(),\n type_annotation: annotation,\n );\n }\n\n method _parse_number ( annotation ) {\n let token := self._read_token();\n self._error( `Invalid number \'${token}\'` )\n if token ~ /^[+-]?0[xob]_/i or token ~ /\\._/;\n let clean := _kdl_clean_number(token);\n if ( clean ~ /^[+-]?0x[0-9a-fA-F]+$/ ) {\n return new KDLValue(\n type: "number",\n kind: "integer",\n value: _kdl_parse_radix( clean, 16 ),\n canonical_number: _kdl_decimal_from_radix( clean, 16 ),\n type_annotation: annotation,\n );\n }\n if ( clean ~ /^[+-]?0o[0-7]+$/ ) {\n return new KDLValue(\n type: "number",\n kind: "integer",\n value: _kdl_parse_radix( clean, 8 ),\n canonical_number: _kdl_decimal_from_radix( clean, 8 ),\n type_annotation: annotation,\n );\n }\n if ( clean ~ /^[+-]?0b[01]+$/ ) {\n return new KDLValue(\n type: "number",\n kind: "integer",\n value: _kdl_parse_radix( clean, 2 ),\n canonical_number: _kdl_decimal_from_radix( clean, 2 ),\n type_annotation: annotation,\n );\n }\n if ( clean ~ /^[+-]?[0-9]+$/ ) {\n return new KDLValue(\n type: "number",\n kind: "integer",\n value: clean + 0,\n canonical_number: _kdl_canonical_decimal_int(clean),\n type_annotation: annotation,\n );\n }\n if ( clean ~ /^[+-]?[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?$/ ) {\n return new KDLValue(\n type: "number",\n kind: "float",\n value: clean + 0,\n canonical_number: _kdl_canonical_float(clean),\n type_annotation: annotation,\n );\n }\n self._error( `Invalid number \'${token}\'` );\n }\n\n method _parse_string () {\n if ( self._peek() \u2261 "#" ) {\n return self._parse_raw_string();\n }\n if ( self._peek() \u2261 "\\"" ) {\n return self._parse_quoted_string();\n }\n return self._parse_identifier_string();\n }\n\n method _parse_identifier_string () {\n let ident := self._read_token();\n self._error("Expected string") if ident \u2261 "";\n self._error( `Reserved KDL identifier \'${ident}\'` )\n if _kdl_reserved_ident(ident);\n self._error( `Invalid KDL identifier \'${ident}\'` )\n if substr( ident, 0, 1 ) \u2261 "."\n and _kdl_is_digit( substr( ident, 1, 1 ) );\n let i := 0;\n while ( i < length ident ) {\n let code := ord( substr( ident, i, 1 ) );\n self._error( `Forbidden codepoint in KDL identifier \'${ident}\'` )\n if _kdl_forbidden_codepoint(code);\n i++;\n }\n return ident;\n }\n\n method _parse_quoted_string () {\n if ( _kdl_starts_with( text, pos, "\\"\\"\\"" ) ) {\n return self._parse_multiline_string( "", false );\n }\n pos++;\n let out := "";\n while ( not self._eof() ) {\n let ch := self._take();\n if ( ch \u2261 "\\"" ) {\n return out;\n }\n if ( ch \u2261 "\\\\" ) {\n out _= self._parse_escape();\n }\n else {\n self._error("Newline in quoted string") if _kdl_is_newline(ch);\n out _= ch;\n }\n }\n self._error("Unterminated quoted string");\n }\n\n method _parse_escape () {\n self._error("Unterminated string escape") if self._eof();\n let ch := self._take();\n if ( ch \u2261 "\\"" ) { return "\\""; }\n if ( ch \u2261 "\\\\" ) { return "\\\\"; }\n if ( ch \u2261 "b" ) { return chr(8); }\n if ( ch \u2261 "f" ) { return chr(12); }\n if ( ch \u2261 "n" ) { return "\\n"; }\n if ( ch \u2261 "r" ) { return "\\r"; }\n if ( ch \u2261 "s" ) { return " "; }\n if ( ch \u2261 "t" ) { return "\\t"; }\n if ( _kdl_is_line_space(ch) ) {\n while ( _kdl_is_line_space(ch) ) {\n if ( _kdl_is_newline(ch) ) {\n self._skip_newline();\n }\n ch := self._peek();\n pos++ if _kdl_is_ws(ch);\n }\n return "";\n }\n if ( ch \u2261 "u" ) {\n self._error("Expected \'{\' in Unicode escape") if self._peek() \u2262 "{";\n pos++;\n let start := pos;\n while ( not self._eof() and self._peek() \u2262 "}" ) {\n self._error("Invalid Unicode escape") if not _kdl_is_hex_digit( self._peek() );\n pos++;\n }\n self._error("Unterminated Unicode escape") if self._peek() \u2262 "}";\n let raw := substr( text, start, pos - start );\n self._error("Unicode escape must contain 1 to 6 hex digits")\n if length raw < 1 or length raw > 6;\n pos++;\n let code := _kdl_from_hex(raw);\n return chr(code);\n }\n self._error( `Invalid string escape \'\\\\${ch}\'` );\n }\n\n method _parse_raw_string () {\n let hashes := "";\n while ( self._peek() \u2261 "#" ) {\n hashes _= "#";\n pos++;\n }\n self._error("Expected raw string quotes") if self._peek() \u2262 "\\"";\n if ( _kdl_starts_with( text, pos, "\\"\\"\\"" ) ) {\n return self._parse_multiline_string( hashes, true );\n }\n pos++;\n let close := "\\"" _ hashes;\n let end := index( text, close, pos );\n self._error("Unterminated raw string") if end < 0;\n let out := substr( text, pos, end - pos );\n self._error("Newline in raw string") if out ~ /[\\r\\n\\v]/;\n pos := end + length close;\n return out;\n }\n\n method _parse_multiline_string ( String hashes, Boolean raw ) {\n pos += 3;\n self._error("Expected newline after multiline string opener")\n if not self._skip_newline();\n let close := "\\"\\"\\"" _ hashes;\n let end := _kdl_find_multiline_close( text, close, pos, raw );\n self._error("Unterminated multiline string") if end < 0;\n let body := substr( text, pos, end - pos );\n pos := end + length close;\n\n let last_nl := -1;\n let i := length body - 1;\n while ( i >= 0 and last_nl < 0 ) {\n if ( substr( body, i, 1 ) \u2261 "\\n" ) {\n last_nl := i;\n }\n i--;\n }\n return "" if last_nl < 0;\n let content := substr( body, 0, last_nl );\n let indent := substr( body, last_nl + 1, length body - last_nl - 1 );\n if ( not raw ) {\n let bs := 0;\n while ( bs < length indent\n and _kdl_is_ws( substr( indent, bs, 1 ) )\n ) {\n bs++;\n }\n if ( bs < length indent and substr( indent, bs, 1 ) \u2261 "\\\\" ) {\n let k := bs + 1;\n let only_ws := true;\n while ( k < length indent ) {\n only_ws := false\n if not _kdl_is_ws( substr( indent, k, 1 ) );\n k++;\n }\n indent := substr( indent, 0, bs ) if only_ws;\n }\n }\n if ( indent \u2261 "" ) {\n let min_indent := null;\n let scan := 0;\n let scan_start := 0;\n while ( scan <= length content ) {\n if ( scan \u2261 length content\n or substr( content, scan, 1 ) \u2261 "\\n"\n ) {\n let raw_line := substr(\n content,\n scan_start,\n scan - scan_start,\n );\n if ( not( raw_line ~ /^\\s*$/ ) ) {\n let width := 0;\n while ( width < length raw_line\n and _kdl_is_ws( substr( raw_line, width, 1 ) )\n ) {\n width++;\n }\n min_indent := width\n if min_indent \u2261 null or width < min_indent;\n }\n scan_start := scan + 1;\n }\n scan++;\n }\n if ( min_indent \u2262 null and min_indent > 0 ) {\n indent := "";\n let si := 0;\n while ( si < min_indent ) {\n indent _= " ";\n si++;\n }\n }\n }\n let out := "";\n i := 0;\n let line_start := 0;\n let emitted := false;\n let continued := false;\n let last_line_continued_with_text := false;\n while ( i <= length content ) {\n if ( i \u2261 length content or substr( content, i, 1 ) \u2261 "\\n" ) {\n let line := substr( content, line_start, i - line_start );\n if ( line ~ /^\\s*$/ ) {\n line := "";\n }\n else if ( continued ) {\n if ( length indent > 0\n and substr( line, 0, length indent ) \u2261 indent\n ) {\n line := substr(\n line,\n length indent,\n length line - length indent,\n );\n }\n }\n else if ( length indent > 0 and substr( line, 0, length indent ) \u2261 indent ) {\n line := substr( line, length indent, length line - length indent );\n }\n else if ( length indent > 0 ) {\n let k := 0;\n while ( k < length line\n and _kdl_is_ws( substr( line, k, 1 ) )\n ) {\n k++;\n }\n let ok_escape_prefix := false;\n if ( not raw and substr( line, k, 1 ) \u2261 "\\\\" ) {\n k++;\n ok_escape_prefix := true;\n while ( k < length line ) {\n ok_escape_prefix := false\n if not _kdl_is_ws( substr( line, k, 1 ) );\n k++;\n }\n }\n self._error("Multiline string indentation mismatch")\n if not ok_escape_prefix;\n }\n\n let line_continues := false;\n if ( not raw ) {\n let j := length line - 1;\n while ( j >= 0 and _kdl_is_ws( substr( line, j, 1 ) ) ) {\n j--;\n }\n if ( j >= 0 and substr( line, j, 1 ) \u2261 "\\\\" ) {\n let slashes := 0;\n while ( j - slashes >= 0\n and substr( line, j - slashes, 1 ) \u2261 "\\\\"\n ) {\n slashes++;\n }\n if ( slashes mod 2 = 1 ) {\n line_continues := true;\n line := substr( line, 0, j );\n }\n }\n }\n self._error("Invalid final multiline whitespace escape")\n if line_continues and line \u2261 "" and indent \u2261 "";\n\n out _= "\\n"\n if emitted and not continued\n and not ( line_continues and line \u2261 "" );\n out _= raw ? line : self._unescape_multiline_line(line);\n emitted := true;\n continued := line_continues;\n last_line_continued_with_text := line_continues and line \u2262 "";\n line_start := i + 1;\n }\n i++;\n }\n self._error("Invalid final multiline whitespace escape")\n if last_line_continued_with_text;\n return out;\n }\n\n method _unescape_multiline_line ( String line ) {\n let out := "";\n let i := 0;\n while ( i < length line ) {\n let ch := substr( line, i, 1 );\n if ( ch \u2262 "\\\\" ) {\n out _= ch;\n i++;\n next;\n }\n\n i++;\n self._error("Unterminated string escape") if i >= length line;\n ch := substr( line, i, 1 );\n if ( ch \u2261 "\\"" ) { out _= "\\""; }\n else if ( ch \u2261 "\\\\" ) { out _= "\\\\"; }\n else if ( ch \u2261 "b" ) { out _= chr(8); }\n else if ( ch \u2261 "f" ) { out _= chr(12); }\n else if ( ch \u2261 "n" ) { out _= "\\n"; }\n else if ( ch \u2261 "r" ) { out _= "\\r"; }\n else if ( ch \u2261 "s" ) { out _= " "; }\n else if ( ch \u2261 "t" ) { out _= "\\t"; }\n else if ( _kdl_is_ws(ch) ) {\n while ( i < length line and _kdl_is_ws( substr( line, i, 1 ) ) ) {\n i++;\n }\n i--;\n }\n else if ( ch \u2261 "u" ) {\n self._error("Expected \'{\' in Unicode escape")\n if substr( line, i + 1, 1 ) \u2262 "{";\n i += 2;\n let start := i;\n while ( i < length line and substr( line, i, 1 ) \u2262 "}" ) {\n self._error("Invalid Unicode escape")\n if not _kdl_is_hex_digit( substr( line, i, 1 ) );\n i++;\n }\n self._error("Unterminated Unicode escape")\n if i >= length line;\n let raw := substr( line, start, i - start );\n self._error("Unicode escape must contain 1 to 6 hex digits")\n if length raw < 1 or length raw > 6;\n let code := _kdl_from_hex(raw);\n out _= chr(code);\n }\n else {\n self._error( `Invalid string escape \'\\\\${ch}\'` );\n }\n i++;\n }\n return out;\n }\n}\n\nfunction _kdl_value ( value ) {\n if ( value instanceof KDLValue ) {\n return value;\n }\n if ( value \u2261 null ) {\n return new KDLValue( type: "null", value: null );\n }\n if ( value instanceof Boolean ) {\n return new KDLValue( type: "boolean", value: value );\n }\n if ( value instanceof Number ) {\n return new KDLValue( type: "number", kind: "float", value: value );\n }\n return new KDLValue( type: "string", value: "" _ value );\n}\n\nfunction _kdl_encode_value ( value, Boolean canonical := false ) {\n let v := _kdl_value(value);\n let prefix := "";\n if ( v.type_annotation() \u2262 null ) {\n prefix := "(" _ _kdl_name( v.type_annotation() ) _ ")";\n }\n if ( v.is_null() ) { return prefix _ "#null"; }\n if ( v.is_boolean() ) {\n return prefix _ ( v.native_value() ? "#true" : "#false" );\n }\n if ( v.is_number() ) {\n if ( canonical and v.canonical_number() \u2262 null ) {\n return prefix _ v.canonical_number();\n }\n return prefix _ ( v.kind() \u2261 "string" ? v.value() : "" _ v.value() );\n }\n if ( v.is_string() ) {\n if ( canonical and _kdl_is_plain_ident( v.native_value() ) ) {\n return prefix _ v.native_value();\n }\n return prefix _ "\\"" _ _kdl_escape_string( v.native_value() ) _ "\\"";\n }\n\n let native := v.native_value();\n try {\n if ( native can "to_String" ) {\n let text := native.to_String();\n if ( canonical and _kdl_is_plain_ident(text) ) {\n return prefix _ text;\n }\n return prefix _ "\\"" _ _kdl_escape_string(text) _ "\\"";\n }\n }\n catch {\n }\n\n die `Cannot serialize KDLValue of type \'${v.type()}\'`;\n}\n\nfunction _kdl_encode_node (\n KDLNode node,\n Number level,\n Boolean canonical := false,\n) {\n let out := _kdl_indent( level, canonical );\n if ( node.type_annotation() \u2262 null ) {\n out _= "(" _ _kdl_name( node.type_annotation() ) _ ")";\n }\n out _= _kdl_name( node.name() );\n for ( let arg in node.args() ) {\n out _= " " _ _kdl_encode_value( arg, canonical );\n }\n if ( canonical ) {\n for ( let kv in _kdl_canonical_props( node.props() ) ) {\n out _= " " _ _kdl_name(kv[0]) _ "="\n _ _kdl_encode_value( kv[1], canonical );\n }\n }\n else {\n for ( let pair in node.props().to_Array() ) {\n let kv := pair{pair};\n out _= " " _ _kdl_name(kv[0]) _ "="\n _ _kdl_encode_value( kv[1], canonical );\n }\n }\n if ( node.children().length() > 0 ) {\n out _= " {\\n";\n let i := 0;\n while ( i < node.children().length() ) {\n out _= _kdl_encode_node(\n node.children()[i],\n level + 1,\n canonical,\n );\n out _= "\\n";\n i++;\n }\n out _= _kdl_indent( level, canonical ) _ "}";\n }\n return out;\n}\n\nfunction _kdl_encode_document ( KDLDocument doc, Boolean canonical := false ) {\n let out := "";\n let i := 0;\n while ( i < doc.nodes().length() ) {\n out _= "\\n" if i > 0;\n out _= _kdl_encode_node( doc.nodes()[i], 0, canonical );\n i++;\n }\n out _= "\\n" if canonical;\n return out;\n}\n\nclass KDL {\n let Boolean canonical := false;\n\n method decode ( String text ) {\n return new _KDLParser( text: text, pos: 0 )._parse_document();\n }\n\n method decode_binarystring ( BinaryString raw ) {\n return self.decode( to_string(raw) );\n }\n\n method encode ( value ) {\n if ( value instanceof KDLDocument ) {\n return _kdl_encode_document( value, canonical );\n }\n if ( value instanceof KDLNode ) {\n return _kdl_encode_node( value, 0, canonical );\n }\n if ( value instanceof Array ) {\n return _kdl_encode_document(\n new KDLDocument( nodes: value ),\n canonical,\n );\n }\n die "KDL.encode expects a KDLDocument, KDLNode, or Array of KDLNode";\n }\n\n method encode_binarystring ( value ) {\n return to_binary( self.encode(value) );\n }\n\n method load ( path ) {\n from std/io import Path;\n die "KDL.load is denied by runtime policy" if __system__{deny_fs};\n die "KDL.load expects a std/io Path object" if not( path instanceof Path );\n return self.decode_binarystring( path.slurp() );\n }\n\n method dump ( path, value ) {\n from std/io import Path;\n die "KDL.dump is denied by runtime policy" if __system__{deny_fs};\n die "KDL.dump expects a std/io Path object" if not( path instanceof Path );\n path.spew( self.encode_binarystring(value) );\n return path;\n }\n}\n';
|
|
41905
41972
|
virtualFiles["/modules/std/data/kdl/json.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/data/kdl/json - JSON-in-KDL structure conversion.\n\n=head1 SYNOPSIS\n\n from std/data/kdl import KDL;\n from std/data/kdl/json import kdl_to_json, json_to_kdl;\n\n let kdl_doc := ( new KDL() ).decode( """- foo=1 bar=#true""" );\n let data := kdl_to_json(kdl_doc);\n let roundtrip := json_to_kdl(data);\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nThis module implements the JSON-in-KDL (JiK) mapping for parsed Zuzu\nobjects. C<kdl_to_json> accepts a C<KDLDocument> or C<KDLNode> and\nreturns native Zuzu data structures. C<json_to_kdl> accepts native\nJSON-like data structures and returns a C<KDLDocument>.\n\nC<kdl_to_json(value, pairlists: true)> maps object-like JiK nodes to\nC<PairList> values instead of C<Dict> values, preserving key order and\nduplicate keys.\n\n=head1 EXPORTS\n\n=head2 Functions\n\n=over\n\n=item C<< kdl_to_json(value, ... PairList opts) >>\n\nParameters: C<value> is a C<KDLDocument>, C<KDLNode>, or compatible KDL\nvalue and C<opts> may include C<pairlists>. Returns: value. Converts\nJSON-in-KDL structures into native JSON-like ZuzuScript data.\n\n=item C<< json_to_kdl(value) >>\n\nParameters: C<value> is a JSON-like ZuzuScript value. Returns:\nC<KDLDocument>. Converts native JSON-like data into JSON-in-KDL nodes.\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/data/kdl/json >> 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\nfrom std/data/kdl import KDLDocument, KDLNode, KDLValue;\nfrom std/data/kdl/xml import xml_to_kdl;\nfrom std/time import Time;\n\n\nfunction _jik_has_props ( KDLNode node ) {\n return node.props().to_Array().length() > 0;\n}\n\nfunction _jik_literal_value ( value ) {\n die "JSON-in-KDL literal must be a KDLValue"\n if not( value instanceof KDLValue );\n if ( value.is_number() and value.kind() \u2261 "string" ) {\n die "JSON-in-KDL does not support non-finite KDL number keywords";\n }\n return value.native_value();\n}\n\nfunction _jik_is_time_native ( value ) {\n return value instanceof Time;\n}\n\nfunction _jik_pad2 ( value ) {\n let text := "" _ value;\n return length text < 2 ? "0" _ text : text;\n}\n\nfunction _jik_pad4 ( value ) {\n let text := "" _ value;\n while ( length text < 4 ) {\n text := "0" _ text;\n }\n return text;\n}\n\nfunction _jik_time_text ( value ) {\n return _jik_pad4( value.year() )\n _ "-" _ _jik_pad2( value.mon() )\n _ "-" _ _jik_pad2( value.day_of_month() )\n _ "T" _ _jik_pad2( value.hour() )\n _ ":" _ _jik_pad2( value.min() )\n _ ":" _ _jik_pad2( value.sec() );\n}\n\nfunction _jik_value ( value ) {\n if ( value instanceof KDLValue ) {\n return value;\n }\n if ( value \u2261 null ) {\n return new KDLValue( type: "null", value: null );\n }\n\n if ( value instanceof Boolean ) {\n return new KDLValue( type: "boolean", value: value );\n }\n if ( value instanceof Number ) {\n return new KDLValue( type: "number", kind: "float", value: value );\n }\n if ( value instanceof String or value instanceof BinaryString ) {\n return new KDLValue( type: "string", value: "" _ value );\n }\n if ( _jik_is_time_native(value) ) {\n return new KDLValue(\n type: "string",\n value: _jik_time_text(value),\n type_annotation: "date-time",\n );\n }\n\n return new KDLValue( type: typeof value, value: value );\n}\n\nfunction _jik_is_array_native ( value ) {\n return value instanceof Array or value instanceof Set or value instanceof Bag;\n}\n\nfunction _jik_is_object_native ( value ) {\n return value instanceof Dict or value instanceof PairList;\n}\n\nfunction _jik_is_kdl_native ( value ) {\n return value instanceof KDLDocument or value instanceof KDLNode;\n}\n\nfunction _jik_is_xml_native ( value ) {\n try {\n if ( value can nodeType ) {\n return true if value.nodeType() \u2262 null;\n }\n }\n catch {\n }\n\n try {\n return false if not( value can documentElement );\n value.documentElement();\n return true;\n }\n catch {\n }\n\n return false;\n}\n\nfunction _jik_is_opaque_literal_native ( value ) {\n return not _jik_is_array_native(value)\n and not _jik_is_object_native(value)\n and not _jik_is_kdl_native(value)\n and not _jik_is_xml_native(value);\n}\n\nfunction _jik_is_literal_native ( value ) {\n return value \u2261 null\n or value instanceof Boolean\n or value instanceof Number\n or value instanceof String\n or value instanceof BinaryString\n or _jik_is_time_native(value)\n or value instanceof KDLValue\n or _jik_is_opaque_literal_native(value);\n}\n\nfunction _jik_sorted_array ( value ) {\n if ( value instanceof Set or value instanceof Bag ) {\n return value.sortstr();\n }\n return value;\n}\n\nfunction _jik_pairs ( obj ) {\n let out := [];\n if ( obj instanceof PairList ) {\n for ( let p in obj.to_Array() ) {\n out.push( p{pair} );\n }\n return out;\n }\n\n for ( let key in obj.sorted_keys() ) {\n out.push( [ key, obj.get(key) ] );\n }\n return out;\n}\n\nfunction _jik_native_to_node;\nfunction _jik_native_to_nodes;\n\nfunction _jik_xml_nodes ( value ) {\n return xml_to_kdl(value).nodes();\n}\n\nfunction _jik_kdl_nodes ( value ) {\n if ( value instanceof KDLDocument ) {\n return value.nodes();\n }\n return [ value ];\n}\n\nfunction _jik_structural_nodes ( value, String name := "-" ) {\n let nodes := _jik_is_kdl_native(value)\n ? _jik_kdl_nodes(value)\n : _jik_xml_nodes(value);\n\n if ( name \u2261 "-" ) {\n return nodes.length() = 0 ? [ new KDLNode( name: name ) ] : nodes;\n }\n return [ new KDLNode( name: name, children: nodes ) ];\n}\n\nfunction _jik_make_array_node ( value, String name := "-" ) {\n let items := _jik_sorted_array(value);\n let all_literals := true;\n for ( let item in items ) {\n all_literals := false unless _jik_is_literal_native(item);\n }\n\n let annotate := items.length() < 2;\n if ( all_literals ) {\n return new KDLNode(\n name: name,\n type_annotation: annotate ? "array" : null,\n args: items.map( fn item -> _jik_value(item) ),\n );\n }\n\n let children := [];\n for ( let item in items ) {\n for ( let child in _jik_native_to_nodes( item, "-" ) ) {\n children.push(child);\n }\n }\n\n return new KDLNode(\n name: name,\n type_annotation: annotate ? "array" : null,\n children: children,\n );\n}\n\nfunction _jik_object_needs_annotation ( Array pairs, Boolean pairlist ) {\n if ( pairs.length() = 0 ) {\n return true;\n }\n if ( pairlist ) {\n return pairs.all( fn pair -> pair[0] \u2261 "-" );\n }\n return pairs.length() = 1 and pairs[0][0] \u2261 "-"\n and not _jik_is_literal_native( pairs[0][1] );\n}\n\nfunction _jik_make_object_node ( value, String name := "-" ) {\n let pairs := _jik_pairs(value);\n let pairlist := value instanceof PairList;\n let props := new PairList();\n let children := [];\n\n if ( pairlist ) {\n for ( let pair in pairs ) {\n for ( let child in _jik_native_to_nodes( pair[1], pair[0] ) ) {\n children.push(child);\n }\n }\n }\n else {\n for ( let pair in pairs ) {\n if ( _jik_is_literal_native( pair[1] ) ) {\n props.add( pair[0], _jik_value( pair[1] ) );\n }\n else {\n for ( let child in _jik_native_to_nodes( pair[1], pair[0] ) ) {\n children.push(child);\n }\n }\n }\n }\n\n return new KDLNode(\n name: name,\n type_annotation: _jik_object_needs_annotation( pairs, pairlist )\n ? "object"\n : null,\n props: props,\n children: children,\n );\n}\n\nfunction _jik_native_to_node ( value, String name := "-" ) {\n if ( _jik_is_literal_native(value) ) {\n return new KDLNode( name: name, args: [ _jik_value(value) ] );\n }\n if ( _jik_is_array_native(value) ) {\n return _jik_make_array_node( value, name );\n }\n if ( _jik_is_object_native(value) ) {\n return _jik_make_object_node( value, name );\n }\n if ( _jik_is_kdl_native(value) or _jik_is_xml_native(value) ) {\n let nodes := _jik_structural_nodes( value, name );\n return nodes.length() = 1 ? nodes[0] : new KDLNode(\n name: name,\n children: nodes,\n );\n }\n die `Cannot convert ${typeof value} to JSON-in-KDL`;\n}\n\nfunction _jik_native_to_nodes ( value, String name := "-" ) {\n if ( _jik_is_kdl_native(value) or _jik_is_xml_native(value) ) {\n return _jik_structural_nodes( value, name );\n }\n return [ _jik_native_to_node( value, name ) ];\n}\n\nfunction _jik_node_has_only_dash_children ( KDLNode node ) {\n return node.children().all( fn child -> child.name() \u2261 "-" );\n}\n\nfunction _jik_node_kind ( KDLNode node ) {\n let annotation := node.type_annotation();\n if ( annotation \u2261 "array" or annotation \u2261 "object" ) {\n return annotation;\n }\n if ( _jik_has_props(node) ) {\n return "object";\n }\n if ( node.children().length() > 0 ) {\n if ( _jik_node_has_only_dash_children(node) ) {\n return "array";\n }\n return "object";\n }\n if ( node.args().length() = 1 ) {\n return "literal";\n }\n if ( node.args().length() > 1 ) {\n return "array";\n }\n die "Empty JSON-in-KDL node must be annotated as array or object";\n}\n\nfunction _jik_node_to_native;\n\nfunction _jik_array_from_node ( KDLNode node, PairList opts ) {\n die "JSON-in-KDL array node cannot contain properties"\n if _jik_has_props(node);\n die "Empty JSON-in-KDL array node must be annotated as array"\n if node.args().length() = 0\n and node.children().length() = 0\n and node.type_annotation() \u2262 "array";\n die "JSON-in-KDL array child nodes must be named \'-\'"\n unless _jik_node_has_only_dash_children(node);\n\n let out := [];\n for ( let arg in node.args() ) {\n out.push( _jik_literal_value(arg) );\n }\n for ( let child in node.children() ) {\n out.push( _jik_node_to_native( child, opts ) );\n }\n return out;\n}\n\nfunction _jik_store_object_item ( obj, String key, value, Boolean pairlists ) {\n if ( pairlists ) {\n obj.add( key, value );\n return;\n }\n die `Duplicate JSON-in-KDL object key \'${key}\'` if obj.exists(key);\n obj.set( key, value );\n}\n\nfunction _jik_object_from_node ( KDLNode node, PairList opts ) {\n die "JSON-in-KDL object node cannot contain unnamed arguments"\n if node.args().length() > 0;\n die "Empty JSON-in-KDL object node must be annotated as object"\n if not _jik_has_props(node)\n and node.children().length() = 0\n and node.type_annotation() \u2262 "object";\n\n let pairlists := opts.get( "pairlists", false );\n let out := pairlists ? new PairList() : {};\n\n for ( let pair in node.props().to_Array() ) {\n let kv := pair{pair};\n _jik_store_object_item(\n out,\n kv[0],\n _jik_literal_value( kv[1] ),\n pairlists,\n );\n }\n for ( let child in node.children() ) {\n _jik_store_object_item(\n out,\n child.name(),\n _jik_node_to_native( child, opts ),\n pairlists,\n );\n }\n return out;\n}\n\nfunction _jik_node_to_native ( KDLNode node, PairList opts ) {\n let kind := _jik_node_kind(node);\n if ( kind \u2261 "literal" ) {\n die "JSON-in-KDL literal node must contain one argument only"\n if node.args().length() \u2262 1\n or _jik_has_props(node)\n or node.children().length() > 0;\n return _jik_literal_value( node.args()[0] );\n }\n if ( kind \u2261 "array" ) {\n return _jik_array_from_node( node, opts );\n }\n if ( kind \u2261 "object" ) {\n return _jik_object_from_node( node, opts );\n }\n die `Unknown JSON-in-KDL node kind \'${kind}\'`;\n}\n\nfunction kdl_to_json ( value, ... PairList opts ) {\n if ( value instanceof KDLNode ) {\n return _jik_node_to_native( value, opts );\n }\n if ( value instanceof KDLDocument ) {\n die "JSON-in-KDL document must contain exactly one top-level node"\n if value.nodes().length() \u2262 1;\n return _jik_node_to_native( value.nodes()[0], opts );\n }\n die "kdl_to_json expects a KDLDocument or KDLNode";\n}\n\nfunction json_to_kdl ( value ) {\n if ( value instanceof KDLDocument ) {\n return value;\n }\n if ( value instanceof KDLNode ) {\n return value;\n }\n return new KDLDocument( nodes: [ _jik_native_to_node(value) ] );\n}\n';
|
|
41906
|
-
virtualFiles["/modules/std/data/kdl/xml.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/data/kdl/xml - XML-in-KDL structure conversion.\n\n=head1 SYNOPSIS\n\n from std/data/kdl import KDL;\n from std/data/kdl/xml import kdl_to_xml, xml_to_kdl;\n from std/data/xml import XML;\n\n let kdl_doc := ( new KDL() ).decode(\n "a href=\\"http://example.com\\" \\"here\'s a link\\""\n );\n let xml_doc := kdl_to_xml(kdl_doc);\n let roundtrip := xml_to_kdl(xml_doc);\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nThis module implements the XML-in-KDL (XiK) mapping for parsed Zuzu\nobjects. C<kdl_to_xml> accepts a C<KDLDocument> or C<KDLNode> and\nreturns an C<XMLDocument>. C<xml_to_kdl> accepts an C<XMLDocument> or\nC<XMLNode> and returns a C<KDLDocument>.\n\n=head1 EXPORTS\n\n=head2 Functions\n\n=over\n\n=item C<< kdl_to_xml(value) >>\n\nParameters: C<value> is a C<KDLDocument>, C<KDLNode>, or compatible KDL\nvalue. Returns: C<XMLDocument>. Converts XML-in-KDL structures into an\nXML document.\n\n=item C<< xml_to_kdl(value) >>\n\nParameters: C<value> is an C<XMLDocument>, C<XMLNode>, or compatible XML\nvalue. Returns: C<KDLDocument>. Converts XML data into XML-in-KDL nodes.\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/data/kdl/xml >> 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\nfrom std/data/kdl import KDLDocument, KDLNode, KDLValue;\nfrom std/data/xml import XML;\nfrom std/data/xml/escape import escape_xml;\nfrom std/string import index, substr;\n\nfunction _xik_starts_with ( String text, String prefix ) {\n return substr( text, 0, length prefix ) \u2261 prefix;\n}\n\nfunction _xik_has_props ( KDLNode node ) {\n return node.props().to_Array().length() > 0;\n}\n\nfunction _xik_string_value ( value, String context ) {\n die `${context} must be a KDLValue` if not( value instanceof KDLValue );\n die `${context} must be a string value` if not value.is_string();\n return value.native_value();\n}\n\nfunction _xik_single_string_arg ( KDLNode node, String context ) {\n die `${context} must contain one string argument`\n if node.args().length() \u2262 1;\n return _xik_string_value( node.args()[0], context );\n}\n\nfunction _xik_no_props_or_children ( KDLNode node, String context ) {\n die `${context} cannot contain properties` if _xik_has_props(node);\n die `${context} cannot contain children`\n if node.children().length() > 0;\n}\n\nfunction _xik_attr_text ( PairList props ) {\n let out := "";\n for ( let pair in props.to_Array() ) {\n let kv := pair{pair};\n let key := kv[0];\n let value := _xik_string_value(\n kv[1],\n `XML-in-KDL attribute \'${key}\'`,\n );\n out _= ` ${key}="${escape_xml(value)}"`;\n }\n return out;\n}\n\nfunction _xik_comment_text ( KDLNode node ) {\n _xik_no_props_or_children( node, "XML-in-KDL comment node" );\n let text := _xik_single_string_arg( node, "XML-in-KDL comment node" );\n die "XML-in-KDL comment cannot contain \'--\'" if index( text, "--" ) >= 0;\n die "XML-in-KDL comment cannot end with \'-\'"\n if length text > 0 and substr( text, length text - 1, 1 ) \u2261 "-";\n return "<!--" _ text _ "-->";\n}\n\nfunction _xik_doctype_text ( KDLNode node ) {\n _xik_no_props_or_children( node, "XML-in-KDL doctype node" );\n let text := _xik_single_string_arg( node, "XML-in-KDL doctype node" );\n die "XML-in-KDL doctype cannot contain \'>\'" if index( text, ">" ) >= 0;\n return "<!DOCTYPE " _ text _ ">";\n}\n\nfunction _xik_pi_text ( KDLNode node ) {\n die "XML-in-KDL processing instruction cannot contain children"\n if node.children().length() > 0;\n\n let target := substr( node.name(), 1, length node.name() - 1 );\n die "XML-in-KDL processing instruction has an empty target"\n if target \u2261 "";\n\n let content := "";\n if ( node.args().length() > 0 ) {\n _xik_no_props_or_children(\n node,\n "XML-in-KDL unstructured processing instruction",\n );\n content := _xik_single_string_arg(\n node,\n "XML-in-KDL unstructured processing instruction",\n );\n }\n else {\n content := _xik_attr_text( node.props() );\n content := substr( content, 1, length content - 1 )\n if length content > 0;\n }\n\n die "XML-in-KDL processing instruction cannot contain \'?>\'"\n if index( content, "?>" ) >= 0;\n\n return content \u2261 "" ? `<?${target}?>` : `<?${target} ${content}?>`;\n}\n\nfunction _xik_text_node_text ( KDLNode node ) {\n _xik_no_props_or_children( node, "XML-in-KDL text node" );\n return escape_xml( _xik_single_string_arg( node, "XML-in-KDL text node" ) );\n}\n\nfunction _xik_node_text;\n\nfunction _xik_element_text ( KDLNode node ) {\n die `XML-in-KDL element \'${node.name()}\' cannot have type annotations`\n if node.type_annotation() \u2262 null;\n\n let args := node.args();\n let children := node.children();\n die `XML-in-KDL element \'${node.name()}\' cannot contain multiple arguments`\n if args.length() > 1;\n die `XML-in-KDL element \'${node.name()}\' cannot mix text argument and children`\n if args.length() > 0 and children.length() > 0;\n\n let attrs := _xik_attr_text( node.props() );\n if ( args.length() = 0 and children.length() = 0 ) {\n return `<${node.name()}${attrs}/>`;\n }\n\n let body := "";\n if ( args.length() = 1 ) {\n body := escape_xml(\n _xik_string_value(\n args[0],\n `XML-in-KDL element \'${node.name()}\' text argument`,\n ),\n );\n }\n else {\n for ( let child in children ) {\n body _= _xik_node_text(child);\n }\n }\n\n return `<${node.name()}${attrs}>${body}</${node.name()}>`;\n}\n\nfunction _xik_node_text ( KDLNode node ) {\n let name := node.name();\n if ( name \u2261 "-" ) {\n return _xik_text_node_text(node);\n }\n if ( name \u2261 "!" ) {\n return _xik_comment_text(node);\n }\n if ( name \u2261 "!doctype" ) {\n return _xik_doctype_text(node);\n }\n if ( _xik_starts_with( name, "?" ) ) {\n return _xik_pi_text(node);\n }\n return _xik_element_text(node);\n}\n\nfunction _xik_is_element_node ( KDLNode node ) {\n let name := node.name();\n return name \u2262 "-" and name \u2262 "!" and name \u2262 "!doctype"\n and not _xik_starts_with( name, "?" );\n}\n\nfunction kdl_to_xml ( value ) {\n let nodes;\n if ( value instanceof KDLDocument ) {\n nodes := value.nodes();\n }\n else if ( value instanceof KDLNode ) {\n nodes := [ value ];\n }\n else {\n die "kdl_to_xml expects a KDLDocument or KDLNode";\n }\n\n let element_count := 0;\n let out := "";\n for ( let node in nodes ) {\n element_count++ if _xik_is_element_node(node);\n out _= _xik_node_text(node);\n }\n\n die "XML-in-KDL document must contain exactly one top-level element"\n if element_count \u2262 1;\n\n return XML.parse(out);\n}\n\nfunction _xik_kdl_value ( String text ) {\n return new KDLValue( type: "string", value: text );\n}\n\nfunction _xik_xml_attrs_to_kdl ( xml_node ) {\n let props := new PairList();\n for ( let attr in xml_node.attributes() ) {\n props.add( attr.nodeName(), _xik_kdl_value( attr.nodeValue() ) );\n }\n return props;\n}\n\nfunction _xik_xml_text_to_kdl ( xml_node ) {\n return new KDLNode(\n name: "-",\n args: [ _xik_kdl_value( xml_node.nodeValue() ?: "" ) ],\n );\n}\n\nfunction _xik_xml_comment_to_kdl ( xml_node ) {\n return new KDLNode(\n name: "!",\n args: [ _xik_kdl_value( xml_node.nodeValue() ?: "" ) ],\n );\n}\n\nfunction _xik_xml_node_to_kdl;\n\nfunction _xik_xml_pi_to_kdl ( xml_node ) {\n return new KDLNode(\n name: "?" _ xml_node.nodeName(),\n args: [ _xik_kdl_value( xml_node.nodeValue() ?: "" ) ],\n );\n}\n\nfunction _xik_xml_doctype_to_kdl ( xml_node ) {\n return new KDLNode(\n name: "!doctype",\n args: [ _xik_kdl_value( xml_node.nodeValue() ?: xml_node.nodeName() ) ],\n );\n}\n\nfunction _xik_xml_element_to_kdl ( xml_node ) {\n let child_nodes := xml_node.childNodes();\n let text_only := child_nodes.length() > 0;\n let text := "";\n\n for ( let child in child_nodes ) {\n let kind := 0 + child.nodeType();\n if ( kind = 3 or kind = 4 ) {\n text _= child.nodeValue() ?: "";\n }\n else {\n text_only := false;\n }\n }\n\n if ( text_only ) {\n return new KDLNode(\n name: xml_node.nodeName(),\n props: _xik_xml_attrs_to_kdl(xml_node),\n args: [ _xik_kdl_value(text) ],\n );\n }\n\n let children := [];\n for ( let child in child_nodes ) {\n let kdl_child := _xik_xml_node_to_kdl(child);\n children.push(kdl_child) if kdl_child \u2262 null;\n }\n\n return new KDLNode(\n name: xml_node.nodeName(),\n props: _xik_xml_attrs_to_kdl(xml_node),\n children: children,\n );\n}\n\nfunction _xik_xml_node_to_kdl ( xml_node ) {\n switch ( 0 + xml_node.nodeType() ) {\n case 1:\n return _xik_xml_element_to_kdl(xml_node);\n case 3, 4:\n return _xik_xml_text_to_kdl(xml_node);\n case 7:\n return _xik_xml_pi_to_kdl(xml_node);\n case 8:\n return _xik_xml_comment_to_kdl(xml_node);\n case 10:\n return _xik_xml_doctype_to_kdl(xml_node);\n }\n return null;\n}\n\nfunction _xik_xml_document_nodes ( xml_doc ) {\n let raw_nodes := null;\n try {\n raw_nodes := xml_doc.childNodes();\n }\n catch {\n try {\n raw_nodes := xml_doc.findnodes("/node()");\n }\n catch {\n let root := xml_doc.documentElement();\n raw_nodes := root \u2261 null ? [] : [ root ];\n }\n }\n\n if ( raw_nodes.length() = 0 ) {\n let root := xml_doc.documentElement();\n raw_nodes := root \u2261 null ? [] : [ root ];\n }\n\n let nodes := [];\n for ( let raw in raw_nodes ) {\n let node := _xik_xml_node_to_kdl(raw);\n nodes.push(node) if node \u2262 null;\n }\n return nodes;\n}\n\nfunction xml_to_kdl ( value ) {\n let node_type := null;\n try {\n node_type := 0 + value.nodeType();\n }\n catch {\n }\n\n if ( node_type \u2262 null ) {\n if ( node_type = 9 ) {\n return new KDLDocument( nodes: _xik_xml_document_nodes(value) );\n }\n\n let node := _xik_xml_node_to_kdl(value);\n die "xml_to_kdl cannot convert this XML node type" if node \u2261 null;\n return new KDLDocument( nodes: [ node ] );\n }\n\n try {\n value.documentElement();\n return new KDLDocument( nodes: _xik_xml_document_nodes(value) );\n }\n catch {\n die "xml_to_kdl expects an XMLDocument or XMLNode";\n }\n}\n';
|
|
41973
|
+
virtualFiles["/modules/std/data/kdl/xml.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/data/kdl/xml - XML-in-KDL structure conversion.\n\n=head1 SYNOPSIS\n\n from std/data/kdl import KDL;\n from std/data/kdl/xml import kdl_to_xml, xml_to_kdl;\n from std/data/xml import XML;\n\n let kdl_doc := ( new KDL() ).decode(\n "a href=\\"http://example.com\\" \\"here\'s a link\\""\n );\n let xml_doc := kdl_to_xml(kdl_doc);\n let roundtrip := xml_to_kdl(xml_doc);\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nThis module implements the XML-in-KDL (XiK) mapping for parsed Zuzu\nobjects. C<kdl_to_xml> accepts a C<KDLDocument> or C<KDLNode> and\nreturns an C<XMLDocument>. C<xml_to_kdl> accepts an C<XMLDocument> or\nC<XMLNode> and returns a C<KDLDocument>.\n\n=head1 EXPORTS\n\n=head2 Functions\n\n=over\n\n=item C<< kdl_to_xml(value) >>\n\nParameters: C<value> is a C<KDLDocument>, C<KDLNode>, or compatible KDL\nvalue. Returns: C<XMLDocument>. Converts XML-in-KDL structures into an\nXML document.\n\n=item C<< xml_to_kdl(value) >>\n\nParameters: C<value> is an C<XMLDocument>, C<XMLNode>, or compatible XML\nvalue. Returns: C<KDLDocument>. Converts XML data into XML-in-KDL nodes.\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/data/kdl/xml >> 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\nfrom std/data/kdl import KDLDocument, KDLNode, KDLValue;\nfrom std/data/xml import XML;\nfrom std/data/xml/escape import escape_xml;\nfrom std/string import index, substr;\n\nfunction _xik_starts_with ( String text, String prefix ) {\n return substr( text, 0, length prefix ) \u2261 prefix;\n}\n\nfunction _xik_has_props ( KDLNode node ) {\n return node.props().to_Array().length() > 0;\n}\n\nfunction _xik_string_value ( value, String context ) {\n die `${context} must be a KDLValue` if not( value instanceof KDLValue );\n die `${context} must be a string value` if not value.is_string();\n return value.native_value();\n}\n\nfunction _xik_single_string_arg ( KDLNode node, String context ) {\n die `${context} must contain one string argument`\n if node.args().length() \u2262 1;\n return _xik_string_value( node.args()[0], context );\n}\n\nfunction _xik_no_props_or_children ( KDLNode node, String context ) {\n die `${context} cannot contain properties` if _xik_has_props(node);\n die `${context} cannot contain children`\n if node.children().length() > 0;\n}\n\nfunction _xik_attr_text ( PairList props ) {\n let out := "";\n for ( let pair in props.to_Array() ) {\n let kv := pair{pair};\n let key := kv[0];\n let value := _xik_string_value(\n kv[1],\n `XML-in-KDL attribute \'${key}\'`,\n );\n out _= ` ${key}="${escape_xml(value)}"`;\n }\n return out;\n}\n\nfunction _xik_comment_text ( KDLNode node ) {\n _xik_no_props_or_children( node, "XML-in-KDL comment node" );\n let text := _xik_single_string_arg( node, "XML-in-KDL comment node" );\n die "XML-in-KDL comment cannot contain \'--\'" if index( text, "--" ) >= 0;\n die "XML-in-KDL comment cannot end with \'-\'"\n if length text > 0 and substr( text, length text - 1, 1 ) \u2261 "-";\n return "<!--" _ text _ "-->";\n}\n\nfunction _xik_doctype_text ( KDLNode node ) {\n _xik_no_props_or_children( node, "XML-in-KDL doctype node" );\n let text := _xik_single_string_arg( node, "XML-in-KDL doctype node" );\n die "XML-in-KDL doctype cannot contain \'>\'" if index( text, ">" ) >= 0;\n return "<!DOCTYPE " _ text _ ">";\n}\n\nfunction _xik_pi_text ( KDLNode node ) {\n die "XML-in-KDL processing instruction cannot contain children"\n if node.children().length() > 0;\n\n let target := substr( node.name(), 1, length node.name() - 1 );\n die "XML-in-KDL processing instruction has an empty target"\n if target \u2261 "";\n\n let content := "";\n if ( node.args().length() > 0 ) {\n _xik_no_props_or_children(\n node,\n "XML-in-KDL unstructured processing instruction",\n );\n content := _xik_single_string_arg(\n node,\n "XML-in-KDL unstructured processing instruction",\n );\n }\n else {\n content := _xik_attr_text( node.props() );\n content := substr( content, 1, length content - 1 )\n if length content > 0;\n }\n\n die "XML-in-KDL processing instruction cannot contain \'?>\'"\n if index( content, "?>" ) >= 0;\n\n return content \u2261 "" ? `<?${target}?>` : `<?${target} ${content}?>`;\n}\n\nfunction _xik_text_node_text ( KDLNode node ) {\n _xik_no_props_or_children( node, "XML-in-KDL text node" );\n return escape_xml( _xik_single_string_arg( node, "XML-in-KDL text node" ) );\n}\n\nfunction _xik_node_text;\n\nfunction _xik_element_text ( KDLNode node ) {\n die `XML-in-KDL element \'${node.name()}\' cannot have type annotations`\n if node.type_annotation() \u2262 null;\n\n let args := node.args();\n let children := node.children();\n die `XML-in-KDL element \'${node.name()}\' cannot contain multiple arguments`\n if args.length() > 1;\n die `XML-in-KDL element \'${node.name()}\' cannot mix text argument and children`\n if args.length() > 0 and children.length() > 0;\n\n let attrs := _xik_attr_text( node.props() );\n if ( args.length() = 0 and children.length() = 0 ) {\n return `<${node.name()}${attrs}/>`;\n }\n\n let body := "";\n if ( args.length() = 1 ) {\n body := escape_xml(\n _xik_string_value(\n args[0],\n `XML-in-KDL element \'${node.name()}\' text argument`,\n ),\n );\n }\n else {\n for ( let child in children ) {\n body _= _xik_node_text(child);\n }\n }\n\n return `<${node.name()}${attrs}>${body}</${node.name()}>`;\n}\n\nfunction _xik_node_text ( KDLNode node ) {\n let name := node.name();\n if ( name \u2261 "-" ) {\n return _xik_text_node_text(node);\n }\n if ( name \u2261 "!" ) {\n return _xik_comment_text(node);\n }\n if ( name \u2261 "!doctype" ) {\n return _xik_doctype_text(node);\n }\n if ( _xik_starts_with( name, "?" ) ) {\n return _xik_pi_text(node);\n }\n return _xik_element_text(node);\n}\n\nfunction _xik_is_element_node ( KDLNode node ) {\n let name := node.name();\n return name \u2262 "-" and name \u2262 "!" and name \u2262 "!doctype"\n and not _xik_starts_with( name, "?" );\n}\n\nfunction kdl_to_xml ( value ) {\n let nodes;\n if ( value instanceof KDLDocument ) {\n nodes := value.nodes();\n }\n else if ( value instanceof KDLNode ) {\n nodes := [ value ];\n }\n else {\n die "kdl_to_xml expects a KDLDocument or KDLNode";\n }\n\n let element_count := 0;\n let out := "";\n for ( let node in nodes ) {\n element_count++ if _xik_is_element_node(node);\n out _= _xik_node_text(node);\n }\n\n die "XML-in-KDL document must contain exactly one top-level element"\n if element_count \u2262 1;\n\n return XML.parse(out);\n}\n\nfunction _xik_kdl_value ( String text ) {\n return new KDLValue( type: "string", value: text );\n}\n\nfunction _xik_xml_attrs_to_kdl ( xml_node ) {\n let props := new PairList();\n for ( let attr in xml_node.attributes() ) {\n props.add( attr.nodeName(), _xik_kdl_value( attr.nodeValue() ) );\n }\n return props;\n}\n\nfunction _xik_xml_text_to_kdl ( xml_node ) {\n return new KDLNode(\n name: "-",\n args: [ _xik_kdl_value( xml_node.nodeValue() ?: "" ) ],\n );\n}\n\nfunction _xik_xml_comment_to_kdl ( xml_node ) {\n return new KDLNode(\n name: "!",\n args: [ _xik_kdl_value( xml_node.nodeValue() ?: "" ) ],\n );\n}\n\nfunction _xik_xml_node_to_kdl;\n\nfunction _xik_xml_pi_to_kdl ( xml_node ) {\n return new KDLNode(\n name: "?" _ xml_node.nodeName(),\n args: [ _xik_kdl_value( xml_node.nodeValue() ?: "" ) ],\n );\n}\n\nfunction _xik_xml_doctype_to_kdl ( xml_node ) {\n return new KDLNode(\n name: "!doctype",\n args: [ _xik_kdl_value( xml_node.nodeValue() or? xml_node.nodeName() ) ],\n );\n}\n\nfunction _xik_xml_element_to_kdl ( xml_node ) {\n let child_nodes := xml_node.childNodes();\n let text_only := child_nodes.length() > 0;\n let text := "";\n\n for ( let child in child_nodes ) {\n let kind := 0 + child.nodeType();\n if ( kind = 3 or kind = 4 ) {\n text _= child.nodeValue() ?: "";\n }\n else {\n text_only := false;\n }\n }\n\n if ( text_only ) {\n return new KDLNode(\n name: xml_node.nodeName(),\n props: _xik_xml_attrs_to_kdl(xml_node),\n args: [ _xik_kdl_value(text) ],\n );\n }\n\n let children := [];\n for ( let child in child_nodes ) {\n let kdl_child := _xik_xml_node_to_kdl(child);\n children.push(kdl_child) if kdl_child \u2262 null;\n }\n\n return new KDLNode(\n name: xml_node.nodeName(),\n props: _xik_xml_attrs_to_kdl(xml_node),\n children: children,\n );\n}\n\nfunction _xik_xml_node_to_kdl ( xml_node ) {\n switch ( 0 + xml_node.nodeType() ) {\n case 1:\n return _xik_xml_element_to_kdl(xml_node);\n case 3, 4:\n return _xik_xml_text_to_kdl(xml_node);\n case 7:\n return _xik_xml_pi_to_kdl(xml_node);\n case 8:\n return _xik_xml_comment_to_kdl(xml_node);\n case 10:\n return _xik_xml_doctype_to_kdl(xml_node);\n }\n return null;\n}\n\nfunction _xik_xml_document_nodes ( xml_doc ) {\n let raw_nodes := null;\n try {\n raw_nodes := xml_doc.childNodes();\n }\n catch {\n try {\n raw_nodes := xml_doc.findnodes("/node()");\n }\n catch {\n let root := xml_doc.documentElement();\n raw_nodes := root \u2261 null ? [] : [ root ];\n }\n }\n\n if ( raw_nodes.length() = 0 ) {\n let root := xml_doc.documentElement();\n raw_nodes := root \u2261 null ? [] : [ root ];\n }\n\n let nodes := [];\n for ( let raw in raw_nodes ) {\n let node := _xik_xml_node_to_kdl(raw);\n nodes.push(node) if node \u2262 null;\n }\n return nodes;\n}\n\nfunction xml_to_kdl ( value ) {\n let node_type := null;\n try {\n node_type := 0 + value.nodeType();\n }\n catch {\n }\n\n if ( node_type \u2262 null ) {\n if ( node_type = 9 ) {\n return new KDLDocument( nodes: _xik_xml_document_nodes(value) );\n }\n\n let node := _xik_xml_node_to_kdl(value);\n die "xml_to_kdl cannot convert this XML node type" if node \u2261 null;\n return new KDLDocument( nodes: [ node ] );\n }\n\n try {\n value.documentElement();\n return new KDLDocument( nodes: _xik_xml_document_nodes(value) );\n }\n catch {\n die "xml_to_kdl expects an XMLDocument or XMLNode";\n }\n}\n';
|
|
41907
41974
|
virtualFiles["/modules/std/path/jsonpointer.zzm"] = `=encoding utf8
|
|
41908
41975
|
|
|
41909
41976
|
=head1 NAME
|
|
@@ -46459,8 +46526,8 @@ function unescape_xml ( value ) {
|
|
|
46459
46526
|
`;
|
|
46460
46527
|
virtualFiles["/modules/std/path/simple.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/path/simple - Tiny JSONPath/XPath-like traversal helper.\n\n=head1 SYNOPSIS\n\n from std/path/simple import SimplePath;\n\n let data := {\n store: {\n books: [\n { author: "Nigel Rees" },\n { author: "J. R. R. Tolkien" },\n ],\n },\n };\n\n let p := new SimplePath( path: "store.books[*].author" );\n for ( let name in p.query(data) ) {\n say name;\n }\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nC<SimplePath> implements only a tiny subset of path traversal:\n\n=over\n\n=item * C<.something>\n\nDict/PairList lookup by key.\n\n=item * C<.*>\n\nDict/PairList wildcard that yields all values.\n\n=item * C<[1]>\n\nArray lookup by numeric index.\n\n=item * C<[-1]>\n\nNegative indexes count from the end of arrays.\n\n=item * C<[*]>\n\nArray/Bag/Set wildcard that yields all items.\n\n=back\n\nNo other syntax is supported.\n\nThe public API intentionally mirrors key C<std/path/z> methods:\nC<get>, C<select>, C<query>, C<first>, C<exists>,\nC<expression>, C<assign_first>, C<assign_all>, C<assign_maybe>,\nC<ref_first>, C<ref_all>, and C<ref_maybe>.\n\nThe path operators C<@>, C<@@>, and C<@?> can be set to use this module\nin a lexical scope:\n\n from std/path/simple import SimplePath;\n SimplePath.use();\n\n=head1 EXPORTS\n\n=head2 Classes\n\n=over\n\n=item C<< SimplePath({ path: String }) >>\n\nConstructs a simple path selector. Returns: C<SimplePath>.\n\n=over\n\n=item C<< SimplePath.use() >>\n\nParameters: none. Returns: C<null>. Makes this path class the lexical\nimplementation for C<@>, C<@@>, and C<@?>.\n\n=item C<< path.expression() >>\n\nParameters: none. Returns: C<String>. Returns the original path\nexpression.\n\n=item C<< path.get(value) >>, C<< path.select(value) >>, C<< path.query(value) >>\n\nParameters: C<value> is the query root. Returns: C<Array>. Evaluates the\npath and returns selected values.\n\n=item C<< path.first(value, fallback) >>\n\nParameters: C<value> is the query root and C<fallback> is returned when\nthere is no match. Returns: value. Returns the first selected value.\n\n=item C<< path.exists(value) >>\n\nParameters: C<value> is the query root. Returns: C<Boolean>. Returns\ntrue when the path selects at least one value.\n\n=item C<< path.assign_first(target, value, op := ":=", weak := false) >>\n\nParameters: C<target> is the query root, C<value> is the assignment\nvalue, C<op> is an assignment operator, and C<weak> is accepted for path\nAPI compatibility. Returns: value. Updates the first selected location.\n\n=item C<< path.assign_all(target, value, op := ":=", weak := false) >>\n\nParameters: same as C<assign_first>. Returns: value. Updates every\nselected location.\n\n=item C<< path.assign_maybe(target, value, op := ":=", weak := false) >>\n\nParameters: same as C<assign_first>. Returns: C<Boolean>. Updates the\nfirst selected location when one exists.\n\n=item C<< path.ref_first(target) >>\n\nParameters: C<target> is the query root. Returns: C<Function>. Returns a\nreference-like getter/setter for the first selected location.\n\n=item C<< path.ref_all(target) >>\n\nParameters: C<target> is the query root. Returns: C<Array>. Returns\nreference-like getter/setters for all selected locations.\n\n=item C<< path.ref_maybe(target) >>\n\nParameters: C<target> is the query root. Returns: C<Function> or\nC<null>. Returns a reference-like getter/setter for the first selected\nlocation when one exists.\n\n=back\n\n=back\n\n=cut\n\nfrom std/string import substr;\n\n\nclass SimplePath {\n let String path;\n let Array _steps := [];\n\n static method use () {\n from std/internals import setupperprop;\n setupperprop( 1, "paths", self );\n }\n\n method __build__ () {\n _steps := self._parse(path);\n }\n\n method expression () {\n return path;\n }\n\n method _parse ( String raw ) {\n let text := raw;\n let steps := [];\n let i := 0;\n\n while ( i < length text ) {\n let ch := substr( text, i, 1 );\n if ( ch \u2261 "." ) {\n i++;\n die "SimplePath parse error: expected name or * after \'.\'"\n if i >= length text;\n let after_dot := substr( text, i, 1 );\n if ( after_dot \u2261 "*" ) {\n steps.push( { kind: "dict_wildcard" } );\n i++;\n next;\n }\n let start := i;\n while ( i < length text ) {\n let c := substr( text, i, 1 );\n last if c \u2261 "." or c \u2261 "[" or c \u2261 "]";\n i++;\n }\n let key := substr( text, start, i - start );\n die "SimplePath parse error: empty key"\n if key \u2261 "";\n steps.push( { kind: "key", value: key } );\n next;\n }\n\n if ( ch \u2261 "[" ) {\n let close := i + 1;\n while ( close < length text and substr( text, close, 1 ) \u2262 "]" ) {\n close++;\n }\n die "SimplePath parse error: missing closing \']\'"\n if close >= length text;\n let inner := substr( text, i + 1, close - i - 1 );\n if ( inner \u2261 "*" ) {\n steps.push( { kind: "list_wildcard" } );\n }\n else if ( inner ~ /^-?[0-9]+$/ ) {\n steps.push( { kind: "index", value: int(inner) } );\n }\n else {\n die `SimplePath parse error: unsupported bracket token \'[${inner}]\'`;\n }\n i := close + 1;\n next;\n }\n\n let start := i;\n while ( i < length text ) {\n let c := substr( text, i, 1 );\n last if c \u2261 "." or c \u2261 "[" or c \u2261 "]";\n i++;\n }\n let key := substr( text, start, i - start );\n die `SimplePath parse error: unexpected token \'${ch}\'`\n if key \u2261 "";\n steps.push( { kind: "key", value: key } );\n }\n\n return steps;\n }\n\n method _node ( value, parent, key ) {\n return {\n value: value,\n parent: parent,\n key: key,\n };\n }\n\n method _evaluate_nodes ( value ) {\n let current := [ self._node( value, null, null ) ];\n\n for ( let step in _steps ) {\n let next_nodes := [];\n\n for ( let node in current ) {\n let v := node{value};\n\n if ( step{kind} \u2261 "key" ) {\n if ( v instanceof Dict and v.exists( step{value} ) ) {\n next_nodes.push( self._node( v.get( step{value} ), node, step{value} ) );\n }\n else if ( v instanceof PairList ) {\n for ( let item in v.get_all( step{value} ) ) {\n next_nodes.push( self._node( item, node, step{value} ) );\n }\n }\n next;\n }\n\n if ( step{kind} \u2261 "dict_wildcard" ) {\n if ( v instanceof Dict ) {\n for ( let k in v.keys() ) {\n next_nodes.push( self._node( v.get(k), node, k ) );\n }\n }\n else if ( v instanceof PairList ) {\n for ( let pair in v.to_Array() ) {\n next_nodes.push( self._node( pair.value, node, pair.key ) );\n }\n }\n next;\n }\n\n if ( step{kind} \u2261 "index" ) {\n if ( v instanceof Array ) {\n let idx := step{value};\n if ( idx < 0 ) {\n idx := v.length() + idx;\n }\n if ( idx >= 0 and idx < v.length() ) {\n next_nodes.push( self._node( v[idx], node, idx ) );\n }\n }\n next;\n }\n\n if ( step{kind} \u2261 "list_wildcard" ) {\n if ( v instanceof Array ) {\n let i := 0;\n while ( i < v.length() ) {\n next_nodes.push( self._node( v[i], node, i ) );\n i++;\n }\n }\n else if ( v instanceof Bag or v instanceof Set ) {\n for ( let item in v.to_Array() ) {\n next_nodes.push( self._node( item, node, "*" ) );\n }\n }\n next;\n }\n }\n\n current := next_nodes;\n }\n\n return current;\n }\n\n method _evaluate ( value ) {\n return self._evaluate_nodes(value).map( fn n -> n{value} );\n }\n\n method get ( value ) {\n return self._evaluate(value);\n }\n\n method select ( value ) {\n return self._evaluate(value);\n }\n\n method query ( value ) {\n return self._evaluate(value);\n }\n\n method first ( value, fallback ) {\n let out := self._evaluate(value);\n return out.length() = 0 ? fallback : out[0];\n }\n\n method exists ( value ) {\n return self._evaluate(value).length() > 0;\n }\n\n method _assign_node ( Dict node, value ) {\n let parent := node{parent};\n die "SimplePath assignment target has no parent node"\n if parent \u2261 null;\n\n let container := parent{value};\n let key := node{key};\n\n if ( container instanceof Array ) {\n die "SimplePath assignment expects numeric array index"\n if not( key instanceof Number );\n container[key] := value;\n return value;\n }\n\n if ( container instanceof Dict ) {\n die "SimplePath assignment expects string dict key"\n if not( key instanceof String );\n container{(key)} := value;\n return value;\n }\n\n if ( container instanceof PairList ) {\n die "SimplePath assignment expects string pairlist key"\n if not( key instanceof String );\n container.set( key, value );\n return value;\n }\n\n die `SimplePath assignment target container \'${typeof container}\' is not assignable`;\n }\n\n method _ref_for_node ( Dict node ) {\n let parent := node{parent};\n die "SimplePath assignment target has no parent node"\n if parent \u2261 null;\n\n let container := parent{value};\n let key := node{key};\n\n if ( container instanceof Array ) {\n die "SimplePath assignment expects numeric array index"\n if not( key instanceof Number );\n return \\ container[key];\n }\n\n if ( container instanceof Dict ) {\n die "SimplePath assignment expects string dict key"\n if not( key instanceof String );\n return \\ container{(key)};\n }\n\n if ( container instanceof PairList ) {\n die "SimplePath assignment expects string pairlist key"\n if not( key instanceof String );\n return \\ container{(key)};\n }\n\n die `SimplePath assignment target container \'${typeof container}\' is not assignable`;\n }\n\n method _apply_assignment_ref ( ref, value, op := ":=", weak := false ) {\n die "SimplePath weak assignment is not supported"\n if weak;\n\n if ( op \u2261 ":=" ) {\n return ref(value);\n }\n\n let current := ref();\n\n if ( op \u2261 "+=" ) {\n current += value;\n }\n else if ( op \u2261 "-=" ) {\n current -= value;\n }\n else if ( op \u2261 "*=" or op \u2261 "\xD7=" ) {\n current *= value;\n }\n else if ( op \u2261 "/=" or op \u2261 "\xF7=" ) {\n current /= value;\n }\n else if ( op \u2261 "**=" ) {\n current **= value;\n }\n else if ( op \u2261 "_=" ) {\n current _= value;\n }\n else if ( op \u2261 "?:=" ) {\n current ?:= value;\n }\n else if ( op \u2261 "~=" ) {\n current ~= value[0] -> value[1](m);\n }\n else {\n die `Unsupported path assignment operator \'${op}\'`;\n }\n\n ref(current);\n return current;\n }\n\n method _assign_all_result ( value, op, last_result ) {\n return op \u2261 "~=" ? last_result : value;\n }\n\n method assign_first ( target, value, op := ":=", weak := false ) {\n let nodes := self._evaluate_nodes(target);\n die "SimplePath assignment found no matches"\n if nodes.length() \u2261 0;\n return self._apply_assignment_ref(\n self._ref_for_node( nodes[0] ),\n value,\n op,\n weak,\n );\n }\n\n method assign_all ( target, value, op := ":=", weak := false ) {\n let nodes := self._evaluate_nodes(target);\n if ( nodes.length() \u2261 0 ) {\n return self._assign_all_result( value, op, value );\n }\n\n let i := 0;\n let last_result := value;\n while ( i < nodes.length() ) {\n last_result := self._apply_assignment_ref(\n self._ref_for_node( nodes[i] ),\n value,\n op,\n weak,\n );\n i++;\n }\n\n return self._assign_all_result( value, op, last_result );\n }\n\n method assign_maybe ( target, value, op := ":=", weak := false ) {\n let nodes := self._evaluate_nodes(target);\n if ( nodes.length() \u2261 0 ) {\n return false;\n }\n\n self._apply_assignment_ref(\n self._ref_for_node( nodes[0] ),\n value,\n op,\n weak,\n );\n return true;\n }\n\n method ref_first ( target ) {\n let nodes := self._evaluate_nodes(target);\n die "SimplePath assignment found no matches"\n if nodes.length() \u2261 0;\n return self._ref_for_node( nodes[0] );\n }\n\n method ref_all ( target ) {\n let nodes := self._evaluate_nodes(target);\n let out := [];\n for ( let node in nodes ) {\n out.push( self._ref_for_node(node) );\n }\n return out;\n }\n\n method ref_maybe ( target ) {\n let nodes := self._evaluate_nodes(target);\n return nodes.length() \u2261 0 ? null : self._ref_for_node( nodes[0] );\n }\n}\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/path/simple >> 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';
|
|
46461
46528
|
virtualFiles["/modules/std/path/z.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/path/z - Pure Zuzu implementation of ZPath selectors.\n\n=head1 SYNOPSIS\n\n from std/path/z import ZPath;\n from std/time import Time;\n\n let data := {\n users: [\n { name: "Ada", age: 32, updated: new Time() },\n { name: "Bob", age: 27 },\n ],\n };\n\n let names := query( data, "/users/*/name" );\n let zp := new ZPath( path: "/users/#0/name" );\n say( zp.first( data, "n/a" ) );\n say( exists( data, "/users/#9/name" ) );\n say( first( data, "/users/#0/updated/@year" ) );\n say( zp.assign_first( data, "Adele" ) );\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nNative (pure-Zuzu) path traversal for structured values.\n\n=head1 EXPORTS\n\n=head2 Classes\n\n=over\n\n=item C<< ZPath({ path: String, ast? }) >>\n\nConstructs a compiled ZPath selector. Returns: C<ZPath>.\n\n=over\n\n=item C<< ZPath.use() >>\n\nParameters: none. Returns: C<null>. Makes this path class the lexical\nimplementation for C<@>, C<@@>, and C<@?>.\n\n=item C<< path.get_evaluator() >>\n\nParameters: none. Returns: C<Evaluator>. Returns the evaluator used for\nthis path.\n\n=item C<< path.evaluate(raw, meta := {}) >>\n\nParameters: C<raw> is the query root and C<meta> is optional evaluation\nmetadata. Returns: C<Array>. Evaluates the path and returns selected\nnodes.\n\n=item C<< path.get(raw) >>, C<< path.select(raw) >>, C<< path.query(raw) >>\n\nParameters: C<raw> is the query root. Returns: C<Array>. Evaluates the\npath and returns selected primitive values.\n\n=item C<< path.first(raw, fallback?) >>\n\nParameters: C<raw> is the query root and C<fallback> is optional.\nReturns: value. Returns the first selected value or C<fallback>/C<null>.\n\n=item C<< path.exists(raw) >>\n\nParameters: C<raw> is the query root. Returns: C<Boolean>. Returns true\nwhen the path selects at least one value.\n\n=item C<< path.assign_first(raw, value, op := ":=", weak := false) >>\n\nParameters: C<raw> is the query root, C<value> is the assignment value,\nC<op> is an assignment operator, and C<weak> requests weak assignment.\nReturns: value. Updates the first selected node or throws if none match.\n\n=item C<< path.assign_all(raw, value, op := ":=", weak := false) >>\n\nParameters: same as C<assign_first>. Returns: value. Updates every\nselected node.\n\n=item C<< path.assign_maybe(raw, value, op := ":=", weak := false) >>\n\nParameters: same as C<assign_first>. Returns: C<Boolean>. Updates the\nfirst selected node when one exists.\n\n=item C<< path.ref_first(raw) >>\n\nParameters: C<raw> is the query root. Returns: C<Function>. Returns a\nreference-like getter/setter for the first selected node.\n\n=item C<< path.ref_all(raw) >>\n\nParameters: C<raw> is the query root. Returns: C<Array>. Returns\nreference-like getter/setters for all selected nodes.\n\n=item C<< path.ref_maybe(raw) >>\n\nParameters: C<raw> is the query root. Returns: C<Function> or C<null>.\nReturns a reference-like getter/setter for the first selected node when\none exists.\n\n=back\n\n=back\n\n=head1 USE WITH PATH OPERATORS\n\nThe path operators C<@>, C<@@>, and C<@?> can be set to use this module\nin a lexical scope.\n\n from std/path/z import ZPath;\n\n function find_usernames (data) {\n ZPath.use();\n return data @@ "/users/*/name";\n }\n\nHowever, for repeatedly used paths it may be more efficient to compile the\npath once and use many times:\n\n let _usernames_zpath;\n function find_usernames (data) {\n from std/path/z import ZPath;\n _usernames_zpath ?:= new ZPath( path: "/users/*/name" );\n return data @@ _usernames_zpath;\n }\n\n=head1 SUPPORTED TYPES\n\n=over\n\n=item B<Null>, B<Boolean>, B<Number>, B<String>, B<BinaryString>, B<Regexp>\n\nTreated as terminal nodes. These objects cannot have child objects.\n\n=item B<Array>\n\nArray items can be indexed by number.\n\n=item B<Bag>, B<Set>\n\nItems cannot be indexed by number, but can be returned by "*".\n\n=item B<Dict>\n\nValues are named by their key.\n\n=item B<PairList>\n\nPairs can be indexed by number, named by their key, or use a combination of\nboth.\n\n {{ foo: 11, bar: -1, foo: 22, foo: 33 }}\n\nC<< /#2 >> (0-based index) will retrieve C<< foo: 22 >>.\nC<< /foo >> will retrieve C<< foo: 11 >>, C<< foo: 22 >>, and C<< foo: 33 >>.\nC<< /foo#2 >> (0-based index on just values with key "foo") will retrieve C<< foo: 33 >>.\n\nNote that rather than just retrieving the value, a Pair object is retrieved.\nThe selected Pair exposes C<< @key >> and C<< @value >> attributes. Path\nassignment to a selected Pair, or to its C<< @value >> attribute, replaces\nthat pair entry\'s value while preserving pair order and duplicate keys.\n\n=item B<< Pair >>\n\nPair objects do not have child objects but do have C<< @key >> and\nC<< @value >> attributes.\n\n let pairlist := {{ foo: 11, bar: -1, foo: 22, foo: 33 }};\n say( first( pairlist, "/#2/@key" ) ); // "foo"\n say( first( pairlist, "/#2/@value" ) ); // 22\n\n=item B<< Time >>\n\nTime is treated as a terminal node with attributes C<< @year >>,\nC<< @month >>, C<< @day >>, C<< @hour >>, C<< @min >>, and C<< @sec >>.\n\nSee C<< std/time >>.\n\n=item B<< Path >>\n\nPaths representing files are treated as terminal nodes with attributes\ncorresponding to the values from the C<stat> system call: C<< @dev >>,\nC<< @ino >>, C<< @mode >>, C<< @nlink >>, C<< @uid >>, C<< @gid >>,\nC<< @rdev >>, C<< @size >>, C<< @atime >>, C<< @mtime >>, C<< @ctime >>,\nC<< @blksize >>, and C<< @blocks >>.\n\nSee C<< std/io >>.\n\n=item B<< XMLDocument >>, B<< XMLNode >>, etc.\n\nAre treated roughly how the ZPath specification suggests.\n\n /html/body/table/tbody/tr // all rows in the tbody\n /html/body/table/tbody/tr#0 // the first row in the tbody\n /html/body/table/tbody/#0 // the child element in the tbody\n /html/body/table[@id] // all tables that have an id attribute\n\nSee C<< std/data/xml >>.\n\n=back\n\n=head1 SEE ALSO\n\nSpecification: L<https://zpath.me>.\n\nZPath specification: L<https://zpath.me>.\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/path/z >> 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\nfrom std/path/z/parser import Parser;\nfrom std/path/z/evaluate import Evaluator;\nfrom std/path/z/context import Ctx;\n\nlet _cache;\ndo {\n from std/cache/lru try import Cache;\n if ( Cache ) {\n _cache := new Cache( capacity: 16 );\n }\n};\n\nclass ZPath {\n let String path;\n let ast;\n let ev;\n\n static method use () {\n from std/internals import setupperprop;\n setupperprop( 1, "paths", self );\n }\n\n method __build__ () {\n ev := self.get_evaluator;\n const p := new Parser( allowed_operators: ev.operator_definitions() );\n ast ?:= _cache\n ? _cache.get( path, fn x \u2192 p.parse_top_level_terms(x) )\n : p.parse_top_level_terms(path);\n }\n\n method get_evaluator () {\n return new Evaluator();\n }\n\n method evaluate ( raw, meta := {} ) {\n\n meta.set( "level", 0 ) unless meta.defined( "level" );\n const ctx := new Ctx(\n root: raw,\n nodeset: meta.get( "nodeset", null ),\n parentset: meta.get( "parentset", null ),\n meta: meta,\n );\n\n const short_circuit := ( meta.get( "want", "all" ) in [ "first", "exists" ] );\n\n let results := [];\n for ( let term in ast ) {\n for ( let node in ev.eval_expr( term, ctx ) ) {\n let next_node := ev.maybe_apply_action( node, ctx );\n results.push(next_node);\n return results if short_circuit;\n }\n }\n\n return results;\n }\n\n method get ( raw ) {\n return self.evaluate(raw).map( fn r \u2192 r.primitive_value );\n }\n\n method select ( raw ) {\n return self.evaluate(raw).map( fn r \u2192 r.primitive_value );\n }\n\n method query ( raw ) {\n return self.evaluate(raw).map( fn r \u2192 r.primitive_value );\n }\n\n method first ( raw, fallback? ) {\n let got := self.evaluate( raw, { want: "first" } );\n return got.empty ? fallback : got[0].primitive_value;\n }\n\n method exists ( raw ) {\n let got := self.evaluate( raw, { want: "exists" } );\n return not got.empty;\n }\n\n method _apply_assignment_ref ( ref, value, op := ":=", weak := false ) {\n if ( op \u2261 ":=" ) {\n return weak ? ref( value, true ) : ref(value);\n }\n\n let current := ref();\n\n if ( op \u2261 "+=" ) {\n current += value;\n }\n else if ( op \u2261 "-=" ) {\n current -= value;\n }\n else if ( op \u2261 "*=" or op \u2261 "\xD7=" ) {\n current *= value;\n }\n else if ( op \u2261 "/=" or op \u2261 "\xF7=" ) {\n current /= value;\n }\n else if ( op \u2261 "**=" ) {\n current **= value;\n }\n else if ( op \u2261 "_=" ) {\n current _= value;\n }\n else if ( op \u2261 "?:=" ) {\n current ?:= value;\n }\n else if ( op \u2261 "~=" ) {\n current ~= value[0] -> value[1](m);\n }\n else {\n die `Unsupported path assignment operator \'${op}\'`;\n }\n\n ref(current);\n return current;\n }\n\n method _assign_all_result ( value, op, last_result ) {\n return op \u2261 "~=" ? last_result : value;\n }\n\n method assign_first ( raw, value, op := ":=", weak := false ) {\n let got := self.evaluate( raw, { want: "first" } );\n die "Path assignment (@) found no matches" if got.empty;\n return self._apply_assignment_ref(\n got[0].ref(),\n value,\n op,\n weak,\n );\n }\n\n method assign_all ( raw, value, op := ":=", weak := false ) {\n let got := self.evaluate(raw);\n if ( got.empty ) {\n return self._assign_all_result( value, op, value );\n }\n\n let last_result := value;\n for ( let node in got ) {\n last_result := self._apply_assignment_ref(\n node.ref(),\n value,\n op,\n weak,\n );\n }\n\n return self._assign_all_result( value, op, last_result );\n }\n\n method assign_maybe ( raw, value, op := ":=", weak := false ) {\n let got := self.evaluate( raw, { want: "first" } );\n if ( got.empty ) {\n return false;\n }\n\n self._apply_assignment_ref( got[0].ref(), value, op, weak );\n return true;\n }\n\n method ref_first ( raw ) {\n let got := self.evaluate( raw, { want: "first" } );\n die "Path assignment (@) found no matches" if got.empty;\n return got[0].ref();\n }\n\n method ref_all ( raw ) {\n return self.evaluate(raw).map( fn n \u2192 n.ref );\n }\n\n method ref_maybe ( raw ) {\n let got := self.evaluate( raw, { want: "first" } );\n return got.empty ? null : got[0].ref();\n }\n}\n';
|
|
46462
|
-
virtualFiles["/modules/std/path/z/context.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/path/z/context - Evaluation context used by std/path/z.\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nThis module provides the pure-Zuzu evaluation context used by ZPath.\n\n=head1 EXPORTS\n\n=head2 Classes\n\n=over\n\n=item C<< Ctx({ root, nodeset?, parentset?, meta? }) >>\n\nConstructs a ZPath evaluation context. Returns: C<Ctx>. Wraps C<root>\nas a C<std/path/z/node> C<Node> and stores the active node sets.\n\n=over\n\n=item C<< ctx.with_nodeset(ns, ps) >>\n\nParameters: C<ns> is the next node set and C<ps> is the parent set.\nReturns: C<Ctx>. Returns a copy of the context with different node\nsets.\n\n=item C<< ctx.nested(extras?) >>\n\nParameters: C<extras> is optional metadata. Returns: C<Ctx>. Returns a\nnested context with incremented metadata level.\n\n=item C<< ctx.root() >>\n\nParameters: none. Returns: C<Node>. Returns the root node.\n\n=item C<< ctx.nodeset() >>\n\nParameters: none. Returns: C<Array>. Returns the current node set.\n\n=item C<< ctx.parentset() >>\n\nParameters: none. Returns: C<Array> or C<null>. Returns the parent node\nset.\n\n=item C<< ctx.meta() >>\n\nParameters: none. Returns: C<Dict>. Returns context metadata.\n\n=back\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/path/z/context >> 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\nfrom std/path/z/node import Node;\n\nclass Ctx {\n let root := null;\n let nodeset := null;\n let parentset := null;\n let meta := null;\n\n method __build__ () {\n if ( not ( root instanceof Node ) ) {\n let root_obj := root;\n let node_type := null;\n\n try {\n node_type := int( "" _ root_obj.nodeType() );\n }\n catch {\n }\n\n let xml_document := node_type = 9;\n if ( not xml_document and root_obj can documentElement ) {\n try {\n root_obj.documentElement();\n xml_document := true;\n }\n catch {\n }\n }\n\n if ( xml_document ) {\n try {\n let de := root_obj.documentElement();\n root_obj := de if de \u2262 null;\n }\n catch {\n }\n }\n\n root := Node.wrap( root_obj );\n }\n\n nodeset ?:= [ root ];\n meta ?:= { level: 0 };\n }\n\n method with_nodeset ( ns, ps ) {\n return new Ctx(\n root: root,\n nodeset: ns,\n parentset: ps,\n meta: meta,\n );\n }\n\n method nested ( extras? ) {\n let extra_meta := extras ?: {};\n\n let next_meta := {\n level: meta.get( "level", 0 ) + 1,\n };\n\n for ( let pair in extra_meta.enumerate ) {\n next_meta.add( pair );\n }\n\n return new Ctx(\n root: root,\n nodeset: nodeset,\n parentset: parentset,\n meta: next_meta,\n );\n }\n\n method root () { return root; }\n method nodeset () { return nodeset; }\n method parentset () { return parentset; }\n method meta () { return meta; }\n}\n';
|
|
46463
|
-
virtualFiles["/modules/std/path/z/evaluate.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/path/z/evaluate - Pure Zuzu evaluator for ZPath expressions.\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nThis module provides the pure-Zuzu evaluator used by ZPath.\n\n=head1 EXPORTS\n\n=head2 Classes\n\n=over\n\n=item C<Evaluator>\n\nPure-Zuzu evaluator for parsed ZPath AST values.\n\n=over\n\n=item C<< evaluator.operator_definitions() >>\n\nParameters: none. Returns: C<Array>. Returns the active operator\ndefinition table.\n\n=item C<< evaluator.function_definitions() >>\n\nParameters: none. Returns: C<Array>. Returns the active function\ndefinition table.\n\n=item C<< evaluator.eval_expr_wrap(ast, ctx) >>\n\nParameters: C<ast> is an AST node and C<ctx> is a C<Ctx>. Returns:\nC<Array>. Evaluates with optional debug dumping.\n\n=item C<< evaluator.eval_expr(ast, ctx) >>\n\nParameters: C<ast> is an AST node and C<ctx> is a C<Ctx>. Returns:\nC<Array>. Evaluates an expression to a node set.\n\n=item C<< evaluator.nested_ctx(ctx, ... PairList extras) >>\n\nParameters: C<ctx> is a C<Ctx> or C<null> and C<extras> are metadata.\nReturns: C<Ctx> or C<null>. Creates a nested evaluation context.\n\n=item C<< evaluator.eval_binop(ast, ctx) >>, C<< evaluator.eval_unop(ast, ctx) >>\n\nParameters: C<ast> is an operator AST node and C<ctx> is a C<Ctx>.\nReturns: C<Array>. Evaluates a binary or unary operator.\n\n=item C<< evaluator.eval_path(path_ast, ctx) >>\n\nParameters: C<path_ast> is a path AST node and C<ctx> is a C<Ctx>.\nReturns: C<Array>. Evaluates a path expression to selected nodes.\n\n=item C<< evaluator.maybe_apply_action(node, ctx) >>\n\nParameters: C<node> is a C<Node> and C<ctx> is a C<Ctx>. Returns:\nC<Node>. Applies any pending path action.\n\n=item C<< evaluator.eval_fn(fn_ast, ctx) >>\n\nParameters: C<fn_ast> is a function-call AST node and C<ctx> is a\nC<Ctx>. Returns: C<Array>. Evaluates a ZPath function call.\n\n=item C<< evaluator.string_replace(string, pattern, replacement) >>\n\nParameters: all arguments are strings or regex-like values. Returns:\nC<String>. Applies ZPath string replacement.\n\n=item C<< evaluator.dedup_nodes(nodes) >>\n\nParameters: C<nodes> is an array of nodes. Returns: C<Array>. Removes\nduplicate nodes while preserving order.\n\n=item C<< evaluator.truthy(n) >>\n\nParameters: C<n> is a node or value. Returns: C<Boolean>. Applies ZPath\ntruthiness.\n\n=item C<< evaluator.to_number(n) >>, C<< evaluator.to_string(n) >>\n\nParameters: C<n> is a node or value. Returns: C<Number> or C<String>.\nCoerces a ZPath value.\n\n=item C<< evaluator.equals(a, b) >>\n\nParameters: C<a> and C<b> are nodes or values. Returns: C<Boolean>.\nCompares two ZPath values.\n\n=back\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/path/z/evaluate >> 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\nfrom std/string import replace, index, substr;\nfrom std/path/z/node import Node;\nfrom std/path/z/functions import STANDARD_FUNCTIONS;\nfrom std/path/z/operators import STANDARD_OPERATORS;\n\nlet _do_dump := false;\n\nclass Evaluator {\n let _operator_definitions;\n let _function_definitions;\n\n let \u03B5 := 0.000000001;\n\n method operator_definitions () {\n _operator_definitions ?:= STANDARD_OPERATORS;\n return _operator_definitions;\n }\n\n method function_definitions () {\n _function_definitions ?:= STANDARD_FUNCTIONS;\n return _function_definitions;\n }\n\n method eval_expr_wrap ( ast, ctx ) {\n from std/dump import Dumper;\n let got := self._real_eval_expr ( ast, ctx );\n say `AST ${Dumper.dump(ast)} CTX ${Dumper.dump(ctx)} \u21D2 GOT ${Dumper.dump(got)}` if _do_dump;\n return got;\n }\n\n method eval_expr ( ast, ctx ) {\n\n switch ( ast{t} : eq ) {\n case "num":\n return [ Node.wrap( ast{v} ) ];\n case "str":\n return [ Node.wrap( ast{v} ) ];\n case "path":\n return self.eval_path( ast, ctx );\n case "fn":\n return self.eval_fn( ast, ctx );\n case "un":\n return self.eval_unop( ast, ctx );\n case "bin":\n return self.eval_binop( ast, ctx );\n case "ternary":\n let c := self.eval_expr( ast{c}, ctx );\n let ab := self.truthy( c.get(0) ) ? "a" : "b";\n return self.eval_expr( ast{(ab)}, ctx );\n case "elvis":\n let left := self.eval_expr( ast{c}, ctx );\n return left if self.truthy( left.get(0) );\n return self.eval_expr( ast{b}, ctx );\n default:\n die "Panic! Unknown AST node type!";\n }\n }\n\n method nested_ctx ( ctx, ... PairList extras ) {\n return null if ctx \u2261 null;\n return ctx.nested( extras );\n }\n\n method _hold_parent ( node, parent ) {\n if ( node \u2262 null and parent \u2262 null ) {\n node.hold_parent(parent);\n }\n return node;\n }\n\n method _children ( node ) {\n let out := [];\n for ( let child in node.children() ) {\n out.push( self._hold_parent( child, node ) );\n }\n return out;\n }\n\n method _attributes ( node ) {\n let out := [];\n for ( let attr in node.attributes() ) {\n out.push( self._hold_parent( attr, node ) );\n }\n return out;\n }\n\n method _indexed_child ( node, i ) {\n return self._hold_parent( node.indexed_child(i), node );\n }\n\n method _named_child ( node, name ) {\n return self._hold_parent( node.named_child(name), node );\n }\n\n method _named_indexed_child ( node, name, i ) {\n return self._hold_parent( node.named_indexed_child( name, i ), node );\n }\n\n method eval_binop ( ast, ctx ) {\n const op := ast{op};\n const op_def := self.operator_definitions().first(\n fn x \u2192 x.get_spelling eq op and x.is_binary()\n );\n if ( op_def and op_def{f} ) {\n const implementation := op_def{f};\n return implementation( op_def, self, ast, ctx, ast{l}, ast{r} );\n }\n return [];\n }\n\n method eval_unop ( ast, ctx ) {\n const op := ast{op};\n const op_def := self.operator_definitions().first(\n fn x \u2192 x.get_spelling eq op and x.is_unary()\n );\n if ( op_def and op_def{f} ) {\n const implementation := op_def{f};\n return implementation( op_def, self, ast, ctx, ast{e} );\n }\n return [];\n }\n\n method eval_path ( path_ast, ctx ) {\n let current := [];\n for ( let n in ctx.nodeset() ) {\n current.push(n);\n }\n\n let parentset := ctx.parentset();\n\n for ( let seg in path_ast{s} ) {\n let next_nodes := [];\n\n if ( seg{k} \u2261 "root" ) {\n next_nodes := [ ctx.root() ];\n }\n else if ( seg{k} \u2261 "dot" ) {\n next_nodes := current;\n }\n else if ( seg{k} \u2261 "parent" ) {\n for ( let n in current ) {\n let p := n.parent();\n if ( p \u2262 null ) {\n next_nodes.push(p);\n }\n }\n next_nodes := self.dedup_nodes(next_nodes);\n }\n else if ( seg{k} \u2261 "ancestors" ) {\n let anc := [];\n for ( let n in current ) {\n let p := n.parent();\n while ( p \u2262 null ) {\n anc.push(p);\n p := p.parent();\n }\n }\n next_nodes := self.dedup_nodes(anc);\n }\n else if ( seg{k} \u2261 "star" ) {\n let kids := [];\n for ( let n in current ) {\n for ( let child in self._children(n) ) {\n if ( child.type() \u2262 "attr" ) {\n kids.push(child);\n }\n }\n }\n next_nodes := self.dedup_nodes(kids);\n }\n else if ( seg{k} \u2261 "desc" ) {\n let acc := [];\n let stack := [];\n for ( let n in current ) {\n stack.push(n);\n }\n\n while ( stack.length() > 0 ) {\n let n := stack.shift();\n acc.push(n);\n for ( let child in self._children(n) ) {\n if ( child.type() \u2262 "attr" ) {\n stack.push(child);\n }\n }\n }\n next_nodes := self.dedup_nodes(acc);\n }\n else if ( seg{k} \u2261 "index" ) {\n let idx := seg{i};\n let kids := [];\n for ( let n in current ) {\n let c := self._indexed_child( n, idx );\n kids.push(c) if c \u2262 null;\n }\n next_nodes := self.dedup_nodes(kids);\n }\n else if ( seg{k} \u2261 "fnseg" ) {\n let out := [];\n for ( let n in current ) {\n let seg_ctx := ctx.with_nodeset( [ n ], current );\n let fn_ast := { t: "fn", n: seg{n}, a: seg{a} };\n let res := self.eval_fn( fn_ast, seg_ctx );\n for ( let x in res ) {\n out.push(x);\n }\n }\n next_nodes := out;\n }\n else if ( seg{k} \u2261 "name" ) {\n let name := seg{n};\n\n if ( name ~ /^\\@/ ) {\n if ( name \u2261 "@*" ) {\n let attrs := [];\n for ( let n in current ) {\n for ( let a in self._attributes(n) ) {\n attrs.push(a);\n }\n }\n next_nodes := self.dedup_nodes(attrs);\n }\n else {\n let attrs := [];\n for ( let n in current ) {\n for ( let a in self._attributes(n) ) {\n if ( a.name() \u2261 name ) {\n attrs.push(a);\n }\n }\n }\n next_nodes := self.dedup_nodes(attrs);\n }\n }\n else {\n let kids := [];\n for ( let n in current ) {\n if ( n.can_have_named_indexed_children() ) {\n let idx := 0;\n while ( true ) {\n let c := self._named_indexed_child( n, name, idx );\n last if c \u2261 null;\n kids.push(c);\n idx++;\n }\n }\n else {\n let c := self._named_child( n, name );\n kids.push(c) if c \u2262 null;\n }\n }\n next_nodes := self.dedup_nodes(kids);\n }\n\n if ( seg.exists("i") and seg{i} \u2262 null ) {\n let idx := seg{i};\n let picked := [];\n for ( let n in current ) {\n let c := self._named_indexed_child( n, name, idx );\n picked.push(c) if c \u2262 null;\n }\n next_nodes := self.dedup_nodes(picked);\n }\n }\n else {\n die "Unknown path segment kind: " _ seg{k};\n }\n\n if ( seg.exists("q") and seg{q}.length() > 0 ) {\n for ( let q in seg{q} ) {\n if ( q.exists("t") and q{t} \u2261 "num" and q{v} ~ /^\\d+$/ ) {\n let idx := 0 + q{v};\n\n if ( next_nodes.length() > 0 and self._node_is_xml(next_nodes[0]) ) {\n next_nodes := ( idx \u2265 0 and idx < next_nodes.length() ) ? [ next_nodes[idx] ] : [];\n }\n else {\n let picked := [];\n for ( let node in next_nodes ) {\n let ch := self._children(node)\n .grep( fn c \u2192 c.type() \u2262 "attr" );\n if ( idx \u2265 0 and idx < ch.length() ) {\n picked.push( ch[idx] );\n }\n }\n next_nodes := picked;\n }\n\n next;\n }\n\n let filtered := [];\n let i := 0;\n while ( i < next_nodes.length() ) {\n let node := next_nodes[i];\n let ns_ctx := ctx.with_nodeset( next_nodes, current );\n let filter_ctx := ns_ctx.with_nodeset( [ node ], next_nodes );\n let r := self.eval_expr( q, self.nested_ctx( filter_ctx, want: "exists" ) );\n\n let ok := false;\n if ( q.exists("t") and q{t} \u2261 "path" ) {\n ok := r.length() > 0;\n }\n else {\n ok := self.truthy( r.length() > 0 ? r[0] : null );\n }\n\n if ( ok ) {\n filtered.push(node);\n }\n i++;\n }\n next_nodes := filtered;\n }\n }\n\n parentset := current;\n current := next_nodes;\n }\n\n return current;\n }\n\n method maybe_apply_action ( node, ctx ) {\n const meta := ctx.meta();\n return node if not meta.exists( "action" );\n const action := meta{action};\n return node if action \u2261 null;\n return node if not action.exists( "op" );\n node.do_action(action);\n return node;\n }\n\n method eval_fn ( fn_ast, ctx ) {\n const name := fn_ast{n};\n const fn_def := self.function_definitions().first( fn f \u2192 f.has_name(name) );\n return fn_def{f}( fn_def, self, fn_ast, ctx, fn_ast{a} ?: [] ) if fn_def;\n return [];\n }\n\n method string_replace ( string, pattern, replacement ) {\n let text := string \u2261 null ? "" : "" _ string;\n let rep := replacement \u2261 null ? "" : "" _ replacement;\n\n try {\n return replace( text, pattern, rep, "g" );\n }\n catch {\n return replace( text, "" _ pattern, rep, "g" );\n }\n }\n\n method dedup_nodes ( nodes ) {\n let seen := {};\n let out := [];\n for ( let n in nodes ) {\n let key := n.id ?: ( "anon:" _ out.length() _ ":" _ ( "" _ n.raw() ) );\n if ( not seen.exists(key) ) {\n seen.set( key, true );\n out.push(n);\n }\n }\n return out;\n }\n\n method truthy ( n ) {\n return false if n \u2261 null;\n let pv := n.primitive_value();\n return pv ? true : false;\n }\n\n method to_number ( n ) {\n return null if n \u2261 null;\n return n.number_value();\n }\n\n method to_string ( n ) {\n return null if n \u2261 null;\n return n.string_value();\n }\n\n method equals ( a, b ) {\n return false if a \u2261 null or b \u2261 null;\n\n let a_type := a.type();\n let b_type := b.type();\n\n if ( b_type eq "null" ) {\n return a_type eq "null";\n }\n if ( a_type eq "null" ) {\n return b_type eq "null";\n }\n\n if ( a_type eq "boolean" and b_type eq "boolean" ) {\n let av := a.primitive_value() ? true : false;\n let bv := b.primitive_value() ? true : false;\n return av \u2261 bv;\n }\n\n if ( a_type \u2261 "number" and b_type \u2261 "number" ) {\n let av := a.number_value();\n let bv := b.number_value();\n return false if av \u2261 null or bv \u2261 null;\n\n if ( ( "" _ av ) ~ /\\./ or ( "" _ bv ) ~ /\\./ ) {\n return abs( av - bv ) < \u03B5;\n }\n\n return av = bv;\n }\n\n let string_like := [ "string", "text", "attr", "comment", "element" ];\n if ( a_type in string_like and b_type in string_like ) {\n let av := a.string_value();\n let bv := b.string_value();\n return av eq bv;\n }\n\n let aid := a.id();\n let bid := b.id();\n return false if aid \u2261 null or bid \u2261 null;\n return aid eq bid;\n }\n\n method _node_is_xml ( n ) {\n if ( n \u2261 null ) {\n return false;\n }\n let raw := n.raw();\n try {\n let node_type := raw.nodeType();\n return node_type \u2262 null;\n }\n catch {\n return false;\n }\n }\n}\n';
|
|
46529
|
+
virtualFiles["/modules/std/path/z/context.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/path/z/context - Evaluation context used by std/path/z.\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nThis module provides the pure-Zuzu evaluation context used by ZPath.\n\n=head1 EXPORTS\n\n=head2 Classes\n\n=over\n\n=item C<< Ctx({ root, nodeset?, parentset?, meta? }) >>\n\nConstructs a ZPath evaluation context. Returns: C<Ctx>. Wraps C<root>\nas a C<std/path/z/node> C<Node> and stores the active node sets.\n\n=over\n\n=item C<< ctx.with_nodeset(ns, ps) >>\n\nParameters: C<ns> is the next node set and C<ps> is the parent set.\nReturns: C<Ctx>. Returns a copy of the context with different node\nsets.\n\n=item C<< ctx.nested(extras?) >>\n\nParameters: C<extras> is optional metadata. Returns: C<Ctx>. Returns a\nnested context with incremented metadata level.\n\n=item C<< ctx.root() >>\n\nParameters: none. Returns: C<Node>. Returns the root node.\n\n=item C<< ctx.nodeset() >>\n\nParameters: none. Returns: C<Array>. Returns the current node set.\n\n=item C<< ctx.parentset() >>\n\nParameters: none. Returns: C<Array> or C<null>. Returns the parent node\nset.\n\n=item C<< ctx.meta() >>\n\nParameters: none. Returns: C<Dict>. Returns context metadata.\n\n=back\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/path/z/context >> 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\nfrom std/path/z/node import Node;\n\nclass Ctx {\n let root := null;\n let nodeset := null;\n let parentset := null;\n let meta := null;\n\n method __build__ () {\n if ( not ( root instanceof Node ) ) {\n let root_obj := root;\n let node_type := null;\n\n try {\n node_type := int( "" _ root_obj.nodeType() );\n }\n catch {\n }\n\n let xml_document := node_type = 9;\n if ( not xml_document and root_obj can documentElement ) {\n try {\n root_obj.documentElement();\n xml_document := true;\n }\n catch {\n }\n }\n\n if ( xml_document ) {\n try {\n let de := root_obj.documentElement();\n root_obj := de if de \u2262 null;\n }\n catch {\n }\n }\n\n root := Node.wrap( root_obj );\n }\n\n nodeset ?:= [ root ];\n meta ?:= { level: 0 };\n }\n\n method with_nodeset ( ns, ps ) {\n return new Ctx(\n root: root,\n nodeset: ns,\n parentset: ps,\n meta: meta,\n );\n }\n\n method nested ( extras? ) {\n // Extras is optional metadata; an empty map is still a value.\n let extra_meta := extras ?: {};\n\n let next_meta := {\n level: meta.get( "level", 0 ) + 1,\n };\n\n for ( let pair in extra_meta.enumerate ) {\n next_meta.add( pair );\n }\n\n return new Ctx(\n root: root,\n nodeset: nodeset,\n parentset: parentset,\n meta: next_meta,\n );\n }\n\n method root () { return root; }\n method nodeset () { return nodeset; }\n method parentset () { return parentset; }\n method meta () { return meta; }\n}\n';
|
|
46530
|
+
virtualFiles["/modules/std/path/z/evaluate.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/path/z/evaluate - Pure Zuzu evaluator for ZPath expressions.\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nThis module provides the pure-Zuzu evaluator used by ZPath.\n\n=head1 EXPORTS\n\n=head2 Classes\n\n=over\n\n=item C<Evaluator>\n\nPure-Zuzu evaluator for parsed ZPath AST values.\n\n=over\n\n=item C<< evaluator.operator_definitions() >>\n\nParameters: none. Returns: C<Array>. Returns the active operator\ndefinition table.\n\n=item C<< evaluator.function_definitions() >>\n\nParameters: none. Returns: C<Array>. Returns the active function\ndefinition table.\n\n=item C<< evaluator.eval_expr_wrap(ast, ctx) >>\n\nParameters: C<ast> is an AST node and C<ctx> is a C<Ctx>. Returns:\nC<Array>. Evaluates with optional debug dumping.\n\n=item C<< evaluator.eval_expr(ast, ctx) >>\n\nParameters: C<ast> is an AST node and C<ctx> is a C<Ctx>. Returns:\nC<Array>. Evaluates an expression to a node set.\n\n=item C<< evaluator.nested_ctx(ctx, ... PairList extras) >>\n\nParameters: C<ctx> is a C<Ctx> or C<null> and C<extras> are metadata.\nReturns: C<Ctx> or C<null>. Creates a nested evaluation context.\n\n=item C<< evaluator.eval_binop(ast, ctx) >>, C<< evaluator.eval_unop(ast, ctx) >>\n\nParameters: C<ast> is an operator AST node and C<ctx> is a C<Ctx>.\nReturns: C<Array>. Evaluates a binary or unary operator.\n\n=item C<< evaluator.eval_path(path_ast, ctx) >>\n\nParameters: C<path_ast> is a path AST node and C<ctx> is a C<Ctx>.\nReturns: C<Array>. Evaluates a path expression to selected nodes.\n\n=item C<< evaluator.maybe_apply_action(node, ctx) >>\n\nParameters: C<node> is a C<Node> and C<ctx> is a C<Ctx>. Returns:\nC<Node>. Applies any pending path action.\n\n=item C<< evaluator.eval_fn(fn_ast, ctx) >>\n\nParameters: C<fn_ast> is a function-call AST node and C<ctx> is a\nC<Ctx>. Returns: C<Array>. Evaluates a ZPath function call.\n\n=item C<< evaluator.string_replace(string, pattern, replacement) >>\n\nParameters: all arguments are strings or regex-like values. Returns:\nC<String>. Applies ZPath string replacement.\n\n=item C<< evaluator.dedup_nodes(nodes) >>\n\nParameters: C<nodes> is an array of nodes. Returns: C<Array>. Removes\nduplicate nodes while preserving order.\n\n=item C<< evaluator.truthy(n) >>\n\nParameters: C<n> is a node or value. Returns: C<Boolean>. Applies ZPath\ntruthiness.\n\n=item C<< evaluator.to_number(n) >>, C<< evaluator.to_string(n) >>\n\nParameters: C<n> is a node or value. Returns: C<Number> or C<String>.\nCoerces a ZPath value.\n\n=item C<< evaluator.equals(a, b) >>\n\nParameters: C<a> and C<b> are nodes or values. Returns: C<Boolean>.\nCompares two ZPath values.\n\n=back\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/path/z/evaluate >> 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\nfrom std/string import replace, index, substr;\nfrom std/path/z/node import Node;\nfrom std/path/z/functions import STANDARD_FUNCTIONS;\nfrom std/path/z/operators import STANDARD_OPERATORS;\n\nlet _do_dump := false;\n\nclass Evaluator {\n let _operator_definitions;\n let _function_definitions;\n\n let \u03B5 := 0.000000001;\n\n method operator_definitions () {\n _operator_definitions ?:= STANDARD_OPERATORS;\n return _operator_definitions;\n }\n\n method function_definitions () {\n _function_definitions ?:= STANDARD_FUNCTIONS;\n return _function_definitions;\n }\n\n method eval_expr_wrap ( ast, ctx ) {\n from std/dump import Dumper;\n let got := self._real_eval_expr ( ast, ctx );\n say `AST ${Dumper.dump(ast)} CTX ${Dumper.dump(ctx)} \u21D2 GOT ${Dumper.dump(got)}` if _do_dump;\n return got;\n }\n\n method eval_expr ( ast, ctx ) {\n\n switch ( ast{t} : eq ) {\n case "num":\n return [ Node.wrap( ast{v} ) ];\n case "str":\n return [ Node.wrap( ast{v} ) ];\n case "path":\n return self.eval_path( ast, ctx );\n case "fn":\n return self.eval_fn( ast, ctx );\n case "un":\n return self.eval_unop( ast, ctx );\n case "bin":\n return self.eval_binop( ast, ctx );\n case "ternary":\n let c := self.eval_expr( ast{c}, ctx );\n let ab := self.truthy( c.get(0) ) ? "a" : "b";\n return self.eval_expr( ast{(ab)}, ctx );\n case "elvis":\n let left := self.eval_expr( ast{c}, ctx );\n let first := left.get( 0, null );\n return left if first \u2262 null and first.primitive_value() \u2262 null;\n return self.eval_expr( ast{b}, ctx );\n default:\n die "Panic! Unknown AST node type!";\n }\n }\n\n method nested_ctx ( ctx, ... PairList extras ) {\n return null if ctx \u2261 null;\n return ctx.nested( extras );\n }\n\n method _hold_parent ( node, parent ) {\n if ( node \u2262 null and parent \u2262 null ) {\n node.hold_parent(parent);\n }\n return node;\n }\n\n method _children ( node ) {\n let out := [];\n for ( let child in node.children() ) {\n out.push( self._hold_parent( child, node ) );\n }\n return out;\n }\n\n method _attributes ( node ) {\n let out := [];\n for ( let attr in node.attributes() ) {\n out.push( self._hold_parent( attr, node ) );\n }\n return out;\n }\n\n method _indexed_child ( node, i ) {\n return self._hold_parent( node.indexed_child(i), node );\n }\n\n method _named_child ( node, name ) {\n return self._hold_parent( node.named_child(name), node );\n }\n\n method _named_indexed_child ( node, name, i ) {\n return self._hold_parent( node.named_indexed_child( name, i ), node );\n }\n\n method eval_binop ( ast, ctx ) {\n const op := ast{op};\n const op_def := self.operator_definitions().first(\n fn x \u2192 x.get_spelling eq op and x.is_binary()\n );\n if ( op_def and op_def{f} ) {\n const implementation := op_def{f};\n return implementation( op_def, self, ast, ctx, ast{l}, ast{r} );\n }\n return [];\n }\n\n method eval_unop ( ast, ctx ) {\n const op := ast{op};\n const op_def := self.operator_definitions().first(\n fn x \u2192 x.get_spelling eq op and x.is_unary()\n );\n if ( op_def and op_def{f} ) {\n const implementation := op_def{f};\n return implementation( op_def, self, ast, ctx, ast{e} );\n }\n return [];\n }\n\n method eval_path ( path_ast, ctx ) {\n let current := [];\n for ( let n in ctx.nodeset() ) {\n current.push(n);\n }\n\n let parentset := ctx.parentset();\n\n for ( let seg in path_ast{s} ) {\n let next_nodes := [];\n\n if ( seg{k} \u2261 "root" ) {\n next_nodes := [ ctx.root() ];\n }\n else if ( seg{k} \u2261 "dot" ) {\n next_nodes := current;\n }\n else if ( seg{k} \u2261 "parent" ) {\n for ( let n in current ) {\n let p := n.parent();\n if ( p \u2262 null ) {\n next_nodes.push(p);\n }\n }\n next_nodes := self.dedup_nodes(next_nodes);\n }\n else if ( seg{k} \u2261 "ancestors" ) {\n let anc := [];\n for ( let n in current ) {\n let p := n.parent();\n while ( p \u2262 null ) {\n anc.push(p);\n p := p.parent();\n }\n }\n next_nodes := self.dedup_nodes(anc);\n }\n else if ( seg{k} \u2261 "star" ) {\n let kids := [];\n for ( let n in current ) {\n for ( let child in self._children(n) ) {\n if ( child.type() \u2262 "attr" ) {\n kids.push(child);\n }\n }\n }\n next_nodes := self.dedup_nodes(kids);\n }\n else if ( seg{k} \u2261 "desc" ) {\n let acc := [];\n let stack := [];\n for ( let n in current ) {\n stack.push(n);\n }\n\n while ( stack.length() > 0 ) {\n let n := stack.shift();\n acc.push(n);\n for ( let child in self._children(n) ) {\n if ( child.type() \u2262 "attr" ) {\n stack.push(child);\n }\n }\n }\n next_nodes := self.dedup_nodes(acc);\n }\n else if ( seg{k} \u2261 "index" ) {\n let idx := seg{i};\n let kids := [];\n for ( let n in current ) {\n let c := self._indexed_child( n, idx );\n kids.push(c) if c \u2262 null;\n }\n next_nodes := self.dedup_nodes(kids);\n }\n else if ( seg{k} \u2261 "fnseg" ) {\n let out := [];\n for ( let n in current ) {\n let seg_ctx := ctx.with_nodeset( [ n ], current );\n let fn_ast := { t: "fn", n: seg{n}, a: seg{a} };\n let res := self.eval_fn( fn_ast, seg_ctx );\n for ( let x in res ) {\n out.push(x);\n }\n }\n next_nodes := out;\n }\n else if ( seg{k} \u2261 "name" ) {\n let name := seg{n};\n\n if ( name ~ /^\\@/ ) {\n if ( name \u2261 "@*" ) {\n let attrs := [];\n for ( let n in current ) {\n for ( let a in self._attributes(n) ) {\n attrs.push(a);\n }\n }\n next_nodes := self.dedup_nodes(attrs);\n }\n else {\n let attrs := [];\n for ( let n in current ) {\n for ( let a in self._attributes(n) ) {\n if ( a.name() \u2261 name ) {\n attrs.push(a);\n }\n }\n }\n next_nodes := self.dedup_nodes(attrs);\n }\n }\n else {\n let kids := [];\n for ( let n in current ) {\n if ( n.can_have_named_indexed_children() ) {\n let idx := 0;\n while ( true ) {\n let c := self._named_indexed_child( n, name, idx );\n last if c \u2261 null;\n kids.push(c);\n idx++;\n }\n }\n else {\n let c := self._named_child( n, name );\n kids.push(c) if c \u2262 null;\n }\n }\n next_nodes := self.dedup_nodes(kids);\n }\n\n if ( seg.exists("i") and seg{i} \u2262 null ) {\n let idx := seg{i};\n let picked := [];\n for ( let n in current ) {\n let c := self._named_indexed_child( n, name, idx );\n picked.push(c) if c \u2262 null;\n }\n next_nodes := self.dedup_nodes(picked);\n }\n }\n else {\n die "Unknown path segment kind: " _ seg{k};\n }\n\n if ( seg.exists("q") and seg{q}.length() > 0 ) {\n for ( let q in seg{q} ) {\n if ( q.exists("t") and q{t} \u2261 "num" and q{v} ~ /^\\d+$/ ) {\n let idx := 0 + q{v};\n\n if ( next_nodes.length() > 0 and self._node_is_xml(next_nodes[0]) ) {\n next_nodes := ( idx \u2265 0 and idx < next_nodes.length() ) ? [ next_nodes[idx] ] : [];\n }\n else {\n let picked := [];\n for ( let node in next_nodes ) {\n let ch := self._children(node)\n .grep( fn c \u2192 c.type() \u2262 "attr" );\n if ( idx \u2265 0 and idx < ch.length() ) {\n picked.push( ch[idx] );\n }\n }\n next_nodes := picked;\n }\n\n next;\n }\n\n let filtered := [];\n let i := 0;\n while ( i < next_nodes.length() ) {\n let node := next_nodes[i];\n let ns_ctx := ctx.with_nodeset( next_nodes, current );\n let filter_ctx := ns_ctx.with_nodeset( [ node ], next_nodes );\n let r := self.eval_expr( q, self.nested_ctx( filter_ctx, want: "exists" ) );\n\n let ok := false;\n if ( q.exists("t") and q{t} \u2261 "path" ) {\n ok := r.length() > 0;\n }\n else {\n ok := self.truthy( r.length() > 0 ? r[0] : null );\n }\n\n if ( ok ) {\n filtered.push(node);\n }\n i++;\n }\n next_nodes := filtered;\n }\n }\n\n parentset := current;\n current := next_nodes;\n }\n\n return current;\n }\n\n method maybe_apply_action ( node, ctx ) {\n const meta := ctx.meta();\n return node if not meta.exists( "action" );\n const action := meta{action};\n return node if action \u2261 null;\n return node if not action.exists( "op" );\n node.do_action(action);\n return node;\n }\n\n method eval_fn ( fn_ast, ctx ) {\n const name := fn_ast{n};\n const fn_def := self.function_definitions().first( fn f \u2192 f.has_name(name) );\n // Function-argument ASTs are null or arrays; empty arrays are values.\n return fn_def{f}( fn_def, self, fn_ast, ctx, fn_ast{a} ?: [] ) if fn_def;\n return [];\n }\n\n method string_replace ( string, pattern, replacement ) {\n let text := string \u2261 null ? "" : "" _ string;\n let rep := replacement \u2261 null ? "" : "" _ replacement;\n\n try {\n return replace( text, pattern, rep, "g" );\n }\n catch {\n return replace( text, "" _ pattern, rep, "g" );\n }\n }\n\n method dedup_nodes ( nodes ) {\n let seen := {};\n let out := [];\n for ( let n in nodes ) {\n // Node ids are null or stable identifiers; only null needs anon keys.\n let key := n.id ?: ( "anon:" _ out.length() _ ":" _ ( "" _ n.raw() ) );\n if ( not seen.exists(key) ) {\n seen.set( key, true );\n out.push(n);\n }\n }\n return out;\n }\n\n method truthy ( n ) {\n return false if n \u2261 null;\n let pv := n.primitive_value();\n return pv ? true : false;\n }\n\n method to_number ( n ) {\n return null if n \u2261 null;\n return n.number_value();\n }\n\n method to_string ( n ) {\n return null if n \u2261 null;\n return n.string_value();\n }\n\n method equals ( a, b ) {\n return false if a \u2261 null or b \u2261 null;\n\n let a_type := a.type();\n let b_type := b.type();\n\n if ( b_type eq "null" ) {\n return a_type eq "null";\n }\n if ( a_type eq "null" ) {\n return b_type eq "null";\n }\n\n if ( a_type eq "boolean" and b_type eq "boolean" ) {\n let av := a.primitive_value() ? true : false;\n let bv := b.primitive_value() ? true : false;\n return av \u2261 bv;\n }\n\n if ( a_type \u2261 "number" and b_type \u2261 "number" ) {\n let av := a.number_value();\n let bv := b.number_value();\n return false if av \u2261 null or bv \u2261 null;\n\n if ( ( "" _ av ) ~ /\\./ or ( "" _ bv ) ~ /\\./ ) {\n return abs( av - bv ) < \u03B5;\n }\n\n return av = bv;\n }\n\n let string_like := [ "string", "text", "attr", "comment", "element" ];\n if ( a_type in string_like and b_type in string_like ) {\n let av := a.string_value();\n let bv := b.string_value();\n return av eq bv;\n }\n\n let aid := a.id();\n let bid := b.id();\n return false if aid \u2261 null or bid \u2261 null;\n return aid eq bid;\n }\n\n method _node_is_xml ( n ) {\n if ( n \u2261 null ) {\n return false;\n }\n let raw := n.raw();\n try {\n let node_type := raw.nodeType();\n return node_type \u2262 null;\n }\n catch {\n return false;\n }\n }\n}\n';
|
|
46464
46531
|
virtualFiles["/modules/std/path/z/functions.zzm"] = `=encoding utf8
|
|
46465
46532
|
|
|
46466
46533
|
=head1 NAME
|
|
@@ -46718,6 +46785,7 @@ const STANDARD_FUNCTIONS := [
|
|
|
46718
46785
|
n += got.length;
|
|
46719
46786
|
}
|
|
46720
46787
|
else {
|
|
46788
|
+
// Parentset can be an empty array; only null falls back.
|
|
46721
46789
|
const cur := ctx.parentset ?: ctx.nodeset;
|
|
46722
46790
|
n := cur.length;
|
|
46723
46791
|
}
|
|
@@ -47337,11 +47405,11 @@ const STANDARD_FUNCTIONS := [
|
|
|
47337
47405
|
];
|
|
47338
47406
|
`;
|
|
47339
47407
|
virtualFiles["/modules/std/path/z/lexer.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/path/z/lexer - Pure Zuzu lexer for ZPath expressions.\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nThis module provides the pure-Zuzu lexer used by ZPath.\n\n=head1 EXPORTS\n\n=head2 Classes\n\n=over\n\n=item C<< Lexer({ src: String, allowed_operators: Array }) >>\n\nConstructs a ZPath lexer. Returns: C<Lexer>. Tokenizes C<src> using the\nallowed operator table.\n\n=over\n\n=item C<< lexer.peek() >>\n\nParameters: none. Returns: C<Dict>. Returns the current token without\nadvancing.\n\n=item C<< lexer.peek_n(n) >>\n\nParameters: C<n> is a zero-based lookahead offset. Returns: C<Dict>.\nReturns a token ahead of the current position.\n\n=item C<< lexer.peek_kind() >>\n\nParameters: none. Returns: C<String>. Returns the current token kind.\n\n=item C<< lexer.peek_kind_n(n) >>\n\nParameters: C<n> is a zero-based lookahead offset. Returns: C<String>.\nReturns a token kind ahead of the current position.\n\n=item C<< lexer.next_tok() >>\n\nParameters: none. Returns: C<Dict>. Consumes and returns the current\ntoken.\n\n=item C<< lexer.expect(k) >>\n\nParameters: C<k> is the expected token kind. Returns: C<Dict>. Consumes\nand returns the current token, throwing when it has a different kind.\n\n=item C<< lexer.known_operators() >>\n\nParameters: none. Returns: C<Array>. Returns the allowed operator\ndefinitions.\n\n=back\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/path/z/lexer >> 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\nfrom std/string import substr;\n\nclass Lexer {\n let src := "";\n let scan_i := 0;\n let toks := [];\n let pos := 0;\n let allowed_operators;\n\n method __build__ () {\n die "Expected some operators" unless allowed_operators;\n toks := self._tokenize(src);\n }\n\n method peek () {\n return toks[pos];\n }\n\n method peek_n ( n ) {\n return toks[pos + n];\n }\n\n method peek_kind () {\n return toks[pos]{k};\n }\n\n method peek_kind_n ( n ) {\n return toks[pos + n]{k};\n }\n\n method next_tok () {\n let tok := toks[pos];\n pos++;\n return tok;\n }\n\n method expect ( k ) {\n let t := self.next_tok();\n die `Expected ${k}, got ${t{k}}` if t{k} \u2262 k;\n return t;\n }\n\n method _is_ws ( c ) {\n return c \u2262 null and c ~ /\\s/;\n }\n\n method _prev_sig ( chars, idx ) {\n let j := idx - 1;\n while ( j >= 0 ) {\n if ( chars[j] ~ /\\s/ ) {\n j--;\n next;\n }\n return chars[j];\n }\n return null;\n }\n\n method _next_sig ( chars, idx, n ) {\n let j := idx + 1;\n while ( j < n ) {\n if ( chars[j] ~ /\\s/ ) {\n j++;\n next;\n }\n return chars[j];\n }\n return null;\n }\n\n method _is_path_ctx_prev ( c ) {\n return c \u2261 "[" or c \u2261 "(" or c \u2261 "," or c \u2261 ":" or c \u2261 "?" or c \u2261 "/";\n }\n\n method _is_path_ctx_next ( c ) {\n return c \u2261 "]" or c \u2261 ")" or c \u2261 "," or c \u2261 ":" or c \u2261 "?" or c \u2261 "/";\n }\n\n method _ws_on_both ( left, right ) {\n return self._is_ws(left) and self._is_ws(right);\n }\n\n method known_operators () {\n return allowed_operators;\n }\n\n method _sorted_operators () {\n return self.known_operators()\n .grep( fn o \u2192 not o.lexer_should_ignore )\n .sort( fn ( x, y ) \u2192 y.char_length <=> x.char_length );\n }\n\n method _operator_at ( chars, i, n, ops ) {\n let oi := 0;\n while ( oi < ops.length ) {\n let op := ops[oi];\n let spell := op{spelling};\n let m := length spell;\n if ( i + m <= n ) {\n let got := "";\n let j := 0;\n while ( j < m ) {\n got _= chars[i + j];\n j++;\n }\n if ( got \u2261 spell ) {\n return op;\n }\n }\n oi++;\n }\n return null;\n }\n\n method _is_name_char ( c ) {\n return c ~ /[A-Za-z0-9_\\-]/;\n }\n\n method _allows_operator_kind ( kind ) {\n return allowed_operators.first( fn op \u2192 op.get_kind() \u2261 kind )\n ? true\n : false;\n }\n\n method _tokenize ( String source ) {\n let t := [];\n let chars := [];\n let n := length source;\n let ops := self._sorted_operators();\n let z := 0;\n while ( z < n ) {\n chars.push( substr( source, z, 1 ) );\n z++;\n }\n\n let i := 0;\n while ( i < n ) {\n let ch := chars[i];\n\n if ( ch ~ /\\s/ ) {\n i++;\n next;\n }\n\n let prev := i > 0 ? chars[i - 1] : null;\n let nxt := i + 1 < n ? chars[i + 1] : null;\n\n let prev_nonws := self._prev_sig( chars, i );\n let next_nonws := self._next_sig( chars, i, n );\n\n function push_token ( tok ) {\n tok{ws_before} := self._is_ws(prev) ? true : false;\n tok{ws_after} := self._is_ws(nxt) ? true : false;\n t.push( tok );\n }\n\n function push_sized_token ( tok, after ) {\n tok{ws_before} := self._is_ws(prev) ? true : false;\n tok{ws_after} := self._is_ws(after) ? true : false;\n t.push( tok );\n }\n\n if ( ch \u2261 "/" ) {\n if (\n self._ws_on_both( prev, nxt )\n and prev_nonws \u2262 null and next_nonws \u2262 null\n and not self._is_path_ctx_prev(prev_nonws)\n and not self._is_path_ctx_next(next_nonws)\n ) {\n push_token( { k: "SLASH", v: "/" } );\n i++;\n next;\n }\n if (\n ( self._is_ws(prev) xor self._is_ws(nxt) )\n and prev_nonws \u2262 null and next_nonws \u2262 null\n and not self._is_path_ctx_prev(prev_nonws)\n and not self._is_path_ctx_next(next_nonws)\n ) {\n die `Binary operator \'/\' requires whitespace around it`;\n }\n t.push( { k: "SLASH_PATH", v: "/" } );\n i++;\n next;\n }\n\n let op := self._operator_at( chars, i, n, ops );\n if ( op \u2262 null ) {\n let spell := op.get_spelling();\n let need_ws := op.requires_whitespace();\n let m := length spell;\n let op_prev := i > 0 ? chars[i - 1] : null;\n let op_next := i + m < n ? chars[i + m] : null;\n let path_ambiguous_star :=\n ( spell \u2261 "*" or spell \u2261 "**" )\n and not self._ws_on_both( op_prev, op_next );\n\n let left_name := false;\n let right_name := false;\n if ( spell ~ /^[A-Za-z_]/ ) {\n left_name := op_prev \u2262 null and self._is_name_char(op_prev);\n right_name := op_next \u2262 null and self._is_name_char(op_next);\n }\n\n if (\n not path_ambiguous_star\n and not left_name\n and not right_name\n ) {\n if ( need_ws ) {\n if ( self._ws_on_both( op_prev, op_next ) ) {\n push_sized_token( { k: op.get_kind(), v: spell }, op_next );\n i := i + m;\n next;\n }\n die `Binary operator \'${spell}\' requires whitespace around it`;\n }\n push_sized_token( { k: op.get_kind(), v: spell }, op_next );\n i := i + m;\n next;\n }\n }\n\n if ( ch \u2261 "(" ) { t.push( { k: "LPAREN", v: "(" } ); i++; next; }\n if ( ch \u2261 ")" ) { t.push( { k: "RPAREN", v: ")" } ); i++; next; }\n if ( ch \u2261 "[" ) { t.push( { k: "LBRACK", v: "[" } ); i++; next; }\n if ( ch \u2261 "]" ) { t.push( { k: "RBRACK", v: "]" } ); i++; next; }\n if ( ch \u2261 "," ) { t.push( { k: "COMMA", v: "," } ); i++; next; }\n\n if ( ch \u2261 "." ) {\n if ( i + 2 < n and chars[i + 1] \u2261 "." and chars[i + 2] \u2261 "*" ) {\n push_token( { k: "DOTDOTSTAR", v: "..*" } );\n i := i + 3;\n next;\n }\n if ( i + 1 < n and chars[i + 1] \u2261 "." ) {\n push_token( { k: "DOTDOT", v: ".." } );\n i := i + 2;\n next;\n }\n push_token( { k: "DOT", v: "." } );\n i++;\n next;\n }\n\n if ( ch \u2261 "*" and self._ws_on_both( prev, nxt ) ) {\n push_token( { k: "STAR", v: "*" } );\n i++;\n next;\n }\n\n if ( ch \u2261 "*" ) {\n if ( i + 1 < n and chars[i + 1] \u2261 "*" ) {\n push_token( { k: "STARSTAR", v: "**" } );\n i := i + 2;\n next;\n }\n push_token( { k: "STAR_PATH", v: "*" } );\n i++;\n next;\n }\n\n if ( self._allows_operator_kind( "ELVIS" ) and ch \u2261 "?" and nxt \u2261 ":" ) {\n let op_next := i + 2 < n ? chars[i + 2] : null;\n if ( self._ws_on_both( prev, op_next ) ) {\n t.push( {\n k: "ELVIS",\n v: "?:",\n ws_before: self._is_ws(prev) ? true : false,\n ws_after: self._is_ws(op_next) ? true : false,\n } );\n i := i + 2;\n next;\n }\n die "Elvis operator \'?:\' requires whitespace around it";\n }\n\n if ( ch \u2261 "?" or ch \u2261 ":" ) {\n if ( self._ws_on_both( prev, nxt ) ) {\n push_token( { k: ch \u2261 "?" ? "QMARK" : "COLON", v: ch } );\n i++;\n next;\n }\n die `Ternary operator \'${ch}\' requires whitespace around it`;\n }\n\n if ( ch \u2261 "\\"" or ch \u2261 "\'" ) {\n let quote := ch;\n i++;\n let s := "";\n let esc := false;\n while ( i < n ) {\n let cc := chars[i];\n i++;\n if ( esc ) {\n if ( cc \u2261 "\\\\" or cc \u2261 quote or cc \u2261 "\\"" or cc \u2261 "\'" ) {\n s _= cc;\n }\n else {\n s _= self._unescape_char(cc);\n }\n esc := false;\n next;\n }\n if ( cc \u2261 "\\\\" ) { esc := true; next; }\n last if cc \u2261 quote;\n s _= cc;\n }\n push_token( { k: "STRING", v: s, q: quote } );\n next;\n }\n\n if ( ch \u2261 "#" ) {\n let j := i + 1;\n let neg := false;\n if ( j < n and chars[j] \u2261 "-" ) {\n neg := true;\n j++;\n }\n die "Invalid index \'#\'" if j >= n or not( chars[j] ~ /\\d/ );\n let num := "";\n while ( j < n and chars[j] ~ /\\d/ ) {\n num _= chars[j];\n j++;\n }\n let parsed := 0 + num;\n parsed := 0 - parsed if neg;\n push_token( { k: "INDEX", v: parsed } );\n i := j;\n next;\n }\n\n if ( ch ~ /[0-9]/ ) {\n let j := i;\n let num := "";\n while ( j < n and chars[j] ~ /[0-9.]/ ) {\n num _= chars[j];\n j++;\n }\n push_token( { k: "NUMBER", v: 0 + num } );\n i := j;\n next;\n }\n\n let name := self._read_name( chars, i );\n if ( name{v} \u2262 "" ) {\n push_token( { k: "NAME", v: name{v} } );\n i := name{i};\n next;\n }\n\n die `Unexpected character \'${ch}\' at position ${i}`;\n }\n\n t.push( { k: "EOF", v: "" } );\n return t;\n }\n\n method _unescape_char ( c ) {\n return "\\n" if c \u2261 "n";\n return "\\r" if c \u2261 "r";\n return "\\t" if c \u2261 "t";\n return c;\n }\n\n method _read_name ( chars, start_i ) {\n let n := chars.length();\n let delim := {\n "\\n": true,\n "\\r": true,\n "\\t": true,\n "(": true,\n ")": true,\n "[": true,\n "]": true,\n "/": true,\n ",": true,\n "=": true,\n "&": true,\n "|": true,\n "!": true,\n "<": true,\n ">": true,\n "#": true,\n " ": true,\n };\n let buf := "";\n let esc := false;\n let i := start_i;\n\n while ( i < n ) {\n let c := chars[i];\n if ( esc ) {\n buf _= c;\n esc := false;\n i++;\n next;\n }\n\n if ( c \u2261 "\\\\" ) {\n esc := true;\n i++;\n next;\n }\n\n last if delim.exists(c);\n last if c ~ /\\s/;\n buf _= c;\n i++;\n }\n\n return { v: "", i: start_i } if buf \u2261 "";\n return { v: buf, i: i };\n }\n}\n';
|
|
47340
|
-
virtualFiles["/modules/std/path/z/node.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/path/z/node - Node wrapper used by std/path/z.\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nObjects of this class wrap underlying values and provide traversal and\ncoercion helpers used while evaluating ZPath expressions.\n\n=head1 EXPORTS\n\n=head2 Classes\n\n=over\n\n=item C<Node>\n\nBase wrapper for values traversed by ZPath.\n\n=over\n\n=item C<< Node.from_root(obj) >>\n\nParameters: C<obj> is any query root. Returns: C<Node>. Wraps a root\nvalue for traversal.\n\n=item C<< Node.wrap(obj, parent?, key?, ix?) >>\n\nParameters: C<obj> is any value, with optional parent, key, and index\nmetadata. Returns: C<Node>. Wraps a value in the appropriate node\nsubclass.\n\n=item C<< node.hold_parent(owner) >>\n\nParameters: C<owner> is a parent node. Returns: C<Node>. Retains parent\nmetadata on the node.\n\n=item C<< node.raw() >>, C<< node.value() >>, C<< node.primitive_value() >>\n\nParameters: none. Returns: value. Returns the wrapped raw, node, or\nprimitive value.\n\n=item C<< node.parent() >>\n\nParameters: none. Returns: C<Node> or C<null>. Returns the parent node.\n\n=item C<< node.key() >>, C<< node.ix() >>, C<< node.index() >>, C<< node.id() >>\n\nParameters: none. Returns: value. Returns traversal key, index, or\nstable node id metadata.\n\n=item C<< node.type() >>, C<< node.name() >>\n\nParameters: none. Returns: C<String> or C<null>. Returns the node type\nor name.\n\n=item C<< node.tagged() >>\n\nParameters: none. Returns: C<String> or C<null>. Returns a KDL value\ntag when present.\n\n=item C<< node.has_tagged() >>\n\nParameters: none. Returns: C<Boolean>. Returns true when tag metadata is\navailable.\n\n=item C<< node.string_value() >>, C<< node.number_value() >>\n\nParameters: none. Returns: C<String>, C<Number>, or C<null>. Coerces the\nnode for string or numeric ZPath operations.\n\n=item C<< node.can_have_named_children() >>, C<< node.can_have_indexed_children() >>, C<< node.can_have_named_indexed_children() >>\n\nParameters: none. Returns: C<Boolean>. Reports supported child lookup\nmodes.\n\n=item C<< node.named_child(name) >>, C<< node.indexed_child(i) >>, C<< node.named_indexed_child(name, i) >>\n\nParameters: C<name> is a child name and C<i> is an index. Returns:\nC<Node> or C<null>. Looks up child nodes.\n\n=item C<< node.next_child(child) >>, C<< node.prev_child(child) >>, C<< node.next_sibling() >>, C<< node.prev_sibling() >>\n\nParameters: C<child> is a child node where required. Returns: C<Node> or\nC<null>. Traverses adjacent nodes.\n\n=item C<< node.named_attribute(name) >>\n\nParameters: C<name> is an attribute name. Returns: C<Node> or C<null>.\nLooks up one attribute node.\n\n=item C<< node.children() >>, C<< node.attributes() >>\n\nParameters: none. Returns: C<Array>. Returns child or attribute nodes.\n\n=item C<< node.dump() >>\n\nParameters: none. Returns: C<String>. Returns a diagnostic rendering of\nthe node.\n\n=item C<< node.find(zpath) >>\n\nParameters: C<zpath> is a C<ZPath> object or subclass, such as C<ZZPath>.\nReturns: C<Array>. Evaluates a nested path from this node and returns\nselected node objects.\n\n=item C<< node.do_action(action) >>, C<< node.do_action_on_child(child, action) >>\n\nParameters: C<action> is a path action and C<child> is a child node.\nReturns: value. Applies path mutation actions.\n\n=item C<< node.ref() >>, C<< node.ref_on_child(child) >>\n\nParameters: C<child> is a child node where required. Returns:\nC<Function>. Returns a reference-like getter/setter.\n\n=back\n\n=item C<SimpleNode>, C<StringNode>, C<NumberNode>, C<BooleanNode>, C<NullNode>\n\nScalar node wrappers. They inherit the C<Node> API and override\nC<type>, C<string_value>, or C<number_value> where appropriate.\n\n=item C<ArrayNode>, C<SetNode>, C<BagNode>, C<DictNode>, C<PairListNode>, C<PairNode>\n\nCollection node wrappers. They inherit the C<Node> API and override\nchild, attribute, assignment, and reference methods appropriate to their\ncollection type. PairList children are C<PairNode> objects in pair order;\nC<indexed_child> uses global pair index, while C<named_indexed_child> uses\nthe occurrence index among pairs with the same key.\n\n=item C<KDLDocumentNode>, C<KDLNodeNode>, C<KDLValueNode>\n\nKDL node wrappers. They inherit the C<Node> API and expose KDL children,\nattributes, names, value tags, and scalar coercions.\n\n=item C<XmlNodeNode>\n\nXML node wrapper. It inherits the C<Node> API and exposes XML names,\nchildren, attributes, sibling traversal, and scalar text values.\n\n=item C<TimeNode>\n\nC<std/time> wrapper. It inherits the C<Node> API and exposes time\nattributes plus string and numeric values.\n\n=item C<PathNode>\n\nC<std/io> path wrapper. It inherits the C<Node> API and exposes path\nattributes plus string values.\n\n=item C<WidgetNode>\n\nC<std/gui/objects> widget wrapper. It inherits the C<Node> API and\nexposes widget children by widget class name and index, plus public\nwidget properties as assignable attributes.\n\n=back\n\n=head2 Variables\n\n=over\n\n=item C<determine_class>\n\nType: C<Function>. Chooses the concrete node wrapper class for a raw\nvalue.\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/path/z/node >> 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\nfrom std/data/cbor import TaggedValue;\nfrom std/data/kdl import KDLDocument, KDLNode, KDLValue;\nfrom std/internals import class_name, load_module, ref_id;\nfrom std/io try import Path;\nfrom std/math import Math;\nfrom std/string import substr;\nfrom std/time import Time;\n\n\nfunction determine_class;\nlet _widget_class_loaded := false;\nlet _widget_class := null;\n\nconst _WIDGET_ATTRS := [\n { name: "id", getter: "id", setter: "set_id" },\n { name: "enabled", getter: "enabled", setter: "set_enabled" },\n { name: "visible", getter: "visible", setter: "set_visible" },\n { name: "width", getter: "width", accessor: true },\n { name: "height", getter: "height", accessor: true },\n { name: "minwidth", getter: "minwidth", accessor: true },\n { name: "minheight", getter: "minheight", accessor: true },\n { name: "maxwidth", getter: "maxwidth", accessor: true },\n { name: "maxheight", getter: "maxheight", accessor: true },\n { name: "classes", getter: "classes" },\n { name: "title", getter: "title", setter: "set_title", accessor: true },\n { name: "text", getter: "text", setter: "set_text", accessor: true },\n { name: "value", getter: "value", setter: "set_value", accessor: true },\n { name: "placeholder", getter: "placeholder", setter: "set_placeholder", accessor: true },\n { name: "for", getter: "for_id", setter: "set_for_id", accessor: true },\n { name: "align", getter: "align", accessor: true },\n { name: "gap", getter: "gap", accessor: true },\n { name: "padding", getter: "padding", accessor: true },\n { name: "label", getter: "label", accessor: true },\n { name: "collapsible", getter: "collapsible", accessor: true },\n { name: "collapsed", getter: "collapsed", accessor: true },\n { name: "wrap", getter: "wrap", accessor: true },\n { name: "readonly", getter: "readonly", accessor: true },\n { name: "multiline", getter: "multiline", accessor: true },\n { name: "password", getter: "password", accessor: true },\n { name: "required", getter: "required", accessor: true },\n { name: "src", getter: "src", accessor: true },\n { name: "alt", getter: "alt", accessor: true },\n { name: "fit", getter: "fit", accessor: true },\n { name: "checked", getter: "checked", accessor: true },\n { name: "indeterminate", getter: "indeterminate", accessor: true },\n { name: "name", getter: "name", accessor: true },\n { name: "disabled", getter: "disabled", accessor: true },\n { name: "group", getter: "group", accessor: true },\n { name: "multiple", getter: "multiple", accessor: true },\n { name: "min", getter: "min", accessor: true },\n { name: "max", getter: "max", accessor: true },\n { name: "first_day_of_week", getter: "first_day_of_week", accessor: true },\n { name: "orientation", getter: "orientation", accessor: true },\n { name: "step", getter: "step", accessor: true },\n { name: "show_text", getter: "show_text", accessor: true },\n { name: "selected", getter: "selected", accessor: true },\n { name: "placement", getter: "placement", accessor: true },\n { name: "icon", getter: "icon", setter: "set_icon", accessor: true },\n { name: "closable", getter: "closable", accessor: true },\n { name: "selected_index", getter: "selected_index", accessor: true },\n { name: "selected_path", getter: "selected_path", accessor: true },\n { name: "variant", getter: "variant", accessor: true },\n];\n\nfunction _zpath_widget_class () {\n if ( not _widget_class_loaded ) {\n _widget_class_loaded := true;\n try {\n _widget_class := load_module( "std/gui/objects", "Widget" );\n }\n catch {\n _widget_class := null;\n }\n }\n return _widget_class;\n}\n\nfunction _zpath_is_widget ( obj ) {\n let Widget := _zpath_widget_class();\n return Widget \u2262 null and obj instanceof Widget;\n}\n\nfunction _zpath_xml_node_type ( obj ) {\n return null if not( obj can nodeType );\n try {\n return int( "" _ obj.nodeType() );\n }\n catch {\n }\n return null;\n}\n\nfunction _zpath_is_xml_document ( obj ) {\n return true if _zpath_xml_node_type(obj) = 9;\n return false if not( obj can documentElement );\n try {\n obj.documentElement();\n return true;\n }\n catch {\n }\n return false;\n}\n\nfunction _zpath_is_xml_node ( obj ) {\n return true if _zpath_xml_node_type(obj) \u2262 null;\n return _zpath_is_xml_document(obj);\n}\n\nfunction _zpath_widget_attr_name ( name ) {\n return ( name ~ /^@/ ) ? substr( name, 1 ) : name;\n}\n\nfunction _zpath_widget_attr_spec ( name ) {\n let attr := _zpath_widget_attr_name(name);\n return _WIDGET_ATTRS.first( fn spec \u2192 spec{name} \u2261 attr );\n}\n\nfunction _zpath_widget_attr_applies ( widget, spec ) {\n let attr := spec{name};\n if ( attr in [\n "id",\n "enabled",\n "visible",\n "width",\n "height",\n "minwidth",\n "minheight",\n "maxwidth",\n "maxheight",\n "classes",\n ] ) {\n return true;\n }\n\n switch ( class_name(widget) : eq ) {\n case "Window":\n return attr in [ "title" ];\n case "VBox", "HBox":\n return attr in [ "align", "gap", "padding" ];\n case "Frame":\n return attr in [ "label", "collapsible", "collapsed" ];\n case "Label":\n return attr in [ "text", "for" ];\n case "Text":\n return attr in [ "value", "multiline", "readonly", "wrap" ];\n case "RichText":\n return attr in [ "value", "multiline", "readonly" ];\n case "Image":\n return attr in [ "src", "alt", "fit" ];\n case "Input":\n return attr in [\n "value",\n "placeholder",\n "multiline",\n "readonly",\n "password",\n "required",\n ];\n case "DatePicker":\n return attr in [ "value", "min", "max", "first_day_of_week" ];\n case "Checkbox":\n return attr in [ "label", "checked", "indeterminate" ];\n case "Radio":\n return attr in [ "label", "value", "group", "checked" ];\n case "RadioGroup":\n return attr in [ "name", "value" ];\n case "Select":\n return attr in [ "value", "multiple" ];\n case "Menu":\n return attr in [ "text" ];\n case "MenuItem":\n return attr in [ "text", "disabled" ];\n case "Button":\n return attr in [ "text", "variant" ];\n case "Separator":\n return attr in [ "orientation" ];\n case "Slider":\n return attr in [ "value", "min", "max", "step", "orientation", "readonly" ];\n case "Progress":\n return attr in [ "value", "min", "max", "indeterminate", "show_text" ];\n case "Tabs":\n return attr in [ "selected", "placement" ];\n case "Tab":\n return attr in [ "title", "value", "icon", "selected", "closable" ];\n case "ListView":\n return attr in [ "selected_index", "multiple" ];\n case "TreeView":\n return attr in [ "selected_path", "multiple" ];\n }\n\n return false;\n}\n\nfunction _zpath_widget_get_attr ( widget, spec ) {\n let getter := spec{getter};\n return widget.(getter)();\n}\n\nfunction _zpath_widget_set_attr ( widget, spec, value ) {\n let setter := spec.get( "setter", null );\n if ( setter \u2262 null ) {\n try {\n return widget.(setter)(value);\n }\n catch {\n }\n }\n\n let getter := spec{getter};\n if ( spec.get( "accessor", false ) ) {\n try {\n return widget.(getter)(value);\n }\n catch {\n }\n }\n\n die `Widget attribute @${spec{name}} is not assignable`;\n}\n\nclass Node {\n let raw with set := null;\n let parent but weak := null;\n let _parent_keepalive := null;\n let key := null;\n let id := null;\n let ix := null;\n let tagged with set, has := null;\n\n static method _xml_node_type_code ( value ) {\n try {\n return int( "" _ value.nodeType() );\n }\n catch {\n return null;\n }\n }\n\n static method from_root ( obj ) {\n return self.wrap(obj);\n }\n\n static method wrap ( _obj, parent?, key?, ix? ) {\n\n let obj := _obj;\n\n if ( obj instanceof Node ) {\n return obj;\n }\n\n let Klass := determine_class( obj );\n\n let n := new Klass(\n raw: obj,\n parent: parent,\n key: key,\n ix: ix,\n );\n\n if ( obj instanceof TaggedValue ) {\n n.set_tagged(obj);\n while ( obj instanceof TaggedValue ) {\n obj := obj{value}\n }\n n.set_raw(obj);\n }\n\n n._build_id_with_parent(parent);\n\n return n;\n }\n\n method hold_parent ( owner ) {\n _parent_keepalive := owner;\n return self;\n }\n\n method _use_ref_as_id () {\n return false;\n }\n\n method _build_id () {\n id := self._generate_id;\n }\n\n method _build_id_with_parent ( owner ) {\n id := self._generate_id_with_parent(owner);\n }\n\n method _generate_id () {\n return self._generate_id_with_parent(parent);\n }\n\n method _generate_id_with_parent ( owner ) {\n\n if ( self._use_ref_as_id ) {\n return "ref:" _ ref_id(raw);\n }\n\n if ( owner \u2261 null ) {\n return "root";\n }\n\n if ( ix \u2262 null ) {\n if ( key \u2262 null ) {\n return owner.id _ "/" _ key _ "#" _ ix;\n }\n return owner.id _ "/#" _ ix;\n }\n\n if ( key \u2262 null ) {\n return owner.id _ "/" _ key;\n }\n\n return "rand:" _ floor( 1000 * 1000 * 1000 * Math.rand() );\n }\n\n method raw () { return raw; }\n method parent () { return parent; }\n method key () { return key; }\n method id () { return id; }\n method ix () { return ix; }\n method index () { return ix; }\n method tagged () { return tagged; }\n\n method type () {\n return typeof raw;\n }\n\n method value () {\n return raw;\n }\n\n method primitive_value () {\n return raw;\n }\n\n method string_value () {\n let p := self.primitive_value;\n return p \u2261 null ? null : "" _ p;\n }\n\n method number_value () {\n let v := self.primitive_value();\n return 0 + v if v ~ /^-?(?:[0-9]+(?:\\.[0-9]+)?|\\.[0-9]+)$/;\n return null;\n }\n\n method can_have_named_children () {\n return false;\n }\n\n method can_have_indexed_children () {\n return false;\n }\n\n method can_have_named_indexed_children () {\n return false;\n }\n\n method named_child ( name ) {\n self.children.first( fn kid \u2192 kid.key \u2261 name );\n }\n\n method indexed_child ( i ) {\n self.children.first( fn kid \u2192 kid.ix \u2261 i );\n }\n\n method named_indexed_child ( name, i ) {\n self.children.first( fn kid \u2192 kid.ix \u2261 i and kid.key \u2261 name );\n }\n\n method next_child ( child ) {\n let i := child.ix;\n return null if i \u2261 null;\n return self.indexed_child( i + 1 );\n }\n\n method prev_child ( child ) {\n let i := child.ix;\n return null if i \u2261 null;\n return null if i = 0;\n return self.indexed_child( i - 1 );\n }\n\n method next_sibling () {\n return self.parent.next_child( self );\n }\n\n method prev_sibling () {\n return self.parent.prev_child( self );\n }\n\n method named_attribute ( name ) {\n let attrname := ( name ~ /^@/ ) ? name : `@${name}`;\n self.attributes.first( fn kid \u2192 kid.name \u2261 attrname );\n }\n\n method children () {\n return [];\n }\n\n method attributes () {\n return [];\n }\n\n method name () {\n return key;\n }\n\n method dump () {\n return {\n "@type": self.type(),\n "@id": self.id(),\n "@key": self.key(),\n "@index": self.index(),\n "@value": self.primitive_value(),\n children: self.children().map( fn c -> c.dump() ),\n attributes: self.attributes().map( fn a -> a.dump() ),\n };\n }\n\n method find ( zpath ) {\n from std/path/z import ZPath;\n\n die "Node.find expects a ZPath object"\n unless zpath instanceof ZPath;\n\n return zpath.evaluate(self);\n }\n\n method do_action ( action ) {\n const container_node := self.parent();\n die "Path assignment target has no parent node"\n if container_node \u2261 null;\n return container_node.do_action_on_child( self, action );\n }\n\n method ref () {\n const container_node := self.parent();\n die "Path assignment target has no parent node"\n if container_node \u2261 null;\n return container_node.ref_on_child(self);\n }\n\n method do_action_on_child ( child, action ) {\n die `Path assignment target container \'${self.type()}\' is not assignable`;\n }\n\n method ref_on_child ( child ) {\n die `Path assignment target container \'${self.type()}\' is not assignable`;\n }\n}\n\nclass SimpleNode extends Node {\n}\n\nclass StringNode extends SimpleNode {\n\n method string_value () {\n let raw := self.raw;\n if ( raw instanceof BinaryString ) {\n return to_string( raw );\n }\n return raw;\n }\n\n method type () {\n return "string";\n }\n}\n\nclass NumberNode extends SimpleNode {\n method number_value () {\n let raw := self.raw;\n return raw;\n }\n\n method type () {\n return "number";\n }\n}\n\nclass BooleanNode extends SimpleNode {\n method type () {\n return "boolean";\n }\n}\n\nclass NullNode extends SimpleNode {\n method type () {\n return "null";\n }\n}\n\nclass ArrayNode extends Node {\n\n method _use_ref_as_id () {\n return true;\n }\n\n method type () {\n return "list";\n }\n\n method children () {\n let raw := self.raw;\n let out := [];\n let i := 0;\n while ( i < raw.length ) {\n let child := Node.wrap( raw[i], self, null, i );\n out.push(child);\n i++;\n }\n return out;\n }\n\n method can_have_indexed_children () {\n return false;\n }\n\n method do_action_on_child ( child, action ) {\n return super( child, action ) if action{op} ne ":=";\n\n const ix := child.ix();\n die "Path assignment expects numeric array index" if ix \u2261 null;\n let container := self.raw();\n container[ix] := action{value};\n return action{value};\n }\n\n method ref_on_child ( child ) {\n const ix := child.ix();\n die "Path assignment expects numeric array index" if ix \u2261 null;\n let container := self.raw();\n return \\ container[ix];\n }\n}\n\nclass SetNode extends Node {\n\n method _use_ref_as_id () {\n return true;\n }\n\n method children () {\n let raw := self.raw;\n let out := [];\n let i := 0;\n while ( i < raw.length ) {\n let child := Node.wrap( raw[i], self, null, null );\n out.push(child);\n i++;\n }\n return out;\n }\n}\n\nclass BagNode extends Node {\n\n method _use_ref_as_id () {\n return true;\n }\n\n method children () {\n let raw := self.raw;\n let out := [];\n let i := 0;\n while ( i < raw.length ) {\n let child := Node.wrap( raw[i], self, null, null );\n out.push(child);\n i++;\n }\n return out;\n }\n}\n\nclass DictNode extends Node {\n\n method _use_ref_as_id () {\n return true;\n }\n\n method type () {\n return "map";\n }\n\n method children () {\n let raw := self.raw;\n let out := [];\n for ( let k in raw.keys() ) {\n let child := Node.wrap( raw.get(k), self, k );\n out.push(child);\n }\n return out;\n }\n\n method can_have_named_children () {\n return true;\n }\n\n method do_action_on_child ( child, action ) {\n return super( child, action ) if action{op} ne ":=";\n\n const key := child.key();\n die "Path assignment expects string dict key" if key \u2261 null;\n let container := self.raw();\n container{(key)} := action{value};\n return action{value};\n }\n\n method ref_on_child ( child ) {\n const key := child.key();\n die "Path assignment expects string dict key" if key \u2261 null;\n let container := self.raw();\n return \\ container{(key)};\n }\n}\n\nclass PairListNode extends Node {\n\n method _use_ref_as_id () {\n return true;\n }\n\n method children () {\n let raw := self.raw;\n let pairs := raw.to_Array();\n let out := [];\n let i := 0;\n while ( i < pairs.length() ) {\n let pair := pairs[i];\n out.push( Node.wrap( pair, self, pair.key, i ) );\n i++;\n }\n return out;\n }\n\n method can_have_named_children () {\n return true;\n }\n\n method can_have_indexed_children () {\n return true;\n }\n\n method can_have_named_indexed_children () {\n return true;\n }\n\n method named_child ( name ) {\n return self.named_indexed_child( name, 0 );\n }\n\n method indexed_child ( i ) {\n let pairs := self.raw().to_Array();\n return null if i < 0 or i >= pairs.length();\n let pair := pairs[i];\n return Node.wrap( pair, self, pair.key, i );\n }\n\n method named_indexed_child ( name, i ) {\n let pairs := self.raw().to_Array();\n let seen := 0;\n let pos := 0;\n while ( pos < pairs.length() ) {\n let pair := pairs[pos];\n if ( pair.key \u2261 name ) {\n if ( seen = i ) {\n return Node.wrap( pair, self, pair.key, pos );\n }\n seen++;\n }\n pos++;\n }\n return null;\n }\n\n method _replace_value_at ( pair_ix, value ) {\n let container := self.raw();\n let pairs := container.to_Array();\n die "Path assignment expects numeric pairlist index"\n if pair_ix \u2261 null or pair_ix < 0 or pair_ix >= pairs.length();\n\n container.clear();\n let i := 0;\n while ( i < pairs.length() ) {\n let pair := pairs[i];\n container.add( pair.key, i = pair_ix ? value : pair.value );\n i++;\n }\n return value;\n }\n\n method do_action_on_child ( child, action ) {\n return super( child, action ) if action{op} ne ":=";\n\n return self._replace_value_at( child.ix(), action{value} );\n }\n\n method ref_on_child ( child ) {\n let pair_ix := child.ix();\n return function ( ... args ) {\n let current := self.indexed_child(pair_ix);\n die "Path assignment target pairlist entry no longer exists"\n if current \u2261 null;\n return current.raw().value if args.length() = 0;\n return self._replace_value_at( pair_ix, args[0] );\n };\n }\n}\n\nclass PairNode extends Node {\n\n method attributes () {\n let raw := self.raw;\n return [ "key", "value" ]\n .map( fn a \u2192 Node.wrap( raw.(a)(), self, `@${a}` ) );\n }\n\n method do_action_on_child ( child, action ) {\n return super( child, action ) if action{op} ne ":=";\n return super( child, action ) if child.key() ne "@value";\n\n let container := self.parent();\n return super( child, action )\n if container \u2261 null or not ( container instanceof PairListNode );\n return container.do_action_on_child( self, action );\n }\n\n method ref_on_child ( child ) {\n return super(child) if child.key() ne "@value";\n\n let container := self.parent();\n return super(child)\n if container \u2261 null or not ( container instanceof PairListNode );\n return container.ref_on_child(self);\n }\n}\n\nclass KDLDocumentNode extends Node {\n\n method _use_ref_as_id () {\n return true;\n }\n\n method type () {\n return "document";\n }\n\n method children () {\n let raw := self.raw;\n let out := [];\n let i := 0;\n while ( i < raw.nodes().length() ) {\n let child := raw.nodes()[i];\n out.push( Node.wrap( child, self, child.name(), i ) );\n i++;\n }\n return out;\n }\n\n method can_have_named_children () {\n return true;\n }\n\n method can_have_indexed_children () {\n return true;\n }\n\n method can_have_named_indexed_children () {\n return true;\n }\n\n method indexed_child ( i ) {\n return self.children().get( i, null );\n }\n\n method named_indexed_child ( name, i ) {\n let n := 0;\n for ( let child in self.children() ) {\n if ( child.key() \u2261 name ) {\n return child if n \u2261 i;\n n++;\n }\n }\n return null;\n }\n}\n\nclass KDLNodeNode extends Node {\n\n method _use_ref_as_id () {\n return true;\n }\n\n method type () {\n return "element";\n }\n\n method name () {\n return self.raw().name();\n }\n\n method children () {\n let raw := self.raw;\n let out := [];\n let i := 0;\n for ( let arg in raw.args() ) {\n out.push( Node.wrap( arg, self, null, i ) );\n i++;\n }\n for ( let child in raw.children() ) {\n out.push( Node.wrap( child, self, child.name(), i ) );\n i++;\n }\n return out;\n }\n\n method attributes () {\n let raw := self.raw;\n let out := [];\n let i := 0;\n for ( let pair in raw.props().to_Array() ) {\n out.push( Node.wrap( pair.value, self, "@" _ pair.key, i ) );\n i++;\n }\n return out;\n }\n\n method can_have_named_children () {\n return true;\n }\n\n method can_have_indexed_children () {\n return true;\n }\n\n method can_have_named_indexed_children () {\n return true;\n }\n\n method indexed_child ( i ) {\n return self.children().get( i, null );\n }\n\n method named_indexed_child ( name, i ) {\n let n := 0;\n for ( let child in self.children() ) {\n if ( child.key() \u2261 name ) {\n return child if n \u2261 i;\n n++;\n }\n }\n return null;\n }\n}\n\nclass KDLValueNode extends Node {\n\n method type () {\n return self.raw().type();\n }\n\n method value () {\n return self.raw().native_value();\n }\n\n method primitive_value () {\n return self.raw().native_value();\n }\n\n method string_value () {\n return self.raw().to_String();\n }\n\n method number_value () {\n try {\n return self.raw().to_Number();\n }\n catch {\n return null;\n }\n }\n\n method has_tagged () {\n return true;\n }\n\n method tagged () {\n let ann := self.raw().type_annotation();\n return {\n tag: ann \u2261 null ? null : "" _ ann,\n value: self.raw().native_value(),\n };\n }\n}\n\nclass XmlNodeNode extends Node {\n\n method _generate_id () {\n return\n try {\n let i := self.raw.unique_id;\n i \u2261 null ? super() : `xml:${i}`;\n }\n catch {\n super();\n };\n }\n\n method type () {\n let raw := self.raw;\n if ( _zpath_is_xml_document(raw) ) {\n return "document";\n }\n switch ( Node._xml_node_type_code(raw) ) {\n case 1:\n return "element";\n case 2, 18:\n return "attr";\n case 3, 4:\n return "text";\n case 8:\n return "comment";\n case 9:\n return "document";\n }\n\n return super();\n }\n\n method name () {\n let raw := self.raw;\n switch ( Node._xml_node_type_code(raw) ) {\n case 1:\n return raw.nodeName();\n case 2, 18:\n return "@" _ raw.nodeName();\n case 3, 4:\n return "#text";\n }\n\n return super();\n }\n\n method next_child ( child ) {\n return child.next_sibling;\n }\n\n method prev_child ( child ) {\n return child.prev_sibling;\n }\n\n method next_sibling () {\n let x := self.raw.nextSibling;\n return null if x \u2261 null;\n return Node.wrap( x, self.parent, x.nodeName );\n }\n\n method prev_sibling () {\n let x := self.raw.previousSibling;\n return null if x \u2261 null;\n return Node.wrap( x, self.parent, x.nodeName );\n }\n\n method primitive_value () {\n let raw := self.raw;\n if ( _zpath_is_xml_document(raw) ) {\n let de := raw.documentElement();\n return de \u2261 null ? null : de.textContent();\n }\n switch ( Node._xml_node_type_code(raw) ) {\n case 1:\n return raw.textContent;\n case 2, 18:\n return raw.nodeValue();\n case 3, 4, 8:\n return raw.data;\n case 9:\n let de := raw.documentElement();\n return de \u2261 null ? null : de.textContent();\n }\n\n return super();\n }\n\n method string_value () {\n let raw := self.raw;\n if ( _zpath_is_xml_document(raw) ) {\n let de := raw.documentElement();\n return de \u2261 null ? null : de.textContent();\n }\n switch ( Node._xml_node_type_code(raw) ) {\n case 1:\n return raw.textContent;\n case 2, 18:\n return raw.nodeValue();\n case 3, 4, 8:\n return raw.data;\n case 9:\n let de := raw.documentElement();\n return de \u2261 null ? null : de.textContent();\n }\n\n return super();\n }\n\n method can_have_named_children () {\n return true;\n }\n\n method can_have_indexed_children () {\n return true;\n }\n\n method can_have_named_indexed_children () {\n return true;\n }\n\n method children () {\n let raw := self.raw;\n if ( _zpath_is_xml_document(raw) ) {\n let de := raw.documentElement();\n return de \u2261 null ? []\n : [ Node.wrap( de, self, de.nodeName(), 0 ) ];\n }\n let node_type := Node._xml_node_type_code(raw);\n if ( node_type = 9 ) {\n let de := raw.documentElement();\n return de \u2261 null ? []\n : [ Node.wrap( de, self, de.nodeName(), 0 ) ];\n }\n\n if ( node_type = 1 ) {\n let kids := [];\n let count := {};\n for ( let child in raw.childNodes() ) {\n let nm := child.nodeName();\n let n := count.exists(nm) ? count.get( nm ) : 0;\n count.set( nm, n + 1 );\n kids.push( Node.wrap( child, self, nm, n ) );\n }\n return kids;\n }\n\n return [];\n }\n\n method attributes () {\n let raw := self.raw;\n let out := super();\n if ( Node._xml_node_type_code(raw) \u2261 1 ) {\n for ( let attr in raw.attributes() ) {\n let n := Node.wrap( attr, self, "@" _ attr.nodeName() );\n out.push(n);\n }\n }\n return out;\n }\n}\n\nclass TimeNode extends Node {\n\n method string_value () {\n let raw := self.raw;\n return raw.datetime;\n }\n\n method number_value () {\n let raw := self.raw;\n return raw.epoch;\n }\n\n method attributes () {\n let raw := self.raw;\n return [ "year", "month", "day", "hour", "min", "sec" ]\n .map( fn a \u2192 Node.wrap( raw.(a)(), self, `@${a}` ) );\n }\n}\n\nclass PathNode extends Node {\n\n method string_value () {\n let raw := self.raw;\n return raw.to_String;\n }\n\n method attributes () {\n let raw := self.raw;\n if ( raw.is_file ) {\n const stat := raw.stat;\n return stat.keys.map( fn a \u2192 Node.wrap( stat{a}, self, `@${a}` ) );\n }\n\n return super();\n }\n}\n\nclass WidgetNode extends Node {\n\n method _use_ref_as_id () {\n return true;\n }\n\n method type () {\n return "widget";\n }\n\n method name () {\n return class_name( self.raw() ) ?: "Widget";\n }\n\n method can_have_named_children () {\n return true;\n }\n\n method can_have_indexed_children () {\n return true;\n }\n\n method can_have_named_indexed_children () {\n return true;\n }\n\n method children () {\n let out := [];\n let i := 0;\n for ( let child in self.raw().children() ) {\n let nm := class_name(child) ?: "Widget";\n out.push( Node.wrap( child, self, nm, i ) );\n i++;\n }\n return out;\n }\n\n method indexed_child ( i ) {\n return self.children().get( i, null );\n }\n\n method named_indexed_child ( name, i ) {\n let n := 0;\n for ( let child in self.children() ) {\n if ( child.key() \u2261 name ) {\n return child if n \u2261 i;\n n++;\n }\n }\n return null;\n }\n\n method attributes () {\n let raw := self.raw();\n let out := [];\n let i := 0;\n for ( let spec in _WIDGET_ATTRS ) {\n next unless _zpath_widget_attr_applies( raw, spec );\n try {\n let value := _zpath_widget_get_attr( raw, spec );\n out.push( Node.wrap( value, self, "@" _ spec{name}, i ) );\n i++;\n }\n catch {\n }\n }\n return out;\n }\n\n method do_action_on_child ( child, action ) {\n return super( child, action ) if action{op} ne ":=";\n\n let spec := _zpath_widget_attr_spec( child.key() );\n return super( child, action ) if spec \u2261 null;\n\n _zpath_widget_set_attr( self.raw(), spec, action{value} );\n return action{value};\n }\n\n method ref_on_child ( child ) {\n let spec := _zpath_widget_attr_spec( child.key() );\n return super(child) if spec \u2261 null;\n\n let widget := self.raw();\n return function ( ... args ) {\n if ( args.length() = 0 ) {\n return _zpath_widget_get_attr( widget, spec );\n }\n _zpath_widget_set_attr( widget, spec, args[0] );\n return args[0];\n };\n }\n}\n\n// Need to define the body of this function late so it can refer back to\n// classes that have been declared.\n\nfunction determine_class ( obj ) {\n let Klass;\n\n let real_obj := obj;\n while ( real_obj instanceof TaggedValue ) {\n real_obj := real_obj{value};\n }\n\n if ( real_obj instanceof Null ) {\n Klass := NullNode;\n }\n else if ( real_obj instanceof Boolean ) {\n Klass := BooleanNode;\n }\n else if ( real_obj instanceof Number ) {\n Klass := NumberNode;\n }\n else if ( real_obj instanceof String or real_obj instanceof BinaryString ) {\n Klass := StringNode;\n }\n else if ( real_obj instanceof Array ) {\n Klass := ArrayNode;\n }\n else if ( real_obj instanceof Bag ) {\n Klass := BagNode;\n }\n else if ( real_obj instanceof Set ) {\n Klass := SetNode;\n }\n else if ( real_obj instanceof Dict ) {\n Klass := DictNode;\n }\n else if ( real_obj instanceof PairList ) {\n Klass := PairListNode;\n }\n else if ( real_obj instanceof Pair ) {\n Klass := PairNode;\n }\n else if ( real_obj instanceof KDLDocument ) {\n Klass := KDLDocumentNode;\n }\n else if ( real_obj instanceof KDLNode ) {\n Klass := KDLNodeNode;\n }\n else if ( real_obj instanceof KDLValue ) {\n Klass := KDLValueNode;\n }\n else if ( _zpath_is_xml_node(real_obj) ) {\n Klass := XmlNodeNode;\n }\n else if ( real_obj instanceof Time ) {\n Klass := TimeNode;\n }\n else if ( Path and ( real_obj instanceof Path ) ) {\n Klass := PathNode;\n }\n else if ( real_obj instanceof Object ) {\n if ( real_obj can __zpath_node_class__ ) {\n Klass = real_obj.__zpath_node_class__;\n }\n }\n\n if ( Klass \u2261 null and _zpath_is_widget(real_obj) ) {\n Klass := WidgetNode;\n }\n\n if ( Klass \u2261 null ) {\n Klass := XmlNodeNode if _zpath_is_xml_node(real_obj);\n }\n\n return Klass ?: Node;\n}\n';
|
|
47408
|
+
virtualFiles["/modules/std/path/z/node.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/path/z/node - Node wrapper used by std/path/z.\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nObjects of this class wrap underlying values and provide traversal and\ncoercion helpers used while evaluating ZPath expressions.\n\n=head1 EXPORTS\n\n=head2 Classes\n\n=over\n\n=item C<Node>\n\nBase wrapper for values traversed by ZPath.\n\n=over\n\n=item C<< Node.from_root(obj) >>\n\nParameters: C<obj> is any query root. Returns: C<Node>. Wraps a root\nvalue for traversal.\n\n=item C<< Node.wrap(obj, parent?, key?, ix?) >>\n\nParameters: C<obj> is any value, with optional parent, key, and index\nmetadata. Returns: C<Node>. Wraps a value in the appropriate node\nsubclass.\n\n=item C<< node.hold_parent(owner) >>\n\nParameters: C<owner> is a parent node. Returns: C<Node>. Retains parent\nmetadata on the node.\n\n=item C<< node.raw() >>, C<< node.value() >>, C<< node.primitive_value() >>\n\nParameters: none. Returns: value. Returns the wrapped raw, node, or\nprimitive value.\n\n=item C<< node.parent() >>\n\nParameters: none. Returns: C<Node> or C<null>. Returns the parent node.\n\n=item C<< node.key() >>, C<< node.ix() >>, C<< node.index() >>, C<< node.id() >>\n\nParameters: none. Returns: value. Returns traversal key, index, or\nstable node id metadata.\n\n=item C<< node.type() >>, C<< node.name() >>\n\nParameters: none. Returns: C<String> or C<null>. Returns the node type\nor name.\n\n=item C<< node.tagged() >>\n\nParameters: none. Returns: C<String> or C<null>. Returns a KDL value\ntag when present.\n\n=item C<< node.has_tagged() >>\n\nParameters: none. Returns: C<Boolean>. Returns true when tag metadata is\navailable.\n\n=item C<< node.string_value() >>, C<< node.number_value() >>\n\nParameters: none. Returns: C<String>, C<Number>, or C<null>. Coerces the\nnode for string or numeric ZPath operations.\n\n=item C<< node.can_have_named_children() >>, C<< node.can_have_indexed_children() >>, C<< node.can_have_named_indexed_children() >>\n\nParameters: none. Returns: C<Boolean>. Reports supported child lookup\nmodes.\n\n=item C<< node.named_child(name) >>, C<< node.indexed_child(i) >>, C<< node.named_indexed_child(name, i) >>\n\nParameters: C<name> is a child name and C<i> is an index. Returns:\nC<Node> or C<null>. Looks up child nodes.\n\n=item C<< node.next_child(child) >>, C<< node.prev_child(child) >>, C<< node.next_sibling() >>, C<< node.prev_sibling() >>\n\nParameters: C<child> is a child node where required. Returns: C<Node> or\nC<null>. Traverses adjacent nodes.\n\n=item C<< node.named_attribute(name) >>\n\nParameters: C<name> is an attribute name. Returns: C<Node> or C<null>.\nLooks up one attribute node.\n\n=item C<< node.children() >>, C<< node.attributes() >>\n\nParameters: none. Returns: C<Array>. Returns child or attribute nodes.\n\n=item C<< node.dump() >>\n\nParameters: none. Returns: C<String>. Returns a diagnostic rendering of\nthe node.\n\n=item C<< node.find(zpath) >>\n\nParameters: C<zpath> is a C<ZPath> object or subclass, such as C<ZZPath>.\nReturns: C<Array>. Evaluates a nested path from this node and returns\nselected node objects.\n\n=item C<< node.do_action(action) >>, C<< node.do_action_on_child(child, action) >>\n\nParameters: C<action> is a path action and C<child> is a child node.\nReturns: value. Applies path mutation actions.\n\n=item C<< node.ref() >>, C<< node.ref_on_child(child) >>\n\nParameters: C<child> is a child node where required. Returns:\nC<Function>. Returns a reference-like getter/setter.\n\n=back\n\n=item C<SimpleNode>, C<StringNode>, C<NumberNode>, C<BooleanNode>, C<NullNode>\n\nScalar node wrappers. They inherit the C<Node> API and override\nC<type>, C<string_value>, or C<number_value> where appropriate.\n\n=item C<ArrayNode>, C<SetNode>, C<BagNode>, C<DictNode>, C<PairListNode>, C<PairNode>\n\nCollection node wrappers. They inherit the C<Node> API and override\nchild, attribute, assignment, and reference methods appropriate to their\ncollection type. PairList children are C<PairNode> objects in pair order;\nC<indexed_child> uses global pair index, while C<named_indexed_child> uses\nthe occurrence index among pairs with the same key.\n\n=item C<KDLDocumentNode>, C<KDLNodeNode>, C<KDLValueNode>\n\nKDL node wrappers. They inherit the C<Node> API and expose KDL children,\nattributes, names, value tags, and scalar coercions.\n\n=item C<XmlNodeNode>\n\nXML node wrapper. It inherits the C<Node> API and exposes XML names,\nchildren, attributes, sibling traversal, and scalar text values.\n\n=item C<TimeNode>\n\nC<std/time> wrapper. It inherits the C<Node> API and exposes time\nattributes plus string and numeric values.\n\n=item C<PathNode>\n\nC<std/io> path wrapper. It inherits the C<Node> API and exposes path\nattributes plus string values.\n\n=item C<WidgetNode>\n\nC<std/gui/objects> widget wrapper. It inherits the C<Node> API and\nexposes widget children by widget class name and index, plus public\nwidget properties as assignable attributes.\n\n=back\n\n=head2 Variables\n\n=over\n\n=item C<determine_class>\n\nType: C<Function>. Chooses the concrete node wrapper class for a raw\nvalue.\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/path/z/node >> 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\nfrom std/data/cbor import TaggedValue;\nfrom std/data/kdl import KDLDocument, KDLNode, KDLValue;\nfrom std/internals import class_name, load_module, ref_id;\nfrom std/io try import Path;\nfrom std/math import Math;\nfrom std/string import substr;\nfrom std/time import Time;\n\n\nfunction determine_class;\nlet _widget_class_loaded := false;\nlet _widget_class := null;\n\nconst _WIDGET_ATTRS := [\n { name: "id", getter: "id", setter: "set_id" },\n { name: "enabled", getter: "enabled", setter: "set_enabled" },\n { name: "visible", getter: "visible", setter: "set_visible" },\n { name: "width", getter: "width", accessor: true },\n { name: "height", getter: "height", accessor: true },\n { name: "minwidth", getter: "minwidth", accessor: true },\n { name: "minheight", getter: "minheight", accessor: true },\n { name: "maxwidth", getter: "maxwidth", accessor: true },\n { name: "maxheight", getter: "maxheight", accessor: true },\n { name: "classes", getter: "classes" },\n { name: "title", getter: "title", setter: "set_title", accessor: true },\n { name: "text", getter: "text", setter: "set_text", accessor: true },\n { name: "value", getter: "value", setter: "set_value", accessor: true },\n { name: "placeholder", getter: "placeholder", setter: "set_placeholder", accessor: true },\n { name: "for", getter: "for_id", setter: "set_for_id", accessor: true },\n { name: "align", getter: "align", accessor: true },\n { name: "gap", getter: "gap", accessor: true },\n { name: "padding", getter: "padding", accessor: true },\n { name: "label", getter: "label", accessor: true },\n { name: "collapsible", getter: "collapsible", accessor: true },\n { name: "collapsed", getter: "collapsed", accessor: true },\n { name: "wrap", getter: "wrap", accessor: true },\n { name: "readonly", getter: "readonly", accessor: true },\n { name: "multiline", getter: "multiline", accessor: true },\n { name: "password", getter: "password", accessor: true },\n { name: "required", getter: "required", accessor: true },\n { name: "src", getter: "src", accessor: true },\n { name: "alt", getter: "alt", accessor: true },\n { name: "fit", getter: "fit", accessor: true },\n { name: "checked", getter: "checked", accessor: true },\n { name: "indeterminate", getter: "indeterminate", accessor: true },\n { name: "name", getter: "name", accessor: true },\n { name: "disabled", getter: "disabled", accessor: true },\n { name: "group", getter: "group", accessor: true },\n { name: "multiple", getter: "multiple", accessor: true },\n { name: "min", getter: "min", accessor: true },\n { name: "max", getter: "max", accessor: true },\n { name: "first_day_of_week", getter: "first_day_of_week", accessor: true },\n { name: "orientation", getter: "orientation", accessor: true },\n { name: "step", getter: "step", accessor: true },\n { name: "show_text", getter: "show_text", accessor: true },\n { name: "selected", getter: "selected", accessor: true },\n { name: "placement", getter: "placement", accessor: true },\n { name: "icon", getter: "icon", setter: "set_icon", accessor: true },\n { name: "closable", getter: "closable", accessor: true },\n { name: "selected_index", getter: "selected_index", accessor: true },\n { name: "selected_path", getter: "selected_path", accessor: true },\n { name: "variant", getter: "variant", accessor: true },\n];\n\nfunction _zpath_widget_class () {\n if ( not _widget_class_loaded ) {\n _widget_class_loaded := true;\n try {\n _widget_class := load_module( "std/gui/objects", "Widget" );\n }\n catch {\n _widget_class := null;\n }\n }\n return _widget_class;\n}\n\nfunction _zpath_is_widget ( obj ) {\n let Widget := _zpath_widget_class();\n return Widget \u2262 null and obj instanceof Widget;\n}\n\nfunction _zpath_xml_node_type ( obj ) {\n return null if not( obj can nodeType );\n try {\n return int( "" _ obj.nodeType() );\n }\n catch {\n }\n return null;\n}\n\nfunction _zpath_is_xml_document ( obj ) {\n return true if _zpath_xml_node_type(obj) = 9;\n return false if not( obj can documentElement );\n try {\n obj.documentElement();\n return true;\n }\n catch {\n }\n return false;\n}\n\nfunction _zpath_is_xml_node ( obj ) {\n return true if _zpath_xml_node_type(obj) \u2262 null;\n return _zpath_is_xml_document(obj);\n}\n\nfunction _zpath_widget_attr_name ( name ) {\n return ( name ~ /^@/ ) ? substr( name, 1 ) : name;\n}\n\nfunction _zpath_widget_attr_spec ( name ) {\n let attr := _zpath_widget_attr_name(name);\n return _WIDGET_ATTRS.first( fn spec \u2192 spec{name} \u2261 attr );\n}\n\nfunction _zpath_widget_attr_applies ( widget, spec ) {\n let attr := spec{name};\n if ( attr in [\n "id",\n "enabled",\n "visible",\n "width",\n "height",\n "minwidth",\n "minheight",\n "maxwidth",\n "maxheight",\n "classes",\n ] ) {\n return true;\n }\n\n switch ( class_name(widget) : eq ) {\n case "Window":\n return attr in [ "title" ];\n case "VBox", "HBox":\n return attr in [ "align", "gap", "padding" ];\n case "Frame":\n return attr in [ "label", "collapsible", "collapsed" ];\n case "Label":\n return attr in [ "text", "for" ];\n case "Text":\n return attr in [ "value", "multiline", "readonly", "wrap" ];\n case "RichText":\n return attr in [ "value", "multiline", "readonly" ];\n case "Image":\n return attr in [ "src", "alt", "fit" ];\n case "Input":\n return attr in [\n "value",\n "placeholder",\n "multiline",\n "readonly",\n "password",\n "required",\n ];\n case "DatePicker":\n return attr in [ "value", "min", "max", "first_day_of_week" ];\n case "Checkbox":\n return attr in [ "label", "checked", "indeterminate" ];\n case "Radio":\n return attr in [ "label", "value", "group", "checked" ];\n case "RadioGroup":\n return attr in [ "name", "value" ];\n case "Select":\n return attr in [ "value", "multiple" ];\n case "Menu":\n return attr in [ "text" ];\n case "MenuItem":\n return attr in [ "text", "disabled" ];\n case "Button":\n return attr in [ "text", "variant" ];\n case "Separator":\n return attr in [ "orientation" ];\n case "Slider":\n return attr in [ "value", "min", "max", "step", "orientation", "readonly" ];\n case "Progress":\n return attr in [ "value", "min", "max", "indeterminate", "show_text" ];\n case "Tabs":\n return attr in [ "selected", "placement" ];\n case "Tab":\n return attr in [ "title", "value", "icon", "selected", "closable" ];\n case "ListView":\n return attr in [ "selected_index", "multiple" ];\n case "TreeView":\n return attr in [ "selected_path", "multiple" ];\n }\n\n return false;\n}\n\nfunction _zpath_widget_get_attr ( widget, spec ) {\n let getter := spec{getter};\n return widget.(getter)();\n}\n\nfunction _zpath_widget_set_attr ( widget, spec, value ) {\n let setter := spec.get( "setter", null );\n if ( setter \u2262 null ) {\n try {\n return widget.(setter)(value);\n }\n catch {\n }\n }\n\n let getter := spec{getter};\n if ( spec.get( "accessor", false ) ) {\n try {\n return widget.(getter)(value);\n }\n catch {\n }\n }\n\n die `Widget attribute @${spec{name}} is not assignable`;\n}\n\nclass Node {\n let raw with set := null;\n let parent but weak := null;\n let _parent_keepalive := null;\n let key := null;\n let id := null;\n let ix := null;\n let tagged with set, has := null;\n\n static method _xml_node_type_code ( value ) {\n try {\n return int( "" _ value.nodeType() );\n }\n catch {\n return null;\n }\n }\n\n static method from_root ( obj ) {\n return self.wrap(obj);\n }\n\n static method wrap ( _obj, parent?, key?, ix? ) {\n\n let obj := _obj;\n\n if ( obj instanceof Node ) {\n return obj;\n }\n\n let Klass := determine_class( obj );\n\n let n := new Klass(\n raw: obj,\n parent: parent,\n key: key,\n ix: ix,\n );\n\n if ( obj instanceof TaggedValue ) {\n n.set_tagged(obj);\n while ( obj instanceof TaggedValue ) {\n obj := obj{value}\n }\n n.set_raw(obj);\n }\n\n n._build_id_with_parent(parent);\n\n return n;\n }\n\n method hold_parent ( owner ) {\n _parent_keepalive := owner;\n return self;\n }\n\n method _use_ref_as_id () {\n return false;\n }\n\n method _build_id () {\n id := self._generate_id;\n }\n\n method _build_id_with_parent ( owner ) {\n id := self._generate_id_with_parent(owner);\n }\n\n method _generate_id () {\n return self._generate_id_with_parent(parent);\n }\n\n method _generate_id_with_parent ( owner ) {\n\n if ( self._use_ref_as_id ) {\n return "ref:" _ ref_id(raw);\n }\n\n if ( owner \u2261 null ) {\n return "root";\n }\n\n if ( ix \u2262 null ) {\n if ( key \u2262 null ) {\n return owner.id _ "/" _ key _ "#" _ ix;\n }\n return owner.id _ "/#" _ ix;\n }\n\n if ( key \u2262 null ) {\n return owner.id _ "/" _ key;\n }\n\n return "rand:" _ floor( 1000 * 1000 * 1000 * Math.rand() );\n }\n\n method raw () { return raw; }\n method parent () { return parent; }\n method key () { return key; }\n method id () { return id; }\n method ix () { return ix; }\n method index () { return ix; }\n method tagged () { return tagged; }\n\n method type () {\n return typeof raw;\n }\n\n method value () {\n return raw;\n }\n\n method primitive_value () {\n return raw;\n }\n\n method string_value () {\n let p := self.primitive_value;\n return p \u2261 null ? null : "" _ p;\n }\n\n method number_value () {\n let v := self.primitive_value();\n return 0 + v if v ~ /^-?(?:[0-9]+(?:\\.[0-9]+)?|\\.[0-9]+)$/;\n return null;\n }\n\n method can_have_named_children () {\n return false;\n }\n\n method can_have_indexed_children () {\n return false;\n }\n\n method can_have_named_indexed_children () {\n return false;\n }\n\n method named_child ( name ) {\n self.children.first( fn kid \u2192 kid.key \u2261 name );\n }\n\n method indexed_child ( i ) {\n self.children.first( fn kid \u2192 kid.ix \u2261 i );\n }\n\n method named_indexed_child ( name, i ) {\n self.children.first( fn kid \u2192 kid.ix \u2261 i and kid.key \u2261 name );\n }\n\n method next_child ( child ) {\n let i := child.ix;\n return null if i \u2261 null;\n return self.indexed_child( i + 1 );\n }\n\n method prev_child ( child ) {\n let i := child.ix;\n return null if i \u2261 null;\n return null if i = 0;\n return self.indexed_child( i - 1 );\n }\n\n method next_sibling () {\n return self.parent.next_child( self );\n }\n\n method prev_sibling () {\n return self.parent.prev_child( self );\n }\n\n method named_attribute ( name ) {\n let attrname := ( name ~ /^@/ ) ? name : `@${name}`;\n self.attributes.first( fn kid \u2192 kid.name \u2261 attrname );\n }\n\n method children () {\n return [];\n }\n\n method attributes () {\n return [];\n }\n\n method name () {\n return key;\n }\n\n method dump () {\n return {\n "@type": self.type(),\n "@id": self.id(),\n "@key": self.key(),\n "@index": self.index(),\n "@value": self.primitive_value(),\n children: self.children().map( fn c -> c.dump() ),\n attributes: self.attributes().map( fn a -> a.dump() ),\n };\n }\n\n method find ( zpath ) {\n from std/path/z import ZPath;\n\n die "Node.find expects a ZPath object"\n unless zpath instanceof ZPath;\n\n return zpath.evaluate(self);\n }\n\n method do_action ( action ) {\n const container_node := self.parent();\n die "Path assignment target has no parent node"\n if container_node \u2261 null;\n return container_node.do_action_on_child( self, action );\n }\n\n method ref () {\n const container_node := self.parent();\n die "Path assignment target has no parent node"\n if container_node \u2261 null;\n return container_node.ref_on_child(self);\n }\n\n method do_action_on_child ( child, action ) {\n die `Path assignment target container \'${self.type()}\' is not assignable`;\n }\n\n method ref_on_child ( child ) {\n die `Path assignment target container \'${self.type()}\' is not assignable`;\n }\n}\n\nclass SimpleNode extends Node {\n}\n\nclass StringNode extends SimpleNode {\n\n method string_value () {\n let raw := self.raw;\n if ( raw instanceof BinaryString ) {\n return to_string( raw );\n }\n return raw;\n }\n\n method type () {\n return "string";\n }\n}\n\nclass NumberNode extends SimpleNode {\n method number_value () {\n let raw := self.raw;\n return raw;\n }\n\n method type () {\n return "number";\n }\n}\n\nclass BooleanNode extends SimpleNode {\n method type () {\n return "boolean";\n }\n}\n\nclass NullNode extends SimpleNode {\n method type () {\n return "null";\n }\n}\n\nclass ArrayNode extends Node {\n\n method _use_ref_as_id () {\n return true;\n }\n\n method type () {\n return "list";\n }\n\n method children () {\n let raw := self.raw;\n let out := [];\n let i := 0;\n while ( i < raw.length ) {\n let child := Node.wrap( raw[i], self, null, i );\n out.push(child);\n i++;\n }\n return out;\n }\n\n method can_have_indexed_children () {\n return false;\n }\n\n method do_action_on_child ( child, action ) {\n return super( child, action ) if action{op} ne ":=";\n\n const ix := child.ix();\n die "Path assignment expects numeric array index" if ix \u2261 null;\n let container := self.raw();\n container[ix] := action{value};\n return action{value};\n }\n\n method ref_on_child ( child ) {\n const ix := child.ix();\n die "Path assignment expects numeric array index" if ix \u2261 null;\n let container := self.raw();\n return \\ container[ix];\n }\n}\n\nclass SetNode extends Node {\n\n method _use_ref_as_id () {\n return true;\n }\n\n method children () {\n let raw := self.raw;\n let out := [];\n let i := 0;\n while ( i < raw.length ) {\n let child := Node.wrap( raw[i], self, null, null );\n out.push(child);\n i++;\n }\n return out;\n }\n}\n\nclass BagNode extends Node {\n\n method _use_ref_as_id () {\n return true;\n }\n\n method children () {\n let raw := self.raw;\n let out := [];\n let i := 0;\n while ( i < raw.length ) {\n let child := Node.wrap( raw[i], self, null, null );\n out.push(child);\n i++;\n }\n return out;\n }\n}\n\nclass DictNode extends Node {\n\n method _use_ref_as_id () {\n return true;\n }\n\n method type () {\n return "map";\n }\n\n method children () {\n let raw := self.raw;\n let out := [];\n for ( let k in raw.keys() ) {\n let child := Node.wrap( raw.get(k), self, k );\n out.push(child);\n }\n return out;\n }\n\n method can_have_named_children () {\n return true;\n }\n\n method do_action_on_child ( child, action ) {\n return super( child, action ) if action{op} ne ":=";\n\n const key := child.key();\n die "Path assignment expects string dict key" if key \u2261 null;\n let container := self.raw();\n container{(key)} := action{value};\n return action{value};\n }\n\n method ref_on_child ( child ) {\n const key := child.key();\n die "Path assignment expects string dict key" if key \u2261 null;\n let container := self.raw();\n return \\ container{(key)};\n }\n}\n\nclass PairListNode extends Node {\n\n method _use_ref_as_id () {\n return true;\n }\n\n method children () {\n let raw := self.raw;\n let pairs := raw.to_Array();\n let out := [];\n let i := 0;\n while ( i < pairs.length() ) {\n let pair := pairs[i];\n out.push( Node.wrap( pair, self, pair.key, i ) );\n i++;\n }\n return out;\n }\n\n method can_have_named_children () {\n return true;\n }\n\n method can_have_indexed_children () {\n return true;\n }\n\n method can_have_named_indexed_children () {\n return true;\n }\n\n method named_child ( name ) {\n return self.named_indexed_child( name, 0 );\n }\n\n method indexed_child ( i ) {\n let pairs := self.raw().to_Array();\n return null if i < 0 or i >= pairs.length();\n let pair := pairs[i];\n return Node.wrap( pair, self, pair.key, i );\n }\n\n method named_indexed_child ( name, i ) {\n let pairs := self.raw().to_Array();\n let seen := 0;\n let pos := 0;\n while ( pos < pairs.length() ) {\n let pair := pairs[pos];\n if ( pair.key \u2261 name ) {\n if ( seen = i ) {\n return Node.wrap( pair, self, pair.key, pos );\n }\n seen++;\n }\n pos++;\n }\n return null;\n }\n\n method _replace_value_at ( pair_ix, value ) {\n let container := self.raw();\n let pairs := container.to_Array();\n die "Path assignment expects numeric pairlist index"\n if pair_ix \u2261 null or pair_ix < 0 or pair_ix >= pairs.length();\n\n container.clear();\n let i := 0;\n while ( i < pairs.length() ) {\n let pair := pairs[i];\n container.add( pair.key, i = pair_ix ? value : pair.value );\n i++;\n }\n return value;\n }\n\n method do_action_on_child ( child, action ) {\n return super( child, action ) if action{op} ne ":=";\n\n return self._replace_value_at( child.ix(), action{value} );\n }\n\n method ref_on_child ( child ) {\n let pair_ix := child.ix();\n return function ( ... args ) {\n let current := self.indexed_child(pair_ix);\n die "Path assignment target pairlist entry no longer exists"\n if current \u2261 null;\n return current.raw().value if args.length() = 0;\n return self._replace_value_at( pair_ix, args[0] );\n };\n }\n}\n\nclass PairNode extends Node {\n\n method attributes () {\n let raw := self.raw;\n return [ "key", "value" ]\n .map( fn a \u2192 Node.wrap( raw.(a)(), self, `@${a}` ) );\n }\n\n method do_action_on_child ( child, action ) {\n return super( child, action ) if action{op} ne ":=";\n return super( child, action ) if child.key() ne "@value";\n\n let container := self.parent();\n return super( child, action )\n if container \u2261 null or not ( container instanceof PairListNode );\n return container.do_action_on_child( self, action );\n }\n\n method ref_on_child ( child ) {\n return super(child) if child.key() ne "@value";\n\n let container := self.parent();\n return super(child)\n if container \u2261 null or not ( container instanceof PairListNode );\n return container.ref_on_child(self);\n }\n}\n\nclass KDLDocumentNode extends Node {\n\n method _use_ref_as_id () {\n return true;\n }\n\n method type () {\n return "document";\n }\n\n method children () {\n let raw := self.raw;\n let out := [];\n let i := 0;\n while ( i < raw.nodes().length() ) {\n let child := raw.nodes()[i];\n out.push( Node.wrap( child, self, child.name(), i ) );\n i++;\n }\n return out;\n }\n\n method can_have_named_children () {\n return true;\n }\n\n method can_have_indexed_children () {\n return true;\n }\n\n method can_have_named_indexed_children () {\n return true;\n }\n\n method indexed_child ( i ) {\n return self.children().get( i, null );\n }\n\n method named_indexed_child ( name, i ) {\n let n := 0;\n for ( let child in self.children() ) {\n if ( child.key() \u2261 name ) {\n return child if n \u2261 i;\n n++;\n }\n }\n return null;\n }\n}\n\nclass KDLNodeNode extends Node {\n\n method _use_ref_as_id () {\n return true;\n }\n\n method type () {\n return "element";\n }\n\n method name () {\n return self.raw().name();\n }\n\n method children () {\n let raw := self.raw;\n let out := [];\n let i := 0;\n for ( let arg in raw.args() ) {\n out.push( Node.wrap( arg, self, null, i ) );\n i++;\n }\n for ( let child in raw.children() ) {\n out.push( Node.wrap( child, self, child.name(), i ) );\n i++;\n }\n return out;\n }\n\n method attributes () {\n let raw := self.raw;\n let out := [];\n let i := 0;\n for ( let pair in raw.props().to_Array() ) {\n out.push( Node.wrap( pair.value, self, "@" _ pair.key, i ) );\n i++;\n }\n return out;\n }\n\n method can_have_named_children () {\n return true;\n }\n\n method can_have_indexed_children () {\n return true;\n }\n\n method can_have_named_indexed_children () {\n return true;\n }\n\n method indexed_child ( i ) {\n return self.children().get( i, null );\n }\n\n method named_indexed_child ( name, i ) {\n let n := 0;\n for ( let child in self.children() ) {\n if ( child.key() \u2261 name ) {\n return child if n \u2261 i;\n n++;\n }\n }\n return null;\n }\n}\n\nclass KDLValueNode extends Node {\n\n method type () {\n return self.raw().type();\n }\n\n method value () {\n return self.raw().native_value();\n }\n\n method primitive_value () {\n return self.raw().native_value();\n }\n\n method string_value () {\n return self.raw().to_String();\n }\n\n method number_value () {\n try {\n return self.raw().to_Number();\n }\n catch {\n return null;\n }\n }\n\n method has_tagged () {\n return true;\n }\n\n method tagged () {\n let ann := self.raw().type_annotation();\n return {\n tag: ann \u2261 null ? null : "" _ ann,\n value: self.raw().native_value(),\n };\n }\n}\n\nclass XmlNodeNode extends Node {\n\n method _generate_id () {\n return\n try {\n let i := self.raw.unique_id;\n i \u2261 null ? super() : `xml:${i}`;\n }\n catch {\n super();\n };\n }\n\n method type () {\n let raw := self.raw;\n if ( _zpath_is_xml_document(raw) ) {\n return "document";\n }\n switch ( Node._xml_node_type_code(raw) ) {\n case 1:\n return "element";\n case 2, 18:\n return "attr";\n case 3, 4:\n return "text";\n case 8:\n return "comment";\n case 9:\n return "document";\n }\n\n return super();\n }\n\n method name () {\n let raw := self.raw;\n switch ( Node._xml_node_type_code(raw) ) {\n case 1:\n return raw.nodeName();\n case 2, 18:\n return "@" _ raw.nodeName();\n case 3, 4:\n return "#text";\n }\n\n return super();\n }\n\n method next_child ( child ) {\n return child.next_sibling;\n }\n\n method prev_child ( child ) {\n return child.prev_sibling;\n }\n\n method next_sibling () {\n let x := self.raw.nextSibling;\n return null if x \u2261 null;\n return Node.wrap( x, self.parent, x.nodeName );\n }\n\n method prev_sibling () {\n let x := self.raw.previousSibling;\n return null if x \u2261 null;\n return Node.wrap( x, self.parent, x.nodeName );\n }\n\n method primitive_value () {\n let raw := self.raw;\n if ( _zpath_is_xml_document(raw) ) {\n let de := raw.documentElement();\n return de \u2261 null ? null : de.textContent();\n }\n switch ( Node._xml_node_type_code(raw) ) {\n case 1:\n return raw.textContent;\n case 2, 18:\n return raw.nodeValue();\n case 3, 4, 8:\n return raw.data;\n case 9:\n let de := raw.documentElement();\n return de \u2261 null ? null : de.textContent();\n }\n\n return super();\n }\n\n method string_value () {\n let raw := self.raw;\n if ( _zpath_is_xml_document(raw) ) {\n let de := raw.documentElement();\n return de \u2261 null ? null : de.textContent();\n }\n switch ( Node._xml_node_type_code(raw) ) {\n case 1:\n return raw.textContent;\n case 2, 18:\n return raw.nodeValue();\n case 3, 4, 8:\n return raw.data;\n case 9:\n let de := raw.documentElement();\n return de \u2261 null ? null : de.textContent();\n }\n\n return super();\n }\n\n method can_have_named_children () {\n return true;\n }\n\n method can_have_indexed_children () {\n return true;\n }\n\n method can_have_named_indexed_children () {\n return true;\n }\n\n method children () {\n let raw := self.raw;\n if ( _zpath_is_xml_document(raw) ) {\n let de := raw.documentElement();\n return de \u2261 null ? []\n : [ Node.wrap( de, self, de.nodeName(), 0 ) ];\n }\n let node_type := Node._xml_node_type_code(raw);\n if ( node_type = 9 ) {\n let de := raw.documentElement();\n return de \u2261 null ? []\n : [ Node.wrap( de, self, de.nodeName(), 0 ) ];\n }\n\n if ( node_type = 1 ) {\n let kids := [];\n let count := {};\n for ( let child in raw.childNodes() ) {\n let nm := child.nodeName();\n let n := count.exists(nm) ? count.get( nm ) : 0;\n count.set( nm, n + 1 );\n kids.push( Node.wrap( child, self, nm, n ) );\n }\n return kids;\n }\n\n return [];\n }\n\n method attributes () {\n let raw := self.raw;\n let out := super();\n if ( Node._xml_node_type_code(raw) \u2261 1 ) {\n for ( let attr in raw.attributes() ) {\n let n := Node.wrap( attr, self, "@" _ attr.nodeName() );\n out.push(n);\n }\n }\n return out;\n }\n}\n\nclass TimeNode extends Node {\n\n method string_value () {\n let raw := self.raw;\n return raw.datetime;\n }\n\n method number_value () {\n let raw := self.raw;\n return raw.epoch;\n }\n\n method attributes () {\n let raw := self.raw;\n return [ "year", "month", "day", "hour", "min", "sec" ]\n .map( fn a \u2192 Node.wrap( raw.(a)(), self, `@${a}` ) );\n }\n}\n\nclass PathNode extends Node {\n\n method string_value () {\n let raw := self.raw;\n return raw.to_String;\n }\n\n method attributes () {\n let raw := self.raw;\n if ( raw.is_file ) {\n const stat := raw.stat;\n return stat.keys.map( fn a \u2192 Node.wrap( stat{a}, self, `@${a}` ) );\n }\n\n return super();\n }\n}\n\nclass WidgetNode extends Node {\n\n method _use_ref_as_id () {\n return true;\n }\n\n method type () {\n return "widget";\n }\n\n method name () {\n return class_name( self.raw() ) or? "Widget";\n }\n\n method can_have_named_children () {\n return true;\n }\n\n method can_have_indexed_children () {\n return true;\n }\n\n method can_have_named_indexed_children () {\n return true;\n }\n\n method children () {\n let out := [];\n let i := 0;\n for ( let child in self.raw().children() ) {\n let nm := class_name(child) or? "Widget";\n out.push( Node.wrap( child, self, nm, i ) );\n i++;\n }\n return out;\n }\n\n method indexed_child ( i ) {\n return self.children().get( i, null );\n }\n\n method named_indexed_child ( name, i ) {\n let n := 0;\n for ( let child in self.children() ) {\n if ( child.key() \u2261 name ) {\n return child if n \u2261 i;\n n++;\n }\n }\n return null;\n }\n\n method attributes () {\n let raw := self.raw();\n let out := [];\n let i := 0;\n for ( let spec in _WIDGET_ATTRS ) {\n next unless _zpath_widget_attr_applies( raw, spec );\n try {\n let value := _zpath_widget_get_attr( raw, spec );\n out.push( Node.wrap( value, self, "@" _ spec{name}, i ) );\n i++;\n }\n catch {\n }\n }\n return out;\n }\n\n method do_action_on_child ( child, action ) {\n return super( child, action ) if action{op} ne ":=";\n\n let spec := _zpath_widget_attr_spec( child.key() );\n return super( child, action ) if spec \u2261 null;\n\n _zpath_widget_set_attr( self.raw(), spec, action{value} );\n return action{value};\n }\n\n method ref_on_child ( child ) {\n let spec := _zpath_widget_attr_spec( child.key() );\n return super(child) if spec \u2261 null;\n\n let widget := self.raw();\n return function ( ... args ) {\n if ( args.length() = 0 ) {\n return _zpath_widget_get_attr( widget, spec );\n }\n _zpath_widget_set_attr( widget, spec, args[0] );\n return args[0];\n };\n }\n}\n\n// Need to define the body of this function late so it can refer back to\n// classes that have been declared.\n\nfunction determine_class ( obj ) {\n let Klass;\n\n let real_obj := obj;\n while ( real_obj instanceof TaggedValue ) {\n real_obj := real_obj{value};\n }\n\n if ( real_obj instanceof Null ) {\n Klass := NullNode;\n }\n else if ( real_obj instanceof Boolean ) {\n Klass := BooleanNode;\n }\n else if ( real_obj instanceof Number ) {\n Klass := NumberNode;\n }\n else if ( real_obj instanceof String or real_obj instanceof BinaryString ) {\n Klass := StringNode;\n }\n else if ( real_obj instanceof Array ) {\n Klass := ArrayNode;\n }\n else if ( real_obj instanceof Bag ) {\n Klass := BagNode;\n }\n else if ( real_obj instanceof Set ) {\n Klass := SetNode;\n }\n else if ( real_obj instanceof Dict ) {\n Klass := DictNode;\n }\n else if ( real_obj instanceof PairList ) {\n Klass := PairListNode;\n }\n else if ( real_obj instanceof Pair ) {\n Klass := PairNode;\n }\n else if ( real_obj instanceof KDLDocument ) {\n Klass := KDLDocumentNode;\n }\n else if ( real_obj instanceof KDLNode ) {\n Klass := KDLNodeNode;\n }\n else if ( real_obj instanceof KDLValue ) {\n Klass := KDLValueNode;\n }\n else if ( _zpath_is_xml_node(real_obj) ) {\n Klass := XmlNodeNode;\n }\n else if ( real_obj instanceof Time ) {\n Klass := TimeNode;\n }\n else if ( Path and ( real_obj instanceof Path ) ) {\n Klass := PathNode;\n }\n else if ( real_obj instanceof Object ) {\n if ( real_obj can __zpath_node_class__ ) {\n Klass = real_obj.__zpath_node_class__;\n }\n }\n\n if ( Klass \u2261 null and _zpath_is_widget(real_obj) ) {\n Klass := WidgetNode;\n }\n\n if ( Klass \u2261 null ) {\n Klass := XmlNodeNode if _zpath_is_xml_node(real_obj);\n }\n\n // The dispatch probe returns a class object or null.\n return Klass ?: Node;\n}\n';
|
|
47341
47409
|
virtualFiles["/modules/std/path/z/operators.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/path/z/operators - Operator definitions for ZPath.\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nThis module defines the base ZPath operator model and the standard\noperator table used by C<std/path/z>.\n\n=head1 EXPORTS\n\n=head2 Traits\n\n=over\n\n=item C<EvalHelpers>\n\nShared helpers for operator and function definitions.\n\n=over\n\n=item C<< helper.wrap(value) >>\n\nParameters: C<value> is any value. Returns: C<Array>. Wraps C<value> as\na one-item ZPath node array.\n\n=item C<< helper.wrap_for_array(value) >>\n\nParameters: C<value> is any value. Returns: C<Node>. Wraps C<value> as a\nZPath node for array results.\n\n=back\n\n=back\n\n=head2 Classes\n\n=over\n\n=item C<< Operator({ spelling: String, kind: String, precedence: Number, ... }) >>\n\nConstructs an operator definition. Returns: C<Operator>.\n\n=over\n\n=item C<< operator.is_unary() >>, C<< operator.is_binary() >>\n\nParameters: none. Returns: C<Boolean>. Reports whether the operator is\nunary or binary.\n\n=item C<< operator.requires_whitespace() >>\n\nParameters: none. Returns: C<Boolean>. Reports whether the lexer\nrequires whitespace around the operator.\n\n=item C<< operator.lexer_should_ignore() >>\n\nParameters: none. Returns: C<Boolean>. Reports whether the lexer should\nignore this operator definition.\n\n=item C<< operator.is_right_associative() >>\n\nParameters: none. Returns: C<Boolean>. Reports whether the operator is\nright associative.\n\n=item C<< operator.char_length() >>\n\nParameters: none. Returns: C<Number>. Returns the operator spelling\nlength.\n\n=item C<< operator.precedence_is(lvl) >>\n\nParameters: C<lvl> is a precedence level. Returns: C<Boolean>. Returns\ntrue when the operator has that precedence.\n\n=back\n\n=back\n\n=head2 Constants\n\n=over\n\n=item C<STANDARD_OPERATORS>\n\nType: C<Array>. Standard ZPath operator definitions.\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/path/z/operators >> 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\nfrom std/path/z/node import Node;\n\nfunction _floaty_modulus ( ln, rn ) {\n let count := floor( ln / rn ); //\n return ln - ( count * rn );\n}\n\ntrait EvalHelpers {\n method _handle_numeric_operand ( ev, ctx, expr ) {\n const result := ev.eval_expr( expr, ev.nested_ctx( ctx ) );\n return 0 unless result.length;\n return ev.to_number( result[0] );\n }\n\n method _handle_stringy_operand ( ev, ctx, expr ) {\n const result := ev.eval_expr( expr, ev.nested_ctx( ctx ) );\n return 0 unless result.length;\n return ev.to_number( result[0] );\n }\n\n method wrap ( value ) {\n return [ Node.wrap( value ) ];\n }\n\n method wrap_for_array ( value ) {\n return Node.wrap( value );\n }\n}\n\nclass Operator with EvalHelpers {\n let String spelling with get;\n let String alias with get, has;\n let String kind with get;\n let Number precedence with get;\n let Boolean unary := false;\n let Boolean require_ws := false;\n let Boolean lex_ignore := false;\n let Boolean right_assoc := false;\n let Function f;\n\n method is_unary () {\n return unary;\n }\n\n method is_binary () {\n return not unary;\n }\n\n method requires_whitespace () {\n return require_ws;\n }\n\n method lexer_should_ignore () {\n return lex_ignore;\n }\n\n method is_right_associative () {\n return right_assoc;\n }\n\n method char_length () {\n return length spelling;\n }\n\n method precedence_is ( lvl ) {\n return precedence = lvl;\n }\n}\n\nconst STANDARD_OPERATORS := [\n new Operator(\n spelling: "||",\n kind: "OROR",\n precedence: 2,\n f: function ( op, ev, ast, ctx, left, right ) {\n const left_val := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n if ( left_val.length and ev.truthy( left_val[0] ) ) {\n return op.wrap( true );\n }\n const right_val := ev.eval_expr( right, ev.nested_ctx( ctx ) );\n if ( right_val.length and ev.truthy( right_val[0] ) ) {\n return op.wrap( true );\n }\n return op.wrap( false );\n },\n ),\n\n new Operator(\n spelling: "&&",\n kind: "ANDAND",\n precedence: 4,\n f: function ( op, ev, ast, ctx, left, right ) {\n const left_val := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n if ( left_val.length and ev.truthy( left_val[0] ) ) {\n const right_val := ev.eval_expr( right, ev.nested_ctx( ctx ) );\n if ( right_val.length and ev.truthy( right_val[0] ) ) {\n return op.wrap( true );\n }\n }\n return op.wrap( false );\n },\n ),\n\n new Operator(\n spelling: "==",\n kind: "EQEQ",\n precedence: 12,\n f: function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n const right_vals := ev.eval_expr( right, ev.nested_ctx( ctx ) );\n let is_eq := false;\n\n if ( left_vals and right_vals ) {\n for ( let ln in left_vals ) {\n last if is_eq;\n for ( let rn in right_vals ) {\n last if is_eq;\n if ( ev.equals( ln, rn ) ) {\n is_eq := true;\n }\n }\n }\n }\n\n return op.wrap( is_eq );\n },\n ),\n\n new Operator(\n spelling: "!=",\n kind: "NEQ",\n precedence: 12,\n f: function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n const right_vals := ev.eval_expr( right, ev.nested_ctx( ctx ) );\n let is_eq := false;\n\n if ( left_vals and right_vals ) {\n for ( let ln in left_vals ) {\n last if is_eq;\n for ( let rn in right_vals ) {\n last if is_eq;\n if ( ev.equals( ln, rn ) ) {\n is_eq := true;\n }\n }\n }\n }\n\n return op.wrap( not is_eq );\n },\n ),\n\n new Operator(\n spelling: ">=",\n kind: "GE",\n precedence: 14,\n f: function ( op, ev, ast, ctx, left, right ) {\n let left_val := op._handle_numeric_operand( ev, ctx, left );\n let right_val := op._handle_numeric_operand( ev, ctx, right );\n if ( ( left_val \u2261 null ) or ( right_val \u2261 null ) ) {\n left_val := op._handle_stringy_operand( ev, ctx, left );\n right_val := op._handle_stringy_operand( ev, ctx, right );\n return op.wrap( left_val ge right_val );\n }\n return op.wrap( left_val \u2265 right_val );\n },\n ),\n\n new Operator(\n spelling: "<=",\n kind: "LE",\n precedence: 14,\n f: function ( op, ev, ast, ctx, left, right ) {\n let left_val := op._handle_numeric_operand( ev, ctx, left );\n let right_val := op._handle_numeric_operand( ev, ctx, right );\n if ( ( left_val \u2261 null ) or ( right_val \u2261 null ) ) {\n left_val := op._handle_stringy_operand( ev, ctx, left );\n right_val := op._handle_stringy_operand( ev, ctx, right );\n return op.wrap( left_val le right_val );\n }\n return op.wrap( left_val \u2264 right_val );\n },\n ),\n\n new Operator(\n spelling: "+",\n kind: "PLUS",\n require_ws: true,\n precedence: 16,\n f: function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val + right_val );\n },\n ),\n\n new Operator(\n spelling: "-",\n kind: "MINUS",\n require_ws: true,\n precedence: 16,\n f: function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val - right_val );\n },\n ),\n\n new Operator(\n spelling: "%",\n kind: "PCT",\n require_ws: true,\n precedence: 18,\n f: function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n if ( ( left_val ~ /\\./ ) or ( right_val ~ /\\./ ) ) {\n return op.wrap( _floaty_modulus( left_val, right_val ) );\n }\n return op.wrap( left_val mod right_val );\n },\n ),\n\n new Operator(\n spelling: "*",\n kind: "TIMES",\n require_ws: true,\n precedence: 18,\n lex_ignore: true,\n alias: "STAR",\n f: function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val \xD7 right_val );\n },\n ),\n\n new Operator(\n spelling: "/",\n kind: "DIVIDE",\n require_ws: true,\n precedence: 18,\n lex_ignore: true,\n alias: "SLASH",\n f: function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val \xF7 right_val );\n },\n ),\n\n new Operator(\n spelling: "^",\n kind: "BXOR",\n precedence: 8,\n f: function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val ^ right_val );\n },\n ),\n\n new Operator(\n spelling: "&",\n kind: "BAND",\n precedence: 10,\n f: function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val & right_val );\n },\n ),\n\n new Operator(\n spelling: "|",\n kind: "BOR",\n precedence: 6,\n f: function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val | right_val );\n },\n ),\n\n new Operator(\n spelling: ">",\n kind: "GT",\n precedence: 14,\n f: function ( op, ev, ast, ctx, left, right ) {\n let left_val := op._handle_numeric_operand( ev, ctx, left );\n let right_val := op._handle_numeric_operand( ev, ctx, right );\n if ( ( left_val \u2261 null ) or ( right_val \u2261 null ) ) {\n left_val := op._handle_stringy_operand( ev, ctx, left );\n right_val := op._handle_stringy_operand( ev, ctx, right );\n return op.wrap( left_val gt right_val );\n }\n return op.wrap( left_val > right_val );\n },\n ),\n\n new Operator(\n spelling: "<",\n kind: "LT",\n precedence: 14,\n f: function ( op, ev, ast, ctx, left, right ) {\n let left_val := op._handle_numeric_operand( ev, ctx, left );\n let right_val := op._handle_numeric_operand( ev, ctx, right );\n if ( ( left_val \u2261 null ) or ( right_val \u2261 null ) ) {\n left_val := op._handle_stringy_operand( ev, ctx, left );\n right_val := op._handle_stringy_operand( ev, ctx, right );\n return op.wrap( left_val le right_val );\n }\n return op.wrap( left_val < right_val );\n },\n ),\n\n new Operator(\n spelling: "!",\n kind: "NOT",\n unary: true,\n precedence: 20,\n f: function ( op, ev, ast, ctx, expr ) {\n const got := ev.eval_expr( expr, ctx );\n const value := got.length() > 0 ? got[0] : null;\n return op.wrap( not ev.truthy(value) );\n },\n ),\n\n new Operator(\n spelling: "~",\n kind: "BNOT",\n unary: true,\n precedence: 20,\n f: function ( op, ev, ast, ctx, expr ) {\n const value := op._handle_numeric_operand( ev, ctx, expr );\n return op.wrap( ~value );\n },\n ),\n];\n';
|
|
47342
47410
|
virtualFiles["/modules/std/path/z/parser.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/path/z/parser - Pure Zuzu parser for ZPath expressions.\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nThis module provides the pure-Zuzu parser used by ZPath.\n\n=head1 EXPORTS\n\n=head2 Classes\n\n=over\n\n=item C<< Parser({ lexer_class?, allowed_operators }) >>\n\nConstructs a ZPath parser. Returns: C<Parser>.\n\n=over\n\n=item C<< parser.parse_top_level_terms(src) >>\n\nParameters: C<src> is a ZPath expression string. Returns: C<Array>.\nParses comma-separated top-level expression terms.\n\n=item C<< parser.parse_expression(lx) >>\n\nParameters: C<lx> is a C<Lexer>. Returns: C<Dict>. Parses an expression.\n\n=item C<< parser.parse_ternary(lx) >>\n\nParameters: C<lx> is a C<Lexer>. Returns: C<Dict>. Parses ternary and\nElvis expressions.\n\n=item C<< parser.parse_subexpression(lx, min_prec) >>\n\nParameters: C<lx> is a C<Lexer> and C<min_prec> is a precedence floor.\nReturns: C<Dict>. Parses a precedence-climbing subexpression.\n\n=item C<< parser.parse_primary(lx) >>\n\nParameters: C<lx> is a C<Lexer>. Returns: C<Dict>. Parses a primary\nexpression.\n\n=back\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/path/z/parser >> 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\nfrom std/string import trim;\nfrom std/path/z/lexer import Lexer;\n\nclass Parser {\n let lexer_class;\n let allowed_operators;\n let _binop_prec := {};\n let _unop_prec := {};\n let _need_ws := {};\n let _right_assoc := {};\n let _path_terminators := {};\n let _allow_elvis := false;\n\n method __build__ () {\n lexer_class ?:= Lexer;\n self._init_path_terminators();\n for ( let op in allowed_operators ) {\n let spell := op.get_spelling();\n if ( op.is_unary() ) {\n _unop_prec.set( spell, op.get_precedence() );\n }\n else {\n if ( op.get_kind() \u2261 "ELVIS" ) {\n _allow_elvis := true;\n _path_terminators.set( op.get_kind(), true );\n next;\n }\n _binop_prec.set( spell, op.get_precedence() );\n _path_terminators.set( op.get_kind(), true );\n if ( op.has_alias() ) {\n _path_terminators.set( op.get_alias(), true );\n }\n if ( op.requires_whitespace() ) {\n _need_ws.set( spell, true );\n }\n if ( op.is_right_associative() ) {\n _right_assoc.set( spell, true );\n }\n }\n }\n }\n\n method _init_path_terminators () {\n for ( let k in [\n "EOF",\n "COMMA",\n "RPAREN",\n "RBRACK",\n "QMARK",\n "COLON",\n ] ) {\n _path_terminators.set( k, true );\n }\n }\n\n method parse_top_level_terms ( src ) {\n let terms := [];\n let lexer := new lexer_class(\n src: src,\n allowed_operators: allowed_operators,\n );\n\n while ( true ) {\n let expr := self.parse_expression(lexer);\n terms.push(expr);\n if ( lexer.peek_kind() \u2261 "COMMA" ) {\n lexer.next_tok();\n next;\n }\n lexer.expect("EOF");\n last;\n }\n\n return terms;\n }\n\n method _trim ( s ) {\n return trim(s);\n }\n\n method parse_expression ( lx ) {\n return self.parse_ternary(lx);\n }\n\n method parse_ternary ( lx ) {\n let cond := self.parse_subexpression( lx, 1 );\n\n if ( lx.peek_kind() \u2261 "QMARK" ) {\n lx.next_tok();\n let then := self.parse_expression(lx); // ZZPath should use: self.parse_subexpression( lx, 1 )\n lx.expect("COLON");\n let els := self.parse_expression(lx);\n return { t: "ternary", c: cond, a: then, b: els };\n }\n if ( _allow_elvis and lx.peek_kind() \u2261 "ELVIS" ) {\n lx.next_tok();\n let fallback := self.parse_expression(lx);\n return { t: "elvis", c: cond, b: fallback };\n }\n\n return cond;\n }\n\n method parse_subexpression ( lx, min_prec ) {\n let left := self._parse_maybe_unary( lx, min_prec );\n\n while ( true ) {\n let spell := lx.peek{v};\n let op_prec := _binop_prec.get( spell, null );\n last if op_prec \u2261 null or op_prec < min_prec;\n\n let op := lx.next_tok;\n if ( _need_ws.exists( spell ) ) {\n if ( not ( op{ws_before} and op{ws_after} ) ) {\n die `Binary operator \'${spell}\' requires whitespace around it`;\n }\n }\n\n let next_min := _right_assoc.exists(spell) ? op_prec : op_prec + 1;\n let right := self.parse_subexpression( lx, next_min );\n left := { t: "bin", op: spell, l: left, r: right };\n }\n\n return left;\n }\n\n method _parse_maybe_unary ( lx, min_prec ) {\n let spell := lx.peek{v};\n let op_prec := _unop_prec.get( spell, null );\n if ( op_prec \u2262 null and op_prec >= min_prec ) {\n let op := lx.next_tok{v};\n let e := self._parse_maybe_unary( lx, op_prec );\n return { t: "un", op: op, e: e };\n }\n return self.parse_primary( lx );\n }\n\n method parse_primary ( lx ) {\n let k := lx.peek_kind;\n\n if ( k \u2261 "NUMBER" ) {\n return { t: "num", v: lx.next_tok(){v} };\n }\n if ( k \u2261 "STRING" ) {\n return { t: "str", v: lx.next_tok(){v} };\n }\n if ( k \u2261 "LPAREN" ) {\n lx.next_tok();\n let e := self.parse_expression(lx);\n lx.expect("RPAREN");\n return e;\n }\n\n if ( k \u2261 "NAME" and lx.peek_kind_n(1) \u2261 "LPAREN" ) {\n let name := lx.next_tok(){v};\n lx.expect("LPAREN");\n let args := [];\n if ( lx.peek_kind() \u2262 "RPAREN" ) {\n args.push( self.parse_expression(lx) );\n while ( lx.peek_kind() \u2261 "COMMA" ) {\n lx.next_tok();\n args.push( self.parse_expression(lx) );\n }\n }\n lx.expect("RPAREN");\n return { t: "fn", n: name, a: args };\n }\n\n return self._parse_path_expr(lx);\n }\n\n method _is_path_terminator ( k ) {\n return _path_terminators.exists(k);\n }\n\n method _parse_path_expr ( lx ) {\n let segs := [];\n\n if ( lx.peek_kind() \u2261 "SLASH_PATH" ) {\n lx.next_tok();\n let root := { k: "root", q: [] };\n segs.push(root);\n\n if ( lx.peek_kind() \u2261 "LBRACK" ) {\n root{q} := self._parse_qualifiers(lx);\n }\n\n if ( self._is_path_terminator( lx.peek_kind() ) ) {\n return { t: "path", s: segs };\n }\n }\n else if ( lx.peek_kind() \u2261 "LBRACK" ) {\n let seg := { k: "dot", q: self._parse_qualifiers(lx) };\n segs.push(seg);\n if ( self._is_path_terminator( lx.peek_kind() ) ) {\n return { t: "path", s: segs };\n }\n }\n\n if (\n lx.peek_kind() \u2262 "SLASH_PATH"\n and not self._is_path_terminator( lx.peek_kind() )\n ) {\n segs.push( self._parse_path_segment(lx) );\n }\n\n while ( lx.peek_kind() \u2261 "SLASH_PATH" ) {\n lx.next_tok();\n if ( lx.peek_kind() \u2261 "LBRACK" ) {\n let seg := { k: "star", q: [] };\n seg{q} := self._parse_qualifiers(lx);\n segs.push(seg);\n next;\n }\n segs.push( self._parse_path_segment(lx) );\n }\n\n return { t: "path", s: segs };\n }\n\n method _parse_path_segment ( lx ) {\n let k := lx.peek_kind();\n let seg := null;\n\n if ( k \u2261 "DOT" ) {\n lx.next_tok();\n seg := { k: "dot" };\n }\n else if ( k \u2261 "DOTDOT" ) {\n lx.next_tok();\n seg := { k: "parent" };\n }\n else if ( k \u2261 "DOTDOTSTAR" ) {\n lx.next_tok();\n seg := { k: "ancestors" };\n }\n else if ( k \u2261 "STAR_PATH" ) {\n lx.next_tok();\n seg := { k: "star" };\n }\n else if ( k \u2261 "STARSTAR" ) {\n lx.next_tok();\n seg := { k: "desc" };\n }\n else if ( k \u2261 "INDEX" ) {\n let i := lx.next_tok(){v};\n seg := { k: "index", i: i };\n }\n else if ( k \u2261 "NUMBER" ) {\n let i := lx.next_tok(){v};\n seg := { k: "index", i: i };\n }\n else if ( k \u2261 "NAME" and lx.peek_kind_n(1) \u2261 "LPAREN" ) {\n let name := lx.next_tok(){v};\n lx.expect("LPAREN");\n let args := [];\n if ( lx.peek_kind() \u2262 "RPAREN" ) {\n args.push( self.parse_expression(lx) );\n while ( lx.peek_kind() \u2261 "COMMA" ) {\n lx.next_tok();\n args.push( self.parse_expression(lx) );\n }\n }\n lx.expect("RPAREN");\n seg := { k: "fnseg", n: name, a: args };\n }\n else if ( k \u2261 "NAME" ) {\n let n := lx.next_tok(){v};\n seg := { k: "name", n: n };\n }\n else {\n die `Unexpected token in path segment: ${k}`;\n }\n\n if ( seg{k} \u2261 "name" and lx.peek_kind() \u2261 "INDEX" ) {\n seg{i} := lx.next_tok(){v};\n }\n\n seg{q} := self._parse_qualifiers(lx);\n return seg;\n }\n\n method _parse_qualifiers ( lx ) {\n let q := [];\n\n while ( lx.peek_kind() \u2261 "LBRACK" ) {\n lx.next_tok();\n let e := self.parse_expression(lx);\n lx.expect("RBRACK");\n q.push(e);\n }\n\n return q;\n }\n}\n';
|
|
47343
47411
|
virtualFiles["/modules/std/path/zz.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/path/zz - ZuzuScript-flavoured path selectors.\n\n=head1 SYNOPSIS\n\n from std/path/zz import ZZPath;\n\n let data := { users: [ { name: "Ada" } ] };\n say( ( new ZZPath( path: "/users/#0/name" ) ).first(data) );\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nThis module provides the public C<ZZPath> class. It currently reuses the\nZPath traversal, parser, lexer, assignment, and reference\nmachinery while routing expression operators through\nC<std/path/zz/operators> and expression functions through\nC<std/path/zz/functions>.\n\n=head1 EXPORTS\n\n=head2 Classes\n\n=over\n\n=item C<ZZEvaluator>\n\nEvaluator class that supplies ZZPath operators and functions.\n\n=over\n\n=item C<< evaluator.operator_definitions() >>\n\nParameters: none. Returns: C<Array>. Returns ZZPath operator\ndefinitions.\n\n=item C<< evaluator.function_definitions() >>\n\nParameters: none. Returns: C<Array>. Returns ZZPath function\ndefinitions.\n\n=back\n\n=item C<< ZZPath({ path: String }) >>\n\nConstructs a ZZPath query object. Returns: C<ZZPath>. Inherits the\npublic query, assignment, and reference methods from C<ZPath>.\n\n=over\n\n=item C<< path.get_evaluator() >>\n\nParameters: none. Returns: C<ZZEvaluator>. Returns the evaluator used\nfor this path.\n\n=item C<< node.find(path) >>\n\nC<ZZPath> inherits from C<ZPath>, so a C<ZZPath> object can be passed to\nC<std/path/z/node> C<Node.find()>.\n\n=back\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/path/zz >> 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\nfrom std/path/z import ZPath;\nfrom std/path/z/evaluate import Evaluator as ZEvaluator;\n\nclass ZZEvaluator extends ZEvaluator {\n method operator_definitions () {\n from std/path/zz/operators import STANDARD_OPERATORS;\n return STANDARD_OPERATORS;\n }\n\n method function_definitions () {\n from std/path/zz/functions import STANDARD_FUNCTIONS;\n return STANDARD_FUNCTIONS;\n }\n}\n\nclass ZZPath extends ZPath {\n method get_evaluator () {\n return new ZZEvaluator();\n }\n}\n';
|
|
47344
|
-
virtualFiles["/modules/std/path/zz/functions.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/path/zz/functions - Function definitions for ZZPath expressions.\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nThis module extends the base ZPath function table with\nZuzuScript-flavoured path expression helpers.\n\n=head1 EXPORTS\n\n=head2 Constants\n\n=over\n\n=item C<STANDARD_FUNCTION_NAMES>\n\nType: C<Array>. Names of inherited ZPath functions available to ZZPath.\n\n=item C<STANDARD_FUNCTIONS>\n\nType: C<Array>. Complete ZZPath function definition table.\n\n=back\n\n=head2 Functions\n\n=over\n\n=item C<< first_arg_node(name, ev, ctx, args) >>\n\nParameters: function name, evaluator, context, and argument AST nodes.\nReturns: C<Node> or C<null>. Resolves the first argument or current\ncontext node.\n\n=item C<< first_number_arg(name, ev, ctx, args) >>, C<< nth_number_arg(name, ev, ctx, args, i) >>\n\nParameters: function name, evaluator, context, arguments, and optional\nindex. Returns: C<Number>. Resolves an argument as a number.\n\n=item C<< first_string_arg(name, ev, ctx, args) >>, C<< nth_string_arg(name, ev, ctx, args, i) >>\n\nParameters: function name, evaluator, context, arguments, and optional\nindex. Returns: C<String>. Resolves an argument as a string.\n\n=item C<< nth_value_arg(name, ev, ctx, args, i) >>\n\nParameters: function name, evaluator, context, arguments, and index.\nReturns: value. Resolves an argument as a primitive value.\n\n=item C<< nth_array_arg(name, ev, ctx, args, i) >>\n\nParameters: function name, evaluator, context, arguments, and index.\nReturns: C<Array>. Resolves an argument as an array value.\n\n=item C<< number_args(ev, ctx, args) >>\n\nParameters: evaluator, context, and argument AST nodes. Returns:\nC<Array>. Resolves all arguments as numbers.\n\n=item C<< first_arg_value(name, ev, ctx, args) >>\n\nParameters: function name, evaluator, context, and arguments. Returns:\nvalue. Resolves the first argument as a primitive value.\n\n=item C<< z_function(spelling) >>\n\nParameters: C<spelling> is a base ZPath function name. Returns:\nC<Function>. Returns the inherited function implementation.\n\n=item C<< string_index_of(funk, ev, ast, ctx, args) >>\n\nParameters: standard ZPath function callback arguments. Returns:\nC<Array>. Implements string C<index-of>.\n\n=item C<< string_rindex(funk, ev, ast, ctx, args) >>\n\nParameters: standard ZPath function callback arguments. Returns:\nC<Array>. Implements string reverse-index lookup.\n\n=item C<< defined_function(funk, ev, ast, ctx, args) >>\n\nParameters: standard ZPath function callback arguments. Returns:\nC<Array>. Implements defined-value testing.\n\n=item C<< empty_function(funk, ev, ast, ctx, args) >>\n\nParameters: standard ZPath function callback arguments. Returns:\nC<Array>. Implements empty-value testing.\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/path/zz/functions >> 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\nfrom std/path/z/functions import Func, STANDARD_FUNCTIONS as Z_STANDARD_FUNCTIONS;\n\n\nconst STANDARD_FUNCTION_NAMES := [\n "true",\n "false",\n "null",\n "tag",\n "url",\n "local-name",\n "key",\n "value",\n "index",\n "count",\n "is-first",\n "is-last",\n "next",\n "prev",\n];\n\nfunction first_arg_node ( name, ev, ctx, args ) {\n if ( args.length() = 0 ) {\n return ctx.nodeset.get( 0, null );\n }\n if ( args.length() = 1 ) {\n const got := ev.eval_expr( args[0], ev.nested_ctx( ctx ) );\n return got.get( 0, null );\n }\n die `Too many arguments for ${name}()`;\n}\n\nfunction first_number_arg ( name, ev, ctx, args ) {\n const node := first_arg_node( name, ev, ctx, args );\n return ev.to_number(node) ?: 0;\n}\n\nfunction nth_number_arg ( name, ev, ctx, args, i ) {\n die `Not enough arguments for ${name}()` if args.length() <= i;\n const got := ev.eval_expr( args[i], ev.nested_ctx( ctx ) );\n return ev.to_number( got.get( 0, null ) ) ?: 0;\n}\n\nfunction nth_string_arg ( name, ev, ctx, args, i ) {\n die `Not enough arguments for ${name}()` if args.length() <= i;\n const got := ev.eval_expr( args[i], ev.nested_ctx( ctx ) );\n const value := ev.to_string( got.get( 0, null ) );\n return value \u2261 null ? "" : "" _ value;\n}\n\nfunction nth_value_arg ( name, ev, ctx, args, i ) {\n die `Not enough arguments for ${name}()` if args.length() <= i;\n const got := ev.eval_expr( args[i], ev.nested_ctx( ctx ) );\n const node := got.get( 0, null );\n return node \u2261 null ? null : node.primitive_value();\n}\n\nfunction nth_array_arg ( name, ev, ctx, args, i ) {\n die `Not enough arguments for ${name}()` if args.length() <= i;\n const got := ev.eval_expr( args[i], ev.nested_ctx( ctx ) );\n if ( got.length() = 1 ) {\n const one := got[0].primitive_value();\n return one if one instanceof Array;\n }\n return got.map( fn node \u2192 node.primitive_value() );\n}\n\nfunction number_args ( ev, ctx, args ) {\n let nums := [];\n if ( args.length() = 0 ) {\n for ( let node in ctx.nodeset ) {\n const value := ev.to_number(node);\n nums.push(value) if value instanceof Number;\n }\n return nums;\n }\n for ( let arg in args ) {\n const got := ev.eval_expr( arg, ev.nested_ctx( ctx ) );\n for ( let node in got ) {\n const value := ev.to_number(node);\n nums.push(value) if value instanceof Number;\n }\n }\n return nums;\n}\n\nfunction first_string_arg ( name, ev, ctx, args ) {\n const node := first_arg_node( name, ev, ctx, args );\n const value := ev.to_string(node);\n return value \u2261 null ? "" : "" _ value;\n}\n\nfunction first_arg_value ( name, ev, ctx, args ) {\n const node := first_arg_node( name, ev, ctx, args );\n return node \u2261 null ? null : node.primitive_value();\n}\n\nfunction z_function ( spelling ) {\n const func := Z_STANDARD_FUNCTIONS.first( fn f \u2192 f.has_name(spelling) );\n die `std/path/z/functions is missing ${spelling}()` if func \u2261 null;\n return func;\n}\n\nfunction string_index_of ( funk, ev, ast, ctx, args ) {\n from std/string import index;\n die "Too many arguments for index-of()" if args.length() > 3;\n if ( args.length() = 2 ) {\n return funk.wrap( index(\n nth_string_arg( "index-of", ev, ctx, args, 0 ),\n nth_string_arg( "index-of", ev, ctx, args, 1 ),\n ) );\n }\n return funk.wrap( index(\n nth_string_arg( "index-of", ev, ctx, args, 0 ),\n nth_string_arg( "index-of", ev, ctx, args, 1 ),\n nth_number_arg( "index-of", ev, ctx, args, 2 ),\n ) );\n}\n\nfunction string_rindex ( funk, ev, ast, ctx, args ) {\n from std/string import rindex;\n const name := funk.get_spelling;\n die `Too many arguments for ${name}()` if args.length() > 3;\n if ( args.length() = 2 ) {\n return funk.wrap( rindex(\n nth_string_arg( name, ev, ctx, args, 0 ),\n nth_string_arg( name, ev, ctx, args, 1 ),\n ) );\n }\n return funk.wrap( rindex(\n nth_string_arg( name, ev, ctx, args, 0 ),\n nth_string_arg( name, ev, ctx, args, 1 ),\n nth_number_arg( name, ev, ctx, args, 2 ),\n ) );\n}\n\nfunction defined_function ( funk, ev, ast, ctx, args ) {\n const node := first_arg_node( "defined", ev, ctx, args );\n return funk.wrap( node \u2262 null and node.primitive_value() \u2262 null );\n}\n\nfunction empty_function ( funk, ev, ast, ctx, args ) {\n const value := first_arg_value( "empty", ev, ctx, args );\n return funk.wrap( true ) if value \u2261 null;\n\n if (\n value instanceof Array or\n value instanceof Bag or\n value instanceof Set or\n value instanceof Dict or\n value instanceof PairList\n ) {\n return funk.wrap( value.length() = 0 );\n }\n\n die `empty() expects a Collection or null, got ${typeof value}`;\n}\n\n// Start with a base of functions inherited from ZPath:\nconst STANDARD_FUNCTIONS := STANDARD_FUNCTION_NAMES.map( fn n \u2192 z_function(n) );\n\n// Add equivalents for ZuzuScript word-like unary operators:\nSTANDARD_FUNCTIONS.push(\n new Func(\n spelling: "abs",\n f: function ( funk, ev, ast, ctx, args ) {\n return funk.wrap( abs first_number_arg( "abs", ev, ctx, args ) );\n },\n ),\n new Func(\n spelling: "floor",\n f: function ( funk, ev, ast, ctx, args ) {\n return funk.wrap( floor first_number_arg( "floor", ev, ctx, args ) );\n },\n ),\n new Func(\n spelling: "ceil",\n f: function ( funk, ev, ast, ctx, args ) {\n return funk.wrap( ceil first_number_arg( "ceil", ev, ctx, args ) );\n },\n ),\n new Func(\n spelling: "round",\n f: function ( funk, ev, ast, ctx, args ) {\n return funk.wrap( round first_number_arg( "round", ev, ctx, args ) );\n },\n ),\n new Func(\n spelling: "int",\n f: function ( funk, ev, ast, ctx, args ) {\n return funk.wrap( int first_number_arg( "int", ev, ctx, args ) );\n },\n ),\n new Func(\n spelling: "sqrt",\n f: function ( funk, ev, ast, ctx, args ) {\n return funk.wrap( sqrt first_number_arg( "sqrt", ev, ctx, args ) );\n },\n ),\n new Func(\n spelling: "uc",\n f: function ( funk, ev, ast, ctx, args ) {\n return funk.wrap( uc first_string_arg( "uc", ev, ctx, args ) );\n },\n ),\n new Func(\n spelling: "lc",\n f: function ( funk, ev, ast, ctx, args ) {\n return funk.wrap( lc first_string_arg( "lc", ev, ctx, args ) );\n },\n ),\n new Func(\n spelling: "length",\n f: function ( funk, ev, ast, ctx, args ) {\n return funk.wrap( length first_string_arg( "length", ev, ctx, args ) );\n },\n ),\n new Func(\n spelling: "not",\n f: function ( funk, ev, ast, ctx, args ) {\n const node := first_arg_node( "not", ev, ctx, args );\n return funk.wrap( not ev.truthy(node) );\n },\n ),\n new Func(\n spelling: "typeof",\n f: function ( funk, ev, ast, ctx, args ) {\n const node := first_arg_node( "typeof", ev, ctx, args );\n const value := node \u2261 null ? null : node.primitive_value();\n return funk.wrap( typeof value );\n },\n ),\n new Func(\n spelling: "defined",\n f: defined_function,\n ),\n new Func(\n spelling: "empty",\n f: empty_function,\n ),\n);\n\n// Functions from std/internals:\nSTANDARD_FUNCTIONS.push(\n new Func(\n spelling: "string",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/internals import to_String;\n return funk.wrap( to_String( first_arg_value(\n "string",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n new Func(\n spelling: "number",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/internals import to_Number;\n return funk.wrap( to_Number( first_arg_value(\n "number",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n new Func(\n spelling: "boolean",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/internals import to_Boolean;\n return funk.wrap( to_Boolean( first_arg_value(\n "boolean",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n new Func(\n spelling: "regexp",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/internals import to_Regexp;\n return funk.wrap( to_Regexp( first_arg_value(\n "regexp",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n);\n\n// Functions from std/math:\nSTANDARD_FUNCTIONS.push(\n new Func(\n spelling: "sum",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/math import Math;\n return funk.wrap( Math.sum( number_args( ev, ctx, args ) ) );\n },\n ),\n new Func(\n spelling: "min",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/math import Math;\n return funk.wrap( Math.min( number_args( ev, ctx, args ) ) );\n },\n ),\n new Func(\n spelling: "max",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/math import Math;\n return funk.wrap( Math.max( number_args( ev, ctx, args ) ) );\n },\n ),\n new Func(\n spelling: "clamp",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/math import Math;\n die "Too many arguments for clamp()" if args.length() > 3;\n return funk.wrap( Math.clamp(\n nth_number_arg( "clamp", ev, ctx, args, 0 ),\n nth_number_arg( "clamp", ev, ctx, args, 1 ),\n nth_number_arg( "clamp", ev, ctx, args, 2 ),\n ) );\n },\n ),\n);\n\n// Functions from std/string:\nSTANDARD_FUNCTIONS.push(\n new Func(\n spelling: "substr",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import substr;\n die "Too many arguments for substr()" if args.length() > 3;\n if ( args.length() = 2 ) {\n return funk.wrap( substr(\n nth_string_arg( "substr", ev, ctx, args, 0 ),\n nth_number_arg( "substr", ev, ctx, args, 1 ),\n ) );\n }\n return funk.wrap( substr(\n nth_string_arg( "substr", ev, ctx, args, 0 ),\n nth_number_arg( "substr", ev, ctx, args, 1 ),\n nth_number_arg( "substr", ev, ctx, args, 2 ),\n ) );\n },\n ),\n new Func(\n spelling: "index-of",\n f: string_index_of,\n ),\n new Func(\n spelling: "rindex",\n f: string_rindex,\n ),\n new Func(\n spelling: "last-index-of",\n f: string_rindex,\n ),\n new Func(\n spelling: "contains",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import contains;\n die "Too many arguments for contains()" if args.length() > 2;\n return funk.wrap( contains(\n nth_string_arg( "contains", ev, ctx, args, 0 ),\n nth_string_arg( "contains", ev, ctx, args, 1 ),\n ) );\n },\n ),\n new Func(\n spelling: "chr",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import chr;\n die "Too many arguments for chr()" if args.length() > 1;\n return funk.wrap( chr( nth_number_arg(\n "chr",\n ev,\n ctx,\n args,\n 0,\n ) ) );\n },\n ),\n new Func(\n spelling: "ord",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import ord;\n die "Too many arguments for ord()" if args.length() > 2;\n if ( args.length() = 1 ) {\n return funk.wrap( ord( nth_string_arg(\n "ord",\n ev,\n ctx,\n args,\n 0,\n ) ) );\n }\n return funk.wrap( ord(\n nth_string_arg( "ord", ev, ctx, args, 0 ),\n nth_number_arg( "ord", ev, ctx, args, 1 ),\n ) );\n },\n ),\n new Func(\n spelling: "replace",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import replace;\n die "Too many arguments for replace()" if args.length() > 4;\n if ( args.length() = 3 ) {\n return funk.wrap( replace(\n nth_string_arg( "replace", ev, ctx, args, 0 ),\n nth_value_arg( "replace", ev, ctx, args, 1 ),\n nth_string_arg( "replace", ev, ctx, args, 2 ),\n ) );\n }\n return funk.wrap( replace(\n nth_string_arg( "replace", ev, ctx, args, 0 ),\n nth_value_arg( "replace", ev, ctx, args, 1 ),\n nth_string_arg( "replace", ev, ctx, args, 2 ),\n nth_string_arg( "replace", ev, ctx, args, 3 ),\n ) );\n },\n ),\n new Func(\n spelling: "search",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import search;\n die "Too many arguments for search()" if args.length() > 3;\n if ( args.length() = 2 ) {\n return funk.wrap( search(\n nth_string_arg( "search", ev, ctx, args, 0 ),\n nth_value_arg( "search", ev, ctx, args, 1 ),\n ) );\n }\n return funk.wrap( search(\n nth_string_arg( "search", ev, ctx, args, 0 ),\n nth_value_arg( "search", ev, ctx, args, 1 ),\n nth_string_arg( "search", ev, ctx, args, 2 ),\n ) );\n },\n ),\n new Func(\n spelling: "starts_with",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import starts_with;\n die "Too many arguments for starts_with()" if args.length() > 2;\n return funk.wrap( starts_with(\n nth_string_arg( "starts_with", ev, ctx, args, 0 ),\n nth_string_arg( "starts_with", ev, ctx, args, 1 ),\n ) );\n },\n ),\n new Func(\n spelling: "ends_with",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import ends_with;\n die "Too many arguments for ends_with()" if args.length() > 2;\n return funk.wrap( ends_with(\n nth_string_arg( "ends_with", ev, ctx, args, 0 ),\n nth_string_arg( "ends_with", ev, ctx, args, 1 ),\n ) );\n },\n ),\n new Func(\n spelling: "matches",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import matches;\n die "Too many arguments for matches()" if args.length() > 3;\n if ( args.length() = 2 ) {\n return funk.wrap( matches(\n nth_string_arg( "matches", ev, ctx, args, 0 ),\n nth_value_arg( "matches", ev, ctx, args, 1 ),\n ) );\n }\n return funk.wrap( matches(\n nth_string_arg( "matches", ev, ctx, args, 0 ),\n nth_value_arg( "matches", ev, ctx, args, 1 ),\n nth_string_arg( "matches", ev, ctx, args, 2 ),\n ) );\n },\n ),\n new Func(\n spelling: "pattern_to_regexp",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import pattern_to_regexp;\n die "Too many arguments for pattern_to_regexp()"\n if args.length() > 2;\n if ( args.length() = 1 ) {\n return funk.wrap( pattern_to_regexp(\n nth_string_arg( "pattern_to_regexp", ev, ctx, args, 0 ),\n ) );\n }\n return funk.wrap( pattern_to_regexp(\n nth_string_arg( "pattern_to_regexp", ev, ctx, args, 0 ),\n nth_value_arg( "pattern_to_regexp", ev, ctx, args, 1 ),\n ) );\n },\n ),\n new Func(\n spelling: "quotemeta",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import quotemeta;\n die "Too many arguments for quotemeta()" if args.length() > 1;\n return funk.wrap( quotemeta(\n nth_string_arg( "quotemeta", ev, ctx, args, 0 ),\n ) );\n },\n ),\n new Func(\n spelling: "sprint",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import sprint;\n die "Not enough arguments for sprint()" if args.length() = 0;\n die "Too many arguments for sprint()" if args.length() > 8;\n\n const fmt := nth_string_arg( "sprint", ev, ctx, args, 0 );\n if ( args.length() = 1 ) {\n return funk.wrap( sprint(fmt) );\n }\n if ( args.length() = 2 ) {\n return funk.wrap( sprint(\n fmt,\n nth_value_arg( "sprint", ev, ctx, args, 1 ),\n ) );\n }\n if ( args.length() = 3 ) {\n return funk.wrap( sprint(\n fmt,\n nth_value_arg( "sprint", ev, ctx, args, 1 ),\n nth_value_arg( "sprint", ev, ctx, args, 2 ),\n ) );\n }\n if ( args.length() = 4 ) {\n return funk.wrap( sprint(\n fmt,\n nth_value_arg( "sprint", ev, ctx, args, 1 ),\n nth_value_arg( "sprint", ev, ctx, args, 2 ),\n nth_value_arg( "sprint", ev, ctx, args, 3 ),\n ) );\n }\n if ( args.length() = 5 ) {\n return funk.wrap( sprint(\n fmt,\n nth_value_arg( "sprint", ev, ctx, args, 1 ),\n nth_value_arg( "sprint", ev, ctx, args, 2 ),\n nth_value_arg( "sprint", ev, ctx, args, 3 ),\n nth_value_arg( "sprint", ev, ctx, args, 4 ),\n ) );\n }\n if ( args.length() = 6 ) {\n return funk.wrap( sprint(\n fmt,\n nth_value_arg( "sprint", ev, ctx, args, 1 ),\n nth_value_arg( "sprint", ev, ctx, args, 2 ),\n nth_value_arg( "sprint", ev, ctx, args, 3 ),\n nth_value_arg( "sprint", ev, ctx, args, 4 ),\n nth_value_arg( "sprint", ev, ctx, args, 5 ),\n ) );\n }\n if ( args.length() = 7 ) {\n return funk.wrap( sprint(\n fmt,\n nth_value_arg( "sprint", ev, ctx, args, 1 ),\n nth_value_arg( "sprint", ev, ctx, args, 2 ),\n nth_value_arg( "sprint", ev, ctx, args, 3 ),\n nth_value_arg( "sprint", ev, ctx, args, 4 ),\n nth_value_arg( "sprint", ev, ctx, args, 5 ),\n nth_value_arg( "sprint", ev, ctx, args, 6 ),\n ) );\n }\n return funk.wrap( sprint(\n fmt,\n nth_value_arg( "sprint", ev, ctx, args, 1 ),\n nth_value_arg( "sprint", ev, ctx, args, 2 ),\n nth_value_arg( "sprint", ev, ctx, args, 3 ),\n nth_value_arg( "sprint", ev, ctx, args, 4 ),\n nth_value_arg( "sprint", ev, ctx, args, 5 ),\n nth_value_arg( "sprint", ev, ctx, args, 6 ),\n nth_value_arg( "sprint", ev, ctx, args, 7 ),\n ) );\n },\n ),\n new Func(\n spelling: "split",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import split;\n die "Too many arguments for split()" if args.length() > 3;\n if ( args.length() = 2 ) {\n return funk.wrap( split(\n nth_string_arg( "split", ev, ctx, args, 0 ),\n nth_value_arg( "split", ev, ctx, args, 1 ),\n ) );\n }\n return funk.wrap( split(\n nth_string_arg( "split", ev, ctx, args, 0 ),\n nth_value_arg( "split", ev, ctx, args, 1 ),\n nth_number_arg( "split", ev, ctx, args, 2 ),\n ) );\n },\n ),\n new Func(\n spelling: "join",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import join;\n die "Too many arguments for join()" if args.length() > 2;\n return funk.wrap( join(\n nth_string_arg( "join", ev, ctx, args, 0 ),\n nth_array_arg( "join", ev, ctx, args, 1 ),\n ) );\n },\n ),\n new Func(\n spelling: "trim",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import trim;\n die "Too many arguments for trim()" if args.length() > 1;\n return funk.wrap( trim( first_string_arg(\n "trim",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n new Func(\n spelling: "pad",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import pad;\n die "Too many arguments for pad()" if args.length() > 4;\n if ( args.length() = 2 ) {\n return funk.wrap( pad(\n nth_string_arg( "pad", ev, ctx, args, 0 ),\n nth_number_arg( "pad", ev, ctx, args, 1 ),\n ) );\n }\n if ( args.length() = 3 ) {\n return funk.wrap( pad(\n nth_string_arg( "pad", ev, ctx, args, 0 ),\n nth_number_arg( "pad", ev, ctx, args, 1 ),\n nth_string_arg( "pad", ev, ctx, args, 2 ),\n ) );\n }\n return funk.wrap( pad(\n nth_string_arg( "pad", ev, ctx, args, 0 ),\n nth_number_arg( "pad", ev, ctx, args, 1 ),\n nth_string_arg( "pad", ev, ctx, args, 2 ),\n nth_string_arg( "pad", ev, ctx, args, 3 ),\n ) );\n },\n ),\n new Func(\n spelling: "chomp",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import chomp;\n die "Too many arguments for chomp()" if args.length() > 1;\n return funk.wrap( chomp( first_string_arg(\n "chomp",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n new Func(\n spelling: "title",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import title;\n die "Too many arguments for title()" if args.length() > 1;\n return funk.wrap( title( first_string_arg(\n "title",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n new Func(\n spelling: "snake",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import snake;\n die "Too many arguments for snake()" if args.length() > 1;\n return funk.wrap( snake( first_string_arg(\n "snake",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n new Func(\n spelling: "kebab",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import kebab;\n die "Too many arguments for kebab()" if args.length() > 1;\n return funk.wrap( kebab( first_string_arg(\n "kebab",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n new Func(\n spelling: "camel",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import camel;\n die "Too many arguments for camel()" if args.length() > 1;\n return funk.wrap( camel( first_string_arg(\n "camel",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n);\n';
|
|
47412
|
+
virtualFiles["/modules/std/path/zz/functions.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/path/zz/functions - Function definitions for ZZPath expressions.\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nThis module extends the base ZPath function table with\nZuzuScript-flavoured path expression helpers.\n\n=head1 EXPORTS\n\n=head2 Constants\n\n=over\n\n=item C<STANDARD_FUNCTION_NAMES>\n\nType: C<Array>. Names of inherited ZPath functions available to ZZPath.\n\n=item C<STANDARD_FUNCTIONS>\n\nType: C<Array>. Complete ZZPath function definition table.\n\n=back\n\n=head2 Functions\n\n=over\n\n=item C<< first_arg_node(name, ev, ctx, args) >>\n\nParameters: function name, evaluator, context, and argument AST nodes.\nReturns: C<Node> or C<null>. Resolves the first argument or current\ncontext node.\n\n=item C<< first_number_arg(name, ev, ctx, args) >>, C<< nth_number_arg(name, ev, ctx, args, i) >>\n\nParameters: function name, evaluator, context, arguments, and optional\nindex. Returns: C<Number>. Resolves an argument as a number.\n\n=item C<< first_string_arg(name, ev, ctx, args) >>, C<< nth_string_arg(name, ev, ctx, args, i) >>\n\nParameters: function name, evaluator, context, arguments, and optional\nindex. Returns: C<String>. Resolves an argument as a string.\n\n=item C<< nth_value_arg(name, ev, ctx, args, i) >>\n\nParameters: function name, evaluator, context, arguments, and index.\nReturns: value. Resolves an argument as a primitive value.\n\n=item C<< nth_array_arg(name, ev, ctx, args, i) >>\n\nParameters: function name, evaluator, context, arguments, and index.\nReturns: C<Array>. Resolves an argument as an array value.\n\n=item C<< number_args(ev, ctx, args) >>\n\nParameters: evaluator, context, and argument AST nodes. Returns:\nC<Array>. Resolves all arguments as numbers.\n\n=item C<< first_arg_value(name, ev, ctx, args) >>\n\nParameters: function name, evaluator, context, and arguments. Returns:\nvalue. Resolves the first argument as a primitive value.\n\n=item C<< z_function(spelling) >>\n\nParameters: C<spelling> is a base ZPath function name. Returns:\nC<Function>. Returns the inherited function implementation.\n\n=item C<< string_index_of(funk, ev, ast, ctx, args) >>\n\nParameters: standard ZPath function callback arguments. Returns:\nC<Array>. Implements string C<index-of>.\n\n=item C<< string_rindex(funk, ev, ast, ctx, args) >>\n\nParameters: standard ZPath function callback arguments. Returns:\nC<Array>. Implements string reverse-index lookup.\n\n=item C<< defined_function(funk, ev, ast, ctx, args) >>\n\nParameters: standard ZPath function callback arguments. Returns:\nC<Array>. Implements defined-value testing.\n\n=item C<< empty_function(funk, ev, ast, ctx, args) >>\n\nParameters: standard ZPath function callback arguments. Returns:\nC<Array>. Implements empty-value testing.\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/path/zz/functions >> 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\nfrom std/path/z/functions import Func, STANDARD_FUNCTIONS as Z_STANDARD_FUNCTIONS;\n\n\nconst STANDARD_FUNCTION_NAMES := [\n "true",\n "false",\n "null",\n "tag",\n "url",\n "local-name",\n "key",\n "value",\n "index",\n "count",\n "is-first",\n "is-last",\n "next",\n "prev",\n];\n\nfunction first_arg_node ( name, ev, ctx, args ) {\n if ( args.length() = 0 ) {\n return ctx.nodeset.get( 0, null );\n }\n if ( args.length() = 1 ) {\n const got := ev.eval_expr( args[0], ev.nested_ctx( ctx ) );\n return got.get( 0, null );\n }\n die `Too many arguments for ${name}()`;\n}\n\nfunction first_number_arg ( name, ev, ctx, args ) {\n const node := first_arg_node( name, ev, ctx, args );\n // Numeric coercion may produce 0; only null falls back.\n return ev.to_number(node) ?: 0;\n}\n\nfunction nth_number_arg ( name, ev, ctx, args, i ) {\n die `Not enough arguments for ${name}()` if args.length() <= i;\n const got := ev.eval_expr( args[i], ev.nested_ctx( ctx ) );\n // Numeric coercion may produce 0; only null falls back.\n return ev.to_number( got.get( 0, null ) ) ?: 0;\n}\n\nfunction nth_string_arg ( name, ev, ctx, args, i ) {\n die `Not enough arguments for ${name}()` if args.length() <= i;\n const got := ev.eval_expr( args[i], ev.nested_ctx( ctx ) );\n const value := ev.to_string( got.get( 0, null ) );\n return value \u2261 null ? "" : "" _ value;\n}\n\nfunction nth_value_arg ( name, ev, ctx, args, i ) {\n die `Not enough arguments for ${name}()` if args.length() <= i;\n const got := ev.eval_expr( args[i], ev.nested_ctx( ctx ) );\n const node := got.get( 0, null );\n return node \u2261 null ? null : node.primitive_value();\n}\n\nfunction nth_array_arg ( name, ev, ctx, args, i ) {\n die `Not enough arguments for ${name}()` if args.length() <= i;\n const got := ev.eval_expr( args[i], ev.nested_ctx( ctx ) );\n if ( got.length() = 1 ) {\n const one := got[0].primitive_value();\n return one if one instanceof Array;\n }\n return got.map( fn node \u2192 node.primitive_value() );\n}\n\nfunction number_args ( ev, ctx, args ) {\n let nums := [];\n if ( args.length() = 0 ) {\n for ( let node in ctx.nodeset ) {\n const value := ev.to_number(node);\n nums.push(value) if value instanceof Number;\n }\n return nums;\n }\n for ( let arg in args ) {\n const got := ev.eval_expr( arg, ev.nested_ctx( ctx ) );\n for ( let node in got ) {\n const value := ev.to_number(node);\n nums.push(value) if value instanceof Number;\n }\n }\n return nums;\n}\n\nfunction first_string_arg ( name, ev, ctx, args ) {\n const node := first_arg_node( name, ev, ctx, args );\n const value := ev.to_string(node);\n return value \u2261 null ? "" : "" _ value;\n}\n\nfunction first_arg_value ( name, ev, ctx, args ) {\n const node := first_arg_node( name, ev, ctx, args );\n return node \u2261 null ? null : node.primitive_value();\n}\n\nfunction z_function ( spelling ) {\n const func := Z_STANDARD_FUNCTIONS.first( fn f \u2192 f.has_name(spelling) );\n die `std/path/z/functions is missing ${spelling}()` if func \u2261 null;\n return func;\n}\n\nfunction string_index_of ( funk, ev, ast, ctx, args ) {\n from std/string import index;\n die "Too many arguments for index-of()" if args.length() > 3;\n if ( args.length() = 2 ) {\n return funk.wrap( index(\n nth_string_arg( "index-of", ev, ctx, args, 0 ),\n nth_string_arg( "index-of", ev, ctx, args, 1 ),\n ) );\n }\n return funk.wrap( index(\n nth_string_arg( "index-of", ev, ctx, args, 0 ),\n nth_string_arg( "index-of", ev, ctx, args, 1 ),\n nth_number_arg( "index-of", ev, ctx, args, 2 ),\n ) );\n}\n\nfunction string_rindex ( funk, ev, ast, ctx, args ) {\n from std/string import rindex;\n const name := funk.get_spelling;\n die `Too many arguments for ${name}()` if args.length() > 3;\n if ( args.length() = 2 ) {\n return funk.wrap( rindex(\n nth_string_arg( name, ev, ctx, args, 0 ),\n nth_string_arg( name, ev, ctx, args, 1 ),\n ) );\n }\n return funk.wrap( rindex(\n nth_string_arg( name, ev, ctx, args, 0 ),\n nth_string_arg( name, ev, ctx, args, 1 ),\n nth_number_arg( name, ev, ctx, args, 2 ),\n ) );\n}\n\nfunction defined_function ( funk, ev, ast, ctx, args ) {\n const node := first_arg_node( "defined", ev, ctx, args );\n return funk.wrap( node \u2262 null and node.primitive_value() \u2262 null );\n}\n\nfunction empty_function ( funk, ev, ast, ctx, args ) {\n const value := first_arg_value( "empty", ev, ctx, args );\n return funk.wrap( true ) if value \u2261 null;\n\n if (\n value instanceof Array or\n value instanceof Bag or\n value instanceof Set or\n value instanceof Dict or\n value instanceof PairList\n ) {\n return funk.wrap( value.length() = 0 );\n }\n\n die `empty() expects a Collection or null, got ${typeof value}`;\n}\n\n// Start with a base of functions inherited from ZPath:\nconst STANDARD_FUNCTIONS := STANDARD_FUNCTION_NAMES.map( fn n \u2192 z_function(n) );\n\n// Add equivalents for ZuzuScript word-like unary operators:\nSTANDARD_FUNCTIONS.push(\n new Func(\n spelling: "abs",\n f: function ( funk, ev, ast, ctx, args ) {\n return funk.wrap( abs first_number_arg( "abs", ev, ctx, args ) );\n },\n ),\n new Func(\n spelling: "floor",\n f: function ( funk, ev, ast, ctx, args ) {\n return funk.wrap( floor first_number_arg( "floor", ev, ctx, args ) );\n },\n ),\n new Func(\n spelling: "ceil",\n f: function ( funk, ev, ast, ctx, args ) {\n return funk.wrap( ceil first_number_arg( "ceil", ev, ctx, args ) );\n },\n ),\n new Func(\n spelling: "round",\n f: function ( funk, ev, ast, ctx, args ) {\n return funk.wrap( round first_number_arg( "round", ev, ctx, args ) );\n },\n ),\n new Func(\n spelling: "int",\n f: function ( funk, ev, ast, ctx, args ) {\n return funk.wrap( int first_number_arg( "int", ev, ctx, args ) );\n },\n ),\n new Func(\n spelling: "sqrt",\n f: function ( funk, ev, ast, ctx, args ) {\n return funk.wrap( sqrt first_number_arg( "sqrt", ev, ctx, args ) );\n },\n ),\n new Func(\n spelling: "uc",\n f: function ( funk, ev, ast, ctx, args ) {\n return funk.wrap( uc first_string_arg( "uc", ev, ctx, args ) );\n },\n ),\n new Func(\n spelling: "lc",\n f: function ( funk, ev, ast, ctx, args ) {\n return funk.wrap( lc first_string_arg( "lc", ev, ctx, args ) );\n },\n ),\n new Func(\n spelling: "length",\n f: function ( funk, ev, ast, ctx, args ) {\n return funk.wrap( length first_string_arg( "length", ev, ctx, args ) );\n },\n ),\n new Func(\n spelling: "not",\n f: function ( funk, ev, ast, ctx, args ) {\n const node := first_arg_node( "not", ev, ctx, args );\n return funk.wrap( not ev.truthy(node) );\n },\n ),\n new Func(\n spelling: "typeof",\n f: function ( funk, ev, ast, ctx, args ) {\n const node := first_arg_node( "typeof", ev, ctx, args );\n const value := node \u2261 null ? null : node.primitive_value();\n return funk.wrap( typeof value );\n },\n ),\n new Func(\n spelling: "defined",\n f: defined_function,\n ),\n new Func(\n spelling: "empty",\n f: empty_function,\n ),\n);\n\n// Functions from std/internals:\nSTANDARD_FUNCTIONS.push(\n new Func(\n spelling: "string",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/internals import to_String;\n return funk.wrap( to_String( first_arg_value(\n "string",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n new Func(\n spelling: "number",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/internals import to_Number;\n return funk.wrap( to_Number( first_arg_value(\n "number",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n new Func(\n spelling: "boolean",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/internals import to_Boolean;\n return funk.wrap( to_Boolean( first_arg_value(\n "boolean",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n new Func(\n spelling: "regexp",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/internals import to_Regexp;\n return funk.wrap( to_Regexp( first_arg_value(\n "regexp",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n);\n\n// Functions from std/math:\nSTANDARD_FUNCTIONS.push(\n new Func(\n spelling: "sum",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/math import Math;\n return funk.wrap( Math.sum( number_args( ev, ctx, args ) ) );\n },\n ),\n new Func(\n spelling: "min",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/math import Math;\n return funk.wrap( Math.min( number_args( ev, ctx, args ) ) );\n },\n ),\n new Func(\n spelling: "max",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/math import Math;\n return funk.wrap( Math.max( number_args( ev, ctx, args ) ) );\n },\n ),\n new Func(\n spelling: "clamp",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/math import Math;\n die "Too many arguments for clamp()" if args.length() > 3;\n return funk.wrap( Math.clamp(\n nth_number_arg( "clamp", ev, ctx, args, 0 ),\n nth_number_arg( "clamp", ev, ctx, args, 1 ),\n nth_number_arg( "clamp", ev, ctx, args, 2 ),\n ) );\n },\n ),\n);\n\n// Functions from std/string:\nSTANDARD_FUNCTIONS.push(\n new Func(\n spelling: "substr",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import substr;\n die "Too many arguments for substr()" if args.length() > 3;\n if ( args.length() = 2 ) {\n return funk.wrap( substr(\n nth_string_arg( "substr", ev, ctx, args, 0 ),\n nth_number_arg( "substr", ev, ctx, args, 1 ),\n ) );\n }\n return funk.wrap( substr(\n nth_string_arg( "substr", ev, ctx, args, 0 ),\n nth_number_arg( "substr", ev, ctx, args, 1 ),\n nth_number_arg( "substr", ev, ctx, args, 2 ),\n ) );\n },\n ),\n new Func(\n spelling: "index-of",\n f: string_index_of,\n ),\n new Func(\n spelling: "rindex",\n f: string_rindex,\n ),\n new Func(\n spelling: "last-index-of",\n f: string_rindex,\n ),\n new Func(\n spelling: "contains",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import contains;\n die "Too many arguments for contains()" if args.length() > 2;\n return funk.wrap( contains(\n nth_string_arg( "contains", ev, ctx, args, 0 ),\n nth_string_arg( "contains", ev, ctx, args, 1 ),\n ) );\n },\n ),\n new Func(\n spelling: "chr",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import chr;\n die "Too many arguments for chr()" if args.length() > 1;\n return funk.wrap( chr( nth_number_arg(\n "chr",\n ev,\n ctx,\n args,\n 0,\n ) ) );\n },\n ),\n new Func(\n spelling: "ord",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import ord;\n die "Too many arguments for ord()" if args.length() > 2;\n if ( args.length() = 1 ) {\n return funk.wrap( ord( nth_string_arg(\n "ord",\n ev,\n ctx,\n args,\n 0,\n ) ) );\n }\n return funk.wrap( ord(\n nth_string_arg( "ord", ev, ctx, args, 0 ),\n nth_number_arg( "ord", ev, ctx, args, 1 ),\n ) );\n },\n ),\n new Func(\n spelling: "replace",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import replace;\n die "Too many arguments for replace()" if args.length() > 4;\n if ( args.length() = 3 ) {\n return funk.wrap( replace(\n nth_string_arg( "replace", ev, ctx, args, 0 ),\n nth_value_arg( "replace", ev, ctx, args, 1 ),\n nth_string_arg( "replace", ev, ctx, args, 2 ),\n ) );\n }\n return funk.wrap( replace(\n nth_string_arg( "replace", ev, ctx, args, 0 ),\n nth_value_arg( "replace", ev, ctx, args, 1 ),\n nth_string_arg( "replace", ev, ctx, args, 2 ),\n nth_string_arg( "replace", ev, ctx, args, 3 ),\n ) );\n },\n ),\n new Func(\n spelling: "search",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import search;\n die "Too many arguments for search()" if args.length() > 3;\n if ( args.length() = 2 ) {\n return funk.wrap( search(\n nth_string_arg( "search", ev, ctx, args, 0 ),\n nth_value_arg( "search", ev, ctx, args, 1 ),\n ) );\n }\n return funk.wrap( search(\n nth_string_arg( "search", ev, ctx, args, 0 ),\n nth_value_arg( "search", ev, ctx, args, 1 ),\n nth_string_arg( "search", ev, ctx, args, 2 ),\n ) );\n },\n ),\n new Func(\n spelling: "starts_with",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import starts_with;\n die "Too many arguments for starts_with()" if args.length() > 2;\n return funk.wrap( starts_with(\n nth_string_arg( "starts_with", ev, ctx, args, 0 ),\n nth_string_arg( "starts_with", ev, ctx, args, 1 ),\n ) );\n },\n ),\n new Func(\n spelling: "ends_with",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import ends_with;\n die "Too many arguments for ends_with()" if args.length() > 2;\n return funk.wrap( ends_with(\n nth_string_arg( "ends_with", ev, ctx, args, 0 ),\n nth_string_arg( "ends_with", ev, ctx, args, 1 ),\n ) );\n },\n ),\n new Func(\n spelling: "matches",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import matches;\n die "Too many arguments for matches()" if args.length() > 3;\n if ( args.length() = 2 ) {\n return funk.wrap( matches(\n nth_string_arg( "matches", ev, ctx, args, 0 ),\n nth_value_arg( "matches", ev, ctx, args, 1 ),\n ) );\n }\n return funk.wrap( matches(\n nth_string_arg( "matches", ev, ctx, args, 0 ),\n nth_value_arg( "matches", ev, ctx, args, 1 ),\n nth_string_arg( "matches", ev, ctx, args, 2 ),\n ) );\n },\n ),\n new Func(\n spelling: "pattern_to_regexp",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import pattern_to_regexp;\n die "Too many arguments for pattern_to_regexp()"\n if args.length() > 2;\n if ( args.length() = 1 ) {\n return funk.wrap( pattern_to_regexp(\n nth_string_arg( "pattern_to_regexp", ev, ctx, args, 0 ),\n ) );\n }\n return funk.wrap( pattern_to_regexp(\n nth_string_arg( "pattern_to_regexp", ev, ctx, args, 0 ),\n nth_value_arg( "pattern_to_regexp", ev, ctx, args, 1 ),\n ) );\n },\n ),\n new Func(\n spelling: "quotemeta",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import quotemeta;\n die "Too many arguments for quotemeta()" if args.length() > 1;\n return funk.wrap( quotemeta(\n nth_string_arg( "quotemeta", ev, ctx, args, 0 ),\n ) );\n },\n ),\n new Func(\n spelling: "sprint",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import sprint;\n die "Not enough arguments for sprint()" if args.length() = 0;\n die "Too many arguments for sprint()" if args.length() > 8;\n\n const fmt := nth_string_arg( "sprint", ev, ctx, args, 0 );\n if ( args.length() = 1 ) {\n return funk.wrap( sprint(fmt) );\n }\n if ( args.length() = 2 ) {\n return funk.wrap( sprint(\n fmt,\n nth_value_arg( "sprint", ev, ctx, args, 1 ),\n ) );\n }\n if ( args.length() = 3 ) {\n return funk.wrap( sprint(\n fmt,\n nth_value_arg( "sprint", ev, ctx, args, 1 ),\n nth_value_arg( "sprint", ev, ctx, args, 2 ),\n ) );\n }\n if ( args.length() = 4 ) {\n return funk.wrap( sprint(\n fmt,\n nth_value_arg( "sprint", ev, ctx, args, 1 ),\n nth_value_arg( "sprint", ev, ctx, args, 2 ),\n nth_value_arg( "sprint", ev, ctx, args, 3 ),\n ) );\n }\n if ( args.length() = 5 ) {\n return funk.wrap( sprint(\n fmt,\n nth_value_arg( "sprint", ev, ctx, args, 1 ),\n nth_value_arg( "sprint", ev, ctx, args, 2 ),\n nth_value_arg( "sprint", ev, ctx, args, 3 ),\n nth_value_arg( "sprint", ev, ctx, args, 4 ),\n ) );\n }\n if ( args.length() = 6 ) {\n return funk.wrap( sprint(\n fmt,\n nth_value_arg( "sprint", ev, ctx, args, 1 ),\n nth_value_arg( "sprint", ev, ctx, args, 2 ),\n nth_value_arg( "sprint", ev, ctx, args, 3 ),\n nth_value_arg( "sprint", ev, ctx, args, 4 ),\n nth_value_arg( "sprint", ev, ctx, args, 5 ),\n ) );\n }\n if ( args.length() = 7 ) {\n return funk.wrap( sprint(\n fmt,\n nth_value_arg( "sprint", ev, ctx, args, 1 ),\n nth_value_arg( "sprint", ev, ctx, args, 2 ),\n nth_value_arg( "sprint", ev, ctx, args, 3 ),\n nth_value_arg( "sprint", ev, ctx, args, 4 ),\n nth_value_arg( "sprint", ev, ctx, args, 5 ),\n nth_value_arg( "sprint", ev, ctx, args, 6 ),\n ) );\n }\n return funk.wrap( sprint(\n fmt,\n nth_value_arg( "sprint", ev, ctx, args, 1 ),\n nth_value_arg( "sprint", ev, ctx, args, 2 ),\n nth_value_arg( "sprint", ev, ctx, args, 3 ),\n nth_value_arg( "sprint", ev, ctx, args, 4 ),\n nth_value_arg( "sprint", ev, ctx, args, 5 ),\n nth_value_arg( "sprint", ev, ctx, args, 6 ),\n nth_value_arg( "sprint", ev, ctx, args, 7 ),\n ) );\n },\n ),\n new Func(\n spelling: "split",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import split;\n die "Too many arguments for split()" if args.length() > 3;\n if ( args.length() = 2 ) {\n return funk.wrap( split(\n nth_string_arg( "split", ev, ctx, args, 0 ),\n nth_value_arg( "split", ev, ctx, args, 1 ),\n ) );\n }\n return funk.wrap( split(\n nth_string_arg( "split", ev, ctx, args, 0 ),\n nth_value_arg( "split", ev, ctx, args, 1 ),\n nth_number_arg( "split", ev, ctx, args, 2 ),\n ) );\n },\n ),\n new Func(\n spelling: "join",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import join;\n die "Too many arguments for join()" if args.length() > 2;\n return funk.wrap( join(\n nth_string_arg( "join", ev, ctx, args, 0 ),\n nth_array_arg( "join", ev, ctx, args, 1 ),\n ) );\n },\n ),\n new Func(\n spelling: "trim",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import trim;\n die "Too many arguments for trim()" if args.length() > 1;\n return funk.wrap( trim( first_string_arg(\n "trim",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n new Func(\n spelling: "pad",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import pad;\n die "Too many arguments for pad()" if args.length() > 4;\n if ( args.length() = 2 ) {\n return funk.wrap( pad(\n nth_string_arg( "pad", ev, ctx, args, 0 ),\n nth_number_arg( "pad", ev, ctx, args, 1 ),\n ) );\n }\n if ( args.length() = 3 ) {\n return funk.wrap( pad(\n nth_string_arg( "pad", ev, ctx, args, 0 ),\n nth_number_arg( "pad", ev, ctx, args, 1 ),\n nth_string_arg( "pad", ev, ctx, args, 2 ),\n ) );\n }\n return funk.wrap( pad(\n nth_string_arg( "pad", ev, ctx, args, 0 ),\n nth_number_arg( "pad", ev, ctx, args, 1 ),\n nth_string_arg( "pad", ev, ctx, args, 2 ),\n nth_string_arg( "pad", ev, ctx, args, 3 ),\n ) );\n },\n ),\n new Func(\n spelling: "chomp",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import chomp;\n die "Too many arguments for chomp()" if args.length() > 1;\n return funk.wrap( chomp( first_string_arg(\n "chomp",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n new Func(\n spelling: "title",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import title;\n die "Too many arguments for title()" if args.length() > 1;\n return funk.wrap( title( first_string_arg(\n "title",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n new Func(\n spelling: "snake",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import snake;\n die "Too many arguments for snake()" if args.length() > 1;\n return funk.wrap( snake( first_string_arg(\n "snake",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n new Func(\n spelling: "kebab",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import kebab;\n die "Too many arguments for kebab()" if args.length() > 1;\n return funk.wrap( kebab( first_string_arg(\n "kebab",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n new Func(\n spelling: "camel",\n f: function ( funk, ev, ast, ctx, args ) {\n from std/string import camel;\n die "Too many arguments for camel()" if args.length() > 1;\n return funk.wrap( camel( first_string_arg(\n "camel",\n ev,\n ctx,\n args,\n ) ) );\n },\n ),\n);\n';
|
|
47345
47413
|
virtualFiles["/modules/std/path/zz/operators.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/path/zz/operators - Operator definitions for ZZPath expressions.\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by all implementations of ZuzuScript.\n\n=head1 DESCRIPTION\n\nThis module extends the base ZPath operator model with\nZuzuScript-flavoured expression operators.\n\n=head1 EXPORTS\n\n=head2 Classes\n\n=over\n\n=item C<Operator>\n\nZZPath operator class extending C<std/path/z/operators> C<Operator>.\n\n=back\n\n=head2 Functions\n\n=over\n\n=item C<< string_operand(ev, ctx, expr) >>, C<< string_operands(ev, ctx, left, right) >>\n\nParameters: evaluator, context, and expression AST nodes. Returns:\nC<String> or C<Array>. Evaluates one or two operands as strings.\n\n=item C<< first_eval_value(values) >>, C<< primitive_eval_value(values) >>\n\nParameters: C<values> is an evaluated node array. Returns: value.\nExtracts the first or primitive value used by ZZPath operators.\n\n=item C<< collection_operand(ev, ctx, expr) >>\n\nParameters: evaluator, context, and expression AST node. Returns:\ncollection value. Evaluates an operand for collection operators.\n\n=item C<< builtin_can(value, method_name) >>\n\nParameters: C<value> is any value and C<method_name> is a method name.\nReturns: C<Boolean>. Tests whether built-in values support a method.\n\n=back\n\n=head2 Constants\n\n=over\n\n=item C<STANDARD_OPERATORS>\n\nType: C<Array>. Complete ZZPath operator definition table.\n\n=item C<logical_negation>, C<logical_and>, C<logical_nand>, C<logical_butnot>, C<logical_xor>, C<logical_nor>, C<logical_xnor>, C<logical_onlyif>, C<logical_or>, C<logical_and_value>, C<logical_nand_value>, C<logical_butnot_value>, C<logical_xor_value>, C<logical_nor_value>, C<logical_xnor_value>, C<logical_onlyif_value>, C<logical_or_value>\n\nType: C<Function>. Logical operator implementations.\n\n=item C<numeric_power>, C<numeric_multiplication>, C<numeric_division>, C<numeric_modulus>, C<numeric_addition>, C<numeric_subtraction>, C<numeric_left_shift>, C<numeric_right_shift>, C<numeric_equality>, C<numeric_inequality>, C<numeric_less_than>, C<numeric_greater_than>, C<numeric_less_than_or_equal>, C<numeric_greater_than_or_equal>, C<numeric_compare>, C<numeric_divides>, C<numeric_not_divides>\n\nType: C<Function>. Numeric operator implementations.\n\n=item C<string_concatenation>, C<string_equality>, C<string_inequality>, C<string_greater_than>, C<string_greater_than_or_equal>, C<string_less_than>, C<string_less_than_or_equal>, C<string_compare>, C<string_equality_insensitive>, C<string_inequality_insensitive>, C<string_greater_than_insensitive>, C<string_greater_than_or_equal_insensitive>, C<string_less_than_insensitive>, C<string_less_than_or_equal_insensitive>, C<string_compare_insensitive>\n\nType: C<Function>. String operator implementations.\n\n=item C<regexp_match>, C<bitwise_and>, C<bitwise_xor>, C<bitwise_or>, C<set_union>, C<set_intersection>, C<set_difference>, C<collection_membership>, C<collection_non_membership>, C<set_subsetof>, C<set_supersetof>, C<set_equivalentof>, C<type_aware_equality>, C<type_aware_inequality>, C<object_can>\n\nType: C<Function>. Regular expression, bitwise, collection, type-aware,\nand object capability operator implementations.\n\n=back\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/path/zz/operators >> 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\nfrom std/path/z/operators import Operator as ZOperator;\n\n\nclass Operator extends ZOperator;\n\nconst logical_negation := function ( op, ev, ast, ctx, expr ) {\n const got := ev.eval_expr( expr, ev.nested_ctx( ctx ) );\n const value := got.length() > 0 ? got[0] : null;\n return op.wrap( not ev.truthy(value) );\n};\n\nconst numeric_power := function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val ** right_val );\n};\n\nconst numeric_multiplication := function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val \xD7 right_val );\n};\n\nconst numeric_division := function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val \xF7 right_val );\n};\n\nconst numeric_modulus := function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val mod right_val );\n};\n\nconst numeric_left_shift := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n const first := left_vals.length() > 0 ? left_vals[0] : null;\n const left_val := first \u2261 null ? null : first.primitive_value();\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val \xAB right_val );\n};\n\nconst numeric_right_shift := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n const first := left_vals.length() > 0 ? left_vals[0] : null;\n const left_val := first \u2261 null ? null : first.primitive_value();\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val \xBB right_val );\n};\n\nconst numeric_addition := function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val + right_val );\n};\n\nconst numeric_subtraction := function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val - right_val );\n};\n\nconst numeric_equality := function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val = right_val );\n};\n\nconst numeric_inequality := function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val \u2260 right_val );\n};\n\nconst numeric_less_than := function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val < right_val );\n};\n\nconst numeric_greater_than := function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val > right_val );\n};\n\nconst numeric_less_than_or_equal := function (\n op,\n ev,\n ast,\n ctx,\n left,\n right,\n) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val \u2264 right_val );\n};\n\nconst numeric_greater_than_or_equal := function (\n op,\n ev,\n ast,\n ctx,\n left,\n right,\n) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val \u2265 right_val );\n};\n\nconst numeric_compare := function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val <=> right_val );\n};\n\nconst numeric_divides := function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( right_val mod left_val = 0 );\n};\n\nconst numeric_not_divides := function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( right_val mod left_val );\n};\n\nfunction string_operand ( ev, ctx, expr ) {\n const result := ev.eval_expr( expr, ev.nested_ctx( ctx ) );\n return "" unless result.length;\n const value := ev.to_string( result[0] );\n return value \u2261 null ? "" : "" _ value;\n}\n\nfunction string_operands ( ev, ctx, left, right ) {\n return [\n string_operand( ev, ctx, left ),\n string_operand( ev, ctx, right ),\n ];\n}\n\nconst string_concatenation := function ( op, ev, ast, ctx, left, right ) {\n const values := string_operands( ev, ctx, left, right );\n return op.wrap( values[0] _ values[1] );\n};\n\nconst string_equality := function ( op, ev, ast, ctx, left, right ) {\n const values := string_operands( ev, ctx, left, right );\n return op.wrap( values[0] eq values[1] );\n};\n\nconst string_inequality := function ( op, ev, ast, ctx, left, right ) {\n const values := string_operands( ev, ctx, left, right );\n return op.wrap( values[0] ne values[1] );\n};\n\nconst string_greater_than := function ( op, ev, ast, ctx, left, right ) {\n const values := string_operands( ev, ctx, left, right );\n return op.wrap( values[0] gt values[1] );\n};\n\nconst string_greater_than_or_equal := function ( op, ev, ast, ctx, left, right ) {\n const values := string_operands( ev, ctx, left, right );\n return op.wrap( values[0] ge values[1] );\n};\n\nconst string_less_than := function ( op, ev, ast, ctx, left, right ) {\n const values := string_operands( ev, ctx, left, right );\n return op.wrap( values[0] lt values[1] );\n};\n\nconst string_less_than_or_equal := function ( op, ev, ast, ctx, left, right ) {\n const values := string_operands( ev, ctx, left, right );\n return op.wrap( values[0] le values[1] );\n};\n\nconst string_compare := function ( op, ev, ast, ctx, left, right ) {\n const values := string_operands( ev, ctx, left, right );\n return op.wrap( values[0] cmp values[1] );\n};\n\nconst string_equality_insensitive := function ( op, ev, ast, ctx, left, right ) {\n const values := string_operands( ev, ctx, left, right );\n const left_val := lc values[0];\n const right_val := lc values[1];\n return op.wrap( left_val eq right_val );\n};\n\nconst string_inequality_insensitive := function ( op, ev, ast, ctx, left, right ) {\n const values := string_operands( ev, ctx, left, right );\n const left_val := lc values[0];\n const right_val := lc values[1];\n return op.wrap( left_val ne right_val );\n};\n\nconst string_greater_than_insensitive := function ( op, ev, ast, ctx, left, right ) {\n const values := string_operands( ev, ctx, left, right );\n const left_val := lc values[0];\n const right_val := lc values[1];\n return op.wrap( left_val gt right_val );\n};\n\nconst string_greater_than_or_equal_insensitive := function (\n op,\n ev,\n ast,\n ctx,\n left,\n right,\n) {\n const values := string_operands( ev, ctx, left, right );\n const left_val := lc values[0];\n const right_val := lc values[1];\n return op.wrap( left_val ge right_val );\n};\n\nconst string_less_than_insensitive := function ( op, ev, ast, ctx, left, right ) {\n const values := string_operands( ev, ctx, left, right );\n const left_val := lc values[0];\n const right_val := lc values[1];\n return op.wrap( left_val lt right_val );\n};\n\nconst string_less_than_or_equal_insensitive := function (\n op,\n ev,\n ast,\n ctx,\n left,\n right,\n) {\n const values := string_operands( ev, ctx, left, right );\n const left_val := lc values[0];\n const right_val := lc values[1];\n return op.wrap( left_val le right_val );\n};\n\nconst string_compare_insensitive := function ( op, ev, ast, ctx, left, right ) {\n const values := string_operands( ev, ctx, left, right );\n const left_val := lc values[0];\n const right_val := lc values[1];\n return op.wrap( left_val cmp right_val );\n};\n\nconst regexp_match := function ( op, ev, ast, ctx, left, right ) {\n const values := string_operands( ev, ctx, left, right );\n return op.wrap( values[0] ~ values[1] );\n};\n\nconst bitwise_and := function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val & right_val );\n};\n\nconst bitwise_xor := function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val ^ right_val );\n};\n\nconst bitwise_or := function ( op, ev, ast, ctx, left, right ) {\n const left_val := op._handle_numeric_operand( ev, ctx, left );\n const right_val := op._handle_numeric_operand( ev, ctx, right );\n return op.wrap( left_val | right_val );\n};\n\nfunction first_eval_value ( values ) {\n return values.length() > 0 ? values[0] : null;\n}\n\nfunction primitive_eval_value ( values ) {\n const first := first_eval_value(values);\n return first \u2261 null ? null : first.primitive_value();\n}\n\nfunction collection_operand ( ev, ctx, expr ) {\n const values := ev.eval_expr( expr, ev.nested_ctx( ctx ) );\n if ( values.length() = 1 ) {\n return values[0].primitive_value();\n }\n return values.map( fn value \u2192 value.primitive_value() );\n}\n\nconst logical_and := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n if ( ev.truthy( first_eval_value(left_vals) ) ) {\n return ev.eval_expr( right, ev.nested_ctx( ctx ) );\n }\n return op.wrap(false);\n};\n\nconst logical_nand := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n if ( not ev.truthy( first_eval_value(left_vals) ) ) {\n return op.wrap(true);\n }\n const right_vals := ev.eval_expr( right, ev.nested_ctx( ctx ) );\n return op.wrap( ev.truthy( first_eval_value(right_vals) ) ? false : true );\n};\n\nconst logical_butnot := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n if ( not ev.truthy( first_eval_value(left_vals) ) ) {\n return op.wrap(false);\n }\n const right_vals := ev.eval_expr( right, ev.nested_ctx( ctx ) );\n return op.wrap( ev.truthy( first_eval_value(right_vals) ) ? false : true );\n};\n\nconst logical_xor := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n const right_vals := ev.eval_expr( right, ev.nested_ctx( ctx ) );\n const left_truth := ev.truthy( first_eval_value(left_vals) );\n const right_truth := ev.truthy( first_eval_value(right_vals) );\n return op.wrap( ( left_truth xor right_truth ) ? true : false );\n};\n\nconst logical_nor := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n if ( ev.truthy( first_eval_value(left_vals) ) ) {\n return op.wrap(false);\n }\n const right_vals := ev.eval_expr( right, ev.nested_ctx( ctx ) );\n return op.wrap( ev.truthy( first_eval_value(right_vals) ) ? false : true );\n};\n\nconst logical_xnor := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n const right_vals := ev.eval_expr( right, ev.nested_ctx( ctx ) );\n const left_truth := ev.truthy( first_eval_value(left_vals) );\n const right_truth := ev.truthy( first_eval_value(right_vals) );\n return op.wrap( left_truth \u2261 right_truth );\n};\n\nconst logical_onlyif := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n if ( not ev.truthy( first_eval_value(left_vals) ) ) {\n return op.wrap(true);\n }\n const right_vals := ev.eval_expr( right, ev.nested_ctx( ctx ) );\n return op.wrap( ev.truthy( first_eval_value(right_vals) ) ? true : false );\n};\n\nconst logical_or := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n if ( ev.truthy( first_eval_value(left_vals) ) ) {\n return op.wrap(true);\n }\n const right_vals := ev.eval_expr( right, ev.nested_ctx( ctx ) );\n return op.wrap( ev.truthy( first_eval_value(right_vals) ) ? true : false );\n};\n\nconst logical_and_value := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n if ( ev.truthy( first_eval_value(left_vals) ) ) {\n return ev.eval_expr( right, ev.nested_ctx( ctx ) );\n }\n return left_vals;\n};\n\nconst logical_nand_value := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n const left_truth := ev.truthy( first_eval_value(left_vals) );\n const right_vals := ev.eval_expr( right, ev.nested_ctx( ctx ) );\n const right_truth := ev.truthy( first_eval_value(right_vals) );\n if ( left_truth and right_truth ) {\n return op.wrap(false);\n }\n return right_vals if not left_truth and right_truth;\n return op.wrap(true);\n};\n\nconst logical_butnot_value := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n const left_truth := ev.truthy( first_eval_value(left_vals) );\n return left_vals unless left_truth;\n const right_vals := ev.eval_expr( right, ev.nested_ctx( ctx ) );\n return ev.truthy( first_eval_value(right_vals) )\n ? op.wrap(false)\n : left_vals;\n};\n\nconst logical_xor_value := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n const right_vals := ev.eval_expr( right, ev.nested_ctx( ctx ) );\n const left_truth := ev.truthy( first_eval_value(left_vals) );\n const right_truth := ev.truthy( first_eval_value(right_vals) );\n if ( left_truth xor right_truth ) {\n return left_truth ? left_vals : right_vals;\n }\n return op.wrap(false);\n};\n\nconst logical_nor_value := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n const right_vals := ev.eval_expr( right, ev.nested_ctx( ctx ) );\n const left_truth := ev.truthy( first_eval_value(left_vals) );\n const right_truth := ev.truthy( first_eval_value(right_vals) );\n return op.wrap(false) if left_truth and right_truth;\n return right_vals if left_truth and not right_truth;\n return left_vals if not left_truth and right_truth;\n return op.wrap(true);\n};\n\nconst logical_xnor_value := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n const right_vals := ev.eval_expr( right, ev.nested_ctx( ctx ) );\n const left_truth := ev.truthy( first_eval_value(left_vals) );\n const right_truth := ev.truthy( first_eval_value(right_vals) );\n if ( left_truth \u2261 right_truth ) {\n return left_truth ? right_vals : op.wrap(true);\n }\n return left_truth ? right_vals : left_vals;\n};\n\nconst logical_onlyif_value := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n if ( not ev.truthy( first_eval_value(left_vals) ) ) {\n return op.wrap(true);\n }\n return ev.eval_expr( right, ev.nested_ctx( ctx ) );\n};\n\nconst logical_or_value := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n if ( ev.truthy( first_eval_value(left_vals) ) ) {\n return left_vals;\n }\n return ev.eval_expr( right, ev.nested_ctx( ctx ) );\n};\n\nconst set_union := function ( op, ev, ast, ctx, left, right ) {\n const left_val := collection_operand( ev, ctx, left );\n const right_val := collection_operand( ev, ctx, right );\n return op.wrap( left_val union right_val );\n};\n\nconst set_intersection := function ( op, ev, ast, ctx, left, right ) {\n const left_val := collection_operand( ev, ctx, left );\n const right_val := collection_operand( ev, ctx, right );\n return op.wrap( left_val intersection right_val );\n};\n\nconst set_difference := function ( op, ev, ast, ctx, left, right ) {\n const left_val := collection_operand( ev, ctx, left );\n const right_val := collection_operand( ev, ctx, right );\n return op.wrap( left_val \u2216 right_val );\n};\n\nconst collection_membership := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n const left_val := primitive_eval_value(left_vals);\n const right_val := collection_operand( ev, ctx, right );\n return op.wrap( left_val in right_val );\n};\n\nconst collection_non_membership := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n const left_val := primitive_eval_value(left_vals);\n const right_val := collection_operand( ev, ctx, right );\n return op.wrap( left_val \u2209 right_val );\n};\n\nconst set_subsetof := function ( op, ev, ast, ctx, left, right ) {\n const left_val := collection_operand( ev, ctx, left );\n const right_val := collection_operand( ev, ctx, right );\n return op.wrap( left_val subsetof right_val );\n};\n\nconst set_supersetof := function ( op, ev, ast, ctx, left, right ) {\n const left_val := collection_operand( ev, ctx, left );\n const right_val := collection_operand( ev, ctx, right );\n return op.wrap( left_val supersetof right_val );\n};\n\nconst set_equivalentof := function ( op, ev, ast, ctx, left, right ) {\n const left_val := collection_operand( ev, ctx, left );\n const right_val := collection_operand( ev, ctx, right );\n return op.wrap( left_val equivalentof right_val );\n};\n\nconst type_aware_equality := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n const right_vals := ev.eval_expr( right, ev.nested_ctx( ctx ) );\n return op.wrap( ev.equals(\n first_eval_value(left_vals),\n first_eval_value(right_vals),\n ) );\n};\n\nconst type_aware_inequality := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n const right_vals := ev.eval_expr( right, ev.nested_ctx( ctx ) );\n return op.wrap( not ev.equals(\n first_eval_value(left_vals),\n first_eval_value(right_vals),\n ) );\n};\n\nfunction builtin_can ( value, method_name ) {\n return false if value \u2261 null\n or method_name \u2261 null\n or method_name eq "";\n\n if ( value instanceof Array or value instanceof Bag or value instanceof Set ) {\n return method_name in [\n "length",\n "empty",\n "contains",\n "push",\n "to_Array",\n "to_Bag",\n "to_Set",\n ];\n }\n if ( value instanceof Dict or value instanceof PairList ) {\n return method_name in [\n "length",\n "empty",\n "exists",\n "keys",\n "values",\n "to_Array",\n ];\n }\n if ( value instanceof String or value instanceof BinaryString ) {\n return method_name in [ "length" ];\n }\n return false;\n}\n\nconst object_can := function ( op, ev, ast, ctx, left, right ) {\n const left_vals := ev.eval_expr( left, ev.nested_ctx( ctx ) );\n const right_vals := ev.eval_expr( right, ev.nested_ctx( ctx ) );\n const left_val := primitive_eval_value(left_vals);\n const right_val := primitive_eval_value(right_vals);\n return op.wrap(\n ( left_val can (right_val) ) or builtin_can( left_val, right_val )\n );\n};\n\nconst STANDARD_OPERATORS := [\n new Operator(\n spelling: "?:",\n kind: "ELVIS",\n precedence: 2,\n lex_ignore: true,\n ),\n\n new Operator(\n spelling: "+",\n kind: "UPLUS",\n unary: true,\n precedence: 40,\n f: function ( op, ev, ast, ctx, expr ) {\n const value := op._handle_numeric_operand( ev, ctx, expr );\n return op.wrap( +value );\n },\n ),\n\n new Operator(\n spelling: "-",\n kind: "UMINUS",\n unary: true,\n precedence: 40,\n f: function ( op, ev, ast, ctx, expr ) {\n const value := op._handle_numeric_operand( ev, ctx, expr );\n return op.wrap( -value );\n },\n ),\n\n new Operator(\n spelling: "\u221A",\n kind: "SQRT",\n unary: true,\n precedence: 40,\n f: function ( op, ev, ast, ctx, expr ) {\n const value := op._handle_numeric_operand( ev, ctx, expr );\n return op.wrap( sqrt value );\n },\n ),\n\n new Operator(\n spelling: "!",\n kind: "NOT",\n unary: true,\n precedence: 40,\n f: logical_negation,\n ),\n\n new Operator(\n spelling: "\xAC",\n kind: "NOTSYM",\n unary: true,\n precedence: 40,\n f: logical_negation,\n ),\n\n new Operator(\n spelling: "**",\n kind: "POW",\n require_ws: true,\n right_assoc: true,\n precedence: 26,\n f: numeric_power,\n ),\n\n new Operator(\n spelling: "\xD7",\n kind: "TIMES_SIGN",\n precedence: 24,\n f: numeric_multiplication,\n ),\n\n new Operator(\n spelling: "*",\n kind: "TIMES",\n require_ws: true,\n precedence: 24,\n lex_ignore: true,\n alias: "STAR",\n f: numeric_multiplication,\n ),\n\n new Operator(\n spelling: "\xF7",\n kind: "DIVIDE_SIGN",\n precedence: 24,\n f: numeric_division,\n ),\n\n new Operator(\n spelling: "/",\n kind: "DIVIDE",\n require_ws: true,\n precedence: 24,\n lex_ignore: true,\n alias: "SLASH",\n f: numeric_division,\n ),\n\n new Operator(\n spelling: "mod",\n kind: "MOD",\n precedence: 24,\n f: numeric_modulus,\n ),\n\n new Operator(\n spelling: "+",\n kind: "PLUS",\n require_ws: true,\n precedence: 22,\n f: numeric_addition,\n ),\n\n new Operator(\n spelling: "-",\n kind: "MINUS",\n require_ws: true,\n precedence: 22,\n f: numeric_subtraction,\n ),\n\n new Operator(\n spelling: "=",\n kind: "EQ",\n precedence: 10,\n f: numeric_equality,\n ),\n\n new Operator(\n spelling: "\u2260",\n kind: "NE",\n precedence: 10,\n f: numeric_inequality,\n ),\n\n new Operator(\n spelling: "<",\n kind: "LT",\n precedence: 10,\n f: numeric_less_than,\n ),\n\n new Operator(\n spelling: ">",\n kind: "GT",\n precedence: 10,\n f: numeric_greater_than,\n ),\n\n new Operator(\n spelling: "\u2264",\n kind: "LE_SIGN",\n precedence: 10,\n f: numeric_less_than_or_equal,\n ),\n\n new Operator(\n spelling: "<=",\n kind: "LE",\n precedence: 10,\n f: numeric_less_than_or_equal,\n ),\n\n new Operator(\n spelling: "\u2265",\n kind: "GE_SIGN",\n precedence: 10,\n f: numeric_greater_than_or_equal,\n ),\n\n new Operator(\n spelling: ">=",\n kind: "GE",\n precedence: 10,\n f: numeric_greater_than_or_equal,\n ),\n\n new Operator(\n spelling: "\u2276",\n kind: "CMP_SIGN",\n precedence: 10,\n f: numeric_compare,\n ),\n\n new Operator(\n spelling: "<=>",\n kind: "CMP",\n precedence: 10,\n f: numeric_compare,\n ),\n\n new Operator(\n spelling: "\u2277",\n kind: "CMP_REV_SIGN",\n precedence: 10,\n f: numeric_compare,\n ),\n\n new Operator(\n spelling: "\u2223",\n kind: "DIVIDES_SIGN",\n precedence: 10,\n f: numeric_divides,\n ),\n\n new Operator(\n spelling: "divides",\n kind: "DIVIDES",\n precedence: 10,\n f: numeric_divides,\n ),\n\n new Operator(\n spelling: "\u2224",\n kind: "NOT_DIVIDES_SIGN",\n precedence: 10,\n f: numeric_not_divides,\n ),\n\n new Operator(\n spelling: "_",\n kind: "CONCAT",\n require_ws: true,\n precedence: 21,\n f: string_concatenation,\n ),\n\n new Operator(\n spelling: "eq",\n kind: "STR_EQ",\n precedence: 10,\n f: string_equality,\n ),\n\n new Operator(\n spelling: "ne",\n kind: "STR_NE",\n precedence: 10,\n f: string_inequality,\n ),\n\n new Operator(\n spelling: "gt",\n kind: "STR_GT",\n precedence: 10,\n f: string_greater_than,\n ),\n\n new Operator(\n spelling: "ge",\n kind: "STR_GE",\n precedence: 10,\n f: string_greater_than_or_equal,\n ),\n\n new Operator(\n spelling: "lt",\n kind: "STR_LT",\n precedence: 10,\n f: string_less_than,\n ),\n\n new Operator(\n spelling: "le",\n kind: "STR_LE",\n precedence: 10,\n f: string_less_than_or_equal,\n ),\n\n new Operator(\n spelling: "cmp",\n kind: "STR_CMP",\n precedence: 10,\n f: string_compare,\n ),\n\n new Operator(\n spelling: "eqi",\n kind: "STR_EQI",\n precedence: 10,\n f: string_equality_insensitive,\n ),\n\n new Operator(\n spelling: "nei",\n kind: "STR_NEI",\n precedence: 10,\n f: string_inequality_insensitive,\n ),\n\n new Operator(\n spelling: "gti",\n kind: "STR_GTI",\n precedence: 10,\n f: string_greater_than_insensitive,\n ),\n\n new Operator(\n spelling: "gei",\n kind: "STR_GEI",\n precedence: 10,\n f: string_greater_than_or_equal_insensitive,\n ),\n\n new Operator(\n spelling: "lti",\n kind: "STR_LTI",\n precedence: 10,\n f: string_less_than_insensitive,\n ),\n\n new Operator(\n spelling: "lei",\n kind: "STR_LEI",\n precedence: 10,\n f: string_less_than_or_equal_insensitive,\n ),\n\n new Operator(\n spelling: "cmpi",\n kind: "STR_CMPI",\n precedence: 10,\n f: string_compare_insensitive,\n ),\n\n new Operator(\n spelling: "~",\n kind: "REGEXP_MATCH",\n precedence: 10,\n f: regexp_match,\n ),\n\n new Operator(\n spelling: "\xAB",\n kind: "LSHIFT_SIGN",\n precedence: 18,\n f: numeric_left_shift,\n ),\n\n new Operator(\n spelling: "<<",\n kind: "LSHIFT",\n precedence: 18,\n f: numeric_left_shift,\n ),\n\n new Operator(\n spelling: "\xBB",\n kind: "RSHIFT_SIGN",\n precedence: 18,\n f: numeric_right_shift,\n ),\n\n new Operator(\n spelling: ">>",\n kind: "RSHIFT",\n precedence: 18,\n f: numeric_right_shift,\n ),\n\n new Operator(\n spelling: "&",\n kind: "BAND",\n precedence: 16,\n f: bitwise_and,\n ),\n\n new Operator(\n spelling: "^",\n kind: "BXOR",\n precedence: 14,\n f: bitwise_xor,\n ),\n\n new Operator(\n spelling: "|",\n kind: "BOR",\n precedence: 12,\n f: bitwise_or,\n ),\n\n new Operator(\n spelling: "\u22C0",\n kind: "LAND_SIGN",\n precedence: 7,\n f: logical_and,\n ),\n\n new Operator(\n spelling: "and",\n kind: "LAND",\n precedence: 7,\n f: logical_and,\n ),\n\n new Operator(\n spelling: "\u22C0?",\n kind: "LAND_VALUE_SIGN",\n precedence: 7,\n f: logical_and_value,\n ),\n\n new Operator(\n spelling: "and?",\n kind: "LAND_VALUE",\n precedence: 7,\n f: logical_and_value,\n ),\n\n new Operator(\n spelling: "\u22BC",\n kind: "LNAND_SIGN",\n precedence: 7,\n f: logical_nand,\n ),\n\n new Operator(\n spelling: "nand",\n kind: "LNAND",\n precedence: 7,\n f: logical_nand,\n ),\n\n new Operator(\n spelling: "\u22BC?",\n kind: "LNAND_VALUE_SIGN",\n precedence: 7,\n f: logical_nand_value,\n ),\n\n new Operator(\n spelling: "nand?",\n kind: "LNAND_VALUE",\n precedence: 7,\n f: logical_nand_value,\n ),\n\n new Operator(\n spelling: "\u22AD",\n kind: "LBUTNOT_SIGN",\n precedence: 7,\n f: logical_butnot,\n ),\n\n new Operator(\n spelling: "butnot",\n kind: "LBUTNOT",\n precedence: 7,\n f: logical_butnot,\n ),\n\n new Operator(\n spelling: "\u22AD?",\n kind: "LBUTNOT_VALUE_SIGN",\n precedence: 7,\n f: logical_butnot_value,\n ),\n\n new Operator(\n spelling: "butnot?",\n kind: "LBUTNOT_VALUE",\n precedence: 7,\n f: logical_butnot_value,\n ),\n\n new Operator(\n spelling: "\u22BB",\n kind: "LXOR_SIGN",\n precedence: 5,\n f: logical_xor,\n ),\n\n new Operator(\n spelling: "xor",\n kind: "LXOR",\n precedence: 5,\n f: logical_xor,\n ),\n\n new Operator(\n spelling: "\u22BB?",\n kind: "LXOR_VALUE_SIGN",\n precedence: 5,\n f: logical_xor_value,\n ),\n\n new Operator(\n spelling: "xor?",\n kind: "LXOR_VALUE",\n precedence: 5,\n f: logical_xor_value,\n ),\n\n new Operator(\n spelling: "\u22BD",\n kind: "LNOR_SIGN",\n precedence: 5,\n f: logical_nor,\n ),\n\n new Operator(\n spelling: "nor",\n kind: "LNOR",\n precedence: 5,\n f: logical_nor,\n ),\n\n new Operator(\n spelling: "\u22BD?",\n kind: "LNOR_VALUE_SIGN",\n precedence: 5,\n f: logical_nor_value,\n ),\n\n new Operator(\n spelling: "nor?",\n kind: "LNOR_VALUE",\n precedence: 5,\n f: logical_nor_value,\n ),\n\n new Operator(\n spelling: "\u2194",\n kind: "LXNOR_SIGN",\n precedence: 5,\n f: logical_xnor,\n ),\n\n new Operator(\n spelling: "xnor",\n kind: "LXNOR",\n precedence: 5,\n f: logical_xnor,\n ),\n\n new Operator(\n spelling: "\u2194?",\n kind: "LXNOR_VALUE_SIGN",\n precedence: 5,\n f: logical_xnor_value,\n ),\n\n new Operator(\n spelling: "xnor?",\n kind: "LXNOR_VALUE",\n precedence: 5,\n f: logical_xnor_value,\n ),\n\n new Operator(\n spelling: "\u22A8",\n kind: "LONLYIF_SIGN",\n precedence: 4,\n right_assoc: true,\n f: logical_onlyif,\n ),\n\n new Operator(\n spelling: "onlyif",\n kind: "LONLYIF",\n precedence: 4,\n right_assoc: true,\n f: logical_onlyif,\n ),\n\n new Operator(\n spelling: "\u22A8?",\n kind: "LONLYIF_VALUE_SIGN",\n precedence: 4,\n right_assoc: true,\n f: logical_onlyif_value,\n ),\n\n new Operator(\n spelling: "onlyif?",\n kind: "LONLYIF_VALUE",\n precedence: 4,\n right_assoc: true,\n f: logical_onlyif_value,\n ),\n\n new Operator(\n spelling: "\u22C1",\n kind: "LOR_SIGN",\n precedence: 2,\n f: logical_or,\n ),\n\n new Operator(\n spelling: "or",\n kind: "LOR",\n precedence: 2,\n f: logical_or,\n ),\n\n new Operator(\n spelling: "\u22C1?",\n kind: "LOR_VALUE_SIGN",\n precedence: 2,\n f: logical_or_value,\n ),\n\n new Operator(\n spelling: "or?",\n kind: "LOR_VALUE",\n precedence: 2,\n f: logical_or_value,\n ),\n\n new Operator(\n spelling: "\u22C3",\n kind: "SET_UNION_SIGN",\n precedence: 20,\n f: set_union,\n ),\n\n new Operator(\n spelling: "union",\n kind: "SET_UNION",\n precedence: 20,\n f: set_union,\n ),\n\n new Operator(\n spelling: "\u22C2",\n kind: "SET_INTERSECTION_SIGN",\n precedence: 20,\n f: set_intersection,\n ),\n\n new Operator(\n spelling: "intersection",\n kind: "SET_INTERSECTION",\n precedence: 20,\n f: set_intersection,\n ),\n\n new Operator(\n spelling: "\u2216",\n kind: "SET_DIFFERENCE_SIGN",\n precedence: 20,\n f: set_difference,\n ),\n\n new Operator(\n spelling: "\\\\",\n kind: "SET_DIFFERENCE",\n precedence: 20,\n require_ws: true,\n f: set_difference,\n ),\n\n new Operator(\n spelling: "\u2208",\n kind: "MEMBER_SIGN",\n precedence: 10,\n f: collection_membership,\n ),\n\n new Operator(\n spelling: "in",\n kind: "MEMBER",\n precedence: 10,\n f: collection_membership,\n ),\n\n new Operator(\n spelling: "\u2209",\n kind: "NOT_MEMBER_SIGN",\n precedence: 10,\n f: collection_non_membership,\n ),\n\n new Operator(\n spelling: "\u2282",\n kind: "SUBSETOF_SIGN",\n precedence: 10,\n f: set_subsetof,\n ),\n\n new Operator(\n spelling: "subsetof",\n kind: "SUBSETOF",\n precedence: 10,\n f: set_subsetof,\n ),\n\n new Operator(\n spelling: "\u2283",\n kind: "SUPERSETOF_SIGN",\n precedence: 10,\n f: set_supersetof,\n ),\n\n new Operator(\n spelling: "supersetof",\n kind: "SUPERSETOF",\n precedence: 10,\n f: set_supersetof,\n ),\n\n new Operator(\n spelling: "\u2282\u2283",\n kind: "EQUIVALENTOF_SIGN",\n precedence: 10,\n f: set_equivalentof,\n ),\n\n new Operator(\n spelling: "equivalentof",\n kind: "EQUIVALENTOF",\n precedence: 10,\n f: set_equivalentof,\n ),\n\n new Operator(\n spelling: "\u2261",\n kind: "TYPE_EQ_SIGN",\n precedence: 8,\n f: type_aware_equality,\n ),\n\n new Operator(\n spelling: "==",\n kind: "TYPE_EQ",\n precedence: 8,\n f: type_aware_equality,\n ),\n\n new Operator(\n spelling: "\u2262",\n kind: "TYPE_NE_SIGN",\n precedence: 8,\n f: type_aware_inequality,\n ),\n\n new Operator(\n spelling: "!=",\n kind: "TYPE_NE",\n precedence: 8,\n f: type_aware_inequality,\n ),\n\n new Operator(\n spelling: "can",\n kind: "CAN",\n precedence: 10,\n f: object_can,\n ),\n\n new Operator(\n spelling: "~",\n kind: "BNOT",\n unary: true,\n precedence: 40,\n f: function ( op, ev, ast, ctx, expr ) {\n const value := op._handle_numeric_operand( ev, ctx, expr );\n return op.wrap( ~value );\n },\n ),\n];\n';
|
|
47346
47414
|
virtualFiles["/modules/std/template/z.zzm"] = '=encoding utf8\n\n=head1 NAME\n\nstd/template/z - Pure ZuzuScript template engine.\n\n=head1 SYNOPSIS\n\n from std/template/z import ZTemplate;\n\n let inline := new ZTemplate(\n string: "Hello {{ user/name }}!",\n );\n say( inline.process( { user: { name: "Ada" } } ) );\n\n let file_tmpl := new ZTemplate(\n file: "templates/page.zt",\n escape: "html",\n );\n say( file_tmpl.process( data ) );\n\n=head1 IMPLEMENTATION SUPPORT\n\nThis module is supported by zuzu.pl, zuzu-rust, and zuzu-js on Node and\nElectron. It is partially supported by zuzu-js in the browser: complex,\nHTTP/JSON/ZPath pipeline, self-contained, and trait-object rendering\ncoverage passes, but filesystem-backed template loading and database row\nrendering coverage are unsupported.\n\n=head1 DESCRIPTION\n\nC<std/template/z> is a pure ZuzuScript template renderer.\n\nThe module compiles template text into a cached parse tree and renders\nit against data using C<std/path/z> expressions.\n\n=head1 EXPORTS\n\n=head2 Classes\n\n=over\n\n=item C<< ZTemplate({ string?: String, file?, escape?: String, includes?: Boolean }) >>\n\nConstructs a template from inline text or a file path. Returns:\nC<ZTemplate>.\n\n=over\n\n=item C<< template.process(data) >>\n\nParameters: C<data> is the model used for path lookups. Returns:\nC<String>. Renders the template.\n\n=back\n\n=back\n\n=head1 TAG FORMS\n\n=over 4\n\n=item * C<{{ expr }}>\n\nRender expression output using template default escaping.\n\n=item * C<{{ expr :: raw }}>, C<{{ expr :: html }}>\n\nRender expression output using per-tag escape override.\n\n=item * C<{{# expr }}> ... C<{{/expr}}>\n\nBlock form. Each truthy match renders child nodes.\n\n=item * C<{{> include.zt }}>\n\nInclude another template file. Relative include paths resolve from the\ncurrent template\'s file directory.\n\n=back\n\n=head1 ESCAPING\n\nDefault escape mode is C<html>. Supported escape modes are C<html> and\nC<raw>.\n\nC<html> escapes C<&>, C<< < >>, C<E<gt>>, double quotes, and single\nquotes.\n\n=head1 INCLUDE RULES\n\n=over 4\n\n=item * Include processing is enabled by default.\n\n=item * Pass C<includes: false> to disable include tags.\n\n=item * Relative includes require a file-backed template source.\n\n=item * Circular include chains throw a deterministic error.\n\n=back\n\n=head1 KNOWN DIFFERENCES\n\nThis module is implemented on top of C<std/path/z>. Parser/rendering\ndeltas across runtimes are covered in ztests and should be treated as\nimplementation bugs unless explicitly documented.\n\n=head1 COPYRIGHT AND LICENCE\n\nB<< std/template/z >> 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\nfrom std/string import index, substr, trim;\nfrom std/path/z import ZPath;\n\nclass ZTemplate {\n let string;\n let file;\n let String escape := "html";\n let includes := true;\n let source_file := null;\n let Array tree := [];\n let compiled_paths := {};\n\n method __build__ () {\n let has_string := string \u2262 null;\n let has_file := file \u2262 null;\n\n if ( has_string and has_file ) {\n die "Specify only one of \\"string\\" or \\"file\\"";\n }\n if ( not has_string and not has_file ) {\n die "Missing template source: provide \\"string\\" or \\"file\\"";\n }\n\n escape := lc( "" _ escape );\n if ( escape \u2262 "html" and escape \u2262 "raw" ) {\n die `Invalid escape mode "${escape}"`;\n }\n\n if ( has_file ) {\n let loaded := self._read_template_file(file);\n string := loaded{text};\n source_file := loaded{file};\n }\n\n tree := self._parse_template(\n template: string \u2261 null ? "": string,\n current_file: source_file,\n seen_files: {},\n includes: includes,\n );\n }\n\n method process ( data ) {\n die "process() requires a data model" if data \u2261 null;\n\n return self._render_nodes(\n nodes: tree,\n context: data,\n eval_meta: {},\n default_escape: escape,\n );\n }\n\n method _parse_template ( ... PairList args ) {\n let template := args.get( "template", "" );\n let current_file := args.get( "current_file", null );\n let seen_files := args.get( "seen_files", {} );\n let includes_enabled := args.get( "includes", true );\n let root := [];\n\n let stack := [\n {\n expr: null,\n nodes: root,\n },\n ];\n\n let pos := 0;\n while ( true ) {\n let tag_open := index( template, "{{", pos );\n last if tag_open < 0;\n\n let text := substr( template, pos, tag_open - pos );\n self._push_text( stack[ stack.length() - 1 ]{nodes}, text );\n\n let tag_close := index( template, "}}", tag_open + 2 );\n if ( tag_close < 0 ) {\n die `Unterminated tag at character ${tag_open}`;\n }\n\n let raw := substr( template, tag_open + 2, tag_close - tag_open - 2 );\n let tagged := trim(raw);\n\n if ( substr( tagged, 0, 1 ) \u2261 "#" ) {\n let inner := trim( substr( tagged, 1 ) );\n let parsed := self._parse_expression_spec(inner);\n let block := {\n type: "block",\n expr_src: parsed{expr},\n nodes: [],\n };\n stack[ stack.length() - 1 ]{nodes}.push(block);\n stack.push( {\n expr: parsed{expr},\n nodes: block{nodes},\n } );\n }\n else if ( substr( tagged, 0, 1 ) \u2261 "/" ) {\n let inner := trim( substr( tagged, 1 ) );\n let current := stack.pop();\n if ( current \u2261 null or current{expr} \u2261 null ) {\n die `Mismatched close tag {{/${inner}}}`;\n }\n if ( inner \u2262 "" ) {\n let parsed := self._parse_expression_spec(inner);\n if ( current{expr} \u2262 parsed{expr} ) {\n die `Mismatched close tag {{/${inner}}} for {{${current{expr}}}}`;\n }\n }\n }\n else if ( tagged \u2262 "" ) {\n if ( substr( tagged, 0, 1 ) \u2261 ">" ) {\n die "Template includes are disabled"\n if not includes_enabled;\n\n let include_path := trim( substr( tagged, 1 ) );\n die "Empty include path in template tag"\n if include_path \u2261 "";\n\n let resolved := self._resolve_include_path(\n include_path: include_path,\n current_file: current_file,\n );\n\n let key := self._canonical_path(resolved);\n if ( seen_files.exists(key) and seen_files.get(key) ) {\n die `Circular include detected for "${resolved}"`;\n }\n\n seen_files.set( key, true );\n let loaded := self._read_template_file(resolved);\n let include_nodes := self._parse_template(\n template: loaded{text},\n current_file: loaded{file},\n seen_files: seen_files,\n includes: includes_enabled,\n );\n seen_files.remove(key);\n\n for ( let node in include_nodes ) {\n stack[ stack.length() - 1 ]{nodes}.push(node);\n }\n }\n else {\n let parsed := self._parse_expression_spec(tagged);\n stack[ stack.length() - 1 ]{nodes}.push( {\n type: "expr",\n expr_src: parsed{expr},\n escape: parsed{escape},\n } );\n }\n }\n\n pos := tag_close + 2;\n }\n\n let tail := substr( template, pos );\n self._push_text( stack[ stack.length() - 1 ]{nodes}, tail );\n\n if ( stack.length() > 1 ) {\n let missing := stack[ stack.length() - 1 ]{expr};\n die `Missing close tag for {{${missing}}}`;\n }\n\n return root;\n }\n\n method _push_text ( nodes, text ) {\n if ( text \u2262 "" ) {\n nodes.push( {\n type: "text",\n text: text,\n } );\n }\n }\n\n method _read_template_file ( raw_file ) {\n let path_obj := self._to_path(raw_file);\n let text := path_obj.slurp_utf8();\n let canonical := self._canonical_path(path_obj);\n\n return {\n text: text,\n file: canonical,\n };\n }\n\n method _to_path ( raw_file ) {\n from std/io import Path;\n if ( raw_file instanceof Path ) {\n return raw_file;\n }\n return new Path( "" _ raw_file );\n }\n\n method _canonical_path ( path_obj ) {\n let obj := self._to_path(path_obj);\n let canonical := obj.realpath();\n if ( canonical \u2261 null ) {\n canonical := obj.absolute();\n }\n if ( canonical \u2261 null ) {\n return obj.to_String;\n }\n from std/io import Path;\n if ( canonical instanceof Path ) {\n return canonical.to_String;\n }\n return "" _ canonical;\n }\n\n method _resolve_include_path ( ... PairList args ) {\n let include_path := args.get( "include_path" );\n let current_file := args.get( "current_file" );\n\n let include_obj := self._to_path(include_path);\n if ( include_obj.is_absolute() ) {\n return include_obj.to_String;\n }\n\n if ( current_file \u2261 null ) {\n die `Relative include path "${include_path}" requires file-based template source`;\n }\n\n let base := self._to_path(current_file).parent();\n return base.child( include_obj.to_String ).to_String;\n }\n\n method _parse_expression_spec ( raw ) {\n let expr := raw;\n let escape_mode := null;\n\n let split := self._find_escape_separator(raw);\n if ( split \u2262 null ) {\n let lhs := split[0];\n let rhs := split[1];\n if ( rhs \u2261 "html" or rhs \u2261 "raw" ) {\n expr := lhs;\n escape_mode := rhs;\n }\n }\n\n expr := trim(expr);\n if ( expr \u2261 "" ) {\n die "Empty expression in template tag";\n }\n\n return {\n expr: expr,\n escape: escape_mode,\n };\n }\n\n method _find_escape_separator ( text ) {\n let quote := "";\n let i := 0;\n while ( i < length text ) {\n let ch := substr( text, i, 1 );\n if ( quote \u2262 "" ) {\n if ( ch \u2261 "\\\\" ) {\n i += 2;\n next;\n }\n if ( ch \u2261 quote ) {\n quote := "";\n }\n i++;\n next;\n }\n\n if ( ch \u2261 "\\"" or ch \u2261 "\'" ) {\n quote := ch;\n i++;\n next;\n }\n\n if ( ch \u2261 ":" and substr( text, i, 2 ) \u2261 "::" ) {\n let lhs := trim( substr( text, 0, i ) );\n let rhs := lc( trim( substr( text, i + 2 ) ) );\n return [ lhs, rhs ];\n }\n\n i++;\n }\n\n return null;\n }\n\n method _render_nodes ( ... PairList args ) {\n let nodes := args.get( "nodes", [] );\n let context := args.get( "context", null );\n let eval_meta := args.get( "eval_meta", {} );\n let default_escape := args.get( "default_escape", "html" );\n let out := "";\n\n for ( let node in nodes ) {\n if ( node{type} \u2261 "text" ) {\n out _= node{text};\n }\n else if ( node{type} \u2261 "expr" ) {\n let results := self._evaluate_expression(\n expr_src: node{expr_src},\n context: context,\n eval_meta: eval_meta,\n );\n let value := "";\n for ( let matched in results ) {\n let sv := matched.string_value();\n value _= sv \u2261 null ? "" : sv;\n }\n\n let escape_mode := node{escape};\n if ( escape_mode \u2261 null ) {\n escape_mode := default_escape;\n }\n\n if ( escape_mode \u2261 "html" ) {\n out _= self._escape_html(value);\n }\n else {\n out _= value;\n }\n }\n else if ( node{type} \u2261 "block" ) {\n let results := self._evaluate_expression(\n expr_src: node{expr_src},\n context: context,\n eval_meta: eval_meta,\n );\n for ( let matched in results ) {\n let primitive := matched.primitive_value();\n next if not self._truthy(primitive);\n\n let inner_context := context;\n let inner_meta := eval_meta;\n if ( self._node_has_identity(matched) ) {\n inner_context := matched;\n inner_meta := { parentset: results };\n }\n\n out _= self._render_nodes(\n nodes: node{nodes},\n context: inner_context,\n eval_meta: inner_meta,\n default_escape: default_escape,\n );\n }\n }\n }\n\n return out;\n }\n\n method _evaluate_expression ( ... PairList args ) {\n let expr_src := args.get( "expr_src" );\n let context := args.get( "context" );\n let eval_meta := args.get( "eval_meta", {} );\n if ( not compiled_paths.exists(expr_src) ) {\n compiled_paths.set(\n expr_src,\n self._compile_path(expr_src),\n );\n }\n let zpath := compiled_paths.get(expr_src);\n return zpath.evaluate( context, eval_meta );\n }\n\n method _compile_path ( expr_src ) {\n return new ZPath( path: expr_src );\n }\n\n method _node_has_identity ( node ) {\n let parent := node.parent();\n if ( parent \u2261 null ) {\n return false;\n }\n\n return true;\n }\n\n method _truthy ( value ) {\n return value ? true : false;\n }\n\n method _escape_html ( text ) {\n let out := "";\n let i := 0;\n while ( i < length text ) {\n let ch := substr( text, i, 1 );\n if ( ch \u2261 "&" ) {\n out _= "&";\n }\n else if ( ch \u2261 "<" ) {\n out _= "<";\n }\n else if ( ch \u2261 ">" ) {\n out _= ">";\n }\n else if ( ch \u2261 "\\"" ) {\n out _= """;\n }\n else if ( ch \u2261 "\'" ) {\n out _= "'";\n }\n else {\n out _= ch;\n }\n i++;\n }\n return out;\n }\n}\n';
|
|
47347
47415
|
virtualFiles["/modules/std/template/zz.zzm"] = `=encoding utf8
|
|
@@ -47785,7 +47853,7 @@ class ZZTemplate extends ZTemplate {
|
|
|
47785
47853
|
}
|
|
47786
47854
|
});
|
|
47787
47855
|
|
|
47788
|
-
// ../../../../../tmp/zuzu-browser-build.
|
|
47856
|
+
// ../../../../../tmp/zuzu-browser-build.UGYrnC/browser-worker-entry.generated.js
|
|
47789
47857
|
var { createBrowserStdlib } = require_browser_stdlib_generated();
|
|
47790
47858
|
globalThis.__ZUZU_BROWSER_DEFAULT_RUNTIME_OPTIONS__ = createBrowserStdlib();
|
|
47791
47859
|
var { installBrowserWorker } = require_browser_worker_entry();
|