-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy path2941.a87e9af4.iframe.bundle.js.map
1 lines (1 loc) · 179 KB
/
2941.a87e9af4.iframe.bundle.js.map
1
{"version":3,"file":"2941.a87e9af4.iframe.bundle.js","mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsJA;AC8BA;;;AAWA;AAWA;AAaA;AAEA;AA+BA;AAAA;ACaA;AAMA;AAGA;;AAIA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAGA;AACA;AACA;AACA;AAAA;AA4DA;AAAA;AAOA;AAKA;AAKA;AAsBA;AAKA;AAGA;AAGA;AAGA;AAGA;AAGA;AAGA;AAGA;AAKA;AAKA;AAGA;ACxFA;AC3LA;AAiBA;AAiBA;AA2BA;ACzJA;AAIA;AAMA;AAIA;AC2lCA;AAIA;AAIA;AAIA;ACt+BA;AACA;AAAA;AAQA;AAMA;AC1FA;AAoCA;AAcA;AA0CA;AAEA;AAGA;AACA;AACA;AACA;AAAA;ACwcA;AAAA;AAWA;;;;;;;AAOA;AACA;AACA;AACA;;;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;;;;;;;;;;;;;;;;;;;AAAA;;;;AC3oBA;;AAAA;AAKA;AA4CA;AAEA;AAGA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAAA","sources":["webpack://metamask-crx/./node_modules/@spruceid/siwe-parser/dist/abnf.js","webpack://metamask-crx/./node_modules/apg-js/src/apg-api/api.js","webpack://metamask-crx/./node_modules/apg-js/src/apg-api/parser.js","webpack://metamask-crx/./node_modules/apg-js/src/apg-api/rule-attributes.js","webpack://metamask-crx/./node_modules/apg-js/src/apg-api/rule-dependencies.js","webpack://metamask-crx/./node_modules/apg-js/src/apg-api/show-rules.js","webpack://metamask-crx/./node_modules/apg-js/src/apg-conv-api/transformers.js","webpack://metamask-crx/./node_modules/apg-js/src/apg-lib/ast.js","webpack://metamask-crx/./node_modules/apg-js/src/apg-lib/stats.js","webpack://metamask-crx/./node_modules/apg-js/src/apg-lib/trace.js","webpack://metamask-crx/./node_modules/apg-js/src/apg-lib/utilities.js"],"sourcesContent":["\"use strict\";\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nvar _a;\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.ParsedMessage = void 0;\nconst api_1 = __importDefault(require(\"apg-js/src/apg-api/api\"));\nconst node_exports_1 = __importDefault(require(\"apg-js/src/apg-lib/node-exports\"));\nconst utils_1 = require(\"./utils\");\nconst GRAMMAR = `\nsign-in-with-ethereum =\n [ scheme \"://\" ] domain %s\" wants you to sign in with your Ethereum account:\" LF\n address LF\n LF\n [ statement LF ]\n LF\n %s\"URI: \" URI LF\n %s\"Version: \" version LF\n %s\"Chain ID: \" chain-id LF\n %s\"Nonce: \" nonce LF\n %s\"Issued At: \" issued-at\n [ LF %s\"Expiration Time: \" expiration-time ]\n [ LF %s\"Not Before: \" not-before ]\n [ LF %s\"Request ID: \" request-id ]\n [ LF %s\"Resources:\"\n resources ]\n\ndomain = authority\n\naddress = \"0x\" 40*40HEXDIG\n ; Must also conform to captilization\n ; checksum encoding specified in EIP-55\n ; where applicable (EOAs).\n\nstatement = 1*( reserved / unreserved / \" \" )\n ; The purpose is to exclude LF (line breaks).\n\nversion = \"1\"\n\nnonce = 8*( ALPHA / DIGIT )\n\nissued-at = date-time\nexpiration-time = date-time\nnot-before = date-time\n\nrequest-id = *pchar\n\nchain-id = 1*DIGIT\n ; See EIP-155 for valid CHAIN_IDs.\n\nresources = *( LF resource )\n\nresource = \"- \" URI\n\n; ------------------------------------------------------------------------------\n; RFC 3986\n\nURI = scheme \":\" hier-part [ \"?\" query ] [ \"#\" fragment ]\n\nhier-part = \"//\" authority path-abempty\n / path-absolute\n / path-rootless\n / path-empty\n\nscheme = ALPHA *( ALPHA / DIGIT / \"+\" / \"-\" / \".\" )\n\nauthority = [ userinfo \"@\" ] host [ \":\" port ]\nuserinfo = *( unreserved / pct-encoded / sub-delims / \":\" )\nhost = IP-literal / IPv4address / reg-name\nport = *DIGIT\n\nIP-literal = \"[\" ( IPv6address / IPvFuture ) \"]\"\n\nIPvFuture = \"v\" 1*HEXDIG \".\" 1*( unreserved / sub-delims / \":\" )\n\nIPv6address = 6( h16 \":\" ) ls32\n / \"::\" 5( h16 \":\" ) ls32\n / [ h16 ] \"::\" 4( h16 \":\" ) ls32\n / [ *1( h16 \":\" ) h16 ] \"::\" 3( h16 \":\" ) ls32\n / [ *2( h16 \":\" ) h16 ] \"::\" 2( h16 \":\" ) ls32\n / [ *3( h16 \":\" ) h16 ] \"::\" h16 \":\" ls32\n / [ *4( h16 \":\" ) h16 ] \"::\" ls32\n / [ *5( h16 \":\" ) h16 ] \"::\" h16\n / [ *6( h16 \":\" ) h16 ] \"::\"\n\nh16 = 1*4HEXDIG\nls32 = ( h16 \":\" h16 ) / IPv4address\nIPv4address = dec-octet \".\" dec-octet \".\" dec-octet \".\" dec-octet\ndec-octet = DIGIT ; 0-9\n / %x31-39 DIGIT ; 10-99\n / \"1\" 2DIGIT ; 100-199\n / \"2\" %x30-34 DIGIT ; 200-249\n / \"25\" %x30-35 ; 250-255\n\nreg-name = *( unreserved / pct-encoded / sub-delims )\n\npath-abempty = *( \"/\" segment )\npath-absolute = \"/\" [ segment-nz *( \"/\" segment ) ]\npath-rootless = segment-nz *( \"/\" segment )\npath-empty = 0pchar\n\nsegment = *pchar\nsegment-nz = 1*pchar\n\npchar = unreserved / pct-encoded / sub-delims / \":\" / \"@\"\n\nquery = *( pchar / \"/\" / \"?\" )\n\nfragment = *( pchar / \"/\" / \"?\" )\n\npct-encoded = \"%\" HEXDIG HEXDIG\n\nunreserved = ALPHA / DIGIT / \"-\" / \".\" / \"_\" / \"~\"\nreserved = gen-delims / sub-delims\ngen-delims = \":\" / \"/\" / \"?\" / \"#\" / \"[\" / \"]\" / \"@\"\nsub-delims = \"!\" / \"$\" / \"&\" / \"'\" / \"(\" / \")\"\n / \"*\" / \"+\" / \",\" / \";\" / \"=\"\n\n; ------------------------------------------------------------------------------\n; RFC 3339\n\ndate-fullyear = 4DIGIT\ndate-month = 2DIGIT ; 01-12\ndate-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on\n ; month/year\ntime-hour = 2DIGIT ; 00-23\ntime-minute = 2DIGIT ; 00-59\ntime-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second\n ; rules\ntime-secfrac = \".\" 1*DIGIT\ntime-numoffset = (\"+\" / \"-\") time-hour \":\" time-minute\ntime-offset = \"Z\" / time-numoffset\n\npartial-time = time-hour \":\" time-minute \":\" time-second\n [time-secfrac]\nfull-date = date-fullyear \"-\" date-month \"-\" date-mday\nfull-time = partial-time time-offset\n\ndate-time = full-date \"T\" full-time\n\n; ------------------------------------------------------------------------------\n; RFC 5234\n\nALPHA = %x41-5A / %x61-7A ; A-Z / a-z\nLF = %x0A\n ; linefeed\nDIGIT = %x30-39\n ; 0-9\nHEXDIG = DIGIT / \"A\" / \"B\" / \"C\" / \"D\" / \"E\" / \"F\"\n`;\nclass GrammarApi {\n static generateApi() {\n const api = new api_1.default(GRAMMAR);\n api.generate();\n if (api.errors.length) {\n console.error(api.errorsToAscii());\n console.error(api.linesToAscii());\n console.log(api.displayAttributeErrors());\n throw new Error(`ABNF grammar has errors`);\n }\n return api.toObject();\n }\n}\n_a = GrammarApi;\nGrammarApi.grammarObj = _a.generateApi();\nclass ParsedMessage {\n constructor(msg) {\n const parser = new node_exports_1.default.parser();\n parser.ast = new node_exports_1.default.ast();\n const id = node_exports_1.default.ids;\n const scheme = function (state, chars, phraseIndex, phraseLength, data) {\n const ret = id.SEM_OK;\n if (state === id.SEM_PRE && phraseIndex === 0) {\n data.scheme = node_exports_1.default.utils.charsToString(chars, phraseIndex, phraseLength);\n }\n return ret;\n };\n parser.ast.callbacks.scheme = scheme;\n const domain = function (state, chars, phraseIndex, phraseLength, data) {\n const ret = id.SEM_OK;\n if (state === id.SEM_PRE) {\n data.domain = node_exports_1.default.utils.charsToString(chars, phraseIndex, phraseLength);\n }\n return ret;\n };\n parser.ast.callbacks.domain = domain;\n const address = function (state, chars, phraseIndex, phraseLength, data) {\n const ret = id.SEM_OK;\n if (state === id.SEM_PRE) {\n data.address = node_exports_1.default.utils.charsToString(chars, phraseIndex, phraseLength);\n }\n return ret;\n };\n parser.ast.callbacks.address = address;\n const statement = function (state, chars, phraseIndex, phraseLength, data) {\n const ret = id.SEM_OK;\n if (state === id.SEM_PRE) {\n data.statement = node_exports_1.default.utils.charsToString(chars, phraseIndex, phraseLength);\n }\n return ret;\n };\n parser.ast.callbacks.statement = statement;\n const uri = function (state, chars, phraseIndex, phraseLength, data) {\n const ret = id.SEM_OK;\n if (state === id.SEM_PRE) {\n if (!data.uri) {\n data.uri = node_exports_1.default.utils.charsToString(chars, phraseIndex, phraseLength);\n }\n }\n return ret;\n };\n parser.ast.callbacks.uri = uri;\n const version = function (state, chars, phraseIndex, phraseLength, data) {\n const ret = id.SEM_OK;\n if (state === id.SEM_PRE) {\n data.version = node_exports_1.default.utils.charsToString(chars, phraseIndex, phraseLength);\n }\n return ret;\n };\n parser.ast.callbacks.version = version;\n const chainId = function (state, chars, phraseIndex, phraseLength, data) {\n const ret = id.SEM_OK;\n if (state === id.SEM_PRE) {\n data.chainId = (0, utils_1.parseIntegerNumber)(node_exports_1.default.utils.charsToString(chars, phraseIndex, phraseLength));\n }\n return ret;\n };\n parser.ast.callbacks[\"chain-id\"] = chainId;\n const nonce = function (state, chars, phraseIndex, phraseLength, data) {\n const ret = id.SEM_OK;\n if (state === id.SEM_PRE) {\n data.nonce = node_exports_1.default.utils.charsToString(chars, phraseIndex, phraseLength);\n }\n return ret;\n };\n parser.ast.callbacks.nonce = nonce;\n const issuedAt = function (state, chars, phraseIndex, phraseLength, data) {\n const ret = id.SEM_OK;\n if (state === id.SEM_PRE) {\n data.issuedAt = node_exports_1.default.utils.charsToString(chars, phraseIndex, phraseLength);\n }\n return ret;\n };\n parser.ast.callbacks[\"issued-at\"] = issuedAt;\n const expirationTime = function (state, chars, phraseIndex, phraseLength, data) {\n const ret = id.SEM_OK;\n if (state === id.SEM_PRE) {\n data.expirationTime = node_exports_1.default.utils.charsToString(chars, phraseIndex, phraseLength);\n }\n return ret;\n };\n parser.ast.callbacks[\"expiration-time\"] = expirationTime;\n const notBefore = function (state, chars, phraseIndex, phraseLength, data) {\n const ret = id.SEM_OK;\n if (state === id.SEM_PRE) {\n data.notBefore = node_exports_1.default.utils.charsToString(chars, phraseIndex, phraseLength);\n }\n return ret;\n };\n parser.ast.callbacks[\"not-before\"] = notBefore;\n const requestId = function (state, chars, phraseIndex, phraseLength, data) {\n const ret = id.SEM_OK;\n if (state === id.SEM_PRE) {\n data.requestId = node_exports_1.default.utils.charsToString(chars, phraseIndex, phraseLength);\n }\n return ret;\n };\n parser.ast.callbacks[\"request-id\"] = requestId;\n const resources = function (state, chars, phraseIndex, phraseLength, data) {\n const ret = id.SEM_OK;\n if (state === id.SEM_PRE) {\n data.resources = node_exports_1.default.utils\n .charsToString(chars, phraseIndex, phraseLength)\n .slice(3)\n .split(\"\\n- \");\n }\n return ret;\n };\n parser.ast.callbacks.resources = resources;\n const result = parser.parse(GrammarApi.grammarObj, \"sign-in-with-ethereum\", msg);\n if (!result.success) {\n throw new Error(`Invalid message: ${JSON.stringify(result)}`);\n }\n const elements = {};\n parser.ast.translate(elements);\n for (const [key, value] of Object.entries(elements)) {\n this[key] = value;\n }\n if (this.domain.length === 0) {\n throw new Error(\"Domain cannot be empty.\");\n }\n }\n}\nexports.ParsedMessage = ParsedMessage;\n","/* *************************************************************************************\n * copyright: Copyright (c) 2021 Lowell D. Thomas, all rights reserved\n * license: BSD-2-Clause (https://opensource.org/licenses/BSD-2-Clause)\n * ********************************************************************************* */\n// This module is Application Programming Interface (API) for **APG** - the ABNF Parser Generator.\n//\n// *Note on teminology.*\n// APG is a parser generator.\n// However, it really only generates a \"grammar object\" (see below) from the defining SABNF grammar.\n// The generated parser is incomplete at this stage.\n// Remaining, it is the job of the user to develop the generated parser from the grammar object and the **APG** Library (**apg-lib**).\n//\n// The following terminology my help clear up any confusion between the idea of a \"generated parser\" versus a \"generated grammar object\".\n\n// - The generating parser: **APG** is an **APG** parser (yes, there is a circular dependence between **apg-api** and **apg-lib**). We'll call it the generating parser.\n// - The target parser: **APG**'s goal is to generate a parser. We'll call it the target parser.\n// - The target grammar: this is the (ASCII) SABNF grammar defining the target parser.\n// - The target grammar object: **APG** parses the SABNF grammar and generates the JavaScript source for a target grammar object constructor function\n// and/or an actual grammar object.\n// - The final target parser: The user then develops the final target parser using the generated target grammar\n// object and the **APG** parsing library, **apg-lib**.\n// Throws execeptions on fatal errors.\n//\n// src: the input SABNF grammar<br>\n// may be one of:\n// - Buffer of bytes\n// - JavaScript string\n// - Array of integer character codes\nmodule.exports = function api(src) {\n const { Buffer } = require('buffer');\n const thisFileName = 'api.js: ';\n const thisObject = this;\n\n /* PRIVATE PROPERTIES */\n const apglib = require('../apg-lib/node-exports');\n const converter = require('../apg-conv-api/converter');\n const scanner = require('./scanner');\n const parser = new (require('./parser'))();\n const { attributes, showAttributes, showAttributeErrors, showRuleDependencies } = require('./attributes');\n const showRules = require('./show-rules');\n\n /* PRIVATE MEMBERS (FUNCTIONS) */\n /* Convert a phrase (array of character codes) to HTML. */\n const abnfToHtml = function abnfToHtml(chars, beg, len) {\n const NORMAL = 0;\n const CONTROL = 1;\n const INVALID = 2;\n const CONTROL_BEG = `<span class=\"${apglib.style.CLASS_CTRLCHAR}\">`;\n const CONTROL_END = '</span>';\n const INVALID_BEG = `<span class=\"${apglib.style.CLASS_NOMATCH}\">`;\n const INVALID_END = '</span>';\n let end;\n let html = '';\n const TRUE = true;\n while (TRUE) {\n if (!Array.isArray(chars) || chars.length === 0) {\n break;\n }\n if (typeof beg !== 'number') {\n throw new Error('abnfToHtml: beg must be type number');\n }\n if (beg >= chars.length) {\n break;\n }\n if (typeof len !== 'number' || beg + len >= chars.length) {\n end = chars.length;\n } else {\n end = beg + len;\n }\n let state = NORMAL;\n for (let i = beg; i < end; i += 1) {\n const ch = chars[i];\n if (ch >= 32 && ch <= 126) {\n /* normal - printable ASCII characters */\n if (state === CONTROL) {\n html += CONTROL_END;\n state = NORMAL;\n } else if (state === INVALID) {\n html += INVALID_END;\n state = NORMAL;\n }\n /* handle reserved HTML entity characters */\n switch (ch) {\n case 32:\n html += ' ';\n break;\n case 60:\n html += '<';\n break;\n case 62:\n html += '>';\n break;\n case 38:\n html += '&';\n break;\n case 34:\n html += '"';\n break;\n case 39:\n html += ''';\n break;\n case 92:\n html += '\';\n break;\n default:\n html += String.fromCharCode(ch);\n break;\n }\n } else if (ch === 9 || ch === 10 || ch === 13) {\n /* control characters */\n if (state === NORMAL) {\n html += CONTROL_BEG;\n state = CONTROL;\n } else if (state === INVALID) {\n html += INVALID_END + CONTROL_BEG;\n state = CONTROL;\n }\n if (ch === 9) {\n html += 'TAB';\n }\n if (ch === 10) {\n html += 'LF';\n }\n if (ch === 13) {\n html += 'CR';\n }\n } else {\n /* invalid characters */\n if (state === NORMAL) {\n html += INVALID_BEG;\n state = INVALID;\n } else if (state === CONTROL) {\n html += CONTROL_END + INVALID_BEG;\n state = INVALID;\n }\n /* display character as hexadecimal value */\n html += `\\\\x${apglib.utils.charToHex(ch)}`;\n }\n }\n if (state === INVALID) {\n html += INVALID_END;\n }\n if (state === CONTROL) {\n html += CONTROL_END;\n }\n break;\n }\n return html;\n };\n /* Convert a phrase (array of character codes) to ASCII text. */\n const abnfToAscii = function abnfToAscii(chars, beg, len) {\n let str = '';\n for (let i = beg; i < beg + len; i += 1) {\n const ch = chars[i];\n if (ch >= 32 && ch <= 126) {\n str += String.fromCharCode(ch);\n } else {\n switch (ch) {\n case 9:\n str += '\\\\t';\n break;\n case 10:\n str += '\\\\n';\n break;\n case 13:\n str += '\\\\r';\n break;\n default:\n str += '\\\\unknown';\n break;\n }\n }\n }\n return str;\n };\n /* translate lines (SABNF grammar) to ASCII text */\n const linesToAscii = function linesToAscii(lines) {\n let str = 'Annotated Input Grammar';\n lines.forEach((val) => {\n str += '\\n';\n str += `line no: ${val.lineNo}`;\n str += ` : char index: ${val.beginChar}`;\n str += ` : length: ${val.length}`;\n str += ` : abnf: ${abnfToAscii(thisObject.chars, val.beginChar, val.length)}`;\n });\n str += '\\n';\n return str;\n };\n /* translate lines (SABNF grammar) to HTML */\n const linesToHtml = function linesToHtml(lines) {\n let html = '';\n html += `<table class=\"${apglib.style.CLASS_GRAMMAR}\">\\n`;\n const title = 'Annotated Input Grammar';\n html += `<caption>${title}</caption>\\n`;\n html += '<tr>';\n html += '<th>line<br>no.</th><th>first<br>char</th><th><br>length</th><th><br>text</th>';\n html += '</tr>\\n';\n lines.forEach((val) => {\n html += '<tr>';\n html += `<td>${val.lineNo}`;\n html += `</td><td>${val.beginChar}`;\n html += `</td><td>${val.length}`;\n html += `</td><td>${abnfToHtml(thisObject.chars, val.beginChar, val.length)}`;\n html += '</td>';\n html += '</tr>\\n';\n });\n\n html += '</table>\\n';\n return html;\n };\n /* Format the error messages to HTML, for page display. */\n const errorsToHtml = function errorsToHtml(errors, lines, chars, title) {\n const [style] = apglib;\n let html = '';\n const errorArrow = `<span class=\"${style.CLASS_NOMATCH}\">»</span>`;\n html += `<p><table class=\"${style.CLASS_GRAMMAR}\">\\n`;\n if (title && typeof title === 'string') {\n html += `<caption>${title}</caption>\\n`;\n }\n html += '<tr><th>line<br>no.</th><th>line<br>offset</th><th>error<br>offset</th><th><br>text</th></tr>\\n';\n errors.forEach((val) => {\n let line;\n let relchar;\n let beg;\n let end;\n let text;\n let prefix = '';\n let suffix = '';\n if (lines.length === 0) {\n text = errorArrow;\n relchar = 0;\n } else {\n line = lines[val.line];\n beg = line.beginChar;\n if (val.char > beg) {\n prefix = abnfToHtml(chars, beg, val.char - beg);\n }\n beg = val.char;\n end = line.beginChar + line.length;\n if (beg < end) {\n suffix = abnfToHtml(chars, beg, end - beg);\n }\n text = prefix + errorArrow + suffix;\n relchar = val.char - line.beginChar;\n html += '<tr>';\n html += `<td>${val.line}</td><td>${line.beginChar}</td><td>${relchar}</td><td>${text}</td>`;\n html += '</tr>\\n';\n html += '<tr>';\n html += `<td colspan=\"3\"></td><td>↑: ${apglib.utils.stringToAsciiHtml(val.msg)}</td>`;\n html += '</tr>\\n';\n }\n });\n html += '</table></p>\\n';\n return html;\n };\n /* Display an array of errors in ASCII text */\n const errorsToAscii = function errorsToAscii(errors, lines, chars) {\n let str;\n let line;\n let beg;\n let len;\n str = '';\n errors.forEach((error) => {\n line = lines[error.line];\n str += `${line.lineNo}: `;\n str += `${line.beginChar}: `;\n str += `${error.char - line.beginChar}: `;\n beg = line.beginChar;\n len = error.char - line.beginChar;\n str += abnfToAscii(chars, beg, len);\n str += ' >> ';\n beg = error.char;\n len = line.beginChar + line.length - error.char;\n str += abnfToAscii(chars, beg, len);\n str += '\\n';\n str += `${line.lineNo}: `;\n str += `${line.beginChar}: `;\n str += `${error.char - line.beginChar}: `;\n str += 'error: ';\n str += error.msg;\n str += '\\n';\n });\n return str;\n };\n let isScanned = false;\n let isParsed = false;\n let isTranslated = false;\n let haveAttributes = false;\n let attributeErrors = 0;\n let lineMap;\n\n /* PUBLIC PROPERTIES */\n // The input SABNF grammar as a JavaScript string.\n // this.sabnf;\n // The input SABNF grammar as an array of character codes.\n // this.chars;\n // An array of line objects, defining each line of the input SABNF grammar\n // - lineNo : the zero-based line number\n // - beginChar : offset (into `this.chars`) of the first character in the line\n // - length : the number of characters in the line\n // - textLength : the number of characters of text in the line, excluding the line ending characters\n // - endType : \"CRLF\", \"LF\", \"CR\" or \"none\" if the last line has no line ending characters\n // - invalidChars : `true` if the line contains invalid characters, `false` otherwise\n // this.lines;\n // An array of rule names and data.\n // - name : the rule name\n // - lower : the rule name in lower case\n // - index : the index of the rule (ordered by appearance in SABNF grammar)\n // - isBkr : `true` if this rule has been back referenced, `false` otherwise\n // - opcodes : array of opcodes for this rule\n // - attrs : the rule attributes\n // - ctrl : system data\n // this.rules;\n // An array of UDT names and data.\n // this.udts;\n // An array of errors, if any.\n // - line : the line number containing the error\n // - char : the character offset of the error\n // - msg : the error message\n this.errors = [];\n\n /* CONSTRUCTOR */\n if (Buffer.isBuffer(src)) {\n this.chars = converter.decode('BINARY', src);\n } else if (Array.isArray(src)) {\n this.chars = src.slice();\n } else if (typeof src === 'string') {\n this.chars = converter.decode('STRING', src);\n } else {\n throw new Error(`${thisFileName}input source is not a string, byte Buffer or character array`);\n }\n this.sabnf = converter.encode('STRING', this.chars);\n\n /* PUBLIC MEMBERS (FUNCTIONS) */\n // Scan the input SABNF grammar for invalid characters and catalog the lines via `this.lines`.\n // - strict : (optional) if `true`, all lines, including the last must end with CRLF (\\r\\n),\n // if `false` (in any JavaScript sense) then line endings may be any mix of CRLF, LF, CR, or end-of-file.\n // - trace (*) : (optional) a parser trace object, which will trace the parser that does the scan\n this.scan = function scan(strict, trace) {\n this.lines = scanner(this.chars, this.errors, strict, trace);\n isScanned = true;\n };\n // Parse the input SABNF grammar for correct syntax.\n // - strict : (optional) if `true`, the input grammar must be strict ABNF, conforming to [RFC 5234](https://tools.ietf.org/html/rfc5234)\n // and [RFC 7405](https://tools.ietf.org/html/rfc7405). No superset features allowed.\n // - trace (\\*) : (optional) a parser trace object, which will trace the syntax parser\n //\n // <i>(*)NOTE: the trace option was used primarily during development.\n // Error detection and reporting is now fairly robust and tracing should be unnecessary. Use at your own peril.</i>\n this.parse = function parse(strict, lite, trace) {\n if (!isScanned) {\n throw new Error(`${thisFileName}grammar not scanned`);\n }\n parser.syntax(this.chars, this.lines, this.errors, strict, lite, trace);\n isParsed = true;\n };\n // Translate the SABNF grammar syntax into the opcodes that will guide the parser for this grammar.\n this.translate = function translate() {\n if (!isParsed) {\n throw new Error(`${thisFileName}grammar not scanned and parsed`);\n }\n const ret = parser.semantic(this.chars, this.lines, this.errors);\n if (this.errors.length === 0) {\n this.rules = ret.rules;\n this.udts = ret.udts;\n lineMap = ret.lineMap;\n isTranslated = true;\n }\n };\n // Compute the attributes of each rule.\n this.attributes = function attrs() {\n if (!isTranslated) {\n throw new Error(`${thisFileName}grammar not scanned, parsed and translated`);\n }\n attributeErrors = attributes(this.rules, this.udts, lineMap, this.errors);\n haveAttributes = true;\n return attributeErrors;\n };\n // This function will perform the full suite of steps required to generate a parser grammar object\n // from the input SABNF grammar.\n this.generate = function generate(strict) {\n this.lines = scanner(this.chars, this.errors, strict);\n if (this.errors.length) {\n return;\n }\n parser.syntax(this.chars, this.lines, this.errors, strict);\n if (this.errors.length) {\n return;\n }\n const ret = parser.semantic(this.chars, this.lines, this.errors);\n if (this.errors.length) {\n return;\n }\n this.rules = ret.rules;\n this.udts = ret.udts;\n lineMap = ret.lineMap;\n\n attributeErrors = attributes(this.rules, this.udts, lineMap, this.errors);\n haveAttributes = true;\n };\n // Display the rules.\n // Must scan, parse and translate before calling this function, otherwise there are no rules to display.\n // - order\n // - \"index\" or \"i\", index order (default)\n // - \"alpha\" or \"a\", alphabetical order\n // - none of above, index order (default)\n this.displayRules = function displayRules(order = 'index') {\n if (!isTranslated) {\n throw new Error(`${thisFileName}grammar not scanned, parsed and translated`);\n }\n return showRules(this.rules, this.udts, order);\n };\n // Display the rule dependencies.\n // Must scan, parse, translate and compute attributes before calling this function.\n // Otherwise the rule dependencies are not known.\n // - order\n // - \"index\" or \"i\", index order (default)\n // - \"alpha\" or \"a\", alphabetical order\n // - \"type\" or \"t\", ordered by type (alphabetical within each type/group)\n // - none of above, index order (default)\n this.displayRuleDependencies = function displayRuleDependencies(order = 'index') {\n if (!haveAttributes) {\n throw new Error(`${thisFileName}no attributes - must be preceeded by call to attributes()`);\n }\n return showRuleDependencies(order);\n };\n // Display the attributes.\n // Must scan, parse, translate and compute attributes before calling this function.\n // - order\n // - \"index\" or \"i\", index order (default)\n // - \"alpha\" or \"a\", alphabetical order\n // - \"type\" or \"t\", ordered by type (alphabetical within each type/group)\n // - none of above, type order (default)\n this.displayAttributes = function displayAttributes(order = 'index') {\n if (!haveAttributes) {\n throw new Error(`${thisFileName}no attributes - must be preceeded by call to attributes()`);\n }\n if (attributeErrors) {\n showAttributeErrors(order);\n }\n return showAttributes(order);\n };\n this.displayAttributeErrors = function displayAttributeErrors() {\n if (!haveAttributes) {\n throw new Error(`${thisFileName}no attributes - must be preceeded by call to attributes()`);\n }\n return showAttributeErrors();\n };\n // Returns a parser grammar object constructor function as a JavaScript string.\n // This object can then be used to construct a parser.\n this.toSource = function toSource(config = undefined) {\n if (!haveAttributes) {\n throw new Error(`${thisFileName}can't generate parser source - must be preceeded by call to attributes()`);\n }\n if (attributeErrors) {\n throw new Error(`${thisFileName}can't generate parser source - attributes have ${attributeErrors} errors`);\n }\n return parser.generateSource(this.chars, this.lines, this.rules, this.udts, config);\n };\n // Returns a parser grammar object.\n // This grammar object may be used by the application to construct a parser.\n this.toObject = function toObject() {\n if (!haveAttributes) {\n throw new Error(`${thisFileName}can't generate parser source - must be preceeded by call to attributes()`);\n }\n if (attributeErrors) {\n throw new Error(`${thisFileName}can't generate parser source - attributes have ${attributeErrors} errors`);\n }\n return parser.generateObject(this.sabnf, this.rules, this.udts);\n };\n // Display errors in text format, suitable for `console.log()`.\n this.errorsToAscii = function errorsToAsciiFunc() {\n return errorsToAscii(this.errors, this.lines, this.chars);\n };\n // Display errors in HTML format, suitable for web page display.\n // (`apg-lib.css` required for proper styling)\n this.errorsToHtml = function errorsToHtmlFunc(title) {\n return errorsToHtml(this.errors, this.lines, this.chars, title);\n };\n // Generate an annotated the SABNF grammar display in text format.\n this.linesToAscii = function linesToAsciiFunc() {\n return linesToAscii(this.lines);\n };\n // Generate an annotated the SABNF grammar display in HTML format.\n // (`apg-lib.css` required for proper styling)\n this.linesToHtml = function linesToHtmlFunc() {\n return linesToHtml(this.lines);\n };\n // This function was only used by apg.html which has been abandoned.\n /*\n this.getAttributesObject = function () {\n return null;\n };\n */\n};\n","/* *************************************************************************************\n * copyright: Copyright (c) 2021 Lowell D. Thomas, all rights reserved\n * license: BSD-2-Clause (https://opensource.org/licenses/BSD-2-Clause)\n * ********************************************************************************* */\n// This module converts an input SABNF grammar text file into a\n// grammar object that can be used with `apg-lib` in an application parser.\n// **apg** is, in fact itself, an ABNF parser that generates an SABNF parser.\n// It is based on the grammar<br>\n// `./dist/abnf-for-sabnf-grammar.bnf`.<br>\n// In its syntax phase, **apg** analyzes the user's input SABNF grammar for correct syntax, generating an AST as it goes.\n// In its semantic phase, **apg** translates the AST to generate the parser for the input grammar.\nmodule.exports = function exportParser() {\n const thisFileName = 'parser: ';\n const ApgLib = require('../apg-lib/node-exports');\n const id = ApgLib.ids;\n const syn = new (require('./syntax-callbacks'))();\n const sem = new (require('./semantic-callbacks'))();\n const sabnfGrammar = new (require('./sabnf-grammar'))();\n // eslint-disable-next-line new-cap\n const parser = new ApgLib.parser();\n // eslint-disable-next-line new-cap\n parser.ast = new ApgLib.ast();\n parser.callbacks = syn.callbacks;\n parser.ast.callbacks = sem.callbacks;\n\n /* find the line containing the given character index */\n const findLine = function findLine(lines, charIndex, charLength) {\n if (charIndex < 0 || charIndex >= charLength) {\n /* return error if out of range */\n return -1;\n }\n for (let i = 0; i < lines.length; i += 1) {\n if (charIndex >= lines[i].beginChar && charIndex < lines[i].beginChar + lines[i].length) {\n return i;\n }\n }\n /* should never reach here */\n return -1;\n };\n const translateIndex = function translateIndex(map, index) {\n let ret = -1;\n if (index < map.length) {\n for (let i = index; i < map.length; i += 1) {\n if (map[i] !== null) {\n ret = map[i];\n break;\n }\n }\n }\n return ret;\n };\n /* helper function when removing redundant opcodes */\n const reduceOpcodes = function reduceOpcodes(rules) {\n rules.forEach((rule) => {\n const opcodes = [];\n const map = [];\n let reducedIndex = 0;\n rule.opcodes.forEach((op) => {\n if (op.type === id.ALT && op.children.length === 1) {\n map.push(null);\n } else if (op.type === id.CAT && op.children.length === 1) {\n map.push(null);\n } else if (op.type === id.REP && op.min === 1 && op.max === 1) {\n map.push(null);\n } else {\n map.push(reducedIndex);\n opcodes.push(op);\n reducedIndex += 1;\n }\n });\n map.push(reducedIndex);\n /* translate original opcode indexes to the reduced set. */\n opcodes.forEach((op) => {\n if (op.type === id.ALT || op.type === id.CAT) {\n for (let i = 0; i < op.children.length; i += 1) {\n op.children[i] = translateIndex(map, op.children[i]);\n }\n }\n });\n rule.opcodes = opcodes;\n });\n };\n /* Parse the grammar - the syntax phase. */\n /* SABNF grammar syntax errors are caught and reported here. */\n this.syntax = function syntax(chars, lines, errors, strict, lite, trace) {\n if (trace) {\n if (trace.traceObject !== 'traceObject') {\n throw new TypeError(`${thisFileName}trace argument is not a trace object`);\n }\n parser.trace = trace;\n }\n const data = {};\n data.errors = errors;\n data.strict = !!strict;\n data.lite = !!lite;\n data.lines = lines;\n data.findLine = findLine;\n data.charsLength = chars.length;\n data.ruleCount = 0;\n const result = parser.parse(sabnfGrammar, 'file', chars, data);\n if (!result.success) {\n errors.push({\n line: 0,\n char: 0,\n msg: 'syntax analysis of input grammar failed',\n });\n }\n };\n /* Parse the grammar - the semantic phase, translates the AST. */\n /* SABNF grammar syntax errors are caught and reported here. */\n this.semantic = function semantic(chars, lines, errors) {\n const data = {};\n data.errors = errors;\n data.lines = lines;\n data.findLine = findLine;\n data.charsLength = chars.length;\n parser.ast.translate(data);\n if (errors.length) {\n return null;\n }\n /* Remove unneeded operators. */\n /* ALT operators with a single alternate */\n /* CAT operators with a single phrase to concatenate */\n /* REP(1,1) operators (`1*1RuleName` or `1RuleName` is the same as just `RuleName`.) */\n reduceOpcodes(data.rules);\n return {\n rules: data.rules,\n udts: data.udts,\n lineMap: data.rulesLineMap,\n };\n };\n // Generate a grammar constructor function.\n // An object instantiated from this constructor is used with the `apg-lib` `parser()` function.\n this.generateSource = function generateSource(chars, lines, rules, udts, config) {\n let source = '';\n let typescript = false;\n let lite = false;\n // config may have multiple grammar object type options in which case\n // --typescript > --lite > no options\n if (config) {\n if (config.typescript) {\n typescript = true;\n lite = false;\n } else if (config.lite) {\n typescript = false;\n lite = true;\n }\n }\n let i;\n let bkrname;\n let bkrlower;\n let opcodeCount = 0;\n let charCodeMin = Infinity;\n let charCodeMax = 0;\n const ruleNames = [];\n const udtNames = [];\n let alt = 0;\n let cat = 0;\n let rnm = 0;\n let udt = 0;\n let rep = 0;\n let and = 0;\n let not = 0;\n let tls = 0;\n let tbs = 0;\n let trg = 0;\n let bkr = 0;\n let bka = 0;\n let bkn = 0;\n let abg = 0;\n let aen = 0;\n rules.forEach((rule) => {\n ruleNames.push(rule.lower);\n opcodeCount += rule.opcodes.length;\n rule.opcodes.forEach((op) => {\n switch (op.type) {\n case id.ALT:\n alt += 1;\n break;\n case id.CAT:\n cat += 1;\n break;\n case id.RNM:\n rnm += 1;\n break;\n case id.UDT:\n udt += 1;\n break;\n case id.REP:\n rep += 1;\n break;\n case id.AND:\n and += 1;\n break;\n case id.NOT:\n not += 1;\n break;\n case id.BKA:\n bka += 1;\n break;\n case id.BKN:\n bkn += 1;\n break;\n case id.BKR:\n bkr += 1;\n break;\n case id.ABG:\n abg += 1;\n break;\n case id.AEN:\n aen += 1;\n break;\n case id.TLS:\n tls += 1;\n for (i = 0; i < op.string.length; i += 1) {\n if (op.string[i] < charCodeMin) {\n charCodeMin = op.string[i];\n }\n if (op.string[i] > charCodeMax) {\n charCodeMax = op.string[i];\n }\n }\n break;\n case id.TBS:\n tbs += 1;\n for (i = 0; i < op.string.length; i += 1) {\n if (op.string[i] < charCodeMin) {\n charCodeMin = op.string[i];\n }\n if (op.string[i] > charCodeMax) {\n charCodeMax = op.string[i];\n }\n }\n break;\n case id.TRG:\n trg += 1;\n if (op.min < charCodeMin) {\n charCodeMin = op.min;\n }\n if (op.max > charCodeMax) {\n charCodeMax = op.max;\n }\n break;\n default:\n throw new Error('generateSource: unrecognized opcode');\n }\n });\n });\n ruleNames.sort();\n if (udts.length > 0) {\n udts.forEach((udtFunc) => {\n udtNames.push(udtFunc.lower);\n });\n udtNames.sort();\n }\n source += '// copyright: Copyright (c) 2024 Lowell D. Thomas, all rights reserved<br>\\n';\n source += '// license: BSD-2-Clause (https://opensource.org/licenses/BSD-2-Clause)<br>\\n';\n source += '//\\n';\n source += '// Generated by apg-js, Version 4.4.0 [apg-js](https://github.com/ldthomas/apg-js)\\n';\n if (config) {\n if (config.funcName) {\n source += `const ${config.funcName} = function grammar(){\\n`;\n } else if (typescript) {\n source += 'export function grammar(){\\n';\n } else if (lite) {\n source += 'export default function grammar(){\\n';\n } else {\n source += `module.exports = function grammar(){\\n`;\n }\n } else {\n source += `module.exports = function grammar(){\\n`;\n }\n source += ' // ```\\n';\n source += ' // SUMMARY\\n';\n source += ` // rules = ${rules.length}\\n`;\n source += ` // udts = ${udts.length}\\n`;\n source += ` // opcodes = ${opcodeCount}\\n`;\n source += ' // --- ABNF original opcodes\\n';\n source += ` // ALT = ${alt}\\n`;\n source += ` // CAT = ${cat}\\n`;\n source += ` // REP = ${rep}\\n`;\n source += ` // RNM = ${rnm}\\n`;\n source += ` // TLS = ${tls}\\n`;\n source += ` // TBS = ${tbs}\\n`;\n source += ` // TRG = ${trg}\\n`;\n source += ' // --- SABNF superset opcodes\\n';\n source += ` // UDT = ${udt}\\n`;\n source += ` // AND = ${and}\\n`;\n source += ` // NOT = ${not}\\n`;\n if (!lite) {\n source += ` // BKA = ${bka}\\n`;\n source += ` // BKN = ${bkn}\\n`;\n source += ` // BKR = ${bkr}\\n`;\n source += ` // ABG = ${abg}\\n`;\n source += ` // AEN = ${aen}\\n`;\n }\n source += ' // characters = [';\n if (tls + tbs + trg === 0) {\n source += ' none defined ]';\n } else {\n source += `${charCodeMin} - ${charCodeMax}]`;\n }\n if (udt > 0) {\n source += ' + user defined';\n }\n source += '\\n';\n source += ' // ```\\n';\n source += ' /* OBJECT IDENTIFIER (for internal parser use) */\\n';\n source += \" this.grammarObject = 'grammarObject';\\n\";\n source += '\\n';\n source += ' /* RULES */\\n';\n source += ' this.rules = [];\\n';\n rules.forEach((rule, ii) => {\n let thisRule = ' this.rules[';\n thisRule += ii;\n thisRule += \"] = { name: '\";\n thisRule += rule.name;\n thisRule += \"', lower: '\";\n thisRule += rule.lower;\n thisRule += \"', index: \";\n thisRule += rule.index;\n thisRule += ', isBkr: ';\n thisRule += rule.isBkr;\n thisRule += ' };\\n';\n source += thisRule;\n });\n source += '\\n';\n source += ' /* UDTS */\\n';\n source += ' this.udts = [];\\n';\n if (udts.length > 0) {\n udts.forEach((udtFunc, ii) => {\n let thisUdt = ' this.udts[';\n thisUdt += ii;\n thisUdt += \"] = { name: '\";\n thisUdt += udtFunc.name;\n thisUdt += \"', lower: '\";\n thisUdt += udtFunc.lower;\n thisUdt += \"', index: \";\n thisUdt += udtFunc.index;\n thisUdt += ', empty: ';\n thisUdt += udtFunc.empty;\n thisUdt += ', isBkr: ';\n thisUdt += udtFunc.isBkr;\n thisUdt += ' };\\n';\n source += thisUdt;\n });\n }\n source += '\\n';\n source += ' /* OPCODES */\\n';\n rules.forEach((rule, ruleIndex) => {\n if (ruleIndex > 0) {\n source += '\\n';\n }\n source += ` /* ${rule.name} */\\n`;\n source += ` this.rules[${ruleIndex}].opcodes = [];\\n`;\n rule.opcodes.forEach((op, opIndex) => {\n let prefix;\n switch (op.type) {\n case id.ALT:\n source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = { type: ${\n op.type\n }, children: [${op.children.toString()}] };// ALT\\n`;\n break;\n case id.CAT:\n source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = { type: ${\n op.type\n }, children: [${op.children.toString()}] };// CAT\\n`;\n break;\n case id.RNM:\n source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = { type: ${op.type}, index: ${\n op.index\n } };// RNM(${rules[op.index].name})\\n`;\n break;\n case id.BKR:\n if (op.index >= rules.length) {\n bkrname = udts[op.index - rules.length].name;\n bkrlower = udts[op.index - rules.length].lower;\n } else {\n bkrname = rules[op.index].name;\n bkrlower = rules[op.index].lower;\n }\n prefix = '%i';\n if (op.bkrCase === id.BKR_MODE_CS) {\n prefix = '%s';\n }\n if (op.bkrMode === id.BKR_MODE_UM) {\n prefix += '%u';\n } else {\n prefix += '%p';\n }\n bkrname = prefix + bkrname;\n source +=\n ` this.rules[${ruleIndex}].opcodes[${opIndex}] = { type: ${op.type}, index: ${op.index}, lower: '${bkrlower}'` +\n `, bkrCase: ${op.bkrCase}, bkrMode: ${op.bkrMode} };// BKR(\\\\${bkrname})\\n`;\n break;\n case id.UDT:\n source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = { type: ${op.type}, empty: ${\n op.empty\n }, index: ${op.index} };// UDT(${udts[op.index].name})\\n`;\n break;\n case id.REP:\n source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = { type: ${op.type}, min: ${op.min}, max: ${op.max} };// REP\\n`;\n break;\n case id.AND:\n source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = { type: ${op.type} };// AND\\n`;\n break;\n case id.NOT:\n source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = { type: ${op.type} };// NOT\\n`;\n break;\n case id.ABG:\n source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = { type: ${op.type} };// ABG(%^)\\n`;\n break;\n case id.AEN:\n source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = { type: ${op.type} };// AEN(%$)\\n`;\n break;\n case id.BKA:\n source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = { type: ${op.type} };// BKA\\n`;\n break;\n case id.BKN:\n source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = { type: ${op.type} };// BKN\\n`;\n break;\n case id.TLS:\n source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = { type: ${\n op.type\n }, string: [${op.string.toString()}] };// TLS\\n`;\n break;\n case id.TBS:\n source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = { type: ${\n op.type\n }, string: [${op.string.toString()}] };// TBS\\n`;\n break;\n case id.TRG:\n source += ` this.rules[${ruleIndex}].opcodes[${opIndex}] = { type: ${op.type}, min: ${op.min}, max: ${op.max} };// TRG\\n`;\n break;\n default:\n throw new Error('parser.js: ~143: unrecognized opcode');\n }\n });\n });\n source += '\\n';\n source += ' // The `toString()` function will display the original grammar file(s) that produced these opcodes.\\n';\n source += ' this.toString = function toString(){\\n';\n source += ' let str = \"\";\\n';\n let str;\n lines.forEach((line) => {\n const end = line.beginChar + line.length;\n str = '';\n source += ' str += \"';\n for (let ii = line.beginChar; ii < end; ii += 1) {\n switch (chars[ii]) {\n case 9:\n str = ' ';\n break;\n case 10:\n str = '\\\\n';\n break;\n case 13:\n str = '\\\\r';\n break;\n case 34:\n str = '\\\\\"';\n break;\n case 92:\n str = '\\\\\\\\';\n break;\n default:\n str = String.fromCharCode(chars[ii]);\n break;\n }\n source += str;\n }\n source += '\";\\n';\n });\n source += ' return str;\\n';\n source += ' }\\n';\n source += '}\\n';\n return source;\n };\n // Generate a grammar file object.\n // Returns the same object as instantiating the constructor function returned by<br>\n // `this.generateSource()`.<br>\n this.generateObject = function generateObject(stringArg, rules, udts) {\n const obj = {};\n const ruleNames = [];\n const udtNames = [];\n const string = stringArg.slice(0);\n obj.grammarObject = 'grammarObject';\n rules.forEach((rule) => {\n ruleNames.push(rule.lower);\n });\n ruleNames.sort();\n if (udts.length > 0) {\n udts.forEach((udtFunc) => {\n udtNames.push(udtFunc.lower);\n });\n udtNames.sort();\n }\n obj.callbacks = [];\n ruleNames.forEach((name) => {\n obj.callbacks[name] = false;\n });\n if (udts.length > 0) {\n udtNames.forEach((name) => {\n obj.callbacks[name] = false;\n });\n }\n obj.rules = rules;\n obj.udts = udts;\n obj.toString = function toStringFunc() {\n return string;\n };\n return obj;\n };\n};\n","/* *************************************************************************************\n * copyright: Copyright (c) 2021 Lowell D. Thomas, all rights reserved\n * license: BSD-2-Clause (https://opensource.org/licenses/BSD-2-Clause)\n * ********************************************************************************* */\n// This module does the heavy lifting for attribute generation.\nmodule.exports = (function exportRuleAttributes() {\n const id = require('../apg-lib/identifiers');\n const thisFile = 'rule-attributes.js';\n let state = null;\n function isEmptyOnly(attr) {\n if (attr.left || attr.nested || attr.right || attr.cyclic) {\n return false;\n }\n return attr.empty;\n }\n function isRecursive(attr) {\n if (attr.left || attr.nested || attr.right || attr.cyclic) {\n return true;\n }\n return false;\n }\n function isCatNested(attrs, count) {\n let i = 0;\n let j = 0;\n let k = 0;\n /* 1. if any child is nested, CAT is nested */\n for (i = 0; i < count; i += 1) {\n if (attrs[i].nested) {\n return true;\n }\n }\n /* 2.) the left-most right recursive child\n is followed by at least one non-empty child */\n for (i = 0; i < count; i += 1) {\n if (attrs[i].right && !attrs[i].leaf) {\n for (j = i + 1; j < count; j += 1) {\n if (!isEmptyOnly(attrs[j])) {\n return true;\n }\n }\n }\n }\n /* 3.) the right-most left recursive child\n is preceded by at least one non-empty child */\n for (i = count - 1; i >= 0; i -= 1) {\n if (attrs[i].left && !attrs[i].leaf) {\n for (j = i - 1; j >= 0; j -= 1) {\n if (!isEmptyOnly(attrs[j])) {\n return true;\n }\n }\n }\n }\n /* 4. there is at lease one recursive child between\n the left-most and right-most non-recursive, non-empty children */\n for (i = 0; i < count; i += 1) {\n if (!attrs[i].empty && !isRecursive(attrs[i])) {\n for (j = i + 1; j < count; j += 1) {\n if (isRecursive(attrs[j])) {\n for (k = j + 1; k < count; k += 1) {\n if (!attrs[k].empty && !isRecursive(attrs[k])) {\n return true;\n }\n }\n }\n }\n }\n }\n\n /* none of the above */\n return false;\n }\n function isCatCyclic(attrs, count) {\n /* if all children are cyclic, CAT is cyclic */\n for (let i = 0; i < count; i += 1) {\n if (!attrs[i].cyclic) {\n return false;\n }\n }\n return true;\n }\n function isCatLeft(attrs, count) {\n /* if the left-most non-empty is left, CAT is left */\n for (let i = 0; i < count; i += 1) {\n if (attrs[i].left) {\n return true;\n }\n if (!attrs[i].empty) {\n return false;\n }\n /* keep looking */\n }\n return false; /* all left-most are empty */\n }\n function isCatRight(attrs, count) {\n /* if the right-most non-empty is right, CAT is right */\n for (let i = count - 1; i >= 0; i -= 1) {\n if (attrs[i].right) {\n return true;\n }\n if (!attrs[i].empty) {\n return false;\n }\n /* keep looking */\n }\n return false;\n }\n function isCatEmpty(attrs, count) {\n /* if all children are empty, CAT is empty */\n for (let i = 0; i < count; i += 1) {\n if (!attrs[i].empty) {\n return false;\n }\n }\n return true;\n }\n function isCatFinite(attrs, count) {\n /* if all children are finite, CAT is finite */\n for (let i = 0; i < count; i += 1) {\n if (!attrs[i].finite) {\n return false;\n }\n }\n return true;\n }\n function cat(stateArg, opcodes, opIndex, iAttr) {\n let i = 0;\n const opCat = opcodes[opIndex];\n const count = opCat.children.length;\n\n /* generate an empty array of child attributes */\n const childAttrs = [];\n for (i = 0; i < count; i += 1) {\n childAttrs.push(stateArg.attrGen());\n }\n for (i = 0; i < count; i += 1) {\n // eslint-disable-next-line no-use-before-define\n opEval(stateArg, opcodes, opCat.children[i], childAttrs[i]);\n }\n iAttr.left = isCatLeft(childAttrs, count);\n iAttr.right = isCatRight(childAttrs, count);\n iAttr.nested = isCatNested(childAttrs, count);\n iAttr.empty = isCatEmpty(childAttrs, count);\n iAttr.finite = isCatFinite(childAttrs, count);\n iAttr.cyclic = isCatCyclic(childAttrs, count);\n }\n function alt(stateArg, opcodes, opIndex, iAttr) {\n let i = 0;\n const opAlt = opcodes[opIndex];\n const count = opAlt.children.length;\n\n /* generate an empty array of child attributes */\n const childAttrs = [];\n for (i = 0; i < count; i += 1) {\n childAttrs.push(stateArg.attrGen());\n }\n for (i = 0; i < count; i += 1) {\n // eslint-disable-next-line no-use-before-define\n opEval(stateArg, opcodes, opAlt.children[i], childAttrs[i]);\n }\n\n /* if any child attribute is true, ALT is true */\n iAttr.left = false;\n iAttr.right = false;\n iAttr.nested = false;\n iAttr.empty = false;\n iAttr.finite = false;\n iAttr.cyclic = false;\n for (i = 0; i < count; i += 1) {\n if (childAttrs[i].left) {\n iAttr.left = true;\n }\n if (childAttrs[i].nested) {\n iAttr.nested = true;\n }\n if (childAttrs[i].right) {\n iAttr.right = true;\n }\n if (childAttrs[i].empty) {\n iAttr.empty = true;\n }\n if (childAttrs[i].finite) {\n iAttr.finite = true;\n }\n if (childAttrs[i].cyclic) {\n iAttr.cyclic = true;\n }\n }\n }\n function bkr(stateArg, opcodes, opIndex, iAttr) {\n const opBkr = opcodes[opIndex];\n if (opBkr.index >= stateArg.ruleCount) {\n /* use UDT values */\n iAttr.empty = stateArg.udts[opBkr.index - stateArg.ruleCount].empty;\n iAttr.finite = true;\n } else {\n /* use the empty and finite values from the back referenced rule */\n // eslint-disable-next-line no-use-before-define\n ruleAttrsEval(stateArg, opBkr.index, iAttr);\n\n /* however, this is a terminal node like TLS */\n iAttr.left = false;\n iAttr.nested = false;\n iAttr.right = false;\n iAttr.cyclic = false;\n }\n }\n\n function opEval(stateArg, opcodes, opIndex, iAttr) {\n stateArg.attrInit(iAttr);\n const opi = opcodes[opIndex];\n switch (opi.type) {\n case id.ALT:\n alt(stateArg, opcodes, opIndex, iAttr);\n break;\n case id.CAT:\n cat(stateArg, opcodes, opIndex, iAttr);\n break;\n case id.REP:\n opEval(stateArg, opcodes, opIndex + 1, iAttr);\n if (opi.min === 0) {\n iAttr.empty = true;\n iAttr.finite = true;\n }\n break;\n case id.RNM:\n // eslint-disable-next-line no-use-before-define\n ruleAttrsEval(stateArg, opcodes[opIndex].index, iAttr);\n break;\n case id.BKR:\n bkr(stateArg, opcodes, opIndex, iAttr);\n break;\n case id.AND:\n case id.NOT:\n case id.BKA:\n case id.BKN:\n opEval(stateArg, opcodes, opIndex + 1, iAttr);\n iAttr.empty = true;\n break;\n case id.TLS:\n iAttr.empty = !opcodes[opIndex].string.length;\n iAttr.finite = true;\n iAttr.cyclic = false;\n break;\n case id.TBS:\n case id.TRG:\n iAttr.empty = false;\n iAttr.finite = true;\n iAttr.cyclic = false;\n break;\n case id.UDT:\n iAttr.empty = opi.empty;\n iAttr.finite = true;\n iAttr.cyclic = false;\n break;\n case id.ABG:\n case id.AEN:\n iAttr.empty = true;\n iAttr.finite = true;\n iAttr.cyclic = false;\n break;\n default:\n throw new Error(`unknown opcode type: ${opi}`);\n }\n }\n // The main logic for handling rules that:\n // - have already be evaluated\n // - have not been evaluated and is the first occurrence on this branch\n // - second occurrence on this branch for the start rule\n // - second occurrence on this branch for non-start rules\n function ruleAttrsEval(stateArg, ruleIndex, iAttr) {\n const attri = stateArg.attrsWorking[ruleIndex];\n if (attri.isComplete) {\n /* just use the completed values */\n stateArg.attrCopy(iAttr, attri);\n } else if (!attri.isOpen) {\n /* open the rule and traverse it */\n attri.isOpen = true;\n opEval(stateArg, attri.rule.opcodes, 0, iAttr);\n /* complete this rule's attributes */\n attri.left = iAttr.left;\n attri.right = iAttr.right;\n attri.nested = iAttr.nested;\n attri.empty = iAttr.empty;\n attri.finite = iAttr.finite;\n attri.cyclic = iAttr.cyclic;\n attri.leaf = false;\n attri.isOpen = false;\n attri.isComplete = true;\n } else if (ruleIndex === stateArg.startRule) {\n /* use recursive leaf values */\n if (ruleIndex === stateArg.startRule) {\n iAttr.left = true;\n iAttr.right = true;\n iAttr.cyclic = true;\n iAttr.leaf = true;\n }\n } else {\n /* non-start rule terminal leaf */\n iAttr.finite = true;\n }\n }\n // The main driver for the attribute generation.\n const ruleAttributes = (stateArg) => {\n state = stateArg;\n let i = 0;\n let j = 0;\n const iAttr = state.attrGen();\n for (i = 0; i < state.ruleCount; i += 1) {\n /* initialize working attributes */\n for (j = 0; j < state.ruleCount; j += 1) {\n state.attrInit(state.attrsWorking[j]);\n }\n state.startRule = i;\n ruleAttrsEval(state, i, iAttr);\n\n /* save off the working attributes for this rule */\n state.attrCopy(state.attrs[i], state.attrsWorking[i]);\n }\n state.attributesComplete = true;\n let attri = null;\n for (i = 0; i < state.ruleCount; i += 1) {\n attri = state.attrs[i];\n if (attri.left || !attri.finite || attri.cyclic) {\n const temp = state.attrGen(attri.rule);\n state.attrCopy(temp, attri);\n state.attrsErrors.push(temp);\n state.attrsErrorCount += 1;\n }\n }\n };\n const truth = (val) => (val ? 't' : 'f');\n const tError = (val) => (val ? 'e' : 'f');\n const fError = (val) => (val ? 't' : 'e');\n const showAttr = (seq, index, attr, dep) => {\n let str = `${seq}:${index}:`;\n str += `${tError(attr.left)} `;\n str += `${truth(attr.nested)} `;\n str += `${truth(attr.right)} `;\n str += `${tError(attr.cyclic)} `;\n str += `${fError(attr.finite)} `;\n str += `${truth(attr.empty)}:`;\n str += `${state.typeToString(dep.recursiveType)}:`;\n str += dep.recursiveType === id.ATTR_MR ? dep.groupNumber : '-';\n str += `:${attr.rule.name}\\n`;\n return str;\n };\n\n const showLegend = () => {\n let str = 'LEGEND - t=true, f=false, e=error\\n';\n str += 'sequence:rule index:left nested right cyclic finite empty:type:group number:rule name\\n';\n return str;\n };\n const showAttributeErrors = () => {\n let attri = null;\n let depi = null;\n let str = '';\n str += 'RULE ATTRIBUTES WITH ERRORS\\n';\n str += showLegend();\n if (state.attrsErrorCount) {\n for (let i = 0; i < state.attrsErrorCount; i += 1) {\n attri = state.attrsErrors[i];\n depi = state.ruleDeps[attri.rule.index];\n str += showAttr(i, attri.rule.index, attri, depi);\n }\n } else {\n str += '<none>\\n';\n }\n return str;\n };\n\n const show = (type) => {\n let i = 0;\n let ii = 0;\n let attri = null;\n let depi = null;\n let str = '';\n let { ruleIndexes } = state;\n // let udtIndexes = state.udtIndexes;\n if (type === 97) {\n ruleIndexes = state.ruleAlphaIndexes;\n // udtIndexes = state.udtAlphaIndexes;\n } else if (type === 116) {\n ruleIndexes = state.ruleTypeIndexes;\n // udtIndexes = state.udtAlphaIndexes;\n }\n /* show all attributes */\n for (i = 0; i < state.ruleCount; i += 1) {\n ii = ruleIndexes[i];\n attri = state.attrs[ii];\n depi = state.ruleDeps[ii];\n str += showAttr(i, ii, attri, depi);\n }\n return str;\n };\n\n // Display the rule attributes.\n // - order\n // - \"index\" or \"i\", index order (default)\n // - \"alpha\" or \"a\", alphabetical order\n // - \"type\" or \"t\", ordered by type (alphabetical within each type/group)\n // - none of above, index order (default)\n const showAttributes = (order = 'index') => {\n if (!state.attributesComplete) {\n throw new Error(`${thisFile}:showAttributes: attributes not available`);\n }\n let str = '';\n const leader = 'RULE ATTRIBUTES\\n';\n if (order.charCodeAt(0) === 97) {\n str += 'alphabetical by rule name\\n';\n str += leader;\n str += showLegend();\n str += show(97);\n } else if (order.charCodeAt(0) === 116) {\n str += 'ordered by rule type\\n';\n str += leader;\n str += showLegend();\n str += show(116);\n } else {\n str += 'ordered by rule index\\n';\n str += leader;\n str += showLegend();\n str += show();\n }\n return str;\n };\n\n /* Destructuring assignment - see MDN Web Docs */\n return { ruleAttributes, showAttributes, showAttributeErrors };\n})();\n","/* *************************************************************************************\n * copyright: Copyright (c) 2021 Lowell D. Thomas, all rights reserved\n * license: BSD-2-Clause (https://opensource.org/licenses/BSD-2-Clause)\n * ********************************************************************************* */\n// Determine rule dependencies and types.\n// For each rule, determine which other rules it refers to\n// and which of the other rules refer back to it.\n//\n// Rule types are:\n// - non-recursive - the rule never refers to itself, even indirectly\n// - recursive - the rule refers to itself, possibly indirectly\n// - mutually-recursive - belongs to a group of two or more rules, each of which refers to every other rule in the group, including itself.\nmodule.exports = (() => {\n const id = require('../apg-lib/identifiers');\n let state = null; /* keep a global reference to the state for the show functions */\n\n /* scan the opcodes of the indexed rule and discover which rules it references and which rule refer back to it */\n const scan = (ruleCount, ruleDeps, index, isScanned) => {\n let i = 0;\n let j = 0;\n const rdi = ruleDeps[index];\n isScanned[index] = true;\n const op = rdi.rule.opcodes;\n for (i = 0; i < op.length; i += 1) {\n const opi = op[i];\n if (opi.type === id.RNM) {\n rdi.refersTo[opi.index] = true;\n if (!isScanned[opi.index]) {\n scan(ruleCount, ruleDeps, opi.index, isScanned);\n }\n for (j = 0; j < ruleCount; j += 1) {\n if (ruleDeps[opi.index].refersTo[j]) {\n rdi.refersTo[j] = true;\n }\n }\n } else if (opi.type === id.UDT) {\n rdi.refersToUdt[opi.index] = true;\n } else if (opi.type === id.BKR) {\n if (opi.index < ruleCount) {\n rdi.refersTo[opi.index] = true;\n if (!isScanned[opi.index]) {\n scan(ruleCount, ruleDeps, opi.index, isScanned);\n }\n } else {\n rdi.refersToUdt[ruleCount - opi.index] = true;\n }\n }\n }\n };\n // Determine the rule dependencies, types and mutually recursive groups.\n const ruleDependencies = (stateArg) => {\n state = stateArg; /* make it global */\n let i = 0;\n let j = 0;\n let groupCount = 0;\n let rdi = null;\n let rdj = null;\n let newGroup = false;\n state.dependenciesComplete = false;\n\n /* make a working array of rule scanned markers */\n const isScanned = state.falseArray(state.ruleCount);\n\n /* discover the rule dependencies */\n for (i = 0; i < state.ruleCount; i += 1) {\n state.falsifyArray(isScanned);\n scan(state.ruleCount, state.ruleDeps, i, isScanned);\n }\n /* discover all rules referencing each rule */\n for (i = 0; i < state.ruleCount; i += 1) {\n for (j = 0; j < state.ruleCount; j += 1) {\n if (i !== j) {\n if (state.ruleDeps[j].refersTo[i]) {\n state.ruleDeps[i].referencedBy[j] = true;\n }\n }\n }\n }\n /* find the non-recursive and recursive types */\n for (i = 0; i < state.ruleCount; i += 1) {\n state.ruleDeps[i].recursiveType = id.ATTR_N;\n if (state.ruleDeps[i].refersTo[i]) {\n state.ruleDeps[i].recursiveType = id.ATTR_R;\n }\n }\n\n /* find the mutually-recursive groups, if any */\n groupCount = -1;\n for (i = 0; i < state.ruleCount; i += 1) {\n rdi = state.ruleDeps[i];\n if (rdi.recursiveType === id.ATTR_R) {\n newGroup = true;\n for (j = 0; j < state.ruleCount; j += 1) {\n if (i !== j) {\n rdj = state.ruleDeps[j];\n if (rdj.recursiveType === id.ATTR_R) {\n if (rdi.refersTo[j] && rdj.refersTo[i]) {\n if (newGroup) {\n groupCount += 1;\n rdi.recursiveType = id.ATTR_MR;\n rdi.groupNumber = groupCount;\n newGroup = false;\n }\n rdj.recursiveType = id.ATTR_MR;\n rdj.groupNumber = groupCount;\n }\n }\n }\n }\n }\n }\n state.isMutuallyRecursive = groupCount > -1;\n\n /* sort the rules/UDTS */\n state.ruleAlphaIndexes.sort(state.compRulesAlpha);\n state.ruleTypeIndexes.sort(state.compRulesAlpha);\n state.ruleTypeIndexes.sort(state.compRulesType);\n if (state.isMutuallyRecursive) {\n state.ruleTypeIndexes.sort(state.compRulesGroup);\n }\n if (state.udtCount) {\n state.udtAlphaIndexes.sort(state.compUdtsAlpha);\n }\n\n state.dependenciesComplete = true;\n };\n const show = (type = null) => {\n let i = 0;\n let j = 0;\n let count = 0;\n let startSeg = 0;\n const maxRule = state.ruleCount - 1;\n const maxUdt = state.udtCount - 1;\n const lineLength = 100;\n let str = '';\n let pre = '';\n const toArrow = '=> ';\n const byArrow = '<= ';\n let first = false;\n let rdi = null;\n let { ruleIndexes } = state;\n let { udtIndexes } = state;\n if (type === 97) {\n ruleIndexes = state.ruleAlphaIndexes;\n udtIndexes = state.udtAlphaIndexes;\n } else if (type === 116) {\n ruleIndexes = state.ruleTypeIndexes;\n udtIndexes = state.udtAlphaIndexes;\n }\n for (i = 0; i < state.ruleCount; i += 1) {\n rdi = state.ruleDeps[ruleIndexes[i]];\n pre = `${ruleIndexes[i]}:${state.typeToString(rdi.recursiveType)}:`;\n if (state.isMutuallyRecursive) {\n pre += rdi.groupNumber > -1 ? rdi.groupNumber : '-';\n pre += ':';\n }\n pre += ' ';\n str += `${pre + state.rules[ruleIndexes[i]].name}\\n`;\n first = true;\n count = 0;\n startSeg = str.length;\n str += pre;\n for (j = 0; j < state.ruleCount; j += 1) {\n if (rdi.refersTo[ruleIndexes[j]]) {\n if (first) {\n str += toArrow;\n first = false;\n str += state.ruleDeps[ruleIndexes[j]].rule.name;\n } else {\n str += `, ${state.ruleDeps[ruleIndexes[j]].rule.name}`;\n }\n count += 1;\n }\n if (str.length - startSeg > lineLength && j !== maxRule) {\n str += `\\n${pre}${toArrow}`;\n startSeg = str.length;\n }\n }\n if (state.udtCount) {\n for (j = 0; j < state.udtCount; j += 1) {\n if (rdi.refersToUdt[udtIndexes[j]]) {\n if (first) {\n str += toArrow;\n first = false;\n str += state.udts[udtIndexes[j]].name;\n } else {\n str += `, ${state.udts[udtIndexes[j]].name}`;\n }\n count += 1;\n }\n if (str.length - startSeg > lineLength && j !== maxUdt) {\n str += `\\n${pre}${toArrow}`;\n startSeg = str.length;\n }\n }\n }\n if (count === 0) {\n str += '=> <none>\\n';\n }\n if (first === false) {\n str += '\\n';\n }\n first = true;\n count = 0;\n startSeg = str.length;\n str += pre;\n for (j = 0; j < state.ruleCount; j += 1) {\n if (rdi.referencedBy[ruleIndexes[j]]) {\n if (first) {\n str += byArrow;\n first = false;\n str += state.ruleDeps[ruleIndexes[j]].rule.name;\n } else {\n str += `, ${state.ruleDeps[ruleIndexes[j]].rule.name}`;\n }\n count += 1;\n }\n if (str.length - startSeg > lineLength && j !== maxRule) {\n str += `\\n${pre}${toArrow}`;\n startSeg = str.length;\n }\n }\n if (count === 0) {\n str += '<= <none>\\n';\n }\n if (first === false) {\n str += '\\n';\n }\n str += '\\n';\n }\n return str;\n };\n // Display the rule dependencies.\n // - order\n // - \"index\" or \"i\", index order (default)\n // - \"alpha\" or \"a\", alphabetical order\n // - \"type\" or \"t\", ordered by type (alphabetical within each type/group)\n // - none of above, index order (default)\n const showRuleDependencies = (order = 'index') => {\n let str = 'RULE DEPENDENCIES(index:type:[group number:])\\n';\n str += '=> refers to rule names\\n';\n str += '<= referenced by rule names\\n';\n if (!state.dependenciesComplete) {\n return str;\n }\n\n if (order.charCodeAt(0) === 97) {\n str += 'alphabetical by rule name\\n';\n str += show(97);\n } else if (order.charCodeAt(0) === 116) {\n str += 'ordered by rule type\\n';\n str += show(116);\n } else {\n str += 'ordered by rule index\\n';\n str += show(null);\n }\n return str;\n };\n\n /* Destructuring assignment - see MDN Web Docs */\n return { ruleDependencies, showRuleDependencies };\n})();\n","/* *************************************************************************************\n * copyright: Copyright (c) 2021 Lowell D. Thomas, all rights reserved\n * license: BSD-2-Clause (https://opensource.org/licenses/BSD-2-Clause)\n * ********************************************************************************* */\nmodule.exports = (function exfn() {\n const thisFileName = 'show-rules.js';\n // Display the rules.\n // This function may be called before the attributes calculation.\n // Sorting is done independently from the attributes.\n // - order\n // - \"index\" or \"i\", index order (default)\n // - \"alpha\" or \"a\", alphabetical order\n // - none of above, index order (default)\n const showRules = function showRules(rulesIn = [], udtsIn = [], order = 'index') {\n const thisFuncName = 'showRules';\n let alphaArray = [];\n let udtAlphaArray = [];\n const indexArray = [];\n const udtIndexArray = [];\n const rules = rulesIn;\n const udts = udtsIn;\n const ruleCount = rulesIn.length;\n const udtCount = udtsIn.length;\n let str = 'RULE/UDT NAMES';\n let i;\n function compRulesAlpha(left, right) {\n if (rules[left].lower < rules[right].lower) {\n return -1;\n }\n if (rules[left].lower > rules[right].lower) {\n return 1;\n }\n return 0;\n }\n function compUdtsAlpha(left, right) {\n if (udts[left].lower < udts[right].lower) {\n return -1;\n }\n if (udts[left].lower > udts[right].lower) {\n return 1;\n }\n return 0;\n }\n if (!(Array.isArray(rulesIn) && rulesIn.length)) {\n throw new Error(`${thisFileName}:${thisFuncName}: rules arg must be array with length > 0`);\n }\n if (!Array.isArray(udtsIn)) {\n throw new Error(`${thisFileName}:${thisFuncName}: udts arg must be array`);\n }\n\n for (i = 0; i < ruleCount; i += 1) {\n indexArray.push(i);\n }\n alphaArray = indexArray.slice(0);\n alphaArray.sort(compRulesAlpha);\n if (udtCount) {\n for (i = 0; i < udtCount; i += 1) {\n udtIndexArray.push(i);\n }\n udtAlphaArray = udtIndexArray.slice(0);\n udtAlphaArray.sort(compUdtsAlpha);\n }\n if (order.charCodeAt(0) === 97) {\n str += ' - alphabetical by rule/UDT name\\n';\n for (i = 0; i < ruleCount; i += 1) {\n str += `${i}: ${alphaArray[i]}: ${rules[alphaArray[i]].name}\\n`;\n }\n if (udtCount) {\n for (i = 0; i < udtCount; i += 1) {\n str += `${i}: ${udtAlphaArray[i]}: ${udts[udtAlphaArray[i]].name}\\n`;\n }\n }\n } else {\n str += ' - ordered by rule/UDT index\\n';\n for (i = 0; i < ruleCount; i += 1) {\n str += `${i}: ${rules[i].name}\\n`;\n }\n if (udtCount) {\n for (i = 0; i < udtCount; i += 1) {\n str += `${i}: ${udts[i].name}\\n`;\n }\n }\n }\n return str;\n };\n return showRules;\n})();\n","/* eslint-disable prefer-destructuring */\n/* eslint-disable no-plusplus */\n/* eslint-disable no-bitwise */\n/* *************************************************************************************\n * copyright: Copyright (c) 2021 Lowell D. Thomas, all rights reserved\n * license: BSD-2-Clause (https://opensource.org/licenses/BSD-2-Clause)\n * ********************************************************************************* */\n// This module contains the actual encoding and decoding algorithms.\n// Throws \"RangeError\" exceptions on characters or bytes out of range for the given encoding.\n\n'use strict;';\n\nconst { Buffer } = require('buffer');\n\n/* decoding error codes */\nconst NON_SHORTEST = 0xfffffffc;\nconst TRAILING = 0xfffffffd;\nconst RANGE = 0xfffffffe;\nconst ILL_FORMED = 0xffffffff;\n\n/* mask[n] = 2**n - 1, ie. mask[n] = n bits on. e.g. mask[6] = %b111111 */\nconst mask = [0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023];\n\n/* ascii[n] = 'HH', where 0xHH = n, eg. ascii[254] = 'FE' */\nconst ascii = [\n '00',\n '01',\n '02',\n '03',\n '04',\n '05',\n '06',\n '07',\n '08',\n '09',\n '0A',\n '0B',\n '0C',\n '0D',\n '0E',\n '0F',\n '10',\n '11',\n '12',\n '13',\n '14',\n '15',\n '16',\n '17',\n '18',\n '19',\n '1A',\n '1B',\n '1C',\n '1D',\n '1E',\n '1F',\n '20',\n '21',\n '22',\n '23',\n '24',\n '25',\n '26',\n '27',\n '28',\n '29',\n '2A',\n '2B',\n '2C',\n '2D',\n '2E',\n '2F',\n '30',\n '31',\n '32',\n '33',\n '34',\n '35',\n '36',\n '37',\n '38',\n '39',\n '3A',\n '3B',\n '3C',\n '3D',\n '3E',\n '3F',\n '40',\n '41',\n '42',\n '43',\n '44',\n '45',\n '46',\n '47',\n '48',\n '49',\n '4A',\n '4B',\n '4C',\n '4D',\n '4E',\n '4F',\n '50',\n '51',\n '52',\n '53',\n '54',\n '55',\n '56',\n '57',\n '58',\n '59',\n '5A',\n '5B',\n '5C',\n '5D',\n '5E',\n '5F',\n '60',\n '61',\n '62',\n '63',\n '64',\n '65',\n '66',\n '67',\n '68',\n '69',\n '6A',\n '6B',\n '6C',\n '6D',\n '6E',\n '6F',\n '70',\n '71',\n '72',\n '73',\n '74',\n '75',\n '76',\n '77',\n '78',\n '79',\n '7A',\n '7B',\n '7C',\n '7D',\n '7E',\n '7F',\n '80',\n '81',\n '82',\n '83',\n '84',\n '85',\n '86',\n '87',\n '88',\n '89',\n '8A',\n '8B',\n '8C',\n '8D',\n '8E',\n '8F',\n '90',\n '91',\n '92',\n '93',\n '94',\n '95',\n '96',\n '97',\n '98',\n '99',\n '9A',\n '9B',\n '9C',\n '9D',\n '9E',\n '9F',\n 'A0',\n 'A1',\n 'A2',\n 'A3',\n 'A4',\n 'A5',\n 'A6',\n 'A7',\n 'A8',\n 'A9',\n 'AA',\n 'AB',\n 'AC',\n 'AD',\n 'AE',\n 'AF',\n 'B0',\n 'B1',\n 'B2',\n 'B3',\n 'B4',\n 'B5',\n 'B6',\n 'B7',\n 'B8',\n 'B9',\n 'BA',\n 'BB',\n 'BC',\n 'BD',\n 'BE',\n 'BF',\n 'C0',\n 'C1',\n 'C2',\n 'C3',\n 'C4',\n 'C5',\n 'C6',\n 'C7',\n 'C8',\n 'C9',\n 'CA',\n 'CB',\n 'CC',\n 'CD',\n 'CE',\n 'CF',\n 'D0',\n 'D1',\n 'D2',\n 'D3',\n 'D4',\n 'D5',\n 'D6',\n 'D7',\n 'D8',\n 'D9',\n 'DA',\n 'DB',\n 'DC',\n 'DD',\n 'DE',\n 'DF',\n 'E0',\n 'E1',\n 'E2',\n 'E3',\n 'E4',\n 'E5',\n 'E6',\n 'E7',\n 'E8',\n 'E9',\n 'EA',\n 'EB',\n 'EC',\n 'ED',\n 'EE',\n 'EF',\n 'F0',\n 'F1',\n 'F2',\n 'F3',\n 'F4',\n 'F5',\n 'F6',\n 'F7',\n 'F8',\n 'F9',\n 'FA',\n 'FB',\n 'FC',\n 'FD',\n 'FE',\n 'FF',\n];\n\n/* vector of base 64 characters */\nconst base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split('');\n\n/* vector of base 64 character codes */\nconst base64codes = [];\nbase64chars.forEach((char) => {\n base64codes.push(char.charCodeAt(0));\n});\n\n// The UTF8 algorithms.\nexports.utf8 = {\n encode(chars) {\n const bytes = [];\n chars.forEach((char) => {\n if (char >= 0 && char <= 0x7f) {\n bytes.push(char);\n } else if (char <= 0x7ff) {\n bytes.push(0xc0 + ((char >> 6) & mask[5]));\n bytes.push(0x80 + (char & mask[6]));\n } else if (char < 0xd800 || (char > 0xdfff && char <= 0xffff)) {\n bytes.push(0xe0 + ((char >> 12) & mask[4]));\n bytes.push(0x80 + ((char >> 6) & mask[6]));\n bytes.push(0x80 + (char & mask[6]));\n } else if (char >= 0x10000 && char <= 0x10ffff) {\n const u = (char >> 16) & mask[5];\n bytes.push(0xf0 + (u >> 2));\n bytes.push(0x80 + ((u & mask[2]) << 4) + ((char >> 12) & mask[4]));\n bytes.push(0x80 + ((char >> 6) & mask[6]));\n bytes.push(0x80 + (char & mask[6]));\n } else {\n throw new RangeError(`utf8.encode: character out of range: char: ${char}`);\n }\n });\n return Buffer.from(bytes);\n },\n decode(buf, bom) {\n /* bytes functions return error for non-shortest forms & values out of range */\n function bytes2(b1, b2) {\n /* U+0080..U+07FF */\n /* 00000000 00000yyy yyxxxxxx | 110yyyyy 10xxxxxx */\n if ((b2 & 0xc0) !== 0x80) {\n return TRAILING;\n }\n const x = ((b1 & mask[5]) << 6) + (b2 & mask[6]);\n if (x < 0x80) {\n return NON_SHORTEST;\n }\n return x;\n }\n function bytes3(b1, b2, b3) {\n /* U+0800..U+FFFF */\n /* 00000000 zzzzyyyy yyxxxxxx | 1110zzzz 10yyyyyy 10xxxxxx */\n if ((b3 & 0xc0) !== 0x80 || (b2 & 0xc0) !== 0x80) {\n return TRAILING;\n }\n const x = ((b1 & mask[4]) << 12) + ((b2 & mask[6]) << 6) + (b3 & mask[6]);\n if (x < 0x800) {\n return NON_SHORTEST;\n }\n if (x >= 0xd800 && x <= 0xdfff) {\n return RANGE;\n }\n return x;\n }\n function bytes4(b1, b2, b3, b4) {\n /* U+10000..U+10FFFF */\n /* 000uuuuu zzzzyyyy yyxxxxxx | 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx */\n if ((b4 & 0xc0) !== 0x80 || (b3 & 0xc0) !== 0x80 || (b2 & 0xc0) !== 0x80) {\n return TRAILING;\n }\n const x =\n ((((b1 & mask[3]) << 2) + ((b2 >> 4) & mask[2])) << 16) +\n ((b2 & mask[4]) << 12) +\n ((b3 & mask[6]) << 6) +\n (b4 & mask[6]);\n if (x < 0x10000) {\n return NON_SHORTEST;\n }\n if (x > 0x10ffff) {\n return RANGE;\n }\n return x;\n }\n let c;\n let b1;\n let i1;\n let i2;\n let i3;\n let inc;\n const len = buf.length;\n let i = bom ? 3 : 0;\n const chars = [];\n while (i < len) {\n b1 = buf[i];\n c = ILL_FORMED;\n const TRUE = true;\n while (TRUE) {\n if (b1 >= 0 && b1 <= 0x7f) {\n /* U+0000..U+007F 00..7F */\n c = b1;\n inc = 1;\n break;\n }\n i1 = i + 1;\n if (i1 < len && b1 >= 0xc2 && b1 <= 0xdf) {\n /* U+0080..U+07FF C2..DF 80..BF */\n c = bytes2(b1, buf[i1]);\n inc = 2;\n break;\n }\n i2 = i + 2;\n if (i2 < len && b1 >= 0xe0 && b1 <= 0xef) {\n /* U+0800..U+FFFF */\n c = bytes3(b1, buf[i1], buf[i2]);\n inc = 3;\n break;\n }\n i3 = i + 3;\n if (i3 < len && b1 >= 0xf0 && b1 <= 0xf4) {\n /* U+10000..U+10FFFF */\n c = bytes4(b1, buf[i1], buf[i2], buf[i3]);\n inc = 4;\n break;\n }\n /* if we fall through to here, it is an ill-formed sequence */\n break;\n }\n if (c > 0x10ffff) {\n const at = `byte[${i}]`;\n if (c === ILL_FORMED) {\n throw new RangeError(`utf8.decode: ill-formed UTF8 byte sequence found at: ${at}`);\n }\n if (c === TRAILING) {\n throw new RangeError(`utf8.decode: illegal trailing byte found at: ${at}`);\n }\n if (c === RANGE) {\n throw new RangeError(`utf8.decode: code point out of range found at: ${at}`);\n }\n if (c === NON_SHORTEST) {\n throw new RangeError(`utf8.decode: non-shortest form found at: ${at}`);\n }\n throw new RangeError(`utf8.decode: unrecognized error found at: ${at}`);\n }\n chars.push(c);\n i += inc;\n }\n return chars;\n },\n};\n\n// The UTF16BE algorithms.\nexports.utf16be = {\n encode(chars) {\n const bytes = [];\n let char;\n let h;\n let l;\n for (let i = 0; i < chars.length; i += 1) {\n char = chars[i];\n if ((char >= 0 && char <= 0xd7ff) || (char >= 0xe000 && char <= 0xffff)) {\n bytes.push((char >> 8) & mask[8]);\n bytes.push(char & mask[8]);\n } else if (char >= 0x10000 && char <= 0x10ffff) {\n l = char - 0x10000;\n h = 0xd800 + (l >> 10);\n l = 0xdc00 + (l & mask[10]);\n bytes.push((h >> 8) & mask[8]);\n bytes.push(h & mask[8]);\n bytes.push((l >> 8) & mask[8]);\n bytes.push(l & mask[8]);\n } else {\n throw new RangeError(`utf16be.encode: UTF16BE value out of range: char[${i}]: ${char}`);\n }\n }\n return Buffer.from(bytes);\n },\n decode(buf, bom) {\n /* assumes caller has insured that buf is a Buffer of bytes */\n if (buf.length % 2 > 0) {\n throw new RangeError(`utf16be.decode: data length must be even multiple of 2: length: ${buf.length}`);\n }\n const chars = [];\n const len = buf.length;\n let i = bom ? 2 : 0;\n let j = 0;\n let c;\n let inc;\n let i1;\n let i3;\n let high;\n let low;\n while (i < len) {\n const TRUE = true;\n while (TRUE) {\n i1 = i + 1;\n if (i1 < len) {\n high = (buf[i] << 8) + buf[i1];\n if (high < 0xd800 || high > 0xdfff) {\n c = high;\n inc = 2;\n break;\n }\n i3 = i + 3;\n if (i3 < len) {\n low = (buf[i + 2] << 8) + buf[i3];\n if (high <= 0xdbff && low >= 0xdc00 && low <= 0xdfff) {\n c = 0x10000 + ((high - 0xd800) << 10) + (low - 0xdc00);\n inc = 4;\n break;\n }\n }\n }\n /* if we fall through to here, it is an ill-formed sequence */\n throw new RangeError(`utf16be.decode: ill-formed UTF16BE byte sequence found: byte[${i}]`);\n }\n chars[j++] = c;\n i += inc;\n }\n return chars;\n },\n};\n\n// The UTF16LE algorithms.\nexports.utf16le = {\n encode(chars) {\n const bytes = [];\n let char;\n let h;\n let l;\n for (let i = 0; i < chars.length; i += 1) {\n char = chars[i];\n if ((char >= 0 && char <= 0xd7ff) || (char >= 0xe000 && char <= 0xffff)) {\n bytes.push(char & mask[8]);\n bytes.push((char >> 8) & mask[8]);\n } else if (char >= 0x10000 && char <= 0x10ffff) {\n l = char - 0x10000;\n h = 0xd800 + (l >> 10);\n l = 0xdc00 + (l & mask[10]);\n bytes.push(h & mask[8]);\n bytes.push((h >> 8) & mask[8]);\n bytes.push(l & mask[8]);\n bytes.push((l >> 8) & mask[8]);\n } else {\n throw new RangeError(`utf16le.encode: UTF16LE value out of range: char[${i}]: ${char}`);\n }\n }\n return Buffer.from(bytes);\n },\n decode(buf, bom) {\n /* assumes caller has insured that buf is a Buffer of bytes */\n if (buf.length % 2 > 0) {\n throw new RangeError(`utf16le.decode: data length must be even multiple of 2: length: ${buf.length}`);\n }\n const chars = [];\n const len = buf.length;\n let i = bom ? 2 : 0;\n let j = 0;\n let c;\n let inc;\n let i1;\n let i3;\n let high;\n let low;\n while (i < len) {\n const TRUE = true;\n while (TRUE) {\n i1 = i + 1;\n if (i1 < len) {\n high = (buf[i1] << 8) + buf[i];\n if (high < 0xd800 || high > 0xdfff) {\n c = high;\n inc = 2;\n break;\n }\n i3 = i + 3;\n if (i3 < len) {\n low = (buf[i3] << 8) + buf[i + 2];\n if (high <= 0xdbff && low >= 0xdc00 && low <= 0xdfff) {\n c = 0x10000 + ((high - 0xd800) << 10) + (low - 0xdc00);\n inc = 4;\n break;\n }\n }\n }\n /* if we fall through to here, it is an ill-formed sequence */\n throw new RangeError(`utf16le.decode: ill-formed UTF16LE byte sequence found: byte[${i}]`);\n }\n chars[j++] = c;\n i += inc;\n }\n return chars;\n },\n};\n\n// The UTF32BE algorithms.\nexports.utf32be = {\n encode(chars) {\n const buf = Buffer.alloc(chars.length * 4);\n let i = 0;\n chars.forEach((char) => {\n if ((char >= 0xd800 && char <= 0xdfff) || char > 0x10ffff) {\n throw new RangeError(`utf32be.encode: UTF32BE character code out of range: char[${i / 4}]: ${char}`);\n }\n buf[i++] = (char >> 24) & mask[8];\n buf[i++] = (char >> 16) & mask[8];\n buf[i++] = (char >> 8) & mask[8];\n buf[i++] = char & mask[8];\n });\n return buf;\n },\n decode(buf, bom) {\n /* caller to insure buf is a Buffer of bytes */\n if (buf.length % 4 > 0) {\n throw new RangeError(`utf32be.decode: UTF32BE byte length must be even multiple of 4: length: ${buf.length}`);\n }\n const chars = [];\n let i = bom ? 4 : 0;\n for (; i < buf.length; i += 4) {\n const char = (buf[i] << 24) + (buf[i + 1] << 16) + (buf[i + 2] << 8) + buf[i + 3];\n if ((char >= 0xd800 && char <= 0xdfff) || char > 0x10ffff) {\n throw new RangeError(`utf32be.decode: UTF32BE character code out of range: char[${i / 4}]: ${char}`);\n }\n chars.push(char);\n }\n return chars;\n },\n};\n\n// The UTF32LE algorithms.\nexports.utf32le = {\n encode(chars) {\n const buf = Buffer.alloc(chars.length * 4);\n let i = 0;\n chars.forEach((char) => {\n if ((char >= 0xd800 && char <= 0xdfff) || char > 0x10ffff) {\n throw new RangeError(`utf32le.encode: UTF32LE character code out of range: char[${i / 4}]: ${char}`);\n }\n buf[i++] = char & mask[8];\n buf[i++] = (char >> 8) & mask[8];\n buf[i++] = (char >> 16) & mask[8];\n buf[i++] = (char >> 24) & mask[8];\n });\n return buf;\n },\n decode(buf, bom) {\n /* caller to insure buf is a Buffer of bytes */\n if (buf.length % 4 > 0) {\n throw new RangeError(`utf32be.decode: UTF32LE byte length must be even multiple of 4: length: ${buf.length}`);\n }\n const chars = [];\n let i = bom ? 4 : 0;\n for (; i < buf.length; i += 4) {\n const char = (buf[i + 3] << 24) + (buf[i + 2] << 16) + (buf[i + 1] << 8) + buf[i];\n if ((char >= 0xd800 && char <= 0xdfff) || char > 0x10ffff) {\n throw new RangeError(`utf32le.encode: UTF32LE character code out of range: char[${i / 4}]: ${char}`);\n }\n chars.push(char);\n }\n return chars;\n },\n};\n\n// The UINT7 algorithms. ASCII or 7-bit unsigned integers.\nexports.uint7 = {\n encode(chars) {\n const buf = Buffer.alloc(chars.length);\n for (let i = 0; i < chars.length; i += 1) {\n if (chars[i] > 0x7f) {\n throw new RangeError(`uint7.encode: UINT7 character code out of range: char[${i}]: ${chars[i]}`);\n }\n buf[i] = chars[i];\n }\n return buf;\n },\n decode(buf) {\n const chars = [];\n for (let i = 0; i < buf.length; i += 1) {\n if (buf[i] > 0x7f) {\n throw new RangeError(`uint7.decode: UINT7 character code out of range: byte[${i}]: ${buf[i]}`);\n }\n chars[i] = buf[i];\n }\n return chars;\n },\n};\n\n// The UINT8 algorithms. BINARY, Latin 1 or 8-bit unsigned integers.\nexports.uint8 = {\n encode(chars) {\n const buf = Buffer.alloc(chars.length);\n for (let i = 0; i < chars.length; i += 1) {\n if (chars[i] > 0xff) {\n throw new RangeError(`uint8.encode: UINT8 character code out of range: char[${i}]: ${chars[i]}`);\n }\n buf[i] = chars[i];\n }\n return buf;\n },\n decode(buf) {\n const chars = [];\n for (let i = 0; i < buf.length; i += 1) {\n chars[i] = buf[i];\n }\n return chars;\n },\n};\n\n// The UINT16BE algorithms. Big-endian 16-bit unsigned integers.\nexports.uint16be = {\n encode(chars) {\n const buf = Buffer.alloc(chars.length * 2);\n let i = 0;\n chars.forEach((char) => {\n if (char > 0xffff) {\n throw new RangeError(`uint16be.encode: UINT16BE character code out of range: char[${i / 2}]: ${char}`);\n }\n buf[i++] = (char >> 8) & mask[8];\n buf[i++] = char & mask[8];\n });\n return buf;\n },\n decode(buf) {\n if (buf.length % 2 > 0) {\n throw new RangeError(`uint16be.decode: UINT16BE byte length must be even multiple of 2: length: ${buf.length}`);\n }\n const chars = [];\n for (let i = 0; i < buf.length; i += 2) {\n chars.push((buf[i] << 8) + buf[i + 1]);\n }\n return chars;\n },\n};\n\n// The UINT16LE algorithms. Little-endian 16-bit unsigned integers.\nexports.uint16le = {\n encode(chars) {\n const buf = Buffer.alloc(chars.length * 2);\n let i = 0;\n chars.forEach((char) => {\n if (char > 0xffff) {\n throw new RangeError(`uint16le.encode: UINT16LE character code out of range: char[${i / 2}]: ${char}`);\n }\n buf[i++] = char & mask[8];\n buf[i++] = (char >> 8) & mask[8];\n });\n return buf;\n },\n decode(buf) {\n if (buf.length % 2 > 0) {\n throw new RangeError(`uint16le.decode: UINT16LE byte length must be even multiple of 2: length: ${buf.length}`);\n }\n const chars = [];\n for (let i = 0; i < buf.length; i += 2) {\n chars.push((buf[i + 1] << 8) + buf[i]);\n }\n return chars;\n },\n};\n\n// The UINT32BE algorithms. Big-endian 32-bit unsigned integers.\nexports.uint32be = {\n encode(chars) {\n const buf = Buffer.alloc(chars.length * 4);\n let i = 0;\n chars.forEach((char) => {\n buf[i++] = (char >> 24) & mask[8];\n buf[i++] = (char >> 16) & mask[8];\n buf[i++] = (char >> 8) & mask[8];\n buf[i++] = char & mask[8];\n });\n return buf;\n },\n decode(buf) {\n if (buf.length % 4 > 0) {\n throw new RangeError(`uint32be.decode: UINT32BE byte length must be even multiple of 4: length: ${buf.length}`);\n }\n const chars = [];\n for (let i = 0; i < buf.length; i += 4) {\n chars.push((buf[i] << 24) + (buf[i + 1] << 16) + (buf[i + 2] << 8) + buf[i + 3]);\n }\n return chars;\n },\n};\n\n// The UINT32LE algorithms. Little-endian 32-bit unsigned integers.\nexports.uint32le = {\n encode(chars) {\n const buf = Buffer.alloc(chars.length * 4);\n let i = 0;\n chars.forEach((char) => {\n buf[i++] = char & mask[8];\n buf[i++] = (char >> 8) & mask[8];\n buf[i++] = (char >> 16) & mask[8];\n buf[i++] = (char >> 24) & mask[8];\n });\n return buf;\n },\n decode(buf) {\n /* caller to insure buf is a Buffer of bytes */\n if (buf.length % 4 > 0) {\n throw new RangeError(`uint32le.decode: UINT32LE byte length must be even multiple of 4: length: ${buf.length}`);\n }\n const chars = [];\n for (let i = 0; i < buf.length; i += 4) {\n chars.push((buf[i + 3] << 24) + (buf[i + 2] << 16) + (buf[i + 1] << 8) + buf[i]);\n }\n return chars;\n },\n};\n\n// The STRING algorithms. Converts JavaScript strings to Array of 32-bit integers and vice versa.\n// Uses the node.js Buffer's native \"utf16le\" capabilites.\nexports.string = {\n encode(chars) {\n return exports.utf16le.encode(chars).toString('utf16le');\n },\n decode(str) {\n return exports.utf16le.decode(Buffer.from(str, 'utf16le'), 0);\n },\n};\n\n// The ESCAPED algorithms.\n// Note that ESCAPED format contains only ASCII characters.\n// The characters are always in the form of a Buffer of bytes.\nexports.escaped = {\n // Encodes an Array of 32-bit integers into ESCAPED format.\n encode(chars) {\n const bytes = [];\n for (let i = 0; i < chars.length; i += 1) {\n const char = chars[i];\n if (char === 96) {\n bytes.push(char);\n bytes.push(char);\n } else if (char === 10) {\n bytes.push(char);\n } else if (char >= 32 && char <= 126) {\n bytes.push(char);\n } else {\n let str = '';\n if (char >= 0 && char <= 31) {\n str += `\\`x${ascii[char]}`;\n } else if (char >= 127 && char <= 255) {\n str += `\\`x${ascii[char]}`;\n } else if (char >= 0x100 && char <= 0xffff) {\n str += `\\`u${ascii[(char >> 8) & mask[8]]}${ascii[char & mask[8]]}`;\n } else if (char >= 0x10000 && char <= 0xffffffff) {\n str += '`u{';\n const digit = (char >> 24) & mask[8];\n if (digit > 0) {\n str += ascii[digit];\n }\n str += `${ascii[(char >> 16) & mask[8]] + ascii[(char >> 8) & mask[8]] + ascii[char & mask[8]]}}`;\n } else {\n throw new Error('escape.encode(char): char > 0xffffffff not allowed');\n }\n const buf = Buffer.from(str);\n buf.forEach((b) => {\n bytes.push(b);\n });\n }\n }\n return Buffer.from(bytes);\n },\n // Decodes ESCAPED format from a Buffer of bytes to an Array of 32-bit integers.\n decode(buf) {\n function isHex(hex) {\n if ((hex >= 48 && hex <= 57) || (hex >= 65 && hex <= 70) || (hex >= 97 && hex <= 102)) {\n return true;\n }\n return false;\n }\n function getx(i, len, bufArg) {\n const ret = { char: null, nexti: i + 2, error: true };\n if (i + 1 < len) {\n if (isHex(bufArg[i]) && isHex(bufArg[i + 1])) {\n const str = String.fromCodePoint(bufArg[i], bufArg[i + 1]);\n ret.char = parseInt(str, 16);\n if (!Number.isNaN(ret.char)) {\n ret.error = false;\n }\n }\n }\n return ret;\n }\n function getu(i, len, bufArg) {\n const ret = { char: null, nexti: i + 4, error: true };\n if (i + 3 < len) {\n if (isHex(bufArg[i]) && isHex(bufArg[i + 1]) && isHex(bufArg[i + 2]) && isHex(bufArg[i + 3])) {\n const str = String.fromCodePoint(bufArg[i], bufArg[i + 1], bufArg[i + 2], bufArg[i + 3]);\n ret.char = parseInt(str, 16);\n if (!Number.isNaN(ret.char)) {\n ret.error = false;\n }\n }\n }\n return ret;\n }\n function getU(i, len, bufArg) {\n const ret = { char: null, nexti: i + 4, error: true };\n let str = '';\n while (i < len && isHex(bufArg[i])) {\n str += String.fromCodePoint(bufArg[i]);\n // eslint-disable-next-line no-param-reassign\n i += 1;\n }\n ret.char = parseInt(str, 16);\n if (bufArg[i] === 125 && !Number.isNaN(ret.char)) {\n ret.error = false;\n }\n ret.nexti = i + 1;\n return ret;\n }\n const chars = [];\n const len = buf.length;\n let i1;\n let ret;\n let error;\n let i = 0;\n while (i < len) {\n const TRUE = true;\n while (TRUE) {\n error = true;\n if (buf[i] !== 96) {\n /* unescaped character */\n chars.push(buf[i]);\n i += 1;\n error = false;\n break;\n }\n i1 = i + 1;\n if (i1 >= len) {\n break;\n }\n if (buf[i1] === 96) {\n /* escaped grave accent */\n chars.push(96);\n i += 2;\n error = false;\n break;\n }\n if (buf[i1] === 120) {\n ret = getx(i1 + 1, len, buf);\n if (ret.error) {\n break;\n }\n /* escaped hex */\n chars.push(ret.char);\n i = ret.nexti;\n error = false;\n break;\n }\n if (buf[i1] === 117) {\n if (buf[i1 + 1] === 123) {\n ret = getU(i1 + 2, len, buf);\n if (ret.error) {\n break;\n }\n /* escaped utf-32 */\n chars.push(ret.char);\n i = ret.nexti;\n error = false;\n break;\n }\n ret = getu(i1 + 1, len, buf);\n if (ret.error) {\n break;\n }\n /* escaped utf-16 */\n chars.push(ret.char);\n i = ret.nexti;\n error = false;\n break;\n }\n break;\n }\n if (error) {\n throw new Error(`escaped.decode: ill-formed escape sequence at buf[${i}]`);\n }\n }\n return chars;\n },\n};\n\n// The line end conversion algorigthms.\nconst CR = 13;\nconst LF = 10;\nexports.lineEnds = {\n crlf(chars) {\n const lfchars = [];\n let i = 0;\n while (i < chars.length) {\n switch (chars[i]) {\n case CR:\n if (i + 1 < chars.length && chars[i + 1] === LF) {\n i += 2;\n } else {\n i += 1;\n }\n lfchars.push(CR);\n lfchars.push(LF);\n break;\n case LF:\n lfchars.push(CR);\n lfchars.push(LF);\n i += 1;\n break;\n default:\n lfchars.push(chars[i]);\n i += 1;\n break;\n }\n }\n if (lfchars.length > 0 && lfchars[lfchars.length - 1] !== LF) {\n lfchars.push(CR);\n lfchars.push(LF);\n }\n return lfchars;\n },\n lf(chars) {\n const lfchars = [];\n let i = 0;\n while (i < chars.length) {\n switch (chars[i]) {\n case CR:\n if (i + 1 < chars.length && chars[i + 1] === LF) {\n i += 2;\n } else {\n i += 1;\n }\n lfchars.push(LF);\n break;\n case LF:\n lfchars.push(LF);\n i += 1;\n break;\n default:\n lfchars.push(chars[i]);\n i += 1;\n break;\n }\n }\n if (lfchars.length > 0 && lfchars[lfchars.length - 1] !== LF) {\n lfchars.push(LF);\n }\n return lfchars;\n },\n};\n\n// The base 64 algorithms.\nexports.base64 = {\n encode(buf) {\n if (buf.length === 0) {\n return Buffer.alloc(0);\n }\n let i;\n let j;\n let n;\n let tail = buf.length % 3;\n tail = tail > 0 ? 3 - tail : 0;\n let units = (buf.length + tail) / 3;\n const base64 = Buffer.alloc(units * 4);\n if (tail > 0) {\n units -= 1;\n }\n i = 0;\n j = 0;\n for (let u = 0; u < units; u += 1) {\n n = buf[i++] << 16;\n n += buf[i++] << 8;\n n += buf[i++];\n base64[j++] = base64codes[(n >> 18) & mask[6]];\n base64[j++] = base64codes[(n >> 12) & mask[6]];\n base64[j++] = base64codes[(n >> 6) & mask[6]];\n base64[j++] = base64codes[n & mask[6]];\n }\n if (tail === 0) {\n return base64;\n }\n if (tail === 1) {\n n = buf[i++] << 16;\n n += buf[i] << 8;\n base64[j++] = base64codes[(n >> 18) & mask[6]];\n base64[j++] = base64codes[(n >> 12) & mask[6]];\n base64[j++] = base64codes[(n >> 6) & mask[6]];\n base64[j] = base64codes[64];\n return base64;\n }\n if (tail === 2) {\n n = buf[i] << 16;\n base64[j++] = base64codes[(n >> 18) & mask[6]];\n base64[j++] = base64codes[(n >> 12) & mask[6]];\n base64[j++] = base64codes[64];\n base64[j] = base64codes[64];\n return base64;\n }\n return undefined;\n },\n decode(codes) {\n /* remove white space and ctrl characters, validate & translate characters */\n function validate(buf) {\n const chars = [];\n let tail = 0;\n for (let i = 0; i < buf.length; i += 1) {\n const char = buf[i];\n const TRUE = true;\n while (TRUE) {\n if (char === 32 || char === 9 || char === 10 || char === 13) {\n break;\n }\n if (char >= 65 && char <= 90) {\n chars.push(char - 65);\n break;\n }\n if (char >= 97 && char <= 122) {\n chars.push(char - 71);\n break;\n }\n if (char >= 48 && char <= 57) {\n chars.push(char + 4);\n break;\n }\n if (char === 43) {\n chars.push(62);\n break;\n }\n if (char === 47) {\n chars.push(63);\n break;\n }\n if (char === 61) {\n chars.push(64);\n tail += 1;\n break;\n }\n /* invalid character */\n throw new RangeError(`base64.decode: invalid character buf[${i}]: ${char}`);\n }\n }\n /* validate length */\n if (chars.length % 4 > 0) {\n throw new RangeError(`base64.decode: string length not integral multiple of 4: ${chars.length}`);\n }\n /* validate tail */\n switch (tail) {\n case 0:\n break;\n case 1:\n if (chars[chars.length - 1] !== 64) {\n throw new RangeError('base64.decode: one tail character found: not last character');\n }\n break;\n case 2:\n if (chars[chars.length - 1] !== 64 || chars[chars.length - 2] !== 64) {\n throw new RangeError('base64.decode: two tail characters found: not last characters');\n }\n break;\n default:\n throw new RangeError(`base64.decode: more than two tail characters found: ${tail}`);\n }\n return { tail, buf: Buffer.from(chars) };\n }\n\n if (codes.length === 0) {\n return Buffer.alloc(0);\n }\n const val = validate(codes);\n const { tail } = val;\n const base64 = val.buf;\n let i;\n let j;\n let n;\n let units = base64.length / 4;\n const buf = Buffer.alloc(units * 3 - tail);\n if (tail > 0) {\n units -= 1;\n }\n j = 0;\n i = 0;\n for (let u = 0; u < units; u += 1) {\n n = base64[i++] << 18;\n n += base64[i++] << 12;\n n += base64[i++] << 6;\n n += base64[i++];\n buf[j++] = (n >> 16) & mask[8];\n buf[j++] = (n >> 8) & mask[8];\n buf[j++] = n & mask[8];\n }\n if (tail === 1) {\n n = base64[i++] << 18;\n n += base64[i++] << 12;\n n += base64[i] << 6;\n buf[j++] = (n >> 16) & mask[8];\n buf[j] = (n >> 8) & mask[8];\n }\n if (tail === 2) {\n n = base64[i++] << 18;\n n += base64[i++] << 12;\n buf[j] = (n >> 16) & mask[8];\n }\n return buf;\n },\n // Converts a base 64 Buffer of bytes to a JavaScript string with line breaks.\n toString(buf) {\n if (buf.length % 4 > 0) {\n throw new RangeError(`base64.toString: input buffer length not multiple of 4: ${buf.length}`);\n }\n let str = '';\n let lineLen = 0;\n function buildLine(c1, c2, c3, c4) {\n switch (lineLen) {\n case 76:\n str += `\\r\\n${c1}${c2}${c3}${c4}`;\n lineLen = 4;\n break;\n case 75:\n str += `${c1}\\r\\n${c2}${c3}${c4}`;\n lineLen = 3;\n break;\n case 74:\n str += `${c1 + c2}\\r\\n${c3}${c4}`;\n lineLen = 2;\n break;\n case 73:\n str += `${c1 + c2 + c3}\\r\\n${c4}`;\n lineLen = 1;\n break;\n default:\n str += c1 + c2 + c3 + c4;\n lineLen += 4;\n break;\n }\n }\n function validate(c) {\n if (c >= 65 && c <= 90) {\n return true;\n }\n if (c >= 97 && c <= 122) {\n return true;\n }\n if (c >= 48 && c <= 57) {\n return true;\n }\n if (c === 43) {\n return true;\n }\n if (c === 47) {\n return true;\n }\n if (c === 61) {\n return true;\n }\n return false;\n }\n for (let i = 0; i < buf.length; i += 4) {\n for (let j = i; j < i + 4; j += 1) {\n if (!validate(buf[j])) {\n throw new RangeError(`base64.toString: buf[${j}]: ${buf[j]} : not valid base64 character code`);\n }\n }\n buildLine(\n String.fromCharCode(buf[i]),\n String.fromCharCode(buf[i + 1]),\n String.fromCharCode(buf[i + 2]),\n String.fromCharCode(buf[i + 3])\n );\n }\n return str;\n },\n};\n","/* eslint-disable guard-for-in */\n/* eslint-disable no-restricted-syntax */\n/* *************************************************************************************\n * copyright: Copyright (c) 2021 Lowell D. Thomas, all rights reserved\n * license: BSD-2-Clause (https://opensource.org/licenses/BSD-2-Clause)\n * ********************************************************************************* */\n// This module is used by the parser to build an [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) (AST).\n// The AST can be thought of as a subset of the full parse tree.\n// Each node of the AST holds the phrase that was matched at the corresponding, named parse tree node.\n// It is built as the parser successfully matches phrases to the rule names\n// (`RNM` operators) and `UDT`s as it parses an input string.\n// The user controls which `RNM` or `UDT` names to keep on the AST.\n// The user can also associate callback functions with some or all of the retained\n// AST nodes to be used to translate the node phrases. That is, associate semantic\n// actions to the matched phrases.\n// Translating the AST rather that attempting to apply semantic actions during\n// the parsing process, has the advantage that there is no backtracking and that the phrases\n// are known while traversing down tree as will as up.\n//\n// Let `ast` be an `ast.js` object. To identify a node to be kept on the AST:\n// ```\n// ast.callbacks[\"rulename\"] = true; (all nodes default to false)\n// ```\n// To associate a callback function with a node:\n// ```\n// ast.callbacks[\"rulename\"] = fn\n// ```\n// `rulename` is any `RNM` or `UDT` name defined by the associated grammar\n// and `fn` is a user-written callback function.\n// (See [`apg-examples`](https://github.com/ldthomas/apg-js2-examples/tree/master/ast) for examples of how to create an AST,\n// define the nodes and callback functions and attach it to a parser.)\nmodule.exports = function exportsAst() {\n const id = require('./identifiers');\n const utils = require('./utilities');\n\n const thisFileName = 'ast.js: ';\n const that = this;\n let rules = null;\n let udts = null;\n let chars = null;\n let nodeCount = 0;\n const nodesDefined = [];\n const nodeCallbacks = [];\n const stack = [];\n const records = [];\n this.callbacks = [];\n this.astObject = 'astObject';\n /* called by the parser to initialize the AST with the rules, UDTs and the input characters */\n this.init = function init(rulesIn, udtsIn, charsIn) {\n stack.length = 0;\n records.length = 0;\n nodesDefined.length = 0;\n nodeCount = 0;\n rules = rulesIn;\n udts = udtsIn;\n chars = charsIn;\n let i;\n const list = [];\n for (i = 0; i < rules.length; i += 1) {\n list.push(rules[i].lower);\n }\n for (i = 0; i < udts.length; i += 1) {\n list.push(udts[i].lower);\n }\n nodeCount = rules.length + udts.length;\n for (i = 0; i < nodeCount; i += 1) {\n nodesDefined[i] = false;\n nodeCallbacks[i] = null;\n }\n for (const index in that.callbacks) {\n const lower = index.toLowerCase();\n i = list.indexOf(lower);\n if (i < 0) {\n throw new Error(`${thisFileName}init: node '${index}' not a rule or udt name`);\n }\n if (typeof that.callbacks[index] === 'function') {\n nodesDefined[i] = true;\n nodeCallbacks[i] = that.callbacks[index];\n }\n if (that.callbacks[index] === true) {\n nodesDefined[i] = true;\n }\n }\n };\n /* AST node definitions - called by the parser's `RNM` operator */\n this.ruleDefined = function ruleDefined(index) {\n return nodesDefined[index] !== false;\n };\n /* AST node definitions - called by the parser's `UDT` operator */\n this.udtDefined = function udtDefined(index) {\n return nodesDefined[rules.length + index] !== false;\n };\n /* called by the parser's `RNM` & `UDT` operators */\n /* builds a record for the downward traversal of the node */\n this.down = function down(callbackIndex, name) {\n const thisIndex = records.length;\n stack.push(thisIndex);\n records.push({\n name,\n thisIndex,\n thatIndex: null,\n state: id.SEM_PRE,\n callbackIndex,\n phraseIndex: null,\n phraseLength: null,\n stack: stack.length,\n });\n return thisIndex;\n };\n /* called by the parser's `RNM` & `UDT` operators */\n /* builds a record for the upward traversal of the node */\n this.up = function up(callbackIndex, name, phraseIndex, phraseLength) {\n const thisIndex = records.length;\n const thatIndex = stack.pop();\n records.push({\n name,\n thisIndex,\n thatIndex,\n state: id.SEM_POST,\n callbackIndex,\n phraseIndex,\n phraseLength,\n stack: stack.length,\n });\n records[thatIndex].thatIndex = thisIndex;\n records[thatIndex].phraseIndex = phraseIndex;\n records[thatIndex].phraseLength = phraseLength;\n return thisIndex;\n };\n // Called by the user to translate the AST.\n // Translate means to associate or apply some semantic action to the\n // phrases that were syntactically matched to the AST nodes according\n // to the defining grammar.\n // ```\n // data - optional user-defined data\n // passed to the callback functions by the translator\n // ```\n this.translate = function translate(data) {\n let ret;\n let callback;\n let record;\n for (let i = 0; i < records.length; i += 1) {\n record = records[i];\n callback = nodeCallbacks[record.callbackIndex];\n if (record.state === id.SEM_PRE) {\n if (callback !== null) {\n ret = callback(id.SEM_PRE, chars, record.phraseIndex, record.phraseLength, data);\n if (ret === id.SEM_SKIP) {\n i = record.thatIndex;\n }\n }\n } else if (callback !== null) {\n callback(id.SEM_POST, chars, record.phraseIndex, record.phraseLength, data);\n }\n }\n };\n /* called by the parser to reset the length of the records array */\n /* necessary on backtracking */\n this.setLength = function setLength(length) {\n records.length = length;\n if (length > 0) {\n stack.length = records[length - 1].stack;\n } else {\n stack.length = 0;\n }\n };\n /* called by the parser to get the length of the records array */\n this.getLength = function getLength() {\n return records.length;\n };\n /* helper for XML display */\n function indent(n) {\n let ret = '';\n for (let i = 0; i < n; i += 1) {\n ret += ' ';\n }\n return ret;\n }\n // Generate an `XML` version of the AST.\n // Useful if you want to use a special or favorite XML parser to translate the\n // AST.\n // ```\n // mode - the display mode of the captured phrases\n // - default mode is \"ascii\"\n // - can be: \"ascii\"\n // \"decimal\"\n // \"hexadecimal\"\n // \"unicode\"\n // ```\n this.toXml = function toSml(modeArg) {\n let display = utils.charsToDec;\n let caption = 'decimal integer character codes';\n if (typeof modeArg === 'string' && modeArg.length >= 3) {\n const mode = modeArg.slice(0, 3).toLowerCase();\n if (mode === 'asc') {\n display = utils.charsToAscii;\n caption = 'ASCII for printing characters, hex for non-printing';\n } else if (mode === 'hex') {\n display = utils.charsToHex;\n caption = 'hexadecimal integer character codes';\n } else if (mode === 'uni') {\n display = utils.charsToUnicode;\n caption = 'Unicode UTF-32 integer character codes';\n }\n }\n let xml = '';\n let depth = 0;\n xml += '<?xml version=\"1.0\" encoding=\"utf-8\"?>\\n';\n xml += `<root nodes=\"${records.length / 2}\" characters=\"${chars.length}\">\\n`;\n xml += `<!-- input string, ${caption} -->\\n`;\n xml += indent(depth + 2);\n xml += display(chars);\n xml += '\\n';\n records.forEach((rec) => {\n if (rec.state === id.SEM_PRE) {\n depth += 1;\n xml += indent(depth);\n xml += `<node name=\"${rec.name}\" index=\"${rec.phraseIndex}\" length=\"${rec.phraseLength}\">\\n`;\n xml += indent(depth + 2);\n xml += display(chars, rec.phraseIndex, rec.phraseLength);\n xml += '\\n';\n } else {\n xml += indent(depth);\n xml += `</node><!-- name=\"${rec.name}\" -->\\n`;\n depth -= 1;\n }\n });\n\n xml += '</root>\\n';\n return xml;\n };\n /* generate a JavaScript object version of the AST */\n /* for the phrase-matching engine apg-exp */\n this.phrases = function phrases() {\n const obj = {};\n let i;\n let record;\n for (i = 0; i < records.length; i += 1) {\n record = records[i];\n if (record.state === id.SEM_PRE) {\n if (!Array.isArray(obj[record.name])) {\n obj[record.name] = [];\n }\n obj[record.name].push({\n index: record.phraseIndex,\n length: record.phraseLength,\n });\n }\n }\n return obj;\n };\n};\n","/* *************************************************************************************\n * copyright: Copyright (c) 2021 Lowell D. Thomas, all rights reserved\n * license: BSD-2-Clause (https://opensource.org/licenses/BSD-2-Clause)\n * ********************************************************************************* */\n// This module is the constructor for the statistics gathering object.\n// The statistics are nothing more than keeping a count of the\n// number of times each node in the parse tree is traversed.\n//\n// Counts are collected for each of the individual types of operators.\n// Additionally, counts are collected for each of the individually named\n// `RNM` and `UDT` operators.\nmodule.exports = function statsFunc() {\n const id = require('./identifiers');\n const utils = require('./utilities');\n const style = require('./style');\n\n const thisFileName = 'stats.js: ';\n let rules = [];\n let udts = [];\n const stats = [];\n let totals;\n const ruleStats = [];\n const udtStats = [];\n this.statsObject = 'statsObject';\n const nameId = 'stats';\n /* `Array.sort()` callback function for sorting `RNM` and `UDT` operators alphabetically by name. */\n const sortAlpha = function sortAlpha(lhs, rhs) {\n if (lhs.lower < rhs.lower) {\n return -1;\n }\n if (lhs.lower > rhs.lower) {\n return 1;\n }\n return 0;\n };\n /* `Array.sort()` callback function for sorting `RNM` and `UDT` operators by hit count. */\n const sortHits = function sortHits(lhs, rhs) {\n if (lhs.total < rhs.total) {\n return 1;\n }\n if (lhs.total > rhs.total) {\n return -1;\n }\n return sortAlpha(lhs, rhs);\n };\n /* `Array.sort()` callback function for sorting `RNM` and `UDT` operators by index */\n /* (in the order in which they appear in the SABNF grammar). */\n const sortIndex = function sortIndex(lhs, rhs) {\n if (lhs.index < rhs.index) {\n return -1;\n }\n if (lhs.index > rhs.index) {\n return 1;\n }\n return 0;\n };\n const EmptyStat = function EmptyStat() {\n this.empty = 0;\n this.match = 0;\n this.nomatch = 0;\n this.total = 0;\n };\n /* Zero out all stats */\n const clear = function clear() {\n stats.length = 0;\n totals = new EmptyStat();\n stats[id.ALT] = new EmptyStat();\n stats[id.CAT] = new EmptyStat();\n stats[id.REP] = new EmptyStat();\n stats[id.RNM] = new EmptyStat();\n stats[id.TRG] = new EmptyStat();\n stats[id.TBS] = new EmptyStat();\n stats[id.TLS] = new EmptyStat();\n stats[id.UDT] = new EmptyStat();\n stats[id.AND] = new EmptyStat();\n stats[id.NOT] = new EmptyStat();\n stats[id.BKR] = new EmptyStat();\n stats[id.BKA] = new EmptyStat();\n stats[id.BKN] = new EmptyStat();\n stats[id.ABG] = new EmptyStat();\n stats[id.AEN] = new EmptyStat();\n ruleStats.length = 0;\n for (let i = 0; i < rules.length; i += 1) {\n ruleStats.push({\n empty: 0,\n match: 0,\n nomatch: 0,\n total: 0,\n name: rules[i].name,\n lower: rules[i].lower,\n index: rules[i].index,\n });\n }\n if (udts.length > 0) {\n udtStats.length = 0;\n for (let i = 0; i < udts.length; i += 1) {\n udtStats.push({\n empty: 0,\n match: 0,\n nomatch: 0,\n total: 0,\n name: udts[i].name,\n lower: udts[i].lower,\n index: udts[i].index,\n });\n }\n }\n };\n /* increment the designated operator hit count by one */\n const incStat = function incStat(stat, state) {\n stat.total += 1;\n switch (state) {\n case id.EMPTY:\n stat.empty += 1;\n break;\n case id.MATCH:\n stat.match += 1;\n break;\n case id.NOMATCH:\n stat.nomatch += 1;\n break;\n default:\n throw new Error(`${thisFileName}collect(): incStat(): unrecognized state: ${state}`);\n }\n };\n /* helper for toHtml() */\n const displayRow = function displayRow(name, stat) {\n let html = '';\n html += '<tr>';\n html += `<td class=\"${style.CLASS_ACTIVE}\">${name}</td>`;\n html += `<td class=\"${style.CLASS_EMPTY}\">${stat.empty}</td>`;\n html += `<td class=\"${style.CLASS_MATCH}\">${stat.match}</td>`;\n html += `<td class=\"${style.CLASS_NOMATCH}\">${stat.nomatch}</td>`;\n html += `<td class=\"${style.CLASS_ACTIVE}\">${stat.total}</td>`;\n html += '</tr>\\n';\n return html;\n };\n const displayOpsOnly = function displayOpsOnly() {\n let html = '';\n html += displayRow('ALT', stats[id.ALT]);\n html += displayRow('CAT', stats[id.CAT]);\n html += displayRow('REP', stats[id.REP]);\n html += displayRow('RNM', stats[id.RNM]);\n html += displayRow('TRG', stats[id.TRG]);\n html += displayRow('TBS', stats[id.TBS]);\n html += displayRow('TLS', stats[id.TLS]);\n html += displayRow('UDT', stats[id.UDT]);\n html += displayRow('AND', stats[id.AND]);\n html += displayRow('NOT', stats[id.NOT]);\n html += displayRow('BKR', stats[id.BKR]);\n html += displayRow('BKA', stats[id.BKA]);\n html += displayRow('BKN', stats[id.BKN]);\n html += displayRow('ABG', stats[id.ABG]);\n html += displayRow('AEN', stats[id.AEN]);\n html += displayRow('totals', totals);\n return html;\n };\n /* helper for toHtml() */\n const displayRules = function displayRules() {\n let html = '';\n html += '<tr><th></th><th></th><th></th><th></th><th></th></tr>\\n';\n html += '<tr><th>rules</th><th></th><th></th><th></th><th></th></tr>\\n';\n for (let i = 0; i < rules.length; i += 1) {\n if (ruleStats[i].total > 0) {\n html += '<tr>';\n html += `<td class=\"${style.CLASS_ACTIVE}\">${ruleStats[i].name}</td>`;\n html += `<td class=\"${style.CLASS_EMPTY}\">${ruleStats[i].empty}</td>`;\n html += `<td class=\"${style.CLASS_MATCH}\">${ruleStats[i].match}</td>`;\n html += `<td class=\"${style.CLASS_NOMATCH}\">${ruleStats[i].nomatch}</td>`;\n html += `<td class=\"${style.CLASS_ACTIVE}\">${ruleStats[i].total}</td>`;\n html += '</tr>\\n';\n }\n }\n if (udts.length > 0) {\n html += '<tr><th></th><th></th><th></th><th></th><th></th></tr>\\n';\n html += '<tr><th>udts</th><th></th><th></th><th></th><th></th></tr>\\n';\n for (let i = 0; i < udts.length; i += 1) {\n if (udtStats[i].total > 0) {\n html += '<tr>';\n html += `<td class=\"${style.CLASS_ACTIVE}\">${udtStats[i].name}</td>`;\n html += `<td class=\"${style.CLASS_EMPTY}\">${udtStats[i].empty}</td>`;\n html += `<td class=\"${style.CLASS_MATCH}\">${udtStats[i].match}</td>`;\n html += `<td class=\"${style.CLASS_NOMATCH}\">${udtStats[i].nomatch}</td>`;\n html += `<td class=\"${style.CLASS_ACTIVE}\">${udtStats[i].total}</td>`;\n html += '</tr>\\n';\n }\n }\n }\n return html;\n };\n /* called only by the parser to validate a stats object */\n this.validate = function validate(name) {\n let ret = false;\n if (typeof name === 'string' && nameId === name) {\n ret = true;\n }\n return ret;\n };\n /* no verification of input - only called by parser() */\n this.init = function init(inputRules, inputUdts) {\n rules = inputRules;\n udts = inputUdts;\n clear();\n };\n /* This function is the main interaction with the parser. */\n /* The parser calls it after each node has been traversed. */\n this.collect = function collect(op, result) {\n incStat(totals, result.state, result.phraseLength);\n incStat(stats[op.type], result.state, result.phraseLength);\n if (op.type === id.RNM) {\n incStat(ruleStats[op.index], result.state, result.phraseLength);\n }\n if (op.type === id.UDT) {\n incStat(udtStats[op.index], result.state, result.phraseLength);\n }\n };\n // Display the statistics as an HTML table.\n // - *type*\n // - \"ops\" - (default) display only the total hit counts for all operator types.\n // - \"index\" - additionally, display the hit counts for the individual `RNM` and `UDT` operators ordered by index.\n // - \"hits\" - additionally, display the hit counts for the individual `RNM` and `UDT` operators by hit count.\n // - \"alpha\" - additionally, display the hit counts for the individual `RNM` and `UDT` operators by name alphabetically.\n // - *caption* - optional caption for the table\n this.toHtml = function toHtml(type, caption) {\n let html = '';\n html += `<table class=\"${style.CLASS_STATS}\">\\n`;\n if (typeof caption === 'string') {\n html += `<caption>${caption}</caption>\\n`;\n }\n html += `<tr><th class=\"${style.CLASS_ACTIVE}\">ops</th>\\n`;\n html += `<th class=\"${style.CLASS_EMPTY}\">EMPTY</th>\\n`;\n html += `<th class=\"${style.CLASS_MATCH}\">MATCH</th>\\n`;\n html += `<th class=\"${style.CLASS_NOMATCH}\">NOMATCH</th>\\n`;\n html += `<th class=\"${style.CLASS_ACTIVE}\">totals</th></tr>\\n`;\n const test = true;\n while (test) {\n if (type === undefined) {\n html += displayOpsOnly();\n break;\n }\n if (type === null) {\n html += displayOpsOnly();\n break;\n }\n if (type === 'ops') {\n html += displayOpsOnly();\n break;\n }\n if (type === 'index') {\n ruleStats.sort(sortIndex);\n if (udtStats.length > 0) {\n udtStats.sort(sortIndex);\n }\n html += displayOpsOnly();\n html += displayRules();\n break;\n }\n if (type === 'hits') {\n ruleStats.sort(sortHits);\n if (udtStats.length > 0) {\n udtStats.sort(sortIndex);\n }\n html += displayOpsOnly();\n html += displayRules();\n break;\n }\n if (type === 'alpha') {\n ruleStats.sort(sortAlpha);\n if (udtStats.length > 0) {\n udtStats.sort(sortAlpha);\n }\n html += displayOpsOnly();\n html += displayRules();\n break;\n }\n break;\n }\n html += '</table>\\n';\n return html;\n };\n // Display the stats table in a complete HTML5 page.\n this.toHtmlPage = function toHtmlPage(type, caption, title) {\n return utils.htmlToPage(this.toHtml(type, caption), title);\n };\n};\n","/* eslint-disable func-names */\n/* eslint-disable prefer-destructuring */\n/* eslint-disable no-restricted-syntax */\n/* eslint-disable guard-for-in */\n/* *************************************************************************************\n * copyright: Copyright (c) 2021 Lowell D. Thomas, all rights reserved\n * license: BSD-2-Clause (https://opensource.org/licenses/BSD-2-Clause)\n * ********************************************************************************* */\n// This module provides a means of tracing the parser through the parse tree as it goes.\n// It is the primary debugging facility for debugging both the SABNF grammar syntax\n// and the input strings that are supposed to be valid grammar sentences.\n// It is also a very informative and educational tool for understanding\n// how a parser actually operates for a given language.\n//\n// Tracing is the process of generating and saving a record of information for each passage\n// of the parser through a parse tree node. And since it traverses each node twice, once down the tree\n// and once coming back up, there are two records for each node.\n// This, obviously, has the potential of generating lots of records.\n// And since these records are normally displayed on a web page\n// it is important to have a means to limit the actual number of records generated to\n// probably no more that a few thousand. This is almost always enough to find any errors.\n// The problem is to get the *right* few thousand records.\n// Therefore, this module has a number of ways of limiting and/or filtering, the number and type of records.\n// Considerable effort has been made to make this filtering of the trace output as simple\n// and intuitive as possible.\n//\n// However, the ability to filter the trace records, or for that matter even understand what they are\n// and the information they contain, does require a minimum amount of understanding of the APG parsing\n// method. The parse tree nodes are all represented by APG operators. They break down into two natural groups.\n// - The `RNM` operators and `UDT` operators are named phrases.\n// These are names chosen by the writer of the SABNF grammar to represent special phrases of interest.\n// - All others collect, concatenate and otherwise manipulate various intermediate phrases along the way.\n//\n// There are separate means of filtering which of these operators in each of these two groups get traced.\n// Let `trace` be an instantiated `trace.js` object.\n// Prior to parsing the string, filtering the rules and UDTs can be defined as follows:\n// ```\n// trace.filter.rules[\"rulename\"] = true;\n// /* trace rule name \"rulename\" */\n// trace.filter.rules[\"udtname\"] = true;\n// /* trace UDT name \"udtname\" */\n// trace.filter.rules[\"<ALL>\"] = true;\n// /* trace all rules and UDTs (the default) */\n// trace.filter.rules[\"<NONE>\"] = true;\n// /* trace no rules or UDTS */\n// ```\n// If any rule or UDT name other than \"<ALL>\" or \"<NONE>\" is specified, all other names are turned off.\n// Therefore, to be selective of rule names, a filter statement is required for each rule/UDT name desired.\n//\n// Filtering of the other operators follows a similar procedure.\n// ```\n// trace.filter.operators[\"TRG\"] = true;\n// /* trace the terminal range, TRG, operators */\n// trace.filter.operators[\"CAT\"] = true;\n// /* trace the concatenations, CAT, operators */\n// trace.filter.operators[\"<ALL>\"] = true;\n// /* trace all operators */\n// trace.filter.operators[\"<NONE>\"] = true;\n// /* trace no operators (the default) */\n// ```\n// If any operator name other than \"<ALL>\" or \"<NONE>\" is specified, all other names are turned off.\n// Therefore, to be selective of operator names, a filter statement is required for each name desired.\n//\n// There is, additionally, a means for limiting the total number of filtered or saved trace records.\n// See the function, `setMaxRecords(max)` below. This will result in only the last `max` records being saved.\n//\n// (See [`apg-examples`](https://github.com/ldthomas/apg-js-examples) for examples of using `trace.js`.)\nmodule.exports = function exportTrace() {\n const utils = require('./utilities');\n const style = require('./style');\n const circular = new (require('./circular-buffer'))();\n const id = require('./identifiers');\n\n const thisFileName = 'trace.js: ';\n const that = this;\n const MODE_HEX = 16;\n const MODE_DEC = 10;\n const MODE_ASCII = 8;\n const MODE_UNICODE = 32;\n const MAX_PHRASE = 80;\n const MAX_TLS = 5;\n const records = [];\n let maxRecords = 5000;\n let lastRecord = -1;\n let filteredRecords = 0;\n let treeDepth = 0;\n const recordStack = [];\n let chars = null;\n let rules = null;\n let udts = null;\n const operatorFilter = [];\n const ruleFilter = [];\n /* special trace table phrases */\n const PHRASE_END = `<span class=\"${style.CLASS_LINEEND}\">•</span>`;\n const PHRASE_CONTINUE = `<span class=\"${style.CLASS_LINEEND}\">…</span>`;\n const PHRASE_EMPTY = `<span class=\"${style.CLASS_EMPTY}\">𝜺</span>`;\n /* filter the non-RNM & non-UDT operators */\n const initOperatorFilter = function () {\n const setOperators = function (set) {\n operatorFilter[id.ALT] = set;\n operatorFilter[id.CAT] = set;\n operatorFilter[id.REP] = set;\n operatorFilter[id.TLS] = set;\n operatorFilter[id.TBS] = set;\n operatorFilter[id.TRG] = set;\n operatorFilter[id.AND] = set;\n operatorFilter[id.NOT] = set;\n operatorFilter[id.BKR] = set;\n operatorFilter[id.BKA] = set;\n operatorFilter[id.BKN] = set;\n operatorFilter[id.ABG] = set;\n operatorFilter[id.AEN] = set;\n };\n let items = 0;\n // eslint-disable-next-line no-unused-vars\n for (const name in that.filter.operators) {\n items += 1;\n }\n if (items === 0) {\n /* case 1: no operators specified: default: do not trace any operators */\n setOperators(false);\n return;\n }\n for (const name in that.filter.operators) {\n const upper = name.toUpperCase();\n if (upper === '<ALL>') {\n /* case 2: <all> operators specified: trace all operators ignore all other operator commands */\n setOperators(true);\n return;\n }\n if (upper === '<NONE>') {\n /* case 3: <none> operators specified: trace NO operators ignore all other operator commands */\n setOperators(false);\n return;\n }\n }\n setOperators(false);\n for (const name in that.filter.operators) {\n const upper = name.toUpperCase();\n /* case 4: one or more individual operators specified: trace 'true' operators only */\n if (upper === 'ALT') {\n operatorFilter[id.ALT] = that.filter.operators[name] === true;\n } else if (upper === 'CAT') {\n operatorFilter[id.CAT] = that.filter.operators[name] === true;\n } else if (upper === 'REP') {\n operatorFilter[id.REP] = that.filter.operators[name] === true;\n } else if (upper === 'AND') {\n operatorFilter[id.AND] = that.filter.operators[name] === true;\n } else if (upper === 'NOT') {\n operatorFilter[id.NOT] = that.filter.operators[name] === true;\n } else if (upper === 'TLS') {\n operatorFilter[id.TLS] = that.filter.operators[name] === true;\n } else if (upper === 'TBS') {\n operatorFilter[id.TBS] = that.filter.operators[name] === true;\n } else if (upper === 'TRG') {\n operatorFilter[id.TRG] = that.filter.operators[name] === true;\n } else if (upper === 'BKR') {\n operatorFilter[id.BKR] = that.filter.operators[name] === true;\n } else if (upper === 'BKA') {\n operatorFilter[id.BKA] = that.filter.operators[name] === true;\n } else if (upper === 'BKN') {\n operatorFilter[id.BKN] = that.filter.operators[name] === true;\n } else if (upper === 'ABG') {\n operatorFilter[id.ABG] = that.filter.operators[name] === true;\n } else if (upper === 'AEN') {\n operatorFilter[id.AEN] = that.filter.operators[name] === true;\n } else {\n throw new Error(\n `${thisFileName}initOpratorFilter: '${name}' not a valid operator name.` +\n ` Must be <all>, <none>, alt, cat, rep, tls, tbs, trg, and, not, bkr, bka or bkn`\n );\n }\n }\n };\n /* filter the rule and `UDT` named operators */\n const initRuleFilter = function () {\n const setRules = function (set) {\n operatorFilter[id.RNM] = set;\n operatorFilter[id.UDT] = set;\n const count = rules.length + udts.length;\n ruleFilter.length = 0;\n for (let i = 0; i < count; i += 1) {\n ruleFilter.push(set);\n }\n };\n let items;\n let i;\n const list = [];\n for (i = 0; i < rules.length; i += 1) {\n list.push(rules[i].lower);\n }\n for (i = 0; i < udts.length; i += 1) {\n list.push(udts[i].lower);\n }\n ruleFilter.length = 0;\n items = 0;\n // eslint-disable-next-line no-unused-vars\n for (const name in that.filter.rules) {\n items += 1;\n }\n if (items === 0) {\n /* case 1: default to all rules & udts */\n setRules(true);\n return;\n }\n for (const name in that.filter.rules) {\n const lower = name.toLowerCase();\n if (lower === '<all>') {\n /* case 2: trace all rules ignore all other rule commands */\n setRules(true);\n return;\n }\n if (lower === '<none>') {\n /* case 3: trace no rules */\n setRules(false);\n return;\n }\n }\n /* case 4: trace only individually specified rules */\n setRules(false);\n operatorFilter[id.RNM] = true;\n operatorFilter[id.UDT] = true;\n for (const name in that.filter.rules) {\n const lower = name.toLowerCase();\n i = list.indexOf(lower);\n if (i < 0) {\n throw new Error(`${thisFileName}initRuleFilter: '${name}' not a valid rule or udt name`);\n }\n ruleFilter[i] = that.filter.rules[name] === true;\n }\n };\n /* used by other APG components to verify that they have a valid trace object */\n this.traceObject = 'traceObject';\n this.filter = {\n operators: [],\n rules: [],\n };\n // Set the maximum number of records to keep (default = 5000).\n // Each record number larger than `maxRecords`\n // will result in deleting the previously oldest record.\n // - `max`: maximum number of records to retain (default = 5000)\n // - `last`: last record number to retain, (default = -1 for (unknown) actual last record)\n this.setMaxRecords = function (max, last) {\n lastRecord = -1;\n if (typeof max === 'number' && max > 0) {\n maxRecords = Math.ceil(max);\n } else {\n maxRecords = 0;\n return;\n }\n if (typeof last === 'number') {\n lastRecord = Math.floor(last);\n if (lastRecord < 0) {\n lastRecord = -1;\n }\n }\n };\n // Returns `maxRecords` to the caller.\n this.getMaxRecords = function () {\n return maxRecords;\n };\n // Returns `lastRecord` to the caller.\n this.getLastRecord = function () {\n return lastRecord;\n };\n /* Called only by the `parser.js` object. No verification of input. */\n this.init = function (rulesIn, udtsIn, charsIn) {\n records.length = 0;\n recordStack.length = 0;\n filteredRecords = 0;\n treeDepth = 0;\n chars = charsIn;\n rules = rulesIn;\n udts = udtsIn;\n initOperatorFilter();\n initRuleFilter();\n circular.init(maxRecords);\n };\n /* returns true if this records passes through the designated filter, false if the record is to be skipped */\n const filterOps = function (op) {\n let ret = false;\n if (op.type === id.RNM) {\n if (operatorFilter[op.type] && ruleFilter[op.index]) {\n ret = true;\n } else {\n ret = false;\n }\n } else if (op.type === id.UDT) {\n if (operatorFilter[op.type] && ruleFilter[rules.length + op.index]) {\n ret = true;\n } else {\n ret = false;\n }\n } else {\n ret = operatorFilter[op.type];\n }\n return ret;\n };\n const filterRecords = function (record) {\n if (lastRecord === -1) {\n return true;\n }\n if (record <= lastRecord) {\n return true;\n }\n return false;\n };\n /* Collect the \"down\" record. */\n this.down = function (op, state, offset, length, anchor, lookAround) {\n if (filterRecords(filteredRecords) && filterOps(op)) {\n recordStack.push(filteredRecords);\n records[circular.increment()] = {\n dirUp: false,\n depth: treeDepth,\n thisLine: filteredRecords,\n thatLine: undefined,\n opcode: op,\n state,\n phraseIndex: offset,\n phraseLength: length,\n lookAnchor: anchor,\n lookAround,\n };\n filteredRecords += 1;\n treeDepth += 1;\n }\n };\n /* Collect the \"up\" record. */\n this.up = function (op, state, offset, length, anchor, lookAround) {\n if (filterRecords(filteredRecords) && filterOps(op)) {\n const thisLine = filteredRecords;\n const thatLine = recordStack.pop();\n const thatRecord = circular.getListIndex(thatLine);\n if (thatRecord !== -1) {\n records[thatRecord].thatLine = thisLine;\n }\n treeDepth -= 1;\n records[circular.increment()] = {\n dirUp: true,\n depth: treeDepth,\n thisLine,\n thatLine,\n opcode: op,\n state,\n phraseIndex: offset,\n phraseLength: length,\n lookAnchor: anchor,\n lookAround,\n };\n filteredRecords += 1;\n }\n };\n /* convert the trace records to a tree of nodes */\n const toTreeObj = function () {\n /* private helper functions */\n function nodeOpcode(node, opcode) {\n let name;\n let casetype;\n let modetype;\n if (opcode) {\n node.op = { id: opcode.type, name: utils.opcodeToString(opcode.type) };\n node.opData = undefined;\n switch (opcode.type) {\n case id.RNM:\n node.opData = rules[opcode.index].name;\n break;\n case id.UDT:\n node.opData = udts[opcode.index].name;\n break;\n case id.BKR:\n if (opcode.index < rules.length) {\n name = rules[opcode.index].name;\n } else {\n name = udts[opcode.index - rules.length].name;\n }\n casetype = opcode.bkrCase === id.BKR_MODE_CI ? '%i' : '%s';\n modetype = opcode.bkrMode === id.BKR_MODE_UM ? '%u' : '%p';\n node.opData = `\\\\\\\\${casetype}${modetype}${name}`;\n break;\n case id.TLS:\n node.opData = [];\n for (let i = 0; i < opcode.string.length; i += 1) {\n node.opData.push(opcode.string[i]);\n }\n break;\n case id.TBS:\n node.opData = [];\n for (let i = 0; i < opcode.string.length; i += 1) {\n node.opData.push(opcode.string[i]);\n }\n break;\n case id.TRG:\n node.opData = [opcode.min, opcode.max];\n break;\n case id.REP:\n node.opData = [opcode.min, opcode.max];\n break;\n default:\n throw new Error('unrecognized opcode');\n }\n } else {\n node.op = { id: undefined, name: undefined };\n node.opData = undefined;\n }\n }\n function nodePhrase(state, index, length) {\n if (state === id.MATCH) {\n return {\n index,\n length,\n };\n }\n if (state === id.NOMATCH) {\n return {\n index,\n length: 0,\n };\n }\n if (state === id.EMPTY) {\n return {\n index,\n length: 0,\n };\n }\n return null;\n }\n let nodeId = -1;\n function nodeDown(parent, record, depth) {\n const node = {\n // eslint-disable-next-line no-plusplus\n id: nodeId++,\n branch: -1,\n parent,\n up: false,\n down: false,\n depth,\n children: [],\n };\n if (record) {\n node.down = true;\n node.state = { id: record.state, name: utils.stateToString(record.state) };\n node.phrase = null;\n nodeOpcode(node, record.opcode);\n } else {\n node.state = { id: undefined, name: undefined };\n node.phrase = nodePhrase();\n nodeOpcode(node, undefined);\n }\n return node;\n }\n function nodeUp(node, record) {\n if (record) {\n node.up = true;\n node.state = { id: record.state, name: utils.stateToString(record.state) };\n node.phrase = nodePhrase(record.state, record.phraseIndex, record.phraseLength);\n if (!node.down) {\n nodeOpcode(node, record.opcode);\n }\n }\n }\n /* walk the final tree: label branches and count leaf nodes */\n let leafNodes = 0;\n let depth = -1;\n let branchCount = 1;\n function walk(node) {\n depth += 1;\n node.branch = branchCount;\n if (depth > treeDepth) {\n treeDepth = depth;\n }\n if (node.children.length === 0) {\n leafNodes += 1;\n } else {\n for (let i = 0; i < node.children.length; i += 1) {\n if (i > 0) {\n branchCount += 1;\n }\n node.children[i].leftMost = false;\n node.children[i].rightMost = false;\n if (node.leftMost) {\n node.children[i].leftMost = i === 0;\n }\n if (node.rightMost) {\n node.children[i].rightMost = i === node.children.length - 1;\n }\n walk(node.children[i]);\n }\n }\n depth -= 1;\n }\n function display(node, offset) {\n let name;\n const obj = {};\n obj.id = node.id;\n obj.branch = node.branch;\n obj.leftMost = node.leftMost;\n obj.rightMost = node.rightMost;\n name = node.state.name ? node.state.name : 'ACTIVE';\n obj.state = { id: node.state.id, name };\n name = node.op.name ? node.op.name : '?';\n obj.op = { id: node.op.id, name };\n if (typeof node.opData === 'string') {\n obj.opData = node.opData;\n } else if (Array.isArray(node.opData)) {\n obj.opData = [];\n for (let i = 0; i < node.opData.length; i += 1) {\n obj.opData[i] = node.opData[i];\n }\n } else {\n obj.opData = undefined;\n }\n if (node.phrase) {\n obj.phrase = { index: node.phrase.index, length: node.phrase.length };\n } else {\n obj.phrase = null;\n }\n obj.depth = node.depth;\n obj.children = [];\n for (let i = 0; i < node.children.length; i += 1) {\n const c = i !== node.children.length - 1;\n obj.children[i] = display(node.children[i], offset, c);\n }\n return obj;\n }\n\n /* construct the tree beginning here */\n const branch = [];\n let root;\n let node;\n let parent;\n let record;\n let firstRecord = true;\n /* push a dummy node so the root node will have a non-null parent */\n const dummy = nodeDown(null, null, -1);\n branch.push(dummy);\n node = dummy;\n circular.forEach((lineIndex) => {\n record = records[lineIndex];\n if (firstRecord) {\n firstRecord = false;\n if (record.depth > 0) {\n /* push some dummy nodes to fill in for missing records */\n const num = record.dirUp ? record.depth + 1 : record.depth;\n for (let i = 0; i < num; i += 1) {\n parent = node;\n node = nodeDown(node, null, i);\n branch.push(node);\n parent.children.push(node);\n }\n }\n }\n if (record.dirUp) {\n /* handle the next record up */\n node = branch.pop();\n nodeUp(node, record);\n node = branch[branch.length - 1];\n } else {\n /* handle the next record down */\n parent = node;\n node = nodeDown(node, record, record.depth);\n branch.push(node);\n parent.children.push(node);\n }\n });\n\n /* if not at root, walk it up to root */\n while (branch.length > 1) {\n node = branch.pop();\n nodeUp(node, null);\n }\n /* maybe redundant or paranoid tests: these should never happen */\n if (dummy.children.length === 0) {\n throw new Error('trace.toTree(): parse tree has no nodes');\n }\n if (branch.length === 0) {\n throw new Error('trace.toTree(): integrity check: dummy root node disappeared?');\n }\n\n /* if no record for start rule: find the pseudo root node (first dummy node above a real node) */\n root = dummy.children[0];\n let prev = root;\n while (root && !root.down && !root.up) {\n prev = root;\n root = root.children[0];\n }\n root = prev;\n\n /* walk the tree of nodes: label brances and count leaves */\n root.leftMost = true;\n root.rightMost = true;\n walk(root);\n root.branch = 0;\n\n /* generate the exported object */\n const obj = {};\n obj.string = [];\n for (let i = 0; i < chars.length; i += 1) {\n obj.string[i] = chars[i];\n }\n /* generate the exported rule names */\n obj.rules = [];\n for (let i = 0; i < rules.length; i += 1) {\n obj.rules[i] = rules[i].name;\n }\n /* generate the exported UDT names */\n obj.udts = [];\n for (let i = 0; i < udts.length; i += 1) {\n obj.udts[i] = udts[i].name;\n }\n /* generate the ids */\n obj.id = {};\n obj.id.ALT = { id: id.ALT, name: 'ALT' };\n obj.id.CAT = { id: id.CAT, name: 'CAT' };\n obj.id.REP = { id: id.REP, name: 'REP' };\n obj.id.RNM = { id: id.RNM, name: 'RNM' };\n obj.id.TLS = { id: id.TLS, name: 'TLS' };\n obj.id.TBS = { id: id.TBS, name: 'TBS' };\n obj.id.TRG = { id: id.TRG, name: 'TRG' };\n obj.id.UDT = { id: id.UDT, name: 'UDT' };\n obj.id.AND = { id: id.AND, name: 'AND' };\n obj.id.NOT = { id: id.NOT, name: 'NOT' };\n obj.id.BKR = { id: id.BKR, name: 'BKR' };\n obj.id.BKA = { id: id.BKA, name: 'BKA' };\n obj.id.BKN = { id: id.BKN, name: 'BKN' };\n obj.id.ABG = { id: id.ABG, name: 'ABG' };\n obj.id.AEN = { id: id.AEN, name: 'AEN' };\n obj.id.ACTIVE = { id: id.ACTIVE, name: 'ACTIVE' };\n obj.id.MATCH = { id: id.MATCH, name: 'MATCH' };\n obj.id.EMPTY = { id: id.EMPTY, name: 'EMPTY' };\n obj.id.NOMATCH = { id: id.NOMATCH, name: 'NOMATCH' };\n /* generate the max tree depth */\n obj.treeDepth = treeDepth;\n /* generate the number of leaf nodes (branches) */\n obj.leafNodes = leafNodes;\n /* generate the types of the left- and right-most branches */\n let branchesIncomplete;\n if (root.down) {\n if (root.up) {\n branchesIncomplete = 'none';\n } else {\n branchesIncomplete = 'right';\n }\n } else if (root.up) {\n branchesIncomplete = 'left';\n } else {\n branchesIncomplete = 'both';\n }\n obj.branchesIncomplete = branchesIncomplete;\n obj.tree = display(root, root.depth, false);\n return obj;\n };\n // Returns the trace records as JSON parse tree object.\n // - stringify: if `true`, the object is 'stringified' before returning, otherwise, the object itself is returned.\n this.toTree = function (stringify) {\n const obj = toTreeObj();\n if (stringify) {\n return JSON.stringify(obj);\n }\n return obj;\n };\n // Translate the trace records to HTML format and create a complete HTML page for browser display.\n this.toHtmlPage = function (mode, caption, title) {\n return utils.htmlToPage(this.toHtml(mode, caption), title);\n };\n\n /* From here on down, these are just helper functions for `toHtml()`. */\n const htmlHeader = function (mode, caption) {\n /* open the page */\n /* write the HTML5 header with table style */\n /* open the <table> tag */\n let modeName;\n switch (mode) {\n case MODE_HEX:\n modeName = 'hexadecimal';\n break;\n case MODE_DEC:\n modeName = 'decimal';\n break;\n case MODE_ASCII:\n modeName = 'ASCII';\n break;\n case MODE_UNICODE:\n modeName = 'UNICODE';\n break;\n default:\n throw new Error(`${thisFileName}htmlHeader: unrecognized mode: ${mode}`);\n }\n let header = '';\n header += `<p>display mode: ${modeName}</p>\\n`;\n header += `<table class=\"${style.CLASS_TRACE}\">\\n`;\n if (typeof caption === 'string') {\n header += `<caption>${caption}</caption>`;\n }\n return header;\n };\n const htmlFooter = function () {\n let footer = '';\n /* close the </table> tag */\n footer += '</table>\\n';\n /* display a table legend */\n footer += `<p class=\"${style.CLASS_MONOSPACE}\">legend:<br>\\n`;\n footer += '(a) - line number<br>\\n';\n footer += '(b) - matching line number<br>\\n';\n footer += '(c) - phrase offset<br>\\n';\n footer += '(d) - phrase length<br>\\n';\n footer += '(e) - tree depth<br>\\n';\n footer += '(f) - operator state<br>\\n';\n footer += ` - <span class=\"${style.CLASS_ACTIVE}\">↓</span> phrase opened<br>\\n`;\n footer += ` - <span class=\"${style.CLASS_MATCH}\">↑M</span> phrase matched<br>\\n`;\n footer += ` - <span class=\"${style.CLASS_EMPTY}\">↑E</span> empty phrase matched<br>\\n`;\n footer += ` - <span class=\"${style.CLASS_NOMATCH}\">↑N</span> phrase not matched<br>\\n`;\n footer +=\n 'operator - ALT, CAT, REP, RNM, TRG, TLS, TBS<sup>†</sup>, UDT, AND, NOT, BKA, BKN, BKR, ABG, AEN<sup>‡</sup><br>\\n';\n footer += `phrase - up to ${MAX_PHRASE} characters of the phrase being matched<br>\\n`;\n footer += ` - <span class=\"${style.CLASS_MATCH}\">matched characters</span><br>\\n`;\n footer += ` - <span class=\"${style.CLASS_LOOKAHEAD}\">matched characters in look ahead mode</span><br>\\n`;\n footer += ` - <span class=\"${style.CLASS_LOOKBEHIND}\">matched characters in look behind mode</span><br>\\n`;\n footer += ` - <span class=\"${style.CLASS_REMAINDER}\">remainder characters(not yet examined by parser)</span><br>\\n`;\n footer += ` - <span class=\"${style.CLASS_CTRLCHAR}\">control characters, TAB, LF, CR, etc. (ASCII mode only)</span><br>\\n`;\n footer += ` - ${PHRASE_EMPTY} empty string<br>\\n`;\n footer += ` - ${PHRASE_END} end of input string<br>\\n`;\n footer += ` - ${PHRASE_CONTINUE} input string display truncated<br>\\n`;\n footer += '</p>\\n';\n footer += `<p class=\"${style.CLASS_MONOSPACE}\">\\n`;\n footer += '<sup>†</sup>original ABNF operators:<br>\\n';\n footer += 'ALT - alternation<br>\\n';\n footer += 'CAT - concatenation<br>\\n';\n footer += 'REP - repetition<br>\\n';\n footer += 'RNM - rule name<br>\\n';\n footer += 'TRG - terminal range<br>\\n';\n footer += 'TLS - terminal literal string (case insensitive)<br>\\n';\n footer += 'TBS - terminal binary string (case sensitive)<br>\\n';\n footer += '<br>\\n';\n footer += '<sup>‡</sup>super set SABNF operators:<br>\\n';\n footer += 'UDT - user-defined terminal<br>\\n';\n footer += 'AND - positive look ahead<br>\\n';\n footer += 'NOT - negative look ahead<br>\\n';\n footer += 'BKA - positive look behind<br>\\n';\n footer += 'BKN - negative look behind<br>\\n';\n footer += 'BKR - back reference<br>\\n';\n footer += 'ABG - anchor - begin of input string<br>\\n';\n footer += 'AEN - anchor - end of input string<br>\\n';\n footer += '</p>\\n';\n return footer;\n };\n this.indent = function (depth) {\n let html = '';\n for (let i = 0; i < depth; i += 1) {\n html += '.';\n }\n return html;\n };\n /* format the TRG operator */\n const displayTrg = function (mode, op) {\n let html = '';\n if (op.type === id.TRG) {\n if (mode === MODE_HEX || mode === MODE_UNICODE) {\n let hex = op.min.toString(16).toUpperCase();\n if (hex.length % 2 !== 0) {\n hex = `0${hex}`;\n }\n html += mode === MODE_HEX ? '%x' : 'U+';\n html += hex;\n hex = op.max.toString(16).toUpperCase();\n if (hex.length % 2 !== 0) {\n hex = `0${hex}`;\n }\n html += `–${hex}`;\n } else {\n html = `%d${op.min.toString(10)}–${op.max.toString(10)}`;\n }\n }\n return html;\n };\n /* format the REP operator */\n const displayRep = function (mode, op) {\n let html = '';\n if (op.type === id.REP) {\n if (mode === MODE_HEX) {\n let hex = op.min.toString(16).toUpperCase();\n if (hex.length % 2 !== 0) {\n hex = `0${hex}`;\n }\n html = `x${hex}`;\n if (op.max < Infinity) {\n hex = op.max.toString(16).toUpperCase();\n if (hex.length % 2 !== 0) {\n hex = `0${hex}`;\n }\n } else {\n hex = 'inf';\n }\n html += `–${hex}`;\n } else if (op.max < Infinity) {\n html = `${op.min.toString(10)}–${op.max.toString(10)}`;\n } else {\n html = `${op.min.toString(10)}–inf`;\n }\n }\n return html;\n };\n /* format the TBS operator */\n const displayTbs = function (mode, op) {\n let html = '';\n if (op.type === id.TBS) {\n const len = Math.min(op.string.length, MAX_TLS * 2);\n if (mode === MODE_HEX || mode === MODE_UNICODE) {\n html += mode === MODE_HEX ? '%x' : 'U+';\n for (let i = 0; i < len; i += 1) {\n let hex;\n if (i > 0) {\n html += '.';\n }\n hex = op.string[i].toString(16).toUpperCase();\n if (hex.length % 2 !== 0) {\n hex = `0${hex}`;\n }\n html += hex;\n }\n } else {\n html = '%d';\n for (let i = 0; i < len; i += 1) {\n if (i > 0) {\n html += '.';\n }\n html += op.string[i].toString(10);\n }\n }\n if (len < op.string.length) {\n html += PHRASE_CONTINUE;\n }\n }\n return html;\n };\n /* format the TLS operator */\n const displayTls = function (mode, op) {\n let html = '';\n if (op.type === id.TLS) {\n const len = Math.min(op.string.length, MAX_TLS);\n if (mode === MODE_HEX || mode === MODE_DEC) {\n let charu;\n let charl;\n let base;\n if (mode === MODE_HEX) {\n html = '%x';\n base = 16;\n } else {\n html = '%d';\n base = 10;\n }\n for (let i = 0; i < len; i += 1) {\n if (i > 0) {\n html += '.';\n }\n charl = op.string[i];\n if (charl >= 97 && charl <= 122) {\n charu = charl - 32;\n html += `${charu.toString(base)}/${charl.toString(base)}`.toUpperCase();\n } else if (charl >= 65 && charl <= 90) {\n charu = charl;\n charl += 32;\n html += `${charu.toString(base)}/${charl.toString(base)}`.toUpperCase();\n } else {\n html += charl.toString(base).toUpperCase();\n }\n }\n if (len < op.string.length) {\n html += PHRASE_CONTINUE;\n }\n } else {\n html = '\"';\n for (let i = 0; i < len; i += 1) {\n html += utils.asciiChars[op.string[i]];\n }\n if (len < op.string.length) {\n html += PHRASE_CONTINUE;\n }\n html += '\"';\n }\n }\n return html;\n };\n const subPhrase = function (mode, charsArg, index, length, prev) {\n if (length === 0) {\n return '';\n }\n let phrase = '';\n const comma = prev ? ',' : '';\n switch (mode) {\n case MODE_HEX:\n phrase = comma + utils.charsToHex(charsArg, index, length);\n break;\n case MODE_DEC:\n if (prev) {\n return `,${utils.charsToDec(charsArg, index, length)}`;\n }\n phrase = comma + utils.charsToDec(charsArg, index, length);\n break;\n case MODE_UNICODE:\n phrase = utils.charsToUnicode(charsArg, index, length);\n break;\n case MODE_ASCII:\n default:\n phrase = utils.charsToAsciiHtml(charsArg, index, length);\n break;\n }\n return phrase;\n };\n /* display phrases matched in look-behind mode */\n const displayBehind = function (mode, charsArg, state, index, length, anchor) {\n let html = '';\n let beg1;\n let len1;\n let beg2;\n let len2;\n let lastchar = PHRASE_END;\n const spanBehind = `<span class=\"${style.CLASS_LOOKBEHIND}\">`;\n const spanRemainder = `<span class=\"${style.CLASS_REMAINDER}\">`;\n const spanend = '</span>';\n let prev = false;\n switch (state) {\n case id.EMPTY:\n html += PHRASE_EMPTY;\n /* // eslint-disable-next-line no-fallthrough */\n case id.NOMATCH:\n case id.MATCH:\n case id.ACTIVE:\n beg1 = index - length;\n len1 = anchor - beg1;\n beg2 = anchor;\n len2 = charsArg.length - beg2;\n break;\n default:\n throw new Error('unrecognized state');\n }\n lastchar = PHRASE_END;\n if (len1 > MAX_PHRASE) {\n len1 = MAX_PHRASE;\n lastchar = PHRASE_CONTINUE;\n len2 = 0;\n } else if (len1 + len2 > MAX_PHRASE) {\n lastchar = PHRASE_CONTINUE;\n len2 = MAX_PHRASE - len1;\n }\n if (len1 > 0) {\n html += spanBehind;\n html += subPhrase(mode, charsArg, beg1, len1, prev);\n html += spanend;\n prev = true;\n }\n if (len2 > 0) {\n html += spanRemainder;\n html += subPhrase(mode, charsArg, beg2, len2, prev);\n html += spanend;\n }\n return html + lastchar;\n };\n const displayForward = function (mode, charsArg, state, index, length, spanAhead) {\n let html = '';\n let beg1;\n let len1;\n let beg2;\n let len2;\n let lastchar = PHRASE_END;\n const spanRemainder = `<span class=\"${style.CLASS_REMAINDER}\">`;\n const spanend = '</span>';\n let prev = false;\n switch (state) {\n case id.EMPTY:\n html += PHRASE_EMPTY;\n /* // eslint-disable-next-line no-fallthrough */\n case id.NOMATCH:\n case id.ACTIVE:\n beg1 = index;\n len1 = 0;\n beg2 = index;\n len2 = charsArg.length - beg2;\n break;\n case id.MATCH:\n beg1 = index;\n len1 = length;\n beg2 = index + len1;\n len2 = charsArg.length - beg2;\n break;\n default:\n throw new Error('unrecognized state');\n }\n lastchar = PHRASE_END;\n if (len1 > MAX_PHRASE) {\n len1 = MAX_PHRASE;\n lastchar = PHRASE_CONTINUE;\n len2 = 0;\n } else if (len1 + len2 > MAX_PHRASE) {\n lastchar = PHRASE_CONTINUE;\n len2 = MAX_PHRASE - len1;\n }\n if (len1 > 0) {\n html += spanAhead;\n html += subPhrase(mode, charsArg, beg1, len1, prev);\n html += spanend;\n prev = true;\n }\n if (len2 > 0) {\n html += spanRemainder;\n html += subPhrase(mode, charsArg, beg2, len2, prev);\n html += spanend;\n }\n return html + lastchar;\n };\n /* display phrases matched in look-ahead mode */\n const displayAhead = function (mode, charsArg, state, index, length) {\n const spanAhead = `<span class=\"${style.CLASS_LOOKAHEAD}\">`;\n return displayForward(mode, charsArg, state, index, length, spanAhead);\n };\n /* display phrases matched in normal parsing mode */\n const displayNone = function (mode, charsArg, state, index, length) {\n const spanAhead = `<span class=\"${style.CLASS_MATCH}\">`;\n return displayForward(mode, charsArg, state, index, length, spanAhead);\n };\n /* Returns the filtered records, formatted as an HTML table. */\n const htmlTable = function (mode) {\n if (rules === null) {\n return '';\n }\n let html = '';\n let thisLine;\n let thatLine;\n let lookAhead;\n let lookBehind;\n let lookAround;\n let anchor;\n html += '<tr><th>(a)</th><th>(b)</th><th>(c)</th><th>(d)</th><th>(e)</th><th>(f)</th>';\n html += '<th>operator</th><th>phrase</th></tr>\\n';\n circular.forEach((lineIndex) => {\n const line = records[lineIndex];\n thisLine = line.thisLine;\n thatLine = line.thatLine !== undefined ? line.thatLine : '--';\n lookAhead = false;\n lookBehind = false;\n lookAround = false;\n if (line.lookAround === id.LOOKAROUND_AHEAD) {\n lookAhead = true;\n lookAround = true;\n anchor = line.lookAnchor;\n }\n if (line.opcode.type === id.AND || line.opcode.type === id.NOT) {\n lookAhead = true;\n lookAround = true;\n anchor = line.phraseIndex;\n }\n if (line.lookAround === id.LOOKAROUND_BEHIND) {\n lookBehind = true;\n lookAround = true;\n anchor = line.lookAnchor;\n }\n if (line.opcode.type === id.BKA || line.opcode.type === id.BKN) {\n lookBehind = true;\n lookAround = true;\n anchor = line.phraseIndex;\n }\n html += '<tr>';\n html += `<td>${thisLine}</td><td>${thatLine}</td>`;\n html += `<td>${line.phraseIndex}</td>`;\n html += `<td>${line.phraseLength}</td>`;\n html += `<td>${line.depth}</td>`;\n html += '<td>';\n switch (line.state) {\n case id.ACTIVE:\n html += `<span class=\"${style.CLASS_ACTIVE}\">↓ </span>`;\n break;\n case id.MATCH:\n html += `<span class=\"${style.CLASS_MATCH}\">↑M</span>`;\n break;\n case id.NOMATCH:\n html += `<span class=\"${style.CLASS_NOMATCH}\">↑N</span>`;\n break;\n case id.EMPTY:\n html += `<span class=\"${style.CLASS_EMPTY}\">↑E</span>`;\n break;\n default:\n html += `<span class=\"${style.CLASS_ACTIVE}\">--</span>`;\n break;\n }\n html += '</td>';\n html += '<td>';\n html += that.indent(line.depth);\n if (lookAhead) {\n html += `<span class=\"${style.CLASS_LOOKAHEAD}\">`;\n } else if (lookBehind) {\n html += `<span class=\"${style.CLASS_LOOKBEHIND}\">`;\n }\n html += utils.opcodeToString(line.opcode.type);\n if (line.opcode.type === id.RNM) {\n html += `(${rules[line.opcode.index].name}) `;\n }\n if (line.opcode.type === id.BKR) {\n const casetype = line.opcode.bkrCase === id.BKR_MODE_CI ? '%i' : '%s';\n const modetype = line.opcode.bkrMode === id.BKR_MODE_UM ? '%u' : '%p';\n html += `(\\\\${casetype}${modetype}${rules[line.opcode.index].name}) `;\n }\n if (line.opcode.type === id.UDT) {\n html += `(${udts[line.opcode.index].name}) `;\n }\n if (line.opcode.type === id.TRG) {\n html += `(${displayTrg(mode, line.opcode)}) `;\n }\n if (line.opcode.type === id.TBS) {\n html += `(${displayTbs(mode, line.opcode)}) `;\n }\n if (line.opcode.type === id.TLS) {\n html += `(${displayTls(mode, line.opcode)}) `;\n }\n if (line.opcode.type === id.REP) {\n html += `(${displayRep(mode, line.opcode)}) `;\n }\n if (lookAround) {\n html += '</span>';\n }\n html += '</td>';\n html += '<td>';\n if (lookBehind) {\n html += displayBehind(mode, chars, line.state, line.phraseIndex, line.phraseLength, anchor);\n } else if (lookAhead) {\n html += displayAhead(mode, chars, line.state, line.phraseIndex, line.phraseLength);\n } else {\n html += displayNone(mode, chars, line.state, line.phraseIndex, line.phraseLength);\n }\n html += '</td></tr>\\n';\n });\n html += '<tr><th>(a)</th><th>(b)</th><th>(c)</th><th>(d)</th><th>(e)</th><th>(f)</th>';\n html += '<th>operator</th><th>phrase</th></tr>\\n';\n html += '</table>\\n';\n return html;\n };\n // Translate the trace records to HTML format.\n // - *modearg* - can be `\"ascii\"`, `\"decimal\"`, `\"hexadecimal\"` or `\"unicode\"`.\n // Determines the format of the string character code display.\n // - *caption* - optional caption for the HTML table.\n this.toHtml = function (modearg, caption) {\n /* writes the trace records as a table in a complete html page */\n let mode = MODE_ASCII;\n if (typeof modearg === 'string' && modearg.length >= 3) {\n const modein = modearg.toLowerCase().slice(0, 3);\n if (modein === 'hex') {\n mode = MODE_HEX;\n } else if (modein === 'dec') {\n mode = MODE_DEC;\n } else if (modein === 'uni') {\n mode = MODE_UNICODE;\n }\n }\n let html = '';\n html += htmlHeader(mode, caption);\n html += htmlTable(mode);\n html += htmlFooter();\n return html;\n };\n};\n","/* eslint-disable func-names */\n/* *************************************************************************************\n * copyright: Copyright (c) 2021 Lowell D. Thomas, all rights reserved\n * license: BSD-2-Clause (https://opensource.org/licenses/BSD-2-Clause)\n * ********************************************************************************* */\n// This module exports a variety of utility functions that support\n// [`apg`](https://github.com/ldthomas/apg-js2), [`apg-lib`](https://github.com/ldthomas/apg-js2-lib)\n// and the generated parser applications.\n\nconst style = require('./style');\nconst converter = require('../apg-conv-api/converter');\nconst emitCss = require('./emitcss');\nconst id = require('./identifiers');\n\nconst thisFileName = 'utilities.js: ';\n\n/* translate (implied) phrase beginning character and length to actual first and last character indexes */\n/* used by multiple phrase handling functions */\nconst getBounds = function (length, begArg, len) {\n let end;\n let beg = begArg;\n const TRUE = true;\n while (TRUE) {\n if (length <= 0) {\n beg = 0;\n end = 0;\n break;\n }\n if (typeof beg !== 'number') {\n beg = 0;\n end = length;\n break;\n }\n if (beg >= length) {\n beg = length;\n end = length;\n break;\n }\n if (typeof len !== 'number') {\n end = length;\n break;\n }\n end = beg + len;\n if (end > length) {\n end = length;\n break;\n }\n break;\n }\n return {\n beg,\n end,\n };\n};\n// Generates a complete, minimal HTML5 page, inserting the user's HTML text on the page.\n// - *html* - the page text in HTML format\n// - *title* - the HTML page `<title>` - defaults to `htmlToPage`.\nexports.htmlToPage = function (html, titleArg) {\n let title;\n if (typeof html !== 'string') {\n throw new Error(`${thisFileName}htmlToPage: input HTML is not a string`);\n }\n if (typeof titleArg !== 'string') {\n title = 'htmlToPage';\n } else {\n title = titleArg;\n }\n let page = '';\n page += '<!DOCTYPE html>\\n';\n page += '<html lang=\"en\">\\n';\n page += '<head>\\n';\n page += '<meta charset=\"utf-8\">\\n';\n page += `<title>${title}</title>\\n`;\n page += '<style>\\n';\n page += emitCss();\n page += '</style>\\n';\n page += '</head>\\n<body>\\n';\n page += `<p>${new Date()}</p>\\n`;\n page += html;\n page += '</body>\\n</html>\\n';\n return page;\n};\n// Formats the returned object from `parser.parse()`\n// into an HTML table.\n// ```\n// return {\n// success : sysData.success,\n// state : sysData.state,\n// length : charsLength,\n// matched : sysData.phraseLength,\n// maxMatched : maxMatched,\n// maxTreeDepth : maxTreeDepth,\n// nodeHits : nodeHits,\n// inputLength : chars.length,\n// subBegin : charsBegin,\n// subEnd : charsEnd,\n// subLength : charsLength\n// };\n// ```\nexports.parserResultToHtml = function (result, caption) {\n let cap = null;\n if (typeof caption === 'string' && caption !== '') {\n cap = caption;\n }\n let success;\n let state;\n if (result.success === true) {\n success = `<span class=\"${style.CLASS_MATCH}\">true</span>`;\n } else {\n success = `<span class=\"${style.CLASS_NOMATCH}\">false</span>`;\n }\n if (result.state === id.EMPTY) {\n state = `<span class=\"${style.CLASS_EMPTY}\">EMPTY</span>`;\n } else if (result.state === id.MATCH) {\n state = `<span class=\"${style.CLASS_MATCH}\">MATCH</span>`;\n } else if (result.state === id.NOMATCH) {\n state = `<span class=\"${style.CLASS_NOMATCH}\">NOMATCH</span>`;\n } else {\n state = `<span class=\"${style.CLASS_NOMATCH}\">unrecognized</span>`;\n }\n let html = '';\n html += `<table class=\"${style.CLASS_STATE}\">\\n`;\n if (cap) {\n html += `<caption>${cap}</caption>\\n`;\n }\n html += '<tr><th>state item</th><th>value</th><th>description</th></tr>\\n';\n html += `<tr><td>parser success</td><td>${success}</td>\\n`;\n html += `<td><span class=\"${style.CLASS_MATCH}\">true</span> if the parse succeeded,\\n`;\n html += ` <span class=\"${style.CLASS_NOMATCH}\">false</span> otherwise`;\n html += '<br><i>NOTE: for success, entire string must be matched</i></td></tr>\\n';\n html += `<tr><td>parser state</td><td>${state}</td>\\n`;\n html += `<td><span class=\"${style.CLASS_EMPTY}\">EMPTY</span>, `;\n html += `<span class=\"${style.CLASS_MATCH}\">MATCH</span> or \\n`;\n html += `<span class=\"${style.CLASS_NOMATCH}\">NOMATCH</span></td></tr>\\n`;\n html += `<tr><td>string length</td><td>${result.length}</td><td>length of the input (sub)string</td></tr>\\n`;\n html += `<tr><td>matched length</td><td>${result.matched}</td><td>number of input string characters matched</td></tr>\\n`;\n html += `<tr><td>max matched</td><td>${result.maxMatched}</td><td>maximum number of input string characters matched</td></tr>\\n`;\n html += `<tr><td>max tree depth</td><td>${result.maxTreeDepth}</td><td>maximum depth of the parse tree reached</td></tr>\\n`;\n html += `<tr><td>node hits</td><td>${result.nodeHits}</td><td>number of parse tree node hits (opcode function calls)</td></tr>\\n`;\n html += `<tr><td>input length</td><td>${result.inputLength}</td><td>length of full input string</td></tr>\\n`;\n html += `<tr><td>sub-string begin</td><td>${result.subBegin}</td><td>sub-string first character index</td></tr>\\n`;\n html += `<tr><td>sub-string end</td><td>${result.subEnd}</td><td>sub-string end-of-string index</td></tr>\\n`;\n html += `<tr><td>sub-string length</td><td>${result.subLength}</td><td>sub-string length</td></tr>\\n`;\n html += '</table>\\n';\n return html;\n};\n// Translates a sub-array of integer character codes into a string.\n// Very useful in callback functions to translate the matched phrases into strings.\nexports.charsToString = function (chars, phraseIndex, phraseLength) {\n let beg;\n let end;\n if (typeof phraseIndex === 'number') {\n if (phraseIndex >= chars.length) {\n return '';\n }\n beg = phraseIndex < 0 ? 0 : phraseIndex;\n } else {\n beg = 0;\n }\n if (typeof phraseLength === 'number') {\n if (phraseLength <= 0) {\n return '';\n }\n end = phraseLength > chars.length - beg ? chars.length : beg + phraseLength;\n } else {\n end = chars.length;\n }\n if (beg < end) {\n return converter.encode('UTF16LE', chars.slice(beg, end)).toString('utf16le');\n }\n return '';\n};\n// Translates a string into an array of integer character codes.\nexports.stringToChars = function (string) {\n return converter.decode('STRING', string);\n};\n// Translates an opcode identifier into a human-readable string.\nexports.opcodeToString = function (type) {\n let ret = 'unknown';\n switch (type) {\n case id.ALT:\n ret = 'ALT';\n break;\n case id.CAT:\n ret = 'CAT';\n break;\n case id.RNM:\n ret = 'RNM';\n break;\n case id.UDT:\n ret = 'UDT';\n break;\n case id.AND:\n ret = 'AND';\n break;\n case id.NOT:\n ret = 'NOT';\n break;\n case id.REP:\n ret = 'REP';\n break;\n case id.TRG:\n ret = 'TRG';\n break;\n case id.TBS:\n ret = 'TBS';\n break;\n case id.TLS:\n ret = 'TLS';\n break;\n case id.BKR:\n ret = 'BKR';\n break;\n case id.BKA:\n ret = 'BKA';\n break;\n case id.BKN:\n ret = 'BKN';\n break;\n case id.ABG:\n ret = 'ABG';\n break;\n case id.AEN:\n ret = 'AEN';\n break;\n default:\n throw new Error('unrecognized opcode');\n }\n return ret;\n};\n// Translates an state identifier into a human-readable string.\nexports.stateToString = function (state) {\n let ret = 'unknown';\n switch (state) {\n case id.ACTIVE:\n ret = 'ACTIVE';\n break;\n case id.MATCH:\n ret = 'MATCH';\n break;\n case id.EMPTY:\n ret = 'EMPTY';\n break;\n case id.NOMATCH:\n ret = 'NOMATCH';\n break;\n default:\n throw new Error('unrecognized state');\n }\n return ret;\n};\n// Array which translates all 128, 7-bit ASCII character codes to their respective HTML format.\nexports.asciiChars = [\n 'NUL',\n 'SOH',\n 'STX',\n 'ETX',\n 'EOT',\n 'ENQ',\n 'ACK',\n 'BEL',\n 'BS',\n 'TAB',\n 'LF',\n 'VT',\n 'FF',\n 'CR',\n 'SO',\n 'SI',\n 'DLE',\n 'DC1',\n 'DC2',\n 'DC3',\n 'DC4',\n 'NAK',\n 'SYN',\n 'ETB',\n 'CAN',\n 'EM',\n 'SUB',\n 'ESC',\n 'FS',\n 'GS',\n 'RS',\n 'US',\n ' ',\n '!',\n '"',\n '#',\n '$',\n '%',\n '&',\n ''',\n '(',\n ')',\n '*',\n '+',\n ',',\n '-',\n '.',\n '/',\n '0',\n '1',\n '2',\n '3',\n '4',\n '5',\n '6',\n '7',\n '8',\n '9',\n ':',\n ';',\n '<',\n '=',\n '>',\n '?',\n '@',\n 'A',\n 'B',\n 'C',\n 'D',\n 'E',\n 'F',\n 'G',\n 'H',\n 'I',\n 'J',\n 'K',\n 'L',\n 'M',\n 'N',\n 'O',\n 'P',\n 'Q',\n 'R',\n 'S',\n 'T',\n 'U',\n 'V',\n 'W',\n 'X',\n 'Y',\n 'Z',\n '[',\n '\',\n ']',\n '^',\n '_',\n '`',\n 'a',\n 'b',\n 'c',\n 'd',\n 'e',\n 'f',\n 'g',\n 'h',\n 'i',\n 'j',\n 'k',\n 'l',\n 'm',\n 'n',\n 'o',\n 'p',\n 'q',\n 'r',\n 's',\n 't',\n 'u',\n 'v',\n 'w',\n 'x',\n 'y',\n 'z',\n '{',\n '|',\n '}',\n '~',\n 'DEL',\n];\n// Translates a single character to hexadecimal with leading zeros for 2, 4, or 8 digit display.\nexports.charToHex = function (char) {\n let ch = char.toString(16).toUpperCase();\n switch (ch.length) {\n case 1:\n case 3:\n case 7:\n ch = `0${ch}`;\n break;\n case 2:\n case 6:\n ch = `00${ch}`;\n break;\n case 4:\n break;\n case 5:\n ch = `000${ch}`;\n break;\n default:\n throw new Error('unrecognized option');\n }\n return ch;\n};\n// Translates a sub-array of character codes to decimal display format.\nexports.charsToDec = function (chars, beg, len) {\n let ret = '';\n if (!Array.isArray(chars)) {\n throw new Error(`${thisFileName}charsToDec: input must be an array of integers`);\n }\n const bounds = getBounds(chars.length, beg, len);\n if (bounds.end > bounds.beg) {\n ret += chars[bounds.beg];\n for (let i = bounds.beg + 1; i < bounds.end; i += 1) {\n ret += `,${chars[i]}`;\n }\n }\n return ret;\n};\n// Translates a sub-array of character codes to hexadecimal display format.\nexports.charsToHex = function (chars, beg, len) {\n let ret = '';\n if (!Array.isArray(chars)) {\n throw new Error(`${thisFileName}charsToHex: input must be an array of integers`);\n }\n const bounds = getBounds(chars.length, beg, len);\n if (bounds.end > bounds.beg) {\n ret += `\\\\x${exports.charToHex(chars[bounds.beg])}`;\n for (let i = bounds.beg + 1; i < bounds.end; i += 1) {\n ret += `,\\\\x${exports.charToHex(chars[i])}`;\n }\n }\n return ret;\n};\nexports.charsToHtmlEntities = function (chars, beg, len) {\n let ret = '';\n if (!Array.isArray(chars)) {\n throw new Error(`${thisFileName}charsToHex: input must be an array of integers`);\n }\n const bounds = getBounds(chars.length, beg, len);\n if (bounds.end > bounds.beg) {\n for (let i = bounds.beg; i < bounds.end; i += 1) {\n ret += `&#x${chars[i].toString(16)};`;\n }\n }\n return ret;\n};\n// Translates a sub-array of character codes to Unicode display format.\nfunction isUnicode(char) {\n if (char >= 0xd800 && char <= 0xdfff) {\n return false;\n }\n if (char > 0x10ffff) {\n return false;\n }\n return true;\n}\nexports.charsToUnicode = function (chars, beg, len) {\n let ret = '';\n if (!Array.isArray(chars)) {\n throw new Error(`${thisFileName}charsToUnicode: input must be an array of integers`);\n }\n const bounds = getBounds(chars.length, beg, len);\n if (bounds.end > bounds.beg) {\n for (let i = bounds.beg; i < bounds.end; i += 1) {\n if (isUnicode(chars[i])) {\n ret += `&#${chars[i]};`;\n } else {\n ret += ` U+${exports.charToHex(chars[i])}`;\n }\n }\n }\n return ret;\n};\n// Translates a sub-array of character codes to JavaScript Unicode display format (`\\uXXXX`).\nexports.charsToJsUnicode = function (chars, beg, len) {\n let ret = '';\n if (!Array.isArray(chars)) {\n throw new Error(`${thisFileName}charsToJsUnicode: input must be an array of integers`);\n }\n const bounds = getBounds(chars.length, beg, len);\n if (bounds.end > bounds.beg) {\n ret += `\\\\u${exports.charToHex(chars[bounds.beg])}`;\n for (let i = bounds.beg + 1; i < bounds.end; i += 1) {\n ret += `,\\\\u${exports.charToHex(chars[i])}`;\n }\n }\n return ret;\n};\n// Translates a sub-array of character codes to printing ASCII character display format.\nexports.charsToAscii = function (chars, beg, len) {\n let ret = '';\n if (!Array.isArray(chars)) {\n throw new Error(`${thisFileName}charsToAscii: input must be an array of integers`);\n }\n const bounds = getBounds(chars.length, beg, len);\n for (let i = bounds.beg; i < bounds.end; i += 1) {\n const char = chars[i];\n if (char >= 32 && char <= 126) {\n ret += String.fromCharCode(char);\n } else {\n ret += `\\\\x${exports.charToHex(char)}`;\n }\n }\n return ret;\n};\n// Translates a sub-array of character codes to HTML display format.\nexports.charsToAsciiHtml = function (chars, beg, len) {\n if (!Array.isArray(chars)) {\n throw new Error(`${thisFileName}charsToAsciiHtml: input must be an array of integers`);\n }\n let html = '';\n let char;\n const bounds = getBounds(chars.length, beg, len);\n for (let i = bounds.beg; i < bounds.end; i += 1) {\n char = chars[i];\n if (char < 32 || char === 127) {\n /* control characters */\n html += `<span class=\"${style.CLASS_CTRLCHAR}\">${exports.asciiChars[char]}</span>`;\n } else if (char > 127) {\n /* non-ASCII */\n html += `<span class=\"${style.CLASS_CTRLCHAR}\">U+${exports.charToHex(char)}</span>`;\n } else {\n /* printing ASCII, 32 <= char <= 126 */\n html += exports.asciiChars[char];\n }\n }\n return html;\n};\n// Translates a JavaScript string to HTML display format.\nexports.stringToAsciiHtml = function (str) {\n const chars = converter.decode('STRING', str);\n return this.charsToAsciiHtml(chars);\n};\n"],"names":[],"sourceRoot":""}