Files
Foundry-VTT-Docker/resources/app/node_modules/peggy/lib/compiler/asts.js

146 lines
3.7 KiB
JavaScript
Raw Normal View History

2025-01-04 00:34:03 +01:00
"use strict";
const visitor = require("./visitor");
/**
* Combine two things, each of which might be an array, into a single value,
* in the order [...a, ...b].
*
* @template T
* @param {T | T[]} a
* @param {T | T[]} b
* @returns {T | T[]}
*/
function combinePossibleArrays(a, b) {
// First might be an array, second will not. Either might be null.
if (!(a && b)) {
return a || b;
}
const aa = Array.isArray(a) ? a : [a];
aa.push(b);
return aa;
}
// AST utilities.
const asts = {
/**
* Find the rule with the given name, if it exists.
*
* @param {PEG.ast.Grammar} ast
* @param {string} name
* @returns {PEG.ast.Rule | undefined}
*/
findRule(ast, name) {
for (let i = 0; i < ast.rules.length; i++) {
if (ast.rules[i].name === name) {
return ast.rules[i];
}
}
return undefined;
},
/**
* Find the index of the rule with the given name, if it exists.
* Otherwise returns -1.
*
* @param {PEG.ast.Grammar} ast
* @param {string} name
* @returns {number}
*/
indexOfRule(ast, name) {
for (let i = 0; i < ast.rules.length; i++) {
if (ast.rules[i].name === name) {
return i;
}
}
// istanbul ignore next Presence of rules checked using another approach that not involve this function
// Any time when it is called the rules always exist
return -1;
},
alwaysConsumesOnSuccess(ast, node) {
function consumesTrue() { return true; }
function consumesFalse() { return false; }
const consumes = visitor.build({
choice(node) {
return node.alternatives.every(consumes);
},
sequence(node) {
return node.elements.some(consumes);
},
simple_and: consumesFalse,
simple_not: consumesFalse,
optional: consumesFalse,
zero_or_more: consumesFalse,
repeated(node) {
// If minimum is `null` it is equals to maximum (parsed from `|exact|` syntax)
const min = node.min ? node.min : node.max;
// If the low boundary is variable then it can be zero.
// Expression, repeated zero times, does not consume any input
// but always matched - so it does not always consumes on success
if (min.type !== "constant" || min.value === 0) {
return false;
}
if (consumes(node.expression)) {
return true;
}
// |node.delimiter| used only when |node.expression| match at least two times
// The first `if` filtered out all non-constant minimums, so at this point
// |min.value| is always a constant
if (min.value > 1 && node.delimiter && consumes(node.delimiter)) {
return true;
}
return false;
},
semantic_and: consumesFalse,
semantic_not: consumesFalse,
rule_ref(node) {
const rule = asts.findRule(ast, node.name);
// Because we run all checks in one stage, some rules could be missing.
// Checking for missing rules could be executed in parallel to this check
return rule ? consumes(rule) : undefined;
},
library_ref() {
// No way to know for external rules.
return false;
},
literal(node) {
return node.value !== "";
},
class: consumesTrue,
any: consumesTrue,
});
return consumes(node);
},
combine(asts) {
return asts.reduce((combined, ast) => {
combined.topLevelInitializer = combinePossibleArrays(
combined.topLevelInitializer,
ast.topLevelInitializer
);
combined.initializer = combinePossibleArrays(
combined.initializer,
ast.initializer
);
combined.rules = combined.rules.concat(ast.rules);
return combined;
});
},
};
module.exports = asts;