@swaggerexpert/json-pointer
Version:
RCF 6901 implementation of JSON Pointer
1,466 lines (1,379 loc) • 89.6 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["JSONPointer"] = factory();
else
root["JSONPointer"] = factory();
})(self, () => {
return /******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ 58:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _escape_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(980);
/* harmony import */ var _errors_JSONPointerCompileError_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(853);
const compile = referenceTokens => {
if (!Array.isArray(referenceTokens)) {
throw new TypeError('Reference tokens must be a list of strings or numbers');
}
try {
if (referenceTokens.length === 0) {
return '';
}
return `/${referenceTokens.map(referenceToken => {
if (typeof referenceToken !== 'string' && typeof referenceToken !== 'number') {
throw new TypeError('Reference token must be a string or number');
}
return (0,_escape_js__WEBPACK_IMPORTED_MODULE_1__["default"])(String(referenceToken));
}).join('/')}`;
} catch (error) {
throw new _errors_JSONPointerCompileError_js__WEBPACK_IMPORTED_MODULE_0__["default"]('Unexpected error during JSON Pointer compilation', {
cause: error,
referenceTokens
});
}
};
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (compile);
/***/ }),
/***/ 71:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _JSONPointerEvaluateError_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(369);
class JSONPointerKeyError extends _JSONPointerEvaluateError_js__WEBPACK_IMPORTED_MODULE_0__["default"] {}
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (JSONPointerKeyError);
/***/ }),
/***/ 133:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _errors_JSONPointerError_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(628);
class EvaluationRealm {
name = '';
isArray(node) {
throw new _errors_JSONPointerError_js__WEBPACK_IMPORTED_MODULE_0__["default"]('Realm.isArray(node) must be implemented in a subclass');
}
isObject(node) {
throw new _errors_JSONPointerError_js__WEBPACK_IMPORTED_MODULE_0__["default"]('Realm.isObject(node) must be implemented in a subclass');
}
sizeOf(node) {
throw new _errors_JSONPointerError_js__WEBPACK_IMPORTED_MODULE_0__["default"]('Realm.sizeOf(node) must be implemented in a subclass');
}
has(node, referenceToken) {
throw new _errors_JSONPointerError_js__WEBPACK_IMPORTED_MODULE_0__["default"]('Realm.has(node) must be implemented in a subclass');
}
evaluate(node, referenceToken) {
throw new _errors_JSONPointerError_js__WEBPACK_IMPORTED_MODULE_0__["default"]('Realm.evaluate(node) must be implemented in a subclass');
}
}
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (EvaluationRealm);
/***/ }),
/***/ 142:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _CSTTranslator_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(979);
/* harmony import */ var _unescape_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(415);
class ASTTranslator extends _CSTTranslator_js__WEBPACK_IMPORTED_MODULE_0__["default"] {
getTree() {
const {
root
} = super.getTree();
return root.children.filter(({
type
}) => type === 'reference-token').map(({
text
}) => (0,_unescape_js__WEBPACK_IMPORTED_MODULE_1__["default"])(text));
}
}
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (ASTTranslator);
/***/ }),
/***/ 348:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var apg_lite__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(646);
/* harmony import */ var _grammar_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(678);
const grammar = new _grammar_js__WEBPACK_IMPORTED_MODULE_1__["default"]();
const parser = new apg_lite__WEBPACK_IMPORTED_MODULE_0__.Parser();
const testArrayIndex = referenceToken => {
if (typeof referenceToken !== 'string') return false;
try {
return parser.parse(grammar, 'array-index', referenceToken).success;
} catch {
return false;
}
};
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (testArrayIndex);
/***/ }),
/***/ 353:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
class TraceBuilder {
#trace;
#path;
#realm;
constructor(trace, context = {}) {
this.#trace = trace;
this.#trace.steps = [];
this.#trace.failed = false;
this.#trace.failedAt = -1;
this.#trace.message = `JSON Pointer "${context.jsonPointer}" was successfully evaluated against the provided value`;
this.#trace.context = {
...context,
realm: context.realm.name
};
this.#path = [];
this.#realm = context.realm;
}
step({
referenceToken,
input,
output,
success = true,
reason
}) {
const position = this.#path.length;
this.#path.push(referenceToken);
const step = {
referenceToken,
referenceTokenPosition: position,
input,
inputType: this.#realm.isObject(input) ? 'object' : this.#realm.isArray(input) ? 'array' : 'unrecognized',
output,
success
};
if (reason) {
step.reason = reason;
}
this.#trace.steps.push(step);
if (!success) {
this.#trace.failed = true;
this.#trace.failedAt = position;
this.#trace.message = reason;
}
}
}
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (TraceBuilder);
/***/ }),
/***/ 369:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _JSONPointerError_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(628);
class JSONPointerEvaluateError extends _JSONPointerError_js__WEBPACK_IMPORTED_MODULE_0__["default"] {}
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (JSONPointerEvaluateError);
/***/ }),
/***/ 415:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
const unescape = referenceToken => {
if (typeof referenceToken !== 'string') {
throw new TypeError('Reference token must be a string');
}
return referenceToken.replace(/~1/g, '/').replace(/~0/g, '~');
};
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (unescape);
/***/ }),
/***/ 427:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _parse_index_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(583);
/* harmony import */ var _test_array_dash_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(922);
/* harmony import */ var _test_array_index_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(348);
/* harmony import */ var _trace_TraceBuilder_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(353);
/* harmony import */ var _realms_json_index_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(839);
/* harmony import */ var _errors_JSONPointerEvaluateError_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(369);
/* harmony import */ var _errors_JSONPointerTypeError_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(932);
/* harmony import */ var _errors_JSONPointerIndexError_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(874);
/* harmony import */ var _errors_JSONPointerKeyError_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(71);
const evaluate = (value, jsonPointer, {
strictArrays = true,
strictObjects = true,
realm = new _realms_json_index_js__WEBPACK_IMPORTED_MODULE_3__["default"](),
trace = true
} = {}) => {
const {
result: parseResult,
tree: referenceTokens,
trace: parseTrace
} = (0,_parse_index_js__WEBPACK_IMPORTED_MODULE_0__["default"])(jsonPointer, {
trace: !!trace
});
const tracer = typeof trace === 'object' && trace !== null ? new _trace_TraceBuilder_js__WEBPACK_IMPORTED_MODULE_8__["default"](trace, {
jsonPointer,
referenceTokens,
strictArrays,
strictObjects,
realm,
value
}) : null;
try {
let output;
if (!parseResult.success) {
let message = `Invalid JSON Pointer: "${jsonPointer}". Syntax error at position ${parseResult.maxMatched}`;
message += parseTrace ? `, expected ${parseTrace.inferExpectations()}` : '';
throw new _errors_JSONPointerEvaluateError_js__WEBPACK_IMPORTED_MODULE_4__["default"](message, {
jsonPointer,
currentValue: value,
realm: realm.name
});
}
return referenceTokens.reduce((current, referenceToken, referenceTokenPosition) => {
if (realm.isArray(current)) {
if ((0,_test_array_dash_js__WEBPACK_IMPORTED_MODULE_1__["default"])(referenceToken)) {
if (strictArrays) {
throw new _errors_JSONPointerIndexError_js__WEBPACK_IMPORTED_MODULE_6__["default"](`Invalid array index "-" at position ${referenceTokenPosition} in "${jsonPointer}". The "-" token always refers to a nonexistent element during evaluation`, {
jsonPointer,
referenceTokens,
referenceToken,
referenceTokenPosition,
currentValue: current,
realm: realm.name
});
} else {
output = realm.evaluate(current, String(realm.sizeOf(current)));
tracer?.step({
referenceToken,
input: current,
output
});
return output;
}
}
if (!(0,_test_array_index_js__WEBPACK_IMPORTED_MODULE_2__["default"])(referenceToken)) {
throw new _errors_JSONPointerIndexError_js__WEBPACK_IMPORTED_MODULE_6__["default"](`Invalid array index "${referenceToken}" at position ${referenceTokenPosition} in "${jsonPointer}": index MUST be "0", or digits without a leading "0"`, {
jsonPointer,
referenceTokens,
referenceToken,
referenceTokenPosition,
currentValue: current,
realm: realm.name
});
}
const index = Number(referenceToken);
if (!Number.isSafeInteger(index)) {
throw new _errors_JSONPointerIndexError_js__WEBPACK_IMPORTED_MODULE_6__["default"](`Invalid array index "${referenceToken}" at position ${referenceTokenPosition} in "${jsonPointer}": index must be a safe integer`, {
jsonPointer,
referenceTokens,
referenceToken,
referenceTokenPosition,
currentValue: current,
realm: realm.name
});
}
if (!realm.has(current, referenceToken) && strictArrays) {
throw new _errors_JSONPointerIndexError_js__WEBPACK_IMPORTED_MODULE_6__["default"](`Invalid array index "${referenceToken}" at position ${referenceTokenPosition} in "${jsonPointer}": index not found in array`, {
jsonPointer,
referenceTokens,
referenceToken,
referenceTokenPosition,
currentValue: current,
realm: realm.name
});
}
output = realm.evaluate(current, referenceToken);
tracer?.step({
referenceToken,
input: current,
output
});
return output;
}
if (realm.isObject(current)) {
if (!realm.has(current, referenceToken) && strictObjects) {
throw new _errors_JSONPointerKeyError_js__WEBPACK_IMPORTED_MODULE_7__["default"](`Invalid object key "${referenceToken}" at position ${referenceTokenPosition} in "${jsonPointer}": key not found in object`, {
jsonPointer,
referenceTokens,
referenceToken,
referenceTokenPosition,
currentValue: current,
realm: realm.name
});
}
output = realm.evaluate(current, referenceToken);
tracer?.step({
referenceToken,
input: current,
output
});
return output;
}
throw new _errors_JSONPointerTypeError_js__WEBPACK_IMPORTED_MODULE_5__["default"](`Invalid reference token "${referenceToken}" at position ${referenceTokenPosition} in "${jsonPointer}": cannot be applied to a non-object/non-array value`, {
jsonPointer,
referenceTokens,
referenceToken,
referenceTokenPosition,
currentValue: current,
realm: realm.name
});
}, value);
} catch (error) {
tracer?.step({
referenceToken: error.referenceToken,
input: error.currentValue,
success: false,
reason: error.message
});
if (error instanceof _errors_JSONPointerEvaluateError_js__WEBPACK_IMPORTED_MODULE_4__["default"]) {
throw error;
}
throw new _errors_JSONPointerEvaluateError_js__WEBPACK_IMPORTED_MODULE_4__["default"]('Unexpected error during JSON Pointer evaluation', {
cause: error,
jsonPointer,
referenceTokens
});
}
};
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (evaluate);
/***/ }),
/***/ 525:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var apg_lite__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(646);
/* harmony import */ var _grammar_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(678);
const grammar = new _grammar_js__WEBPACK_IMPORTED_MODULE_1__["default"]();
const parser = new apg_lite__WEBPACK_IMPORTED_MODULE_0__.Parser();
const testArrayLocation = referenceToken => {
if (typeof referenceToken !== 'string') return false;
try {
return parser.parse(grammar, 'array-location', referenceToken).success;
} catch {
return false;
}
};
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (testArrayLocation);
/***/ }),
/***/ 533:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var apg_lite__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(646);
/* harmony import */ var _grammar_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(678);
const grammar = new _grammar_js__WEBPACK_IMPORTED_MODULE_1__["default"]();
const parser = new apg_lite__WEBPACK_IMPORTED_MODULE_0__.Parser();
const testReferenceToken = referenceToken => {
if (typeof referenceToken !== 'string') return false;
try {
return parser.parse(grammar, 'reference-token', referenceToken).success;
} catch {
return false;
}
};
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (testReferenceToken);
/***/ }),
/***/ 544:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _parse_index_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(583);
const testJSONPointer = jsonPointer => {
try {
const parseResult = (0,_parse_index_js__WEBPACK_IMPORTED_MODULE_0__["default"])(jsonPointer, {
translator: null
});
return parseResult.result.success;
} catch {
return false;
}
};
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (testJSONPointer);
/***/ }),
/***/ 583:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var apg_lite__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(646);
/* harmony import */ var _grammar_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(678);
/* harmony import */ var _errors_JSONPointerParseError_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(819);
/* harmony import */ var _translators_ASTTranslator_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(142);
/* harmony import */ var _trace_Trace_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(992);
const grammar = new _grammar_js__WEBPACK_IMPORTED_MODULE_4__["default"]();
const parse = (jsonPointer, {
translator = new _translators_ASTTranslator_js__WEBPACK_IMPORTED_MODULE_2__["default"](),
stats = false,
trace = false
} = {}) => {
if (typeof jsonPointer !== 'string') {
throw new TypeError('JSON Pointer must be a string');
}
try {
const parser = new apg_lite__WEBPACK_IMPORTED_MODULE_0__.Parser();
if (translator) parser.ast = translator;
if (stats) parser.stats = new apg_lite__WEBPACK_IMPORTED_MODULE_0__.Stats();
if (trace) parser.trace = new _trace_Trace_js__WEBPACK_IMPORTED_MODULE_3__["default"]();
const result = parser.parse(grammar, 'json-pointer', jsonPointer);
return {
result,
tree: result.success && translator ? parser.ast.getTree() : undefined,
stats: parser.stats,
trace: parser.trace
};
} catch (error) {
throw new _errors_JSONPointerParseError_js__WEBPACK_IMPORTED_MODULE_1__["default"]('Unexpected error during JSON Pointer parsing', {
cause: error,
jsonPointer
});
}
};
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (parse);
/***/ }),
/***/ 628:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
class JSONPointerError extends Error {
constructor(message, options = undefined) {
super(message, options);
this.name = this.constructor.name;
if (typeof message === 'string') {
this.message = message;
}
if (typeof Error.captureStackTrace === 'function') {
Error.captureStackTrace(this, this.constructor);
} else {
this.stack = new Error(message).stack;
}
/**
* This needs to stay here until our minimum supported version of Node.js is >= 16.9.0.
* Node.js is >= 16.9.0 supports error causes natively.
*/
if (options != null && typeof options === 'object' && Object.prototype.hasOwnProperty.call(options, 'cause') && !('cause' in this)) {
const {
cause
} = options;
this.cause = cause;
if (cause instanceof Error && 'stack' in cause) {
this.stack = `${this.stack}\nCAUSE: ${cause.stack}`;
}
}
/**
* Allows to assign arbitrary properties to the error object.
*/
if (options != null && typeof options === 'object') {
const {
cause,
...causelessOptions
} = options;
Object.assign(this, causelessOptions);
}
}
}
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (JSONPointerError);
/***/ }),
/***/ 632:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _EvaluationRealm_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(133);
/* harmony import */ var _errors_JSONPointerEvaluateError_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(369);
class CompositeEvaluationRealm extends _EvaluationRealm_js__WEBPACK_IMPORTED_MODULE_0__["default"] {
name = 'composite';
realms = [];
constructor(realms) {
super();
this.realms = realms;
}
isArray(node) {
return this.#findRealm(node).isArray(node);
}
isObject(node) {
return this.#findRealm(node).isObject(node);
}
sizeOf(node) {
return this.#findRealm(node).sizeOf(node);
}
has(node, referenceToken) {
return this.#findRealm(node).has(node, referenceToken);
}
evaluate(node, referenceToken) {
return this.#findRealm(node).evaluate(node, referenceToken);
}
#findRealm(node) {
for (const realm of this.realms) {
if (realm.isArray(node) || realm.isObject(node)) {
return realm;
}
}
throw new _errors_JSONPointerEvaluateError_js__WEBPACK_IMPORTED_MODULE_1__["default"]('No suitable evaluation realm found for value', {
currentValue: node
});
}
}
const compose = (...realms) => new CompositeEvaluationRealm(realms);
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (compose);
/***/ }),
/***/ 646:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Ast: () => (/* binding */ Ast),
/* harmony export */ Parser: () => (/* binding */ Parser),
/* harmony export */ Stats: () => (/* binding */ Stats),
/* harmony export */ Trace: () => (/* binding */ Trace),
/* harmony export */ identifiers: () => (/* binding */ identifiers),
/* harmony export */ utilities: () => (/* binding */ utilities)
/* harmony export */ });
/* *************************************************************************************
* copyright: Copyright (c) 2023 Lowell D. Thomas, all rights reserved
* license: BSD-2-Clause (https://opensource.org/licenses/BSD-2-Clause)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ********************************************************************************* */
const Parser = function fnparser() {
const id = identifiers;
const utils = utilities;
const p = this;
const thisFileName = 'parser.js: Parser(): ';
const systemData = function systemData() {
this.state = id.ACTIVE;
this.phraseLength = 0;
this.refresh = () => {
this.state = id.ACTIVE;
this.phraseLength = 0;
};
};
p.ast = undefined;
p.stats = undefined;
p.trace = undefined;
p.callbacks = [];
let lookAhead = 0;
let treeDepth = 0;
let maxTreeDepth = 0;
let nodeHits = 0;
let maxMatched = 0;
let rules = undefined;
let udts = undefined;
let opcodes = undefined;
let chars = undefined;
let sysData = new systemData();
let ruleCallbacks = undefined;
let udtCallbacks = undefined;
let userData = undefined;
const clear = () => {
lookAhead = 0;
treeDepth = 0;
maxTreeDepth = 0;
nodeHits = 0;
maxMatched = 0;
rules = undefined;
udts = undefined;
opcodes = undefined;
chars = undefined;
sysData.refresh();
ruleCallbacks = undefined;
udtCallbacks = undefined;
userData = undefined;
};
const initializeCallbacks = () => {
const functionName = `${thisFileName}initializeCallbacks(): `;
let i;
ruleCallbacks = [];
udtCallbacks = [];
for (i = 0; i < rules.length; i += 1) {
ruleCallbacks[i] = undefined;
}
for (i = 0; i < udts.length; i += 1) {
udtCallbacks[i] = undefined;
}
let func;
const list = [];
for (i = 0; i < rules.length; i += 1) {
list.push(rules[i].lower);
}
for (i = 0; i < udts.length; i += 1) {
list.push(udts[i].lower);
}
for (const index in p.callbacks) {
if (p.callbacks.hasOwnProperty(index)) {
i = list.indexOf(index.toLowerCase());
if (i < 0) {
throw new Error(`${functionName}syntax callback '${index}' not a rule or udt name`);
}
func = p.callbacks[index] ? p.callbacks[index] : undefined;
if (typeof func === 'function' || func === undefined) {
if (i < rules.length) {
ruleCallbacks[i] = func;
} else {
udtCallbacks[i - rules.length] = func;
}
} else {
throw new Error(`${functionName}syntax callback[${index}] must be function reference or falsy)`);
}
}
}
};
p.parse = (grammar, startName, inputString, callbackData) => {
const functionName = `${thisFileName}parse(): `;
clear();
chars = utils.stringToChars(inputString);
rules = grammar.rules;
udts = grammar.udts;
const lower = startName.toLowerCase();
let startIndex = undefined;
for (const i in rules) {
if (rules.hasOwnProperty(i)) {
if (lower === rules[i].lower) {
startIndex = rules[i].index;
break;
}
}
}
if (startIndex === undefined) {
throw new Error(`${functionName}start rule name '${startRule}' not recognized`);
}
initializeCallbacks();
if (p.trace) {
p.trace.init(rules, udts, chars);
}
if (p.stats) {
p.stats.init(rules, udts);
}
if (p.ast) {
p.ast.init(rules, udts, chars);
}
userData = callbackData;
/* create a dummy opcode for the start rule */
opcodes = [
{
type: id.RNM,
index: startIndex,
},
];
/* execute the start rule */
opExecute(0, 0);
opcodes = undefined;
/* test and return the sysData */
let success = false;
switch (sysData.state) {
case id.ACTIVE:
throw new Error(`${functionName}final state should never be 'ACTIVE'`);
case id.NOMATCH:
success = false;
break;
case id.EMPTY:
case id.MATCH:
if (sysData.phraseLength === chars.length) {
success = true;
} else {
success = false;
}
break;
default:
throw new Error('unrecognized state');
}
return {
success,
state: sysData.state,
stateName: id.idName(sysData.state),
length: chars.length,
matched: sysData.phraseLength,
maxMatched,
maxTreeDepth,
nodeHits,
};
};
// The `ALT` operator.<br>
// Executes its child nodes, from left to right, until it finds a match.
// Fails if *all* of its child nodes fail.
const opALT = (opIndex, phraseIndex) => {
const op = opcodes[opIndex];
for (let i = 0; i < op.children.length; i += 1) {
opExecute(op.children[i], phraseIndex);
if (sysData.state !== id.NOMATCH) {
break;
}
}
};
// The `CAT` operator.<br>
// Executes all of its child nodes, from left to right,
// concatenating the matched phrases.
// Fails if *any* child nodes fail.
const opCAT = (opIndex, phraseIndex) => {
let success;
let astLength;
let catCharIndex;
let catPhrase;
const op = opcodes[opIndex];
if (p.ast) {
astLength = p.ast.getLength();
}
success = true;
catCharIndex = phraseIndex;
catPhrase = 0;
for (let i = 0; i < op.children.length; i += 1) {
opExecute(op.children[i], catCharIndex);
if (sysData.state === id.NOMATCH) {
success = false;
break;
} else {
catCharIndex += sysData.phraseLength;
catPhrase += sysData.phraseLength;
}
}
if (success) {
sysData.state = catPhrase === 0 ? id.EMPTY : id.MATCH;
sysData.phraseLength = catPhrase;
} else {
sysData.state = id.NOMATCH;
sysData.phraseLength = 0;
if (p.ast) {
p.ast.setLength(astLength);
}
}
};
// The `REP` operator.<br>
// Repeatedly executes its single child node,
// concatenating each of the matched phrases found.
// The number of repetitions executed and its final sysData depends
// on its `min` & `max` repetition values.
const opREP = (opIndex, phraseIndex) => {
let astLength;
let repCharIndex;
let repPhrase;
let repCount;
const op = opcodes[opIndex];
if (op.max === 0) {
// this is an empty-string acceptor
// deprecated: use the TLS empty string operator, "", instead
sysData.state = id.EMPTY;
sysData.phraseLength = 0;
return;
}
repCharIndex = phraseIndex;
repPhrase = 0;
repCount = 0;
if (p.ast) {
astLength = p.ast.getLength();
}
while (1) {
if (repCharIndex >= chars.length) {
/* exit on end of input string */
break;
}
opExecute(opIndex + 1, repCharIndex);
if (sysData.state === id.NOMATCH) {
/* always end if the child node fails */
break;
}
if (sysData.state === id.EMPTY) {
/* REP always succeeds when the child node returns an empty phrase */
/* this may not seem obvious, but that's the way it works out */
break;
}
repCount += 1;
repPhrase += sysData.phraseLength;
repCharIndex += sysData.phraseLength;
if (repCount === op.max) {
/* end on maxed out reps */
break;
}
}
/* evaluate the match count according to the min, max values */
if (sysData.state === id.EMPTY) {
sysData.state = repPhrase === 0 ? id.EMPTY : id.MATCH;
sysData.phraseLength = repPhrase;
} else if (repCount >= op.min) {
sysData.state = repPhrase === 0 ? id.EMPTY : id.MATCH;
sysData.phraseLength = repPhrase;
} else {
sysData.state = id.NOMATCH;
sysData.phraseLength = 0;
if (p.ast) {
p.ast.setLength(astLength);
}
}
};
// Validate the callback function's returned sysData values.
// It's the user's responsibility to get them right
// but `RNM` fails if not.
const validateRnmCallbackResult = (rule, sysData, charsLeft, down) => {
if (sysData.phraseLength > charsLeft) {
let str = `${thisFileName}opRNM(${rule.name}): callback function error: `;
str += `sysData.phraseLength: ${sysData.phraseLength}`;
str += ` must be <= remaining chars: ${charsLeft}`;
throw new Error(str);
}
switch (sysData.state) {
case id.ACTIVE:
if (!down) {
throw new Error(
`${thisFileName}opRNM(${rule.name}): callback function return error. ACTIVE state not allowed.`
);
}
break;
case id.EMPTY:
sysData.phraseLength = 0;
break;
case id.MATCH:
if (sysData.phraseLength === 0) {
sysData.state = id.EMPTY;
}
break;
case id.NOMATCH:
sysData.phraseLength = 0;
break;
default:
throw new Error(
`${thisFileName}opRNM(${rule.name}): callback function return error. Unrecognized return state: ${sysData.state}`
);
}
};
// The `RNM` operator.<br>
// This operator will acts as a root node for a parse tree branch below and
// returns the matched phrase to its parent.
// However, its larger responsibility is handling user-defined callback functions and `AST` nodes.
// Note that the `AST` is a separate object, but `RNM` calls its functions to create its nodes.
const opRNM = (opIndex, phraseIndex) => {
let astLength;
let astDefined;
let savedOpcodes;
const op = opcodes[opIndex];
const rule = rules[op.index];
const callback = ruleCallbacks[rule.index];
/* ignore AST in look ahead (AND or NOT operator above) */
if (!lookAhead) {
astDefined = p.ast && p.ast.ruleDefined(op.index);
if (astDefined) {
astLength = p.ast.getLength();
p.ast.down(op.index, rules[op.index].name);
}
}
if (callback) {
/* call user's callback going down the parse tree*/
const charsLeft = chars.length - phraseIndex;
callback(sysData, chars, phraseIndex, userData);
validateRnmCallbackResult(rule, sysData, charsLeft, true);
if (sysData.state === id.ACTIVE) {
savedOpcodes = opcodes;
opcodes = rule.opcodes;
opExecute(0, phraseIndex);
opcodes = savedOpcodes;
/* call user's callback going up the parse tree*/
callback(sysData, chars, phraseIndex, userData);
validateRnmCallbackResult(rule, sysData, charsLeft, false);
} /* implied else clause: just accept the callback sysData - RNM acting as UDT */
} else {
/* no callback - just execute the rule */
savedOpcodes = opcodes;
opcodes = rule.opcodes;
opExecute(0, phraseIndex, sysData);
opcodes = savedOpcodes;
}
if (!lookAhead) {
/* end AST */
if (astDefined) {
if (sysData.state === id.NOMATCH) {
p.ast.setLength(astLength);
} else {
p.ast.up(op.index, rule.name, phraseIndex, sysData.phraseLength);
}
}
}
};
// The `TRG` operator.<br>
// Succeeds if the single first character of the phrase is
// within the `min - max` range.
const opTRG = (opIndex, phraseIndex) => {
const op = opcodes[opIndex];
sysData.state = id.NOMATCH;
if (phraseIndex < chars.length) {
if (op.min <= chars[phraseIndex] && chars[phraseIndex] <= op.max) {
sysData.state = id.MATCH;
sysData.phraseLength = 1;
}
}
};
// The `TBS` operator.<br>
// Matches its pre-defined phrase against the input string.
// All characters must match exactly.
// Case-sensitive literal strings (`'string'` & `%s"string"`) are translated to `TBS`
// operators by `apg`.
// Phrase length of zero is not allowed.
// Empty phrases can only be defined with `TLS` operators.
const opTBS = (opIndex, phraseIndex) => {
const op = opcodes[opIndex];
const len = op.string.length;
sysData.state = id.NOMATCH;
if (phraseIndex + len <= chars.length) {
for (let i = 0; i < len; i += 1) {
if (chars[phraseIndex + i] !== op.string[i]) {
return;
}
}
sysData.state = id.MATCH;
sysData.phraseLength = len;
} /* implied else NOMATCH */
};
// The `TLS` operator.<br>
// Matches its pre-defined phrase against the input string.
// A case-insensitive match is attempted for ASCII alphbetical characters.
// `TLS` is the only operator that explicitly allows empty phrases.
// `apg` will fail for empty `TBS`, case-sensitive strings (`''`) or
// zero repetitions (`0*0RuleName` or `0RuleName`).
const opTLS = (opIndex, phraseIndex) => {
let code;
const op = opcodes[opIndex];
sysData.state = id.NOMATCH;
const len = op.string.length;
if (len === 0) {
/* EMPTY match allowed for TLS */
sysData.state = id.EMPTY;
return;
}
if (phraseIndex + len <= chars.length) {
for (let i = 0; i < len; i += 1) {
code = chars[phraseIndex + i];
if (code >= 65 && code <= 90) {
code += 32;
}
if (code !== op.string[i]) {
return;
}
}
sysData.state = id.MATCH;
sysData.phraseLength = len;
} /* implied else NOMATCH */
};
// Validate the callback function's returned sysData values.
// It's the user's responsibility to get it right but `UDT` fails if not.
const validateUdtCallbackResult = (udt, sysData, charsLeft) => {
if (sysData.phraseLength > charsLeft) {
let str = `${thisFileName}opUDT(${udt.name}): callback function error: `;
str += `sysData.phraseLength: ${sysData.phraseLength}`;
str += ` must be <= remaining chars: ${charsLeft}`;
throw new Error(str);
}
switch (sysData.state) {
case id.ACTIVE:
throw new Error(`${thisFileName}opUDT(${udt.name}) ACTIVE state return not allowed.`);
case id.EMPTY:
if (udt.empty) {
sysData.phraseLength = 0;
} else {
throw new Error(`${thisFileName}opUDT(${udt.name}) may not return EMPTY.`);
}
break;
case id.MATCH:
if (sysData.phraseLength === 0) {
if (udt.empty) {
sysData.state = id.EMPTY;
} else {
throw new Error(`${thisFileName}opUDT(${udt.name}) may not return EMPTY.`);
}
}
break;
case id.NOMATCH:
sysData.phraseLength = 0;
break;
default:
throw new Error(
`${thisFileName}opUDT(${udt.name}): callback function return error. Unrecognized return state: ${sysData.state}`
);
}
};
// The `UDT` operator.<br>
// Simply calls the user's callback function, but operates like `RNM` with regard to the `AST`
// and back referencing.
// There is some ambiguity here. `UDT`s act as terminals for phrase recognition but as named rules
// for `AST` nodes and back referencing.
// See [`ast.js`](./ast.html) for usage.
const opUDT = (opIndex, phraseIndex) => {
let astLength;
let astIndex;
let astDefined;
const op = opcodes[opIndex];
const udt = udts[op.index];
sysData.UdtIndex = udt.index;
/* ignore AST in look ahead */
if (!lookAhead) {
astDefined = p.ast && p.ast.udtDefined(op.index);
if (astDefined) {
astIndex = rules.length + op.index;
astLength = p.ast.getLength();
p.ast.down(astIndex, udt.name);
}
}
/* call the UDT */
const charsLeft = chars.length - phraseIndex;
udtCallbacks[op.index](sysData, chars, phraseIndex, userData);
validateUdtCallbackResult(udt, sysData, charsLeft);
if (!lookAhead) {
/* end AST */
if (astDefined) {
if (sysData.state === id.NOMATCH) {
p.ast.setLength(astLength);
} else {
p.ast.up(astIndex, udt.name, phraseIndex, sysData.phraseLength);
}
}
}
};
// The `AND` operator.<br>
// This is the positive `look ahead` operator.
// Executes its single child node, returning the EMPTY state
// if it succeedsand NOMATCH if it fails.
// *Always* backtracks on any matched phrase and returns EMPTY on success.
const opAND = (opIndex, phraseIndex) => {
lookAhead += 1;
opExecute(opIndex + 1, phraseIndex);
lookAhead -= 1;
sysData.phraseLength = 0;
switch (sysData.state) {
case id.EMPTY:
sysData.state = id.EMPTY;
break;
case id.MATCH:
sysData.state = id.EMPTY;
break;
case id.NOMATCH:
sysData.state = id.NOMATCH;
break;
default:
throw new Error(`opAND: invalid state ${sysData.state}`);
}
};
// The `NOT` operator.<br>
// This is the negative `look ahead` operator.
// Executes its single child node, returning the EMPTY state
// if it *fails* and NOMATCH if it succeeds.
// *Always* backtracks on any matched phrase and returns EMPTY
// on success (failure of its child node).
const opNOT = (opIndex, phraseIndex) => {
lookAhead += 1;
opExecute(opIndex + 1, phraseIndex);
lookAhead -= 1;
sysData.phraseLength = 0;
switch (sysData.state) {
case id.EMPTY:
case id.MATCH:
sysData.state = id.NOMATCH;
break;
case id.NOMATCH:
sysData.state = id.EMPTY;
break;
default:
throw new Error(`opNOT: invalid state ${sysData.state}`);
}
};
const opExecute = (opIndex, phraseIndex) => {
const functionName = `${thisFileName}opExecute(): `;
const op = opcodes[opIndex];
nodeHits += 1;
if (treeDepth > maxTreeDepth) {
maxTreeDepth = treeDepth;
}
treeDepth += 1;
sysData.refresh();
if (p.trace) {
p.trace.down(op, phraseIndex);
}
switch (op.type) {
case id.ALT:
opALT(opIndex, phraseIndex);
break;
case id.CAT:
opCAT(opIndex, phraseIndex);
break;
case id.REP:
opREP(opIndex, phraseIndex);
break;
case id.RNM:
opRNM(opIndex, phraseIndex);
break;
case id.TRG:
opTRG(opIndex, phraseIndex);
break;
case id.TBS:
opTBS(opIndex, phraseIndex);
break;
case id.TLS:
opTLS(opIndex, phraseIndex);
break;
case id.UDT:
opUDT(opIndex, phraseIndex);
break;
case id.AND:
opAND(opIndex, phraseIndex);
break;
case id.NOT:
opNOT(opIndex, phraseIndex);
break;
default:
throw new Error(`${functionName}unrecognized operator`);
}
if (!lookAhead) {
if (phraseIndex + sysData.phraseLength > maxMatched) {
maxMatched = phraseIndex + sysData.phraseLength;
}
}
if (p.stats) {
p.stats.collect(op, sysData);
}
if (p.trace) {
p.trace.up(op, sysData.state, phraseIndex, sysData.phraseLength);
}
treeDepth -= 1;
};
};
const Ast = function fnast() {
const thisFileName = 'parser.js: Ast()): ';
const id = identifiers;
const utils = utilities;
const a = this;
let rules = undefined;
let udts = undefined;
let chars = undefined;
let nodeCount = 0;
const nodeCallbacks = [];
const stack = [];
const records = [];
a.callbacks = [];
/* called by the parser to initialize the AST with the rules, UDTs and the input characters */
a.init = (rulesIn, udtsIn, charsIn) => {
stack.length = 0;
records.length = 0;
nodeCount = 0;
rules = rulesIn;
udts = udtsIn;
chars = charsIn;
let i;
const list = [];
for (i = 0; i < rules.length; i += 1) {
list.push(rules[i].lower);
}
for (i = 0; i < udts.length; i += 1) {
list.push(udts[i].lower);
}
nodeCount = rules.length + udts.length;
for (i = 0; i < nodeCount; i += 1) {
nodeCallbacks[i] = undefined;
}
for (const index in a.callbacks) {
if (a.callbacks.hasOwnProperty(index)) {
const lower = index.toLowerCase();
i = list.indexOf(lower);
if (i < 0) {
throw new Error(`${thisFileName}init: node '${index}' not a rule or udt name`);
}
nodeCallbacks[i] = a.callbacks[index];
}
}
};
/* AST node rule callbacks - called by the parser's `RNM` operator */
a.ruleDefined = (index) => !!nodeCallbacks[index];
/* AST node UDT callbacks - called by the parser's `UDT` operator */
a.udtDefined = (index) => !!nodeCallbacks[rules.length + index];
/* called by the parser's `RNM` & `UDT` operators
builds a record for the downward traversal of the node */
a.down = (callbackIndex, name) => {
const thisIndex = records.length;
stack.push(thisIndex);
records.push({
name,
thisIndex,
thatIndex: undefined,
state: id.SEM_PRE,
callbackIndex,
phraseIndex: undefined,
phraseLength: undefined,
stack: stack.length,
});
return thisIndex;
};
/* called by the parser's `RNM` & `UDT` operators */
/* builds a record for the upward traversal of the node */
a.up = (callbackIndex, name, phraseIndex, phraseLength) => {
const thisIndex = records.length;
const thatIndex = stack.pop();
records.push({
name,
thisIndex,
thatIndex,
state: id.SEM_POST,
callbackIndex,
phraseIndex,
phraseLength,
stack: stack.length,
});
records[thatIndex].thatIndex = thisIndex;
records[thatIndex].phraseIndex = phraseIndex;
records[thatIndex].phraseLength = phraseLength;
return thisIndex;
};
// Called by the user to translate the AST.
// Translate means to associate or apply some semantic action to the
// phrases that were syntactically matched to the AST nodes according
// to the defining grammar.
// ```
// data - optional user-defined data
// passed to the callback functions by the translator
// ```
a.translate = (data) => {
let ret;
let callback;
let record;
for (let i = 0; i < records.length; i += 1) {
record = records[i];
callback = nodeCallbacks[record.callbackIndex];
if (callback) {
if (record.state === id.SEM_PRE) {
callback(id.SEM_PRE, chars, record.phraseIndex, record.phraseLength, data);
} else if (callback) {
callback(id.SEM_POST, chars, record.phraseIndex, record.phraseLength, data);
}
}
}
};
/* called by the parser to reset the length of the records array */
/* necessary on backtracking */
a.setLength = (length) => {
records.length = length;
if (length > 0) {
stack.length = records[length - 1].stack;
} else {
stack.length = 0;
}
};
/* called by the parser to get the length of the records array */
a.getLength = () => records.length;
/* helper for XML display */
function indent(n) {
let ret = '';
while (n-- > 0) {
ret += ' ';
}
return ret;
}
// Generate an `XML` version of the AST.
// Useful if you want to use a special or favorite XML parser to translate the
// AST. Node data are JavaScript strings.
a.toXml = () => {
let xml = '';
let depth = 0;
xml += '<?xml version="1.0" encoding="utf-8"?>\n';
xml += `<root nodes="${records.length / 2}" characters="${chars.length}">\n`;
xml += `<!-- input string -->\n`;
xml += indent(depth + 2);
xml += utils.charsToString(chars);
xml += '\n';
records.forEach((rec) => {
if (rec.state === id.SEM_PRE) {
depth += 1;
xml += indent(depth);
xml += `<node name="${rec.name}" index="${rec.phraseIndex}" length="${rec.phraseLength}">\n`;
xml += indent(depth + 2);
xml += utils.charsToString(chars, rec.phraseIndex, rec.phraseLength);
xml += '\n';
} else {
xml += indent(depth);
xml += `</node><!-- name="${rec.name}" -->\n`;
depth -= 1;
}
});
xml += '</root>\n';
return xml;
};
};
const Trace = function fntrace() {
const id = identifiers;
const utils = utilities;
const thisFile = 'parser.js: Trace(): ';
let chars = undefined;
let rules = undefined;
let udts = undefined;
let out = '';
let treeDepth = 0;
const MAX_PHRASE = 100;
const t = this;
const indent = (n) => {
let ret = '';
let count = 0;
if (n >= 0) {
while (n--) {
count += 1;
if (count === 5) {
ret += '|';
count = 0;
} else {
ret += '.';
}
}
}
return ret;
};
t.init = (r, u, c) => {
rules = r;
udts = u;
chars = c;
};
const opName = (op) => {
let name;
switch (op.type) {
case id.ALT:
name = 'ALT';
break;
case id.CAT:
name = 'CAT';
break;
case id.REP:
if (op.