ohm-js
Version:
1 lines • 416 kB
Source Map (JSON)
{"version":3,"file":"ohm.cjs","sources":["../src/common.js","../src/unicode.js","../src/pexprs-main.js","../src/errors.js","../src/util.js","../src/Interval.js","../src/InputStream.js","../src/MatchResult.js","../src/PosInfo.js","../src/Trace.js","../src/pexprs-allowsSkippingPrecedingSpace.js","../src/pexprs-assertAllApplicationsAreValid.js","../src/pexprs-assertChoicesHaveUniformArity.js","../src/pexprs-assertIteratedExprsAreNotNullable.js","../src/nodes.js","../src/pexprs-eval.js","../src/pexprs-getArity.js","../src/pexprs-outputRecipe.js","../src/pexprs-introduceParams.js","../src/pexprs-isNullable.js","../src/pexprs-substituteParams.js","../src/pexprs-toArgumentNameList.js","../src/pexprs-toDisplayString.js","../src/Failure.js","../src/pexprs-toFailure.js","../src/pexprs-toString.js","../src/CaseInsensitiveTerminal.js","../src/pexprs.js","../src/MatchState.js","../src/Matcher.js","../src/Semantics.js","../src/Grammar.js","../src/GrammarDecl.js","../src/Builder.js","../src/makeRecipe.js","built-in-rules.js","../src/main-kernel.js","ohm-grammar.js","../src/buildGrammar.js","operations-and-attributes.js","../src/semanticsDeferredInit.js","../src/findIndentation.js","../src/IndentationSensitive.js","../src/version.js","../src/main.js"],"sourcesContent":["// --------------------------------------------------------------------\n// Private Stuff\n// --------------------------------------------------------------------\n\n// Helpers\n\nconst escapeStringFor = {};\nfor (let c = 0; c < 128; c++) {\n escapeStringFor[c] = String.fromCharCode(c);\n}\nescapeStringFor[\"'\".charCodeAt(0)] = \"\\\\'\";\nescapeStringFor['\"'.charCodeAt(0)] = '\\\\\"';\nescapeStringFor['\\\\'.charCodeAt(0)] = '\\\\\\\\';\nescapeStringFor['\\b'.charCodeAt(0)] = '\\\\b';\nescapeStringFor['\\f'.charCodeAt(0)] = '\\\\f';\nescapeStringFor['\\n'.charCodeAt(0)] = '\\\\n';\nescapeStringFor['\\r'.charCodeAt(0)] = '\\\\r';\nescapeStringFor['\\t'.charCodeAt(0)] = '\\\\t';\nescapeStringFor['\\u000b'.charCodeAt(0)] = '\\\\v';\n\n// --------------------------------------------------------------------\n// Exports\n// --------------------------------------------------------------------\n\nexport function abstract(optMethodName) {\n const methodName = optMethodName || '';\n return function () {\n throw new Error(\n 'this method ' +\n methodName +\n ' is abstract! ' +\n '(it has no implementation in class ' +\n this.constructor.name +\n ')'\n );\n };\n}\n\nexport function assert(cond, message) {\n if (!cond) {\n throw new Error(message || 'Assertion failed');\n }\n}\n\n// Define a lazily-computed, non-enumerable property named `propName`\n// on the object `obj`. `getterFn` will be called to compute the value the\n// first time the property is accessed.\nexport function defineLazyProperty(obj, propName, getterFn) {\n let memo;\n Object.defineProperty(obj, propName, {\n get() {\n if (!memo) {\n memo = getterFn.call(this);\n }\n return memo;\n },\n });\n}\n\nexport function clone(obj) {\n if (obj) {\n return Object.assign({}, obj);\n }\n return obj;\n}\n\nexport function repeatFn(fn, n) {\n const arr = [];\n while (n-- > 0) {\n arr.push(fn());\n }\n return arr;\n}\n\nexport function repeatStr(str, n) {\n return new Array(n + 1).join(str);\n}\n\nexport function repeat(x, n) {\n return repeatFn(() => x, n);\n}\n\nexport function getDuplicates(array) {\n const duplicates = [];\n for (let idx = 0; idx < array.length; idx++) {\n const x = array[idx];\n if (array.lastIndexOf(x) !== idx && duplicates.indexOf(x) < 0) {\n duplicates.push(x);\n }\n }\n return duplicates;\n}\n\nexport function copyWithoutDuplicates(array) {\n const noDuplicates = [];\n array.forEach(entry => {\n if (noDuplicates.indexOf(entry) < 0) {\n noDuplicates.push(entry);\n }\n });\n return noDuplicates;\n}\n\nexport function isSyntactic(ruleName) {\n const firstChar = ruleName[0];\n return firstChar === firstChar.toUpperCase();\n}\n\nexport function isLexical(ruleName) {\n return !isSyntactic(ruleName);\n}\n\nexport function padLeft(str, len, optChar) {\n const ch = optChar || ' ';\n if (str.length < len) {\n return repeatStr(ch, len - str.length) + str;\n }\n return str;\n}\n\n// StringBuffer\n\nexport function StringBuffer() {\n this.strings = [];\n}\n\nStringBuffer.prototype.append = function (str) {\n this.strings.push(str);\n};\n\nStringBuffer.prototype.contents = function () {\n return this.strings.join('');\n};\n\nconst escapeUnicode = str => String.fromCodePoint(parseInt(str, 16));\n\nexport function unescapeCodePoint(s) {\n if (s.charAt(0) === '\\\\') {\n switch (s.charAt(1)) {\n case 'b':\n return '\\b';\n case 'f':\n return '\\f';\n case 'n':\n return '\\n';\n case 'r':\n return '\\r';\n case 't':\n return '\\t';\n case 'v':\n return '\\v';\n case 'x':\n return escapeUnicode(s.slice(2, 4));\n case 'u':\n return s.charAt(2) === '{'\n ? escapeUnicode(s.slice(3, -1))\n : escapeUnicode(s.slice(2, 6));\n default:\n return s.charAt(1);\n }\n } else {\n return s;\n }\n}\n\n// Helper for producing a description of an unknown object in a safe way.\n// Especially useful for error messages where an unexpected type of object was encountered.\nexport function unexpectedObjToString(obj) {\n if (obj == null) {\n return String(obj);\n }\n const baseToString = Object.prototype.toString.call(obj);\n try {\n let typeName;\n if (obj.constructor && obj.constructor.name) {\n typeName = obj.constructor.name;\n } else if (baseToString.indexOf('[object ') === 0) {\n typeName = baseToString.slice(8, -1); // Extract e.g. \"Array\" from \"[object Array]\".\n } else {\n typeName = typeof obj;\n }\n return typeName + ': ' + JSON.stringify(String(obj));\n } catch {\n return baseToString;\n }\n}\n\nexport function checkNotNull(obj, message = 'unexpected null value') {\n if (obj == null) {\n throw new Error(message);\n }\n return obj;\n}\n","// The full list of categories from:\n// https://www.unicode.org/Public/UCD/latest/ucd/extracted/DerivedGeneralCategory.txt.\n\nconst toRegExp = val => new RegExp(String.raw`\\p{${val}}`, 'u');\n\n/*\n grep -v '^#' DerivedGeneralCategory.txt \\\n | cut -d';' -f2 \\\n | awk 'NF{print $1}' \\\n | sort -u \\\n | awk '{printf \"\\x27%s\\x27,\\n\",$1}'\n */\n\nexport const UnicodeCategories = Object.fromEntries(\n [\n 'Cc',\n 'Cf',\n 'Cn',\n 'Co',\n 'Cs',\n 'Ll',\n 'Lm',\n 'Lo',\n 'Lt',\n 'Lu',\n 'Mc',\n 'Me',\n 'Mn',\n 'Nd',\n 'Nl',\n 'No',\n 'Pc',\n 'Pd',\n 'Pe',\n 'Pf',\n 'Pi',\n 'Po',\n 'Ps',\n 'Sc',\n 'Sk',\n 'Sm',\n 'So',\n 'Zl',\n 'Zp',\n 'Zs',\n ].map(cat => [cat, toRegExp(cat)])\n);\nUnicodeCategories['Ltmo'] = /\\p{Lt}|\\p{Lm}|\\p{Lo}/u;\n\n// We only support a few of these for now, but could add more later.\n// See https://www.unicode.org/Public/UCD/latest/ucd/PropertyAliases.txt\nexport const UnicodeBinaryProperties = Object.fromEntries(\n ['XID_Start', 'XID_Continue', 'White_Space'].map(prop => [prop, toRegExp(prop)])\n);\n","import {UnicodeBinaryProperties, UnicodeCategories} from './unicode.js';\nimport * as common from './common.js';\n\n// --------------------------------------------------------------------\n// Private stuff\n// --------------------------------------------------------------------\n\n// General stuff\n\nexport class PExpr {\n constructor() {\n if (this.constructor === PExpr) {\n throw new Error(\"PExpr cannot be instantiated -- it's abstract\");\n }\n }\n\n // Set the `source` property to the interval containing the source for this expression.\n withSource(interval) {\n if (interval) {\n this.source = interval.trimmed();\n }\n return this;\n }\n}\n\n// Any\n\nexport const any = Object.create(PExpr.prototype);\n\n// End\n\nexport const end = Object.create(PExpr.prototype);\n\n// Terminals\n\nexport class Terminal extends PExpr {\n constructor(obj) {\n super();\n this.obj = obj;\n }\n}\n\n// Ranges\n\nexport class Range extends PExpr {\n constructor(from, to) {\n super();\n this.from = from;\n this.to = to;\n // If either `from` or `to` is made up of multiple code units, then\n // the range should consume a full code point, not a single code unit.\n this.matchCodePoint = from.length > 1 || to.length > 1;\n }\n}\n\n// Parameters\n\nexport class Param extends PExpr {\n constructor(index) {\n super();\n this.index = index;\n }\n}\n\n// Alternation\n\nexport class Alt extends PExpr {\n constructor(terms) {\n super();\n this.terms = terms;\n }\n}\n\n// Extend is an implementation detail of rule extension\n\nexport class Extend extends Alt {\n constructor(superGrammar, name, body) {\n const origBody = superGrammar.rules[name].body;\n super([body, origBody]);\n\n this.superGrammar = superGrammar;\n this.name = name;\n this.body = body;\n }\n}\n\n// Splice is an implementation detail of rule overriding with the `...` operator.\nexport class Splice extends Alt {\n constructor(superGrammar, ruleName, beforeTerms, afterTerms) {\n const origBody = superGrammar.rules[ruleName].body;\n super([...beforeTerms, origBody, ...afterTerms]);\n\n this.superGrammar = superGrammar;\n this.ruleName = ruleName;\n this.expansionPos = beforeTerms.length;\n }\n}\n\n// Sequences\n\nexport class Seq extends PExpr {\n constructor(factors) {\n super();\n this.factors = factors;\n }\n}\n\n// Iterators and optionals\n\nexport class Iter extends PExpr {\n constructor(expr) {\n super();\n this.expr = expr;\n }\n}\n\nexport class Star extends Iter {}\nexport class Plus extends Iter {}\nexport class Opt extends Iter {}\n\nStar.prototype.operator = '*';\nPlus.prototype.operator = '+';\nOpt.prototype.operator = '?';\n\nStar.prototype.minNumMatches = 0;\nPlus.prototype.minNumMatches = 1;\nOpt.prototype.minNumMatches = 0;\n\nStar.prototype.maxNumMatches = Number.POSITIVE_INFINITY;\nPlus.prototype.maxNumMatches = Number.POSITIVE_INFINITY;\nOpt.prototype.maxNumMatches = 1;\n\n// Predicates\n\nexport class Not extends PExpr {\n constructor(expr) {\n super();\n this.expr = expr;\n }\n}\n\nexport class Lookahead extends PExpr {\n constructor(expr) {\n super();\n this.expr = expr;\n }\n}\n\n// \"Lexification\"\n\nexport class Lex extends PExpr {\n constructor(expr) {\n super();\n this.expr = expr;\n }\n}\n\n// Rule application\n\nexport class Apply extends PExpr {\n constructor(ruleName, args = []) {\n super();\n this.ruleName = ruleName;\n this.args = args;\n }\n\n isSyntactic() {\n return common.isSyntactic(this.ruleName);\n }\n\n // This method just caches the result of `this.toString()` in a non-enumerable property.\n toMemoKey() {\n if (!this._memoKey) {\n Object.defineProperty(this, '_memoKey', {value: this.toString()});\n }\n return this._memoKey;\n }\n}\n\n// Unicode character\n\nexport class UnicodeChar extends PExpr {\n constructor(categoryOrProp) {\n super();\n this.categoryOrProp = categoryOrProp;\n if (categoryOrProp in UnicodeCategories) {\n this.pattern = UnicodeCategories[categoryOrProp];\n } else if (categoryOrProp in UnicodeBinaryProperties) {\n this.pattern = UnicodeBinaryProperties[categoryOrProp];\n } else {\n throw new Error(\n `Invalid Unicode category or property name: ${JSON.stringify(categoryOrProp)}`\n );\n }\n }\n}\n","import {assert} from './common.js';\nimport * as pexprs from './pexprs-main.js';\n\n// --------------------------------------------------------------------\n// Private stuff\n// --------------------------------------------------------------------\n\nexport function createError(message, optInterval) {\n let e;\n if (optInterval) {\n e = new Error(optInterval.getLineAndColumnMessage() + message);\n e.shortMessage = message;\n e.interval = optInterval;\n } else {\n e = new Error(message);\n }\n return e;\n}\n\n// ----------------- errors about intervals -----------------\n\nexport function intervalSourcesDontMatch() {\n return createError(\"Interval sources don't match\");\n}\n\n// ----------------- errors about grammars -----------------\n\n// Grammar syntax error\n\nexport function grammarSyntaxError(matchFailure) {\n const e = new Error();\n Object.defineProperty(e, 'message', {\n enumerable: true,\n get() {\n return matchFailure.message;\n },\n });\n Object.defineProperty(e, 'shortMessage', {\n enumerable: true,\n get() {\n return 'Expected ' + matchFailure.getExpectedText();\n },\n });\n e.interval = matchFailure.getInterval();\n return e;\n}\n\n// Undeclared grammar\n\nexport function undeclaredGrammar(grammarName, namespace, interval) {\n const message = namespace\n ? `Grammar ${grammarName} is not declared in namespace '${namespace}'`\n : 'Undeclared grammar ' + grammarName;\n return createError(message, interval);\n}\n\n// Duplicate grammar declaration\n\nexport function duplicateGrammarDeclaration(grammar, namespace) {\n return createError('Grammar ' + grammar.name + ' is already declared in this namespace');\n}\n\nexport function grammarDoesNotSupportIncrementalParsing(grammar) {\n return createError(`Grammar '${grammar.name}' does not support incremental parsing`);\n}\n\n// ----------------- rules -----------------\n\n// Undeclared rule\n\nexport function undeclaredRule(ruleName, grammarName, optInterval) {\n return createError(\n 'Rule ' + ruleName + ' is not declared in grammar ' + grammarName,\n optInterval\n );\n}\n\n// Cannot override undeclared rule\n\nexport function cannotOverrideUndeclaredRule(ruleName, grammarName, optSource) {\n return createError(\n 'Cannot override rule ' + ruleName + ' because it is not declared in ' + grammarName,\n optSource\n );\n}\n\n// Cannot extend undeclared rule\n\nexport function cannotExtendUndeclaredRule(ruleName, grammarName, optSource) {\n return createError(\n 'Cannot extend rule ' + ruleName + ' because it is not declared in ' + grammarName,\n optSource\n );\n}\n\n// Duplicate rule declaration\n\nexport function duplicateRuleDeclaration(ruleName, grammarName, declGrammarName, optSource) {\n let message =\n \"Duplicate declaration for rule '\" + ruleName + \"' in grammar '\" + grammarName + \"'\";\n if (grammarName !== declGrammarName) {\n message += \" (originally declared in '\" + declGrammarName + \"')\";\n }\n return createError(message, optSource);\n}\n\n// Wrong number of parameters\n\nexport function wrongNumberOfParameters(ruleName, expected, actual, source) {\n return createError(\n 'Wrong number of parameters for rule ' +\n ruleName +\n ' (expected ' +\n expected +\n ', got ' +\n actual +\n ')',\n source\n );\n}\n\n// Wrong number of arguments\n\nexport function wrongNumberOfArguments(ruleName, expected, actual, expr) {\n return createError(\n 'Wrong number of arguments for rule ' +\n ruleName +\n ' (expected ' +\n expected +\n ', got ' +\n actual +\n ')',\n expr\n );\n}\n\n// Duplicate parameter names\n\nexport function duplicateParameterNames(ruleName, duplicates, source) {\n return createError(\n 'Duplicate parameter names in rule ' + ruleName + ': ' + duplicates.join(', '),\n source\n );\n}\n\n// Invalid parameter expression\n\nexport function invalidParameter(ruleName, expr) {\n return createError(\n 'Invalid parameter to rule ' +\n ruleName +\n ': ' +\n expr +\n ' has arity ' +\n expr.getArity() +\n ', but parameter expressions must have arity 1',\n expr.source\n );\n}\n\n// Application of syntactic rule from lexical rule\n\nconst syntacticVsLexicalNote =\n 'NOTE: A _syntactic rule_ is a rule whose name begins with a capital letter. ' +\n 'See https://ohmjs.org/d/svl for more details.';\n\nexport function applicationOfSyntacticRuleFromLexicalContext(ruleName, applyExpr) {\n return createError(\n 'Cannot apply syntactic rule ' + ruleName + ' from here (inside a lexical context)',\n applyExpr.source\n );\n}\n\n// Lexical rule application used with applySyntactic\n\nexport function applySyntacticWithLexicalRuleApplication(applyExpr) {\n const {ruleName} = applyExpr;\n return createError(\n `applySyntactic is for syntactic rules, but '${ruleName}' is a lexical rule. ` +\n syntacticVsLexicalNote,\n applyExpr.source\n );\n}\n\n// Application of applySyntactic in a syntactic context\n\nexport function unnecessaryExperimentalApplySyntactic(applyExpr) {\n return createError(\n 'applySyntactic is not required here (in a syntactic context)',\n applyExpr.source\n );\n}\n\n// Incorrect argument type\n\nexport function incorrectArgumentType(expectedType, expr) {\n return createError('Incorrect argument type: expected ' + expectedType, expr.source);\n}\n\n// Multiple instances of the super-splice operator (`...`) in the rule body.\n\nexport function multipleSuperSplices(expr) {\n return createError(\"'...' can appear at most once in a rule body\", expr.source);\n}\n\n// Unicode code point escapes\n\nexport function invalidCodePoint(applyWrapper) {\n const node = applyWrapper._node;\n assert(node && node.isNonterminal() && node.ctorName === 'escapeChar_unicodeCodePoint');\n\n // Get an interval that covers all of the hex digits.\n const digitIntervals = applyWrapper.children.slice(1, -1).map(d => d.source);\n const fullInterval = digitIntervals[0].coverageWith(...digitIntervals.slice(1));\n return createError(\n `U+${fullInterval.contents} is not a valid Unicode code point`,\n fullInterval\n );\n}\n\n// ----------------- Kleene operators -----------------\n\nexport function kleeneExprHasNullableOperand(kleeneExpr, applicationStack) {\n const actuals =\n applicationStack.length > 0 ? applicationStack[applicationStack.length - 1].args : [];\n const expr = kleeneExpr.expr.substituteParams(actuals);\n let message =\n 'Nullable expression ' +\n expr +\n \" is not allowed inside '\" +\n kleeneExpr.operator +\n \"' (possible infinite loop)\";\n if (applicationStack.length > 0) {\n const stackTrace = applicationStack\n .map(app => new pexprs.Apply(app.ruleName, app.args))\n .join('\\n');\n message += '\\nApplication stack (most recent application last):\\n' + stackTrace;\n }\n return createError(message, kleeneExpr.expr.source);\n}\n\n// ----------------- arity -----------------\n\nexport function inconsistentArity(ruleName, expected, actual, expr) {\n return createError(\n 'Rule ' +\n ruleName +\n ' involves an alternation which has inconsistent arity ' +\n '(expected ' +\n expected +\n ', got ' +\n actual +\n ')',\n expr.source\n );\n}\n\n// ----------------- properties -----------------\n\nexport function duplicatePropertyNames(duplicates) {\n return createError('Object pattern has duplicate property names: ' + duplicates.join(', '));\n}\n\n// ----------------- constructors -----------------\n\nexport function invalidConstructorCall(grammar, ctorName, children) {\n return createError(\n 'Attempt to invoke constructor ' + ctorName + ' with invalid or unexpected arguments'\n );\n}\n\n// ----------------- convenience -----------------\n\nexport function multipleErrors(errors) {\n const messages = errors.map(e => e.message);\n return createError(['Errors:'].concat(messages).join('\\n- '), errors[0].interval);\n}\n\n// ----------------- semantic -----------------\n\nexport function missingSemanticAction(ctorName, name, type, stack) {\n let stackTrace = stack\n .slice(0, -1)\n .map(info => {\n const ans = ' ' + info[0].name + ' > ' + info[1];\n return info.length === 3 ? ans + \" for '\" + info[2] + \"'\" : ans;\n })\n .join('\\n');\n stackTrace += '\\n ' + name + ' > ' + ctorName;\n\n let moreInfo = '';\n if (ctorName === '_iter') {\n moreInfo = [\n '\\nNOTE: as of Ohm v16, there is no default action for iteration nodes — see ',\n ' https://ohmjs.org/d/dsa for details.',\n ].join('\\n');\n }\n\n const message = [\n `Missing semantic action for '${ctorName}' in ${type} '${name}'.${moreInfo}`,\n 'Action stack (most recent call last):',\n stackTrace,\n ].join('\\n');\n\n const e = createError(message);\n e.name = 'missingSemanticAction';\n return e;\n}\n\nexport function throwErrors(errors) {\n if (errors.length === 1) {\n throw errors[0];\n }\n if (errors.length > 1) {\n throw multipleErrors(errors);\n }\n}\n","import * as common from './common.js';\n\n// --------------------------------------------------------------------\n// Private stuff\n// --------------------------------------------------------------------\n\n// Given an array of numbers `arr`, return an array of the numbers as strings,\n// right-justified and padded to the same length.\nfunction padNumbersToEqualLength(arr) {\n let maxLen = 0;\n const strings = arr.map(n => {\n const str = n.toString();\n maxLen = Math.max(maxLen, str.length);\n return str;\n });\n return strings.map(s => common.padLeft(s, maxLen));\n}\n\n// Produce a new string that would be the result of copying the contents\n// of the string `src` onto `dest` at offset `offest`.\nfunction strcpy(dest, src, offset) {\n const origDestLen = dest.length;\n const start = dest.slice(0, offset);\n const end = dest.slice(offset + src.length);\n return (start + src + end).substr(0, origDestLen);\n}\n\n// Casts the underlying lineAndCol object to a formatted message string,\n// highlighting `ranges`.\nfunction lineAndColumnToMessage(...ranges) {\n const lineAndCol = this;\n const {offset} = lineAndCol;\n const {repeatStr} = common;\n\n const sb = new common.StringBuffer();\n sb.append('Line ' + lineAndCol.lineNum + ', col ' + lineAndCol.colNum + ':\\n');\n\n // An array of the previous, current, and next line numbers as strings of equal length.\n const lineNumbers = padNumbersToEqualLength([\n lineAndCol.prevLine == null ? 0 : lineAndCol.lineNum - 1,\n lineAndCol.lineNum,\n lineAndCol.nextLine == null ? 0 : lineAndCol.lineNum + 1,\n ]);\n\n // Helper for appending formatting input lines to the buffer.\n const appendLine = (num, content, prefix) => {\n sb.append(prefix + lineNumbers[num] + ' | ' + content + '\\n');\n };\n\n // Include the previous line for context if possible.\n if (lineAndCol.prevLine != null) {\n appendLine(0, lineAndCol.prevLine, ' ');\n }\n // Line that the error occurred on.\n appendLine(1, lineAndCol.line, '> ');\n\n // Build up the line that points to the offset and possible indicates one or more ranges.\n // Start with a blank line, and indicate each range by overlaying a string of `~` chars.\n const lineLen = lineAndCol.line.length;\n let indicationLine = repeatStr(' ', lineLen + 1);\n for (let i = 0; i < ranges.length; ++i) {\n let startIdx = ranges[i][0];\n let endIdx = ranges[i][1];\n common.assert(startIdx >= 0 && startIdx <= endIdx, 'range start must be >= 0 and <= end');\n\n const lineStartOffset = offset - lineAndCol.colNum + 1;\n startIdx = Math.max(0, startIdx - lineStartOffset);\n endIdx = Math.min(endIdx - lineStartOffset, lineLen);\n\n indicationLine = strcpy(indicationLine, repeatStr('~', endIdx - startIdx), startIdx);\n }\n const gutterWidth = 2 + lineNumbers[1].length + 3;\n sb.append(repeatStr(' ', gutterWidth));\n indicationLine = strcpy(indicationLine, '^', lineAndCol.colNum - 1);\n sb.append(indicationLine.replace(/ +$/, '') + '\\n');\n\n // Include the next line for context if possible.\n if (lineAndCol.nextLine != null) {\n appendLine(2, lineAndCol.nextLine, ' ');\n }\n return sb.contents();\n}\n\n// --------------------------------------------------------------------\n// Exports\n// --------------------------------------------------------------------\n\nlet builtInRulesCallbacks = [];\n\n// Since Grammar.BuiltInRules is bootstrapped, most of Ohm can't directly depend it.\n// This function allows modules that do depend on the built-in rules to register a callback\n// that will be called later in the initialization process.\nexport function awaitBuiltInRules(cb) {\n builtInRulesCallbacks.push(cb);\n}\n\nexport function announceBuiltInRules(grammar) {\n builtInRulesCallbacks.forEach(cb => {\n cb(grammar);\n });\n builtInRulesCallbacks = null;\n}\n\n// Return an object with the line and column information for the given\n// offset in `str`.\nexport function getLineAndColumn(str, offset) {\n let lineNum = 1;\n let colNum = 1;\n\n let currOffset = 0;\n let lineStartOffset = 0;\n\n let nextLine = null;\n let prevLine = null;\n let prevLineStartOffset = -1;\n\n while (currOffset < offset) {\n const c = str.charAt(currOffset++);\n if (c === '\\n') {\n lineNum++;\n colNum = 1;\n prevLineStartOffset = lineStartOffset;\n lineStartOffset = currOffset;\n } else if (c !== '\\r') {\n colNum++;\n }\n }\n\n // Find the end of the target line.\n let lineEndOffset = str.indexOf('\\n', lineStartOffset);\n if (lineEndOffset === -1) {\n lineEndOffset = str.length;\n } else {\n // Get the next line.\n const nextLineEndOffset = str.indexOf('\\n', lineEndOffset + 1);\n nextLine =\n nextLineEndOffset === -1\n ? str.slice(lineEndOffset)\n : str.slice(lineEndOffset, nextLineEndOffset);\n // Strip leading and trailing EOL char(s).\n nextLine = nextLine.replace(/^\\r?\\n/, '').replace(/\\r$/, '');\n }\n\n // Get the previous line.\n if (prevLineStartOffset >= 0) {\n // Strip trailing EOL char(s).\n prevLine = str.slice(prevLineStartOffset, lineStartOffset).replace(/\\r?\\n$/, '');\n }\n\n // Get the target line, stripping a trailing carriage return if necessary.\n const line = str.slice(lineStartOffset, lineEndOffset).replace(/\\r$/, '');\n\n return {\n offset,\n lineNum,\n colNum,\n line,\n prevLine,\n nextLine,\n toString: lineAndColumnToMessage,\n };\n}\n\n// Return a nicely-formatted string describing the line and column for the\n// given offset in `str` highlighting `ranges`.\nexport function getLineAndColumnMessage(str, offset, ...ranges) {\n return getLineAndColumn(str, offset).toString(...ranges);\n}\n\nexport const uniqueId = (() => {\n let idCounter = 0;\n return prefix => '' + prefix + idCounter++;\n})();\n","import {assert} from './common.js';\nimport * as errors from './errors.js';\nimport * as util from './util.js';\n\n// --------------------------------------------------------------------\n// Private stuff\n// --------------------------------------------------------------------\n\nexport class Interval {\n constructor(sourceString, startIdx, endIdx) {\n // Store the full source in a non-enumerable property, so that when\n // grammars and other objects are printed in the REPL, it's not\n // cluttered with multiple copies of the same long string.\n Object.defineProperty(this, '_sourceString', {\n value: sourceString,\n configurable: false,\n enumerable: false,\n writable: false,\n });\n this.startIdx = startIdx;\n this.endIdx = endIdx;\n }\n\n get sourceString() {\n return this._sourceString;\n }\n\n get contents() {\n if (this._contents === undefined) {\n this._contents = this.sourceString.slice(this.startIdx, this.endIdx);\n }\n return this._contents;\n }\n\n get length() {\n return this.endIdx - this.startIdx;\n }\n\n coverageWith(...intervals) {\n return Interval.coverage(...intervals, this);\n }\n\n collapsedLeft() {\n return new Interval(this.sourceString, this.startIdx, this.startIdx);\n }\n\n collapsedRight() {\n return new Interval(this.sourceString, this.endIdx, this.endIdx);\n }\n\n getLineAndColumn() {\n return util.getLineAndColumn(this.sourceString, this.startIdx);\n }\n\n getLineAndColumnMessage() {\n const range = [this.startIdx, this.endIdx];\n return util.getLineAndColumnMessage(this.sourceString, this.startIdx, range);\n }\n\n // Returns an array of 0, 1, or 2 intervals that represents the result of the\n // interval difference operation.\n minus(that) {\n if (this.sourceString !== that.sourceString) {\n throw errors.intervalSourcesDontMatch();\n } else if (this.startIdx === that.startIdx && this.endIdx === that.endIdx) {\n // `this` and `that` are the same interval!\n return [];\n } else if (this.startIdx < that.startIdx && that.endIdx < this.endIdx) {\n // `that` splits `this` into two intervals\n return [\n new Interval(this.sourceString, this.startIdx, that.startIdx),\n new Interval(this.sourceString, that.endIdx, this.endIdx),\n ];\n } else if (this.startIdx < that.endIdx && that.endIdx < this.endIdx) {\n // `that` contains a prefix of `this`\n return [new Interval(this.sourceString, that.endIdx, this.endIdx)];\n } else if (this.startIdx < that.startIdx && that.startIdx < this.endIdx) {\n // `that` contains a suffix of `this`\n return [new Interval(this.sourceString, this.startIdx, that.startIdx)];\n } else {\n // `that` and `this` do not overlap\n return [this];\n }\n }\n\n // Returns a new Interval that has the same extent as this one, but which is relative\n // to `that`, an Interval that fully covers this one.\n relativeTo(that) {\n if (this.sourceString !== that.sourceString) {\n throw errors.intervalSourcesDontMatch();\n }\n assert(\n this.startIdx >= that.startIdx && this.endIdx <= that.endIdx,\n 'other interval does not cover this one'\n );\n return new Interval(\n this.sourceString,\n this.startIdx - that.startIdx,\n this.endIdx - that.startIdx\n );\n }\n\n // Returns a new Interval which contains the same contents as this one,\n // but with whitespace trimmed from both ends.\n trimmed() {\n const {contents} = this;\n const startIdx = this.startIdx + contents.match(/^\\s*/)[0].length;\n const endIdx = this.endIdx - contents.match(/\\s*$/)[0].length;\n return new Interval(this.sourceString, startIdx, endIdx);\n }\n\n subInterval(offset, len) {\n const newStartIdx = this.startIdx + offset;\n return new Interval(this.sourceString, newStartIdx, newStartIdx + len);\n }\n}\n\nInterval.coverage = function (firstInterval, ...intervals) {\n let {startIdx, endIdx} = firstInterval;\n for (const interval of intervals) {\n if (interval.sourceString !== firstInterval.sourceString) {\n throw errors.intervalSourcesDontMatch();\n } else {\n startIdx = Math.min(startIdx, interval.startIdx);\n endIdx = Math.max(endIdx, interval.endIdx);\n }\n }\n return new Interval(firstInterval.sourceString, startIdx, endIdx);\n};\n","import {Interval} from './Interval.js';\n\nconst MAX_CHAR_CODE = 0xffff;\nexport const MAX_CODE_POINT = 0x10ffff;\n\nexport class InputStream {\n constructor(source) {\n this.source = source;\n this.pos = 0;\n this.examinedLength = 0;\n }\n\n atEnd() {\n const ans = this.pos >= this.source.length;\n this.examinedLength = Math.max(this.examinedLength, this.pos + 1);\n return ans;\n }\n\n next() {\n const ans = this.source[this.pos++];\n this.examinedLength = Math.max(this.examinedLength, this.pos);\n return ans;\n }\n\n nextCharCode() {\n const nextChar = this.next();\n return nextChar && nextChar.charCodeAt(0);\n }\n\n nextCodePoint() {\n const cp = this.source.slice(this.pos++).codePointAt(0);\n // If the code point is beyond plane 0, it takes up two characters.\n if (cp > MAX_CHAR_CODE) {\n this.pos += 1;\n }\n this.examinedLength = Math.max(this.examinedLength, this.pos);\n return cp;\n }\n\n matchString(s, optIgnoreCase) {\n let idx;\n if (optIgnoreCase) {\n /*\n Case-insensitive comparison is a tricky business. Some notable gotchas include the\n \"Turkish I\" problem (http://www.i18nguy.com/unicode/turkish-i18n.html) and the fact\n that the German Esszet (ß) turns into \"SS\" in upper case.\n\n This is intended to be a locale-invariant comparison, which means it may not obey\n locale-specific expectations (e.g. \"i\" => \"İ\").\n\n See also https://unicode.org/faq/casemap_charprop.html#casemap\n */\n for (idx = 0; idx < s.length; idx++) {\n const actual = this.next();\n const expected = s[idx];\n if (actual == null || actual.toUpperCase() !== expected.toUpperCase()) {\n return false;\n }\n }\n return true;\n }\n // Default is case-sensitive comparison.\n for (idx = 0; idx < s.length; idx++) {\n if (this.next() !== s[idx]) {\n return false;\n }\n }\n return true;\n }\n\n sourceSlice(startIdx, endIdx) {\n return this.source.slice(startIdx, endIdx);\n }\n\n interval(startIdx, optEndIdx) {\n return new Interval(this.source, startIdx, optEndIdx ? optEndIdx : this.pos);\n }\n}\n","import * as common from './common.js';\nimport * as util from './util.js';\nimport {Interval} from './Interval.js';\n\n// --------------------------------------------------------------------\n// Private stuff\n// --------------------------------------------------------------------\n\nexport class MatchResult {\n constructor(\n matcher,\n input,\n startExpr,\n cst,\n cstOffset,\n rightmostFailurePosition,\n optRecordedFailures\n ) {\n this.matcher = matcher;\n this.input = input;\n this.startExpr = startExpr;\n this._cst = cst;\n this._cstOffset = cstOffset;\n this._rightmostFailurePosition = rightmostFailurePosition;\n this._rightmostFailures = optRecordedFailures;\n\n if (this.failed()) {\n common.defineLazyProperty(this, 'message', function () {\n const detail = 'Expected ' + this.getExpectedText();\n return (\n util.getLineAndColumnMessage(this.input, this.getRightmostFailurePosition()) + detail\n );\n });\n common.defineLazyProperty(this, 'shortMessage', function () {\n const detail = 'expected ' + this.getExpectedText();\n const errorInfo = util.getLineAndColumn(\n this.input,\n this.getRightmostFailurePosition()\n );\n return 'Line ' + errorInfo.lineNum + ', col ' + errorInfo.colNum + ': ' + detail;\n });\n }\n }\n\n succeeded() {\n return !!this._cst;\n }\n\n failed() {\n return !this.succeeded();\n }\n\n getRightmostFailurePosition() {\n return this._rightmostFailurePosition;\n }\n\n getRightmostFailures() {\n if (!this._rightmostFailures) {\n this.matcher.setInput(this.input);\n const matchResultWithFailures = this.matcher._match(this.startExpr, {\n tracing: false,\n positionToRecordFailures: this.getRightmostFailurePosition(),\n });\n this._rightmostFailures = matchResultWithFailures.getRightmostFailures();\n }\n return this._rightmostFailures;\n }\n\n toString() {\n return this.succeeded()\n ? '[match succeeded]'\n : '[match failed at position ' + this.getRightmostFailurePosition() + ']';\n }\n\n // Return a string summarizing the expected contents of the input stream when\n // the match failure occurred.\n getExpectedText() {\n if (this.succeeded()) {\n throw new Error('cannot get expected text of a successful MatchResult');\n }\n\n const sb = new common.StringBuffer();\n let failures = this.getRightmostFailures();\n\n // Filter out the fluffy failures to make the default error messages more useful\n failures = failures.filter(failure => !failure.isFluffy());\n\n for (let idx = 0; idx < failures.length; idx++) {\n if (idx > 0) {\n if (idx === failures.length - 1) {\n sb.append(failures.length > 2 ? ', or ' : ' or ');\n } else {\n sb.append(', ');\n }\n }\n sb.append(failures[idx].toString());\n }\n return sb.contents();\n }\n\n getInterval() {\n const pos = this.getRightmostFailurePosition();\n return new Interval(this.input, pos, pos);\n }\n}\n","export class PosInfo {\n constructor() {\n this.applicationMemoKeyStack = []; // active applications at this position\n this.memo = {};\n this.maxExaminedLength = 0;\n this.maxRightmostFailureOffset = -1;\n this.currentLeftRecursion = undefined;\n }\n\n isActive(application) {\n return this.applicationMemoKeyStack.indexOf(application.toMemoKey()) >= 0;\n }\n\n enter(application) {\n this.applicationMemoKeyStack.push(application.toMemoKey());\n }\n\n exit() {\n this.applicationMemoKeyStack.pop();\n }\n\n startLeftRecursion(headApplication, memoRec) {\n memoRec.isLeftRecursion = true;\n memoRec.headApplication = headApplication;\n memoRec.nextLeftRecursion = this.currentLeftRecursion;\n this.currentLeftRecursion = memoRec;\n\n const {applicationMemoKeyStack} = this;\n const indexOfFirstInvolvedRule =\n applicationMemoKeyStack.indexOf(headApplication.toMemoKey()) + 1;\n const involvedApplicationMemoKeys = applicationMemoKeyStack.slice(\n indexOfFirstInvolvedRule\n );\n\n memoRec.isInvolved = function (applicationMemoKey) {\n return involvedApplicationMemoKeys.indexOf(applicationMemoKey) >= 0;\n };\n\n memoRec.updateInvolvedApplicationMemoKeys = function () {\n for (let idx = indexOfFirstInvolvedRule; idx < applicationMemoKeyStack.length; idx++) {\n const applicationMemoKey = applicationMemoKeyStack[idx];\n if (!this.isInvolved(applicationMemoKey)) {\n involvedApplicationMemoKeys.push(applicationMemoKey);\n }\n }\n };\n }\n\n endLeftRecursion() {\n this.currentLeftRecursion = this.currentLeftRecursion.nextLeftRecursion;\n }\n\n // Note: this method doesn't get called for the \"head\" of a left recursion -- for LR heads,\n // the memoized result (which starts out being a failure) is always used.\n shouldUseMemoizedResult(memoRec) {\n if (!memoRec.isLeftRecursion) {\n return true;\n }\n const {applicationMemoKeyStack} = this;\n for (let idx = 0; idx < applicationMemoKeyStack.length; idx++) {\n const applicationMemoKey = applicationMemoKeyStack[idx];\n if (memoRec.isInvolved(applicationMemoKey)) {\n return false;\n }\n }\n return true;\n }\n\n memoize(memoKey, memoRec) {\n this.memo[memoKey] = memoRec;\n this.maxExaminedLength = Math.max(this.maxExaminedLength, memoRec.examinedLength);\n this.maxRightmostFailureOffset = Math.max(\n this.maxRightmostFailureOffset,\n memoRec.rightmostFailureOffset\n );\n return memoRec;\n }\n\n clearObsoleteEntries(pos, invalidatedIdx) {\n if (pos + this.maxExaminedLength <= invalidatedIdx) {\n // Optimization: none of the rule applications that were memoized here examined the\n // interval of the input that changed, so nothing has to be invalidated.\n return;\n }\n\n const {memo} = this;\n this.maxExaminedLength = 0;\n this.maxRightmostFailureOffset = -1;\n Object.keys(memo).forEach(k => {\n const memoRec = memo[k];\n if (pos + memoRec.examinedLength > invalidatedIdx) {\n delete memo[k];\n } else {\n this.maxExaminedLength = Math.max(this.maxExaminedLength, memoRec.examinedLength);\n this.maxRightmostFailureOffset = Math.max(\n this.maxRightmostFailureOffset,\n memoRec.rightmostFailureOffset\n );\n }\n });\n }\n}\n","import {Interval} from './Interval.js';\nimport * as common from './common.js';\n\n// --------------------------------------------------------------------\n// Private stuff\n// --------------------------------------------------------------------\n\n// Unicode characters that are used in the `toString` output.\nconst BALLOT_X = '\\u2717';\nconst CHECK_MARK = '\\u2713';\nconst DOT_OPERATOR = '\\u22C5';\nconst RIGHTWARDS_DOUBLE_ARROW = '\\u21D2';\nconst SYMBOL_FOR_HORIZONTAL_TABULATION = '\\u2409';\nconst SYMBOL_FOR_LINE_FEED = '\\u240A';\nconst SYMBOL_FOR_CARRIAGE_RETURN = '\\u240D';\n\nconst Flags = {\n succeeded: 1 << 0,\n isRootNode: 1 << 1,\n isImplicitSpaces: 1 << 2,\n isMemoized: 1 << 3,\n isHeadOfLeftRecursion: 1 << 4,\n terminatesLR: 1 << 5,\n};\n\nfunction spaces(n) {\n return common.repeat(' ', n).join('');\n}\n\n// Return a string representation of a portion of `input` at offset `pos`.\n// The result will contain exactly `len` characters.\nfunction getInputExcerpt(input, pos, len) {\n const excerpt = asEscapedString(input.slice(pos, pos + len));\n\n // Pad the output if necessary.\n if (excerpt.length < len) {\n return excerpt + common.repeat(' ', len - excerpt.length).join('');\n }\n return excerpt;\n}\n\nfunction asEscapedString(obj) {\n if (typeof obj === 'string') {\n // Replace non-printable characters with visible symbols.\n return obj\n .replace(/ /g, DOT_OPERATOR)\n .replace(/\\t/g, SYMBOL_FOR_HORIZONTAL_TABULATION)\n .replace(/\\n/g, SYMBOL_FOR_LINE_FEED)\n .replace(/\\r/g, SYMBOL_FOR_CARRIAGE_RETURN);\n }\n return String(obj);\n}\n\n// ----------------- Trace -----------------\n\nexport class Trace {\n constructor(input, pos1, pos2, expr, succeeded, bindings, optChildren) {\n this.input = input;\n this.pos = this.pos1 = pos1;\n this.pos2 = pos2;\n this.source = new Interval(input, pos1, pos2);\n this.expr = expr;\n this.bindings = bindings;\n this.children = optChildren || [];\n this.terminatingLREntry = null;\n\n this._flags = succeeded ? Flags.succeeded : 0;\n }\n\n get displayString() {\n return this.expr.toDisplayString();\n }\n\n clone() {\n return this.cloneWithExpr(this.expr);\n }\n\n cloneWithExpr(expr) {\n const ans = new Trace(\n this.input,\n this.pos,\n this.pos2,\n expr,\n this.succeeded,\n this.bindings,\n this.children\n );\n\n ans.isHeadOfLeftRecursion = this.isHeadOfLeftRecursion;\n ans.isImplicitSpaces = this.isImplicitSpaces;\n ans.isMemoized = this.isMemoized;\n ans.isRootNode = this.isRootNode;\n ans.terminatesLR = this.terminatesLR;\n ans.terminatingLREntry = this.terminatingLREntry;\n return ans;\n }\n\n // Record the trace information for the terminating condition of the LR loop.\n recordLRTermination(ruleBodyTrace, value) {\n this.terminatingLREntry = new Trace(\n this.input,\n this.pos,\n this.pos2,\n this.expr,\n false,\n [value],\n [ruleBodyTrace]\n );\n this.terminatingLREntry.terminatesLR = true;\n }\n\n // Recursively traverse this trace node and all its descendents, calling a visitor function\n // for each node that is visited. If `vistorObjOrFn` is an object, then its 'enter' property\n // is a function to call before visiting the children of a node, and its 'exit' property is\n // a function to call afterwards. If `visitorObjOrFn` is a function, it represents the 'enter'\n // function.\n //\n // The functions are called with three arguments: the Trace node, its parent Trace, and a number\n // representing the depth of the node in the tree. (The root node has depth 0.) `optThisArg`, if\n // specified, is the value to use for `this` when executing the visitor functions.\n walk(visitorObjOrFn, optThisArg) {\n let visitor = visitorObjOrFn;\n if (typeof visitor === 'function') {\n visitor = {enter: visitor};\n }\n\n function _walk(node, parent, depth) {\n let recurse = true;\n if (visitor.enter) {\n if (visitor.enter.call(optThisArg, node, parent, depth) === Trace.prototype.SKIP) {\n recurse = false;\n }\n }\n if (recurse) {\n node.children.forEach(child => {\n _walk(child, node, depth + 1);\n });\n if (visitor.exit) {\n visitor.exit.call(optThisArg, node, parent, depth);\n }\n }\n }\n if (this.isRootNode) {\n // Don't visit the root node itself, only its children.\n this.children.forEach(c => {\n _walk(c, null, 0);\n });\n } else {\n _walk(this, null, 0);\n }\n }\n\n // Return a string representation of the trace.\n // Sample:\n // 12⋅+⋅2⋅*⋅3 ✓ exp ⇒ \"12\"\n // 12⋅+⋅2⋅*⋅3 ✓ addExp (LR) ⇒ \"12\"\n // 12⋅+⋅2⋅*⋅3 ✗ addExp_plus\n toString() {\n const sb = new common.StringBuffer();\n this.walk((node, parent, depth) => {\n if (!node) {\n return this.SKIP;\n }\n const ctorName = node.expr.constructor.name;\n // Don't print anything for Alt nodes.\n if (ctorName === 'Alt') {\n return;\n }\n sb.append(getInputExcerpt(node.input, node.pos, 10) + spaces(depth * 2 + 1));\n sb.append((node.succeeded ? CHECK_MARK : BALLOT_X) + ' ' + node.displayString);\n if (node.isHeadOfLeftRecursion) {\n sb.append(' (LR)');\n }\n if (node.succeeded) {\n const contents = asEscapedString(node.source.contents);\n sb.append(' ' + RIGHTWARDS_DOUBLE_ARROW + ' ');\n sb.append(typeof contents === 'string' ? '\"' + contents + '\"' : contents);\n }\n sb.append('\\n');\n });\n return sb.contents();\n }\n}\n\n// A value that can be returned from visitor functions to indicate that a\n// node should not be recursed into.\nTrace.prototype.SKIP = {};\n\n// For convenience, create a getter and setter for the boolean flags in `Flags`.\nObject.keys(Flags).forEach(name => {\n const mask = Flags[name];\n Object.defineProperty(Trace.prototype, name, {\n get() {\n return (this._flags & mask) !== 0;\n },\n set(val) {\n if (val) {\n this._flags |= mask;\n } else {\n this._flags &= ~mask;\n }\n },\n });\n});\n","import {abstract} from './common.js';\nimport * as pexprs from './pexprs-main.js';\n\n// --------------------------------------------------------------------\n// Operations\n// --------------------------------------------------------------------\n\n/*\n Return true if we should skip spaces preceding this expression in a syntactic context.\n*/\npexprs.PExpr.prototype.allowsSkippingPrecedingSpace = abstract('allowsSkippingPrecedingSpace');\n\n/*\n Generally, these are all first-order expressions and (with the exception of Apply)\n directly read from the input stream.\n*/\npexprs.any.allowsSkippingPrecedingSpace =\n pexprs.end.allowsSkippingPrecedingSpace =\n pexprs.Apply.prototype.allowsSkippingPrecedingSpace =\n pexprs.Terminal.prototype.allowsSkippingPrecedingSpace =\n pexprs.Range.prototype.allowsSkippingPrecedingSpace =\n pexprs.UnicodeChar.prototype.allowsSkippingPrecedingSpace =\n function () {\n return true;\n };\n\n/*\n Higher-order expressions that don't directly consume input.\n*/\npexprs.Alt.prototype.allowsSkippingPrecedingSpace =\n pexprs.Iter.prototype.allowsSkippingPrecedingSpace =\n pexprs.Lex.prototype.allowsSkippingPrecedingSpace =\n pexprs.Lookahead.prototype.allowsSkippingPrecedingSpace =\n pexprs.Not.prototype.allowsSkippingPrecedingSpace =\n pexprs.Param.prototype.allowsSkippingPrecedingSpace =\n pexprs.Seq.prototype.allowsSkippingPrecedingSpace =\n function () {\n return false;\n };\n","import {abstract, isSyntactic} from './common.js';\nimport * as errors from './errors.js';\nimport * as pexprs from './pexprs-main.js';\nimport * as util from './util.js';\n\nlet BuiltInRules;\n\nutil.awaitBuiltInRules(g => {\n BuiltInRules = g;\n});\n\n// --------------------------------------------------------------------\n// Operations\n// --------------------------------------------------------------------\n\nlet lexifyCount;\n\npexprs.PExpr.prototype.assertAllApplicationsAreValid = function (ruleName, grammar) {\n lexifyCount = 0;\n this._assertAllApplicationsAreValid(ruleName, grammar);\n};\n\npexprs.PExpr.prototype._assertAllApplicationsAreValid = abstract(\n '_assertAllApplicationsAreValid'\n);\n\npexprs.any._assertAllApplicationsAreValid =\n pexprs.end._assertAllApplicationsAreValid =\n pexprs.Terminal.prototype._assertAllApplicationsAreValid =\n pexprs.Range.prototype._assertAllApplicationsAreValid =\n pexprs.Param.prototype._assertAllApplicationsAreValid =\n pexprs.UnicodeChar.prototype._assertAllApplicationsAreValid =\n function (ruleName, grammar) {\n // no-op\n };\n\npexprs.Lex.prototype._assertAllApplicationsAreValid = function (ruleName, grammar) {\n lexifyCount++;\n this.expr._assertAllApplicationsAreValid(ruleName, grammar);\n lexifyCount--;\n};\n\npexprs.Alt.prototype._assertAllApplicationsAreValid = function (ruleName, grammar) {\n for (let idx = 0; idx < this.terms.length; idx++) {\n this.terms[idx]._assertAllApplicationsAreValid(ruleName, grammar);\n }\n};\n\npexprs.Seq.prototype._assertAllApplicationsAreValid = function (ruleName, grammar) {\n for (let idx = 0; idx < this.factors.length; idx++) {\n this.factors[idx]._assertAllApplicationsAreValid(ruleName, grammar);\n }\n};\n\npexprs.Iter.prototype._assertAllApplicationsAreValid =\n pexprs.Not.prototype._assertAllApplicationsAreValid =\n pexprs.Lookahead.prototype._assertAllApplicationsAreValid =\n function (ruleName, grammar) {\n this.expr._assertAllApplicationsAreValid(ruleName, grammar);\n };\n\npexprs.Apply.prototype._assertAllApplicationsAreValid = function (\n ruleName,\n grammar,\n skipSyntacticCheck = false\n) {\n const ruleInfo = grammar.rules[this.ruleName];\n const isContextSyntactic = isSyntactic(ruleName) && lexifyCount === 0;\n\n // Make sure that the r