From 8d2f0dfb0d5a0006f3c8ba2a41669d2aadbd2add Mon Sep 17 00:00:00 2001 From: Ger Hobbelt Date: Mon, 31 Jul 2017 05:05:08 +0200 Subject: [PATCH] updated NPM packages --- lib/util/ebnf-parser.js | 81 - lib/util/ebnf-transform.js | 374 - lib/util/lex-parser.js | 5263 ----- lib/util/parser.js | 5180 ----- lib/util/regexp-lexer.js | 2780 --- lib/util/regexp-set-management.js | 999 - lib/util/safe-code-exec-and-diag.js | 143 - lib/util/transform-parser.js | 2731 --- modules/ebnf-parser | 2 +- modules/jison-lex | 2 +- modules/jison2json | 2 +- modules/lex-parser | 2 +- package-lock.json | 6 +- web/content/assets/js/calculator.js | 2650 --- web/content/assets/js/jison.js | 31778 -------------------------- 15 files changed, 7 insertions(+), 51986 deletions(-) delete mode 100644 lib/util/ebnf-parser.js delete mode 100644 lib/util/ebnf-transform.js delete mode 100644 lib/util/lex-parser.js delete mode 100644 lib/util/parser.js delete mode 100644 lib/util/regexp-lexer.js delete mode 100644 lib/util/regexp-set-management.js delete mode 100644 lib/util/safe-code-exec-and-diag.js delete mode 100644 lib/util/transform-parser.js delete mode 100644 web/content/assets/js/calculator.js delete mode 100644 web/content/assets/js/jison.js diff --git a/lib/util/ebnf-parser.js b/lib/util/ebnf-parser.js deleted file mode 100644 index 2663d4a87..000000000 --- a/lib/util/ebnf-parser.js +++ /dev/null @@ -1,81 +0,0 @@ -var bnf = require("./parser").parser, - ebnf = require("./ebnf-transform"), - jisonlex = require("./lex-parser"); - -exports.parse = function parse(grammar) { - return bnf.parse(grammar); -}; - -exports.transform = ebnf.transform; - -// adds a declaration to the grammar -bnf.yy.addDeclaration = function bnfAddDeclaration(grammar, decl) { - if (decl.start) { - grammar.start = decl.start; - } else if (decl.lex) { - grammar.lex = parseLex(decl.lex.text, decl.lex.position); - } else if (decl.operator) { - if (!grammar.operators) grammar.operators = []; - grammar.operators.push(decl.operator); - } else if (decl.token) { - if (!grammar.extra_tokens) grammar.extra_tokens = []; - grammar.extra_tokens.push(decl.token); - } else if (decl.token_list) { - if (!grammar.extra_tokens) grammar.extra_tokens = []; - decl.token_list.forEach(function (tok) { - grammar.extra_tokens.push(tok); - }); - } else if (decl.parseParams) { - if (!grammar.parseParams) grammar.parseParams = []; - grammar.parseParams = grammar.parseParams.concat(decl.parseParams); - } else if (decl.parserType) { - if (!grammar.options) grammar.options = {}; - grammar.options.type = decl.parserType; - } else if (decl.include) { - if (!grammar.moduleInclude) grammar.moduleInclude = ''; - grammar.moduleInclude += decl.include; - } else if (decl.options) { - if (!grammar.options) grammar.options = {}; - // last occurrence of `%options` wins: - for (var i = 0; i < decl.options.length; i++) { - grammar.options[decl.options[i][0]] = decl.options[i][1]; - } - } else if (decl.unknownDecl) { - if (!grammar.unknownDecls) grammar.unknownDecls = []; - grammar.unknownDecls.push(decl.unknownDecl); - } else if (decl.imports) { - if (!grammar.imports) grammar.imports = []; - grammar.imports.push(decl.imports); - } else if (decl.actionInclude) { - if (!grammar.actionInclude) { - grammar.actionInclude = ''; - } - grammar.actionInclude += decl.actionInclude; - } else if (decl.initCode) { - if (!grammar.moduleInit) { - grammar.moduleInit = []; - } - grammar.moduleInit.push(decl.initCode); // {qualifier: , include: } - } -}; - -// parse an embedded lex section -var parseLex = function bnfParseLex(text, position) { - text = text.replace(/(?:^%lex)|(?:\/lex$)/g, ''); - // We want the lex input to start at the given 'position', if any, - // so that error reports will produce a line number and character index - // which matches the original input file: - position = position || {}; - position.range = position.range || []; - var l = position.first_line | 0; - var c = position.range[0] | 0; - var prelude = ''; - if (l > 1) { - prelude += (new Array(l)).join('\n'); - c -= prelude.length; - } - if (c > 3) { - prelude = '// ' + (new Array(c - 3)).join('.') + prelude; - } - return jisonlex.parse(prelude + text); -}; diff --git a/lib/util/ebnf-transform.js b/lib/util/ebnf-transform.js deleted file mode 100644 index ebe976ac2..000000000 --- a/lib/util/ebnf-transform.js +++ /dev/null @@ -1,374 +0,0 @@ -var EBNF = (function () { - var parser = require('./transform-parser.js'); - var XRegExp = require('xregexp'); - //var assert = require('assert'); - - var devDebug = 0; - - function generatePushAction(handle, offset) { - var terms = handle.terms; - var rv = []; - - for (var i = 0, len = terms.length; i < len; i++) { - rv.push('$' + (i + offset)); - } - rv = rv.join(', '); - // and make sure we contain a term series unambiguously, i.e. anything more complex than - // a single term inside an EBNF check is produced as an array so we can differentiate - // between */+/? EBNF operator results and groups of tokens per individual match. - if (len > 1) { - rv = '[' + rv + ']'; - } - return rv; - } - - function transformExpression(e, opts, emit) { - var type = e[0], - value = e[1], - name = false, - has_transformed = 0; - var list, n; - - if (type === 'xalias') { - type = e[1]; - value = e[2]; - name = e[3]; - if (type) { - e = e.slice(1); - } else { - e = value; - type = e[0]; - value = e[1]; - } - if (devDebug > 3) console.log('xalias: ', e, type, value, name); - } - - if (type === 'symbol') { - // if (e[1][0] === '\\') { - // n = e[1][1]; - // } - // else if (e[1][0] === '\'') { - // n = e[1].substring(1, e[1].length - 1); - // } - // else if (e[1][0] === '"') { - // n = e[1].substring(1, e[1].length - 1); - // } - // else { - n = e[1]; - // } - if (devDebug > 2) console.log('symbol EMIT: ', n + (name ? '[' + name + ']' : '')); - emit(n + (name ? '[' + name + ']' : '')); - } else if (type === '+') { - if (!name) { - name = opts.production + '_repetition_plus' + opts.repid++; - } - if (devDebug > 2) console.log('+ EMIT name: ', name); - emit(name); - - has_transformed = 1; - - opts = optsForProduction(name, opts.grammar); - list = transformExpressionList([value], opts); - opts.grammar[name] = [ - [ - list.fragment, - '$$ = [' + generatePushAction(list, 1) + '];' - ], - [ - name + ' ' + list.fragment, - '$1.push(' + generatePushAction(list, 2) + ');\n$$ = $1;' - ] - ]; - } else if (type === '*') { - if (!name) { - name = opts.production + '_repetition' + opts.repid++; - } - if (devDebug > 2) console.log('* EMIT name: ', name); - emit(name); - - has_transformed = 1; - - opts = optsForProduction(name, opts.grammar); - list = transformExpressionList([value], opts); - opts.grammar[name] = [ - [ - '', - '$$ = [];' - ], - [ - name + ' ' + list.fragment, - '$1.push(' + generatePushAction(list, 2) + ');\n$$ = $1;' - ] - ]; - } else if (type === '?') { - if (!name) { - name = opts.production + '_option' + opts.optid++; - } - if (devDebug > 2) console.log('? EMIT name: ', name); - emit(name); - - has_transformed = 1; - - opts = optsForProduction(name, opts.grammar); - list = transformExpressionList([value], opts); - // you want to be able to check if 0 or 1 occurrences were recognized: since jison - // by default *copies* the lexer token value, i.e. `$$ = $1` is the (optional) default action, - // we will need to set the action up explicitly in case of the 0-count match: - // `$$ = undefined`. - // - // Note that we MUST return an array as the - // '1 occurrence' match CAN carry multiple terms, e.g. in constructs like - // `(T1 T2 T3)?`. - opts.grammar[name] = [ - [ - '', - '$$ = undefined;' - ], - [ - list.fragment, - '$$ = ' + generatePushAction(list, 1) + ';' - ] - ]; - } else if (type === '()') { - if (value.length === 1 && !name) { - list = transformExpressionList(value[0], opts); - if (list.first_transformed_term_index) { - has_transformed = list.first_transformed_term_index; - } - if (devDebug > 2) console.log('group EMIT len=1: ', list); - emit(list); - } else { - if (!name) { - name = opts.production + '_group' + opts.groupid++; - } - if (devDebug > 2) console.log('group EMIT name: ', name); - emit(name); - - has_transformed = 1; - - opts = optsForProduction(name, opts.grammar); - opts.grammar[name] = value.map(function (handle) { - var list = transformExpressionList(handle, opts); - return [ - list.fragment, - '$$ = ' + generatePushAction(list, 1) + ';' - ]; - }); - } - } - - return has_transformed; - } - - function transformExpressionList(list, opts) { - var first_transformed_term_index = false; - var terms = list.reduce(function (tot, e) { - var ci = tot.length; - - var has_transformed = transformExpression(e, opts, function (name) { - if (name.terms) { - tot.push.apply(tot, name.terms); - } else { - tot.push(name); - } - }); - - if (has_transformed) { - first_transformed_term_index = ci + has_transformed; - } - return tot; - }, []); - - return { - fragment: terms.join(' '), - terms: terms, - first_transformed_term_index: first_transformed_term_index // 1-based index - }; - } - - function optsForProduction(id, grammar) { - return { - production: id, - repid: 1, - groupid: 1, - optid: 1, - grammar: grammar - }; - } - - function transformProduction(id, production, grammar) { - var transform_opts = optsForProduction(id, grammar); - return production.map(function (handle) { - var action = null, - opts = null; - var i, len, n; - - if (typeof handle !== 'string') { - action = handle[1]; - opts = handle[2]; - handle = handle[0]; - } - var expressions = parser.parse(handle); - - if (devDebug > 1) console.log('\n================\nEBNF transform expressions:\n ', handle, opts, JSON.stringify(expressions, null, 2)); - - var list = transformExpressionList(expressions, transform_opts); - - var ret = [list.fragment]; - if (action) { - // make sure the action doesn't address any inner items. - if (list.first_transformed_term_index) { - var rhs = list.fragment; - // seek out all names and aliases; strip out literal tokens first as those cannot serve as $names: - var alist = list.terms; // rhs.replace(/'[^']+'/g, '~').replace(/"[^"]+"/g, '~').split(' '); - // we also know at which index the first transformation occurred: - var first_index = list.first_transformed_term_index - 1; - if (devDebug > 2) console.log('alist ~ rhs rule terms: ', alist, rhs); - - var alias_re = new XRegExp('\\[[\\p{Alphabetic}_][\\p{Alphabetic}\\p{Number}_]*\\]'); - var term_re = new XRegExp('^[\\p{Alphabetic}_][\\p{Alphabetic}\\p{Number}_]*$'); - // and collect the PERMITTED aliases: the names of the terms and all the remaining aliases - var good_aliases = {}; - var alias_cnt = {}; - var donotalias = {}; - - // WARNING: this replicates the knowledge/code of jison.js::addName() - var addName = function addNameEBNF(s, i) { - var base = s.replace(/[0-9]+$/, ''); - var dna = donotalias[base]; - - if (good_aliases[s]) { - alias_cnt[s]++; - if (!dna) { - good_aliases[s + alias_cnt[s]] = i + 1; - alias_cnt[s + alias_cnt[s]] = 1; - } - } else { - good_aliases[s] = i + 1; - alias_cnt[s] = 1; - if (!dna) { - good_aliases[s + alias_cnt[s]] = i + 1; - alias_cnt[s + alias_cnt[s]] = 1; - } - } - }; - - // WARNING: this replicates the knowledge/code of jison.js::markBasename() - var markBasename = function markBasenameEBNF(s) { - if (/[0-9]$/.test(s)) { - s = s.replace(/[0-9]+$/, ''); - donotalias[s] = true; - } - }; - - // mark both regular and aliased names, e.g., `id[alias1]` and `id1` - // - // WARNING: this replicates the knowledge/code of jison.js::markBasename()+addName() usage - for (i = 0, len = alist.length; i < len; i++) { - var term = alist[i]; - var alias = term.match(alias_re); - if (alias) { - markBasename(alias[0].substr(1, alias[0].length - 2)); - term = term.replace(alias_re, ''); - } - if (term.match(term_re)) { - markBasename(term); - } - } - // then check & register both regular and aliased names, e.g., `id[alias1]` and `id1` - for (i = 0, len = alist.length; i < len; i++) { - var term = alist[i]; - var alias = term.match(alias_re); - if (alias) { - addName(alias[0].substr(1, alias[0].length - 2), i); - term = term.replace(alias_re, ''); - } - if (term.match(term_re)) { - addName(term, i); - } - } - if (devDebug > 2) console.log('good_aliases: ', { - donotalias: donotalias, - good_aliases: good_aliases, - alias_cnt: alias_cnt, - }); - - // now scan the action for all named and numeric semantic values ($nonterminal / $1) - var nameref_re = new XRegExp('[$@][\\p{Alphabetic}_][\\p{Alphabetic}\\p{Number}_]*\\b', 'g'); - var named_spots = nameref_re.exec(action); - var numbered_spots = action.match(/[$@][0-9]+\b/g); - var max_term_index = list.terms.length; - if (devDebug > 2) console.log('ACTION named_spots: ', named_spots); - if (devDebug > 2) console.log('ACTION numbered_spots: ', numbered_spots); - - // loop through the XRegExp alias regex matches in `action` - while (named_spots) { - n = named_spots[0].substr(1); - if (!good_aliases[n]) { - throw new Error('The action block references the named alias "' + n + '" ' + - 'which is not available in production "' + handle + '"; ' + - 'it probably got removed by the EBNF rule rewrite process.\n' + - 'Be reminded that you cannot reference sub-elements within EBNF */+/? groups, ' + - 'only the outer-most EBNF group alias will remain available at all times ' + - 'due to the EBNF-to-BNF rewrite process.'); - } - - if (alias_cnt[n] !== 1) { - throw new Error('The action block references the ambiguous named alias or term reference "' + n + '" ' + - 'which is mentioned ' + alias_cnt[n] + ' times in production "' + handle + '", implicit and explicit aliases included.\n' + - 'You should either provide unambiguous = uniquely named aliases for these terms or use numeric index references (e.g. `$3`) as a stop-gap in your action code.\n' + - 'Be reminded that you cannot reference sub-elements within EBNF */+/? groups, ' + - 'only the outer-most EBNF group alias will remain available at all times ' + - 'due to the EBNF-to-BNF rewrite process.'); - } - //assert(good_aliases[n] <= max_term_index, 'max term index'); - - named_spots = nameref_re.exec(action); - } - if (numbered_spots) { - for (i = 0, len = numbered_spots.length; i < len; i++) { - n = parseInt(numbered_spots[i].substr(1)); - if (n > max_term_index) { - /* @const */ var n_suffixes = [ 'st', 'nd', 'rd', 'th' ]; - throw new Error('The action block references the ' + n + n_suffixes[Math.max(0, Math.min(3, n - 1))] + ' term, ' + - 'which is not available in production "' + handle + '"; ' + - 'Be reminded that you cannot reference sub-elements within EBNF */+/? groups, ' + - 'only the outer-most EBNF group alias will remain available at all times ' + - 'due to the EBNF-to-BNF rewrite process.'); - } - } - } - } - ret.push(action); - } - if (opts) { - ret.push(opts); - } - if (devDebug > 1) console.log('\n\nEBNF tx result:\n ', JSON.stringify(list, null, 2), JSON.stringify(ret, null, 2)); - - if (ret.length === 1) { - return ret[0]; - } else { - return ret; - } - }); - }; - - function transformGrammar(grammar) { - Object.keys(grammar).forEach(function transformGrammarForKey(id) { - grammar[id] = transformProduction(id, grammar[id], grammar); - }); - }; - - return { - transform: function (ebnf) { - if (devDebug > 0) console.log('EBNF:\n ', JSON.stringify(ebnf, null, 2)); - transformGrammar(ebnf); - if (devDebug > 0) console.log('\n\nEBNF after transformation:\n ', JSON.stringify(ebnf, null, 2)); - return ebnf; - } - }; -})(); - -exports.transform = EBNF.transform; - diff --git a/lib/util/lex-parser.js b/lib/util/lex-parser.js deleted file mode 100644 index bdc67ac08..000000000 --- a/lib/util/lex-parser.js +++ /dev/null @@ -1,5263 +0,0 @@ -/* parser generated by jison 0.4.18-184 */ - -/* - * Returns a Parser object of the following structure: - * - * Parser: { - * yy: {} The so-called "shared state" or rather the *source* of it; - * the real "shared state" `yy` passed around to - * the rule actions, etc. is a derivative/copy of this one, - * not a direct reference! - * } - * - * Parser.prototype: { - * yy: {}, - * EOF: 1, - * TERROR: 2, - * - * trace: function(errorMessage, ...), - * - * JisonParserError: function(msg, hash), - * - * quoteName: function(name), - * Helper function which can be overridden by user code later on: put suitable - * quotes around literal IDs in a description string. - * - * originalQuoteName: function(name), - * The basic quoteName handler provided by JISON. - * `cleanupAfterParse()` will clean up and reset `quoteName()` to reference this function - * at the end of the `parse()`. - * - * describeSymbol: function(symbol), - * Return a more-or-less human-readable description of the given symbol, when - * available, or the symbol itself, serving as its own 'description' for lack - * of something better to serve up. - * - * Return NULL when the symbol is unknown to the parser. - * - * symbols_: {associative list: name ==> number}, - * terminals_: {associative list: number ==> name}, - * nonterminals: {associative list: rule-name ==> {associative list: number ==> rule-alt}}, - * terminal_descriptions_: (if there are any) {associative list: number ==> description}, - * productions_: [...], - * - * performAction: function parser__performAction(yytext, yyleng, yylineno, yyloc, yystate, yysp, yyvstack, yylstack, yystack, yysstack, ...), - * where `...` denotes the (optional) additional arguments the user passed to - * `parser.parse(str, ...)` and specified by way of `%parse-param ...` in the grammar file - * - * The function parameters and `this` have the following value/meaning: - * - `this` : reference to the `yyval` internal object, which has members (`$` and `_$`) - * to store/reference the rule value `$$` and location info `@$`. - * - * One important thing to note about `this` a.k.a. `yyval`: every *reduce* action gets - * to see the same object via the `this` reference, i.e. if you wish to carry custom - * data from one reduce action through to the next within a single parse run, then you - * may get nasty and use `yyval` a.k.a. `this` for storing you own semi-permanent data. - * - * - `yytext` : reference to the lexer value which belongs to the last lexer token used - * to match this rule. This is *not* the look-ahead token, but the last token - * that's actually part of this rule. - * - * Formulated another way, `yytext` is the value of the token immediately preceeding - * the current look-ahead token. - * Caveats apply for rules which don't require look-ahead, such as epsilon rules. - * - * - `yyleng` : ditto as `yytext`, only now for the lexer.yyleng value. - * - * - `yylineno`: ditto as `yytext`, only now for the lexer.yylineno value. - * - * - `yyloc` : ditto as `yytext`, only now for the lexer.yylloc lexer token location info. - * - * - `yystate` : the current parser state number, used internally for dispatching and - * executing the action code chunk matching the rule currently being reduced. - * - * - `yysp` : the current state stack position (a.k.a. 'stack pointer') - * - * This one comes in handy when you are going to do advanced things to the parser - * stacks, all of which are accessible from your action code (see the next entries below). - * - * Also note that you can access this and other stack index values using the new double-hash - * syntax, i.e. `##$ === ##0 === yysp`, while `##1` is the stack index for all things - * related to the first rule term, just like you have `$1`, `@1` and `#1`. - * This is made available to write very advanced grammar action rules, e.g. when you want - * to investigate the parse state stack in your action code, which would, for example, - * be relevant when you wish to implement error diagnostics and reporting schemes similar - * to the work described here: - * - * + Pottier, F., 2016. Reachability and error diagnosis in LR(1) automata. - * In Journées Francophones des Languages Applicatifs. - * - * + Jeffery, C.L., 2003. Generating LR syntax error messages from examples. - * ACM Transactions on Programming Languages and Systems (TOPLAS), 25(5), pp.631–640. - * - * - `yyvstack`: reference to the parser value stack. Also accessed via the `$1` etc. - * constructs. - * - * - `yylstack`: reference to the parser token location stack. Also accessed via - * the `@1` etc. constructs. - * - * - `yystack` : reference to the parser token id stack. Also accessed via the - * `#1` etc. constructs. - * - * Note: this is a bit of a **white lie** as we can statically decode any `#n` reference to - * its numeric token id value, hence that code wouldn't need the `yystack` but *you* might - * want access for your own purposes, such as error analysis as mentioned above! - * - * Note that this stack stores the current stack of *tokens*, that is the sequence of - * already parsed=reduced *nonterminals* (tokens representing rules) and *terminals* - * (lexer tokens *shifted* onto the stack until the rule they belong to is found and - * *reduced*. - * - * - `yysstack`: reference to the parser state stack. This one carries the internal parser - * *states* such as the one in `yystate`, which are used to represent - * the parser state machine in the *parse table*. *Very* *internal* stuff, - * what can I say? If you access this one, you're clearly doing wicked things - * - * - `...` : the extra arguments you specified in the `%parse-param` statement in your - * grammar definition file. - * - * table: [...], - * State transition table - * ---------------------- - * - * index levels are: - * - `state` --> hash table - * - `symbol` --> action (number or array) - * - * If the `action` is an array, these are the elements' meaning: - * - index [0]: 1 = shift, 2 = reduce, 3 = accept - * - index [1]: GOTO `state` - * - * If the `action` is a number, it is the GOTO `state` - * - * defaultActions: {...}, - * - * parseError: function(str, hash, ExceptionClass), - * yyError: function(str, ...), - * yyRecovering: function(), - * yyErrOk: function(), - * yyClearIn: function(), - * - * constructParseErrorInfo: function(error_message, exception_object, expected_token_set, is_recoverable), - * Helper function **which will be set up during the first invocation of the `parse()` method**. - * Produces a new errorInfo 'hash object' which can be passed into `parseError()`. - * See it's use in this parser kernel in many places; example usage: - * - * var infoObj = parser.constructParseErrorInfo('fail!', null, - * parser.collect_expected_token_set(state), true); - * var retVal = parser.parseError(infoObj.errStr, infoObj, parser.JisonParserError); - * - * originalParseError: function(str, hash, ExceptionClass), - * The basic `parseError` handler provided by JISON. - * `cleanupAfterParse()` will clean up and reset `parseError()` to reference this function - * at the end of the `parse()`. - * - * options: { ... parser %options ... }, - * - * parse: function(input[, args...]), - * Parse the given `input` and return the parsed value (or `true` when none was provided by - * the root action, in which case the parser is acting as a *matcher*). - * You MAY use the additional `args...` parameters as per `%parse-param` spec of this grammar: - * these extra `args...` are passed verbatim to the grammar rules' action code. - * - * cleanupAfterParse: function(resultValue, invoke_post_methods, do_not_nuke_errorinfos), - * Helper function **which will be set up during the first invocation of the `parse()` method**. - * This helper API is invoked at the end of the `parse()` call, unless an exception was thrown - * and `%options no-try-catch` has been defined for this grammar: in that case this helper MAY - * be invoked by calling user code to ensure the `post_parse` callbacks are invoked and - * the internal parser gets properly garbage collected under these particular circumstances. - * - * lexer: { - * yy: {...}, A reference to the so-called "shared state" `yy` once - * received via a call to the `.setInput(input, yy)` lexer API. - * EOF: 1, - * ERROR: 2, - * JisonLexerError: function(msg, hash), - * parseError: function(str, hash, ExceptionClass), - * setInput: function(input, [yy]), - * input: function(), - * unput: function(str), - * more: function(), - * reject: function(), - * less: function(n), - * pastInput: function(n), - * upcomingInput: function(n), - * showPosition: function(), - * test_match: function(regex_match_array, rule_index, ...), - * next: function(...), - * lex: function(...), - * begin: function(condition), - * pushState: function(condition), - * popState: function(), - * topState: function(), - * _currentRules: function(), - * stateStackSize: function(), - * cleanupAfterLex: function() - * - * options: { ... lexer %options ... }, - * - * performAction: function(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * rules: [...], - * conditions: {associative list: name ==> set}, - * } - * } - * - * - * token location info (@$, _$, etc.): { - * first_line: n, - * last_line: n, - * first_column: n, - * last_column: n, - * range: [start_number, end_number] - * (where the numbers are indexes into the input string, zero-based) - * } - * - * --- - * - * The `parseError` function receives a 'hash' object with these members for lexer and - * parser errors: - * - * { - * text: (matched text) - * token: (the produced terminal token, if any) - * token_id: (the produced terminal token numeric ID, if any) - * line: (yylineno) - * loc: (yylloc) - * } - * - * parser (grammar) errors will also provide these additional members: - * - * { - * expected: (array describing the set of expected tokens; - * may be UNDEFINED when we cannot easily produce such a set) - * state: (integer (or array when the table includes grammar collisions); - * represents the current internal state of the parser kernel. - * can, for example, be used to pass to the `collect_expected_token_set()` - * API to obtain the expected token set) - * action: (integer; represents the current internal action which will be executed) - * new_state: (integer; represents the next/planned internal state, once the current - * action has executed) - * recoverable: (boolean: TRUE when the parser MAY have an error recovery rule - * available for this particular error) - * state_stack: (array: the current parser LALR/LR internal state stack; this can be used, - * for instance, for advanced error analysis and reporting) - * value_stack: (array: the current parser LALR/LR internal `$$` value stack; this can be used, - * for instance, for advanced error analysis and reporting) - * location_stack: (array: the current parser LALR/LR internal location stack; this can be used, - * for instance, for advanced error analysis and reporting) - * yy: (object: the current parser internal "shared state" `yy` - * as is also available in the rule actions; this can be used, - * for instance, for advanced error analysis and reporting) - * lexer: (reference to the current lexer instance used by the parser) - * parser: (reference to the current parser instance) - * } - * - * while `this` will reference the current parser instance. - * - * When `parseError` is invoked by the lexer, `this` will still reference the related *parser* - * instance, while these additional `hash` fields will also be provided: - * - * { - * lexer: (reference to the current lexer instance which reported the error) - * } - * - * When `parseError` is invoked by the parser due to a **JavaScript exception** being fired - * from either the parser or lexer, `this` will still reference the related *parser* - * instance, while these additional `hash` fields will also be provided: - * - * { - * exception: (reference to the exception thrown) - * } - * - * Please do note that in the latter situation, the `expected` field will be omitted as - * this type of failure is assumed not to be due to *parse errors* but rather due to user - * action code in either parser or lexer failing unexpectedly. - * - * --- - * - * You can specify parser options by setting / modifying the `.yy` object of your Parser instance. - * These options are available: - * - * ### options which are global for all parser instances - * - * Parser.pre_parse: function(yy [, optional parse() args]) - * optional: you can specify a pre_parse() function in the chunk following - * the grammar, i.e. after the last `%%`. - * Parser.post_parse: function(yy, retval [, optional parse() args]) { return retval; } - * optional: you can specify a post_parse() function in the chunk following - * the grammar, i.e. after the last `%%`. When it does not return any value, - * the parser will return the original `retval`. - * - * ### options which can be set up per parser instance - * - * yy: { - * pre_parse: function(yy [, optional parse() args]) - * optional: is invoked before the parse cycle starts (and before the first - * invocation of `lex()`) but immediately after the invocation of - * `parser.pre_parse()`). - * post_parse: function(yy, retval [, optional parse() args]) { return retval; } - * optional: is invoked when the parse terminates due to success ('accept') - * or failure (even when exceptions are thrown). - * `retval` contains the return value to be produced by `Parser.parse()`; - * this function can override the return value by returning another. - * When it does not return any value, the parser will return the original - * `retval`. - * This function is invoked immediately before `Parser.post_parse()`. - * - * parseError: function(str, hash, ExceptionClass) - * optional: overrides the default `parseError` function. - * quoteName: function(name), - * optional: overrides the default `quoteName` function. - * } - * - * parser.lexer.options: { - * pre_lex: function() - * optional: is invoked before the lexer is invoked to produce another token. - * `this` refers to the Lexer object. - * post_lex: function(token) { return token; } - * optional: is invoked when the lexer has produced a token `token`; - * this function can override the returned token value by returning another. - * When it does not return any (truthy) value, the lexer will return - * the original `token`. - * `this` refers to the Lexer object. - * - * ranges: boolean - * optional: `true` ==> token location info will include a .range[] member. - * flex: boolean - * optional: `true` ==> flex-like lexing behaviour where the rules are tested - * exhaustively to find the longest match. - * backtrack_lexer: boolean - * optional: `true` ==> lexer regexes are tested in order and for invoked; - * the lexer terminates the scan when a token is returned by the action code. - * xregexp: boolean - * optional: `true` ==> lexer rule regexes are "extended regex format" requiring the - * `XRegExp` library. When this %option has not been specified at compile time, all lexer - * rule regexes have been written as standard JavaScript RegExp expressions. - * } - */ -var lexParser = (function () { - -// See also: -// http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508 -// but we keep the prototype.constructor and prototype.name assignment lines too for compatibility -// with userland code which might access the derived class in a 'classic' way. -function JisonParserError(msg, hash) { - Object.defineProperty(this, 'name', { - enumerable: false, - writable: false, - value: 'JisonParserError' - }); - - if (msg == null) msg = '???'; - - Object.defineProperty(this, 'message', { - enumerable: false, - writable: true, - value: msg - }); - - this.hash = hash; - - var stacktrace; - if (hash && hash.exception instanceof Error) { - var ex2 = hash.exception; - this.message = ex2.message || msg; - stacktrace = ex2.stack; - } - if (!stacktrace) { - if (Error.hasOwnProperty('captureStackTrace')) { // V8 - Error.captureStackTrace(this, this.constructor); - } else { - stacktrace = (new Error(msg)).stack; - } - } - if (stacktrace) { - Object.defineProperty(this, 'stack', { - enumerable: false, - writable: false, - value: stacktrace - }); - } -} - -if (typeof Object.setPrototypeOf === 'function') { - Object.setPrototypeOf(JisonParserError.prototype, Error.prototype); -} else { - JisonParserError.prototype = Object.create(Error.prototype); -} -JisonParserError.prototype.constructor = JisonParserError; -JisonParserError.prototype.name = 'JisonParserError'; - - - - -// helper: reconstruct the productions[] table -function bp(s) { - var rv = []; - var p = s.pop; - var r = s.rule; - for (var i = 0, l = p.length; i < l; i++) { - rv.push([ - p[i], - r[i] - ]); - } - return rv; -} - - - -// helper: reconstruct the defaultActions[] table -function bda(s) { - var rv = {}; - var d = s.idx; - var g = s.goto; - for (var i = 0, l = d.length; i < l; i++) { - var j = d[i]; - rv[j] = g[i]; - } - return rv; -} - - - -// helper: reconstruct the 'goto' table -function bt(s) { - var rv = []; - var d = s.len; - var y = s.symbol; - var t = s.type; - var a = s.state; - var m = s.mode; - var g = s.goto; - for (var i = 0, l = d.length; i < l; i++) { - var n = d[i]; - var q = {}; - for (var j = 0; j < n; j++) { - var z = y.shift(); - switch (t.shift()) { - case 2: - q[z] = [ - m.shift(), - g.shift() - ]; - break; - - case 0: - q[z] = a.shift(); - break; - - default: - // type === 1: accept - q[z] = [ - 3 - ]; - } - } - rv.push(q); - } - return rv; -} - - - -// helper: runlength encoding with increment step: code, length: step (default step = 0) -// `this` references an array -function s(c, l, a) { - a = a || 0; - for (var i = 0; i < l; i++) { - this.push(c); - c += a; - } -} - -// helper: duplicate sequence from *relative* offset and length. -// `this` references an array -function c(i, l) { - i = this.length - i; - for (l += i; i < l; i++) { - this.push(this[i]); - } -} - -// helper: unpack an array using helpers and data, all passed in an array argument 'a'. -function u(a) { - var rv = []; - for (var i = 0, l = a.length; i < l; i++) { - var e = a[i]; - // Is this entry a helper function? - if (typeof e === 'function') { - i++; - e.apply(rv, a[i]); - } else { - rv.push(e); - } - } - return rv; -} - - -var parser = { - // Code Generator Information Report - // --------------------------------- - // - // Options: - // - // no default action: ............... false - // no try..catch: ................... false - // no default resolve on conflict: false - // on-demand look-ahead: ............ false - // error recovery token skip maximum: 3 - // yyerror in parse actions is: ..... NOT recoverable, - // yyerror in lexer actions and other non-fatal lexer are: - // .................................. NOT recoverable, - // debug grammar/output: ............ false - // has partial LR conflict upgrade: true - // rudimentary token-stack support: false - // parser table compression mode: ... 2 - // export debug tables: ............. false - // export *all* tables: ............. false - // module type: ..................... commonjs - // parser engine type: .............. lalr - // output main() in the module: ..... true - // number of expected conflicts: .... 0 - // - // - // Parser Analysis flags: - // - // all actions are default: ......... false - // uses yyleng: ..................... false - // uses yylineno: ................... false - // uses yytext: ..................... false - // uses yylloc: ..................... false - // uses ParseError API: ............. false - // uses YYERROR: .................... true - // uses YYRECOVERING: ............... false - // uses YYERROK: .................... false - // uses YYCLEARIN: .................. false - // tracks rule values: .............. true - // assigns rule values: ............. true - // uses location tracking: .......... false - // assigns location: ................ false - // uses yystack: .................... false - // uses yysstack: ................... false - // uses yysp: ....................... true - // has error recovery: .............. true - // - // --------- END OF REPORT ----------- - -trace: function no_op_trace() { }, -JisonParserError: JisonParserError, -yy: {}, -options: { - type: "lalr", - hasPartialLrUpgradeOnConflict: true, - errorRecoveryTokenDiscardCount: 3 -}, -symbols_: { - "$": 17, - "$accept": 0, - "$end": 1, - "%%": 19, - "(": 10, - ")": 11, - "*": 7, - "+": 12, - ",": 8, - ".": 15, - "/": 14, - "/!": 28, - "<": 5, - "=": 18, - ">": 6, - "?": 13, - "ACTION": 23, - "ACTION_BODY": 26, - "CHARACTER_LIT": 36, - "CODE": 43, - "EOF": 1, - "ESCAPE_CHAR": 33, - "INCLUDE": 41, - "NAME": 20, - "NAME_BRACE": 29, - "OPTIONS": 37, - "OPTIONS_END": 38, - "OPTION_STRING_VALUE": 39, - "OPTION_VALUE": 40, - "PATH": 42, - "RANGE_REGEX": 34, - "REGEX_SET": 32, - "REGEX_SET_END": 31, - "REGEX_SET_START": 30, - "SPECIAL_GROUP": 27, - "START_COND": 25, - "START_EXC": 22, - "START_INC": 21, - "STRING_LIT": 35, - "UNKNOWN_DECL": 24, - "^": 16, - "action": 55, - "action_body": 57, - "action_comments_body": 58, - "any_group_regex": 67, - "definition": 48, - "definitions": 47, - "error": 2, - "escape_char": 70, - "extra_lexer_module_code": 76, - "include_macro_code": 77, - "init": 46, - "lex": 44, - "module_code_chunk": 78, - "name_expansion": 66, - "name_list": 60, - "names_exclusive": 50, - "names_inclusive": 49, - "nonempty_regex_list": 63, - "option": 75, - "option_list": 74, - "optional_module_code_chunk": 79, - "options": 73, - "range_regex": 71, - "regex": 61, - "regex_base": 65, - "regex_concat": 64, - "regex_list": 62, - "regex_set": 68, - "regex_set_atom": 69, - "rule": 54, - "rule_block": 53, - "rules": 51, - "rules_and_epilogue": 45, - "rules_collective": 52, - "start_conditions": 59, - "string": 72, - "unbracketed_action_body": 56, - "{": 3, - "|": 9, - "}": 4 -}, -terminals_: { - 1: "EOF", - 2: "error", - 3: "{", - 4: "}", - 5: "<", - 6: ">", - 7: "*", - 8: ",", - 9: "|", - 10: "(", - 11: ")", - 12: "+", - 13: "?", - 14: "/", - 15: ".", - 16: "^", - 17: "$", - 18: "=", - 19: "%%", - 20: "NAME", - 21: "START_INC", - 22: "START_EXC", - 23: "ACTION", - 24: "UNKNOWN_DECL", - 25: "START_COND", - 26: "ACTION_BODY", - 27: "SPECIAL_GROUP", - 28: "/!", - 29: "NAME_BRACE", - 30: "REGEX_SET_START", - 31: "REGEX_SET_END", - 32: "REGEX_SET", - 33: "ESCAPE_CHAR", - 34: "RANGE_REGEX", - 35: "STRING_LIT", - 36: "CHARACTER_LIT", - 37: "OPTIONS", - 38: "OPTIONS_END", - 39: "OPTION_STRING_VALUE", - 40: "OPTION_VALUE", - 41: "INCLUDE", - 42: "PATH", - 43: "CODE" -}, -TERROR: 2, -EOF: 1, - -// internals: defined here so the object *structure* doesn't get modified by parse() et al, -// thus helping JIT compilers like Chrome V8. -originalQuoteName: null, -originalParseError: null, -cleanupAfterParse: null, -constructParseErrorInfo: null, - -__reentrant_call_depth: 0, // INTERNAL USE ONLY -__error_infos: [], // INTERNAL USE ONLY: the set of parseErrorInfo objects created since the last cleanup - -// APIs which will be set up depending on user action code analysis: -//yyRecovering: 0, -//yyErrOk: 0, -//yyClearIn: 0, - -// Helper APIs -// ----------- - -// Helper function which can be overridden by user code later on: put suitable quotes around -// literal IDs in a description string. -quoteName: function parser_quoteName(id_str) { - return '"' + id_str + '"'; -}, - -// Return a more-or-less human-readable description of the given symbol, when available, -// or the symbol itself, serving as its own 'description' for lack of something better to serve up. -// -// Return NULL when the symbol is unknown to the parser. -describeSymbol: function parser_describeSymbol(symbol) { - if (symbol !== this.EOF && this.terminal_descriptions_ && this.terminal_descriptions_[symbol]) { - return this.terminal_descriptions_[symbol]; - } - else if (symbol === this.EOF) { - return 'end of input'; - } - else if (this.terminals_[symbol]) { - return this.quoteName(this.terminals_[symbol]); - } - // Otherwise... this might refer to a RULE token i.e. a non-terminal: see if we can dig that one up. - // - // An example of this may be where a rule's action code contains a call like this: - // - // parser.describeSymbol(#$) - // - // to obtain a human-readable description or name of the current grammar rule. This comes handy in - // error handling action code blocks, for example. - var s = this.symbols_; - for (var key in s) { - if (s[key] === symbol) { - return key; - } - } - return null; -}, - -// Produce a (more or less) human-readable list of expected tokens at the point of failure. -// -// The produced list may contain token or token set descriptions instead of the tokens -// themselves to help turning this output into something that easier to read by humans -// unless `do_not_describe` parameter is set, in which case a list of the raw, *numeric*, -// expected terminals and nonterminals is produced. -// -// The returned list (array) will not contain any duplicate entries. -collect_expected_token_set: function parser_collect_expected_token_set(state, do_not_describe) { - var TERROR = this.TERROR; - var tokenset = []; - var check = {}; - // Has this (error?) state been outfitted with a custom expectations description text for human consumption? - // If so, use that one instead of the less palatable token set. - if (!do_not_describe && this.state_descriptions_ && this.state_descriptions_[state]) { - return [ - this.state_descriptions_[state] - ]; - } - for (var p in this.table[state]) { - p = +p; - if (p !== TERROR) { - var d = do_not_describe ? p : this.describeSymbol(p); - if (d && !check[d]) { - tokenset.push(d); - check[d] = true; // Mark this token description as already mentioned to prevent outputting duplicate entries. - } - } - } - return tokenset; -}, -productions_: bp({ - pop: u([ - 44, - 44, - s, - [45, 3], - 46, - 47, - 47, - s, - [48, 9], - 49, - 49, - 50, - 50, - 51, - 51, - s, - [52, 3], - 53, - 53, - 54, - s, - [55, 4], - 56, - 56, - s, - [57, 3], - 58, - 58, - s, - [59, 4], - 60, - 60, - 61, - 62, - 62, - s, - [63, 3], - 64, - 64, - s, - [65, 17], - 66, - 67, - 67, - 68, - 68, - 69, - s, - [69, 4, 1], - 72, - 73, - 74, - 74, - s, - [75, 4], - 76, - 76, - 77, - 77, - 78, - 78, - 79, - 79 -]), - rule: u([ - s, - [4, 3], - 2, - 0, - 0, - 2, - 0, - s, - [2, 3], - 3, - 3, - s, - [1, 5], - 2, - 1, - 2, - c, - [15, 3], - c, - [23, 4], - c, - [18, 6], - 2, - 1, - 5, - 4, - c, - [11, 4], - 3, - 0, - 1, - c, - [15, 3], - 0, - 3, - c, - [32, 3], - 1, - s, - [3, 4], - s, - [2, 5], - c, - [12, 3], - s, - [1, 6], - c, - [16, 3], - c, - [10, 8], - c, - [9, 3], - s, - [3, 3], - c, - [8, 3], - c, - [30, 4], - 0 -]) -}), -performAction: function parser__PerformAction(yystate /* action[1] */, yysp, yyvstack) { -/* this == yyval */ -var yy = this.yy; - -switch (yystate) { -case 1: - /*! Production:: lex : init definitions rules_and_epilogue EOF */ - this.$ = yyvstack[yysp - 1]; - this.$.macros = yyvstack[yysp - 2].macros; - this.$.startConditions = yyvstack[yysp - 2].startConditions; - this.$.unknownDecls = yyvstack[yysp - 2].unknownDecls; - // if there are any options, add them all, otherwise set options to NULL: - // can't check for 'empty object' by `if (yy.options) ...` so we do it this way: - for (var k in yy.options) { - this.$.options = yy.options; - break; - } - if (yy.actionInclude) { - var asrc = yy.actionInclude.join('\n\n'); - // Only a non-empty action code chunk should actually make it through: - if (asrc.trim() !== '') { - this.$.actionInclude = asrc; - } - } - delete yy.options; - delete yy.actionInclude; - return this.$; - break; - -case 2: - /*! Production:: lex : init definitions error EOF */ - yy.parser.yyError("Maybe you did not correctly separate the lexer sections with a '%%' on an otherwise empty line? The lexer spec file should have this structure: definitions %% rules [%% extra_module_code]"); - break; - -case 3: - /*! Production:: rules_and_epilogue : "%%" rules "%%" extra_lexer_module_code */ - if (yyvstack[yysp] && yyvstack[yysp].trim() !== '') { - this.$ = { rules: yyvstack[yysp - 2], moduleInclude: yyvstack[yysp] }; - } else { - this.$ = { rules: yyvstack[yysp - 2] }; - } - break; - -case 4: - /*! Production:: rules_and_epilogue : "%%" rules */ - this.$ = { rules: yyvstack[yysp] }; - break; - -case 5: - /*! Production:: rules_and_epilogue : ε */ - this.$ = { rules: [] }; - break; - -case 6: - /*! Production:: init : ε */ - yy.actionInclude = []; - if (!yy.options) yy.options = {}; - break; - -case 7: - /*! Production:: definitions : definition definitions */ - this.$ = yyvstack[yysp]; - if (yyvstack[yysp - 1] != null) { - if ('length' in yyvstack[yysp - 1]) { - this.$.macros[yyvstack[yysp - 1][0]] = yyvstack[yysp - 1][1]; - } else if (yyvstack[yysp - 1].type === 'names') { - for (var name in yyvstack[yysp - 1].names) { - this.$.startConditions[name] = yyvstack[yysp - 1].names[name]; - } - } else if (yyvstack[yysp - 1].type === 'unknown') { - this.$.unknownDecls.push(yyvstack[yysp - 1].body); - } - } - break; - -case 8: - /*! Production:: definitions : ε */ - this.$ = { - macros: {}, // { hash table } - startConditions: {}, // { hash table } - unknownDecls: [] // [ array of [key,value] pairs } - }; - break; - -case 9: - /*! Production:: definition : NAME regex */ -case 29: - /*! Production:: rule : regex action */ - this.$ = [yyvstack[yysp - 1], yyvstack[yysp]]; - break; - -case 10: - /*! Production:: definition : START_INC names_inclusive */ -case 11: - /*! Production:: definition : START_EXC names_exclusive */ -case 32: - /*! Production:: action : unbracketed_action_body */ -case 33: - /*! Production:: action : include_macro_code */ -case 36: - /*! Production:: action_body : action_comments_body */ -case 79: - /*! Production:: escape_char : ESCAPE_CHAR */ -case 80: - /*! Production:: range_regex : RANGE_REGEX */ -case 90: - /*! Production:: extra_lexer_module_code : optional_module_code_chunk */ -case 94: - /*! Production:: module_code_chunk : CODE */ -case 96: - /*! Production:: optional_module_code_chunk : module_code_chunk */ - this.$ = yyvstack[yysp]; - break; - -case 12: - /*! Production:: definition : "{" action_body "}" */ - yy.actionInclude.push(yyvstack[yysp - 1]); this.$ = null; - break; - -case 13: - /*! Production:: definition : "{" action_body error */ - var l = yyvstack[yysp - 1].split('\n'); - var ab = l.slice(0, 10).join('\n'); - yy.parser.yyError("Seems you did not correctly bracket the lexer 'preparatory' action block in curly braces: '{ ... }'. Offending action body:\n" + ab); - break; - -case 14: - /*! Production:: definition : ACTION */ -case 15: - /*! Production:: definition : include_macro_code */ - yy.actionInclude.push(yyvstack[yysp]); this.$ = null; - break; - -case 16: - /*! Production:: definition : options */ - this.$ = null; - break; - -case 17: - /*! Production:: definition : UNKNOWN_DECL */ - this.$ = {type: 'unknown', body: yyvstack[yysp]}; - break; - -case 18: - /*! Production:: names_inclusive : START_COND */ - this.$ = {type: 'names', names: {}}; this.$.names[yyvstack[yysp]] = 0; - break; - -case 19: - /*! Production:: names_inclusive : names_inclusive START_COND */ - this.$ = yyvstack[yysp - 1]; this.$.names[yyvstack[yysp]] = 0; - break; - -case 20: - /*! Production:: names_exclusive : START_COND */ - this.$ = {type: 'names', names: {}}; this.$.names[yyvstack[yysp]] = 1; - break; - -case 21: - /*! Production:: names_exclusive : names_exclusive START_COND */ - this.$ = yyvstack[yysp - 1]; this.$.names[yyvstack[yysp]] = 1; - break; - -case 22: - /*! Production:: rules : rules rules_collective */ - this.$ = yyvstack[yysp - 1].concat(yyvstack[yysp]); - break; - -case 23: - /*! Production:: rules : ε */ -case 28: - /*! Production:: rule_block : ε */ - this.$ = []; - break; - -case 24: - /*! Production:: rules_collective : start_conditions rule */ - if (yyvstack[yysp - 1]) { - yyvstack[yysp].unshift(yyvstack[yysp - 1]); - } - this.$ = [yyvstack[yysp]]; - break; - -case 25: - /*! Production:: rules_collective : start_conditions "{" rule_block "}" */ - if (yyvstack[yysp - 3]) { - yyvstack[yysp - 1].forEach(function (d) { - d.unshift(yyvstack[yysp - 3]); - }); - } - this.$ = yyvstack[yysp - 1]; - break; - -case 26: - /*! Production:: rules_collective : start_conditions "{" rule_block error */ - if (yyvstack[yysp - 3]) { - yyvstack[yysp - 1].forEach(function (d) { - d.unshift(yyvstack[yysp - 3]); - }); - } - yy.parser.yyError("Seems you did not correctly bracket a lexer rule set inside the start condition <" + yyvstack[yysp - 3].join(',') + "> { rules... } as a terminating curly brace '}' could not be found.", yyvstack[yysp - 1]); - break; - -case 27: - /*! Production:: rule_block : rule_block rule */ - this.$ = $rules; this.$.push(yyvstack[yysp]); - break; - -case 30: - /*! Production:: action : "{" action_body "}" */ -case 41: - /*! Production:: start_conditions : "<" name_list ">" */ - this.$ = yyvstack[yysp - 1]; - break; - -case 31: - /*! Production:: action : "{" action_body error */ - var l = yyvstack[yysp - 1].split('\n'); - var ab = l.slice(0, 10).join('\n'); - yy.parser.yyError("Seems you did not correctly bracket a lexer rule action block in curly braces: '{ ... }'. Offending action body:\n" + ab); - break; - -case 35: - /*! Production:: unbracketed_action_body : unbracketed_action_body ACTION */ - this.$ = yyvstack[yysp - 1] + '\n' + yyvstack[yysp]; - break; - -case 37: - /*! Production:: action_body : action_body "{" action_body "}" action_comments_body */ - this.$ = yyvstack[yysp - 4] + yyvstack[yysp - 3] + yyvstack[yysp - 2] + yyvstack[yysp - 1] + yyvstack[yysp]; - break; - -case 38: - /*! Production:: action_body : action_body "{" action_body error */ - var l = yyvstack[yysp - 1].split('\n'); - var ab = l.slice(0, 10).join('\n'); - yy.parser.yyError("Seems you did not correctly match curly braces '{ ... }' in a lexer rule action block. Offending action body part:\n" + ab); - break; - -case 39: - /*! Production:: action_comments_body : ε */ -case 49: - /*! Production:: regex_list : ε */ -case 97: - /*! Production:: optional_module_code_chunk : ε */ - this.$ = ''; - break; - -case 40: - /*! Production:: action_comments_body : action_comments_body ACTION_BODY */ -case 53: - /*! Production:: regex_concat : regex_concat regex_base */ -case 65: - /*! Production:: regex_base : regex_base range_regex */ -case 75: - /*! Production:: regex_set : regex_set_atom regex_set */ -case 95: - /*! Production:: module_code_chunk : module_code_chunk CODE */ - this.$ = yyvstack[yysp - 1] + yyvstack[yysp]; - break; - -case 42: - /*! Production:: start_conditions : "<" name_list error */ - var l = yyvstack[yysp - 1]; - var ab = l.slice(0, 10).join(',').replace(/[\s\r\n]/g, ' '); - yy.parser.yyError("Seems you did not correctly terminate the start condition set <" + ab + ",???> with a terminating '>'"); - break; - -case 43: - /*! Production:: start_conditions : "<" "*" ">" */ - this.$ = ['*']; - break; - -case 45: - /*! Production:: name_list : NAME */ - this.$ = [yyvstack[yysp]]; - break; - -case 46: - /*! Production:: name_list : name_list "," NAME */ - this.$ = yyvstack[yysp - 2]; this.$.push(yyvstack[yysp]); - break; - -case 47: - /*! Production:: regex : nonempty_regex_list */ - // Detect if the regex ends with a pure (Unicode) word; - // we *do* consider escaped characters which are 'alphanumeric' - // to be equivalent to their non-escaped version, hence these are - // all valid 'words' for the 'easy keyword rules' option: - // - // - hello_kitty - // - γεια_σου_γατούλα - // - \u03B3\u03B5\u03B9\u03B1_\u03C3\u03BF\u03C5_\u03B3\u03B1\u03C4\u03BF\u03CD\u03BB\u03B1 - // - // http://stackoverflow.com/questions/7885096/how-do-i-decode-a-string-with-escaped-unicode#12869914 - // - // As we only check the *tail*, we also accept these as - // 'easy keywords': - // - // - %options - // - %foo-bar - // - +++a:b:c1 - // - // Note the dash in that last example: there the code will consider - // `bar` to be the keyword, which is fine with us as we're only - // interested in the trailing boundary and patching that one for - // the `easy_keyword_rules` option. - this.$ = yyvstack[yysp]; - if (yy.options.easy_keyword_rules) { - // We need to 'protect' `eval` here as keywords are allowed - // to contain double-quotes and other leading cruft. - // `eval` *does* gobble some escapes (such as `\b`) but - // we protect against that through a simple replace regex: - // we're not interested in the special escapes' exact value - // anyway. - // It will also catch escaped escapes (`\\`), which are not - // word characters either, so no need to worry about - // `eval(str)` 'correctly' converting convoluted constructs - // like '\\\\\\\\\\b' in here. - this.$ = this.$ - .replace(/\\\\/g, '.') - .replace(/"/g, '.') - .replace(/\\c[A-Z]/g, '.') - .replace(/\\[^xu0-9]/g, '.'); - - try { - // Convert Unicode escapes and other escapes to their literal characters - // BEFORE we go and check whether this item is subject to the - // `easy_keyword_rules` option. - this.$ = eval('"' + this.$ + '"'); - } - catch (ex) { - console.warn('easy-keyword-rule FAIL on eval: ', ex); - - // make the next keyword test fail: - this.$ = '.'; - } - // a 'keyword' starts with an alphanumeric character, - // followed by zero or more alphanumerics or digits: - var re = new XRegExp('\\w[\\w\\d]*$'); - if (XRegExp.match(this.$, re)) { - this.$ = yyvstack[yysp] + "\\b"; - } else { - this.$ = yyvstack[yysp]; - } - } - break; - -case 50: - /*! Production:: nonempty_regex_list : regex_concat "|" regex_list */ - this.$ = yyvstack[yysp - 2] + '|' + yyvstack[yysp]; - break; - -case 51: - /*! Production:: nonempty_regex_list : "|" regex_list */ - this.$ = '|' + yyvstack[yysp]; - break; - -case 55: - /*! Production:: regex_base : "(" regex_list ")" */ - this.$ = '(' + yyvstack[yysp - 1] + ')'; - break; - -case 56: - /*! Production:: regex_base : SPECIAL_GROUP regex_list ")" */ - this.$ = yyvstack[yysp - 2] + yyvstack[yysp - 1] + ')'; - break; - -case 57: - /*! Production:: regex_base : "(" regex_list error */ - var l = yyvstack[yysp - 1]; - var ab = l.replace(/[\s\r\n]/g, ' ').substring(0, 32); - yy.parser.yyError("Seems you did not correctly bracket a lex rule regex part in '(...)' braces. Unterminated regex part: (" + ab, yyvstack[yysp - 1]); - break; - -case 58: - /*! Production:: regex_base : SPECIAL_GROUP regex_list error */ - var l = yyvstack[yysp - 1]; - var ab = l.replace(/[\s\r\n]/g, ' ').substring(0, 32); - yy.parser.yyError("Seems you did not correctly bracket a lex rule regex part in '(...)' braces. Unterminated regex part: " + yyvstack[yysp - 2] + ab, yyvstack[yysp - 1]); - break; - -case 59: - /*! Production:: regex_base : regex_base "+" */ - this.$ = yyvstack[yysp - 1] + '+'; - break; - -case 60: - /*! Production:: regex_base : regex_base "*" */ - this.$ = yyvstack[yysp - 1] + '*'; - break; - -case 61: - /*! Production:: regex_base : regex_base "?" */ - this.$ = yyvstack[yysp - 1] + '?'; - break; - -case 62: - /*! Production:: regex_base : "/" regex_base */ - this.$ = '(?=' + yyvstack[yysp] + ')'; - break; - -case 63: - /*! Production:: regex_base : "/!" regex_base */ - this.$ = '(?!' + yyvstack[yysp] + ')'; - break; - -case 67: - /*! Production:: regex_base : "." */ - this.$ = '.'; - break; - -case 68: - /*! Production:: regex_base : "^" */ - this.$ = '^'; - break; - -case 69: - /*! Production:: regex_base : "$" */ - this.$ = '$'; - break; - -case 73: - /*! Production:: any_group_regex : REGEX_SET_START regex_set REGEX_SET_END */ -case 91: - /*! Production:: extra_lexer_module_code : optional_module_code_chunk include_macro_code extra_lexer_module_code */ - this.$ = yyvstack[yysp - 2] + yyvstack[yysp - 1] + yyvstack[yysp]; - break; - -case 74: - /*! Production:: any_group_regex : REGEX_SET_START regex_set error */ - var l = yyvstack[yysp - 1]; - var ab = l.replace(/[\s\r\n]/g, ' ').substring(0, 32); - yy.parser.yyError("Seems you did not correctly bracket a lex rule regex set in '[...]' brackets. Unterminated regex set: " + yyvstack[yysp - 2] + ab, yyvstack[yysp - 1]); - break; - -case 78: - /*! Production:: regex_set_atom : name_expansion */ - if (XRegExp._getUnicodeProperty(yyvstack[yysp].replace(/[{}]/g, '')) - && yyvstack[yysp].toUpperCase() !== yyvstack[yysp] - ) { - // treat this as part of an XRegExp `\p{...}` Unicode 'General Category' Property cf. http://unicode.org/reports/tr18/#Categories - this.$ = yyvstack[yysp]; - } else { - this.$ = yyvstack[yysp]; - } - //console.log("name expansion for: ", { name: $name_expansion, redux: $name_expansion.replace(/[{}]/g, ''), output: $$ }); - break; - -case 81: - /*! Production:: string : STRING_LIT */ - this.$ = prepareString(yyvstack[yysp].substr(1, yyvstack[yysp].length - 2)); - break; - -case 86: - /*! Production:: option : NAME */ - yy.options[yyvstack[yysp]] = true; - break; - -case 87: - /*! Production:: option : NAME "=" OPTION_STRING_VALUE */ - yy.options[yyvstack[yysp - 2]] = yyvstack[yysp]; - break; - -case 88: - /*! Production:: option : NAME "=" OPTION_VALUE */ -case 89: - /*! Production:: option : NAME "=" NAME */ - yy.options[yyvstack[yysp - 2]] = parseValue(yyvstack[yysp]); - break; - -case 92: - /*! Production:: include_macro_code : INCLUDE PATH */ - var fs = require('fs'); - var fileContent = fs.readFileSync(yyvstack[yysp], { encoding: 'utf-8' }); - // And no, we don't support nested '%include': - this.$ = '\n// Included by Jison: ' + yyvstack[yysp] + ':\n\n' + fileContent + '\n\n// End Of Include by Jison: ' + yyvstack[yysp] + '\n\n'; - break; - -case 93: - /*! Production:: include_macro_code : INCLUDE error */ - yy.parser.yyError("%include MUST be followed by a valid file path"); - break; - -} -}, -table: bt({ - len: u([ - 13, - 1, - 15, - 4, - 15, - 21, - 2, - 2, - 6, - s, - [11, 4], - 2, - 3, - 1, - 1, - 18, - 3, - 11, - 11, - 30, - 33, - 30, - 23, - 23, - 17, - 17, - s, - [29, 7], - 31, - 5, - s, - [29, 3], - s, - [12, 4], - 3, - 4, - 27, - 27, - 1, - 4, - c, - [36, 3], - 19, - 33, - 30, - 12, - 12, - s, - [29, 5], - 2, - 2, - 30, - 30, - 2, - 7, - 4, - 4, - 12, - 12, - 11, - 11, - 6, - 4, - 11, - 1, - 3, - 6, - 17, - 23, - 3, - c, - [27, 6], - 29, - 2, - 3, - s, - [2, 3], - 1, - s, - [3, 3], - 17, - 16, - 6, - 3, - 1, - 3, - 5, - 3, - 6, - 3, - 24, - 19, - 6, - 20, - 19, - 20, - 14, - 14, - 1, - 14, - 4, - 1, - 17, - 17, - 15, - 3, - 20, - 3, - 19, - 19 -]), - symbol: u([ - 1, - 2, - 3, - s, - [19, 6, 1], - 37, - 41, - 44, - 46, - 1, - c, - [14, 11], - 47, - 48, - 73, - 77, - 1, - 2, - 19, - 45, - c, - [19, 15], - 9, - 10, - s, - [14, 4, 1], - s, - [27, 4, 1], - 33, - 35, - 36, - 61, - s, - [63, 5, 1], - 70, - 72, - 25, - 49, - 25, - 50, - 2, - 3, - 4, - 26, - 57, - 58, - c, - [46, 11], - c, - [11, 33], - 2, - 42, - 20, - 74, - 75, - s, - [1, 3], - 3, - 5, - c, - [85, 6], - 19, - c, - [86, 7], - 51, - c, - [119, 3], - c, - [61, 25], - 9, - 10, - 11, - c, - [44, 5], - c, - [18, 5], - c, - [49, 7], - 37, - 41, - c, - [134, 5], - c, - [30, 25], - s, - [62, 6, 1], - c, - [33, 5], - 7, - s, - [9, 9, 1], - c, - [36, 11], - s, - [34, 4, 1], - 41, - 71, - 2, - c, - [61, 7], - c, - [55, 7], - c, - [53, 8], - c, - [23, 23], - c, - [263, 12], - c, - [17, 22], - c, - [110, 29], - c, - [29, 197], - s, - [31, 7, 1], - 41, - 29, - 32, - 66, - 68, - 69, - c, - [123, 90], - s, - [19, 7, 1], - c, - [12, 38], - c, - [647, 3], - c, - [650, 4], - s, - [1, 5, 1], - c, - [599, 7], - c, - [525, 14], - 43, - c, - [27, 27], - 38, - 20, - 38, - 74, - 75, - 18, - 20, - 38, - c, - [661, 19], - 52, - 59, - c, - [607, 63], - c, - [30, 3], - 11, - c, - [712, 11], - c, - [12, 12], - c, - [493, 142], - 2, - 11, - 2, - 11, - c, - [203, 33], - c, - [840, 28], - 31, - 2, - 29, - 31, - c, - [530, 4], - c, - [7, 4], - c, - [4, 4], - c, - [439, 33], - c, - [1046, 14], - c, - [1096, 5], - c, - [452, 7], - c, - [21, 8], - 38, - 20, - 39, - 40, - 1, - 41, - 43, - 76, - 78, - 79, - c, - [409, 17], - c, - [405, 3], - c, - [890, 11], - 54, - c, - [1171, 8], - 7, - 20, - 60, - c, - [358, 157], - c, - [387, 30], - 31, - c, - [256, 3], - 20, - c, - [654, 3], - c, - [651, 4], - 41, - 77, - c, - [250, 3], - c, - [3, 4], - c, - [250, 16], - 2, - 4, - c, - [251, 13], - 53, - 3, - 23, - 41, - 55, - 56, - 77, - 2, - 6, - 8, - 6, - c, - [4, 3], - c, - [321, 4], - c, - [326, 4], - c, - [310, 7], - 41, - 43, - c, - [46, 15], - c, - [297, 9], - c, - [783, 12], - c, - [89, 8], - c, - [1483, 8], - c, - [25, 9], - 23, - c, - [26, 7], - c, - [45, 19], - c, - [39, 20], - c, - [404, 14], - c, - [14, 14], - 20, - c, - [15, 14], - c, - [489, 5], - c, - [219, 17], - c, - [236, 32], - c, - [217, 4], - c, - [120, 19], - c, - [248, 3], - c, - [162, 31], - c, - [19, 7] -]), - type: u([ - s, - [2, 11], - 0, - 0, - 1, - c, - [14, 13], - 0, - 0, - c, - [7, 4], - c, - [19, 18], - c, - [17, 14], - c, - [21, 5], - 0, - c, - [40, 6], - c, - [31, 15], - s, - [2, 34], - c, - [49, 21], - c, - [69, 48], - c, - [137, 8], - c, - [30, 30], - c, - [33, 28], - c, - [118, 20], - c, - [53, 23], - c, - [23, 20], - c, - [17, 34], - s, - [2, 224], - c, - [239, 202], - c, - [201, 24], - c, - [607, 80], - c, - [291, 188], - c, - [30, 36], - c, - [530, 61], - c, - [389, 26], - c, - [85, 32], - s, - [0, 9], - c, - [370, 203], - c, - [201, 39], - c, - [1454, 5], - c, - [289, 13], - c, - [395, 28], - c, - [297, 11], - c, - [752, 48], - s, - [2, 195] -]), - state: u([ - s, - [1, 4, 1], - 11, - 10, - 15, - 18, - c, - [5, 3], - 19, - 20, - 21, - 23, - 28, - 29, - 34, - 33, - 40, - 42, - 44, - 45, - 48, - 49, - 53, - 55, - c, - [12, 4], - 56, - 57, - c, - [20, 6], - 61, - 63, - c, - [9, 7], - 64, - c, - [8, 7], - 65, - c, - [5, 4], - 66, - c, - [5, 4], - 70, - 67, - 68, - 78, - 49, - 81, - 82, - 84, - c, - [42, 8], - 61, - 61, - 70, - 91, - 68, - 92, - 45, - 96, - 98, - 97, - 100, - 102, - c, - [82, 7], - 103, - 108, - 110, - 111, - 113, - 114, - 120, - 121, - 98, - 97, - 124, - c, - [19, 8], - 125, - 45 -]), - mode: u([ - s, - [2, 13], - 1, - 2, - s, - [1, 7], - c, - [8, 3], - c, - [14, 11], - s, - [1, 15], - s, - [2, 48], - c, - [53, 50], - c, - [129, 5], - c, - [52, 8], - c, - [118, 12], - c, - [25, 26], - c, - [29, 6], - c, - [71, 15], - c, - [52, 12], - c, - [218, 11], - s, - [1, 35], - s, - [2, 234], - c, - [236, 98], - c, - [97, 24], - c, - [24, 15], - c, - [374, 6], - c, - [142, 55], - c, - [470, 4], - c, - [86, 13], - c, - [72, 11], - c, - [565, 52], - c, - [446, 170], - c, - [310, 9], - c, - [202, 25], - c, - [29, 26], - c, - [310, 5], - c, - [241, 75], - c, - [141, 5], - c, - [80, 18], - c, - [861, 204], - c, - [191, 11], - c, - [236, 21], - c, - [1279, 23], - c, - [245, 14], - c, - [402, 6], - c, - [276, 48], - c, - [425, 76], - c, - [93, 68], - c, - [458, 64] -]), - goto: u([ - s, - [6, 11], - s, - [8, 4], - 5, - 6, - 7, - 9, - 12, - 14, - 13, - 5, - 16, - 17, - c, - [14, 11], - 22, - 24, - 26, - 30, - 31, - 32, - 25, - 27, - 35, - 36, - 39, - 37, - 38, - 41, - 43, - s, - [39, 4], - s, - [14, 11], - s, - [15, 11], - s, - [16, 11], - s, - [17, 11], - 47, - 46, - 50, - 51, - 52, - s, - [23, 17], - s, - [7, 3], - s, - [9, 11], - s, - [47, 11], - s, - [52, 3], - 54, - 24, - 52, - c, - [114, 4], - s, - [52, 6], - c, - [120, 7], - 52, - 52, - s, - [49, 3], - 22, - 24, - 49, - c, - [25, 4], - s, - [49, 6], - c, - [25, 7], - 49, - 49, - s, - [54, 3], - 59, - s, - [54, 3], - 58, - 60, - s, - [54, 15], - 62, - s, - [54, 4], - c, - [52, 8], - c, - [46, 8], - c, - [15, 14], - c, - [218, 12], - c, - [12, 12], - s, - [64, 29], - s, - [66, 29], - s, - [67, 29], - s, - [68, 29], - s, - [69, 29], - s, - [70, 29], - s, - [71, 29], - s, - [72, 31], - 35, - 69, - s, - [81, 29], - s, - [82, 29], - s, - [79, 29], - s, - [10, 9], - 71, - 10, - 10, - s, - [18, 12], - s, - [11, 9], - 72, - 11, - 11, - s, - [20, 12], - 74, - 75, - 73, - s, - [36, 3], - 76, - s, - [92, 27], - s, - [93, 27], - 77, - 50, - 85, - 79, - 86, - 86, - 1, - 2, - 4, - 44, - 83, - s, - [44, 6], - 80, - s, - [44, 7], - c, - [565, 25], - s, - [53, 3], - 59, - s, - [53, 3], - 58, - 60, - s, - [53, 15], - 62, - s, - [53, 4], - s, - [51, 12], - s, - [48, 12], - s, - [59, 29], - s, - [60, 29], - s, - [61, 29], - s, - [65, 29], - s, - [80, 29], - 86, - 85, - 88, - 87, - s, - [62, 3], - 59, - s, - [62, 3], - 58, - 60, - s, - [62, 20], - s, - [63, 3], - 59, - s, - [63, 3], - 58, - 60, - s, - [63, 15], - c, - [25, 4], - 63, - 90, - 89, - 76, - 35, - 76, - 69, - s, - [77, 4], - s, - [78, 4], - s, - [19, 12], - s, - [21, 12], - s, - [12, 11], - s, - [13, 11], - s, - [39, 4], - s, - [40, 4], - s, - [83, 11], - 84, - 95, - 93, - 94, - 97, - 97, - 99, - s, - [22, 17], - 101, - c, - [1089, 13], - 104, - 105, - s, - [50, 12], - s, - [55, 29], - s, - [57, 29], - s, - [56, 29], - s, - [58, 29], - s, - [73, 29], - s, - [74, 29], - 75, - 75, - 107, - 75, - 106, - 87, - 87, - 88, - 88, - 89, - 89, - 3, - 90, - 13, - 96, - 96, - 109, - s, - [94, 3], - s, - [24, 17], - s, - [28, 15], - 112, - 115, - 13, - 117, - 116, - 118, - 119, - s, - [45, 3], - s, - [39, 4], - s, - [38, 3], - c, - [291, 3], - s, - [95, 3], - 123, - 122, - c, - [278, 13], - s, - [29, 19], - s, - [39, 4], - s, - [32, 12], - 126, - s, - [32, 7], - s, - [33, 19], - s, - [34, 20], - s, - [41, 14], - s, - [42, 14], - 127, - s, - [43, 14], - s, - [37, 3], - 76, - 91, - s, - [25, 17], - s, - [26, 17], - s, - [27, 15], - 129, - 75, - 128, - s, - [35, 20], - s, - [46, 3], - s, - [30, 19], - s, - [31, 19] -]) -}), -defaultActions: bda({ - idx: u([ - 0, - s, - [8, 5, 1], - s, - [17, 4, 1], - s, - [28, 8, 1], - 37, - 38, - 39, - 41, - 43, - 46, - 47, - 51, - 52, - s, - [56, 7, 1], - s, - [69, 10, 1], - 81, - s, - [84, 8, 1], - s, - [93, 4, 1], - 99, - 100, - 101, - 105, - 106, - 107, - 109, - 111, - 112, - s, - [114, 4, 1], - 119, - s, - [121, 4, 1], - s, - [126, 4, 1] -]), - goto: u([ - 6, - 39, - s, - [14, 4, 1], - 23, - 7, - 9, - 47, - 64, - s, - [66, 7, 1], - 81, - 82, - 79, - 18, - 20, - 92, - 93, - 1, - 2, - 51, - 48, - 59, - 60, - 61, - 65, - 80, - 77, - 78, - 19, - 21, - 12, - 13, - 39, - 40, - 83, - 84, - 22, - 50, - 55, - 57, - 56, - 58, - 73, - 74, - 75, - 87, - 88, - 89, - 3, - 94, - 24, - 28, - 45, - 39, - 38, - 95, - 29, - 39, - 33, - 34, - 41, - 42, - 43, - 91, - 25, - 26, - 27, - 35, - 46, - 30, - 31 -]) -}), -parseError: function parseError(str, hash, ExceptionClass) { - if (hash.recoverable && typeof this.trace === 'function') { - this.trace(str); - hash.destroy(); // destroy... well, *almost*! - } else { - if (!ExceptionClass) { - ExceptionClass = this.JisonParserError; - } - throw new ExceptionClass(str, hash); - } -}, -parse: function parse(input) { - var self = this, - stack = new Array(128), // token stack: stores token which leads to state at the same index (column storage) - sstack = new Array(128), // state stack: stores states (column storage) - - vstack = new Array(128), // semantic value stack - - table = this.table, - sp = 0; // 'stack pointer': index into the stacks - - var recovering = 0; // (only used when the grammar contains error recovery rules) - var TERROR = this.TERROR, - EOF = this.EOF, - ERROR_RECOVERY_TOKEN_DISCARD_COUNT = (this.options.errorRecoveryTokenDiscardCount | 0) || 3; - var NO_ACTION = [0, table.length /* ensures that anyone using this new state will fail dramatically! */]; - - //this.reductionCount = this.shiftCount = 0; - - var lexer; - if (this.__lexer__) { - lexer = this.__lexer__; - } else { - lexer = this.__lexer__ = Object.create(this.lexer); - } - - var sharedState_yy = { - parseError: null, - quoteName: null, - lexer: null, - parser: null, - pre_parse: null, - post_parse: null - }; - // copy state - for (var k in this.yy) { - if (Object.prototype.hasOwnProperty.call(this.yy, k)) { - sharedState_yy[k] = this.yy[k]; - } - } - - sharedState_yy.lexer = lexer; - sharedState_yy.parser = this; - - - - - - - - - - - - - - - - - - - - // *Always* setup `yyError`, `YYRECOVERING`, `yyErrOk` and `yyClearIn` functions as it is paramount - // to have *their* closure match ours -- if we only set them up once, - // any subsequent `parse()` runs will fail in very obscure ways when - // these functions are invoked in the user action code block(s) as - // their closure will still refer to the `parse()` instance which set - // them up. Hence we MUST set them up at the start of every `parse()` run! - if (this.yyError) { - this.yyError = function yyError(str /*, ...args */) { - - - - var error_rule_depth = (this.options.parserErrorsAreRecoverable ? locateNearestErrorRecoveryRule(state) : -1); - var expected = this.collect_expected_token_set(state); - var hash = this.constructParseErrorInfo(str, null, expected, (error_rule_depth >= 0)); - - - // Add any extra args to the hash under the name `extra_error_attributes`: - var args = Array.prototype.slice.call(arguments, 1); - if (args.length) { - hash.extra_error_attributes = args; - } - - var r = this.parseError(str, hash, this.JisonParserError); - return r; - }; - } - - - - - - - lexer.setInput(input, sharedState_yy); - - - - vstack[sp] = null; - sstack[sp] = 0; - stack[sp] = 0; - ++sp; - - - // Does the shared state override the default `parseError` that already comes with this instance? - if (typeof sharedState_yy.parseError === 'function') { - this.parseError = function parseErrorAlt(str, hash, ExceptionClass) { - if (!ExceptionClass) { - ExceptionClass = this.JisonParserError; - } - return sharedState_yy.parseError(str, hash, ExceptionClass); - }; - } else { - this.parseError = this.originalParseError; - } - - // Does the shared state override the default `quoteName` that already comes with this instance? - if (typeof sharedState_yy.quoteName === 'function') { - this.quoteName = sharedState_yy.quoteName; - } else { - this.quoteName = this.originalQuoteName; - } - - // set up the cleanup function; make it an API so that external code can re-use this one in case of - // calamities or when the `%options no-try-catch` option has been specified for the grammar, in which - // case this parse() API method doesn't come with a `finally { ... }` block any more! - // - // NOTE: as this API uses parse() as a closure, it MUST be set again on every parse() invocation, - // or else your `sharedState`, etc. references will be *wrong*! - this.cleanupAfterParse = function parser_cleanupAfterParse(resultValue, invoke_post_methods, do_not_nuke_errorinfos) { - var rv; - - if (invoke_post_methods) { - if (sharedState_yy.post_parse) { - rv = sharedState_yy.post_parse.call(this, sharedState_yy, resultValue); - if (typeof rv !== 'undefined') resultValue = rv; - } - if (this.post_parse) { - rv = this.post_parse.call(this, sharedState_yy, resultValue); - if (typeof rv !== 'undefined') resultValue = rv; - } - } - - if (this.__reentrant_call_depth > 1) return resultValue; // do not (yet) kill the sharedState when this is a reentrant run. - - // clean up the lingering lexer structures as well: - if (lexer.cleanupAfterLex) { - lexer.cleanupAfterLex(do_not_nuke_errorinfos); - } - - // prevent lingering circular references from causing memory leaks: - if (sharedState_yy) { - sharedState_yy.parseError = undefined; - sharedState_yy.quoteName = undefined; - sharedState_yy.lexer = undefined; - sharedState_yy.parser = undefined; - if (lexer.yy === sharedState_yy) { - lexer.yy = undefined; - } - } - sharedState_yy = undefined; - this.parseError = this.originalParseError; - this.quoteName = this.originalQuoteName; - - // nuke the vstack[] array at least as that one will still reference obsoleted user values. - // To be safe, we nuke the other internal stack columns as well... - stack.length = 0; // fastest way to nuke an array without overly bothering the GC - sstack.length = 0; - - vstack.length = 0; - sp = 0; - - // nuke the error hash info instances created during this run. - // Userland code must COPY any data/references - // in the error hash instance(s) it is more permanently interested in. - if (!do_not_nuke_errorinfos) { - for (var i = this.__error_infos.length - 1; i >= 0; i--) { - var el = this.__error_infos[i]; - if (el && typeof el.destroy === 'function') { - el.destroy(); - } - } - this.__error_infos.length = 0; - } - - return resultValue; - }; - - // NOTE: as this API uses parse() as a closure, it MUST be set again on every parse() invocation, - // or else your `lexer`, `sharedState`, etc. references will be *wrong*! - this.constructParseErrorInfo = function parser_constructParseErrorInfo(msg, ex, expected, recoverable) { - var pei = { - errStr: msg, - exception: ex, - text: lexer.match, - value: lexer.yytext, - token: this.describeSymbol(symbol) || symbol, - token_id: symbol, - line: lexer.yylineno, - - expected: expected, - recoverable: recoverable, - state: state, - action: action, - new_state: newState, - symbol_stack: stack, - state_stack: sstack, - value_stack: vstack, - - stack_pointer: sp, - yy: sharedState_yy, - lexer: lexer, - parser: this, - - // and make sure the error info doesn't stay due to potential - // ref cycle via userland code manipulations. - // These would otherwise all be memory leak opportunities! - // - // Note that only array and object references are nuked as those - // constitute the set of elements which can produce a cyclic ref. - // The rest of the members is kept intact as they are harmless. - destroy: function destructParseErrorInfo() { - // remove cyclic references added to error info: - // info.yy = null; - // info.lexer = null; - // info.value = null; - // info.value_stack = null; - // ... - var rec = !!this.recoverable; - for (var key in this) { - if (this.hasOwnProperty(key) && typeof key === 'object') { - this[key] = undefined; - } - } - this.recoverable = rec; - } - }; - // track this instance so we can `destroy()` it once we deem it superfluous and ready for garbage collection! - this.__error_infos.push(pei); - return pei; - }; - - - function lex() { - var token = lexer.lex(); - // if token isn't its numeric value, convert - if (typeof token !== 'number') { - token = self.symbols_[token] || token; - } - return token || EOF; - } - - - var symbol = 0; - var preErrorSymbol = 0; - var lastEofErrorStateDepth = 0; - var state, action, r, t; - var yyval = { - $: true, - _$: undefined, - yy: sharedState_yy - }; - var p, len, this_production; - - var newState; - var retval = false; - - - // Return the rule stack depth where the nearest error rule can be found. - // Return -1 when no error recovery rule was found. - function locateNearestErrorRecoveryRule(state) { - var stack_probe = sp - 1; - var depth = 0; - - // try to recover from error - for (;;) { - // check for error recovery rule in this state - - var t = table[state][TERROR] || NO_ACTION; - if (t[0]) { - // We need to make sure we're not cycling forever: - // once we hit EOF, even when we `yyerrok()` an error, we must - // prevent the core from running forever, - // e.g. when parent rules are still expecting certain input to - // follow after this, for example when you handle an error inside a set - // of braces which are matched by a parent rule in your grammar. - // - // Hence we require that every error handling/recovery attempt - // *after we've hit EOF* has a diminishing state stack: this means - // we will ultimately have unwound the state stack entirely and thus - // terminate the parse in a controlled fashion even when we have - // very complex error/recovery code interplay in the core + user - // action code blocks: - - if (symbol === EOF) { - if (!lastEofErrorStateDepth) { - lastEofErrorStateDepth = sp - 1 - depth; - } else if (lastEofErrorStateDepth <= sp - 1 - depth) { - - --stack_probe; // popStack(1): [symbol, action] - state = sstack[stack_probe]; - ++depth; - continue; - } - } - return depth; - } - if (state === 0 /* $accept rule */ || stack_probe < 1) { - - return -1; // No suitable error recovery rule available. - } - --stack_probe; // popStack(1): [symbol, action] - state = sstack[stack_probe]; - ++depth; - } - } - - - try { - this.__reentrant_call_depth++; - - if (this.pre_parse) { - this.pre_parse.call(this, sharedState_yy); - } - if (sharedState_yy.pre_parse) { - sharedState_yy.pre_parse.call(this, sharedState_yy); - } - - newState = sstack[sp - 1]; - for (;;) { - // retrieve state number from top of stack - state = newState; // sstack[sp - 1]; - - // use default actions if available - if (this.defaultActions[state]) { - action = 2; - newState = this.defaultActions[state]; - } else { - // The single `==` condition below covers both these `===` comparisons in a single - // operation: - // - // if (symbol === null || typeof symbol === 'undefined') ... - if (!symbol) { - symbol = lex(); - } - // read action for current state and first input - t = (table[state] && table[state][symbol]) || NO_ACTION; - newState = t[1]; - action = t[0]; - - - - - // handle parse error - if (!action) { - // first see if there's any chance at hitting an error recovery rule: - var error_rule_depth = locateNearestErrorRecoveryRule(state); - var errStr = null; - var errSymbolDescr = (this.describeSymbol(symbol) || symbol); - var expected = this.collect_expected_token_set(state); - - if (!recovering) { - // Report error - if (typeof lexer.yylineno === 'number') { - errStr = 'Parse error on line ' + (lexer.yylineno + 1) + ': '; - } else { - errStr = 'Parse error: '; - } - if (lexer.showPosition) { - errStr += '\n' + lexer.showPosition(79 - 10, 10) + '\n'; - } - if (expected.length) { - errStr += 'Expecting ' + expected.join(', ') + ', got unexpected ' + errSymbolDescr; - } else { - errStr += 'Unexpected ' + errSymbolDescr; - } - p = this.constructParseErrorInfo(errStr, null, expected, (error_rule_depth >= 0)); - r = this.parseError(p.errStr, p, this.JisonParserError); - - - if (!p.recoverable) { - retval = r; - break; - } else { - // TODO: allow parseError callback to edit symbol and or state at the start of the error recovery process... - } - } - - - - // just recovered from another error - if (recovering === ERROR_RECOVERY_TOKEN_DISCARD_COUNT && error_rule_depth >= 0) { - // only barf a fatal hairball when we're out of look-ahead symbols and none hit a match; - // this DOES discard look-ahead while recovering from an error when said look-ahead doesn't - // suit the error recovery rules... The error HAS been reported already so we're fine with - // throwing away a few items if that is what it takes to match the nearest recovery rule! - if (symbol === EOF || preErrorSymbol === EOF) { - p = this.__error_infos[this.__error_infos.length - 1]; - if (!p) { - p = this.constructParseErrorInfo('Parsing halted while starting to recover from another error.', null, expected, false); - } else { - p.errStr = 'Parsing halted while starting to recover from another error. Previous error which resulted in this fatal result: ' + p.errStr; - p.recoverable = false; - } - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - - // discard current lookahead and grab another - - - - - - symbol = lex(); - - - } - - // try to recover from error - if (error_rule_depth < 0) { - p = this.constructParseErrorInfo((errStr || 'Parsing halted. No suitable error recovery rule available.'), null, expected, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - sp -= error_rule_depth; - - preErrorSymbol = (symbol === TERROR ? 0 : symbol); // save the lookahead token - symbol = TERROR; // insert generic error symbol as new lookahead - // allow N (default: 3) real symbols to be shifted before reporting a new error - recovering = ERROR_RECOVERY_TOKEN_DISCARD_COUNT; - - newState = sstack[sp - 1]; - - - - continue; - } - - - } - - - - - - - - - - switch (action) { - // catch misc. parse failures: - default: - // this shouldn't happen, unless resolve defaults are off - if (action instanceof Array) { - p = this.constructParseErrorInfo(('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol), null, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - // Another case of better safe than sorry: in case state transitions come out of another error recovery process - // or a buggy LUT (LookUp Table): - p = this.constructParseErrorInfo('Parsing halted. No viable error recovery approach available due to internal system failure.', null, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - - // shift: - case 1: - //this.shiftCount++; - stack[sp] = symbol; - vstack[sp] = lexer.yytext; - - sstack[sp] = newState; // push state - ++sp; - symbol = 0; - if (!preErrorSymbol) { // normal execution / no error - // Pick up the lexer details for the current symbol as that one is not 'look-ahead' any more: - - - - - - if (recovering > 0) { - recovering--; - - } - } else { - // error just occurred, resume old lookahead f/ before error, *unless* that drops us straight back into error mode: - symbol = preErrorSymbol; - preErrorSymbol = 0; - - // read action for current state and first input - t = (table[newState] && table[newState][symbol]) || NO_ACTION; - if (!t[0] || symbol === TERROR) { - // forget about that symbol and move forward: this wasn't a 'forgot to insert' error type where - // (simple) stuff might have been missing before the token which caused the error we're - // recovering from now... - // - // Also check if the LookAhead symbol isn't the ERROR token we set as part of the error - // recovery, for then this we would we idling (cycling) on the error forever. - // Yes, this does not take into account the possibility that the *lexer* may have - // produced a *new* TERROR token all by itself, but that would be a very peculiar grammar! - - symbol = 0; - } - } - - continue; - - // reduce: - case 2: - //this.reductionCount++; - this_production = this.productions_[newState - 1]; // `this.productions_[]` is zero-based indexed while states start from 1 upwards... - len = this_production[1]; - - - - - - - // Make sure subsequent `$$ = $1` default action doesn't fail - // for rules where len==0 as then there's no $1 (you're reducing an epsilon rule then!) - // - // Also do this to prevent nasty action block codes to *read* `$0` or `$$` - // and *not* get `undefined` as a result for their efforts! - vstack[sp] = undefined; - - // perform semantic action - yyval.$ = vstack[sp - len]; // default to $$ = $1; result must produce `undefined` when len == 0, as then there's no $1 - - - - - - - - - - - r = this.performAction.call(yyval, newState, sp - 1, vstack); - - if (typeof r !== 'undefined') { - retval = r; - break; - } - - // pop off stack - sp -= len; - - // don't overwrite the `symbol` variable: use a local var to speed things up: - var ntsymbol = this_production[0]; // push nonterminal (reduce) - stack[sp] = ntsymbol; - vstack[sp] = yyval.$; - - // goto new state = table[STATE][NONTERMINAL] - newState = table[sstack[sp - 1]][ntsymbol]; - sstack[sp] = newState; - ++sp; - - continue; - - // accept: - case 3: - retval = true; - // Return the `$accept` rule's `$$` result, if available. - // - // Also note that JISON always adds this top-most `$accept` rule (with implicit, - // default, action): - // - // $accept: $end - // %{ $$ = $1; @$ = @1; %} - // - // which, combined with the parse kernel's `$accept` state behaviour coded below, - // will produce the `$$` value output of the rule as the parse result, - // IFF that result is *not* `undefined`. (See also the parser kernel code.) - // - // In code: - // - // %{ - // @$ = @1; // if location tracking support is included - // if (typeof $1 !== 'undefined') - // return $1; - // else - // return true; // the default parse result if the rule actions don't produce anything - // %} - if (typeof yyval.$ !== 'undefined') { - retval = yyval.$; - } - break; - } - - // break out of loop: we accept or fail with error - break; - } - } catch (ex) { - // report exceptions through the parseError callback too, but keep the exception intact - // if it is a known parser or lexer error which has been thrown by parseError() already: - if (ex instanceof this.JisonParserError) { - throw ex; - } - else if (lexer && typeof lexer.JisonLexerError === 'function' && ex instanceof lexer.JisonLexerError) { - throw ex; - } - else { - p = this.constructParseErrorInfo('Parsing aborted due to exception.', ex, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - } - } finally { - retval = this.cleanupAfterParse(retval, true, true); - this.__reentrant_call_depth--; - } - - return retval; -}, -yyError: 1 -}; -parser.originalParseError = parser.parseError; -parser.originalQuoteName = parser.quoteName; - -var XRegExp = require('xregexp'); // for helping out the `%options xregexp` in the lexer - -function encodeRE (s) { - return s.replace(/([.*+?^${}()|\[\]\/\\])/g, '\\$1').replace(/\\\\u([a-fA-F0-9]{4})/g, '\\u$1'); -} - -function prepareString (s) { - // unescape slashes - s = s.replace(/\\\\/g, "\\"); - s = encodeRE(s); - return s; -} - -// convert string value to number or boolean value, when possible -// (and when this is more or less obviously the intent) -// otherwise produce the string itself as value. -function parseValue(v) { - if (v === 'false') { - return false; - } - if (v === 'true') { - return true; - } - // http://stackoverflow.com/questions/175739/is-there-a-built-in-way-in-javascript-to-check-if-a-string-is-a-valid-number - // Note that the `v` check ensures that we do not convert `undefined`, `null` and `''` (empty string!) - if (v && !isNaN(v)) { - var rv = +v; - if (isFinite(rv)) { - return rv; - } - } - return v; -} -/* lexer generated by jison-lex 0.3.4-166 */ -/* - * Returns a Lexer object of the following structure: - * - * Lexer: { - * yy: {} The so-called "shared state" or rather the *source* of it; - * the real "shared state" `yy` passed around to - * the rule actions, etc. is a derivative/copy of this one, - * not a direct reference! - * } - * - * Lexer.prototype: { - * yy: {}, - * EOF: 1, - * ERROR: 2, - * - * JisonLexerError: function(msg, hash), - * - * performAction: function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * where `...` denotes the (optional) additional arguments the user passed to - * `lexer.lex(...)` and specified by way of `%parse-param ...` in the **parser** grammar file - * - * The function parameters and `this` have the following value/meaning: - * - `this` : reference to the `lexer` instance. - * - * - `yy` : a reference to the `yy` "shared state" object which was passed to the lexer - * by way of the `lexer.setInput(str, yy)` API before. - * - * - `yy_` : lexer instance reference used internally. - * - * - `$avoiding_name_collisions` : index of the matched lexer rule (regex), used internally. - * - * - `YY_START`: the current lexer "start condition" state. - * - * - `...` : the extra arguments you specified in the `%parse-param` statement in your - * **parser** grammar definition file and which are passed to the lexer via - * its `lexer.lex(...)` API. - * - * parseError: function(str, hash, ExceptionClass), - * - * constructLexErrorInfo: function(error_message, is_recoverable), - * Helper function. - * Produces a new errorInfo 'hash object' which can be passed into `parseError()`. - * See it's use in this lexer kernel in many places; example usage: - * - * var infoObj = lexer.constructParseErrorInfo('fail!', true); - * var retVal = lexer.parseError(infoObj.errStr, infoObj, lexer.JisonLexerError); - * - * options: { ... lexer %options ... }, - * - * lex: function([args...]), - * Produce one token of lexed input, which was passed in earlier via the `lexer.setInput()` API. - * You MAY use the additional `args...` parameters as per `%parse-param` spec of the **parser** grammar: - * these extra `args...` are passed verbatim to the lexer rules' action code. - * - * cleanupAfterLex: function(do_not_nuke_errorinfos), - * Helper function. - * This helper API is invoked when the parse process has completed. This helper may - * be invoked by user code to ensure the internal lexer gets properly garbage collected. - * - * setInput: function(input, [yy]), - * input: function(), - * unput: function(str), - * more: function(), - * reject: function(), - * less: function(n), - * pastInput: function(n), - * upcomingInput: function(n), - * showPosition: function(), - * test_match: function(regex_match_array, rule_index), - * next: function(...), - * lex: function(...), - * begin: function(condition), - * pushState: function(condition), - * popState: function(), - * topState: function(), - * _currentRules: function(), - * stateStackSize: function(), - * - * options: { ... lexer %options ... }, - * - * performAction: function(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * rules: [...], - * conditions: {associative list: name ==> set}, - * } - * - * - * token location info (`yylloc`): { - * first_line: n, - * last_line: n, - * first_column: n, - * last_column: n, - * range: [start_number, end_number] - * (where the numbers are indexes into the input string, zero-based) - * } - * - * --- - * - * The `parseError` function receives a 'hash' object with these members for lexer errors: - * - * { - * text: (matched text) - * token: (the produced terminal token, if any) - * token_id: (the produced terminal token numeric ID, if any) - * line: (yylineno) - * loc: (yylloc) - * recoverable: (boolean: TRUE when the parser MAY have an error recovery rule - * available for this particular error) - * yy: (object: the current parser internal "shared state" `yy` - * as is also available in the rule actions; this can be used, - * for instance, for advanced error analysis and reporting) - * lexer: (reference to the current lexer instance used by the parser) - * } - * - * while `this` will reference the current lexer instance. - * - * When `parseError` is invoked by the lexer, the default implementation will - * attempt to invoke `yy.parser.parseError()`; when this callback is not provided - * it will try to invoke `yy.parseError()` instead. When that callback is also not - * provided, a `JisonLexerError` exception will be thrown containing the error - * message and `hash`, as constructed by the `constructLexErrorInfo()` API. - * - * Note that the lexer's `JisonLexerError` error class is passed via the - * `ExceptionClass` argument, which is invoked to construct the exception - * instance to be thrown, so technically `parseError` will throw the object - * produced by the `new ExceptionClass(str, hash)` JavaScript expression. - * - * --- - * - * You can specify lexer options by setting / modifying the `.options` object of your Lexer instance. - * These options are available: - * - * (Options are permanent.) - * - * yy: { - * parseError: function(str, hash, ExceptionClass) - * optional: overrides the default `parseError` function. - * } - * - * lexer.options: { - * pre_lex: function() - * optional: is invoked before the lexer is invoked to produce another token. - * `this` refers to the Lexer object. - * post_lex: function(token) { return token; } - * optional: is invoked when the lexer has produced a token `token`; - * this function can override the returned token value by returning another. - * When it does not return any (truthy) value, the lexer will return - * the original `token`. - * `this` refers to the Lexer object. - * - * WARNING: the next set of options are not meant to be changed. They echo the abilities of - * the lexer as per when it was compiled! - * - * ranges: boolean - * optional: `true` ==> token location info will include a .range[] member. - * flex: boolean - * optional: `true` ==> flex-like lexing behaviour where the rules are tested - * exhaustively to find the longest match. - * backtrack_lexer: boolean - * optional: `true` ==> lexer regexes are tested in order and for invoked; - * the lexer terminates the scan when a token is returned by the action code. - * xregexp: boolean - * optional: `true` ==> lexer rule regexes are "extended regex format" requiring the - * `XRegExp` library. When this %option has not been specified at compile time, all lexer - * rule regexes have been written as standard JavaScript RegExp expressions. - * } - */ - - -var lexer = (function () { -// See also: -// http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508 -// but we keep the prototype.constructor and prototype.name assignment lines too for compatibility -// with userland code which might access the derived class in a 'classic' way. -function JisonLexerError(msg, hash) { - Object.defineProperty(this, 'name', { - enumerable: false, - writable: false, - value: 'JisonLexerError' - }); - - if (msg == null) msg = '???'; - - Object.defineProperty(this, 'message', { - enumerable: false, - writable: true, - value: msg - }); - - this.hash = hash; - - var stacktrace; - if (hash && hash.exception instanceof Error) { - var ex2 = hash.exception; - this.message = ex2.message || msg; - stacktrace = ex2.stack; - } - if (!stacktrace) { - if (Error.hasOwnProperty('captureStackTrace')) { // V8 - Error.captureStackTrace(this, this.constructor); - } else { - stacktrace = (new Error(msg)).stack; - } - } - if (stacktrace) { - Object.defineProperty(this, 'stack', { - enumerable: false, - writable: false, - value: stacktrace - }); - } -} - -if (typeof Object.setPrototypeOf === 'function') { - Object.setPrototypeOf(JisonLexerError.prototype, Error.prototype); -} else { - JisonLexerError.prototype = Object.create(Error.prototype); -} -JisonLexerError.prototype.constructor = JisonLexerError; -JisonLexerError.prototype.name = 'JisonLexerError'; - - - - -var lexer = { - - // Code Generator Information Report - // --------------------------------- - // - // Options: - // - // backtracking: .................... false - // location.ranges: ................. true - // location line+column tracking: ... true - // - // - // Forwarded Parser Analysis flags: - // - // uses yyleng: ..................... false - // uses yylineno: ................... false - // uses yytext: ..................... false - // uses yylloc: ..................... false - // uses lexer values: ............... true / true - // location tracking: ............... false - // location assignment: ............. false - // - // - // Lexer Analysis flags: - // - // uses yyleng: ..................... undefined - // uses yylineno: ................... undefined - // uses yytext: ..................... undefined - // uses yylloc: ..................... undefined - // uses ParseError API: ............. undefined - // uses location tracking & editing: undefined - // uses more() API: ................. undefined - // uses unput() API: ................ undefined - // uses reject() API: ............... undefined - // uses less() API: ................. undefined - // uses display APIs pastInput(), upcomingInput(), showPosition(): - // ............................. undefined - // uses describeYYLLOC() API: ....... undefined - // - // --------- END OF REPORT ----------- - - - EOF: 1, - ERROR: 2, - - // JisonLexerError: JisonLexerError, /// <-- injected by the code generator - - // options: {}, /// <-- injected by the code generator - - // yy: ..., /// <-- injected by setInput() - - __currentRuleSet__: null, /// <-- internal rule set cache for the current lexer state - - __error_infos: [], /// INTERNAL USE ONLY: the set of lexErrorInfo objects created since the last cleanup - - __decompressed: false, /// INTERNAL USE ONLY: mark whether the lexer instance has been 'unfolded' completely and is now ready for use - - done: false, /// INTERNAL USE ONLY - _backtrack: false, /// INTERNAL USE ONLY - _input: '', /// INTERNAL USE ONLY - _more: false, /// INTERNAL USE ONLY - _signaled_error_token: false, /// INTERNAL USE ONLY - - conditionStack: [], /// INTERNAL USE ONLY; managed via `pushState()`, `popState()`, `topState()` and `stateStackSize()` - - match: '', /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks input which has been matched so far for the lexer token under construction. `match` is identical to `yytext` except that this one still contains the matched input string after `lexer.performAction()` has been invoked, where userland code MAY have changed/replaced the `yytext` value entirely! - matched: '', /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks entire input which has been matched so far - matches: false, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks RE match result for last (successful) match attempt - yytext: '', /// ADVANCED USE ONLY: tracks input which has been matched so far for the lexer token under construction; this value is transferred to the parser as the 'token value' when the parser consumes the lexer token produced through a call to the `lex()` API. - offset: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks the 'cursor position' in the input string, i.e. the number of characters matched so far - yyleng: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: length of matched input for the token under construction (`yytext`) - yylineno: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: 'line number' at which the token under construction is located - yylloc: null, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks location info (lines + columns) for the token under construction - - /** - INTERNAL USE: construct a suitable error info hash object instance for `parseError`. - - @public - @this {RegExpLexer} - */ - constructLexErrorInfo: function lexer_constructLexErrorInfo(msg, recoverable) { - /** @constructor */ - var pei = { - errStr: msg, - recoverable: !!recoverable, - text: this.match, // This one MAY be empty; userland code should use the `upcomingInput` API to obtain more text which follows the 'lexer cursor position'... - token: null, - line: this.yylineno, - loc: this.yylloc, - yy: this.yy, - lexer: this, - - /** - and make sure the error info doesn't stay due to potential - ref cycle via userland code manipulations. - These would otherwise all be memory leak opportunities! - - Note that only array and object references are nuked as those - constitute the set of elements which can produce a cyclic ref. - The rest of the members is kept intact as they are harmless. - - @public - @this {LexErrorInfo} - */ - destroy: function destructLexErrorInfo() { - // remove cyclic references added to error info: - // info.yy = null; - // info.lexer = null; - // ... - var rec = !!this.recoverable; - for (var key in this) { - if (this.hasOwnProperty(key) && typeof key === 'object') { - this[key] = undefined; - } - } - this.recoverable = rec; - } - }; - // track this instance so we can `destroy()` it once we deem it superfluous and ready for garbage collection! - this.__error_infos.push(pei); - return pei; - }, - - /** - handler which is invoked when a lexer error occurs. - - @public - @this {RegExpLexer} - */ - parseError: function lexer_parseError(str, hash, ExceptionClass) { - if (!ExceptionClass) { - ExceptionClass = this.JisonLexerError; - } - if (this.yy.parser && typeof this.yy.parser.parseError === 'function') { - return this.yy.parser.parseError(str, hash, ExceptionClass) || this.ERROR; - } else if (typeof this.yy.parseError === 'function') { - return this.yy.parseError(str, hash, ExceptionClass) || this.ERROR; - } else { - throw new ExceptionClass(str, hash); - } - }, - - /** - method which implements `yyerror(str, ...args)` functionality for use inside lexer actions. - - @public - @this {RegExpLexer} - */ - yyerror: function yyError(str /*, ...args */) { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': ' + str, this.options.lexerErrorsAreRecoverable); - - // Add any extra args to the hash under the name `extra_error_attributes`: - var args = Array.prototype.slice.call(arguments, 1); - if (args.length) { - hash.extra_error_attributes = args; - } - - return (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - }, - - /** - final cleanup function for when we have completed lexing the input; - make it an API so that external code can use this one once userland - code has decided it's time to destroy any lingering lexer error - hash object instances and the like: this function helps to clean - up these constructs, which *may* carry cyclic references which would - otherwise prevent the instances from being properly and timely - garbage-collected, i.e. this function helps prevent memory leaks! - - @public - @this {RegExpLexer} - */ - cleanupAfterLex: function lexer_cleanupAfterLex(do_not_nuke_errorinfos) { - var rv; - - // prevent lingering circular references from causing memory leaks: - this.setInput('', {}); - - // nuke the error hash info instances created during this run. - // Userland code must COPY any data/references - // in the error hash instance(s) it is more permanently interested in. - if (!do_not_nuke_errorinfos) { - for (var i = this.__error_infos.length - 1; i >= 0; i--) { - var el = this.__error_infos[i]; - if (el && typeof el.destroy === 'function') { - el.destroy(); - } - } - this.__error_infos.length = 0; - } - - return this; - }, - - /** - clear the lexer token context; intended for internal use only - - @public - @this {RegExpLexer} - */ - clear: function lexer_clear() { - this.yytext = ''; - this.yyleng = 0; - this.match = ''; - this.matches = false; - this._more = false; - this._backtrack = false; - - var col = this.yylloc ? this.yylloc.last_column : 0; - this.yylloc = { - first_line: this.yylineno + 1, - first_column: col, - last_line: this.yylineno + 1, - last_column: col, - - range: (this.options.ranges ? [this.offset, this.offset] : undefined) - }; - }, - - /** - resets the lexer, sets new input - - @public - @this {RegExpLexer} - */ - setInput: function lexer_setInput(input, yy) { - this.yy = yy || this.yy || {}; - - // also check if we've fully initialized the lexer instance, - // including expansion work to be done to go from a loaded - // lexer to a usable lexer: - if (!this.__decompressed) { - // step 1: decompress the regex list: - var rules = this.rules; - for (var i = 0, len = rules.length; i < len; i++) { - var rule_re = rules[i]; - - // compression: is the RE an xref to another RE slot in the rules[] table? - if (typeof rule_re === 'number') { - rules[i] = rules[rule_re]; - } - } - - // step 2: unfold the conditions[] set to make these ready for use: - var conditions = this.conditions; - for (var k in conditions) { - var spec = conditions[k]; - - var rule_ids = spec.rules; - - var len = rule_ids.length; - var rule_regexes = new Array(len + 1); // slot 0 is unused; we use a 1-based index approach here to keep the hottest code in `lexer_next()` fast and simple! - var rule_new_ids = new Array(len + 1); - - for (var i = 0; i < len; i++) { - var idx = rule_ids[i]; - var rule_re = rules[idx]; - rule_regexes[i + 1] = rule_re; - rule_new_ids[i + 1] = idx; - } - - spec.rules = rule_new_ids; - spec.__rule_regexes = rule_regexes; - spec.__rule_count = len; - } - - this.__decompressed = true; - } - - this._input = input || ''; - this.clear(); - this._signaled_error_token = false; - this.done = false; - this.yylineno = 0; - this.matched = ''; - this.conditionStack = ['INITIAL']; - this.__currentRuleSet__ = null; - this.yylloc = { - first_line: 1, - first_column: 0, - last_line: 1, - last_column: 0, - - range: (this.options.ranges ? [0, 0] : undefined) - }; - this.offset = 0; - return this; - }, - - /** - push a new input into the lexer and activate it: - the old input position is stored and will be resumed - once this new input has been consumed. - - Use this API to help implement C-preprocessor-like - `#include` statements. - - Available options: - - - `emit_EOF_at_end` : {int} the `EOF`-like token to emit - when the new input is consumed: use - this to mark the end of the new input - in the parser grammar. zero/falsey - token value means no end marker token - will be emitted before the lexer - resumes reading from the previous input. - - @public - @this {RegExpLexer} - */ - pushInput: function lexer_pushInput(input, label, options) { - options = options || {}; - - this._input = input || ''; - this.clear(); - // this._signaled_error_token = false; - this.done = false; - this.yylineno = 0; - this.matched = ''; - // this.conditionStack = ['INITIAL']; - // this.__currentRuleSet__ = null; - this.yylloc = { - first_line: 1, - first_column: 0, - last_line: 1, - last_column: 0, - - range: (this.options.ranges ? [0, 0] : undefined) - }; - this.offset = 0; - return this; - }, - - /** - consumes and returns one char from the input - - @public - @this {RegExpLexer} - */ - input: function lexer_input() { - if (!this._input) { - //this.done = true; -- don't set `done` as we want the lex()/next() API to be able to produce one custom EOF token match after this anyhow. (lexer can match special <> tokens and perform user action code for a <> match, but only does so *once*) - return null; - } - var ch = this._input[0]; - this.yytext += ch; - this.yyleng++; - this.offset++; - this.match += ch; - this.matched += ch; - // Count the linenumber up when we hit the LF (or a stand-alone CR). - // On CRLF, the linenumber is incremented when you fetch the CR or the CRLF combo - // and we advance immediately past the LF as well, returning both together as if - // it was all a single 'character' only. - var slice_len = 1; - var lines = false; - if (ch === '\n') { - lines = true; - } else if (ch === '\r') { - lines = true; - var ch2 = this._input[1]; - if (ch2 === '\n') { - slice_len++; - ch += ch2; - this.yytext += ch2; - this.yyleng++; - this.offset++; - this.match += ch2; - this.matched += ch2; - if (this.options.ranges) { - this.yylloc.range[1]++; - } - } - } - if (lines) { - this.yylineno++; - this.yylloc.last_line++; - this.yylloc.last_column = 0; - } else { - this.yylloc.last_column++; - } - if (this.options.ranges) { - this.yylloc.range[1]++; - } - - this._input = this._input.slice(slice_len); - return ch; - }, - - /** - unshifts one char (or an entire string) into the input - - @public - @this {RegExpLexer} - */ - unput: function lexer_unput(ch) { - var len = ch.length; - var lines = ch.split(/(?:\r\n?|\n)/g); - - this._input = ch + this._input; - this.yytext = this.yytext.substr(0, this.yytext.length - len); - this.yyleng = this.yytext.length; - this.offset -= len; - this.match = this.match.substr(0, this.match.length - len); - this.matched = this.matched.substr(0, this.matched.length - len); - - if (lines.length > 1) { - this.yylineno -= lines.length - 1; - - this.yylloc.last_line = this.yylineno + 1; - var pre = this.match; - var pre_lines = pre.split(/(?:\r\n?|\n)/g); - if (pre_lines.length === 1) { - pre = this.matched; - pre_lines = pre.split(/(?:\r\n?|\n)/g); - } - this.yylloc.last_column = pre_lines[pre_lines.length - 1].length; - } else { - this.yylloc.last_column -= len; - } - - if (this.options.ranges) { - this.yylloc.range[1] = this.yylloc.range[0] + this.yyleng; - } - this.done = false; - return this; - }, - - /** - cache matched text and append it on next action - - @public - @this {RegExpLexer} - */ - more: function lexer_more() { - this._more = true; - return this; - }, - - /** - signal the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead. - - @public - @this {RegExpLexer} - */ - reject: function lexer_reject() { - if (this.options.backtrack_lexer) { - this._backtrack = true; - } else { - // when the `parseError()` call returns, we MUST ensure that the error is registered. - // We accomplish this by signaling an 'error' token to be produced for the current - // `.lex()` run. - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).' + pos_str, false); - this._signaled_error_token = (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - } - return this; - }, - - /** - retain first n characters of the match - - @public - @this {RegExpLexer} - */ - less: function lexer_less(n) { - return this.unput(this.match.slice(n)); - }, - - /** - return (part of the) already matched input, i.e. for error messages. - - Limit the returned string length to `maxSize` (default: 20). - - Limit the returned string to the `maxLines` number of lines of input (default: 1). - - Negative limit values equal *unlimited*. - - @public - @this {RegExpLexer} - */ - pastInput: function lexer_pastInput(maxSize, maxLines) { - var past = this.matched.substring(0, this.matched.length - this.match.length); - if (maxSize < 0) - maxSize = past.length; - else if (!maxSize) - maxSize = 20; - if (maxLines < 0) - maxLines = past.length; // can't ever have more input lines than this! - else if (!maxLines) - maxLines = 1; - // `substr` anticipation: treat \r\n as a single character and take a little - // more than necessary so that we can still properly check against maxSize - // after we've transformed and limited the newLines in here: - past = past.substr(-maxSize * 2 - 2); - // now that we have a significantly reduced string to process, transform the newlines - // and chop them, then limit them: - var a = past.replace(/\r\n|\r/g, '\n').split('\n'); - a = a.slice(-maxLines); - past = a.join('\n'); - // When, after limiting to maxLines, we still have too much to return, - // do add an ellipsis prefix... - if (past.length > maxSize) { - past = '...' + past.substr(-maxSize); - } - return past; - }, - - /** - return (part of the) upcoming input, i.e. for error messages. - - Limit the returned string length to `maxSize` (default: 20). - - Limit the returned string to the `maxLines` number of lines of input (default: 1). - - Negative limit values equal *unlimited*. - - @public - @this {RegExpLexer} - */ - upcomingInput: function lexer_upcomingInput(maxSize, maxLines) { - var next = this.match; - if (maxSize < 0) - maxSize = next.length + this._input.length; - else if (!maxSize) - maxSize = 20; - if (maxLines < 0) - maxLines = maxSize; // can't ever have more input lines than this! - else if (!maxLines) - maxLines = 1; - // `substring` anticipation: treat \r\n as a single character and take a little - // more than necessary so that we can still properly check against maxSize - // after we've transformed and limited the newLines in here: - if (next.length < maxSize * 2 + 2) { - next += this._input.substring(0, maxSize * 2 + 2); // substring is faster on Chrome/V8 - } - // now that we have a significantly reduced string to process, transform the newlines - // and chop them, then limit them: - var a = next.replace(/\r\n|\r/g, '\n').split('\n'); - a = a.slice(0, maxLines); - next = a.join('\n'); - // When, after limiting to maxLines, we still have too much to return, - // do add an ellipsis postfix... - if (next.length > maxSize) { - next = next.substring(0, maxSize) + '...'; - } - return next; - }, - - /** - return a string which displays the character position where the lexing error occurred, i.e. for error messages - - @public - @this {RegExpLexer} - */ - showPosition: function lexer_showPosition(maxPrefix, maxPostfix) { - var pre = this.pastInput(maxPrefix).replace(/\s/g, ' '); - var c = new Array(pre.length + 1).join('-'); - return pre + this.upcomingInput(maxPostfix).replace(/\s/g, ' ') + '\n' + c + '^'; - }, - - /** - helper function, used to produce a human readable description as a string, given - the input `yylloc` location object. - - Set `display_range_too` to TRUE to include the string character index position(s) - in the description if the `yylloc.range` is available. - - @public - @this {RegExpLexer} - */ - describeYYLLOC: function lexer_describe_yylloc(yylloc, display_range_too) { - var l1 = yylloc.first_line; - var l2 = yylloc.last_line; - var o1 = yylloc.first_column; - var o2 = yylloc.last_column; - var dl = l2 - l1; - var d_o = o2 - o1; - var rv; - if (dl === 0) { - rv = 'line ' + l1 + ', '; - if (d_o === 1) { - rv += 'column ' + o1; - } else { - rv += 'columns ' + o1 + ' .. ' + o2; - } - } else { - rv = 'lines ' + l1 + '(column ' + o1 + ') .. ' + l2 + '(column ' + o2 + ')'; - } - if (yylloc.range && display_range_too) { - var r1 = yylloc.range[0]; - var r2 = yylloc.range[1] - 1; - if (r2 === r1) { - rv += ' {String Offset: ' + r1 + '}'; - } else { - rv += ' {String Offset range: ' + r1 + ' .. ' + r2 + '}'; - } - } - return rv; - // return JSON.stringify(yylloc); - }, - - /** - test the lexed token: return FALSE when not a match, otherwise return token. - - `match` is supposed to be an array coming out of a regex match, i.e. `match[0]` - contains the actually matched text string. - - Also move the input cursor forward and update the match collectors: - - - `yytext` - - `yyleng` - - `match` - - `matches` - - `yylloc` - - `offset` - - @public - @this {RegExpLexer} - */ - test_match: function lexer_test_match(match, indexed_rule) { - var token, - lines, - backup, - match_str, - match_str_len; - - if (this.options.backtrack_lexer) { - // save context - backup = { - yylineno: this.yylineno, - yylloc: { - first_line: this.yylloc.first_line, - last_line: this.yylloc.last_line, - first_column: this.yylloc.first_column, - last_column: this.yylloc.last_column, - - range: (this.options.ranges ? this.yylloc.range.slice(0) : undefined) - }, - yytext: this.yytext, - match: this.match, - matches: this.matches, - matched: this.matched, - yyleng: this.yyleng, - offset: this.offset, - _more: this._more, - _input: this._input, - //_signaled_error_token: this._signaled_error_token, - yy: this.yy, - conditionStack: this.conditionStack.slice(0), - done: this.done - }; - } - - match_str = match[0]; - match_str_len = match_str.length; - // if (match_str.indexOf('\n') !== -1 || match_str.indexOf('\r') !== -1) { - lines = match_str.split(/(?:\r\n?|\n)/g); - if (lines.length > 1) { - this.yylineno += lines.length - 1; - - this.yylloc.last_line = this.yylineno + 1, - this.yylloc.last_column = lines[lines.length - 1].length; - } else { - this.yylloc.last_column += match_str_len; - } - // } - this.yytext += match_str; - this.match += match_str; - this.matches = match; - this.yyleng = this.yytext.length; - if (this.options.ranges) { - this.yylloc.range[1] += match_str_len; - } - // previous lex rules MAY have invoked the `more()` API rather than producing a token: - // those rules will already have moved this `offset` forward matching their match lengths, - // hence we must only add our own match length now: - this.offset += match_str_len; - this._more = false; - this._backtrack = false; - this._input = this._input.slice(match_str_len); - this.matched += match_str; - - // calling this method: - // - // function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START) {...} - token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1] /* = YY_START */); - // otherwise, when the action codes are all simple return token statements: - //token = this.simpleCaseActionClusters[indexed_rule]; - - if (this.done && this._input) { - this.done = false; - } - if (token) { - return token; - } else if (this._backtrack) { - // recover context - for (var k in backup) { - this[k] = backup[k]; - } - this.__currentRuleSet__ = null; - return false; // rule action called reject() implying the next rule should be tested instead. - } else if (this._signaled_error_token) { - // produce one 'error' token as `.parseError()` in `reject()` did not guarantee a failure signal by throwing an exception! - token = this._signaled_error_token; - this._signaled_error_token = false; - return token; - } - return false; - }, - - /** - return next match in input - - @public - @this {RegExpLexer} - */ - next: function lexer_next() { - if (this.done) { - this.clear(); - return this.EOF; - } - if (!this._input) { - this.done = true; - } - - var token, - match, - tempMatch, - index; - if (!this._more) { - this.clear(); - } - var spec = this.__currentRuleSet__; - if (!spec) { - // Update the ruleset cache as we apparently encountered a state change or just started lexing. - // The cache is set up for fast lookup -- we assume a lexer will switch states much less often than it will - // invoke the `lex()` token-producing API and related APIs, hence caching the set for direct access helps - // speed up those activities a tiny bit. - spec = this.__currentRuleSet__ = this._currentRules(); - // Check whether a *sane* condition has been pushed before: this makes the lexer robust against - // user-programmer bugs such as https://github.com/zaach/jison-lex/issues/19 - if (!spec || !spec.rules) { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Internal lexer engine error' + lineno_msg + ': The lex grammar programmer pushed a non-existing condition name "' + this.topState() + '"; this is a fatal error and should be reported to the application programmer team!' + pos_str, false); - // produce one 'error' token until this situation has been resolved, most probably by parse termination! - return (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - } - } - - var rule_ids = spec.rules; - //var dispatch = spec.__dispatch_lut; - var regexes = spec.__rule_regexes; - var len = spec.__rule_count; - - // Note: the arrays are 1-based, while `len` itself is a valid index, - // hence the non-standard less-or-equal check in the next loop condition! - for (var i = 1; i <= len; i++) { - tempMatch = this._input.match(regexes[i]); - if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { - match = tempMatch; - index = i; - if (this.options.backtrack_lexer) { - token = this.test_match(tempMatch, rule_ids[i]); - if (token !== false) { - return token; - } else if (this._backtrack) { - match = undefined; - continue; // rule action called reject() implying a rule MISmatch. - } else { - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - } else if (!this.options.flex) { - break; - } - } - } - if (match) { - token = this.test_match(match, rule_ids[index]); - if (token !== false) { - return token; - } - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - if (!this._input) { - this.done = true; - this.clear(); - return this.EOF; - } else { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': Unrecognized text.' + pos_str, this.options.lexerErrorsAreRecoverable); - token = (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - if (token === this.ERROR) { - // we can try to recover from a lexer error that `parseError()` did not 'recover' for us - // by moving forward at least one character at a time: - if (!this.match.length) { - this.input(); - } - } - return token; - } - }, - - /** - return next match that has a token - - @public - @this {RegExpLexer} - */ - lex: function lexer_lex() { - var r; - // allow the PRE/POST handlers set/modify the return token for maximum flexibility of the generated lexer: - if (typeof this.options.pre_lex === 'function') { - r = this.options.pre_lex.call(this); - } - while (!r) { - r = this.next(); - } - if (typeof this.options.post_lex === 'function') { - // (also account for a userdef function which does not return any value: keep the token as is) - r = this.options.post_lex.call(this, r) || r; - } - return r; - }, - - /** - backwards compatible alias for `pushState()`; - the latter is symmetrical with `popState()` and we advise to use - those APIs in any modern lexer code, rather than `begin()`. - - @public - @this {RegExpLexer} - */ - begin: function lexer_begin(condition) { - return this.pushState(condition); - }, - - /** - activates a new lexer condition state (pushes the new lexer condition state onto the condition stack) - - @public - @this {RegExpLexer} - */ - pushState: function lexer_pushState(condition) { - this.conditionStack.push(condition); - this.__currentRuleSet__ = null; - return this; - }, - - /** - pop the previously active lexer condition state off the condition stack - - @public - @this {RegExpLexer} - */ - popState: function lexer_popState() { - var n = this.conditionStack.length - 1; - if (n > 0) { - this.__currentRuleSet__ = null; - return this.conditionStack.pop(); - } else { - return this.conditionStack[0]; - } - }, - - /** - return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available - - @public - @this {RegExpLexer} - */ - topState: function lexer_topState(n) { - n = this.conditionStack.length - 1 - Math.abs(n || 0); - if (n >= 0) { - return this.conditionStack[n]; - } else { - return 'INITIAL'; - } - }, - - /** - (internal) determine the lexer rule set which is active for the currently active lexer condition state - - @public - @this {RegExpLexer} - */ - _currentRules: function lexer__currentRules() { - if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) { - return this.conditions[this.conditionStack[this.conditionStack.length - 1]]; - } else { - return this.conditions['INITIAL']; - } - }, - - /** - return the number of states currently on the stack - - @public - @this {RegExpLexer} - */ - stateStackSize: function lexer_stateStackSize() { - return this.conditionStack.length; - }, - options: { - xregexp: true, - ranges: true, - trackPosition: true, - easy_keyword_rules: true -}, - JisonLexerError: JisonLexerError, - performAction: function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START) { - -var YYSTATE = YY_START; -switch($avoiding_name_collisions) { -case 7 : -/*! Conditions:: action */ -/*! Rule:: \{ */ - yy.depth++; return 3; -break; -case 8 : -/*! Conditions:: action */ -/*! Rule:: \} */ - - if (yy.depth == 0) { - this.pushState('trail'); - } else { - yy.depth--; - } - return 4; - -break; -case 10 : -/*! Conditions:: conditions */ -/*! Rule:: > */ - this.popState(); return 6; -break; -case 13 : -/*! Conditions:: rules */ -/*! Rule:: {BR}+ */ - /* empty */ -break; -case 14 : -/*! Conditions:: rules */ -/*! Rule:: {WS}+{BR}+ */ - /* empty */ -break; -case 15 : -/*! Conditions:: rules */ -/*! Rule:: {WS}+ */ - this.pushState('indented'); -break; -case 16 : -/*! Conditions:: rules */ -/*! Rule:: %% */ - this.pushState('code'); return 19; -break; -case 17 : -/*! Conditions:: rules */ -/*! Rule:: {ANY_LITERAL_CHAR}+ */ - - // accept any non-regex, non-lex, non-string-delim, - // non-escape-starter, non-space character as-is - return 36; - -break; -case 20 : -/*! Conditions:: options */ -/*! Rule:: "{DOUBLEQUOTED_STRING_CONTENT}" */ - yy_.yytext = this.matches[1]; return 39; // value is always a string type -break; -case 21 : -/*! Conditions:: options */ -/*! Rule:: '{QUOTED_STRING_CONTENT}' */ - yy_.yytext = this.matches[1]; return 39; // value is always a string type -break; -case 22 : -/*! Conditions:: INITIAL start_condition trail rules macro path options */ -/*! Rule:: \/\/[^\r\n]* */ - /* skip single-line comment */ -break; -case 23 : -/*! Conditions:: INITIAL start_condition trail rules macro path options */ -/*! Rule:: \/\*(.|\n|\r)*?\*\/ */ - /* skip multi-line comment */ -break; -case 25 : -/*! Conditions:: options */ -/*! Rule:: {BR}{WS}+(?=\S) */ - /* skip leading whitespace on the next line of input, when followed by more options */ -break; -case 26 : -/*! Conditions:: options */ -/*! Rule:: {BR} */ - this.popState(); return 38; -break; -case 27 : -/*! Conditions:: options */ -/*! Rule:: {WS}+ */ - /* skip whitespace */ -break; -case 29 : -/*! Conditions:: start_condition */ -/*! Rule:: {BR}+ */ - this.popState(); -break; -case 30 : -/*! Conditions:: start_condition */ -/*! Rule:: {WS}+ */ - /* empty */ -break; -case 31 : -/*! Conditions:: trail */ -/*! Rule:: {WS}*{BR}+ */ - this.pushState('rules'); -break; -case 32 : -/*! Conditions:: indented */ -/*! Rule:: \{ */ - yy.depth = 0; this.pushState('action'); return 3; -break; -case 33 : -/*! Conditions:: indented */ -/*! Rule:: %\{(?:.|{BR})*?%\} */ - this.pushState('trail'); yy_.yytext = yy_.yytext.substr(2, yy_.yyleng - 4); return 23; -break; -case 34 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: %\{(?:.|{BR})*?%\} */ - yy_.yytext = yy_.yytext.substr(2, yy_.yyleng - 4); return 23; -break; -case 35 : -/*! Conditions:: indented */ -/*! Rule:: %include\b */ - - // This is an include instruction in place of an action: - // thanks to the `.+` rule immediately below we need to semi-duplicate - // the `%include` token recognition here vs. the almost-identical rule for the same - // further below. - // There's no real harm as we need to do something special in this case anyway: - // push 2 (two!) conditions. - // - // (Anecdotal: to find that we needed to place this almost-copy here to make the test grammar - // parse correctly took several hours as the debug facilities were - and are - too meager to - // quickly diagnose the problem while we hadn't. So the code got littered with debug prints - // and finally it hit me what the *F* went wrong, after which I saw I needed to add *this* rule!) - - // first push the 'trail' condition which will be the follow-up after we're done parsing the path parameter... - this.pushState('trail'); - // then push the immediate need: the 'path' condition. - this.pushState('path'); - return 41; - -break; -case 36 : -/*! Conditions:: indented */ -/*! Rule:: .* */ - this.popState(); return 23; -break; -case 37 : -/*! Conditions:: INITIAL */ -/*! Rule:: {ID} */ - this.pushState('macro'); return 20; -break; -case 38 : -/*! Conditions:: macro */ -/*! Rule:: {BR}+ */ - this.popState('macro'); -break; -case 39 : -/*! Conditions:: macro */ -/*! Rule:: {ANY_LITERAL_CHAR}+ */ - - // accept any non-regex, non-lex, non-string-delim, - // non-escape-starter, non-space character as-is - return 36; - -break; -case 40 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: {BR}+ */ - /* empty */ -break; -case 41 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: \s+ */ - /* empty */ -break; -case 42 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: "{DOUBLEQUOTED_STRING_CONTENT}" */ - yy_.yytext = yy_.yytext.replace(/\\"/g,'"'); return 35; -break; -case 43 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: '{QUOTED_STRING_CONTENT}' */ - yy_.yytext = yy_.yytext.replace(/\\'/g,"'"); return 35; -break; -case 44 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: \[ */ - this.pushState('set'); return 30; -break; -case 57 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: < */ - this.pushState('conditions'); return 5; -break; -case 58 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: \/! */ - return 28; // treated as `(?!atom)` -break; -case 59 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: \/ */ - return 14; // treated as `(?=atom)` -break; -case 61 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: \\. */ - yy_.yytext = yy_.yytext.replace(/^\\/g, ''); return 33; -break; -case 64 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: %options\b */ - this.pushState('options'); return 37; -break; -case 65 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: %s\b */ - this.pushState('start_condition'); return 21; -break; -case 66 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: %x\b */ - this.pushState('start_condition'); return 22; -break; -case 67 : -/*! Conditions:: INITIAL trail code */ -/*! Rule:: %include\b */ - this.pushState('path'); return 41; -break; -case 68 : -/*! Conditions:: INITIAL rules trail code */ -/*! Rule:: %{NAME}([^\r\n]*) */ - - /* ignore unrecognized decl */ - var l0 = Math.max(0, yy_.yylloc.last_column - yy_.yylloc.first_column); - var l2 = 19; - var l1 = Math.min(79 - 4 - l0 - l2, yy_.yylloc.first_column, 0); - console.warn('LEX: ignoring unsupported lexer option ' + dquote(yy_.yytext) + ' while lexing in ' + this.topState() + ' state:\n' + indent(this.showPosition(l1, l2), 4), this._input, ' /////// ', this.matched); - // this.pushState('options'); - yy_.yytext = [ - this.matches[1], // {NAME} - this.matches[2].trim() // optional value/parameters - ]; - return 24; - -break; -case 69 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: %% */ - this.pushState('rules'); return 19; -break; -case 77 : -/*! Conditions:: set */ -/*! Rule:: \] */ - this.popState('set'); return 31; -break; -case 79 : -/*! Conditions:: code */ -/*! Rule:: [^\r\n]+ */ - return 43; // the bit of CODE just before EOF... -break; -case 80 : -/*! Conditions:: path */ -/*! Rule:: {BR} */ - this.popState(); this.unput(yy_.yytext); -break; -case 81 : -/*! Conditions:: path */ -/*! Rule:: "{DOUBLEQUOTED_STRING_CONTENT}" */ - yy_.yytext = this.matches[1]; this.popState(); return 42; -break; -case 82 : -/*! Conditions:: path */ -/*! Rule:: '{QUOTED_STRING_CONTENT}' */ - yy_.yytext = this.matches[1]; this.popState(); return 42; -break; -case 83 : -/*! Conditions:: path */ -/*! Rule:: {WS}+ */ - // skip whitespace in the line -break; -case 84 : -/*! Conditions:: path */ -/*! Rule:: [^\s\r\n]+ */ - this.popState(); return 42; -break; -case 85 : -/*! Conditions:: macro rules */ -/*! Rule:: . */ - - /* b0rk on bad characters */ - var l0 = Math.max(0, yy_.yylloc.last_column - yy_.yylloc.first_column); - var l2 = 39; - var l1 = Math.min(79 - 4 - l0 - l2, yy_.yylloc.first_column, 0); - var rules = (this.topState() === 'macro' ? 'macro\'s' : this.topState()); - var pos_str = this.showPosition(l1, l2); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n\n Offending input:\n' + indent(pos_str, 4); - } - yy_.yyerror('unsupported lexer input: ' + dquote(yy_.yytext) + ' while lexing ' + rules + '\n (i.e. jison lex regexes).\n\n NOTE: When you want the input ' + dquote(yy_.yytext) + ' to be interpreted as a literal part\n of a lex rule regex, you MUST enclose it in double or single quotes,\n e.g. as shown in this error message just before. If not, then know\n that this is not accepted as a regex operator here in\n jison-lex ' + rules + '.' + pos_str); - -break; -case 86 : -/*! Conditions:: * */ -/*! Rule:: . */ - - /* b0rk on bad characters */ - var l0 = Math.max(0, yy_.yylloc.last_column - yy_.yylloc.first_column); - var l2 = 39; - var l1 = Math.min(79 - 4 - l0 - l2, yy_.yylloc.first_column, 0); - var pos_str = this.showPosition(l1, l2); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n\n Offending input:\n' + indent(pos_str, 4); - } - yy_.yyerror('unsupported lexer input: ' + dquote(yy_.yytext) + ' while lexing in ' + dquote(this.topState()) + ' state.' + pos_str); - -break; -default: - return this.simpleCaseActionClusters[$avoiding_name_collisions]; -} -}, - simpleCaseActionClusters: { - - /*! Conditions:: action */ - /*! Rule:: \/\*(.|\n|\r)*?\*\/ */ - 0 : 26, - /*! Conditions:: action */ - /*! Rule:: \/\/.* */ - 1 : 26, - /*! Conditions:: action */ - /*! Rule:: \/[^ /]*?['"{}][^ ]*?\/ */ - 2 : 26, - /*! Conditions:: action */ - /*! Rule:: "(\\\\|\\"|[^"])*" */ - 3 : 26, - /*! Conditions:: action */ - /*! Rule:: '(\\\\|\\'|[^'])*' */ - 4 : 26, - /*! Conditions:: action */ - /*! Rule:: [/"'][^{}/"']+ */ - 5 : 26, - /*! Conditions:: action */ - /*! Rule:: [^{}/"']+ */ - 6 : 26, - /*! Conditions:: conditions */ - /*! Rule:: {NAME} */ - 9 : 20, - /*! Conditions:: conditions */ - /*! Rule:: , */ - 11 : 8, - /*! Conditions:: conditions */ - /*! Rule:: \* */ - 12 : 7, - /*! Conditions:: options */ - /*! Rule:: {NAME} */ - 18 : 20, - /*! Conditions:: options */ - /*! Rule:: = */ - 19 : 18, - /*! Conditions:: options */ - /*! Rule:: [^\s\r\n]+ */ - 24 : 40, - /*! Conditions:: start_condition */ - /*! Rule:: {ID} */ - 28 : 25, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \| */ - 45 : 9, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \(\?: */ - 46 : 27, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \(\?= */ - 47 : 27, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \(\?! */ - 48 : 27, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \( */ - 49 : 10, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \) */ - 50 : 11, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \+ */ - 51 : 12, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \* */ - 52 : 7, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \? */ - 53 : 13, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \^ */ - 54 : 16, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: , */ - 55 : 8, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: <> */ - 56 : 17, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \\([0-7]{1,3}|[rfntvsSbBwWdD\\*+()${}|[\]\/.^?]|c[A-Z]|x[0-9A-F]{2}|u[a-fA-F0-9]{4}) */ - 60 : 33, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \$ */ - 62 : 17, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \. */ - 63 : 15, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \{\d+(,\s?\d+|,)?\} */ - 70 : 34, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \{{ID}\} */ - 71 : 29, - /*! Conditions:: set options */ - /*! Rule:: \{{ID}\} */ - 72 : 29, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \{ */ - 73 : 3, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \} */ - 74 : 4, - /*! Conditions:: set */ - /*! Rule:: (?:\\\\|\\\]|[^\]{])+ */ - 75 : 32, - /*! Conditions:: set */ - /*! Rule:: \{ */ - 76 : 32, - /*! Conditions:: code */ - /*! Rule:: [^\r\n]*(\r|\n)+ */ - 78 : 43, - /*! Conditions:: * */ - /*! Rule:: $ */ - 87 : 1 -}, - rules: [ - /^(?:\/\*(.|\n|\r)*?\*\/)/, -/^(?:\/\/.*)/, -/^(?:\/[^ \/]*?['"{}][^ ]*?\/)/, -/^(?:"(\\\\|\\"|[^"])*")/, -/^(?:'(\\\\|\\'|[^'])*')/, -/^(?:[\/"'][^{}\/"']+)/, -/^(?:[^{}\/"']+)/, -/^(?:\{)/, -/^(?:\})/, -new XRegExp("^(?:([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}\\-_]*(?:[\\p{Alphabetic}\\p{Number}_]))?))", ""), -/^(?:>)/, -/^(?:,)/, -/^(?:\*)/, -/^(?:(\r\n|\n|\r)+)/, -/^(?:([^\S\n\r])+(\r\n|\n|\r)+)/, -/^(?:([^\S\n\r])+)/, -/^(?:%%)/, -/^(?:([^\s!"$%'-,.\/:-?\[-\^{-}])+)/, -new XRegExp("^(?:([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}\\-_]*(?:[\\p{Alphabetic}\\p{Number}_]))?))", ""), -/^(?:=)/, -/^(?:"((?:\\"|\\[^"]|[^"\\])*)")/, -/^(?:'((?:\\'|\\[^']|[^'\\])*)')/, -/^(?:\/\/[^\r\n]*)/, -/^(?:\/\*(.|\n|\r)*?\*\/)/, -/^(?:\S+)/, -/^(?:(\r\n|\n|\r)([^\S\n\r])+(?=\S))/, -/^(?:(\r\n|\n|\r))/, -/^(?:([^\S\n\r])+)/, -new XRegExp("^(?:([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}_])*))", ""), -/^(?:(\r\n|\n|\r)+)/, -/^(?:([^\S\n\r])+)/, -/^(?:([^\S\n\r])*(\r\n|\n|\r)+)/, -/^(?:\{)/, -/^(?:%\{(?:.|(\r\n|\n|\r))*?%\})/, -/^(?:%\{(?:.|(\r\n|\n|\r))*?%\})/, -/^(?:%include\b)/, -/^(?:.*)/, -new XRegExp("^(?:([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}_])*))", ""), -/^(?:(\r\n|\n|\r)+)/, -/^(?:([^\s!"$%'-,.\/:-?\[-\^{-}])+)/, -/^(?:(\r\n|\n|\r)+)/, -/^(?:\s+)/, -/^(?:"((?:\\"|\\[^"]|[^"\\])*)")/, -/^(?:'((?:\\'|\\[^']|[^'\\])*)')/, -/^(?:\[)/, -/^(?:\|)/, -/^(?:\(\?:)/, -/^(?:\(\?=)/, -/^(?:\(\?!)/, -/^(?:\()/, -/^(?:\))/, -/^(?:\+)/, -/^(?:\*)/, -/^(?:\?)/, -/^(?:\^)/, -/^(?:,)/, -/^(?:<>)/, -/^(?:<)/, -/^(?:\/!)/, -/^(?:\/)/, -/^(?:\\([0-7]{1,3}|[$(-+.\/?BDSW\[-\^bdfnr-tvw{-}]|c[A-Z]|x[\dA-F]{2}|u[\dA-Fa-f]{4}))/, -/^(?:\\.)/, -/^(?:\$)/, -/^(?:\.)/, -/^(?:%options\b)/, -/^(?:%s\b)/, -/^(?:%x\b)/, -/^(?:%include\b)/, -new XRegExp("^(?:%([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}\\-_]*(?:[\\p{Alphabetic}\\p{Number}_]))?)([^\\n\\r]*))", ""), -/^(?:%%)/, -/^(?:\{\d+(,\s?\d+|,)?\})/, -new XRegExp("^(?:\\{([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}_])*)\\})", ""), -new XRegExp("^(?:\\{([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}_])*)\\})", ""), -/^(?:\{)/, -/^(?:\})/, -/^(?:(?:\\\\|\\\]|[^\]{])+)/, -/^(?:\{)/, -/^(?:\])/, -/^(?:[^\r\n]*(\r|\n)+)/, -/^(?:[^\r\n]+)/, -/^(?:(\r\n|\n|\r))/, -/^(?:"((?:\\"|\\[^"]|[^"\\])*)")/, -/^(?:'((?:\\'|\\[^']|[^'\\])*)')/, -/^(?:([^\S\n\r])+)/, -/^(?:\S+)/, -/^(?:.)/, -/^(?:.)/, -/^(?:$)/ - ], - conditions: { - "code": { - rules: [ - 67, - 68, - 78, - 79, - 86, - 87 - ], - inclusive: false - }, - "start_condition": { - rules: [ - 22, - 23, - 28, - 29, - 30, - 86, - 87 - ], - inclusive: false - }, - "options": { - rules: [ - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 72, - 86, - 87 - ], - inclusive: false - }, - "conditions": { - rules: [ - 9, - 10, - 11, - 12, - 86, - 87 - ], - inclusive: false - }, - "action": { - rules: [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 86, - 87 - ], - inclusive: false - }, - "path": { - rules: [ - 22, - 23, - 80, - 81, - 82, - 83, - 84, - 86, - 87 - ], - inclusive: false - }, - "set": { - rules: [ - 72, - 75, - 76, - 77, - 86, - 87 - ], - inclusive: false - }, - "indented": { - rules: [ - 32, - 33, - 34, - 35, - 36, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 69, - 70, - 71, - 73, - 74, - 86, - 87 - ], - inclusive: true - }, - "trail": { - rules: [ - 22, - 23, - 31, - 34, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 73, - 74, - 86, - 87 - ], - inclusive: true - }, - "rules": { - rules: [ - 13, - 14, - 15, - 16, - 17, - 22, - 23, - 34, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 68, - 69, - 70, - 71, - 73, - 74, - 85, - 86, - 87 - ], - inclusive: true - }, - "macro": { - rules: [ - 22, - 23, - 34, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 69, - 70, - 71, - 73, - 74, - 85, - 86, - 87 - ], - inclusive: true - }, - "INITIAL": { - rules: [ - 22, - 23, - 34, - 37, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 73, - 74, - 86, - 87 - ], - inclusive: true - } -} -}; - - -function indent(s, i) { - var a = s.split('\n'); - var pf = (new Array(i + 1)).join(' '); - return pf + a.join('\n' + pf); -} - -// properly quote and escape the given input string -function dquote(s) { - var sq = (s.indexOf('\'') >= 0); - var dq = (s.indexOf('"') >= 0); - if (sq && dq) { - s = s.replace(/"/g, '\\"'); - dq = false; - } - if (dq) { - s = '\'' + s + '\''; - } - else { - s = '"' + s + '"'; - } - return s; -}; - -return lexer; -})(); -parser.lexer = lexer; - -function Parser() { - this.yy = {}; -} -Parser.prototype = parser; -parser.Parser = Parser; - -return new Parser(); -})(); - - - - -if (typeof require !== 'undefined' && typeof exports !== 'undefined') { - exports.parser = lexParser; - exports.Parser = lexParser.Parser; - exports.parse = function () { - return lexParser.parse.apply(lexParser, arguments); - }; - -} diff --git a/lib/util/parser.js b/lib/util/parser.js deleted file mode 100644 index 3063b3d63..000000000 --- a/lib/util/parser.js +++ /dev/null @@ -1,5180 +0,0 @@ -/* parser generated by jison 0.4.18-184 */ - -/* - * Returns a Parser object of the following structure: - * - * Parser: { - * yy: {} The so-called "shared state" or rather the *source* of it; - * the real "shared state" `yy` passed around to - * the rule actions, etc. is a derivative/copy of this one, - * not a direct reference! - * } - * - * Parser.prototype: { - * yy: {}, - * EOF: 1, - * TERROR: 2, - * - * trace: function(errorMessage, ...), - * - * JisonParserError: function(msg, hash), - * - * quoteName: function(name), - * Helper function which can be overridden by user code later on: put suitable - * quotes around literal IDs in a description string. - * - * originalQuoteName: function(name), - * The basic quoteName handler provided by JISON. - * `cleanupAfterParse()` will clean up and reset `quoteName()` to reference this function - * at the end of the `parse()`. - * - * describeSymbol: function(symbol), - * Return a more-or-less human-readable description of the given symbol, when - * available, or the symbol itself, serving as its own 'description' for lack - * of something better to serve up. - * - * Return NULL when the symbol is unknown to the parser. - * - * symbols_: {associative list: name ==> number}, - * terminals_: {associative list: number ==> name}, - * nonterminals: {associative list: rule-name ==> {associative list: number ==> rule-alt}}, - * terminal_descriptions_: (if there are any) {associative list: number ==> description}, - * productions_: [...], - * - * performAction: function parser__performAction(yytext, yyleng, yylineno, yyloc, yystate, yysp, yyvstack, yylstack, yystack, yysstack, ...), - * where `...` denotes the (optional) additional arguments the user passed to - * `parser.parse(str, ...)` and specified by way of `%parse-param ...` in the grammar file - * - * The function parameters and `this` have the following value/meaning: - * - `this` : reference to the `yyval` internal object, which has members (`$` and `_$`) - * to store/reference the rule value `$$` and location info `@$`. - * - * One important thing to note about `this` a.k.a. `yyval`: every *reduce* action gets - * to see the same object via the `this` reference, i.e. if you wish to carry custom - * data from one reduce action through to the next within a single parse run, then you - * may get nasty and use `yyval` a.k.a. `this` for storing you own semi-permanent data. - * - * - `yytext` : reference to the lexer value which belongs to the last lexer token used - * to match this rule. This is *not* the look-ahead token, but the last token - * that's actually part of this rule. - * - * Formulated another way, `yytext` is the value of the token immediately preceeding - * the current look-ahead token. - * Caveats apply for rules which don't require look-ahead, such as epsilon rules. - * - * - `yyleng` : ditto as `yytext`, only now for the lexer.yyleng value. - * - * - `yylineno`: ditto as `yytext`, only now for the lexer.yylineno value. - * - * - `yyloc` : ditto as `yytext`, only now for the lexer.yylloc lexer token location info. - * - * - `yystate` : the current parser state number, used internally for dispatching and - * executing the action code chunk matching the rule currently being reduced. - * - * - `yysp` : the current state stack position (a.k.a. 'stack pointer') - * - * This one comes in handy when you are going to do advanced things to the parser - * stacks, all of which are accessible from your action code (see the next entries below). - * - * Also note that you can access this and other stack index values using the new double-hash - * syntax, i.e. `##$ === ##0 === yysp`, while `##1` is the stack index for all things - * related to the first rule term, just like you have `$1`, `@1` and `#1`. - * This is made available to write very advanced grammar action rules, e.g. when you want - * to investigate the parse state stack in your action code, which would, for example, - * be relevant when you wish to implement error diagnostics and reporting schemes similar - * to the work described here: - * - * + Pottier, F., 2016. Reachability and error diagnosis in LR(1) automata. - * In Journées Francophones des Languages Applicatifs. - * - * + Jeffery, C.L., 2003. Generating LR syntax error messages from examples. - * ACM Transactions on Programming Languages and Systems (TOPLAS), 25(5), pp.631–640. - * - * - `yyvstack`: reference to the parser value stack. Also accessed via the `$1` etc. - * constructs. - * - * - `yylstack`: reference to the parser token location stack. Also accessed via - * the `@1` etc. constructs. - * - * - `yystack` : reference to the parser token id stack. Also accessed via the - * `#1` etc. constructs. - * - * Note: this is a bit of a **white lie** as we can statically decode any `#n` reference to - * its numeric token id value, hence that code wouldn't need the `yystack` but *you* might - * want access for your own purposes, such as error analysis as mentioned above! - * - * Note that this stack stores the current stack of *tokens*, that is the sequence of - * already parsed=reduced *nonterminals* (tokens representing rules) and *terminals* - * (lexer tokens *shifted* onto the stack until the rule they belong to is found and - * *reduced*. - * - * - `yysstack`: reference to the parser state stack. This one carries the internal parser - * *states* such as the one in `yystate`, which are used to represent - * the parser state machine in the *parse table*. *Very* *internal* stuff, - * what can I say? If you access this one, you're clearly doing wicked things - * - * - `...` : the extra arguments you specified in the `%parse-param` statement in your - * grammar definition file. - * - * table: [...], - * State transition table - * ---------------------- - * - * index levels are: - * - `state` --> hash table - * - `symbol` --> action (number or array) - * - * If the `action` is an array, these are the elements' meaning: - * - index [0]: 1 = shift, 2 = reduce, 3 = accept - * - index [1]: GOTO `state` - * - * If the `action` is a number, it is the GOTO `state` - * - * defaultActions: {...}, - * - * parseError: function(str, hash, ExceptionClass), - * yyError: function(str, ...), - * yyRecovering: function(), - * yyErrOk: function(), - * yyClearIn: function(), - * - * constructParseErrorInfo: function(error_message, exception_object, expected_token_set, is_recoverable), - * Helper function **which will be set up during the first invocation of the `parse()` method**. - * Produces a new errorInfo 'hash object' which can be passed into `parseError()`. - * See it's use in this parser kernel in many places; example usage: - * - * var infoObj = parser.constructParseErrorInfo('fail!', null, - * parser.collect_expected_token_set(state), true); - * var retVal = parser.parseError(infoObj.errStr, infoObj, parser.JisonParserError); - * - * originalParseError: function(str, hash, ExceptionClass), - * The basic `parseError` handler provided by JISON. - * `cleanupAfterParse()` will clean up and reset `parseError()` to reference this function - * at the end of the `parse()`. - * - * options: { ... parser %options ... }, - * - * parse: function(input[, args...]), - * Parse the given `input` and return the parsed value (or `true` when none was provided by - * the root action, in which case the parser is acting as a *matcher*). - * You MAY use the additional `args...` parameters as per `%parse-param` spec of this grammar: - * these extra `args...` are passed verbatim to the grammar rules' action code. - * - * cleanupAfterParse: function(resultValue, invoke_post_methods, do_not_nuke_errorinfos), - * Helper function **which will be set up during the first invocation of the `parse()` method**. - * This helper API is invoked at the end of the `parse()` call, unless an exception was thrown - * and `%options no-try-catch` has been defined for this grammar: in that case this helper MAY - * be invoked by calling user code to ensure the `post_parse` callbacks are invoked and - * the internal parser gets properly garbage collected under these particular circumstances. - * - * lexer: { - * yy: {...}, A reference to the so-called "shared state" `yy` once - * received via a call to the `.setInput(input, yy)` lexer API. - * EOF: 1, - * ERROR: 2, - * JisonLexerError: function(msg, hash), - * parseError: function(str, hash, ExceptionClass), - * setInput: function(input, [yy]), - * input: function(), - * unput: function(str), - * more: function(), - * reject: function(), - * less: function(n), - * pastInput: function(n), - * upcomingInput: function(n), - * showPosition: function(), - * test_match: function(regex_match_array, rule_index, ...), - * next: function(...), - * lex: function(...), - * begin: function(condition), - * pushState: function(condition), - * popState: function(), - * topState: function(), - * _currentRules: function(), - * stateStackSize: function(), - * cleanupAfterLex: function() - * - * options: { ... lexer %options ... }, - * - * performAction: function(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * rules: [...], - * conditions: {associative list: name ==> set}, - * } - * } - * - * - * token location info (@$, _$, etc.): { - * first_line: n, - * last_line: n, - * first_column: n, - * last_column: n, - * range: [start_number, end_number] - * (where the numbers are indexes into the input string, zero-based) - * } - * - * --- - * - * The `parseError` function receives a 'hash' object with these members for lexer and - * parser errors: - * - * { - * text: (matched text) - * token: (the produced terminal token, if any) - * token_id: (the produced terminal token numeric ID, if any) - * line: (yylineno) - * loc: (yylloc) - * } - * - * parser (grammar) errors will also provide these additional members: - * - * { - * expected: (array describing the set of expected tokens; - * may be UNDEFINED when we cannot easily produce such a set) - * state: (integer (or array when the table includes grammar collisions); - * represents the current internal state of the parser kernel. - * can, for example, be used to pass to the `collect_expected_token_set()` - * API to obtain the expected token set) - * action: (integer; represents the current internal action which will be executed) - * new_state: (integer; represents the next/planned internal state, once the current - * action has executed) - * recoverable: (boolean: TRUE when the parser MAY have an error recovery rule - * available for this particular error) - * state_stack: (array: the current parser LALR/LR internal state stack; this can be used, - * for instance, for advanced error analysis and reporting) - * value_stack: (array: the current parser LALR/LR internal `$$` value stack; this can be used, - * for instance, for advanced error analysis and reporting) - * location_stack: (array: the current parser LALR/LR internal location stack; this can be used, - * for instance, for advanced error analysis and reporting) - * yy: (object: the current parser internal "shared state" `yy` - * as is also available in the rule actions; this can be used, - * for instance, for advanced error analysis and reporting) - * lexer: (reference to the current lexer instance used by the parser) - * parser: (reference to the current parser instance) - * } - * - * while `this` will reference the current parser instance. - * - * When `parseError` is invoked by the lexer, `this` will still reference the related *parser* - * instance, while these additional `hash` fields will also be provided: - * - * { - * lexer: (reference to the current lexer instance which reported the error) - * } - * - * When `parseError` is invoked by the parser due to a **JavaScript exception** being fired - * from either the parser or lexer, `this` will still reference the related *parser* - * instance, while these additional `hash` fields will also be provided: - * - * { - * exception: (reference to the exception thrown) - * } - * - * Please do note that in the latter situation, the `expected` field will be omitted as - * this type of failure is assumed not to be due to *parse errors* but rather due to user - * action code in either parser or lexer failing unexpectedly. - * - * --- - * - * You can specify parser options by setting / modifying the `.yy` object of your Parser instance. - * These options are available: - * - * ### options which are global for all parser instances - * - * Parser.pre_parse: function(yy [, optional parse() args]) - * optional: you can specify a pre_parse() function in the chunk following - * the grammar, i.e. after the last `%%`. - * Parser.post_parse: function(yy, retval [, optional parse() args]) { return retval; } - * optional: you can specify a post_parse() function in the chunk following - * the grammar, i.e. after the last `%%`. When it does not return any value, - * the parser will return the original `retval`. - * - * ### options which can be set up per parser instance - * - * yy: { - * pre_parse: function(yy [, optional parse() args]) - * optional: is invoked before the parse cycle starts (and before the first - * invocation of `lex()`) but immediately after the invocation of - * `parser.pre_parse()`). - * post_parse: function(yy, retval [, optional parse() args]) { return retval; } - * optional: is invoked when the parse terminates due to success ('accept') - * or failure (even when exceptions are thrown). - * `retval` contains the return value to be produced by `Parser.parse()`; - * this function can override the return value by returning another. - * When it does not return any value, the parser will return the original - * `retval`. - * This function is invoked immediately before `Parser.post_parse()`. - * - * parseError: function(str, hash, ExceptionClass) - * optional: overrides the default `parseError` function. - * quoteName: function(name), - * optional: overrides the default `quoteName` function. - * } - * - * parser.lexer.options: { - * pre_lex: function() - * optional: is invoked before the lexer is invoked to produce another token. - * `this` refers to the Lexer object. - * post_lex: function(token) { return token; } - * optional: is invoked when the lexer has produced a token `token`; - * this function can override the returned token value by returning another. - * When it does not return any (truthy) value, the lexer will return - * the original `token`. - * `this` refers to the Lexer object. - * - * ranges: boolean - * optional: `true` ==> token location info will include a .range[] member. - * flex: boolean - * optional: `true` ==> flex-like lexing behaviour where the rules are tested - * exhaustively to find the longest match. - * backtrack_lexer: boolean - * optional: `true` ==> lexer regexes are tested in order and for invoked; - * the lexer terminates the scan when a token is returned by the action code. - * xregexp: boolean - * optional: `true` ==> lexer rule regexes are "extended regex format" requiring the - * `XRegExp` library. When this %option has not been specified at compile time, all lexer - * rule regexes have been written as standard JavaScript RegExp expressions. - * } - */ -var parser = (function () { - -// See also: -// http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508 -// but we keep the prototype.constructor and prototype.name assignment lines too for compatibility -// with userland code which might access the derived class in a 'classic' way. -function JisonParserError(msg, hash) { - Object.defineProperty(this, 'name', { - enumerable: false, - writable: false, - value: 'JisonParserError' - }); - - if (msg == null) msg = '???'; - - Object.defineProperty(this, 'message', { - enumerable: false, - writable: true, - value: msg - }); - - this.hash = hash; - - var stacktrace; - if (hash && hash.exception instanceof Error) { - var ex2 = hash.exception; - this.message = ex2.message || msg; - stacktrace = ex2.stack; - } - if (!stacktrace) { - if (Error.hasOwnProperty('captureStackTrace')) { // V8 - Error.captureStackTrace(this, this.constructor); - } else { - stacktrace = (new Error(msg)).stack; - } - } - if (stacktrace) { - Object.defineProperty(this, 'stack', { - enumerable: false, - writable: false, - value: stacktrace - }); - } -} - -if (typeof Object.setPrototypeOf === 'function') { - Object.setPrototypeOf(JisonParserError.prototype, Error.prototype); -} else { - JisonParserError.prototype = Object.create(Error.prototype); -} -JisonParserError.prototype.constructor = JisonParserError; -JisonParserError.prototype.name = 'JisonParserError'; - - - - -// helper: reconstruct the productions[] table -function bp(s) { - var rv = []; - var p = s.pop; - var r = s.rule; - for (var i = 0, l = p.length; i < l; i++) { - rv.push([ - p[i], - r[i] - ]); - } - return rv; -} - - - -// helper: reconstruct the defaultActions[] table -function bda(s) { - var rv = {}; - var d = s.idx; - var g = s.goto; - for (var i = 0, l = d.length; i < l; i++) { - var j = d[i]; - rv[j] = g[i]; - } - return rv; -} - - - -// helper: reconstruct the 'goto' table -function bt(s) { - var rv = []; - var d = s.len; - var y = s.symbol; - var t = s.type; - var a = s.state; - var m = s.mode; - var g = s.goto; - for (var i = 0, l = d.length; i < l; i++) { - var n = d[i]; - var q = {}; - for (var j = 0; j < n; j++) { - var z = y.shift(); - switch (t.shift()) { - case 2: - q[z] = [ - m.shift(), - g.shift() - ]; - break; - - case 0: - q[z] = a.shift(); - break; - - default: - // type === 1: accept - q[z] = [ - 3 - ]; - } - } - rv.push(q); - } - return rv; -} - - - -// helper: runlength encoding with increment step: code, length: step (default step = 0) -// `this` references an array -function s(c, l, a) { - a = a || 0; - for (var i = 0; i < l; i++) { - this.push(c); - c += a; - } -} - -// helper: duplicate sequence from *relative* offset and length. -// `this` references an array -function c(i, l) { - i = this.length - i; - for (l += i; i < l; i++) { - this.push(this[i]); - } -} - -// helper: unpack an array using helpers and data, all passed in an array argument 'a'. -function u(a) { - var rv = []; - for (var i = 0, l = a.length; i < l; i++) { - var e = a[i]; - // Is this entry a helper function? - if (typeof e === 'function') { - i++; - e.apply(rv, a[i]); - } else { - rv.push(e); - } - } - return rv; -} - - -var parser = { - // Code Generator Information Report - // --------------------------------- - // - // Options: - // - // no default action: ............... false - // no try..catch: ................... false - // no default resolve on conflict: false - // on-demand look-ahead: ............ false - // error recovery token skip maximum: 3 - // yyerror in parse actions is: ..... NOT recoverable, - // yyerror in lexer actions and other non-fatal lexer are: - // .................................. NOT recoverable, - // debug grammar/output: ............ false - // has partial LR conflict upgrade: true - // rudimentary token-stack support: false - // parser table compression mode: ... 2 - // export debug tables: ............. false - // export *all* tables: ............. false - // module type: ..................... commonjs - // parser engine type: .............. lalr - // output main() in the module: ..... true - // number of expected conflicts: .... 0 - // - // - // Parser Analysis flags: - // - // all actions are default: ......... false - // uses yyleng: ..................... false - // uses yylineno: ................... false - // uses yytext: ..................... false - // uses yylloc: ..................... false - // uses ParseError API: ............. false - // uses YYERROR: .................... true - // uses YYRECOVERING: ............... false - // uses YYERROK: .................... false - // uses YYCLEARIN: .................. false - // tracks rule values: .............. true - // assigns rule values: ............. true - // uses location tracking: .......... true - // assigns location: ................ false - // uses yystack: .................... false - // uses yysstack: ................... false - // uses yysp: ....................... true - // has error recovery: .............. true - // - // --------- END OF REPORT ----------- - -trace: function no_op_trace() { }, -JisonParserError: JisonParserError, -yy: {}, -options: { - type: "lalr", - hasPartialLrUpgradeOnConflict: true, - errorRecoveryTokenDiscardCount: 3 -}, -symbols_: { - "$accept": 0, - "$end": 1, - "%%": 14, - "(": 7, - ")": 8, - "*": 9, - "+": 11, - ":": 4, - ";": 5, - "=": 3, - "?": 10, - "ACTION": 15, - "ACTION_BODY": 41, - "ALIAS": 38, - "ARROW_ACTION": 40, - "CODE": 44, - "DEBUG": 19, - "EOF": 1, - "EPSILON": 37, - "ID": 23, - "IMPORT": 21, - "INCLUDE": 42, - "INIT_CODE": 22, - "INTEGER": 36, - "LEFT": 32, - "LEX_BLOCK": 17, - "NAME": 27, - "NONASSOC": 34, - "OPTIONS": 25, - "OPTIONS_END": 26, - "OPTION_STRING_VALUE": 28, - "OPTION_VALUE": 29, - "PARSER_TYPE": 31, - "PARSE_PARAM": 30, - "PATH": 43, - "PREC": 39, - "RIGHT": 33, - "START": 16, - "STRING": 24, - "TOKEN": 18, - "TOKEN_TYPE": 35, - "UNKNOWN_DECL": 20, - "action": 80, - "action_body": 81, - "action_comments_body": 82, - "action_ne": 79, - "associativity": 58, - "declaration": 49, - "declaration_list": 48, - "error": 2, - "expression": 74, - "extra_parser_module_code": 83, - "full_token_definitions": 60, - "grammar": 66, - "handle": 71, - "handle_action": 70, - "handle_list": 69, - "handle_sublist": 72, - "id": 78, - "id_list": 65, - "import_name": 50, - "import_path": 51, - "include_macro_code": 84, - "module_code_chunk": 85, - "one_full_token": 61, - "operator": 57, - "option": 54, - "option_list": 53, - "optional_action_header_block": 47, - "optional_end_block": 46, - "optional_module_code_chunk": 86, - "optional_token_type": 62, - "options": 52, - "parse_params": 55, - "parser_type": 56, - "prec": 76, - "production": 68, - "production_list": 67, - "spec": 45, - "suffix": 75, - "suffixed_expression": 73, - "symbol": 77, - "token_description": 64, - "token_list": 59, - "token_value": 63, - "{": 12, - "|": 6, - "}": 13 -}, -terminals_: { - 1: "EOF", - 2: "error", - 3: "=", - 4: ":", - 5: ";", - 6: "|", - 7: "(", - 8: ")", - 9: "*", - 10: "?", - 11: "+", - 12: "{", - 13: "}", - 14: "%%", - 15: "ACTION", - 16: "START", - 17: "LEX_BLOCK", - 18: "TOKEN", - 19: "DEBUG", - 20: "UNKNOWN_DECL", - 21: "IMPORT", - 22: "INIT_CODE", - 23: "ID", - 24: "STRING", - 25: "OPTIONS", - 26: "OPTIONS_END", - 27: "NAME", - 28: "OPTION_STRING_VALUE", - 29: "OPTION_VALUE", - 30: "PARSE_PARAM", - 31: "PARSER_TYPE", - 32: "LEFT", - 33: "RIGHT", - 34: "NONASSOC", - 35: "TOKEN_TYPE", - 36: "INTEGER", - 37: "EPSILON", - 38: "ALIAS", - 39: "PREC", - 40: "ARROW_ACTION", - 41: "ACTION_BODY", - 42: "INCLUDE", - 43: "PATH", - 44: "CODE" -}, -TERROR: 2, -EOF: 1, - -// internals: defined here so the object *structure* doesn't get modified by parse() et al, -// thus helping JIT compilers like Chrome V8. -originalQuoteName: null, -originalParseError: null, -cleanupAfterParse: null, -constructParseErrorInfo: null, - -__reentrant_call_depth: 0, // INTERNAL USE ONLY -__error_infos: [], // INTERNAL USE ONLY: the set of parseErrorInfo objects created since the last cleanup - -// APIs which will be set up depending on user action code analysis: -//yyRecovering: 0, -//yyErrOk: 0, -//yyClearIn: 0, - -// Helper APIs -// ----------- - -// Helper function which can be overridden by user code later on: put suitable quotes around -// literal IDs in a description string. -quoteName: function parser_quoteName(id_str) { - return '"' + id_str + '"'; -}, - -// Return a more-or-less human-readable description of the given symbol, when available, -// or the symbol itself, serving as its own 'description' for lack of something better to serve up. -// -// Return NULL when the symbol is unknown to the parser. -describeSymbol: function parser_describeSymbol(symbol) { - if (symbol !== this.EOF && this.terminal_descriptions_ && this.terminal_descriptions_[symbol]) { - return this.terminal_descriptions_[symbol]; - } - else if (symbol === this.EOF) { - return 'end of input'; - } - else if (this.terminals_[symbol]) { - return this.quoteName(this.terminals_[symbol]); - } - // Otherwise... this might refer to a RULE token i.e. a non-terminal: see if we can dig that one up. - // - // An example of this may be where a rule's action code contains a call like this: - // - // parser.describeSymbol(#$) - // - // to obtain a human-readable description or name of the current grammar rule. This comes handy in - // error handling action code blocks, for example. - var s = this.symbols_; - for (var key in s) { - if (s[key] === symbol) { - return key; - } - } - return null; -}, - -// Produce a (more or less) human-readable list of expected tokens at the point of failure. -// -// The produced list may contain token or token set descriptions instead of the tokens -// themselves to help turning this output into something that easier to read by humans -// unless `do_not_describe` parameter is set, in which case a list of the raw, *numeric*, -// expected terminals and nonterminals is produced. -// -// The returned list (array) will not contain any duplicate entries. -collect_expected_token_set: function parser_collect_expected_token_set(state, do_not_describe) { - var TERROR = this.TERROR; - var tokenset = []; - var check = {}; - // Has this (error?) state been outfitted with a custom expectations description text for human consumption? - // If so, use that one instead of the less palatable token set. - if (!do_not_describe && this.state_descriptions_ && this.state_descriptions_[state]) { - return [ - this.state_descriptions_[state] - ]; - } - for (var p in this.table[state]) { - p = +p; - if (p !== TERROR) { - var d = do_not_describe ? p : this.describeSymbol(p); - if (d && !check[d]) { - tokenset.push(d); - check[d] = true; // Mark this token description as already mentioned to prevent outputting duplicate entries. - } - } - } - return tokenset; -}, -productions_: bp({ - pop: u([ - s, - [45, 3], - 46, - 46, - s, - [47, 3], - 48, - 48, - s, - [49, 16], - 50, - 50, - 51, - 51, - 52, - 53, - 53, - s, - [54, 4], - s, - [55, 4, 1], - 58, - 58, - 59, - 59, - 60, - 60, - s, - [61, 3], - 62, - s, - [62, 4, 1], - 65, - 66, - 67, - 67, - 68, - 69, - 69, - 70, - 70, - 71, - 71, - 72, - 72, - 73, - 73, - s, - [74, 4], - s, - [75, 4], - 76, - 76, - 77, - 77, - 78, - s, - [79, 5], - 80, - 80, - s, - [81, 5], - 82, - 82, - 83, - 83, - 84, - 84, - 85, - 85, - 86, - 86 -]), - rule: u([ - 5, - 5, - 3, - 0, - 2, - 0, - s, - [2, 3], - 0, - 2, - 1, - 1, - c, - [3, 3], - s, - [1, 5], - s, - [3, 5], - c, - [9, 5], - c, - [18, 3], - s, - [3, 3], - s, - [2, 3], - s, - [1, 3], - 2, - 1, - 2, - 2, - c, - [11, 3], - 0, - c, - [11, 7], - 1, - 4, - 3, - c, - [32, 3], - 2, - 0, - c, - [6, 4], - c, - [38, 4], - c, - [24, 5], - c, - [5, 4], - c, - [59, 6], - 0, - 0, - 1, - 5, - 4, - 4, - c, - [42, 3], - c, - [36, 3], - c, - [6, 3], - 0 -]) -}), -performAction: function parser__PerformAction(yyloc, yystate /* action[1] */, yysp, yyvstack, yylstack) { -/* this == yyval */ -var yy = this.yy; - -switch (yystate) { -case 1: - /*! Production:: spec : declaration_list "%%" grammar optional_end_block EOF */ - this.$ = yyvstack[yysp - 4]; - if (yyvstack[yysp - 1] && yyvstack[yysp - 1].trim() !== '') { - yy.addDeclaration(this.$, { include: yyvstack[yysp - 1] }); - } - return extend(this.$, yyvstack[yysp - 2]); - break; - -case 2: - /*! Production:: spec : declaration_list "%%" grammar error EOF */ - yy.parser.yyError("Maybe you did not correctly separate trailing code from the grammar rule set with a '%%' marker on an otherwise empty line?"); - break; - -case 3: - /*! Production:: spec : declaration_list error EOF */ - yy.parser.yyError("Maybe you did not correctly separate the parse 'header section' (token definitions, options, lexer spec, etc.) from the grammar rule set with a '%%' on an otherwise empty line?"); - break; - -case 5: - /*! Production:: optional_end_block : "%%" extra_parser_module_code */ -case 38: - /*! Production:: parse_params : PARSE_PARAM token_list */ -case 39: - /*! Production:: parser_type : PARSER_TYPE symbol */ -case 71: - /*! Production:: expression : ID */ -case 81: - /*! Production:: symbol : id */ -case 82: - /*! Production:: symbol : STRING */ -case 83: - /*! Production:: id : ID */ -case 86: - /*! Production:: action_ne : ACTION */ -case 87: - /*! Production:: action_ne : include_macro_code */ -case 89: - /*! Production:: action : action_ne */ -case 92: - /*! Production:: action_body : action_comments_body */ -case 96: - /*! Production:: action_comments_body : ACTION_BODY */ -case 98: - /*! Production:: extra_parser_module_code : optional_module_code_chunk */ -case 102: - /*! Production:: module_code_chunk : CODE */ -case 104: - /*! Production:: optional_module_code_chunk : module_code_chunk */ - this.$ = yyvstack[yysp]; - break; - -case 6: - /*! Production:: optional_action_header_block : ε */ -case 10: - /*! Production:: declaration_list : ε */ - this.$ = {}; - break; - -case 7: - /*! Production:: optional_action_header_block : optional_action_header_block ACTION */ -case 8: - /*! Production:: optional_action_header_block : optional_action_header_block include_macro_code */ - this.$ = yyvstack[yysp - 1]; - yy.addDeclaration(this.$, { actionInclude: yyvstack[yysp] }); - break; - -case 9: - /*! Production:: declaration_list : declaration_list declaration */ - this.$ = yyvstack[yysp - 1]; yy.addDeclaration(this.$, yyvstack[yysp]); - break; - -case 11: - /*! Production:: declaration : START id */ - this.$ = {start: yyvstack[yysp]}; - break; - -case 12: - /*! Production:: declaration : LEX_BLOCK */ - this.$ = {lex: {text: yyvstack[yysp], position: yylstack[yysp]}}; - break; - -case 13: - /*! Production:: declaration : operator */ - this.$ = {operator: yyvstack[yysp]}; - break; - -case 14: - /*! Production:: declaration : TOKEN full_token_definitions */ - this.$ = {token_list: yyvstack[yysp]}; - break; - -case 15: - /*! Production:: declaration : ACTION */ -case 16: - /*! Production:: declaration : include_macro_code */ - this.$ = {include: yyvstack[yysp]}; - break; - -case 17: - /*! Production:: declaration : parse_params */ - this.$ = {parseParams: yyvstack[yysp]}; - break; - -case 18: - /*! Production:: declaration : parser_type */ - this.$ = {parserType: yyvstack[yysp]}; - break; - -case 19: - /*! Production:: declaration : options */ - this.$ = {options: yyvstack[yysp]}; - break; - -case 20: - /*! Production:: declaration : DEBUG */ - this.$ = {options: [['debug', true]]}; - break; - -case 21: - /*! Production:: declaration : UNKNOWN_DECL */ - this.$ = {unknownDecl: yyvstack[yysp]}; - break; - -case 22: - /*! Production:: declaration : IMPORT import_name import_path */ - this.$ = {imports: {name: yyvstack[yysp - 1], path: yyvstack[yysp]}}; - break; - -case 23: - /*! Production:: declaration : IMPORT import_name error */ - yy.parser.yyError("You did not specify a legal file path for the '%import' initialization code statement, which must have the format: '%import qualifier_name file_path'."); - break; - -case 24: - /*! Production:: declaration : IMPORT error import_path */ - yy.parser.yyError("Each '%import'-ed initialization code section must be qualified by a name, e.g. 'required' before the import path itself: '%import qualifier_name file_path'."); - break; - -case 25: - /*! Production:: declaration : INIT_CODE import_name action_ne */ - this.$ = {initCode: {qualifier: yyvstack[yysp - 1], include: yyvstack[yysp]}}; - break; - -case 26: - /*! Production:: declaration : INIT_CODE error action_ne */ - yy.parser.yyError("Each '%code' initialization code section must be qualified by a name, e.g. 'required' before the action code itself: '%code qualifier_name {action code}'."); - break; - -case 31: - /*! Production:: options : OPTIONS option_list OPTIONS_END */ -case 84: - /*! Production:: action_ne : "{" action_body "}" */ - this.$ = yyvstack[yysp - 1]; - break; - -case 32: - /*! Production:: option_list : option_list option */ -case 44: - /*! Production:: token_list : token_list symbol */ -case 55: - /*! Production:: id_list : id_list id */ - this.$ = yyvstack[yysp - 1]; this.$.push(yyvstack[yysp]); - break; - -case 33: - /*! Production:: option_list : option */ -case 45: - /*! Production:: token_list : symbol */ -case 56: - /*! Production:: id_list : id */ -case 62: - /*! Production:: handle_list : handle_action */ - this.$ = [yyvstack[yysp]]; - break; - -case 34: - /*! Production:: option : NAME */ - this.$ = [yyvstack[yysp], true]; - break; - -case 35: - /*! Production:: option : NAME "=" OPTION_STRING_VALUE */ - this.$ = [yyvstack[yysp - 2], yyvstack[yysp]]; - break; - -case 36: - /*! Production:: option : NAME "=" OPTION_VALUE */ -case 37: - /*! Production:: option : NAME "=" NAME */ - this.$ = [yyvstack[yysp - 2], parseValue(yyvstack[yysp])]; - break; - -case 40: - /*! Production:: operator : associativity token_list */ - this.$ = [yyvstack[yysp - 1]]; this.$.push.apply(this.$, yyvstack[yysp]); - break; - -case 41: - /*! Production:: associativity : LEFT */ - this.$ = 'left'; - break; - -case 42: - /*! Production:: associativity : RIGHT */ - this.$ = 'right'; - break; - -case 43: - /*! Production:: associativity : NONASSOC */ - this.$ = 'nonassoc'; - break; - -case 46: - /*! Production:: full_token_definitions : optional_token_type id_list */ - var rv = []; - var lst = yyvstack[yysp]; - for (var i = 0, len = lst.length; i < len; i++) { - var id = lst[i]; - var m = {id: id}; - if (yyvstack[yysp - 1]) { - m.type = yyvstack[yysp - 1]; - } - rv.push(m); - } - this.$ = rv; - break; - -case 47: - /*! Production:: full_token_definitions : optional_token_type one_full_token */ - var m = yyvstack[yysp]; - if (yyvstack[yysp - 1]) { - m.type = yyvstack[yysp - 1]; - } - this.$ = [m]; - break; - -case 48: - /*! Production:: one_full_token : id token_value token_description */ - this.$ = { - id: yyvstack[yysp - 2], - value: yyvstack[yysp - 1] - }; - break; - -case 49: - /*! Production:: one_full_token : id token_description */ - this.$ = { - id: yyvstack[yysp - 1], - description: yyvstack[yysp] - }; - break; - -case 50: - /*! Production:: one_full_token : id token_value */ - this.$ = { - id: yyvstack[yysp - 1], - value: yyvstack[yysp], - description: $token_description - }; - break; - -case 51: - /*! Production:: optional_token_type : ε */ - this.$ = false; - break; - -case 57: - /*! Production:: grammar : optional_action_header_block production_list */ - this.$ = yyvstack[yysp - 1]; - this.$.grammar = yyvstack[yysp]; - break; - -case 58: - /*! Production:: production_list : production_list production */ - this.$ = yyvstack[yysp - 1]; - if (yyvstack[yysp][0] in this.$) { - this.$[yyvstack[yysp][0]] = this.$[yyvstack[yysp][0]].concat(yyvstack[yysp][1]); - } else { - this.$[yyvstack[yysp][0]] = yyvstack[yysp][1]; - } - break; - -case 59: - /*! Production:: production_list : production */ - this.$ = {}; this.$[yyvstack[yysp][0]] = yyvstack[yysp][1]; - break; - -case 60: - /*! Production:: production : id ":" handle_list ";" */ - this.$ = [yyvstack[yysp - 3], yyvstack[yysp - 1]]; - break; - -case 61: - /*! Production:: handle_list : handle_list "|" handle_action */ - this.$ = yyvstack[yysp - 2]; - this.$.push(yyvstack[yysp]); - break; - -case 63: - /*! Production:: handle_action : handle prec action */ - this.$ = [(yyvstack[yysp - 2].length ? yyvstack[yysp - 2].join(' ') : '')]; - if (yyvstack[yysp]) { - this.$.push(yyvstack[yysp]); - } - if (yyvstack[yysp - 1]) { - if (yyvstack[yysp - 2].length === 0) { - yy.parser.yyError('You cannot specify a precedence override for an epsilon (a.k.a. empty) rule!'); - } - this.$.push(yyvstack[yysp - 1]); - } - if (this.$.length === 1) { - this.$ = this.$[0]; - } - break; - -case 64: - /*! Production:: handle_action : EPSILON action */ - this.$ = ['']; - if (yyvstack[yysp]) { - this.$.push(yyvstack[yysp]); - } - if (this.$.length === 1) { - this.$ = this.$[0]; - } - break; - -case 65: - /*! Production:: handle : handle suffixed_expression */ - this.$ = yyvstack[yysp - 1]; - this.$.push(yyvstack[yysp]); - break; - -case 66: - /*! Production:: handle : ε */ - this.$ = []; - break; - -case 67: - /*! Production:: handle_sublist : handle_sublist "|" handle */ - this.$ = yyvstack[yysp - 2]; - this.$.push(yyvstack[yysp].join(' ')); - break; - -case 68: - /*! Production:: handle_sublist : handle */ - this.$ = [yyvstack[yysp].join(' ')]; - break; - -case 69: - /*! Production:: suffixed_expression : expression suffix ALIAS */ - this.$ = yyvstack[yysp - 2] + yyvstack[yysp - 1] + "[" + yyvstack[yysp] + "]"; - break; - -case 70: - /*! Production:: suffixed_expression : expression suffix */ -case 97: - /*! Production:: action_comments_body : action_comments_body ACTION_BODY */ -case 103: - /*! Production:: module_code_chunk : module_code_chunk CODE */ - this.$ = yyvstack[yysp - 1] + yyvstack[yysp]; - break; - -case 72: - /*! Production:: expression : STRING */ - // Re-encode the string *anyway* as it will - // be made part of the rule rhs a.k.a. production (type: *string*) again and we want - // to be able to handle all tokens, including *significant space* - // encoded as literal tokens in a grammar such as this: `rule: A ' ' B`. - if (yyvstack[yysp].indexOf("'") >= 0) { - this.$ = '"' + yyvstack[yysp] + '"'; - } else { - this.$ = "'" + yyvstack[yysp] + "'"; - } - break; - -case 73: - /*! Production:: expression : "(" handle_sublist ")" */ - this.$ = '(' + yyvstack[yysp - 1].join(' | ') + ')'; - break; - -case 74: - /*! Production:: expression : "(" handle_sublist error */ - var l = yyvstack[yysp - 1]; - var ab = l.slice(0, 10).join(' | '); - yy.parser.yyError("Seems you did not correctly bracket a grammar rule sublist in '( ... )' brackets. Offending handle sublist:\n" + ab); - break; - -case 75: - /*! Production:: suffix : ε */ -case 90: - /*! Production:: action : ε */ -case 91: - /*! Production:: action_body : ε */ -case 105: - /*! Production:: optional_module_code_chunk : ε */ - this.$ = ''; - break; - -case 79: - /*! Production:: prec : PREC symbol */ - this.$ = { prec: yyvstack[yysp] }; - break; - -case 80: - /*! Production:: prec : ε */ - this.$ = null; - break; - -case 85: - /*! Production:: action_ne : "{" action_body error */ - var l = yyvstack[yysp - 1].split('\n'); - var ab = l.slice(0, 10).join('\n'); - yy.parser.yyError("Seems you did not correctly bracket a parser rule action block in curly braces: '{ ... }'. Offending action body:\n" + ab); - break; - -case 88: - /*! Production:: action_ne : ARROW_ACTION */ - this.$ = '$$ = ' + yyvstack[yysp]; - break; - -case 93: - /*! Production:: action_body : action_body "{" action_body "}" action_comments_body */ - this.$ = yyvstack[yysp - 4] + yyvstack[yysp - 3] + yyvstack[yysp - 2] + yyvstack[yysp - 1] + yyvstack[yysp]; - break; - -case 94: - /*! Production:: action_body : action_body "{" action_body "}" */ - this.$ = yyvstack[yysp - 3] + yyvstack[yysp - 2] + yyvstack[yysp - 1] + yyvstack[yysp]; - break; - -case 95: - /*! Production:: action_body : action_body "{" action_body error */ - var l = yyvstack[yysp - 1].split('\n'); - var ab = l.slice(0, 10).join('\n'); - yy.parser.yyError("Seems you did not correctly match curly braces '{ ... }' in a parser rule action block. Offending action body part:\n" + ab); - break; - -case 99: - /*! Production:: extra_parser_module_code : optional_module_code_chunk include_macro_code extra_parser_module_code */ - this.$ = yyvstack[yysp - 2] + yyvstack[yysp - 1] + yyvstack[yysp]; - break; - -case 100: - /*! Production:: include_macro_code : INCLUDE PATH */ - var fs = require('fs'); - var fileContent = fs.readFileSync(yyvstack[yysp], { encoding: 'utf-8' }); - // And no, we don't support nested '%include': - this.$ = '\n// Included by Jison: ' + yyvstack[yysp] + ':\n\n' + fileContent + '\n\n// End Of Include by Jison: ' + yyvstack[yysp] + '\n\n'; - break; - -case 101: - /*! Production:: include_macro_code : INCLUDE error */ - yy.parser.yyError("%include MUST be followed by a valid file path"); - break; - -} -}, -table: bt({ - len: u([ - 19, - 1, - 24, - 5, - 1, - 17, - 2, - 17, - 17, - 4, - s, - [17, 7], - 4, - 4, - 5, - 2, - s, - [5, 4, -1], - 2, - 2, - 4, - 7, - 1, - 17, - 25, - 17, - 4, - 1, - 4, - 3, - 7, - 7, - 6, - 6, - 21, - 19, - 23, - 23, - 22, - 22, - 21, - 17, - 3, - 2, - 3, - 1, - 1, - 6, - 6, - 3, - 3, - 4, - 1, - 19, - 17, - 22, - s, - [17, 6], - 6, - s, - [19, 3], - 17, - 19, - 17, - c, - [26, 4], - 1, - s, - [3, 3], - 4, - 14, - 18, - 19, - 17, - 18, - 17, - 3, - 4, - 4, - s, - [2, 3], - 6, - c, - [75, 3], - 13, - 9, - 17, - 19, - 19, - 6, - c, - [74, 3], - 13, - 9, - 12, - 4, - 17, - 16, - 16, - 8, - 2, - 2, - c, - [22, 3], - 6, - s, - [13, 4], - 3, - 8, - 5, - 3, - 12, - 16, - 16, - 7, - 4, - 8 -]), - symbol: u([ - 2, - s, - [14, 9, 1], - 25, - s, - [30, 5, 1], - 42, - 45, - 48, - 1, - c, - [20, 17], - 49, - 52, - s, - [55, 4, 1], - 84, - 15, - 23, - 42, - 47, - 66, - c, - [30, 18], - 23, - 78, - c, - [19, 17], - c, - [36, 18], - 35, - 60, - 62, - c, - [38, 34], - c, - [17, 86], - 23, - 24, - 50, - c, - [4, 4], - 23, - 24, - 59, - 77, - 78, - 2, - 43, - c, - [7, 5], - 23, - 24, - 77, - 78, - 27, - 53, - 54, - 23, - 24, - 23, - 24, - 23, - 24, - c, - [210, 3], - 46, - c, - [219, 3], - 67, - 68, - 78, - 84, - c, - [221, 18], - 2, - 4, - 5, - 6, - 12, - s, - [14, 12, 1], - c, - [23, 5], - 36, - 40, - c, - [227, 19], - 61, - 65, - 78, - 23, - c, - [105, 3], - 51, - c, - [3, 3], - 2, - 12, - 15, - 23, - 24, - c, - [36, 3], - c, - [7, 6], - 12, - 15, - 40, - 42, - 79, - 84, - c, - [6, 6], - c, - [55, 10], - c, - [76, 8], - 42, - c, - [150, 3], - c, - [21, 18], - 2, - c, - [119, 20], - c, - [82, 3], - c, - [23, 22], - 1, - c, - [24, 3], - c, - [23, 10], - c, - [67, 7], - 44, - c, - [22, 22], - c, - [130, 31], - c, - [19, 7], - 26, - 27, - 54, - 26, - 27, - 3, - 26, - 27, - s, - [1, 3], - 42, - 44, - 83, - 85, - 86, - c, - [282, 3], - 23, - 68, - 78, - c, - [295, 3], - c, - [3, 3], - c, - [12, 4], - 4, - c, - [71, 11], - c, - [51, 7], - c, - [519, 28], - c, - [313, 9], - 42, - 63, - 64, - c, - [503, 103], - 12, - 13, - 41, - 81, - 82, - c, - [258, 12], - c, - [346, 10], - c, - [19, 36], - c, - [204, 34], - c, - [36, 18], - 26, - 27, - 27, - 28, - 29, - s, - [1, 4], - 42, - 84, - c, - [310, 3], - c, - [3, 4], - c, - [298, 3], - 5, - 6, - 7, - c, - [519, 4], - 37, - 39, - 40, - 42, - 69, - 70, - 71, - c, - [311, 18], - c, - [18, 10], - c, - [88, 8], - c, - [290, 28], - c, - [124, 25], - c, - [240, 3], - c, - [243, 4], - c, - [4, 4], - 26, - 27, - 26, - 27, - c, - [442, 3], - c, - [440, 6], - 42, - 44, - 5, - 6, - 5, - 6, - c, - [133, 7], - c, - [132, 3], - 73, - 74, - 76, - c, - [580, 3], - c, - [652, 4], - 80, - c, - [653, 11], - c, - [284, 46], - c, - [347, 6], - c, - [6, 3], - 1, - c, - [225, 15], - 70, - 71, - c, - [92, 10], - s, - [5, 4, 1], - c, - [116, 7], - c, - [879, 4], - c, - [16, 5], - s, - [9, 4, 1], - c, - [19, 3], - 38, - c, - [20, 3], - 75, - c, - [17, 16], - c, - [16, 17], - c, - [15, 3], - 23, - 24, - 71, - 72, - c, - [189, 4], - c, - [108, 3], - c, - [198, 6], - c, - [93, 4], - c, - [90, 9], - c, - [54, 9], - c, - [13, 35], - 6, - 8, - c, - [80, 6], - 73, - 74, - c, - [184, 4], - c, - [189, 4], - c, - [161, 12], - c, - [140, 39], - c, - [353, 5], - c, - [71, 7] -]), - type: u([ - s, - [2, 17], - 0, - 0, - 1, - c, - [20, 19], - s, - [0, 5], - c, - [10, 5], - s, - [2, 19], - c, - [20, 20], - c, - [68, 19], - s, - [2, 122], - c, - [186, 5], - c, - [199, 5], - c, - [206, 7], - c, - [143, 5], - c, - [146, 11], - c, - [219, 6], - c, - [163, 63], - c, - [95, 8], - c, - [273, 21], - c, - [123, 8], - c, - [282, 143], - c, - [130, 27], - c, - [20, 11], - c, - [313, 9], - c, - [358, 33], - c, - [520, 142], - c, - [346, 122], - c, - [121, 22], - c, - [610, 39], - c, - [182, 73], - c, - [112, 20], - c, - [20, 9], - c, - [751, 62], - c, - [61, 22], - c, - [92, 25], - c, - [47, 18], - c, - [125, 39], - c, - [451, 80], - c, - [932, 9], - c, - [469, 62], - 0, - 0 -]), - state: u([ - 1, - 2, - 5, - 14, - 12, - 13, - 8, - 19, - 11, - 28, - 27, - 30, - 32, - 33, - 35, - 39, - 41, - 42, - 43, - 47, - 42, - 43, - 48, - 43, - 49, - 50, - 52, - 55, - 58, - 59, - 57, - 61, - 60, - 62, - 63, - 67, - 68, - 71, - 73, - 71, - 74, - 43, - 74, - 43, - 76, - 80, - 82, - 81, - 84, - 59, - 86, - 87, - 88, - 91, - 92, - 97, - 99, - 100, - 101, - 103, - 108, - 82, - 81, - 112, - 114, - 111, - 119, - 118, - 71, - 120, - 92, - 121, - 101, - 119, - 122, - 71, - 123, - 43, - 124, - 129, - 128, - 112, - 114, - 136, - 137, - 112, - 114 -]), - mode: u([ - s, - [2, 17], - s, - [1, 17], - c, - [20, 4], - c, - [38, 18], - s, - [2, 35], - c, - [36, 36], - s, - [2, 84], - c, - [192, 18], - c, - [22, 9], - c, - [87, 61], - c, - [67, 20], - c, - [101, 15], - c, - [18, 5], - s, - [2, 126], - c, - [128, 26], - c, - [26, 4], - c, - [3, 4], - c, - [7, 6], - c, - [415, 12], - c, - [11, 22], - c, - [473, 32], - c, - [233, 107], - c, - [340, 113], - c, - [467, 7], - c, - [299, 7], - c, - [138, 43], - c, - [170, 60], - c, - [877, 19], - c, - [17, 5], - c, - [139, 9], - c, - [10, 7], - c, - [713, 61], - c, - [59, 17], - c, - [17, 6], - c, - [85, 16], - c, - [14, 7], - c, - [107, 53], - c, - [53, 22], - c, - [73, 43], - c, - [65, 5], - c, - [880, 4], - c, - [337, 60], - c, - [67, 7] -]), - goto: u([ - s, - [10, 17], - 4, - 3, - 10, - 6, - 7, - 9, - s, - [15, 4, 1], - 23, - 21, - 22, - 24, - 25, - 26, - 20, - s, - [6, 3], - 29, - s, - [9, 17], - 31, - s, - [12, 17], - s, - [13, 17], - 51, - 34, - s, - [15, 17], - s, - [16, 17], - s, - [17, 17], - s, - [18, 17], - s, - [19, 17], - s, - [20, 17], - s, - [21, 17], - 36, - 37, - 38, - 40, - 37, - 38, - 31, - 44, - 46, - 45, - 31, - 44, - 31, - 44, - 51, - 41, - 41, - 42, - 42, - 43, - 43, - 4, - 53, - 54, - 56, - 31, - 20, - 3, - s, - [11, 17], - s, - [83, 25], - s, - [14, 17], - 31, - 52, - 64, - 65, - 66, - 65, - 66, - s, - [27, 7], - s, - [28, 7], - 69, - 70, - 72, - 20, - c, - [4, 4], - s, - [40, 10], - 31, - 44, - s, - [40, 7], - s, - [45, 19], - s, - [81, 23], - s, - [82, 23], - s, - [100, 22], - s, - [101, 22], - s, - [38, 10], - 31, - 44, - s, - [38, 7], - s, - [39, 17], - 75, - 51, - 33, - 33, - 77, - 34, - 34, - 78, - 79, - 105, - 105, - 83, - s, - [57, 3], - 31, - s, - [7, 3], - s, - [8, 3], - s, - [59, 4], - 85, - s, - [46, 10], - 31, - s, - [46, 7], - s, - [47, 17], - s, - [56, 11], - 90, - s, - [56, 6], - 89, - 56, - s, - [22, 17], - s, - [23, 17], - s, - [29, 17], - s, - [30, 17], - s, - [24, 17], - s, - [25, 17], - s, - [91, 3], - 93, - s, - [86, 19], - s, - [87, 19], - s, - [88, 19], - s, - [26, 17], - s, - [44, 19], - s, - [31, 17], - 32, - 32, - 96, - 94, - 95, - 1, - 2, - 5, - 98, - 20, - 104, - 104, - 98, - s, - [102, 3], - s, - [58, 4], - s, - [66, 7], - 102, - s, - [66, 3], - s, - [55, 18], - s, - [50, 10], - 90, - s, - [50, 7], - s, - [49, 17], - s, - [53, 18], - s, - [54, 17], - 105, - 106, - 104, - s, - [92, 3], - 107, - s, - [96, 4], - 35, - 35, - 36, - 36, - 37, - 37, - c, - [425, 3], - s, - [103, 3], - 109, - 110, - 62, - 62, - 80, - 80, - 117, - 80, - 80, - 115, - 116, - 113, - 80, - 80, - 90, - 90, - c, - [624, 4], - s, - [48, 17], - s, - [84, 19], - s, - [85, 19], - c, - [331, 4], - s, - [97, 4], - 99, - s, - [60, 4], - c, - [210, 11], - c, - [85, 6], - s, - [65, 12], - 31, - 44, - s, - [75, 5], - 125, - 126, - 127, - s, - [75, 8], - s, - [71, 16], - s, - [72, 16], - s, - [66, 6], - 64, - 64, - 89, - 89, - 131, - 106, - 130, - 61, - 61, - 63, - 63, - s, - [79, 6], - s, - [70, 9], - 132, - s, - [70, 3], - s, - [76, 13], - s, - [77, 13], - s, - [78, 13], - 134, - 135, - 133, - 68, - 68, - 117, - 68, - 115, - 116, - s, - [94, 3], - 93, - s, - [95, 3], - s, - [69, 12], - s, - [73, 16], - s, - [74, 16], - s, - [66, 6], - s, - [93, 3], - 107, - 67, - 67, - 117, - 67, - 115, - 116 -]) -}), -defaultActions: bda({ - idx: u([ - 0, - 3, - 5, - 7, - 8, - s, - [10, 7, 1], - 24, - 25, - 26, - s, - [29, 4, 1], - 34, - 37, - 38, - s, - [42, 5, 1], - 48, - 50, - 56, - 57, - 58, - 61, - s, - [63, 6, 1], - s, - [70, 7, 1], - 78, - 79, - 80, - 83, - 84, - 86, - 88, - 89, - 90, - s, - [93, 4, 1], - 98, - 100, - 103, - 104, - 105, - 107, - 108, - 109, - 112, - s, - [115, 5, 1], - 121, - 122, - 123, - 125, - 126, - 127, - s, - [131, 5, 1] -]), - goto: u([ - 10, - 6, - 9, - 12, - 13, - s, - [15, 7, 1], - 41, - 42, - 43, - 3, - 11, - 83, - 14, - 52, - 27, - 28, - 45, - 81, - 82, - 100, - 101, - 39, - 33, - 7, - 8, - 59, - 47, - 22, - 23, - 29, - 30, - 24, - 25, - 86, - 87, - 88, - 26, - 44, - 31, - 32, - 1, - 2, - 5, - 102, - 58, - 55, - 49, - 53, - 54, - 96, - 35, - 36, - 37, - 103, - 62, - 48, - 84, - 85, - 97, - 99, - 60, - 65, - 71, - 72, - 66, - 64, - 89, - 61, - 63, - 79, - 76, - 77, - 78, - 95, - 69, - 73, - 74, - 66 -]) -}), -parseError: function parseError(str, hash, ExceptionClass) { - if (hash.recoverable && typeof this.trace === 'function') { - this.trace(str); - hash.destroy(); // destroy... well, *almost*! - } else { - if (!ExceptionClass) { - ExceptionClass = this.JisonParserError; - } - throw new ExceptionClass(str, hash); - } -}, -parse: function parse(input) { - var self = this, - stack = new Array(128), // token stack: stores token which leads to state at the same index (column storage) - sstack = new Array(128), // state stack: stores states (column storage) - - vstack = new Array(128), // semantic value stack - lstack = new Array(128), // location stack - table = this.table, - sp = 0; // 'stack pointer': index into the stacks - - var recovering = 0; // (only used when the grammar contains error recovery rules) - var TERROR = this.TERROR, - EOF = this.EOF, - ERROR_RECOVERY_TOKEN_DISCARD_COUNT = (this.options.errorRecoveryTokenDiscardCount | 0) || 3; - var NO_ACTION = [0, table.length /* ensures that anyone using this new state will fail dramatically! */]; - - //this.reductionCount = this.shiftCount = 0; - - var lexer; - if (this.__lexer__) { - lexer = this.__lexer__; - } else { - lexer = this.__lexer__ = Object.create(this.lexer); - } - - var sharedState_yy = { - parseError: null, - quoteName: null, - lexer: null, - parser: null, - pre_parse: null, - post_parse: null - }; - // copy state - for (var k in this.yy) { - if (Object.prototype.hasOwnProperty.call(this.yy, k)) { - sharedState_yy[k] = this.yy[k]; - } - } - - sharedState_yy.lexer = lexer; - sharedState_yy.parser = this; - - - - - - - - - - - - - - - - - - - - // *Always* setup `yyError`, `YYRECOVERING`, `yyErrOk` and `yyClearIn` functions as it is paramount - // to have *their* closure match ours -- if we only set them up once, - // any subsequent `parse()` runs will fail in very obscure ways when - // these functions are invoked in the user action code block(s) as - // their closure will still refer to the `parse()` instance which set - // them up. Hence we MUST set them up at the start of every `parse()` run! - if (this.yyError) { - this.yyError = function yyError(str /*, ...args */) { - - - - var error_rule_depth = (this.options.parserErrorsAreRecoverable ? locateNearestErrorRecoveryRule(state) : -1); - var expected = this.collect_expected_token_set(state); - var hash = this.constructParseErrorInfo(str, null, expected, (error_rule_depth >= 0)); - - - // Add any extra args to the hash under the name `extra_error_attributes`: - var args = Array.prototype.slice.call(arguments, 1); - if (args.length) { - hash.extra_error_attributes = args; - } - - var r = this.parseError(str, hash, this.JisonParserError); - return r; - }; - } - - - - - - - lexer.setInput(input, sharedState_yy); - - var yyloc = lexer.yylloc || {}; - lstack[sp] = yyloc; - vstack[sp] = null; - sstack[sp] = 0; - stack[sp] = 0; - ++sp; - - - - - - var ranges = lexer.options && lexer.options.ranges; - - // Does the shared state override the default `parseError` that already comes with this instance? - if (typeof sharedState_yy.parseError === 'function') { - this.parseError = function parseErrorAlt(str, hash, ExceptionClass) { - if (!ExceptionClass) { - ExceptionClass = this.JisonParserError; - } - return sharedState_yy.parseError(str, hash, ExceptionClass); - }; - } else { - this.parseError = this.originalParseError; - } - - // Does the shared state override the default `quoteName` that already comes with this instance? - if (typeof sharedState_yy.quoteName === 'function') { - this.quoteName = sharedState_yy.quoteName; - } else { - this.quoteName = this.originalQuoteName; - } - - // set up the cleanup function; make it an API so that external code can re-use this one in case of - // calamities or when the `%options no-try-catch` option has been specified for the grammar, in which - // case this parse() API method doesn't come with a `finally { ... }` block any more! - // - // NOTE: as this API uses parse() as a closure, it MUST be set again on every parse() invocation, - // or else your `sharedState`, etc. references will be *wrong*! - this.cleanupAfterParse = function parser_cleanupAfterParse(resultValue, invoke_post_methods, do_not_nuke_errorinfos) { - var rv; - - if (invoke_post_methods) { - if (sharedState_yy.post_parse) { - rv = sharedState_yy.post_parse.call(this, sharedState_yy, resultValue); - if (typeof rv !== 'undefined') resultValue = rv; - } - if (this.post_parse) { - rv = this.post_parse.call(this, sharedState_yy, resultValue); - if (typeof rv !== 'undefined') resultValue = rv; - } - } - - if (this.__reentrant_call_depth > 1) return resultValue; // do not (yet) kill the sharedState when this is a reentrant run. - - // clean up the lingering lexer structures as well: - if (lexer.cleanupAfterLex) { - lexer.cleanupAfterLex(do_not_nuke_errorinfos); - } - - // prevent lingering circular references from causing memory leaks: - if (sharedState_yy) { - sharedState_yy.parseError = undefined; - sharedState_yy.quoteName = undefined; - sharedState_yy.lexer = undefined; - sharedState_yy.parser = undefined; - if (lexer.yy === sharedState_yy) { - lexer.yy = undefined; - } - } - sharedState_yy = undefined; - this.parseError = this.originalParseError; - this.quoteName = this.originalQuoteName; - - // nuke the vstack[] array at least as that one will still reference obsoleted user values. - // To be safe, we nuke the other internal stack columns as well... - stack.length = 0; // fastest way to nuke an array without overly bothering the GC - sstack.length = 0; - lstack.length = 0; - vstack.length = 0; - sp = 0; - - // nuke the error hash info instances created during this run. - // Userland code must COPY any data/references - // in the error hash instance(s) it is more permanently interested in. - if (!do_not_nuke_errorinfos) { - for (var i = this.__error_infos.length - 1; i >= 0; i--) { - var el = this.__error_infos[i]; - if (el && typeof el.destroy === 'function') { - el.destroy(); - } - } - this.__error_infos.length = 0; - } - - return resultValue; - }; - - // NOTE: as this API uses parse() as a closure, it MUST be set again on every parse() invocation, - // or else your `lexer`, `sharedState`, etc. references will be *wrong*! - this.constructParseErrorInfo = function parser_constructParseErrorInfo(msg, ex, expected, recoverable) { - var pei = { - errStr: msg, - exception: ex, - text: lexer.match, - value: lexer.yytext, - token: this.describeSymbol(symbol) || symbol, - token_id: symbol, - line: lexer.yylineno, - loc: lexer.yylloc || {}, - expected: expected, - recoverable: recoverable, - state: state, - action: action, - new_state: newState, - symbol_stack: stack, - state_stack: sstack, - value_stack: vstack, - location_stack: lstack, - stack_pointer: sp, - yy: sharedState_yy, - lexer: lexer, - parser: this, - - // and make sure the error info doesn't stay due to potential - // ref cycle via userland code manipulations. - // These would otherwise all be memory leak opportunities! - // - // Note that only array and object references are nuked as those - // constitute the set of elements which can produce a cyclic ref. - // The rest of the members is kept intact as they are harmless. - destroy: function destructParseErrorInfo() { - // remove cyclic references added to error info: - // info.yy = null; - // info.lexer = null; - // info.value = null; - // info.value_stack = null; - // ... - var rec = !!this.recoverable; - for (var key in this) { - if (this.hasOwnProperty(key) && typeof key === 'object') { - this[key] = undefined; - } - } - this.recoverable = rec; - } - }; - // track this instance so we can `destroy()` it once we deem it superfluous and ready for garbage collection! - this.__error_infos.push(pei); - return pei; - }; - - - function lex() { - var token = lexer.lex(); - // if token isn't its numeric value, convert - if (typeof token !== 'number') { - token = self.symbols_[token] || token; - } - return token || EOF; - } - - - var symbol = 0; - var preErrorSymbol = 0; - var lastEofErrorStateDepth = 0; - var state, action, r, t; - var yyval = { - $: true, - _$: undefined, - yy: sharedState_yy - }; - var p, len, this_production; - var lstack_begin, lstack_end; - var newState; - var retval = false; - - - // Return the rule stack depth where the nearest error rule can be found. - // Return -1 when no error recovery rule was found. - function locateNearestErrorRecoveryRule(state) { - var stack_probe = sp - 1; - var depth = 0; - - // try to recover from error - for (;;) { - // check for error recovery rule in this state - - var t = table[state][TERROR] || NO_ACTION; - if (t[0]) { - // We need to make sure we're not cycling forever: - // once we hit EOF, even when we `yyerrok()` an error, we must - // prevent the core from running forever, - // e.g. when parent rules are still expecting certain input to - // follow after this, for example when you handle an error inside a set - // of braces which are matched by a parent rule in your grammar. - // - // Hence we require that every error handling/recovery attempt - // *after we've hit EOF* has a diminishing state stack: this means - // we will ultimately have unwound the state stack entirely and thus - // terminate the parse in a controlled fashion even when we have - // very complex error/recovery code interplay in the core + user - // action code blocks: - - if (symbol === EOF) { - if (!lastEofErrorStateDepth) { - lastEofErrorStateDepth = sp - 1 - depth; - } else if (lastEofErrorStateDepth <= sp - 1 - depth) { - - --stack_probe; // popStack(1): [symbol, action] - state = sstack[stack_probe]; - ++depth; - continue; - } - } - return depth; - } - if (state === 0 /* $accept rule */ || stack_probe < 1) { - - return -1; // No suitable error recovery rule available. - } - --stack_probe; // popStack(1): [symbol, action] - state = sstack[stack_probe]; - ++depth; - } - } - - - try { - this.__reentrant_call_depth++; - - if (this.pre_parse) { - this.pre_parse.call(this, sharedState_yy); - } - if (sharedState_yy.pre_parse) { - sharedState_yy.pre_parse.call(this, sharedState_yy); - } - - newState = sstack[sp - 1]; - for (;;) { - // retrieve state number from top of stack - state = newState; // sstack[sp - 1]; - - // use default actions if available - if (this.defaultActions[state]) { - action = 2; - newState = this.defaultActions[state]; - } else { - // The single `==` condition below covers both these `===` comparisons in a single - // operation: - // - // if (symbol === null || typeof symbol === 'undefined') ... - if (!symbol) { - symbol = lex(); - } - // read action for current state and first input - t = (table[state] && table[state][symbol]) || NO_ACTION; - newState = t[1]; - action = t[0]; - - - - - // handle parse error - if (!action) { - // first see if there's any chance at hitting an error recovery rule: - var error_rule_depth = locateNearestErrorRecoveryRule(state); - var errStr = null; - var errSymbolDescr = (this.describeSymbol(symbol) || symbol); - var expected = this.collect_expected_token_set(state); - - if (!recovering) { - // Report error - if (typeof lexer.yylineno === 'number') { - errStr = 'Parse error on line ' + (lexer.yylineno + 1) + ': '; - } else { - errStr = 'Parse error: '; - } - if (lexer.showPosition) { - errStr += '\n' + lexer.showPosition(79 - 10, 10) + '\n'; - } - if (expected.length) { - errStr += 'Expecting ' + expected.join(', ') + ', got unexpected ' + errSymbolDescr; - } else { - errStr += 'Unexpected ' + errSymbolDescr; - } - p = this.constructParseErrorInfo(errStr, null, expected, (error_rule_depth >= 0)); - r = this.parseError(p.errStr, p, this.JisonParserError); - - - if (!p.recoverable) { - retval = r; - break; - } else { - // TODO: allow parseError callback to edit symbol and or state at the start of the error recovery process... - } - } - - - - // just recovered from another error - if (recovering === ERROR_RECOVERY_TOKEN_DISCARD_COUNT && error_rule_depth >= 0) { - // only barf a fatal hairball when we're out of look-ahead symbols and none hit a match; - // this DOES discard look-ahead while recovering from an error when said look-ahead doesn't - // suit the error recovery rules... The error HAS been reported already so we're fine with - // throwing away a few items if that is what it takes to match the nearest recovery rule! - if (symbol === EOF || preErrorSymbol === EOF) { - p = this.__error_infos[this.__error_infos.length - 1]; - if (!p) { - p = this.constructParseErrorInfo('Parsing halted while starting to recover from another error.', null, expected, false); - } else { - p.errStr = 'Parsing halted while starting to recover from another error. Previous error which resulted in this fatal result: ' + p.errStr; - p.recoverable = false; - } - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - - // discard current lookahead and grab another - - - - yyloc = lexer.yylloc || {}; - - symbol = lex(); - - - } - - // try to recover from error - if (error_rule_depth < 0) { - p = this.constructParseErrorInfo((errStr || 'Parsing halted. No suitable error recovery rule available.'), null, expected, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - sp -= error_rule_depth; - - preErrorSymbol = (symbol === TERROR ? 0 : symbol); // save the lookahead token - symbol = TERROR; // insert generic error symbol as new lookahead - // allow N (default: 3) real symbols to be shifted before reporting a new error - recovering = ERROR_RECOVERY_TOKEN_DISCARD_COUNT; - - newState = sstack[sp - 1]; - - - - continue; - } - - - } - - - - - - - - - - switch (action) { - // catch misc. parse failures: - default: - // this shouldn't happen, unless resolve defaults are off - if (action instanceof Array) { - p = this.constructParseErrorInfo(('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol), null, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - // Another case of better safe than sorry: in case state transitions come out of another error recovery process - // or a buggy LUT (LookUp Table): - p = this.constructParseErrorInfo('Parsing halted. No viable error recovery approach available due to internal system failure.', null, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - - // shift: - case 1: - //this.shiftCount++; - stack[sp] = symbol; - vstack[sp] = lexer.yytext; - lstack[sp] = lexer.yylloc || {}; - sstack[sp] = newState; // push state - ++sp; - symbol = 0; - if (!preErrorSymbol) { // normal execution / no error - // Pick up the lexer details for the current symbol as that one is not 'look-ahead' any more: - - - - yyloc = lexer.yylloc || {}; - - if (recovering > 0) { - recovering--; - - } - } else { - // error just occurred, resume old lookahead f/ before error, *unless* that drops us straight back into error mode: - symbol = preErrorSymbol; - preErrorSymbol = 0; - - // read action for current state and first input - t = (table[newState] && table[newState][symbol]) || NO_ACTION; - if (!t[0] || symbol === TERROR) { - // forget about that symbol and move forward: this wasn't a 'forgot to insert' error type where - // (simple) stuff might have been missing before the token which caused the error we're - // recovering from now... - // - // Also check if the LookAhead symbol isn't the ERROR token we set as part of the error - // recovery, for then this we would we idling (cycling) on the error forever. - // Yes, this does not take into account the possibility that the *lexer* may have - // produced a *new* TERROR token all by itself, but that would be a very peculiar grammar! - - symbol = 0; - } - } - - continue; - - // reduce: - case 2: - //this.reductionCount++; - this_production = this.productions_[newState - 1]; // `this.productions_[]` is zero-based indexed while states start from 1 upwards... - len = this_production[1]; - lstack_end = sp; - lstack_begin = lstack_end - (len || 1); - lstack_end--; - - - - // Make sure subsequent `$$ = $1` default action doesn't fail - // for rules where len==0 as then there's no $1 (you're reducing an epsilon rule then!) - // - // Also do this to prevent nasty action block codes to *read* `$0` or `$$` - // and *not* get `undefined` as a result for their efforts! - vstack[sp] = undefined; - - // perform semantic action - yyval.$ = vstack[sp - len]; // default to $$ = $1; result must produce `undefined` when len == 0, as then there's no $1 - - // default location, uses first token for firsts, last for lasts - yyval._$ = { - first_line: lstack[lstack_begin].first_line, - last_line: lstack[lstack_end].last_line, - first_column: lstack[lstack_begin].first_column, - last_column: lstack[lstack_end].last_column - }; - if (ranges) { - yyval._$.range = [lstack[lstack_begin].range[0], lstack[lstack_end].range[1]]; - } - - r = this.performAction.call(yyval, yyloc, newState, sp - 1, vstack, lstack); - - if (typeof r !== 'undefined') { - retval = r; - break; - } - - // pop off stack - sp -= len; - - // don't overwrite the `symbol` variable: use a local var to speed things up: - var ntsymbol = this_production[0]; // push nonterminal (reduce) - stack[sp] = ntsymbol; - vstack[sp] = yyval.$; - lstack[sp] = yyval._$; - // goto new state = table[STATE][NONTERMINAL] - newState = table[sstack[sp - 1]][ntsymbol]; - sstack[sp] = newState; - ++sp; - - continue; - - // accept: - case 3: - retval = true; - // Return the `$accept` rule's `$$` result, if available. - // - // Also note that JISON always adds this top-most `$accept` rule (with implicit, - // default, action): - // - // $accept: $end - // %{ $$ = $1; @$ = @1; %} - // - // which, combined with the parse kernel's `$accept` state behaviour coded below, - // will produce the `$$` value output of the rule as the parse result, - // IFF that result is *not* `undefined`. (See also the parser kernel code.) - // - // In code: - // - // %{ - // @$ = @1; // if location tracking support is included - // if (typeof $1 !== 'undefined') - // return $1; - // else - // return true; // the default parse result if the rule actions don't produce anything - // %} - if (typeof yyval.$ !== 'undefined') { - retval = yyval.$; - } - break; - } - - // break out of loop: we accept or fail with error - break; - } - } catch (ex) { - // report exceptions through the parseError callback too, but keep the exception intact - // if it is a known parser or lexer error which has been thrown by parseError() already: - if (ex instanceof this.JisonParserError) { - throw ex; - } - else if (lexer && typeof lexer.JisonLexerError === 'function' && ex instanceof lexer.JisonLexerError) { - throw ex; - } - else { - p = this.constructParseErrorInfo('Parsing aborted due to exception.', ex, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - } - } finally { - retval = this.cleanupAfterParse(retval, true, true); - this.__reentrant_call_depth--; - } - - return retval; -}, -yyError: 1 -}; -parser.originalParseError = parser.parseError; -parser.originalQuoteName = parser.quoteName; - -var fs = require('fs'); -var transform = require('./ebnf-transform').transform; -var ebnf = false; -var XRegExp = require('xregexp'); // for helping out the `%options xregexp` in the lexer - - -// transform ebnf to bnf if necessary -function extend(json, grammar) { - json.bnf = ebnf ? transform(grammar.grammar) : grammar.grammar; - if (grammar.actionInclude) { - json.actionInclude = grammar.actionInclude; - } - return json; -} - -// convert string value to number or boolean value, when possible -// (and when this is more or less obviously the intent) -// otherwise produce the string itself as value. -function parseValue(v) { - if (v === 'false') { - return false; - } - if (v === 'true') { - return true; - } - // http://stackoverflow.com/questions/175739/is-there-a-built-in-way-in-javascript-to-check-if-a-string-is-a-valid-number - // Note that the `v` check ensures that we do not convert `undefined`, `null` and `''` (empty string!) - if (v && !isNaN(v)) { - var rv = +v; - if (isFinite(rv)) { - return rv; - } - } - return v; -} -/* lexer generated by jison-lex 0.3.4-166 */ -/* - * Returns a Lexer object of the following structure: - * - * Lexer: { - * yy: {} The so-called "shared state" or rather the *source* of it; - * the real "shared state" `yy` passed around to - * the rule actions, etc. is a derivative/copy of this one, - * not a direct reference! - * } - * - * Lexer.prototype: { - * yy: {}, - * EOF: 1, - * ERROR: 2, - * - * JisonLexerError: function(msg, hash), - * - * performAction: function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * where `...` denotes the (optional) additional arguments the user passed to - * `lexer.lex(...)` and specified by way of `%parse-param ...` in the **parser** grammar file - * - * The function parameters and `this` have the following value/meaning: - * - `this` : reference to the `lexer` instance. - * - * - `yy` : a reference to the `yy` "shared state" object which was passed to the lexer - * by way of the `lexer.setInput(str, yy)` API before. - * - * - `yy_` : lexer instance reference used internally. - * - * - `$avoiding_name_collisions` : index of the matched lexer rule (regex), used internally. - * - * - `YY_START`: the current lexer "start condition" state. - * - * - `...` : the extra arguments you specified in the `%parse-param` statement in your - * **parser** grammar definition file and which are passed to the lexer via - * its `lexer.lex(...)` API. - * - * parseError: function(str, hash, ExceptionClass), - * - * constructLexErrorInfo: function(error_message, is_recoverable), - * Helper function. - * Produces a new errorInfo 'hash object' which can be passed into `parseError()`. - * See it's use in this lexer kernel in many places; example usage: - * - * var infoObj = lexer.constructParseErrorInfo('fail!', true); - * var retVal = lexer.parseError(infoObj.errStr, infoObj, lexer.JisonLexerError); - * - * options: { ... lexer %options ... }, - * - * lex: function([args...]), - * Produce one token of lexed input, which was passed in earlier via the `lexer.setInput()` API. - * You MAY use the additional `args...` parameters as per `%parse-param` spec of the **parser** grammar: - * these extra `args...` are passed verbatim to the lexer rules' action code. - * - * cleanupAfterLex: function(do_not_nuke_errorinfos), - * Helper function. - * This helper API is invoked when the parse process has completed. This helper may - * be invoked by user code to ensure the internal lexer gets properly garbage collected. - * - * setInput: function(input, [yy]), - * input: function(), - * unput: function(str), - * more: function(), - * reject: function(), - * less: function(n), - * pastInput: function(n), - * upcomingInput: function(n), - * showPosition: function(), - * test_match: function(regex_match_array, rule_index), - * next: function(...), - * lex: function(...), - * begin: function(condition), - * pushState: function(condition), - * popState: function(), - * topState: function(), - * _currentRules: function(), - * stateStackSize: function(), - * - * options: { ... lexer %options ... }, - * - * performAction: function(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * rules: [...], - * conditions: {associative list: name ==> set}, - * } - * - * - * token location info (`yylloc`): { - * first_line: n, - * last_line: n, - * first_column: n, - * last_column: n, - * range: [start_number, end_number] - * (where the numbers are indexes into the input string, zero-based) - * } - * - * --- - * - * The `parseError` function receives a 'hash' object with these members for lexer errors: - * - * { - * text: (matched text) - * token: (the produced terminal token, if any) - * token_id: (the produced terminal token numeric ID, if any) - * line: (yylineno) - * loc: (yylloc) - * recoverable: (boolean: TRUE when the parser MAY have an error recovery rule - * available for this particular error) - * yy: (object: the current parser internal "shared state" `yy` - * as is also available in the rule actions; this can be used, - * for instance, for advanced error analysis and reporting) - * lexer: (reference to the current lexer instance used by the parser) - * } - * - * while `this` will reference the current lexer instance. - * - * When `parseError` is invoked by the lexer, the default implementation will - * attempt to invoke `yy.parser.parseError()`; when this callback is not provided - * it will try to invoke `yy.parseError()` instead. When that callback is also not - * provided, a `JisonLexerError` exception will be thrown containing the error - * message and `hash`, as constructed by the `constructLexErrorInfo()` API. - * - * Note that the lexer's `JisonLexerError` error class is passed via the - * `ExceptionClass` argument, which is invoked to construct the exception - * instance to be thrown, so technically `parseError` will throw the object - * produced by the `new ExceptionClass(str, hash)` JavaScript expression. - * - * --- - * - * You can specify lexer options by setting / modifying the `.options` object of your Lexer instance. - * These options are available: - * - * (Options are permanent.) - * - * yy: { - * parseError: function(str, hash, ExceptionClass) - * optional: overrides the default `parseError` function. - * } - * - * lexer.options: { - * pre_lex: function() - * optional: is invoked before the lexer is invoked to produce another token. - * `this` refers to the Lexer object. - * post_lex: function(token) { return token; } - * optional: is invoked when the lexer has produced a token `token`; - * this function can override the returned token value by returning another. - * When it does not return any (truthy) value, the lexer will return - * the original `token`. - * `this` refers to the Lexer object. - * - * WARNING: the next set of options are not meant to be changed. They echo the abilities of - * the lexer as per when it was compiled! - * - * ranges: boolean - * optional: `true` ==> token location info will include a .range[] member. - * flex: boolean - * optional: `true` ==> flex-like lexing behaviour where the rules are tested - * exhaustively to find the longest match. - * backtrack_lexer: boolean - * optional: `true` ==> lexer regexes are tested in order and for invoked; - * the lexer terminates the scan when a token is returned by the action code. - * xregexp: boolean - * optional: `true` ==> lexer rule regexes are "extended regex format" requiring the - * `XRegExp` library. When this %option has not been specified at compile time, all lexer - * rule regexes have been written as standard JavaScript RegExp expressions. - * } - */ - - -var lexer = (function () { -// See also: -// http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508 -// but we keep the prototype.constructor and prototype.name assignment lines too for compatibility -// with userland code which might access the derived class in a 'classic' way. -function JisonLexerError(msg, hash) { - Object.defineProperty(this, 'name', { - enumerable: false, - writable: false, - value: 'JisonLexerError' - }); - - if (msg == null) msg = '???'; - - Object.defineProperty(this, 'message', { - enumerable: false, - writable: true, - value: msg - }); - - this.hash = hash; - - var stacktrace; - if (hash && hash.exception instanceof Error) { - var ex2 = hash.exception; - this.message = ex2.message || msg; - stacktrace = ex2.stack; - } - if (!stacktrace) { - if (Error.hasOwnProperty('captureStackTrace')) { // V8 - Error.captureStackTrace(this, this.constructor); - } else { - stacktrace = (new Error(msg)).stack; - } - } - if (stacktrace) { - Object.defineProperty(this, 'stack', { - enumerable: false, - writable: false, - value: stacktrace - }); - } -} - -if (typeof Object.setPrototypeOf === 'function') { - Object.setPrototypeOf(JisonLexerError.prototype, Error.prototype); -} else { - JisonLexerError.prototype = Object.create(Error.prototype); -} -JisonLexerError.prototype.constructor = JisonLexerError; -JisonLexerError.prototype.name = 'JisonLexerError'; - - - - -var lexer = { - - // Code Generator Information Report - // --------------------------------- - // - // Options: - // - // backtracking: .................... false - // location.ranges: ................. true - // location line+column tracking: ... true - // - // - // Forwarded Parser Analysis flags: - // - // uses yyleng: ..................... false - // uses yylineno: ................... false - // uses yytext: ..................... false - // uses yylloc: ..................... false - // uses lexer values: ............... true / true - // location tracking: ............... true - // location assignment: ............. false - // - // - // Lexer Analysis flags: - // - // uses yyleng: ..................... undefined - // uses yylineno: ................... undefined - // uses yytext: ..................... undefined - // uses yylloc: ..................... undefined - // uses ParseError API: ............. undefined - // uses location tracking & editing: undefined - // uses more() API: ................. undefined - // uses unput() API: ................ undefined - // uses reject() API: ............... undefined - // uses less() API: ................. undefined - // uses display APIs pastInput(), upcomingInput(), showPosition(): - // ............................. undefined - // uses describeYYLLOC() API: ....... undefined - // - // --------- END OF REPORT ----------- - - - EOF: 1, - ERROR: 2, - - // JisonLexerError: JisonLexerError, /// <-- injected by the code generator - - // options: {}, /// <-- injected by the code generator - - // yy: ..., /// <-- injected by setInput() - - __currentRuleSet__: null, /// <-- internal rule set cache for the current lexer state - - __error_infos: [], /// INTERNAL USE ONLY: the set of lexErrorInfo objects created since the last cleanup - - __decompressed: false, /// INTERNAL USE ONLY: mark whether the lexer instance has been 'unfolded' completely and is now ready for use - - done: false, /// INTERNAL USE ONLY - _backtrack: false, /// INTERNAL USE ONLY - _input: '', /// INTERNAL USE ONLY - _more: false, /// INTERNAL USE ONLY - _signaled_error_token: false, /// INTERNAL USE ONLY - - conditionStack: [], /// INTERNAL USE ONLY; managed via `pushState()`, `popState()`, `topState()` and `stateStackSize()` - - match: '', /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks input which has been matched so far for the lexer token under construction. `match` is identical to `yytext` except that this one still contains the matched input string after `lexer.performAction()` has been invoked, where userland code MAY have changed/replaced the `yytext` value entirely! - matched: '', /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks entire input which has been matched so far - matches: false, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks RE match result for last (successful) match attempt - yytext: '', /// ADVANCED USE ONLY: tracks input which has been matched so far for the lexer token under construction; this value is transferred to the parser as the 'token value' when the parser consumes the lexer token produced through a call to the `lex()` API. - offset: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks the 'cursor position' in the input string, i.e. the number of characters matched so far - yyleng: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: length of matched input for the token under construction (`yytext`) - yylineno: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: 'line number' at which the token under construction is located - yylloc: null, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks location info (lines + columns) for the token under construction - - /** - INTERNAL USE: construct a suitable error info hash object instance for `parseError`. - - @public - @this {RegExpLexer} - */ - constructLexErrorInfo: function lexer_constructLexErrorInfo(msg, recoverable) { - /** @constructor */ - var pei = { - errStr: msg, - recoverable: !!recoverable, - text: this.match, // This one MAY be empty; userland code should use the `upcomingInput` API to obtain more text which follows the 'lexer cursor position'... - token: null, - line: this.yylineno, - loc: this.yylloc, - yy: this.yy, - lexer: this, - - /** - and make sure the error info doesn't stay due to potential - ref cycle via userland code manipulations. - These would otherwise all be memory leak opportunities! - - Note that only array and object references are nuked as those - constitute the set of elements which can produce a cyclic ref. - The rest of the members is kept intact as they are harmless. - - @public - @this {LexErrorInfo} - */ - destroy: function destructLexErrorInfo() { - // remove cyclic references added to error info: - // info.yy = null; - // info.lexer = null; - // ... - var rec = !!this.recoverable; - for (var key in this) { - if (this.hasOwnProperty(key) && typeof key === 'object') { - this[key] = undefined; - } - } - this.recoverable = rec; - } - }; - // track this instance so we can `destroy()` it once we deem it superfluous and ready for garbage collection! - this.__error_infos.push(pei); - return pei; - }, - - /** - handler which is invoked when a lexer error occurs. - - @public - @this {RegExpLexer} - */ - parseError: function lexer_parseError(str, hash, ExceptionClass) { - if (!ExceptionClass) { - ExceptionClass = this.JisonLexerError; - } - if (this.yy.parser && typeof this.yy.parser.parseError === 'function') { - return this.yy.parser.parseError(str, hash, ExceptionClass) || this.ERROR; - } else if (typeof this.yy.parseError === 'function') { - return this.yy.parseError(str, hash, ExceptionClass) || this.ERROR; - } else { - throw new ExceptionClass(str, hash); - } - }, - - /** - method which implements `yyerror(str, ...args)` functionality for use inside lexer actions. - - @public - @this {RegExpLexer} - */ - yyerror: function yyError(str /*, ...args */) { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': ' + str, this.options.lexerErrorsAreRecoverable); - - // Add any extra args to the hash under the name `extra_error_attributes`: - var args = Array.prototype.slice.call(arguments, 1); - if (args.length) { - hash.extra_error_attributes = args; - } - - return (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - }, - - /** - final cleanup function for when we have completed lexing the input; - make it an API so that external code can use this one once userland - code has decided it's time to destroy any lingering lexer error - hash object instances and the like: this function helps to clean - up these constructs, which *may* carry cyclic references which would - otherwise prevent the instances from being properly and timely - garbage-collected, i.e. this function helps prevent memory leaks! - - @public - @this {RegExpLexer} - */ - cleanupAfterLex: function lexer_cleanupAfterLex(do_not_nuke_errorinfos) { - var rv; - - // prevent lingering circular references from causing memory leaks: - this.setInput('', {}); - - // nuke the error hash info instances created during this run. - // Userland code must COPY any data/references - // in the error hash instance(s) it is more permanently interested in. - if (!do_not_nuke_errorinfos) { - for (var i = this.__error_infos.length - 1; i >= 0; i--) { - var el = this.__error_infos[i]; - if (el && typeof el.destroy === 'function') { - el.destroy(); - } - } - this.__error_infos.length = 0; - } - - return this; - }, - - /** - clear the lexer token context; intended for internal use only - - @public - @this {RegExpLexer} - */ - clear: function lexer_clear() { - this.yytext = ''; - this.yyleng = 0; - this.match = ''; - this.matches = false; - this._more = false; - this._backtrack = false; - - var col = this.yylloc ? this.yylloc.last_column : 0; - this.yylloc = { - first_line: this.yylineno + 1, - first_column: col, - last_line: this.yylineno + 1, - last_column: col, - - range: (this.options.ranges ? [this.offset, this.offset] : undefined) - }; - }, - - /** - resets the lexer, sets new input - - @public - @this {RegExpLexer} - */ - setInput: function lexer_setInput(input, yy) { - this.yy = yy || this.yy || {}; - - // also check if we've fully initialized the lexer instance, - // including expansion work to be done to go from a loaded - // lexer to a usable lexer: - if (!this.__decompressed) { - // step 1: decompress the regex list: - var rules = this.rules; - for (var i = 0, len = rules.length; i < len; i++) { - var rule_re = rules[i]; - - // compression: is the RE an xref to another RE slot in the rules[] table? - if (typeof rule_re === 'number') { - rules[i] = rules[rule_re]; - } - } - - // step 2: unfold the conditions[] set to make these ready for use: - var conditions = this.conditions; - for (var k in conditions) { - var spec = conditions[k]; - - var rule_ids = spec.rules; - - var len = rule_ids.length; - var rule_regexes = new Array(len + 1); // slot 0 is unused; we use a 1-based index approach here to keep the hottest code in `lexer_next()` fast and simple! - var rule_new_ids = new Array(len + 1); - - for (var i = 0; i < len; i++) { - var idx = rule_ids[i]; - var rule_re = rules[idx]; - rule_regexes[i + 1] = rule_re; - rule_new_ids[i + 1] = idx; - } - - spec.rules = rule_new_ids; - spec.__rule_regexes = rule_regexes; - spec.__rule_count = len; - } - - this.__decompressed = true; - } - - this._input = input || ''; - this.clear(); - this._signaled_error_token = false; - this.done = false; - this.yylineno = 0; - this.matched = ''; - this.conditionStack = ['INITIAL']; - this.__currentRuleSet__ = null; - this.yylloc = { - first_line: 1, - first_column: 0, - last_line: 1, - last_column: 0, - - range: (this.options.ranges ? [0, 0] : undefined) - }; - this.offset = 0; - return this; - }, - - /** - push a new input into the lexer and activate it: - the old input position is stored and will be resumed - once this new input has been consumed. - - Use this API to help implement C-preprocessor-like - `#include` statements. - - Available options: - - - `emit_EOF_at_end` : {int} the `EOF`-like token to emit - when the new input is consumed: use - this to mark the end of the new input - in the parser grammar. zero/falsey - token value means no end marker token - will be emitted before the lexer - resumes reading from the previous input. - - @public - @this {RegExpLexer} - */ - pushInput: function lexer_pushInput(input, label, options) { - options = options || {}; - - this._input = input || ''; - this.clear(); - // this._signaled_error_token = false; - this.done = false; - this.yylineno = 0; - this.matched = ''; - // this.conditionStack = ['INITIAL']; - // this.__currentRuleSet__ = null; - this.yylloc = { - first_line: 1, - first_column: 0, - last_line: 1, - last_column: 0, - - range: (this.options.ranges ? [0, 0] : undefined) - }; - this.offset = 0; - return this; - }, - - /** - consumes and returns one char from the input - - @public - @this {RegExpLexer} - */ - input: function lexer_input() { - if (!this._input) { - //this.done = true; -- don't set `done` as we want the lex()/next() API to be able to produce one custom EOF token match after this anyhow. (lexer can match special <> tokens and perform user action code for a <> match, but only does so *once*) - return null; - } - var ch = this._input[0]; - this.yytext += ch; - this.yyleng++; - this.offset++; - this.match += ch; - this.matched += ch; - // Count the linenumber up when we hit the LF (or a stand-alone CR). - // On CRLF, the linenumber is incremented when you fetch the CR or the CRLF combo - // and we advance immediately past the LF as well, returning both together as if - // it was all a single 'character' only. - var slice_len = 1; - var lines = false; - if (ch === '\n') { - lines = true; - } else if (ch === '\r') { - lines = true; - var ch2 = this._input[1]; - if (ch2 === '\n') { - slice_len++; - ch += ch2; - this.yytext += ch2; - this.yyleng++; - this.offset++; - this.match += ch2; - this.matched += ch2; - if (this.options.ranges) { - this.yylloc.range[1]++; - } - } - } - if (lines) { - this.yylineno++; - this.yylloc.last_line++; - this.yylloc.last_column = 0; - } else { - this.yylloc.last_column++; - } - if (this.options.ranges) { - this.yylloc.range[1]++; - } - - this._input = this._input.slice(slice_len); - return ch; - }, - - /** - unshifts one char (or an entire string) into the input - - @public - @this {RegExpLexer} - */ - unput: function lexer_unput(ch) { - var len = ch.length; - var lines = ch.split(/(?:\r\n?|\n)/g); - - this._input = ch + this._input; - this.yytext = this.yytext.substr(0, this.yytext.length - len); - this.yyleng = this.yytext.length; - this.offset -= len; - this.match = this.match.substr(0, this.match.length - len); - this.matched = this.matched.substr(0, this.matched.length - len); - - if (lines.length > 1) { - this.yylineno -= lines.length - 1; - - this.yylloc.last_line = this.yylineno + 1; - var pre = this.match; - var pre_lines = pre.split(/(?:\r\n?|\n)/g); - if (pre_lines.length === 1) { - pre = this.matched; - pre_lines = pre.split(/(?:\r\n?|\n)/g); - } - this.yylloc.last_column = pre_lines[pre_lines.length - 1].length; - } else { - this.yylloc.last_column -= len; - } - - if (this.options.ranges) { - this.yylloc.range[1] = this.yylloc.range[0] + this.yyleng; - } - this.done = false; - return this; - }, - - /** - cache matched text and append it on next action - - @public - @this {RegExpLexer} - */ - more: function lexer_more() { - this._more = true; - return this; - }, - - /** - signal the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead. - - @public - @this {RegExpLexer} - */ - reject: function lexer_reject() { - if (this.options.backtrack_lexer) { - this._backtrack = true; - } else { - // when the `parseError()` call returns, we MUST ensure that the error is registered. - // We accomplish this by signaling an 'error' token to be produced for the current - // `.lex()` run. - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).' + pos_str, false); - this._signaled_error_token = (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - } - return this; - }, - - /** - retain first n characters of the match - - @public - @this {RegExpLexer} - */ - less: function lexer_less(n) { - return this.unput(this.match.slice(n)); - }, - - /** - return (part of the) already matched input, i.e. for error messages. - - Limit the returned string length to `maxSize` (default: 20). - - Limit the returned string to the `maxLines` number of lines of input (default: 1). - - Negative limit values equal *unlimited*. - - @public - @this {RegExpLexer} - */ - pastInput: function lexer_pastInput(maxSize, maxLines) { - var past = this.matched.substring(0, this.matched.length - this.match.length); - if (maxSize < 0) - maxSize = past.length; - else if (!maxSize) - maxSize = 20; - if (maxLines < 0) - maxLines = past.length; // can't ever have more input lines than this! - else if (!maxLines) - maxLines = 1; - // `substr` anticipation: treat \r\n as a single character and take a little - // more than necessary so that we can still properly check against maxSize - // after we've transformed and limited the newLines in here: - past = past.substr(-maxSize * 2 - 2); - // now that we have a significantly reduced string to process, transform the newlines - // and chop them, then limit them: - var a = past.replace(/\r\n|\r/g, '\n').split('\n'); - a = a.slice(-maxLines); - past = a.join('\n'); - // When, after limiting to maxLines, we still have too much to return, - // do add an ellipsis prefix... - if (past.length > maxSize) { - past = '...' + past.substr(-maxSize); - } - return past; - }, - - /** - return (part of the) upcoming input, i.e. for error messages. - - Limit the returned string length to `maxSize` (default: 20). - - Limit the returned string to the `maxLines` number of lines of input (default: 1). - - Negative limit values equal *unlimited*. - - @public - @this {RegExpLexer} - */ - upcomingInput: function lexer_upcomingInput(maxSize, maxLines) { - var next = this.match; - if (maxSize < 0) - maxSize = next.length + this._input.length; - else if (!maxSize) - maxSize = 20; - if (maxLines < 0) - maxLines = maxSize; // can't ever have more input lines than this! - else if (!maxLines) - maxLines = 1; - // `substring` anticipation: treat \r\n as a single character and take a little - // more than necessary so that we can still properly check against maxSize - // after we've transformed and limited the newLines in here: - if (next.length < maxSize * 2 + 2) { - next += this._input.substring(0, maxSize * 2 + 2); // substring is faster on Chrome/V8 - } - // now that we have a significantly reduced string to process, transform the newlines - // and chop them, then limit them: - var a = next.replace(/\r\n|\r/g, '\n').split('\n'); - a = a.slice(0, maxLines); - next = a.join('\n'); - // When, after limiting to maxLines, we still have too much to return, - // do add an ellipsis postfix... - if (next.length > maxSize) { - next = next.substring(0, maxSize) + '...'; - } - return next; - }, - - /** - return a string which displays the character position where the lexing error occurred, i.e. for error messages - - @public - @this {RegExpLexer} - */ - showPosition: function lexer_showPosition(maxPrefix, maxPostfix) { - var pre = this.pastInput(maxPrefix).replace(/\s/g, ' '); - var c = new Array(pre.length + 1).join('-'); - return pre + this.upcomingInput(maxPostfix).replace(/\s/g, ' ') + '\n' + c + '^'; - }, - - /** - helper function, used to produce a human readable description as a string, given - the input `yylloc` location object. - - Set `display_range_too` to TRUE to include the string character index position(s) - in the description if the `yylloc.range` is available. - - @public - @this {RegExpLexer} - */ - describeYYLLOC: function lexer_describe_yylloc(yylloc, display_range_too) { - var l1 = yylloc.first_line; - var l2 = yylloc.last_line; - var o1 = yylloc.first_column; - var o2 = yylloc.last_column; - var dl = l2 - l1; - var d_o = o2 - o1; - var rv; - if (dl === 0) { - rv = 'line ' + l1 + ', '; - if (d_o === 1) { - rv += 'column ' + o1; - } else { - rv += 'columns ' + o1 + ' .. ' + o2; - } - } else { - rv = 'lines ' + l1 + '(column ' + o1 + ') .. ' + l2 + '(column ' + o2 + ')'; - } - if (yylloc.range && display_range_too) { - var r1 = yylloc.range[0]; - var r2 = yylloc.range[1] - 1; - if (r2 === r1) { - rv += ' {String Offset: ' + r1 + '}'; - } else { - rv += ' {String Offset range: ' + r1 + ' .. ' + r2 + '}'; - } - } - return rv; - // return JSON.stringify(yylloc); - }, - - /** - test the lexed token: return FALSE when not a match, otherwise return token. - - `match` is supposed to be an array coming out of a regex match, i.e. `match[0]` - contains the actually matched text string. - - Also move the input cursor forward and update the match collectors: - - - `yytext` - - `yyleng` - - `match` - - `matches` - - `yylloc` - - `offset` - - @public - @this {RegExpLexer} - */ - test_match: function lexer_test_match(match, indexed_rule) { - var token, - lines, - backup, - match_str, - match_str_len; - - if (this.options.backtrack_lexer) { - // save context - backup = { - yylineno: this.yylineno, - yylloc: { - first_line: this.yylloc.first_line, - last_line: this.yylloc.last_line, - first_column: this.yylloc.first_column, - last_column: this.yylloc.last_column, - - range: (this.options.ranges ? this.yylloc.range.slice(0) : undefined) - }, - yytext: this.yytext, - match: this.match, - matches: this.matches, - matched: this.matched, - yyleng: this.yyleng, - offset: this.offset, - _more: this._more, - _input: this._input, - //_signaled_error_token: this._signaled_error_token, - yy: this.yy, - conditionStack: this.conditionStack.slice(0), - done: this.done - }; - } - - match_str = match[0]; - match_str_len = match_str.length; - // if (match_str.indexOf('\n') !== -1 || match_str.indexOf('\r') !== -1) { - lines = match_str.split(/(?:\r\n?|\n)/g); - if (lines.length > 1) { - this.yylineno += lines.length - 1; - - this.yylloc.last_line = this.yylineno + 1, - this.yylloc.last_column = lines[lines.length - 1].length; - } else { - this.yylloc.last_column += match_str_len; - } - // } - this.yytext += match_str; - this.match += match_str; - this.matches = match; - this.yyleng = this.yytext.length; - if (this.options.ranges) { - this.yylloc.range[1] += match_str_len; - } - // previous lex rules MAY have invoked the `more()` API rather than producing a token: - // those rules will already have moved this `offset` forward matching their match lengths, - // hence we must only add our own match length now: - this.offset += match_str_len; - this._more = false; - this._backtrack = false; - this._input = this._input.slice(match_str_len); - this.matched += match_str; - - // calling this method: - // - // function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START) {...} - token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1] /* = YY_START */); - // otherwise, when the action codes are all simple return token statements: - //token = this.simpleCaseActionClusters[indexed_rule]; - - if (this.done && this._input) { - this.done = false; - } - if (token) { - return token; - } else if (this._backtrack) { - // recover context - for (var k in backup) { - this[k] = backup[k]; - } - this.__currentRuleSet__ = null; - return false; // rule action called reject() implying the next rule should be tested instead. - } else if (this._signaled_error_token) { - // produce one 'error' token as `.parseError()` in `reject()` did not guarantee a failure signal by throwing an exception! - token = this._signaled_error_token; - this._signaled_error_token = false; - return token; - } - return false; - }, - - /** - return next match in input - - @public - @this {RegExpLexer} - */ - next: function lexer_next() { - if (this.done) { - this.clear(); - return this.EOF; - } - if (!this._input) { - this.done = true; - } - - var token, - match, - tempMatch, - index; - if (!this._more) { - this.clear(); - } - var spec = this.__currentRuleSet__; - if (!spec) { - // Update the ruleset cache as we apparently encountered a state change or just started lexing. - // The cache is set up for fast lookup -- we assume a lexer will switch states much less often than it will - // invoke the `lex()` token-producing API and related APIs, hence caching the set for direct access helps - // speed up those activities a tiny bit. - spec = this.__currentRuleSet__ = this._currentRules(); - // Check whether a *sane* condition has been pushed before: this makes the lexer robust against - // user-programmer bugs such as https://github.com/zaach/jison-lex/issues/19 - if (!spec || !spec.rules) { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Internal lexer engine error' + lineno_msg + ': The lex grammar programmer pushed a non-existing condition name "' + this.topState() + '"; this is a fatal error and should be reported to the application programmer team!' + pos_str, false); - // produce one 'error' token until this situation has been resolved, most probably by parse termination! - return (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - } - } - - var rule_ids = spec.rules; - //var dispatch = spec.__dispatch_lut; - var regexes = spec.__rule_regexes; - var len = spec.__rule_count; - - // Note: the arrays are 1-based, while `len` itself is a valid index, - // hence the non-standard less-or-equal check in the next loop condition! - for (var i = 1; i <= len; i++) { - tempMatch = this._input.match(regexes[i]); - if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { - match = tempMatch; - index = i; - if (this.options.backtrack_lexer) { - token = this.test_match(tempMatch, rule_ids[i]); - if (token !== false) { - return token; - } else if (this._backtrack) { - match = undefined; - continue; // rule action called reject() implying a rule MISmatch. - } else { - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - } else if (!this.options.flex) { - break; - } - } - } - if (match) { - token = this.test_match(match, rule_ids[index]); - if (token !== false) { - return token; - } - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - if (!this._input) { - this.done = true; - this.clear(); - return this.EOF; - } else { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': Unrecognized text.' + pos_str, this.options.lexerErrorsAreRecoverable); - token = (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - if (token === this.ERROR) { - // we can try to recover from a lexer error that `parseError()` did not 'recover' for us - // by moving forward at least one character at a time: - if (!this.match.length) { - this.input(); - } - } - return token; - } - }, - - /** - return next match that has a token - - @public - @this {RegExpLexer} - */ - lex: function lexer_lex() { - var r; - // allow the PRE/POST handlers set/modify the return token for maximum flexibility of the generated lexer: - if (typeof this.options.pre_lex === 'function') { - r = this.options.pre_lex.call(this); - } - while (!r) { - r = this.next(); - } - if (typeof this.options.post_lex === 'function') { - // (also account for a userdef function which does not return any value: keep the token as is) - r = this.options.post_lex.call(this, r) || r; - } - return r; - }, - - /** - backwards compatible alias for `pushState()`; - the latter is symmetrical with `popState()` and we advise to use - those APIs in any modern lexer code, rather than `begin()`. - - @public - @this {RegExpLexer} - */ - begin: function lexer_begin(condition) { - return this.pushState(condition); - }, - - /** - activates a new lexer condition state (pushes the new lexer condition state onto the condition stack) - - @public - @this {RegExpLexer} - */ - pushState: function lexer_pushState(condition) { - this.conditionStack.push(condition); - this.__currentRuleSet__ = null; - return this; - }, - - /** - pop the previously active lexer condition state off the condition stack - - @public - @this {RegExpLexer} - */ - popState: function lexer_popState() { - var n = this.conditionStack.length - 1; - if (n > 0) { - this.__currentRuleSet__ = null; - return this.conditionStack.pop(); - } else { - return this.conditionStack[0]; - } - }, - - /** - return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available - - @public - @this {RegExpLexer} - */ - topState: function lexer_topState(n) { - n = this.conditionStack.length - 1 - Math.abs(n || 0); - if (n >= 0) { - return this.conditionStack[n]; - } else { - return 'INITIAL'; - } - }, - - /** - (internal) determine the lexer rule set which is active for the currently active lexer condition state - - @public - @this {RegExpLexer} - */ - _currentRules: function lexer__currentRules() { - if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) { - return this.conditions[this.conditionStack[this.conditionStack.length - 1]]; - } else { - return this.conditions['INITIAL']; - } - }, - - /** - return the number of states currently on the stack - - @public - @this {RegExpLexer} - */ - stateStackSize: function lexer_stateStackSize() { - return this.conditionStack.length; - }, - options: { - xregexp: true, - ranges: true, - trackPosition: true, - easy_keyword_rules: true -}, - JisonLexerError: JisonLexerError, - performAction: function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START) { - -var YYSTATE = YY_START; -switch($avoiding_name_collisions) { -case 0 : -/*! Conditions:: token */ -/*! Rule:: {BR} */ - this.popState(); -break; -case 1 : -/*! Conditions:: token */ -/*! Rule:: %% */ - this.popState(); -break; -case 2 : -/*! Conditions:: token */ -/*! Rule:: ; */ - this.popState(); -break; -case 3 : -/*! Conditions:: bnf ebnf */ -/*! Rule:: %% */ - this.pushState('code'); return 14; -break; -case 17 : -/*! Conditions:: options */ -/*! Rule:: "{DOUBLEQUOTED_STRING_CONTENT}" */ - yy_.yytext = this.matches[1]; return 28; // value is always a string type -break; -case 18 : -/*! Conditions:: options */ -/*! Rule:: '{QUOTED_STRING_CONTENT}' */ - yy_.yytext = this.matches[1]; return 28; // value is always a string type -break; -case 19 : -/*! Conditions:: INITIAL ebnf bnf token path options */ -/*! Rule:: \/\/[^\r\n]* */ - /* skip single-line comment */ -break; -case 20 : -/*! Conditions:: INITIAL ebnf bnf token path options */ -/*! Rule:: \/\*(.|\n|\r)*?\*\/ */ - /* skip multi-line comment */ -break; -case 22 : -/*! Conditions:: options */ -/*! Rule:: {BR}{WS}+(?=\S) */ - /* skip leading whitespace on the next line of input, when followed by more options */ -break; -case 23 : -/*! Conditions:: options */ -/*! Rule:: {BR} */ - this.popState(); return 26; -break; -case 24 : -/*! Conditions:: options */ -/*! Rule:: {WS}+ */ - /* skip whitespace */ -break; -case 25 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: {WS}+ */ - /* skip whitespace */ -break; -case 26 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: {BR}+ */ - /* skip newlines */ -break; -case 27 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: \[{ID}\] */ - yy_.yytext = this.matches[1]; return 38; -break; -case 31 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: "{DOUBLEQUOTED_STRING_CONTENT}" */ - yy_.yytext = this.matches[1]; return 24; -break; -case 32 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: '{QUOTED_STRING_CONTENT}' */ - yy_.yytext = this.matches[1]; return 24; -break; -case 37 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: %% */ - this.pushState(ebnf ? 'ebnf' : 'bnf'); return 14; -break; -case 38 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: %ebnf\b */ - if (!yy.options) { yy.options = {}; } ebnf = yy.options.ebnf = true; -break; -case 39 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: %debug\b */ - if (!yy.options) { yy.options = {}; } yy.options.debug = true; return 19; -break; -case 46 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: %token\b */ - this.pushState('token'); return 18; -break; -case 48 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: %options\b */ - this.pushState('options'); return 25; -break; -case 49 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: %lex{LEX_CONTENT}\/lex\b */ - - // remove the %lex../lex wrapper and return the pure lex section: - yy_.yytext = this.matches[1]; - return 17; - -break; -case 52 : -/*! Conditions:: INITIAL ebnf bnf code */ -/*! Rule:: %include\b */ - this.pushState('path'); return 42; -break; -case 53 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: %{NAME}([^\r\n]*) */ - - /* ignore unrecognized decl */ - console.warn('EBNF: ignoring unsupported parser option: ', yy_.yytext, ' while lexing in ', this.topState(), ' state'); - // this.pushState('options'); - yy_.yytext = [ - this.matches[1], // {NAME} - this.matches[2].trim() // optional value/parameters - ]; - return 20; - -break; -case 54 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: <{ID}> */ - yy_.yytext = this.matches[1]; return 35; -break; -case 55 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: \{\{[\w\W]*?\}\} */ - yy_.yytext = yy_.yytext.substr(2, yy_.yyleng - 4); return 15; -break; -case 56 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: %\{(?:.|\r|\n)*?%\} */ - yy_.yytext = yy_.yytext.substr(2, yy_.yyleng - 4); return 15; -break; -case 57 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: \{ */ - yy.depth = 0; this.pushState('action'); return 12; -break; -case 58 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: ->.* */ - yy_.yytext = yy_.yytext.substr(2, yy_.yyleng - 2).trim(); return 40; -break; -case 59 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: →.* */ - yy_.yytext = yy_.yytext.substr(1, yy_.yyleng - 1).trim(); return 40; -break; -case 60 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: {HEX_NUMBER} */ - yy_.yytext = parseInt(yy_.yytext, 16); return 36; -break; -case 61 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: {DECIMAL_NUMBER}(?![xX0-9a-fA-F]) */ - yy_.yytext = parseInt(yy_.yytext, 10); return 36; -break; -case 64 : -/*! Conditions:: action */ -/*! Rule:: \/[^ /]*?['"{}'][^ ]*?\/ */ - return 41; // regexp with braces or quotes (and no spaces) -break; -case 69 : -/*! Conditions:: action */ -/*! Rule:: \{ */ - yy.depth++; return 12; -break; -case 70 : -/*! Conditions:: action */ -/*! Rule:: \} */ - if (yy.depth === 0) { this.popState(); } else { yy.depth--; } return 13; -break; -case 72 : -/*! Conditions:: code */ -/*! Rule:: [^\r\n]+ */ - return 44; // the bit of CODE just before EOF... -break; -case 73 : -/*! Conditions:: path */ -/*! Rule:: {BR} */ - this.popState(); this.unput(yy_.yytext); -break; -case 74 : -/*! Conditions:: path */ -/*! Rule:: "{DOUBLEQUOTED_STRING_CONTENT}" */ - yy_.yytext = this.matches[1]; this.popState(); return 43; -break; -case 75 : -/*! Conditions:: path */ -/*! Rule:: '{QUOTED_STRING_CONTENT}' */ - yy_.yytext = this.matches[1]; this.popState(); return 43; -break; -case 76 : -/*! Conditions:: path */ -/*! Rule:: {WS}+ */ - // skip whitespace in the line -break; -case 77 : -/*! Conditions:: path */ -/*! Rule:: [^\s\r\n]+ */ - this.popState(); return 43; -break; -case 78 : -/*! Conditions:: * */ -/*! Rule:: . */ - - /* b0rk on bad characters */ - var l0 = Math.max(0, yy_.yylloc.last_column - yy_.yylloc.first_column); - var l2 = 39; - var l1 = Math.min(79 - 4 - l0 - l2, yy_.yylloc.first_column, 0); - var pos_str = this.showPosition(l1, l2); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n\n Offending input:\n' + indent(pos_str, 4); - } - yy_.yyerror('unsupported parser input: ' + dquote(yy_.yytext) + ' @ ' + this.describeYYLLOC(yy_.yylloc) + ' while lexing in ' + dquote(this.topState()) + ' state.' + pos_str); - -break; -default: - return this.simpleCaseActionClusters[$avoiding_name_collisions]; -} -}, - simpleCaseActionClusters: { - - /*! Conditions:: bnf ebnf */ - /*! Rule:: %empty\b */ - 4 : 37, - /*! Conditions:: bnf ebnf */ - /*! Rule:: %epsilon\b */ - 5 : 37, - /*! Conditions:: bnf ebnf */ - /*! Rule:: \u0190 */ - 6 : 37, - /*! Conditions:: bnf ebnf */ - /*! Rule:: \u025B */ - 7 : 37, - /*! Conditions:: bnf ebnf */ - /*! Rule:: \u03B5 */ - 8 : 37, - /*! Conditions:: bnf ebnf */ - /*! Rule:: \u03F5 */ - 9 : 37, - /*! Conditions:: ebnf */ - /*! Rule:: \( */ - 10 : 7, - /*! Conditions:: ebnf */ - /*! Rule:: \) */ - 11 : 8, - /*! Conditions:: ebnf */ - /*! Rule:: \* */ - 12 : 9, - /*! Conditions:: ebnf */ - /*! Rule:: \? */ - 13 : 10, - /*! Conditions:: ebnf */ - /*! Rule:: \+ */ - 14 : 11, - /*! Conditions:: options */ - /*! Rule:: {NAME} */ - 15 : 27, - /*! Conditions:: options */ - /*! Rule:: = */ - 16 : 3, - /*! Conditions:: options */ - /*! Rule:: [^\s\r\n]+ */ - 21 : 29, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: {ID} */ - 28 : 23, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: \$end\b */ - 29 : 23, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: \$eof\b */ - 30 : 23, - /*! Conditions:: token */ - /*! Rule:: [^\s\r\n]+ */ - 33 : 'TOKEN_WORD', - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: : */ - 34 : 4, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: ; */ - 35 : 5, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: \| */ - 36 : 6, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: %parser-type\b */ - 40 : 31, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: %prec\b */ - 41 : 39, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: %start\b */ - 42 : 16, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: %left\b */ - 43 : 32, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: %right\b */ - 44 : 33, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: %nonassoc\b */ - 45 : 34, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: %parse-param\b */ - 47 : 30, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: %code\b */ - 50 : 22, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: %import\b */ - 51 : 21, - /*! Conditions:: action */ - /*! Rule:: \/\*(.|\n|\r)*?\*\/ */ - 62 : 41, - /*! Conditions:: action */ - /*! Rule:: \/\/[^\r\n]* */ - 63 : 41, - /*! Conditions:: action */ - /*! Rule:: "{DOUBLEQUOTED_STRING_CONTENT}" */ - 65 : 41, - /*! Conditions:: action */ - /*! Rule:: '{QUOTED_STRING_CONTENT}' */ - 66 : 41, - /*! Conditions:: action */ - /*! Rule:: [/"'][^{}/"']+ */ - 67 : 41, - /*! Conditions:: action */ - /*! Rule:: [^{}/"']+ */ - 68 : 41, - /*! Conditions:: code */ - /*! Rule:: [^\r\n]*(\r|\n)+ */ - 71 : 44, - /*! Conditions:: * */ - /*! Rule:: $ */ - 79 : 1 -}, - rules: [ - /^(?:(\r\n|\n|\r))/, -/^(?:%%)/, -/^(?:;)/, -/^(?:%%)/, -/^(?:%empty\b)/, -/^(?:%epsilon\b)/, -/^(?:\u0190)/, -/^(?:\u025B)/, -/^(?:\u03B5)/, -/^(?:\u03F5)/, -/^(?:\()/, -/^(?:\))/, -/^(?:\*)/, -/^(?:\?)/, -/^(?:\+)/, -new XRegExp("^(?:([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}\\-_]*(?:[\\p{Alphabetic}\\p{Number}_]))?))", ""), -/^(?:=)/, -/^(?:"((?:\\"|\\[^"]|[^"\\])*)")/, -/^(?:'((?:\\'|\\[^']|[^'\\])*)')/, -/^(?:\/\/[^\r\n]*)/, -/^(?:\/\*(.|\n|\r)*?\*\/)/, -/^(?:\S+)/, -/^(?:(\r\n|\n|\r)([^\S\n\r])+(?=\S))/, -/^(?:(\r\n|\n|\r))/, -/^(?:([^\S\n\r])+)/, -/^(?:([^\S\n\r])+)/, -/^(?:(\r\n|\n|\r)+)/, -new XRegExp("^(?:\\[([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}_])*)\\])", ""), -new XRegExp("^(?:([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}_])*))", ""), -/^(?:\$end\b)/, -/^(?:\$eof\b)/, -/^(?:"((?:\\"|\\[^"]|[^"\\])*)")/, -/^(?:'((?:\\'|\\[^']|[^'\\])*)')/, -/^(?:\S+)/, -/^(?::)/, -/^(?:;)/, -/^(?:\|)/, -/^(?:%%)/, -/^(?:%ebnf\b)/, -/^(?:%debug\b)/, -/^(?:%parser-type\b)/, -/^(?:%prec\b)/, -/^(?:%start\b)/, -/^(?:%left\b)/, -/^(?:%right\b)/, -/^(?:%nonassoc\b)/, -/^(?:%token\b)/, -/^(?:%parse-param\b)/, -/^(?:%options\b)/, -/^(?:%lex((?:[^\S\n\r])*(?:(?:\r\n|\n|\r)[\S\s]*?)?(?:\r\n|\n|\r)(?:[^\S\n\r])*)\/lex\b)/, -/^(?:%code\b)/, -/^(?:%import\b)/, -/^(?:%include\b)/, -new XRegExp("^(?:%([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}\\-_]*(?:[\\p{Alphabetic}\\p{Number}_]))?)([^\\n\\r]*))", ""), -new XRegExp("^(?:<([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}_])*)>)", ""), -/^(?:\{\{[\w\W]*?\}\})/, -/^(?:%\{(?:.|\r|\n)*?%\})/, -/^(?:\{)/, -/^(?:->.*)/, -/^(?:→.*)/, -/^(?:(0[Xx][\dA-Fa-f]+))/, -/^(?:([1-9]\d*)(?![\dA-FXa-fx]))/, -/^(?:\/\*(.|\n|\r)*?\*\/)/, -/^(?:\/\/[^\r\n]*)/, -/^(?:\/[^ \/]*?["'{}][^ ]*?\/)/, -/^(?:"((?:\\"|\\[^"]|[^"\\])*)")/, -/^(?:'((?:\\'|\\[^']|[^'\\])*)')/, -/^(?:[\/"'][^{}\/"']+)/, -/^(?:[^{}\/"']+)/, -/^(?:\{)/, -/^(?:\})/, -/^(?:[^\r\n]*(\r|\n)+)/, -/^(?:[^\r\n]+)/, -/^(?:(\r\n|\n|\r))/, -/^(?:"((?:\\"|\\[^"]|[^"\\])*)")/, -/^(?:'((?:\\'|\\[^']|[^'\\])*)')/, -/^(?:([^\S\n\r])+)/, -/^(?:\S+)/, -/^(?:.)/, -/^(?:$)/ - ], - conditions: { - "bnf": { - rules: [ - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 19, - 20, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 78, - 79 - ], - inclusive: true - }, - "ebnf": { - rules: [ - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 19, - 20, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 78, - 79 - ], - inclusive: true - }, - "token": { - rules: [ - 0, - 1, - 2, - 19, - 20, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 78, - 79 - ], - inclusive: true - }, - "action": { - rules: [ - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 78, - 79 - ], - inclusive: false - }, - "code": { - rules: [ - 52, - 71, - 72, - 78, - 79 - ], - inclusive: false - }, - "path": { - rules: [ - 19, - 20, - 73, - 74, - 75, - 76, - 77, - 78, - 79 - ], - inclusive: false - }, - "options": { - rules: [ - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 78, - 79 - ], - inclusive: false - }, - "INITIAL": { - rules: [ - 19, - 20, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 78, - 79 - ], - inclusive: true - } -} -}; - - -function indent(s, i) { - var a = s.split('\n'); - var pf = (new Array(i + 1)).join(' '); - return pf + a.join('\n' + pf); -} - -// properly quote and escape the given input string -function dquote(s) { - var sq = (s.indexOf('\'') >= 0); - var dq = (s.indexOf('"') >= 0); - if (sq && dq) { - s = s.replace(/"/g, '\\"'); - dq = false; - } - if (dq) { - s = '\'' + s + '\''; - } - else { - s = '"' + s + '"'; - } - return s; -}; - -return lexer; -})(); -parser.lexer = lexer; - -function Parser() { - this.yy = {}; -} -Parser.prototype = parser; -parser.Parser = Parser; - -return new Parser(); -})(); - - - - -if (typeof require !== 'undefined' && typeof exports !== 'undefined') { - exports.parser = parser; - exports.Parser = parser.Parser; - exports.parse = function () { - return parser.parse.apply(parser, arguments); - }; - -} diff --git a/lib/util/regexp-lexer.js b/lib/util/regexp-lexer.js deleted file mode 100644 index 84a50029e..000000000 --- a/lib/util/regexp-lexer.js +++ /dev/null @@ -1,2780 +0,0 @@ -// Basic Lexer implemented using JavaScript regular expressions -// Zachary Carter -// MIT Licensed - -'use strict'; - -var XRegExp = require('xregexp'); -var json5 = require('json5'); -var lexParser = require('./lex-parser'); -var setmgmt = require('./regexp-set-management'); -var code_exec = require('./safe-code-exec-and-diag'); -var version = require('./package.json').version; -var assert = require('assert'); - - - -const XREGEXP_UNICODE_ESCAPE_RE = setmgmt.XREGEXP_UNICODE_ESCAPE_RE; // Matches the XRegExp Unicode escape braced part, e.g. `{Number}` -const CHR_RE = setmgmt.CHR_RE; -const SET_PART_RE = setmgmt.SET_PART_RE; -const NOTHING_SPECIAL_RE = setmgmt.NOTHING_SPECIAL_RE; -const UNICODE_BASE_PLANE_MAX_CP = setmgmt.UNICODE_BASE_PLANE_MAX_CP; - -// The expanded regex sets which are equivalent to the given `\\{c}` escapes: -// -// `/\s/`: -const WHITESPACE_SETSTR = setmgmt.WHITESPACE_SETSTR; -// `/\d/`: -const DIGIT_SETSTR = setmgmt.DIGIT_SETSTR; -// `/\w/`: -const WORDCHAR_SETSTR = setmgmt.WORDCHAR_SETSTR; - - - -// see also ./lib/cli.js -/** -@public -@nocollapse -*/ -const defaultJisonLexOptions = { - moduleType: 'commonjs', - debug: false, - enableDebugLogs: false, - json: false, - main: false, // CLI: not:(--main option) - dumpSourceCodeOnFailure: true, - throwErrorOnCompileFailure: true, - - moduleName: undefined, - defaultModuleName: 'lexer', - file: undefined, - outfile: undefined, - inputPath: undefined, - inputFilename: undefined, - warn_cb: undefined, // function(msg) | true (= use Jison.Print) | false (= throw Exception) - - parseParams: undefined, - xregexp: false, - lexerErrorsAreRecoverable: false, - flex: false, - backtrack_lexer: false, - ranges: false, // track position range, i.e. start+end indexes in the input string - trackPosition: true, // track line+column position in the input string - caseInsensitive: false, - showSource: false, - pre_lex: undefined, - post_lex: undefined, -}; - - -// Convert dashed option keys to Camel Case, e.g. `camelCase('camels-have-one-hump')` => `'camelsHaveOneHump'` -/** @public */ -function camelCase(s) { - // Convert first character to lowercase - return s.replace(/^\w/, function (match) { - return match.toLowerCase(); - }) - .replace(/-\w/g, function (match) { - return match.charAt(1).toUpperCase(); - }); -} - -// Merge sets of options. -// -// Convert alternative jison option names to their base option. -// -// The *last* option set which overrides the default wins, where 'override' is -// defined as specifying a not-undefined value which is not equal to the -// default value. -// -// Return a fresh set of options. -/** @public */ -function mkStdOptions(/*...args*/) { - var h = Object.prototype.hasOwnProperty; - - // clone defaults, so we do not modify those constants. - var opts = {}; - var o = defaultJisonLexOptions; - - for (var p in o) { - if (h.call(o, p) && typeof o[p] !== 'undefined') { - opts[p] = o[p]; - } - } - - for (var i = 0, len = arguments.length; i < len; i++) { - o = arguments[i]; - - // clone input (while camel-casing the options), so we do not modify those either. - var o2 = {}; - - for (var p in o) { - if (h.call(o, p) && typeof o[p] !== 'undefined') { - o2[camelCase(p)] = o[p]; - } - } - - // now clean them options up: - if (typeof o2.main !== 'undefined') { - o2.noMain = !o2.main; - } - - delete o2.main; - - // special check for `moduleName` to ensure we detect the 'default' moduleName entering from the CLI - // NOT overriding the moduleName set in the grammar definition file via an `%options` entry: - if (o2.moduleName === o2.defaultModuleName) { - delete o2.moduleName; - } - - // now see if we have an overriding option here: - for (var p in o2) { - if (h.call(o2, p)) { - if (o2[p] !== undefined && o2[p] !== defaultJisonLexOptions[p]) { - opts[p] = o2[p]; - } - } - } - } - - return opts; -} - - -// Autodetect if the input lexer spec is in JSON or JISON -// format when the `options.json` flag is `true`. -// -// Produce the JSON lexer spec result when these are JSON formatted already as that -// would save us the trouble of doing this again, anywhere else in the JISON -// compiler/generator. -// -// Otherwise return the *parsed* lexer spec as it has -// been processed through LEXParser. -function autodetectAndConvertToJSONformat(lexerSpec, options) { - var chk_l = null; - var ex1, err; - - if (typeof lexerSpec === 'string') { - if (options.json) { - try { - chk_l = json5.parse(lexerSpec); - - // When JSON5-based parsing of the lexer spec succeeds, this implies the lexer spec is specified in `JSON mode` - // *OR* there's a JSON/JSON5 format error in the input: - } catch (e) { - ex1 = e; - } - } - if (!chk_l) { - // // WARNING: the lexer may receive options specified in the **grammar spec file**, - // // hence we should mix the options to ensure the lexParser always - // // receives the full set! - // // - // // make sure all options are 'standardized' before we go and mix them together: - // options = mkStdOptions(grammar.options, options); - try { - chk_l = lexParser.parse(lexerSpec, options); - } catch (e) { - if (options.json) { - err = new Error('Could not parse lexer spec in JSON AUTODETECT mode\nError: ' + ex1.message + ' (' + e.message + ')'); - err.secondary_exception = e; - err.stack = ex1.stack; - } else { - err = new Error('Could not parse lexer spec\nError: ' + e.message); - err.stack = e.stack; - } - throw err; - } - } - } else { - chk_l = lexerSpec; - } - - // Save time! Don't reparse the entire lexer spec *again* inside the code generators when that's not necessary: - - return chk_l; -} - - - - - -// HELPER FUNCTION: print the function in source code form, properly indented. -/** @public */ -function printFunctionSourceCode(f) { - return String(f).replace(/^ /gm, ''); -} -/** @public */ -function printFunctionSourceCodeContainer(f, depth) { - var s = String(f); - for (var d = (depth || 2); d > 0; d--) { - s = s.replace(/^ /gm, ''); - } - s = s.replace(/^\s*function\b[^\{]+\{/, '').replace(/\}\s*$/, ''); - return s; -} - - - -// expand macros and convert matchers to RegExp's -function prepareRules(dict, actions, caseHelper, tokens, startConditions, opts) { - var m, i, k, rule, action, conditions, - active_conditions, - rules = dict.rules, - newRules = [], - macros = {}, - regular_rule_count = 0, - simple_rule_count = 0; - - // Assure all options are camelCased: - assert(typeof opts.options['case-insensitive'] === 'undefined'); - - if (!tokens) { - tokens = {}; - } - - // Depending on the location within the regex we need different expansions of the macros: - // one expansion for when a macro is *inside* a `[...]` and another expansion when a macro - // is anywhere else in a regex: - if (dict.macros) { - macros = prepareMacros(dict.macros, opts); - } - - function tokenNumberReplacement(str, token) { - return 'return ' + (tokens[token] || '\'' + token.replace(/'/g, '\\\'') + '\''); - } - - // Make sure a comment does not contain any embedded '*/' end-of-comment marker - // as that would break the generated code - function postprocessComment(str) { - if (Array.isArray(str)) { - str = str.join(' '); - } - str = str.replace(/\*\//g, '*\\/'); // destroy any inner `*/` comment terminator sequence. - return str; - } - - actions.push('switch($avoiding_name_collisions) {'); - - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - m = rule[0]; - - active_conditions = []; - if (Object.prototype.toString.apply(m) !== '[object Array]') { - // implicit add to all inclusive start conditions - for (k in startConditions) { - if (startConditions[k].inclusive) { - active_conditions.push(k); - startConditions[k].rules.push(i); - } - } - } else if (m[0] === '*') { - // Add to ALL start conditions - active_conditions.push('*'); - for (k in startConditions) { - startConditions[k].rules.push(i); - } - rule.shift(); - m = rule[0]; - } else { - // Add to explicit start conditions - conditions = rule.shift(); - m = rule[0]; - for (k = 0; k < conditions.length; k++) { - if (!startConditions.hasOwnProperty(conditions[k])) { - startConditions[conditions[k]] = { - rules: [], - inclusive: false - }; - console.warn('Lexer Warning : "' + conditions[k] + '" start condition should be defined as %s or %x; assuming %x now.'); - } - active_conditions.push(conditions[k]); - startConditions[conditions[k]].rules.push(i); - } - } - - if (typeof m === 'string') { - m = expandMacros(m, macros, opts); - m = new XRegExp('^(?:' + m + ')', opts.options.caseInsensitive ? 'i' : ''); - } - newRules.push(m); - if (typeof rule[1] === 'function') { - rule[1] = String(rule[1]).replace(/^\s*function \(\)\s?\{/, '').replace(/\}\s*$/, ''); - } - action = rule[1]; - action = action.replace(/return '((?:\\'|[^']+)+)'/g, tokenNumberReplacement); - action = action.replace(/return "((?:\\"|[^"]+)+)"/g, tokenNumberReplacement); - - var code = ['\n/*! Conditions::']; - code.push(postprocessComment(active_conditions)); - code.push('*/', '\n/*! Rule:: '); - code.push(postprocessComment(rules[i][0])); - code.push('*/', '\n'); - - // When the action is *only* a simple `return TOKEN` statement, then add it to the caseHelpers; - // otherwise add the additional `break;` at the end. - // - // Note: we do NOT analyze the action block any more to see if the *last* line is a simple - // `return NNN;` statement as there are too many shoddy idioms, e.g. - // - // ``` - // %{ if (cond) - // return TOKEN; - // %} - // ``` - // - // which would then cause havoc when our action code analysis (using regexes or otherwise) was 'too simple' - // to catch these culprits; hence we resort and stick with the most fundamental approach here: - // always append `break;` even when it would be obvious to a human that such would be 'unreachable code'. - var match_nr = /^return[\s\r\n]+((?:'(?:\\'|[^']+)+')|(?:"(?:\\"|[^"]+)+")|\d+)[\s\r\n]*;?$/.exec(action.trim()); - if (match_nr) { - simple_rule_count++; - caseHelper.push([].concat(code, i, ':', match_nr[1]).join(' ').replace(/[\n]/g, '\n ')); - } else { - regular_rule_count++; - actions.push([].concat('case', i, ':', code, action, '\nbreak;').join(' ')); - } - } - actions.push('default:'); - actions.push(' return this.simpleCaseActionClusters[$avoiding_name_collisions];'); - actions.push('}'); - - return { - rules: newRules, - macros: macros, - - regular_rule_count: regular_rule_count, - simple_rule_count: simple_rule_count, - }; -} - - - - - - - -// expand all macros (with maybe one exception) in the given regex: the macros may exist inside `[...]` regex sets or -// elsewhere, which requires two different treatments to expand these macros. -function reduceRegex(s, name, opts, expandAllMacrosInSet_cb, expandAllMacrosElsewhere_cb) { - var orig = s; - var regex_simple_size = 0; - var regex_previous_alts_simple_size = 0; - - function errinfo() { - if (name) { - return 'macro [[' + name + ']]'; - } else { - return 'regex [[' + orig + ']]'; - } - } - - // propagate deferred exceptions = error reports. - if (s instanceof Error) { - return s; - } - - var c1, c2; - var rv = []; - var derr; - var se; - - while (s.length) { - c1 = s.match(CHR_RE); - if (!c1) { - // cope with illegal escape sequences too! - return new Error(errinfo() + ': illegal escape sequence at start of regex part: ' + s); - } else { - c1 = c1[0]; - } - s = s.substr(c1.length); - - switch (c1) { - case '[': - // this is starting a set within the regex: scan until end of set! - var set_content = []; - var l = new Array(UNICODE_BASE_PLANE_MAX_CP + 1); - - while (s.length) { - var inner = s.match(SET_PART_RE); - if (!inner) { - inner = s.match(CHR_RE); - if (!inner) { - // cope with illegal escape sequences too! - return new Error(errinfo() + ': illegal escape sequence at start of regex part: ' + s); - } else { - inner = inner[0]; - } - if (inner === ']') break; - } else { - inner = inner[0]; - } - set_content.push(inner); - s = s.substr(inner.length); - } - - // ensure that we hit the terminating ']': - c2 = s.match(CHR_RE); - if (!c2) { - // cope with illegal escape sequences too! - return new Error(errinfo() + ': regex set expression is broken: "' + s + '"'); - } else { - c2 = c2[0]; - } - if (c2 !== ']') { - return new Error(errinfo() + ': regex set expression is broken: apparently unterminated'); - } - s = s.substr(c2.length); - - se = set_content.join(''); - - // expand any macros in here: - if (expandAllMacrosInSet_cb) { - se = expandAllMacrosInSet_cb(se); - assert(se); - if (se instanceof Error) { - return new Error(errinfo() + ': ' + se.message); - } - } - - derr = setmgmt.set2bitarray(l, se, opts); - if (derr instanceof Error) { - return new Error(errinfo() + ': ' + derr.message); - } - - // find out which set expression is optimal in size: - var s1 = setmgmt.produceOptimizedRegex4Set(l); - - // check if the source regex set potentially has any expansions (guestimate!) - // - // The indexOf('{') picks both XRegExp Unicode escapes and JISON lexer macros, which is perfect for us here. - var has_expansions = (se.indexOf('{') >= 0); - - se = '[' + se + ']'; - - if (!has_expansions && se.length < s1.length) { - s1 = se; - } - rv.push(s1); - break; - - // XRegExp Unicode escape, e.g. `\\p{Number}`: - case '\\p': - c2 = s.match(XREGEXP_UNICODE_ESCAPE_RE); - if (c2) { - c2 = c2[0]; - s = s.substr(c2.length); - - // nothing to expand. - rv.push(c1 + c2); - } else { - // nothing to stretch this match, hence nothing to expand. - rv.push(c1); - } - break; - - // Either a range expression or the start of a macro reference: `.{1,3}` or `{NAME}`. - // Treat it as a macro reference and see if it will expand to anything: - case '{': - c2 = s.match(NOTHING_SPECIAL_RE); - if (c2) { - c2 = c2[0]; - s = s.substr(c2.length); - - var c3 = s[0]; - s = s.substr(c3.length); - if (c3 === '}') { - // possibly a macro name in there... Expand if possible: - c2 = c1 + c2 + c3; - if (expandAllMacrosElsewhere_cb) { - c2 = expandAllMacrosElsewhere_cb(c2); - assert(c2); - if (c2 instanceof Error) { - return new Error(errinfo() + ': ' + c2.message); - } - } - } else { - // not a well-terminated macro reference or something completely different: - // we do not even attempt to expand this as there's guaranteed nothing to expand - // in this bit. - c2 = c1 + c2 + c3; - } - rv.push(c2); - } else { - // nothing to stretch this match, hence nothing to expand. - rv.push(c1); - } - break; - - // Recognize some other regex elements, but there's no need to understand them all. - // - // We are merely interested in any chunks now which do *not* include yet another regex set `[...]` - // nor any `{MACRO}` reference: - default: - // non-set character or word: see how much of this there is for us and then see if there - // are any macros still lurking inside there: - c2 = s.match(NOTHING_SPECIAL_RE); - if (c2) { - c2 = c2[0]; - s = s.substr(c2.length); - - // nothing to expand. - rv.push(c1 + c2); - } else { - // nothing to stretch this match, hence nothing to expand. - rv.push(c1); - } - break; - } - } - - s = rv.join(''); - - // When this result is suitable for use in a set, than we should be able to compile - // it in a regex; that way we can easily validate whether macro X is fit to be used - // inside a regex set: - try { - var re; - re = new XRegExp(s); - re.test(s[0]); - } catch (ex) { - // make sure we produce a regex expression which will fail badly when it is used - // in actual code: - return new Error(errinfo() + ': expands to an invalid regex: /' + s + '/'); - } - - assert(s); - return s; -} - - -// expand macros within macros and cache the result -function prepareMacros(dict_macros, opts) { - var macros = {}; - - // expand a `{NAME}` macro which exists inside a `[...]` set: - function expandMacroInSet(i) { - var k, a, m; - if (!macros[i]) { - m = dict_macros[i]; - - if (m.indexOf('{') >= 0) { - // set up our own record so we can detect definition loops: - macros[i] = { - in_set: false, - elsewhere: null, - raw: dict_macros[i] - }; - - for (k in dict_macros) { - if (dict_macros.hasOwnProperty(k) && i !== k) { - // it doesn't matter if the lexer recognized that the inner macro(s) - // were sitting inside a `[...]` set or not: the fact that they are used - // here in macro `i` which itself sits in a set, makes them *all* live in - // a set so all of them get the same treatment: set expansion style. - // - // Note: make sure we don't try to expand any XRegExp `\p{...}` or `\P{...}` - // macros here: - if (XRegExp._getUnicodeProperty(k)) { - // Work-around so that you can use `\p{ascii}` for a XRegExp slug, a.k.a. - // Unicode 'General Category' Property cf. http://unicode.org/reports/tr18/#Categories, - // while using `\p{ASCII}` as a *macro expansion* of the `ASCII` - // macro: - if (k.toUpperCase() !== k) { - m = new Error('Cannot use name "' + k + '" as a macro name as it clashes with the same XRegExp "\\p{..}" Unicode \'General Category\' Property name. Use all-uppercase macro names, e.g. name your macro "' + k.toUpperCase() + '" to work around this issue or give your offending macro a different name.'); - break; - } - } - - a = m.split('{' + k + '}'); - if (a.length > 1) { - var x = expandMacroInSet(k); - assert(x); - if (x instanceof Error) { - m = x; - break; - } - m = a.join(x); - } - } - } - } - - var mba = setmgmt.reduceRegexToSetBitArray(m, i, opts); - - var s1; - - // propagate deferred exceptions = error reports. - if (mba instanceof Error) { - s1 = mba; - } else { - s1 = setmgmt.bitarray2set(mba, false); - - m = s1; - } - - macros[i] = { - in_set: s1, - elsewhere: null, - raw: dict_macros[i] - }; - } else { - m = macros[i].in_set; - - if (m instanceof Error) { - // this turns out to be an macro with 'issues' and it is used, so the 'issues' do matter: bombs away! - return new Error(m.message); - } - - // detect definition loop: - if (m === false) { - return new Error('Macro name "' + i + '" has an illegal, looping, definition, i.e. it\'s definition references itself, either directly or indirectly, via other macros.'); - } - } - - return m; - } - - function expandMacroElsewhere(i) { - var k, a, m; - - if (macros[i].elsewhere == null) { - m = dict_macros[i]; - - // set up our own record so we can detect definition loops: - macros[i].elsewhere = false; - - // the macro MAY contain other macros which MAY be inside a `[...]` set in this - // macro or elsewhere, hence we must parse the regex: - m = reduceRegex(m, i, opts, expandAllMacrosInSet, expandAllMacrosElsewhere); - // propagate deferred exceptions = error reports. - if (m instanceof Error) { - return m; - } - - macros[i].elsewhere = m; - } else { - m = macros[i].elsewhere; - - if (m instanceof Error) { - // this turns out to be an macro with 'issues' and it is used, so the 'issues' do matter: bombs away! - return m; - } - - // detect definition loop: - if (m === false) { - return new Error('Macro name "' + i + '" has an illegal, looping, definition, i.e. it\'s definition references itself, either directly or indirectly, via other macros.'); - } - } - - return m; - } - - function expandAllMacrosInSet(s) { - var i, x; - - // process *all* the macros inside [...] set: - if (s.indexOf('{') >= 0) { - for (i in macros) { - if (macros.hasOwnProperty(i)) { - var a = s.split('{' + i + '}'); - if (a.length > 1) { - x = expandMacroInSet(i); - assert(x); - if (x instanceof Error) { - return new Error('failure to expand the macro [' + i + '] in set [' + s + ']: ' + x.message); - } - s = a.join(x); - } - - // stop the brute-force expansion attempt when we done 'em all: - if (s.indexOf('{') === -1) { - break; - } - } - } - } - - return s; - } - - function expandAllMacrosElsewhere(s) { - var i, x; - - // When we process the remaining macro occurrences in the regex - // every macro used in a lexer rule will become its own capture group. - // - // Meanwhile the cached expansion will expand any submacros into - // *NON*-capturing groups so that the backreference indexes remain as you'ld - // expect and using macros doesn't require you to know exactly what your - // used macro will expand into, i.e. which and how many submacros it has. - // - // This is a BREAKING CHANGE from vanilla jison 0.4.15! - if (s.indexOf('{') >= 0) { - for (i in macros) { - if (macros.hasOwnProperty(i)) { - // These are all submacro expansions, hence non-capturing grouping is applied: - var a = s.split('{' + i + '}'); - if (a.length > 1) { - x = expandMacroElsewhere(i); - assert(x); - if (x instanceof Error) { - return new Error('failure to expand the macro [' + i + '] in regex /' + s + '/: ' + x.message); - } - s = a.join('(?:' + x + ')'); - } - - // stop the brute-force expansion attempt when we done 'em all: - if (s.indexOf('{') === -1) { - break; - } - } - } - } - - return s; - } - - - var m, i; - - if (opts.debug) console.log('\n############## RAW macros: ', dict_macros); - - // first we create the part of the dictionary which is targeting the use of macros - // *inside* `[...]` sets; once we have completed that half of the expansions work, - // we then go and expand the macros for when they are used elsewhere in a regex: - // iff we encounter submacros then which are used *inside* a set, we can use that - // first half dictionary to speed things up a bit as we can use those expansions - // straight away! - for (i in dict_macros) { - if (dict_macros.hasOwnProperty(i)) { - expandMacroInSet(i); - } - } - - for (i in dict_macros) { - if (dict_macros.hasOwnProperty(i)) { - expandMacroElsewhere(i); - } - } - - if (opts.debug) console.log('\n############### expanded macros: ', macros); - - return macros; -} - - - -// expand macros in a regex; expands them recursively -function expandMacros(src, macros, opts) { - var expansion_count = 0; - - // By the time we call this function `expandMacros` we MUST have expanded and cached all macros already! - // Hence things should be easy in there: - - function expandAllMacrosInSet(s) { - var i, m, x; - - // process *all* the macros inside [...] set: - if (s.indexOf('{') >= 0) { - for (i in macros) { - if (macros.hasOwnProperty(i)) { - m = macros[i]; - - var a = s.split('{' + i + '}'); - if (a.length > 1) { - x = m.in_set; - - assert(x); - if (x instanceof Error) { - // this turns out to be an macro with 'issues' and it is used, so the 'issues' do matter: bombs away! - throw x; - } - - // detect definition loop: - if (x === false) { - return new Error('Macro name "' + i + '" has an illegal, looping, definition, i.e. it\'s definition references itself, either directly or indirectly, via other macros.'); - } - - s = a.join(x); - expansion_count++; - } - - // stop the brute-force expansion attempt when we done 'em all: - if (s.indexOf('{') === -1) { - break; - } - } - } - } - - return s; - } - - function expandAllMacrosElsewhere(s) { - var i, m, x; - - // When we process the main macro occurrences in the regex - // every macro used in a lexer rule will become its own capture group. - // - // Meanwhile the cached expansion will expand any submacros into - // *NON*-capturing groups so that the backreference indexes remain as you'ld - // expect and using macros doesn't require you to know exactly what your - // used macro will expand into, i.e. which and how many submacros it has. - // - // This is a BREAKING CHANGE from vanilla jison 0.4.15! - if (s.indexOf('{') >= 0) { - for (i in macros) { - if (macros.hasOwnProperty(i)) { - m = macros[i]; - - var a = s.split('{' + i + '}'); - if (a.length > 1) { - // These are all main macro expansions, hence CAPTURING grouping is applied: - x = m.elsewhere; - assert(x); - - // detect definition loop: - if (x === false) { - return new Error('Macro name "' + i + '" has an illegal, looping, definition, i.e. it\'s definition references itself, either directly or indirectly, via other macros.'); - } - - s = a.join('(' + x + ')'); - expansion_count++; - } - - // stop the brute-force expansion attempt when we done 'em all: - if (s.indexOf('{') === -1) { - break; - } - } - } - } - - return s; - } - - - // When we process the macro occurrences in the regex - // every macro used in a lexer rule will become its own capture group. - // - // Meanwhile the cached expansion will have expanded any submacros into - // *NON*-capturing groups so that the backreference indexes remain as you'ld - // expect and using macros doesn't require you to know exactly what your - // used macro will expand into, i.e. which and how many submacros it has. - // - // This is a BREAKING CHANGE from vanilla jison 0.4.15! - var s2 = reduceRegex(src, null, opts, expandAllMacrosInSet, expandAllMacrosElsewhere); - // propagate deferred exceptions = error reports. - if (s2 instanceof Error) { - throw s2; - } - - // only when we did expand some actual macros do we take the re-interpreted/optimized/regenerated regex from reduceRegex() - // in order to keep our test cases simple and rules recognizable. This assumes the user can code good regexes on his own, - // as long as no macros are involved... - // - // Also pick the reduced regex when there (potentially) are XRegExp extensions in the original, e.g. `\\p{Number}`, - // unless the `xregexp` output option has been enabled. - if (expansion_count > 0 || (src.indexOf('\\p{') >= 0 && !opts.options.xregexp)) { - src = s2; - } else { - // Check if the reduced regex is smaller in size; when it is, we still go with the new one! - if (s2.length < src.length) { - src = s2; - } - } - - return src; -} - -function prepareStartConditions(conditions) { - var sc, - hash = {}; - for (sc in conditions) { - if (conditions.hasOwnProperty(sc)) { - hash[sc] = {rules:[], inclusive: !conditions[sc]}; - } - } - return hash; -} - -function buildActions(dict, tokens, opts) { - var actions = [dict.actionInclude || '', 'var YYSTATE = YY_START;']; - var tok; - var toks = {}; - var caseHelper = []; - - // tokens: map/array of token numbers to token names - for (tok in tokens) { - var idx = parseInt(tok); - if (idx && idx > 0) { - toks[tokens[tok]] = idx; - } - } - - if (opts.options.flex) { - dict.rules.push(['.', 'console.log(yytext); /* `flex` lexing mode: the last resort rule! */']); - } - - var gen = prepareRules(dict, actions, caseHelper, tokens && toks, opts.conditions, opts); - - var fun = actions.join('\n'); - 'yytext yyleng yylineno yylloc yyerror'.split(' ').forEach(function (yy) { - fun = fun.replace(new RegExp('\\b(' + yy + ')\\b', 'g'), 'yy_.$1'); - }); - - return { - caseHelperInclude: '{\n' + caseHelper.join(',') + '\n}', - - actions: expandParseArguments('function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START, parseParams) {\n', opts) + fun + '\n}', - - rules: gen.rules, - macros: gen.macros, // propagate these for debugging/diagnostic purposes - - regular_rule_count: gen.regular_rule_count, - simple_rule_count: gen.simple_rule_count, - }; -} - -// -// NOTE: this is *almost* a copy of the JisonParserError producing code in -// jison/lib/jison.js @ line 2304:lrGeneratorMixin.generateErrorClass -// -function generateErrorClass() { - /** - * See also: - * http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508 - * but we keep the prototype.constructor and prototype.name assignment lines too for compatibility - * with userland code which might access the derived class in a 'classic' way. - * - * @public - * @constructor - * @nocollapse - */ - function JisonLexerError(msg, hash) { - Object.defineProperty(this, 'name', { - enumerable: false, - writable: false, - value: 'JisonLexerError' - }); - - if (msg == null) msg = '???'; - - Object.defineProperty(this, 'message', { - enumerable: false, - writable: true, - value: msg - }); - - this.hash = hash; - - var stacktrace; - if (hash && hash.exception instanceof Error) { - var ex2 = hash.exception; - this.message = ex2.message || msg; - stacktrace = ex2.stack; - } - if (!stacktrace) { - if (Error.hasOwnProperty('captureStackTrace')) { // V8 - Error.captureStackTrace(this, this.constructor); - } else { - stacktrace = (new Error(msg)).stack; - } - } - if (stacktrace) { - Object.defineProperty(this, 'stack', { - enumerable: false, - writable: false, - value: stacktrace - }); - } - } - - // wrap this init code in a function so we can String(function)-dump it into the generated - // output: that way we only have to write this code *once*! - function __extra_code__() { - if (typeof Object.setPrototypeOf === 'function') { - Object.setPrototypeOf(JisonLexerError.prototype, Error.prototype); - } else { - JisonLexerError.prototype = Object.create(Error.prototype); - } - JisonLexerError.prototype.constructor = JisonLexerError; - JisonLexerError.prototype.name = 'JisonLexerError'; - } - __extra_code__(); - - var prelude = [ - '// See also:', - '// http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508', - '// but we keep the prototype.constructor and prototype.name assignment lines too for compatibility', - '// with userland code which might access the derived class in a \'classic\' way.', - printFunctionSourceCode(JisonLexerError), - printFunctionSourceCodeContainer(__extra_code__), - '', - ]; - - return prelude.join('\n'); -} - - -var jisonLexerErrorDefinition = generateErrorClass(); - - -function generateFakeXRegExpClassSrcCode() { - function fake() { - var __hacky_counter__ = 0; - - /** - * @constructor - * @nocollapse - */ - function XRegExp(re, f) { - this.re = re; - this.flags = f; - this._getUnicodeProperty = function (k) {}; - var fake = /./; // WARNING: this exact 'fake' is also depended upon by the xregexp unit test! - __hacky_counter__++; - fake.__hacky_backy__ = __hacky_counter__; - return fake; - } - } - - return printFunctionSourceCodeContainer(fake); -} - - - -/** @constructor */ -function RegExpLexer(dict, input, tokens, build_options) { - var opts; - var dump = false; - - function test_me(tweak_cb, description, src_exception, ex_callback) { - opts = processGrammar(dict, tokens, build_options); - opts.__in_rules_failure_analysis_mode__ = false; - assert(opts.options); - if (tweak_cb) { - tweak_cb(); - } - var source = generateModuleBody(opts); - try { - // The generated code will always have the `lexer` variable declared at local scope - // as `eval()` will use the local scope. - // - // The compiled code will look something like this: - // - // ``` - // var lexer; - // bla bla... - // ``` - // - // or - // - // ``` - // var lexer = { bla... }; - // ``` - var testcode = [ - '// provide a local version for test purposes:', - jisonLexerErrorDefinition, - '', - generateFakeXRegExpClassSrcCode(), - '', - source, - '', - 'return lexer;'].join('\n'); - var lexer = code_exec(testcode, function generated_code_exec_wrapper(sourcecode) { - //console.log("===============================LEXER TEST CODE\n", sourcecode, "\n=====================END====================\n"); - var lexer_f = new Function('', sourcecode); - return lexer_f(); - }, opts.options, "lexer"); - - if (!lexer) { - throw new Error('no lexer defined *at all*?!'); - } - if (typeof lexer.options !== 'object' || lexer.options == null) { - throw new Error('your lexer class MUST have an .options member object or it won\'t fly!'); - } - if (typeof lexer.setInput !== 'function') { - throw new Error('your lexer class MUST have a .setInput function member or it won\'t fly!'); - } - if (lexer.EOF !== 1 && lexer.ERROR !== 2) { - throw new Error('your lexer class MUST have these constants defined: lexer.EOF = 1 and lexer.ERROR = 2 or it won\'t fly!'); - } - - // When we do NOT crash, we found/killed the problem area just before this call! - if (src_exception && description) { - src_exception.message += '\n (' + description + ')'; - } - - // patch the pre and post handlers in there, now that we have some live code to work with: - if (opts.options) { - var pre = opts.options.pre_lex; - var post = opts.options.post_lex; - // since JSON cannot encode functions, we'll have to do it manually now: - if (typeof pre === 'function') { - lexer.options.pre_lex = pre; - } - if (typeof post === 'function') { - lexer.options.post_lex = post; - } - } - - if (opts.options.showSource) { - if (typeof opts.options.showSource === 'function') { - opts.options.showSource(lexer, source, opts); - } else { - console.log("\nGenerated lexer sourcecode:\n----------------------------------------\n", source, "\n----------------------------------------\n"); - } - } - return lexer; - } catch (ex) { - // if (src_exception) { - // src_exception.message += '\n (' + description + ': ' + ex.message + ')'; - // } - - if (ex_callback) { - ex_callback(ex); - } else if (dump) { - console.log('source code:\n', source); - } - return false; - } - } - - /** @constructor */ - var lexer = test_me(null, null, null, function (ex) { - // When we get an exception here, it means some part of the user-specified lexer is botched. - // - // Now we go and try to narrow down the problem area/category: - if (!test_me(function () { - opts.conditions = []; - opts.showSource = false; - }, (dict.rules.length > 0 ? - 'One or more of your lexer state names are possibly botched?' : - 'Your custom lexer is somehow botched.'), ex, null)) { - if (!test_me(function () { - // opts.conditions = []; - opts.rules = []; - opts.showSource = false; - opts.__in_rules_failure_analysis_mode__ = true; - }, 'One or more of your lexer rules are possibly botched?', ex, null)) { - // kill each rule action block, one at a time and test again after each 'edit': - var rv = false; - for (var i = 0, len = dict.rules.length; i < len; i++) { - dict.rules[i][1] = '{ /* nada */ }'; - rv = test_me(function () { - // opts.conditions = []; - // opts.rules = []; - // opts.__in_rules_failure_analysis_mode__ = true; - }, 'Your lexer rule "' + dict.rules[i][0] + '" action code block is botched?', ex, null); - if (rv) { - break; - } - } - if (!rv) { - test_me(function () { - opts.conditions = []; - opts.rules = []; - opts.performAction = 'null'; - // opts.options = {}; - // opts.caseHelperInclude = '{}'; - opts.showSource = false; - opts.__in_rules_failure_analysis_mode__ = true; - - dump = false; - }, 'One or more of your lexer rule action code block(s) are possibly botched?', ex, null); - } - } - } - throw ex; - }); - - lexer.setInput(input); - - /** @public */ - lexer.generate = function () { - return generateFromOpts(opts); - }; - /** @public */ - lexer.generateModule = function () { - return generateModule(opts); - }; - /** @public */ - lexer.generateCommonJSModule = function () { - return generateCommonJSModule(opts); - }; - /** @public */ - lexer.generateAMDModule = function () { - return generateAMDModule(opts); - }; - - // internal APIs to aid testing: - /** @public */ - lexer.getExpandedMacros = function () { - return opts.macros; - }; - - return lexer; -} - -// code stripping performance test for very simple grammar: -// -// - removing backtracking parser code branches: 730K -> 750K rounds -// - removing all location info tracking: yylineno, yylloc, etc.: 750K -> 900K rounds -// - no `yyleng`: 900K -> 905K rounds -// - no `this.done` as we cannot have a NULL `_input` anymore: 905K -> 930K rounds -// - `simpleCaseActionClusters` as array instead of hash object: 930K -> 940K rounds -// - lexers which have only return stmts, i.e. only a -// `simpleCaseActionClusters` lookup table to produce -// lexer tokens: *inline* the `performAction` call: 940K -> 950K rounds -// - given all the above, you can *inline* what's left of -// `lexer_next()`: 950K -> 955K rounds (? this stuff becomes hard to measure; inaccuracy abounds!) -// -// Total gain when we forget about very minor (and tough to nail) *inlining* `lexer_next()` gains: -// -// 730 -> 950 ~ 30% performance gain. -// - -// As a function can be reproduced in source-code form by any JavaScript engine, we're going to wrap this chunk -// of code in a function so that we can easily get it including it comments, etc.: -/** -@public -@nocollapse -*/ -function getRegExpLexerPrototype() { - return { - EOF: 1, - ERROR: 2, - - // JisonLexerError: JisonLexerError, /// <-- injected by the code generator - - // options: {}, /// <-- injected by the code generator - - // yy: ..., /// <-- injected by setInput() - - __currentRuleSet__: null, /// <-- internal rule set cache for the current lexer state - - __error_infos: [], /// INTERNAL USE ONLY: the set of lexErrorInfo objects created since the last cleanup - - __decompressed: false, /// INTERNAL USE ONLY: mark whether the lexer instance has been 'unfolded' completely and is now ready for use - - done: false, /// INTERNAL USE ONLY - _backtrack: false, /// INTERNAL USE ONLY - _input: '', /// INTERNAL USE ONLY - _more: false, /// INTERNAL USE ONLY - _signaled_error_token: false, /// INTERNAL USE ONLY - - conditionStack: [], /// INTERNAL USE ONLY; managed via `pushState()`, `popState()`, `topState()` and `stateStackSize()` - - match: '', /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks input which has been matched so far for the lexer token under construction. `match` is identical to `yytext` except that this one still contains the matched input string after `lexer.performAction()` has been invoked, where userland code MAY have changed/replaced the `yytext` value entirely! - matched: '', /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks entire input which has been matched so far - matches: false, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks RE match result for last (successful) match attempt - yytext: '', /// ADVANCED USE ONLY: tracks input which has been matched so far for the lexer token under construction; this value is transferred to the parser as the 'token value' when the parser consumes the lexer token produced through a call to the `lex()` API. - offset: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks the 'cursor position' in the input string, i.e. the number of characters matched so far - yyleng: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: length of matched input for the token under construction (`yytext`) - yylineno: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: 'line number' at which the token under construction is located - yylloc: null, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks location info (lines + columns) for the token under construction - - /** - INTERNAL USE: construct a suitable error info hash object instance for `parseError`. - - @public - @this {RegExpLexer} - */ - constructLexErrorInfo: function lexer_constructLexErrorInfo(msg, recoverable) { - /** @constructor */ - var pei = { - errStr: msg, - recoverable: !!recoverable, - text: this.match, // This one MAY be empty; userland code should use the `upcomingInput` API to obtain more text which follows the 'lexer cursor position'... - token: null, - line: this.yylineno, - loc: this.yylloc, - yy: this.yy, - lexer: this, - - /** - and make sure the error info doesn't stay due to potential - ref cycle via userland code manipulations. - These would otherwise all be memory leak opportunities! - - Note that only array and object references are nuked as those - constitute the set of elements which can produce a cyclic ref. - The rest of the members is kept intact as they are harmless. - - @public - @this {LexErrorInfo} - */ - destroy: function destructLexErrorInfo() { - // remove cyclic references added to error info: - // info.yy = null; - // info.lexer = null; - // ... - var rec = !!this.recoverable; - for (var key in this) { - if (this.hasOwnProperty(key) && typeof key === 'object') { - this[key] = undefined; - } - } - this.recoverable = rec; - } - }; - // track this instance so we can `destroy()` it once we deem it superfluous and ready for garbage collection! - this.__error_infos.push(pei); - return pei; - }, - - /** - handler which is invoked when a lexer error occurs. - - @public - @this {RegExpLexer} - */ - parseError: function lexer_parseError(str, hash, ExceptionClass) { - if (!ExceptionClass) { - ExceptionClass = this.JisonLexerError; - } - if (this.yy.parser && typeof this.yy.parser.parseError === 'function') { - return this.yy.parser.parseError(str, hash, ExceptionClass) || this.ERROR; - } else if (typeof this.yy.parseError === 'function') { - return this.yy.parseError(str, hash, ExceptionClass) || this.ERROR; - } else { - throw new ExceptionClass(str, hash); - } - }, - - /** - method which implements `yyerror(str, ...args)` functionality for use inside lexer actions. - - @public - @this {RegExpLexer} - */ - yyerror: function yyError(str /*, ...args */) { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': ' + str, this.options.lexerErrorsAreRecoverable); - - // Add any extra args to the hash under the name `extra_error_attributes`: - var args = Array.prototype.slice.call(arguments, 1); - if (args.length) { - hash.extra_error_attributes = args; - } - - return (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - }, - - /** - final cleanup function for when we have completed lexing the input; - make it an API so that external code can use this one once userland - code has decided it's time to destroy any lingering lexer error - hash object instances and the like: this function helps to clean - up these constructs, which *may* carry cyclic references which would - otherwise prevent the instances from being properly and timely - garbage-collected, i.e. this function helps prevent memory leaks! - - @public - @this {RegExpLexer} - */ - cleanupAfterLex: function lexer_cleanupAfterLex(do_not_nuke_errorinfos) { - var rv; - - // prevent lingering circular references from causing memory leaks: - this.setInput('', {}); - - // nuke the error hash info instances created during this run. - // Userland code must COPY any data/references - // in the error hash instance(s) it is more permanently interested in. - if (!do_not_nuke_errorinfos) { - for (var i = this.__error_infos.length - 1; i >= 0; i--) { - var el = this.__error_infos[i]; - if (el && typeof el.destroy === 'function') { - el.destroy(); - } - } - this.__error_infos.length = 0; - } - - return this; - }, - - /** - clear the lexer token context; intended for internal use only - - @public - @this {RegExpLexer} - */ - clear: function lexer_clear() { - this.yytext = ''; - this.yyleng = 0; - this.match = ''; - this.matches = false; - this._more = false; - this._backtrack = false; - - var col = this.yylloc ? this.yylloc.last_column : 0; - this.yylloc = { - first_line: this.yylineno + 1, - first_column: col, - last_line: this.yylineno + 1, - last_column: col, - - range: (this.options.ranges ? [this.offset, this.offset] : undefined) - }; - }, - - /** - resets the lexer, sets new input - - @public - @this {RegExpLexer} - */ - setInput: function lexer_setInput(input, yy) { - this.yy = yy || this.yy || {}; - - // also check if we've fully initialized the lexer instance, - // including expansion work to be done to go from a loaded - // lexer to a usable lexer: - if (!this.__decompressed) { - // step 1: decompress the regex list: - var rules = this.rules; - for (var i = 0, len = rules.length; i < len; i++) { - var rule_re = rules[i]; - - // compression: is the RE an xref to another RE slot in the rules[] table? - if (typeof rule_re === 'number') { - rules[i] = rules[rule_re]; - } - } - - // step 2: unfold the conditions[] set to make these ready for use: - var conditions = this.conditions; - for (var k in conditions) { - var spec = conditions[k]; - - var rule_ids = spec.rules; - - var len = rule_ids.length; - var rule_regexes = new Array(len + 1); // slot 0 is unused; we use a 1-based index approach here to keep the hottest code in `lexer_next()` fast and simple! - var rule_new_ids = new Array(len + 1); - - for (var i = 0; i < len; i++) { - var idx = rule_ids[i]; - var rule_re = rules[idx]; - rule_regexes[i + 1] = rule_re; - rule_new_ids[i + 1] = idx; - } - - spec.rules = rule_new_ids; - spec.__rule_regexes = rule_regexes; - spec.__rule_count = len; - } - - this.__decompressed = true; - } - - this._input = input || ''; - this.clear(); - this._signaled_error_token = false; - this.done = false; - this.yylineno = 0; - this.matched = ''; - this.conditionStack = ['INITIAL']; - this.__currentRuleSet__ = null; - this.yylloc = { - first_line: 1, - first_column: 0, - last_line: 1, - last_column: 0, - - range: (this.options.ranges ? [0, 0] : undefined) - }; - this.offset = 0; - return this; - }, - - /** - push a new input into the lexer and activate it: - the old input position is stored and will be resumed - once this new input has been consumed. - - Use this API to help implement C-preprocessor-like - `#include` statements. - - Available options: - - - `emit_EOF_at_end` : {int} the `EOF`-like token to emit - when the new input is consumed: use - this to mark the end of the new input - in the parser grammar. zero/falsey - token value means no end marker token - will be emitted before the lexer - resumes reading from the previous input. - - @public - @this {RegExpLexer} - */ - pushInput: function lexer_pushInput(input, label, options) { - options = options || {}; - - this._input = input || ''; - this.clear(); - // this._signaled_error_token = false; - this.done = false; - this.yylineno = 0; - this.matched = ''; - // this.conditionStack = ['INITIAL']; - // this.__currentRuleSet__ = null; - this.yylloc = { - first_line: 1, - first_column: 0, - last_line: 1, - last_column: 0, - - range: (this.options.ranges ? [0, 0] : undefined) - }; - this.offset = 0; - return this; - }, - - /** - consumes and returns one char from the input - - @public - @this {RegExpLexer} - */ - input: function lexer_input() { - if (!this._input) { - //this.done = true; -- don't set `done` as we want the lex()/next() API to be able to produce one custom EOF token match after this anyhow. (lexer can match special <> tokens and perform user action code for a <> match, but only does so *once*) - return null; - } - var ch = this._input[0]; - this.yytext += ch; - this.yyleng++; - this.offset++; - this.match += ch; - this.matched += ch; - // Count the linenumber up when we hit the LF (or a stand-alone CR). - // On CRLF, the linenumber is incremented when you fetch the CR or the CRLF combo - // and we advance immediately past the LF as well, returning both together as if - // it was all a single 'character' only. - var slice_len = 1; - var lines = false; - if (ch === '\n') { - lines = true; - } else if (ch === '\r') { - lines = true; - var ch2 = this._input[1]; - if (ch2 === '\n') { - slice_len++; - ch += ch2; - this.yytext += ch2; - this.yyleng++; - this.offset++; - this.match += ch2; - this.matched += ch2; - if (this.options.ranges) { - this.yylloc.range[1]++; - } - } - } - if (lines) { - this.yylineno++; - this.yylloc.last_line++; - this.yylloc.last_column = 0; - } else { - this.yylloc.last_column++; - } - if (this.options.ranges) { - this.yylloc.range[1]++; - } - - this._input = this._input.slice(slice_len); - return ch; - }, - - /** - unshifts one char (or an entire string) into the input - - @public - @this {RegExpLexer} - */ - unput: function lexer_unput(ch) { - var len = ch.length; - var lines = ch.split(/(?:\r\n?|\n)/g); - - this._input = ch + this._input; - this.yytext = this.yytext.substr(0, this.yytext.length - len); - this.yyleng = this.yytext.length; - this.offset -= len; - this.match = this.match.substr(0, this.match.length - len); - this.matched = this.matched.substr(0, this.matched.length - len); - - if (lines.length > 1) { - this.yylineno -= lines.length - 1; - - this.yylloc.last_line = this.yylineno + 1; - var pre = this.match; - var pre_lines = pre.split(/(?:\r\n?|\n)/g); - if (pre_lines.length === 1) { - pre = this.matched; - pre_lines = pre.split(/(?:\r\n?|\n)/g); - } - this.yylloc.last_column = pre_lines[pre_lines.length - 1].length; - } else { - this.yylloc.last_column -= len; - } - - if (this.options.ranges) { - this.yylloc.range[1] = this.yylloc.range[0] + this.yyleng; - } - this.done = false; - return this; - }, - - /** - cache matched text and append it on next action - - @public - @this {RegExpLexer} - */ - more: function lexer_more() { - this._more = true; - return this; - }, - - /** - signal the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead. - - @public - @this {RegExpLexer} - */ - reject: function lexer_reject() { - if (this.options.backtrack_lexer) { - this._backtrack = true; - } else { - // when the `parseError()` call returns, we MUST ensure that the error is registered. - // We accomplish this by signaling an 'error' token to be produced for the current - // `.lex()` run. - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).' + pos_str, false); - this._signaled_error_token = (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - } - return this; - }, - - /** - retain first n characters of the match - - @public - @this {RegExpLexer} - */ - less: function lexer_less(n) { - return this.unput(this.match.slice(n)); - }, - - /** - return (part of the) already matched input, i.e. for error messages. - - Limit the returned string length to `maxSize` (default: 20). - - Limit the returned string to the `maxLines` number of lines of input (default: 1). - - Negative limit values equal *unlimited*. - - @public - @this {RegExpLexer} - */ - pastInput: function lexer_pastInput(maxSize, maxLines) { - var past = this.matched.substring(0, this.matched.length - this.match.length); - if (maxSize < 0) - maxSize = past.length; - else if (!maxSize) - maxSize = 20; - if (maxLines < 0) - maxLines = past.length; // can't ever have more input lines than this! - else if (!maxLines) - maxLines = 1; - // `substr` anticipation: treat \r\n as a single character and take a little - // more than necessary so that we can still properly check against maxSize - // after we've transformed and limited the newLines in here: - past = past.substr(-maxSize * 2 - 2); - // now that we have a significantly reduced string to process, transform the newlines - // and chop them, then limit them: - var a = past.replace(/\r\n|\r/g, '\n').split('\n'); - a = a.slice(-maxLines); - past = a.join('\n'); - // When, after limiting to maxLines, we still have too much to return, - // do add an ellipsis prefix... - if (past.length > maxSize) { - past = '...' + past.substr(-maxSize); - } - return past; - }, - - /** - return (part of the) upcoming input, i.e. for error messages. - - Limit the returned string length to `maxSize` (default: 20). - - Limit the returned string to the `maxLines` number of lines of input (default: 1). - - Negative limit values equal *unlimited*. - - @public - @this {RegExpLexer} - */ - upcomingInput: function lexer_upcomingInput(maxSize, maxLines) { - var next = this.match; - if (maxSize < 0) - maxSize = next.length + this._input.length; - else if (!maxSize) - maxSize = 20; - if (maxLines < 0) - maxLines = maxSize; // can't ever have more input lines than this! - else if (!maxLines) - maxLines = 1; - // `substring` anticipation: treat \r\n as a single character and take a little - // more than necessary so that we can still properly check against maxSize - // after we've transformed and limited the newLines in here: - if (next.length < maxSize * 2 + 2) { - next += this._input.substring(0, maxSize * 2 + 2); // substring is faster on Chrome/V8 - } - // now that we have a significantly reduced string to process, transform the newlines - // and chop them, then limit them: - var a = next.replace(/\r\n|\r/g, '\n').split('\n'); - a = a.slice(0, maxLines); - next = a.join('\n'); - // When, after limiting to maxLines, we still have too much to return, - // do add an ellipsis postfix... - if (next.length > maxSize) { - next = next.substring(0, maxSize) + '...'; - } - return next; - }, - - /** - return a string which displays the character position where the lexing error occurred, i.e. for error messages - - @public - @this {RegExpLexer} - */ - showPosition: function lexer_showPosition(maxPrefix, maxPostfix) { - var pre = this.pastInput(maxPrefix).replace(/\s/g, ' '); - var c = new Array(pre.length + 1).join('-'); - return pre + this.upcomingInput(maxPostfix).replace(/\s/g, ' ') + '\n' + c + '^'; - }, - - /** - helper function, used to produce a human readable description as a string, given - the input `yylloc` location object. - - Set `display_range_too` to TRUE to include the string character index position(s) - in the description if the `yylloc.range` is available. - - @public - @this {RegExpLexer} - */ - describeYYLLOC: function lexer_describe_yylloc(yylloc, display_range_too) { - var l1 = yylloc.first_line; - var l2 = yylloc.last_line; - var o1 = yylloc.first_column; - var o2 = yylloc.last_column; - var dl = l2 - l1; - var d_o = o2 - o1; - var rv; - if (dl === 0) { - rv = 'line ' + l1 + ', '; - if (d_o === 1) { - rv += 'column ' + o1; - } else { - rv += 'columns ' + o1 + ' .. ' + o2; - } - } else { - rv = 'lines ' + l1 + '(column ' + o1 + ') .. ' + l2 + '(column ' + o2 + ')'; - } - if (yylloc.range && display_range_too) { - var r1 = yylloc.range[0]; - var r2 = yylloc.range[1] - 1; - if (r2 === r1) { - rv += ' {String Offset: ' + r1 + '}'; - } else { - rv += ' {String Offset range: ' + r1 + ' .. ' + r2 + '}'; - } - } - return rv; - // return JSON.stringify(yylloc); - }, - - /** - test the lexed token: return FALSE when not a match, otherwise return token. - - `match` is supposed to be an array coming out of a regex match, i.e. `match[0]` - contains the actually matched text string. - - Also move the input cursor forward and update the match collectors: - - - `yytext` - - `yyleng` - - `match` - - `matches` - - `yylloc` - - `offset` - - @public - @this {RegExpLexer} - */ - test_match: function lexer_test_match(match, indexed_rule, parseParams) { - var token, - lines, - backup, - match_str, - match_str_len; - - if (this.options.backtrack_lexer) { - // save context - backup = { - yylineno: this.yylineno, - yylloc: { - first_line: this.yylloc.first_line, - last_line: this.yylloc.last_line, - first_column: this.yylloc.first_column, - last_column: this.yylloc.last_column, - - range: (this.options.ranges ? this.yylloc.range.slice(0) : undefined) - }, - yytext: this.yytext, - match: this.match, - matches: this.matches, - matched: this.matched, - yyleng: this.yyleng, - offset: this.offset, - _more: this._more, - _input: this._input, - //_signaled_error_token: this._signaled_error_token, - yy: this.yy, - conditionStack: this.conditionStack.slice(0), - done: this.done - }; - } - - match_str = match[0]; - match_str_len = match_str.length; - // if (match_str.indexOf('\n') !== -1 || match_str.indexOf('\r') !== -1) { - lines = match_str.split(/(?:\r\n?|\n)/g); - if (lines.length > 1) { - this.yylineno += lines.length - 1; - - this.yylloc.last_line = this.yylineno + 1, - this.yylloc.last_column = lines[lines.length - 1].length; - } else { - this.yylloc.last_column += match_str_len; - } - // } - this.yytext += match_str; - this.match += match_str; - this.matches = match; - this.yyleng = this.yytext.length; - if (this.options.ranges) { - this.yylloc.range[1] += match_str_len; - } - // previous lex rules MAY have invoked the `more()` API rather than producing a token: - // those rules will already have moved this `offset` forward matching their match lengths, - // hence we must only add our own match length now: - this.offset += match_str_len; - this._more = false; - this._backtrack = false; - this._input = this._input.slice(match_str_len); - this.matched += match_str; - - // calling this method: - // - // function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START) {...} - token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1] /* = YY_START */, parseParams); - // otherwise, when the action codes are all simple return token statements: - //token = this.simpleCaseActionClusters[indexed_rule]; - - if (this.done && this._input) { - this.done = false; - } - if (token) { - return token; - } else if (this._backtrack) { - // recover context - for (var k in backup) { - this[k] = backup[k]; - } - this.__currentRuleSet__ = null; - return false; // rule action called reject() implying the next rule should be tested instead. - } else if (this._signaled_error_token) { - // produce one 'error' token as `.parseError()` in `reject()` did not guarantee a failure signal by throwing an exception! - token = this._signaled_error_token; - this._signaled_error_token = false; - return token; - } - return false; - }, - - /** - return next match in input - - @public - @this {RegExpLexer} - */ - next: function lexer_next(parseParams) { - if (this.done) { - this.clear(); - return this.EOF; - } - if (!this._input) { - this.done = true; - } - - var token, - match, - tempMatch, - index; - if (!this._more) { - this.clear(); - } - var spec = this.__currentRuleSet__; - if (!spec) { - // Update the ruleset cache as we apparently encountered a state change or just started lexing. - // The cache is set up for fast lookup -- we assume a lexer will switch states much less often than it will - // invoke the `lex()` token-producing API and related APIs, hence caching the set for direct access helps - // speed up those activities a tiny bit. - spec = this.__currentRuleSet__ = this._currentRules(); - // Check whether a *sane* condition has been pushed before: this makes the lexer robust against - // user-programmer bugs such as https://github.com/zaach/jison-lex/issues/19 - if (!spec || !spec.rules) { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Internal lexer engine error' + lineno_msg + ': The lex grammar programmer pushed a non-existing condition name "' + this.topState() + '"; this is a fatal error and should be reported to the application programmer team!' + pos_str, false); - // produce one 'error' token until this situation has been resolved, most probably by parse termination! - return (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - } - } - - var rule_ids = spec.rules; - //var dispatch = spec.__dispatch_lut; - var regexes = spec.__rule_regexes; - var len = spec.__rule_count; - - // Note: the arrays are 1-based, while `len` itself is a valid index, - // hence the non-standard less-or-equal check in the next loop condition! - for (var i = 1; i <= len; i++) { - tempMatch = this._input.match(regexes[i]); - if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { - match = tempMatch; - index = i; - if (this.options.backtrack_lexer) { - token = this.test_match(tempMatch, rule_ids[i], parseParams); - if (token !== false) { - return token; - } else if (this._backtrack) { - match = undefined; - continue; // rule action called reject() implying a rule MISmatch. - } else { - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - } else if (!this.options.flex) { - break; - } - } - } - if (match) { - token = this.test_match(match, rule_ids[index], parseParams); - if (token !== false) { - return token; - } - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - if (!this._input) { - this.done = true; - this.clear(); - return this.EOF; - } else { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': Unrecognized text.' + pos_str, this.options.lexerErrorsAreRecoverable); - token = (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - if (token === this.ERROR) { - // we can try to recover from a lexer error that `parseError()` did not 'recover' for us - // by moving forward at least one character at a time: - if (!this.match.length) { - this.input(); - } - } - return token; - } - }, - - /** - return next match that has a token - - @public - @this {RegExpLexer} - */ - lex: function lexer_lex(parseParams) { - var r; - // allow the PRE/POST handlers set/modify the return token for maximum flexibility of the generated lexer: - if (typeof this.options.pre_lex === 'function') { - r = this.options.pre_lex.call(this, parseParams); - } - while (!r) { - r = this.next(parseParams); - } - if (typeof this.options.post_lex === 'function') { - // (also account for a userdef function which does not return any value: keep the token as is) - r = this.options.post_lex.call(this, r, parseParams) || r; - } - return r; - }, - - /** - backwards compatible alias for `pushState()`; - the latter is symmetrical with `popState()` and we advise to use - those APIs in any modern lexer code, rather than `begin()`. - - @public - @this {RegExpLexer} - */ - begin: function lexer_begin(condition) { - return this.pushState(condition); - }, - - /** - activates a new lexer condition state (pushes the new lexer condition state onto the condition stack) - - @public - @this {RegExpLexer} - */ - pushState: function lexer_pushState(condition) { - this.conditionStack.push(condition); - this.__currentRuleSet__ = null; - return this; - }, - - /** - pop the previously active lexer condition state off the condition stack - - @public - @this {RegExpLexer} - */ - popState: function lexer_popState() { - var n = this.conditionStack.length - 1; - if (n > 0) { - this.__currentRuleSet__ = null; - return this.conditionStack.pop(); - } else { - return this.conditionStack[0]; - } - }, - - /** - return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available - - @public - @this {RegExpLexer} - */ - topState: function lexer_topState(n) { - n = this.conditionStack.length - 1 - Math.abs(n || 0); - if (n >= 0) { - return this.conditionStack[n]; - } else { - return 'INITIAL'; - } - }, - - /** - (internal) determine the lexer rule set which is active for the currently active lexer condition state - - @public - @this {RegExpLexer} - */ - _currentRules: function lexer__currentRules() { - if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) { - return this.conditions[this.conditionStack[this.conditionStack.length - 1]]; - } else { - return this.conditions['INITIAL']; - } - }, - - /** - return the number of states currently on the stack - - @public - @this {RegExpLexer} - */ - stateStackSize: function lexer_stateStackSize() { - return this.conditionStack.length; - } - }; -} - -RegExpLexer.prototype = getRegExpLexerPrototype(); - - - - -// Fill in the optional, extra parse parameters (`%parse-param ...`) -// in the generated *lexer*. -// -// See for important context: -// -// https://github.com/zaach/jison/pull/332 -function expandParseArguments(parseFn, options) { - var arglist = (options && options.parseParams); - - if (!arglist) { - parseFn = parseFn.replace(/, parseParams\b/g, ''); - parseFn = parseFn.replace(/\bparseParams\b/g, ''); - } else { - parseFn = parseFn.replace(/, parseParams\b/g, ', ' + arglist.join(', ')); - parseFn = parseFn.replace(/\bparseParams\b/g, arglist.join(', ')); - } - return parseFn; -} - - - -// The lexer code stripper, driven by optimization analysis settings and -// lexer options, which cannot be changed at run-time. -function stripUnusedLexerCode(src, options) { - return src; -} - - - - - -// generate lexer source from a grammar -/** @public */ -function generate(dict, tokens, build_options) { - var opt = processGrammar(dict, tokens, build_options); - - return generateFromOpts(opt); -} - -// process the grammar and build final data structures and functions -/** @public */ -function processGrammar(dict, tokens, build_options) { - build_options = build_options || {}; - var opts = { - // include the knowledge passed through `build_options` about which lexer - // features will actually be *used* by the environment (which in 99.9% - // of cases is a jison *parser*): - // - // (this stuff comes straight from the jison Optimization Analysis.) - // - parseActionsAreAllDefault: build_options.parseActionsAreAllDefault, - parseActionsUseYYLENG: build_options.parseActionsUseYYLENG, - parseActionsUseYYLINENO: build_options.parseActionsUseYYLINENO, - parseActionsUseYYTEXT: build_options.parseActionsUseYYTEXT, - parseActionsUseYYLOC: build_options.parseActionsUseYYLOC, - parseActionsUseParseError: build_options.parseActionsUseParseError, - parseActionsUseYYERROR: build_options.parseActionsUseYYERROR, - parseActionsUseYYERROK: build_options.parseActionsUseYYERROK, - parseActionsUseYYCLEARIN: build_options.parseActionsUseYYCLEARIN, - parseActionsUseValueTracking: build_options.parseActionsUseValueTracking, - parseActionsUseValueAssignment: build_options.parseActionsUseValueAssignment, - parseActionsUseLocationTracking: build_options.parseActionsUseLocationTracking, - parseActionsUseLocationAssignment: build_options.parseActionsUseLocationAssignment, - parseActionsUseYYSTACK: build_options.parseActionsUseYYSTACK, - parseActionsUseYYSSTACK: build_options.parseActionsUseYYSSTACK, - parseActionsUseYYSTACKPOINTER: build_options.parseActionsUseYYSTACKPOINTER, - parserHasErrorRecovery: build_options.parserHasErrorRecovery, - }; - - dict = autodetectAndConvertToJSONformat(dict, build_options) || {}; - - // Feed the possibly reprocessed 'dictionary' above back to the caller - // (for use by our error diagnostic assistance code) - opts.lex_rule_dictionary = dict; - - // Always provide the lexer with an options object, even if it's empty! - // Make sure to camelCase all options: - opts.options = mkStdOptions(build_options, dict.options); - - opts.parseParams = opts.options.parseParams; - - opts.moduleType = opts.options.moduleType; - opts.moduleName = opts.options.moduleName; - - opts.conditions = prepareStartConditions(dict.startConditions); - opts.conditions.INITIAL = { - rules: [], - inclusive: true - }; - - var code = buildActions(dict, tokens, opts); - opts.performAction = code.actions; - opts.caseHelperInclude = code.caseHelperInclude; - opts.rules = code.rules; - opts.macros = code.macros; - - opts.conditionStack = ['INITIAL']; - - opts.actionInclude = (dict.actionInclude || ''); - opts.moduleInclude = (opts.moduleInclude || '') + (dict.moduleInclude || '').trim(); - - return opts; -} - -// Assemble the final source from the processed grammar -/** @public */ -function generateFromOpts(opt) { - var code = ''; - - switch (opt.moduleType) { - case 'js': - code = generateModule(opt); - break; - case 'amd': - code = generateAMDModule(opt); - break; - case 'es': - code = generateESModule(opt); - break; - case 'commonjs': - default: - code = generateCommonJSModule(opt); - break; - } - - return code; -} - -function generateRegexesInitTableCode(opt) { - var a = opt.rules; - var print_xregexp = opt.options && opt.options.xregexp; - a = a.map(function generateXRegExpInitCode(re) { - if (re instanceof XRegExp) { - // When we don't need the special XRegExp sauce at run-time, we do with the original - // JavaScript RegExp instance a.k.a. 'native regex': - if (re.xregexp.isNative || !print_xregexp) { - return re; - } - // And make sure to escape the regex to make it suitable for placement inside a *string* - // as it is passed as a string argument to the XRegExp constructor here. - return 'new XRegExp("' + re.xregexp.source.replace(/[\\"]/g, '\\$&') + '", "' + re.xregexp.flags + '")'; - } else { - return re; - } - }); - return a.join(',\n'); -} - -function generateModuleBody(opt) { - // make the JSON output look more like JavaScript: - function cleanupJSON(str) { - str = str.replace(/ "rules": \[/g, ' rules: ['); - str = str.replace(/ "inclusive": /g, ' inclusive: '); - return str; - } - - function produceOptions(opts) { - var obj = {}; - var do_not_pass = { - debug: !opts.debug, // do not include this item when it is FALSE as there's no debug tracing built into the generated grammar anyway! - enableDebugLogs: 1, - json: 1, - _: 1, - noMain: 1, - dumpSourceCodeOnFailure: 1, - throwErrorOnCompileFailure: 1, - reportStats: 1, - file: 1, - outfile: 1, - inputPath: 1, - inputFilename: 1, - defaultModuleName: 1, - moduleName: 1, - moduleType: 1, - lexerErrorsAreRecoverable: 0, - flex: 0, - backtrack_lexer: 0, - caseInsensitive: 0, - showSource: 1, - parseActionsAreAllDefault: 1, - parseActionsUseYYLENG: 1, - parseActionsUseYYLINENO: 1, - parseActionsUseYYTEXT: 1, - parseActionsUseYYLOC: 1, - parseActionsUseParseError: 1, - parseActionsUseYYERROR: 1, - parseActionsUseYYERROK: 1, - parseActionsUseYYCLEARIN: 1, - parseActionsUseValueTracking: 1, - parseActionsUseValueAssignment: 1, - parseActionsUseLocationTracking: 1, - parseActionsUseLocationAssignment: 1, - parseActionsUseYYSTACK: 1, - parseActionsUseYYSSTACK: 1, - parseActionsUseYYSTACKPOINTER: 1, - parserHasErrorRecovery: 1, - }; - for (var k in opts) { - if (!do_not_pass[k] && opts[k] != null && opts[k] !== false) { - // make sure numeric values are encoded as numeric, the rest as boolean/string. - if (typeof opts[k] === 'string') { - var f = parseFloat(opts[k]); - if (f == opts[k]) { - obj[k] = f; - continue; - } - } - obj[k] = opts[k]; - } - } - - // And now some options which should receive some special processing: - var pre = obj.pre_lex; - var post = obj.post_lex; - // since JSON cannot encode functions, we'll have to do it manually at run-time, i.e. later on: - if (pre) { - obj.pre_lex = true; - } - if (post) { - obj.post_lex = true; - } - - var js = JSON.stringify(obj, null, 2); - - js = js.replace(new XRegExp(' "([\\p{Alphabetic}_][\\p{Alphabetic}_\\p{Number}]*)": ', 'g'), ' $1: '); - js = js.replace(/^( +)pre_lex: true(,)?$/gm, function (m, ls, tc) { - return ls + 'pre_lex: ' + String(pre) + (tc || ''); - }); - js = js.replace(/^( +)post_lex: true(,)?$/gm, function (m, ls, tc) { - return ls + 'post_lex: ' + String(post) + (tc || ''); - }); - return js; - } - - - var out; - if (opt.rules.length > 0 || opt.__in_rules_failure_analysis_mode__) { - var descr; - - // we don't mind that the `test_me()` code above will have this `lexer` variable re-defined: - // JavaScript is fine with that. - var code = [` -var lexer = { -`, '' /* slot #1: placeholder for analysis report further below */]; - - // get the RegExpLexer.prototype in source code form: - var protosrc = printFunctionSourceCodeContainer(getRegExpLexerPrototype, 1); - // and strip off the surrounding bits we don't want: - protosrc = protosrc - .replace(/^[\s\r\n]*return[\s\r\n]*\{/, '') - .replace(/\s*\};[\s\r\n]*$/, ''); - protosrc = expandParseArguments(protosrc, opt.options); - protosrc = stripUnusedLexerCode(protosrc, opt); - code.push(protosrc + ',\n'); - - assert(opt.options); - // Assure all options are camelCased: - assert(typeof opt.options['case-insensitive'] === 'undefined'); - - code.push(' options: ' + produceOptions(opt.options)); - - var performActionCode = String(opt.performAction); - var simpleCaseActionClustersCode = String(opt.caseHelperInclude); - var rulesCode = generateRegexesInitTableCode(opt); - var conditionsCode = cleanupJSON(JSON.stringify(opt.conditions, null, 2)); - code.push(`, - JisonLexerError: JisonLexerError, - performAction: ${performActionCode}, - simpleCaseActionClusters: ${simpleCaseActionClustersCode}, - rules: [ - ${rulesCode} - ], - conditions: ${conditionsCode} -}; -`); - - // inject analysis report now: - code[1] = ` - // Code Generator Information Report - // --------------------------------- - // - // Options: - // - // backtracking: .................... ${opt.options.backtrack_lexer} - // location.ranges: ................. ${opt.options.ranges} - // location line+column tracking: ... ${opt.options.trackPosition} - // - // - // Forwarded Parser Analysis flags: - // - // uses yyleng: ..................... ${opt.parseActionsUseYYLENG} - // uses yylineno: ................... ${opt.parseActionsUseYYLINENO} - // uses yytext: ..................... ${opt.parseActionsUseYYTEXT} - // uses yylloc: ..................... ${opt.parseActionsUseYYLOC} - // uses lexer values: ............... ${opt.parseActionsUseValueTracking} / ${opt.parseActionsUseValueAssignment} - // location tracking: ............... ${opt.parseActionsUseLocationTracking} - // location assignment: ............. ${opt.parseActionsUseLocationAssignment} - // - // - // Lexer Analysis flags: - // - // uses yyleng: ..................... ${opt.lexerActionsUseYYLENG} - // uses yylineno: ................... ${opt.lexerActionsUseYYLINENO} - // uses yytext: ..................... ${opt.lexerActionsUseYYTEXT} - // uses yylloc: ..................... ${opt.lexerActionsUseYYLOC} - // uses ParseError API: ............. ${opt.lexerActionsUseParseError} - // uses location tracking & editing: ${opt.lexerActionsUseLocationTracking} - // uses more() API: ................. ${opt.lexerActionsUseMore} - // uses unput() API: ................ ${opt.lexerActionsUseUnput} - // uses reject() API: ............... ${opt.lexerActionsUseReject} - // uses less() API: ................. ${opt.lexerActionsUseLess} - // uses display APIs pastInput(), upcomingInput(), showPosition(): - // ............................. ${opt.lexerActionsUseDisplayAPIs} - // uses describeYYLLOC() API: ....... ${opt.lexerActionsUseDescribeYYLOC} - // - // --------- END OF REPORT ----------- - -`; - - out = code.join(''); - } else { - // We're clearly looking at a custom lexer here as there's no lexer rules at all. - // - // We are re-purposing the `%{...%}` `actionInclude` code block here as it serves no purpose otherwise. - // - // Meanwhile we make sure we have the `lexer` variable declared in *local scope* no matter - // what crazy stuff (or lack thereof) the userland code is pulling in the `actionInclude` chunk. - out = 'var lexer;\n'; - - if (opt.actionInclude) { - out += opt.actionInclude + (!opt.actionInclude.match(/;[\s\r\n]*$/) ? ';' : '') + '\n'; - } - } - - // The output of this function is guaranteed to read something like this: - // - // ``` - // var lexer; - // - // bla bla bla bla ... lotsa bla bla; - // ``` - // - // and that should work nicely as an `eval()`-able piece of source code. - return out; -} - -function generateGenericHeaderComment() { - var out = '/* lexer generated by jison-lex ' + version + ' */\n' - + '/*\n' - + ' * Returns a Lexer object of the following structure:\n' - + ' *\n' - + ' * Lexer: {\n' - + ' * yy: {} The so-called "shared state" or rather the *source* of it;\n' - + ' * the real "shared state" `yy` passed around to\n' - + ' * the rule actions, etc. is a derivative/copy of this one,\n' - + ' * not a direct reference!\n' - + ' * }\n' - + ' *\n' - + ' * Lexer.prototype: {\n' - + ' * yy: {},\n' - + ' * EOF: 1,\n' - + ' * ERROR: 2,\n' - + ' *\n' - + ' * JisonLexerError: function(msg, hash),\n' - + ' *\n' - + ' * performAction: function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START, ...),\n' - + ' * where `...` denotes the (optional) additional arguments the user passed to\n' - + ' * `lexer.lex(...)` and specified by way of `%parse-param ...` in the **parser** grammar file\n' - + ' *\n' - + ' * The function parameters and `this` have the following value/meaning:\n' - + ' * - `this` : reference to the `lexer` instance.\n' - + ' *\n' - + ' * - `yy` : a reference to the `yy` "shared state" object which was passed to the lexer\n' - + ' * by way of the `lexer.setInput(str, yy)` API before.\n' - + ' *\n' - + ' * - `yy_` : lexer instance reference used internally.\n' - + ' *\n' - + ' * - `$avoiding_name_collisions` : index of the matched lexer rule (regex), used internally.\n' - + ' *\n' - + ' * - `YY_START`: the current lexer "start condition" state.\n' - + ' *\n' - + ' * - `...` : the extra arguments you specified in the `%parse-param` statement in your\n' - + ' * **parser** grammar definition file and which are passed to the lexer via\n' - + ' * its `lexer.lex(...)` API.\n' - + ' *\n' - + ' * parseError: function(str, hash, ExceptionClass),\n' - + ' *\n' - + ' * constructLexErrorInfo: function(error_message, is_recoverable),\n' - + ' * Helper function.\n' - + ' * Produces a new errorInfo \'hash object\' which can be passed into `parseError()`.\n' - + ' * See it\'s use in this lexer kernel in many places; example usage:\n' - + ' *\n' - + ' * var infoObj = lexer.constructParseErrorInfo(\'fail!\', true);\n' - + ' * var retVal = lexer.parseError(infoObj.errStr, infoObj, lexer.JisonLexerError);\n' - + ' *\n' - + ' * options: { ... lexer %options ... },\n' - + ' *\n' - + ' * lex: function([args...]),\n' - + ' * Produce one token of lexed input, which was passed in earlier via the `lexer.setInput()` API.\n' - + ' * You MAY use the additional `args...` parameters as per `%parse-param` spec of the **parser** grammar:\n' - + ' * these extra `args...` are passed verbatim to the lexer rules\' action code.\n' - + ' *\n' - + ' * cleanupAfterLex: function(do_not_nuke_errorinfos),\n' - + ' * Helper function.\n' - + ' * This helper API is invoked when the parse process has completed. This helper may\n' - + ' * be invoked by user code to ensure the internal lexer gets properly garbage collected.\n' - + ' *\n' - + ' * setInput: function(input, [yy]),\n' - + ' * input: function(),\n' - + ' * unput: function(str),\n' - + ' * more: function(),\n' - + ' * reject: function(),\n' - + ' * less: function(n),\n' - + ' * pastInput: function(n),\n' - + ' * upcomingInput: function(n),\n' - + ' * showPosition: function(),\n' - + ' * test_match: function(regex_match_array, rule_index),\n' - + ' * next: function(...),\n' - + ' * lex: function(...),\n' - + ' * begin: function(condition),\n' - + ' * pushState: function(condition),\n' - + ' * popState: function(),\n' - + ' * topState: function(),\n' - + ' * _currentRules: function(),\n' - + ' * stateStackSize: function(),\n' - + ' *\n' - + ' * options: { ... lexer %options ... },\n' - + ' *\n' - + ' * performAction: function(yy, yy_, $avoiding_name_collisions, YY_START, ...),\n' - + ' * rules: [...],\n' - + ' * conditions: {associative list: name ==> set},\n' - + ' * }\n' - + ' *\n' - + ' *\n' - + ' * token location info (`yylloc`): {\n' - + ' * first_line: n,\n' - + ' * last_line: n,\n' - + ' * first_column: n,\n' - + ' * last_column: n,\n' - + ' * range: [start_number, end_number]\n' - + ' * (where the numbers are indexes into the input string, zero-based)\n' - + ' * }\n' - + ' *\n' - + ' * ---\n' - + ' *\n' - + ' * The `parseError` function receives a \'hash\' object with these members for lexer errors:\n' - + ' *\n' - + ' * {\n' - + ' * text: (matched text)\n' - + ' * token: (the produced terminal token, if any)\n' - + ' * token_id: (the produced terminal token numeric ID, if any)\n' - + ' * line: (yylineno)\n' - + ' * loc: (yylloc)\n' - + ' * recoverable: (boolean: TRUE when the parser MAY have an error recovery rule\n' - + ' * available for this particular error)\n' - + ' * yy: (object: the current parser internal "shared state" `yy`\n' - + ' * as is also available in the rule actions; this can be used,\n' - + ' * for instance, for advanced error analysis and reporting)\n' - + ' * lexer: (reference to the current lexer instance used by the parser)\n' - + ' * }\n' - + ' *\n' - + ' * while `this` will reference the current lexer instance.\n' - + ' *\n' - + ' * When `parseError` is invoked by the lexer, the default implementation will\n' - + ' * attempt to invoke `yy.parser.parseError()`; when this callback is not provided\n' - + ' * it will try to invoke `yy.parseError()` instead. When that callback is also not\n' - + ' * provided, a `JisonLexerError` exception will be thrown containing the error\n' - + ' * message and `hash`, as constructed by the `constructLexErrorInfo()` API.\n' - + ' *\n' - + ' * Note that the lexer\'s `JisonLexerError` error class is passed via the\n' - + ' * `ExceptionClass` argument, which is invoked to construct the exception\n' - + ' * instance to be thrown, so technically `parseError` will throw the object\n' - + ' * produced by the `new ExceptionClass(str, hash)` JavaScript expression.\n' - + ' *\n' - + ' * ---\n' - + ' *\n' - + ' * You can specify lexer options by setting / modifying the `.options` object of your Lexer instance.\n' - + ' * These options are available:\n' - + ' *\n' - + ' * (Options are permanent.)\n' - + ' * \n' - + ' * yy: {\n' - + ' * parseError: function(str, hash, ExceptionClass)\n' - + ' * optional: overrides the default `parseError` function.\n' - + ' * }\n' - + ' *\n' - + ' * lexer.options: {\n' - + ' * pre_lex: function()\n' - + ' * optional: is invoked before the lexer is invoked to produce another token.\n' - + ' * `this` refers to the Lexer object.\n' - + ' * post_lex: function(token) { return token; }\n' - + ' * optional: is invoked when the lexer has produced a token `token`;\n' - + ' * this function can override the returned token value by returning another.\n' - + ' * When it does not return any (truthy) value, the lexer will return\n' - + ' * the original `token`.\n' - + ' * `this` refers to the Lexer object.\n' - + ' *\n' - + ' * WARNING: the next set of options are not meant to be changed. They echo the abilities of\n' - + ' * the lexer as per when it was compiled!\n' - + ' *\n' - + ' * ranges: boolean\n' - + ' * optional: `true` ==> token location info will include a .range[] member.\n' - + ' * flex: boolean\n' - + ' * optional: `true` ==> flex-like lexing behaviour where the rules are tested\n' - + ' * exhaustively to find the longest match.\n' - + ' * backtrack_lexer: boolean\n' - + ' * optional: `true` ==> lexer regexes are tested in order and for invoked;\n' - + ' * the lexer terminates the scan when a token is returned by the action code.\n' - + ' * xregexp: boolean\n' - + ' * optional: `true` ==> lexer rule regexes are "extended regex format" requiring the\n' - + ' * `XRegExp` library. When this %option has not been specified at compile time, all lexer\n' - + ' * rule regexes have been written as standard JavaScript RegExp expressions.\n' - + ' * }\n' - + ' */\n'; - - return out; -} - -function prepareOptions(opt) { - opt = opt || {}; - - // check for illegal identifier - if (!opt.moduleName || !opt.moduleName.match(/^[a-zA-Z_$][a-zA-Z0-9_$\.]*$/)) { - if (opt.moduleName) { - var msg = 'WARNING: The specified moduleName "' + opt.moduleName + '" is illegal (only characters [a-zA-Z0-9_$] and "." dot are accepted); using the default moduleName "lexer" instead.'; - if (typeof opt.warn_cb === 'function') { - opt.warn_cb(msg); - } else { - // do not treat as warning; barf hairball instead so that this oddity gets noticed right away! - throw new Error(msg); - } - } - opt.moduleName = 'lexer'; - } - return opt; -} - -function generateModule(opt) { - opt = prepareOptions(opt); - - var out = [ - generateGenericHeaderComment(), - '', - 'var ' + opt.moduleName + ' = (function () {', - jisonLexerErrorDefinition, - '', - generateModuleBody(opt), - '', - (opt.moduleInclude ? opt.moduleInclude + ';' : ''), - '', - 'return lexer;', - '})();' - ]; - - return out.join('\n'); -} - -function generateAMDModule(opt) { - opt = prepareOptions(opt); - - var out = [ - generateGenericHeaderComment(), - '', - 'define([], function () {', - jisonLexerErrorDefinition, - '', - generateModuleBody(opt), - '', - (opt.moduleInclude ? opt.moduleInclude + ';' : ''), - '', - 'return lexer;', - '});' - ]; - - return out.join('\n'); -} - -function generateESModule(opt) { - opt = prepareOptions(opt); - - var out = [ - generateGenericHeaderComment(), - '', - 'var lexer = (function () {', - jisonLexerErrorDefinition, - '', - generateModuleBody(opt), - '', - (opt.moduleInclude ? opt.moduleInclude + ';' : ''), - '', - 'return lexer;', - '})();', - '', - 'export {lexer};' - ]; - - return out.join('\n'); -} - -function generateCommonJSModule(opt) { - opt = prepareOptions(opt); - - var out = [ - generateGenericHeaderComment(), - '', - 'var ' + opt.moduleName + ' = (function () {', - jisonLexerErrorDefinition, - '', - generateModuleBody(opt), - '', - (opt.moduleInclude ? opt.moduleInclude + ';' : ''), - '', - 'return lexer;', - '})();', - '', - 'if (typeof require !== \'undefined\' && typeof exports !== \'undefined\') {', - ' exports.lexer = ' + opt.moduleName + ';', - ' exports.lex = function () {', - ' return ' + opt.moduleName + '.lex.apply(lexer, arguments);', - ' };', - '}' - ]; - - return out.join('\n'); -} - -RegExpLexer.generate = generate; - -RegExpLexer.defaultJisonLexOptions = defaultJisonLexOptions; -RegExpLexer.mkStdOptions = mkStdOptions; -RegExpLexer.camelCase = camelCase; -RegExpLexer.printFunctionSourceCode = printFunctionSourceCode; -RegExpLexer.printFunctionSourceCodeContainer = printFunctionSourceCodeContainer; -RegExpLexer.autodetectAndConvertToJSONformat = autodetectAndConvertToJSONformat; - -module.exports = RegExpLexer; - diff --git a/lib/util/regexp-set-management.js b/lib/util/regexp-set-management.js deleted file mode 100644 index bd9e0326c..000000000 --- a/lib/util/regexp-set-management.js +++ /dev/null @@ -1,999 +0,0 @@ -// -// Helper library for set definitions -// -// MIT Licensed -// -// -// This code is intended to help parse regex set expressions and mix them -// together, i.e. to answer questions like this: -// -// what is the resulting regex set expression when we mix the regex set -// `[a-z]` with the regex set `[^\s]` where with 'mix' we mean that any -// input which matches either input regex should match the resulting -// regex set. (a.k.a. Full Outer Join, see also http://www.diffen.com/difference/Inner_Join_vs_Outer_Join) -// - -'use strict'; - -var XRegExp = require('xregexp'); -var assert = require('assert'); - - - - -const XREGEXP_UNICODE_ESCAPE_RE = /^\{[A-Za-z0-9 \-\._]+\}/; // Matches the XRegExp Unicode escape braced part, e.g. `{Number}` -const CHR_RE = /^(?:[^\\]|\\[^cxu0-9]|\\[0-9]{1,3}|\\c[A-Z]|\\x[0-9a-fA-F]{2}|\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]+\})/; -const SET_PART_RE = /^(?:[^\\\]]|\\[^cxu0-9]|\\[0-9]{1,3}|\\c[A-Z]|\\x[0-9a-fA-F]{2}|\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]+\})+/; -const NOTHING_SPECIAL_RE = /^(?:[^\\\[\]\(\)\|^\{\}]|\\[^cxu0-9]|\\[0-9]{1,3}|\\c[A-Z]|\\x[0-9a-fA-F]{2}|\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]+\})+/; -const SET_IS_SINGLE_PCODE_RE = /^\\[dDwWsS]$|^\\p\{[A-Za-z0-9 \-\._]+\}$/; - -const UNICODE_BASE_PLANE_MAX_CP = 65535; - -// The expanded regex sets which are equivalent to the given `\\{c}` escapes: -// -// `/\s/`: -const WHITESPACE_SETSTR = ' \f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff'; -// `/\d/`: -const DIGIT_SETSTR = '0-9'; -// `/\w/`: -const WORDCHAR_SETSTR = 'A-Za-z0-9_'; - - - - - -// Helper for `bitarray2set()`: convert character code to a representation string suitable for use in a regex -function i2c(i) { - var c, x; - - switch (i) { - case 10: - return '\\n'; - - case 13: - return '\\r'; - - case 9: - return '\\t'; - - case 8: - return '\\b'; - - case 12: - return '\\f'; - - case 11: - return '\\v'; - - case 45: // ASCII/Unicode for '-' dash - return '\\-'; - - case 91: // '[' - return '\\['; - - case 92: // '\\' - return '\\\\'; - - case 93: // ']' - return '\\]'; - - case 94: // ']' - return '\\^'; - } - if (i < 32 - || i > 0xFFF0 /* Unicode Specials, also in UTF16 */ - || (i >= 0xD800 && i <= 0xDFFF) /* Unicode Supplementary Planes; we're TOAST in JavaScript as we're NOT UTF-16 but UCS-2! */ - || String.fromCharCode(i).match(/[\u2028\u2029]/) /* Code compilation via `new Function()` does not like to see these, or rather: treats them as just another form of CRLF, which breaks your generated regex code! */ - ) { - // Detail about a detail: - // U+2028 and U+2029 are part of the `\s` regex escape code (`\s` and `[\s]` match either of these) and when placed in a JavaScript - // source file verbatim (without escaping it as a `\uNNNN` item) then JavaScript will interpret it as such and consequently report - // a b0rked generated parser, as the generated code would include this regex right here. - // Hence we MUST escape these buggers everywhere we go... - x = i.toString(16); - if (x.length >= 1 && i <= 0xFFFF) { - c = '0000' + x; - return '\\u' + c.substr(c.length - 4); - } else { - return '\\u{' + x + '}'; - } - } - return String.fromCharCode(i); -} - - -// Helper collection for `bitarray2set()`: we have expanded all these cached `\\p{NAME}` regex sets when creating -// this bitarray and now we should look at these expansions again to see if `bitarray2set()` can produce a -// `\\p{NAME}` shorthand to represent [part of] the bitarray: -var Pcodes_bitarray_cache = {}; -var Pcodes_bitarray_cache_test_order = []; - -// Helper collection for `bitarray2set()` for minifying special cases of result sets which can be represented by -// a single regex 'escape', e.g. `\d` for digits 0-9. -var EscCode_bitarray_output_refs; - -// now initialize the EscCodes_... table above: -init_EscCode_lookup_table(); - -function init_EscCode_lookup_table() { - var s, bitarr, set2esc = {}, esc2bitarr = {}; - - // patch global lookup tables for the time being, while we calculate their *real* content in this function: - EscCode_bitarray_output_refs = { - esc2bitarr: {}, - set2esc: {} - }; - Pcodes_bitarray_cache_test_order = []; - - // `/\S': - bitarr = []; - set2bitarray(bitarr, '^' + WHITESPACE_SETSTR); - s = bitarray2set(bitarr); - esc2bitarr['S'] = bitarr; - set2esc[s] = 'S'; - // set2esc['^' + s] = 's'; - Pcodes_bitarray_cache['\\S'] = bitarr; - - // `/\s': - bitarr = []; - set2bitarray(bitarr, WHITESPACE_SETSTR); - s = bitarray2set(bitarr); - esc2bitarr['s'] = bitarr; - set2esc[s] = 's'; - // set2esc['^' + s] = 'S'; - Pcodes_bitarray_cache['\\s'] = bitarr; - - // `/\D': - bitarr = []; - set2bitarray(bitarr, '^' + DIGIT_SETSTR); - s = bitarray2set(bitarr); - esc2bitarr['D'] = bitarr; - set2esc[s] = 'D'; - // set2esc['^' + s] = 'd'; - Pcodes_bitarray_cache['\\D'] = bitarr; - - // `/\d': - bitarr = []; - set2bitarray(bitarr, DIGIT_SETSTR); - s = bitarray2set(bitarr); - esc2bitarr['d'] = bitarr; - set2esc[s] = 'd'; - // set2esc['^' + s] = 'D'; - Pcodes_bitarray_cache['\\d'] = bitarr; - - // `/\W': - bitarr = []; - set2bitarray(bitarr, '^' + WORDCHAR_SETSTR); - s = bitarray2set(bitarr); - esc2bitarr['W'] = bitarr; - set2esc[s] = 'W'; - // set2esc['^' + s] = 'w'; - Pcodes_bitarray_cache['\\W'] = bitarr; - - // `/\w': - bitarr = []; - set2bitarray(bitarr, WORDCHAR_SETSTR); - s = bitarray2set(bitarr); - esc2bitarr['w'] = bitarr; - set2esc[s] = 'w'; - // set2esc['^' + s] = 'W'; - Pcodes_bitarray_cache['\\w'] = bitarr; - - EscCode_bitarray_output_refs = { - esc2bitarr: esc2bitarr, - set2esc: set2esc - }; - - updatePcodesBitarrayCacheTestOrder(); -} - -function updatePcodesBitarrayCacheTestOrder(opts) { - var t = new Array(UNICODE_BASE_PLANE_MAX_CP + 1); - var l = {}; - var user_has_xregexp = opts && opts.options && opts.options.xregexp; - var i, j, k, ba; - - // mark every character with which regex pcodes they are part of: - for (k in Pcodes_bitarray_cache) { - ba = Pcodes_bitarray_cache[k]; - - if (!user_has_xregexp && k.indexOf('\\p{') >= 0) { - continue; - } - - var cnt = 0; - for (i = 0; i <= UNICODE_BASE_PLANE_MAX_CP; i++) { - if (ba[i]) { - cnt++; - if (!t[i]) { - t[i] = [k]; - } else { - t[i].push(k); - } - } - } - l[k] = cnt; - } - - // now dig out the unique ones: only need one per pcode. - // - // We ASSUME every \\p{NAME} 'pcode' has at least ONE character - // in it that is ONLY matched by that particular pcode. - // If this assumption fails, nothing is lost, but our 'regex set - // optimized representation' will be sub-optimal as than this pcode - // won't be tested during optimization. - // - // Now that would be a pity, so the assumption better holds... - // Turns out the assumption doesn't hold already for /\S/ + /\D/ - // as the second one (\D) is a pure subset of \S. So we have to - // look for markers which match multiple escapes/pcodes for those - // ones where a unique item isn't available... - var lut = []; - var done = {}; - var keys = Object.keys(Pcodes_bitarray_cache); - - for (i = 0; i <= UNICODE_BASE_PLANE_MAX_CP; i++) { - k = t[i][0]; - if (t[i].length === 1 && !done[k]) { - assert(l[k] > 0); - lut.push([i, k]); - done[k] = true; - } - } - - for (j = 0; keys[j]; j++) { - k = keys[j]; - - if (!user_has_xregexp && k.indexOf('\\p{') >= 0) { - continue; - } - - if (!done[k]) { - assert(l[k] > 0); - // find a minimum span character to mark this one: - var w = Infinity; - var rv; - ba = Pcodes_bitarray_cache[k]; - for (i = 0; i <= UNICODE_BASE_PLANE_MAX_CP; i++) { - if (ba[i]) { - var tl = t[i].length; - if (tl > 1 && tl < w) { - assert(l[k] > 0); - rv = [i, k]; - w = tl; - } - } - } - if (rv) { - done[k] = true; - lut.push(rv); - } - } - } - - // order from large set to small set so that small sets don't gobble - // characters also represented by overlapping larger set pcodes. - // - // Again we assume something: that finding the large regex pcode sets - // before the smaller, more specialized ones, will produce a more - // optimal minification of the regex set expression. - // - // This is a guestimate/heuristic only! - lut.sort(function (a, b) { - var k1 = a[1]; - var k2 = b[1]; - var ld = l[k2] - l[k1]; - if (ld) { - return ld; - } - // and for same-size sets, order from high to low unique identifier. - return b[0] - a[0]; - }); - - Pcodes_bitarray_cache_test_order = lut; -} - - - - - - -// 'Join' a regex set `[...]` into a Unicode range spanning logic array, flagging every character in the given set. -function set2bitarray(bitarr, s, opts) { - var orig = s; - var set_is_inverted = false; - var bitarr_orig; - - function mark(d1, d2) { - if (d2 == null) d2 = d1; - for (var i = d1; i <= d2; i++) { - bitarr[i] = true; - } - } - - function add2bitarray(dst, src) { - for (var i = 0; i <= UNICODE_BASE_PLANE_MAX_CP; i++) { - if (src[i]) { - dst[i] = true; - } - } - } - - function eval_escaped_code(s) { - var c; - // decode escaped code? If none, just take the character as-is - if (s.indexOf('\\') === 0) { - var l = s.substr(0, 2); - switch (l) { - case '\\c': - c = s.charCodeAt(2) - 'A'.charCodeAt(0) + 1; - return String.fromCharCode(c); - - case '\\x': - s = s.substr(2); - c = parseInt(s, 16); - return String.fromCharCode(c); - - case '\\u': - s = s.substr(2); - if (s[0] === '{') { - s = s.substr(1, s.length - 2); - } - c = parseInt(s, 16); - if (c >= 0x10000) { - return new Error('We do NOT support Extended Plane Unicode Codepoints (i.e. CodePoints beyond U:FFFF) in regex set expressions, e.g. \\u{' + s + '}'); - } - return String.fromCharCode(c); - - case '\\0': - case '\\1': - case '\\2': - case '\\3': - case '\\4': - case '\\5': - case '\\6': - case '\\7': - s = s.substr(1); - c = parseInt(s, 8); - return String.fromCharCode(c); - - case '\\r': - return '\r'; - - case '\\n': - return '\n'; - - case '\\v': - return '\v'; - - case '\\f': - return '\f'; - - case '\\t': - return '\t'; - - case '\\b': - return '\b'; - - default: - // just the character itself: - return s.substr(1); - } - } else { - return s; - } - } - - if (s && s.length) { - var c1, c2; - - // inverted set? - if (s[0] === '^') { - set_is_inverted = true; - s = s.substr(1); - bitarr_orig = bitarr; - bitarr = new Array(UNICODE_BASE_PLANE_MAX_CP + 1); - } - - // BITARR collects flags for characters set. Inversion means the complement set of character is st instead. - // This results in an OR operations when sets are joined/chained. - - while (s.length) { - c1 = s.match(CHR_RE); - if (!c1) { - // hit an illegal escape sequence? cope anyway! - c1 = s[0]; - } else { - c1 = c1[0]; - // Quick hack for XRegExp escapes inside a regex `[...]` set definition: we *could* try to keep those - // intact but it's easier to unfold them here; this is not nice for when the grammar specifies explicit - // XRegExp support, but alas, we'll get there when we get there... ;-) - switch (c1) { - case '\\p': - s = s.substr(c1.length); - c2 = s.match(XREGEXP_UNICODE_ESCAPE_RE); - if (c2) { - c2 = c2[0]; - s = s.substr(c2.length); - // do we have this one cached already? - var pex = c1 + c2; - var ba4p = Pcodes_bitarray_cache[pex]; - if (!ba4p) { - // expand escape: - var xr = new XRegExp('[' + pex + ']'); // TODO: case-insensitive grammar??? - // rewrite to a standard `[...]` regex set: XRegExp will do this for us via `XRegExp.toString()`: - var xs = '' + xr; - // remove the wrapping `/.../` to get at the (possibly *combined* series of) `[...]` sets inside: - xs = xs.substr(1, xs.length - 2); - - ba4p = reduceRegexToSetBitArray(xs, pex, opts); - - Pcodes_bitarray_cache[pex] = ba4p; - updatePcodesBitarrayCacheTestOrder(opts); - } - // merge bitarrays: - add2bitarray(bitarr, ba4p); - continue; - } - break; - - case '\\S': - case '\\s': - case '\\W': - case '\\w': - case '\\d': - case '\\D': - // these can't participate in a range, but need to be treated special: - s = s.substr(c1.length); - // check for \S, \s, \D, \d, \W, \w and expand them: - var ba4e = EscCode_bitarray_output_refs.esc2bitarr[c1[1]]; - assert(ba4e); - add2bitarray(bitarr, ba4e); - continue; - - case '\\b': - // matches a backspace: https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions#special-backspace - c1 = '\u0008'; - break; - } - } - var v1 = eval_escaped_code(c1); - // propagate deferred exceptions = error reports. - if (v1 instanceof Error) { - return v1; - } - v1 = v1.charCodeAt(0); - s = s.substr(c1.length); - - if (s[0] === '-' && s.length >= 2) { - // we can expect a range like 'a-z': - s = s.substr(1); - c2 = s.match(CHR_RE); - if (!c2) { - // hit an illegal escape sequence? cope anyway! - c2 = s[0]; - } else { - c2 = c2[0]; - } - var v2 = eval_escaped_code(c2); - // propagate deferred exceptions = error reports. - if (v2 instanceof Error) { - return v1; - } - v2 = v2.charCodeAt(0); - s = s.substr(c2.length); - - // legal ranges go UP, not /DOWN! - if (v1 <= v2) { - mark(v1, v2); - } else { - console.warn('INVALID CHARACTER RANGE found in regex: ', { re: orig, start: c1, start_n: v1, end: c2, end_n: v2 }); - mark(v1); - mark('-'.charCodeAt(0)); - mark(v2); - } - continue; - } - mark(v1); - } - - // When we have marked all slots, '^' NEGATES the set, hence we flip all slots. - // - // Since a regex like `[^]` should match everything(?really?), we don't need to check if the MARK - // phase actually marked anything at all: the `^` negation will correctly flip=mark the entire - // range then. - if (set_is_inverted) { - for (var i = 0; i <= UNICODE_BASE_PLANE_MAX_CP; i++) { - if (!bitarr[i]) { - bitarr_orig[i] = true; - } - } - } - } - return false; -} - - -// convert a simple bitarray back into a regex set `[...]` content: -function bitarray2set(l, output_inverted_variant, output_minimized) { - // construct the inverse(?) set from the mark-set: - // - // Before we do that, we inject a sentinel so that our inner loops - // below can be simple and fast: - l[UNICODE_BASE_PLANE_MAX_CP + 1] = 1; - // now reconstruct the regex set: - var rv = []; - var i, j, cnt, lut, tn, tspec, match, pcode, ba4pcode, l2; - var bitarr_is_cloned = false; - var l_orig = l; - - if (output_inverted_variant) { - // generate the inverted set, hence all unmarked slots are part of the output range: - cnt = 0; - for (i = 0; i <= UNICODE_BASE_PLANE_MAX_CP; i++) { - if (!l[i]) { - cnt++; - } - } - if (cnt === UNICODE_BASE_PLANE_MAX_CP + 1) { - // When there's nothing in the output we output a special 'match-nothing' regex: `[^\S\s]`. - // BUT... since we output the INVERTED set, we output the match-all set instead: - return '\\S\\s'; - } - else if (cnt === 0) { - // When we find the entire Unicode range is in the output match set, we replace this with - // a shorthand regex: `[\S\s]` - // BUT... since we output the INVERTED set, we output the match-nothing set instead: - return '^\\S\\s'; - } - - // Now see if we can replace several bits by an escape / pcode: - if (output_minimized) { - lut = Pcodes_bitarray_cache_test_order; - for (tn = 0; lut[tn]; tn++) { - tspec = lut[tn]; - // check if the uniquely identifying char is in the inverted set: - if (!l[tspec[0]]) { - // check if the pcode is covered by the inverted set: - pcode = tspec[1]; - ba4pcode = Pcodes_bitarray_cache[pcode]; - match = 0; - for (j = 0; j <= UNICODE_BASE_PLANE_MAX_CP; j++) { - if (ba4pcode[j]) { - if (!l[j]) { - // match in current inverted bitset, i.e. there's at - // least one 'new' bit covered by this pcode/escape: - match++; - } else if (l_orig[j]) { - // mismatch! - match = false; - break; - } - } - } - - // We're only interested in matches which actually cover some - // yet uncovered bits: `match !== 0 && match !== false`. - // - // Apply the heuristic that the pcode/escape is only going to be used - // when it covers *more* characters than its own identifier's length: - if (match && match > pcode.length) { - rv.push(pcode); - - // and nuke the bits in the array which match the given pcode: - // make sure these edits are visible outside this function as - // `l` is an INPUT parameter (~ not modified)! - if (!bitarr_is_cloned) { - l2 = new Array(UNICODE_BASE_PLANE_MAX_CP + 1); - for (j = 0; j <= UNICODE_BASE_PLANE_MAX_CP; j++) { - l2[j] = l[j] || ba4pcode[j]; // `!(!l[j] && !ba4pcode[j])` - } - // recreate sentinel - l2[UNICODE_BASE_PLANE_MAX_CP + 1] = 1; - l = l2; - bitarr_is_cloned = true; - } else { - for (j = 0; j <= UNICODE_BASE_PLANE_MAX_CP; j++) { - l[j] = l[j] || ba4pcode[j]; - } - } - } - } - } - } - - i = 0; - while (i <= UNICODE_BASE_PLANE_MAX_CP) { - // find first character not in original set: - while (l[i]) { - i++; - } - if (i >= UNICODE_BASE_PLANE_MAX_CP + 1) { - break; - } - // find next character not in original set: - for (j = i + 1; !l[j]; j++) {} /* empty loop */ - // generate subset: - rv.push(i2c(i)); - if (j - 1 > i) { - rv.push((j - 2 > i ? '-' : '') + i2c(j - 1)); - } - i = j; - } - } else { - // generate the non-inverted set, hence all logic checks are inverted here... - cnt = 0; - for (i = 0; i <= UNICODE_BASE_PLANE_MAX_CP; i++) { - if (l[i]) { - cnt++; - } - } - if (cnt === UNICODE_BASE_PLANE_MAX_CP + 1) { - // When we find the entire Unicode range is in the output match set, we replace this with - // a shorthand regex: `[\S\s]` - return '\\S\\s'; - } - else if (cnt === 0) { - // When there's nothing in the output we output a special 'match-nothing' regex: `[^\S\s]`. - return '^\\S\\s'; - } - - // Now see if we can replace several bits by an escape / pcode: - if (output_minimized) { - lut = Pcodes_bitarray_cache_test_order; - for (tn = 0; lut[tn]; tn++) { - tspec = lut[tn]; - // check if the uniquely identifying char is in the set: - if (l[tspec[0]]) { - // check if the pcode is covered by the set: - pcode = tspec[1]; - ba4pcode = Pcodes_bitarray_cache[pcode]; - match = 0; - for (j = 0; j <= UNICODE_BASE_PLANE_MAX_CP; j++) { - if (ba4pcode[j]) { - if (l[j]) { - // match in current bitset, i.e. there's at - // least one 'new' bit covered by this pcode/escape: - match++; - } else if (!l_orig[j]) { - // mismatch! - match = false; - break; - } - } - } - - // We're only interested in matches which actually cover some - // yet uncovered bits: `match !== 0 && match !== false`. - // - // Apply the heuristic that the pcode/escape is only going to be used - // when it covers *more* characters than its own identifier's length: - if (match && match > pcode.length) { - rv.push(pcode); - - // and nuke the bits in the array which match the given pcode: - // make sure these edits are visible outside this function as - // `l` is an INPUT parameter (~ not modified)! - if (!bitarr_is_cloned) { - l2 = new Array(UNICODE_BASE_PLANE_MAX_CP + 1); - for (j = 0; j <= UNICODE_BASE_PLANE_MAX_CP; j++) { - l2[j] = l[j] && !ba4pcode[j]; - } - // recreate sentinel - l2[UNICODE_BASE_PLANE_MAX_CP + 1] = 1; - l = l2; - bitarr_is_cloned = true; - } else { - for (j = 0; j <= UNICODE_BASE_PLANE_MAX_CP; j++) { - l[j] = l[j] && !ba4pcode[j]; - } - } - } - } - } - } - - i = 0; - while (i <= UNICODE_BASE_PLANE_MAX_CP) { - // find first character not in original set: - while (!l[i]) { - i++; - } - if (i >= UNICODE_BASE_PLANE_MAX_CP + 1) { - break; - } - // find next character not in original set: - for (j = i + 1; l[j]; j++) {} /* empty loop */ - if (j > UNICODE_BASE_PLANE_MAX_CP + 1) { - j = UNICODE_BASE_PLANE_MAX_CP + 1; - } - // generate subset: - rv.push(i2c(i)); - if (j - 1 > i) { - rv.push((j - 2 > i ? '-' : '') + i2c(j - 1)); - } - i = j; - } - } - - assert(rv.length); - var s = rv.join(''); - assert(s); - - // Check if the set is better represented by one of the regex escapes: - var esc4s = EscCode_bitarray_output_refs.set2esc[s]; - if (esc4s) { - // When we hit a special case like this, it is always the shortest notation, hence wins on the spot! - return '\\' + esc4s; - } - return s; -} - - - - - -// Pretty brutal conversion of 'regex' `s` back to raw regex set content: strip outer [...] when they're there; -// ditto for inner combos of sets, i.e. `]|[` as in `[0-9]|[a-z]`. -function reduceRegexToSetBitArray(s, name, opts) { - var orig = s; - - // propagate deferred exceptions = error reports. - if (s instanceof Error) { - return s; - } - - var l = new Array(UNICODE_BASE_PLANE_MAX_CP + 1); - var internal_state = 0; - var derr; - - while (s.length) { - var c1 = s.match(CHR_RE); - if (!c1) { - // cope with illegal escape sequences too! - return new Error('illegal escape sequence at start of regex part: "' + s + '" of regex "' + orig + '"'); - } else { - c1 = c1[0]; - } - s = s.substr(c1.length); - - switch (c1) { - case '[': - // this is starting a set within the regex: scan until end of set! - var set_content = []; - while (s.length) { - var inner = s.match(SET_PART_RE); - if (!inner) { - inner = s.match(CHR_RE); - if (!inner) { - // cope with illegal escape sequences too! - return new Error('illegal escape sequence at start of regex part: ' + s + '" of regex "' + orig + '"'); - } else { - inner = inner[0]; - } - if (inner === ']') break; - } else { - inner = inner[0]; - } - set_content.push(inner); - s = s.substr(inner.length); - } - - // ensure that we hit the terminating ']': - var c2 = s.match(CHR_RE); - if (!c2) { - // cope with illegal escape sequences too! - return new Error('regex set expression is broken in regex: "' + orig + '" --> "' + s + '"'); - } else { - c2 = c2[0]; - } - if (c2 !== ']') { - return new Error('regex set expression is broken in regex: ' + orig); - } - s = s.substr(c2.length); - - var se = set_content.join(''); - if (!internal_state) { - derr = set2bitarray(l, se, opts); - // propagate deferred exceptions = error reports. - if (derr instanceof Error) { - return derr; - } - - // a set is to use like a single character in a longer literal phrase, hence input `[abc]word[def]` would thus produce output `[abc]`: - internal_state = 1; - } - break; - - // Strip unescaped pipes to catch constructs like `\\r|\\n` and turn them into - // something ready for use inside a regex set, e.g. `\\r\\n`. - // - // > Of course, we realize that converting more complex piped constructs this way - // > will produce something you might not expect, e.g. `A|WORD2` which - // > would end up as the set `[AW]` which is something else than the input - // > entirely. - // > - // > However, we can only depend on the user (grammar writer) to realize this and - // > prevent this from happening by not creating such oddities in the input grammar. - case '|': - // a|b --> [ab] - internal_state = 0; - break; - - case '(': - // (a) --> a - // - // TODO - right now we treat this as 'too complex': - - // Strip off some possible outer wrappers which we know how to remove. - // We don't worry about 'damaging' the regex as any too-complex regex will be caught - // in the validation check at the end; our 'strippers' here would not damage useful - // regexes anyway and them damaging the unacceptable ones is fine. - s = s.replace(/^\((?:\?:)?(.*?)\)$/, '$1'); // (?:...) -> ... and (...) -> ... - s = s.replace(/^\^?(.*?)\$?$/, '$1'); // ^...$ --> ... (catch these both inside and outside the outer grouping, hence do the ungrouping twice: one before, once after this) - s = s.replace(/^\((?:\?:)?(.*?)\)$/, '$1'); // (?:...) -> ... and (...) -> ... - - return new Error('[macro [' + name + '] is unsuitable for use inside regex set expressions: "[' + orig + ']"]'); - - case '.': - case '*': - case '+': - case '?': - // wildcard - // - // TODO - right now we treat this as 'too complex': - return new Error('[macro [' + name + '] is unsuitable for use inside regex set expressions: "[' + orig + ']"]'); - - case '{': // range, e.g. `x{1,3}`, or macro? - // TODO - right now we treat this as 'too complex': - return new Error('[macro [' + name + '] is unsuitable for use inside regex set expressions: "[' + orig + ']"]'); - - default: - // literal character or word: take the first character only and ignore the rest, so that - // the constructed set for `word|noun` would be `[wb]`: - if (!internal_state) { - derr = set2bitarray(l, c1, opts); - // propagate deferred exceptions = error reports. - if (derr instanceof Error) { - return derr; - } - - internal_state = 2; - } - break; - } - } - - s = bitarray2set(l); - - // When this result is suitable for use in a set, than we should be able to compile - // it in a regex; that way we can easily validate whether macro X is fit to be used - // inside a regex set: - try { - var re; - assert(s); - assert(!(s instanceof Error)); - re = new XRegExp('[' + s + ']'); - re.test(s[0]); - - // One thing is apparently *not* caught by the RegExp compile action above: `[a[b]c]` - // so we check for lingering UNESCAPED brackets in here as those cannot be: - if (/[^\\][\[\]]/.exec(s)) { - throw new Error('unescaped brackets in set data'); - } - } catch (ex) { - // make sure we produce a set range expression which will fail badly when it is used - // in actual code: - s = new Error('[macro [' + name + '] is unsuitable for use inside regex set expressions: "[' + s + ']"]: ' + ex.message); - } - - assert(s); - // propagate deferred exceptions = error reports. - if (s instanceof Error) { - return s; - } - return l; -} - - - - -// Convert bitarray representing, for example, `'0-9'` to regex string `[0-9]` -// -- or in this example it can be further optimized to only `\d`! -function produceOptimizedRegex4Set(bitarr) { - // First try to produce a minimum regex from the bitarray directly: - var s1 = bitarray2set(bitarr, false, true); - - // and when the regex set turns out to match a single pcode/escape, then - // use that one as-is: - if (s1.match(SET_IS_SINGLE_PCODE_RE)) { - // When we hit a special case like this, it is always the shortest notation, hence wins on the spot! - return s1; - } else { - s1 = '[' + s1 + ']'; - } - - // Now try to produce a minimum regex from the *inverted* bitarray via negation: - // Because we look at a negated bitset, there's no use looking for matches with - // special cases here. - var s2 = bitarray2set(bitarr, true, true); - - if (s2[0] === '^') { - s2 = s2.substr(1); - if (s2.match(SET_IS_SINGLE_PCODE_RE)) { - // When we hit a special case like this, it is always the shortest notation, hence wins on the spot! - return s2; - } - } else { - s2 = '^' + s2; - } - s2 = '[' + s2 + ']'; - - // Then, as some pcode/escapes still happen to deliver a LARGER regex string in the end, - // we also check against the plain, unadulterated regex set expressions: - // - // First try to produce a minimum regex from the bitarray directly: - var s3 = bitarray2set(bitarr, false, false); - - // and when the regex set turns out to match a single pcode/escape, then - // use that one as-is: - if (s3.match(SET_IS_SINGLE_PCODE_RE)) { - // When we hit a special case like this, it is always the shortest notation, hence wins on the spot! - return s3; - } else { - s3 = '[' + s3 + ']'; - } - - // Now try to produce a minimum regex from the *inverted* bitarray via negation: - // Because we look at a negated bitset, there's no use looking for matches with - // special cases here. - var s4 = bitarray2set(bitarr, true, false); - - if (s4[0] === '^') { - s4 = s4.substr(1); - if (s4.match(SET_IS_SINGLE_PCODE_RE)) { - // When we hit a special case like this, it is always the shortest notation, hence wins on the spot! - return s4; - } - } else { - s4 = '^' + s4; - } - s4 = '[' + s4 + ']'; - - if (s2.length < s1.length) { - s1 = s2; - } - if (s3.length < s1.length) { - s1 = s3; - } - if (s4.length < s1.length) { - s1 = s4; - } - - return s1; -} - - - - - - -module.exports = { - XREGEXP_UNICODE_ESCAPE_RE: XREGEXP_UNICODE_ESCAPE_RE, - CHR_RE: CHR_RE, - SET_PART_RE: SET_PART_RE, - NOTHING_SPECIAL_RE: NOTHING_SPECIAL_RE, - SET_IS_SINGLE_PCODE_RE: SET_IS_SINGLE_PCODE_RE, - - UNICODE_BASE_PLANE_MAX_CP: UNICODE_BASE_PLANE_MAX_CP, - - WHITESPACE_SETSTR: WHITESPACE_SETSTR, - DIGIT_SETSTR: DIGIT_SETSTR, - WORDCHAR_SETSTR: WORDCHAR_SETSTR, - - set2bitarray: set2bitarray, - bitarray2set: bitarray2set, - produceOptimizedRegex4Set: produceOptimizedRegex4Set, - reduceRegexToSetBitArray: reduceRegexToSetBitArray, -}; - diff --git a/lib/util/safe-code-exec-and-diag.js b/lib/util/safe-code-exec-and-diag.js deleted file mode 100644 index 9193bb394..000000000 --- a/lib/util/safe-code-exec-and-diag.js +++ /dev/null @@ -1,143 +0,0 @@ -// -// Helper library for safe code execution/compilation, including dumping offending code to file for further error analysis -// (the idea was originally coded in https://github.com/GerHobbelt/jison/commit/85e367d03b977780516d2b643afbe6f65ee758f2 ) -// -// MIT Licensed -// -// -// This code is intended to help test and diagnose arbitrary chunks of code, answering questions like this: -// -// the given code fails, but where exactly and why? It's precise failure conditions are 'hidden' due to -// the stuff running inside an `eval()` or `Function(...)` call, so we want the code dumped to file so that -// we can test the code in a different environment so that we can see what precisely is causing the failure. -// - -'use strict'; - -var fs = require('fs'); -var path = require('path'); - -var assert = require('assert'); - - - - -// Helper function: pad number with leading zeroes -function pad(n, p) { - p = p || 2; - var rv = '0000' + n; - return rv.slice(-p); -} - - -// attempt to dump in one of several locations: first winner is *it*! -function dumpSourceToFile(sourcecode, errname, err_id, options, ex) { - var dumpfile; - - try { - var dumpPaths = [(options.outfile ? path.dirname(options.outfile) : null), options.inputPath, process.cwd()]; - var dumpName = (options.inputFilename || options.moduleName || options.defaultModuleName || errname).replace(/[^a-z0-9_]/ig, "_"); - - var ts = new Date(); - var tm = ts.getUTCFullYear() + - '_' + pad(ts.getUTCMonth() + 1) + - '_' + pad(ts.getUTCDate()) + - 'T' + pad(ts.getUTCHours()) + - '' + pad(ts.getUTCMinutes()) + - '' + pad(ts.getUTCSeconds()) + - '.' + pad(ts.getUTCMilliseconds(), 3) + - 'Z'; - - dumpName += '.fatal_' + err_id + '_dump_' + tm + '.js'; - - for (var i = 0, l = dumpPaths.length; i < l; i++) { - if (!dumpPaths[i]) { - continue; - } - - try { - dumpfile = path.normalize(dumpPaths[i] + '/' + dumpName); - fs.writeFileSync(dumpfile, sourcecode, 'utf8'); - console.error("****** offending generated " + errname + " source code dumped into file: ", dumpfile); - break; // abort loop once a dump action was successful! - } catch (ex3) { - //console.error("generated " + errname + " source code fatal DUMPING error ATTEMPT: ", i, " = ", ex3.message, " -- while attempting to dump into file: ", dumpfile, "\n", ex3.stack); - if (i === l - 1) { - throw ex3; - } - } - } - } catch (ex2) { - console.error("generated " + errname + " source code fatal DUMPING error: ", ex2.message, " -- while attempting to dump into file: ", dumpfile, "\n", ex2.stack); - } - - // augment the exception info, when available: - if (ex) { - ex.offending_source_code = sourcecode; - ex.offending_source_title = errname; - ex.offending_source_dumpfile = dumpfile; - } -} - - - - -// -// `code_execution_rig` is a function which gets executed, while it is fed the `sourcecode` as a parameter. -// When the `code_execution_rig` crashes, its failure is caught and (using the `options`) the sourcecode -// is dumped to file for later diagnosis. -// -// Two options drive the internal behaviour: -// -// - options.dumpSourceCodeOnFailure -- default: FALSE -// - options.throwErrorOnCompileFailure -- default: FALSE -// -// Dumpfile naming and path are determined through these options: -// -// - options.outfile -// - options.inputPath -// - options.inputFilename -// - options.moduleName -// - options.defaultModuleName -// -function exec_and_diagnose_this_stuff(sourcecode, code_execution_rig, options, title) { - options = options || {}; - var errname = "" + title; - var err_id = errname.replace(/[^a-z0-9_]/ig, "_"); - if (err_id.length === 0) { - err_id = "exec_crash"; - } - const debug = false; - - if (debug) console.warn('generated ' + errname + ' code under EXEC TEST:\n', sourcecode); - - var p; - try { - // p = eval(sourcecode); - if (typeof code_execution_rig !== 'function') { - throw new Error("safe-code-exec-and-diag: code_execution_rig MUST be a JavaScript function"); - } - p = code_execution_rig.call(this, sourcecode, options, errname, debug); - } catch (ex) { - console.error("generated " + errname + " source code fatal error: ", ex.message); - - if (options.dumpSourceCodeOnFailure) { - dumpSourceToFile(sourcecode, errname, err_id, options, ex); - } - - if (options.throwErrorOnCompileFailure) { - throw ex; - } - } - return p; -} - - - - - - - - -module.exports = exec_and_diagnose_this_stuff; - diff --git a/lib/util/transform-parser.js b/lib/util/transform-parser.js deleted file mode 100644 index 073e4d630..000000000 --- a/lib/util/transform-parser.js +++ /dev/null @@ -1,2731 +0,0 @@ -/* parser generated by jison 0.4.18-184 */ - -/* - * Returns a Parser object of the following structure: - * - * Parser: { - * yy: {} The so-called "shared state" or rather the *source* of it; - * the real "shared state" `yy` passed around to - * the rule actions, etc. is a derivative/copy of this one, - * not a direct reference! - * } - * - * Parser.prototype: { - * yy: {}, - * EOF: 1, - * TERROR: 2, - * - * trace: function(errorMessage, ...), - * - * JisonParserError: function(msg, hash), - * - * quoteName: function(name), - * Helper function which can be overridden by user code later on: put suitable - * quotes around literal IDs in a description string. - * - * originalQuoteName: function(name), - * The basic quoteName handler provided by JISON. - * `cleanupAfterParse()` will clean up and reset `quoteName()` to reference this function - * at the end of the `parse()`. - * - * describeSymbol: function(symbol), - * Return a more-or-less human-readable description of the given symbol, when - * available, or the symbol itself, serving as its own 'description' for lack - * of something better to serve up. - * - * Return NULL when the symbol is unknown to the parser. - * - * symbols_: {associative list: name ==> number}, - * terminals_: {associative list: number ==> name}, - * nonterminals: {associative list: rule-name ==> {associative list: number ==> rule-alt}}, - * terminal_descriptions_: (if there are any) {associative list: number ==> description}, - * productions_: [...], - * - * performAction: function parser__performAction(yytext, yyleng, yylineno, yyloc, yystate, yysp, yyvstack, yylstack, yystack, yysstack, ...), - * where `...` denotes the (optional) additional arguments the user passed to - * `parser.parse(str, ...)` and specified by way of `%parse-param ...` in the grammar file - * - * The function parameters and `this` have the following value/meaning: - * - `this` : reference to the `yyval` internal object, which has members (`$` and `_$`) - * to store/reference the rule value `$$` and location info `@$`. - * - * One important thing to note about `this` a.k.a. `yyval`: every *reduce* action gets - * to see the same object via the `this` reference, i.e. if you wish to carry custom - * data from one reduce action through to the next within a single parse run, then you - * may get nasty and use `yyval` a.k.a. `this` for storing you own semi-permanent data. - * - * - `yytext` : reference to the lexer value which belongs to the last lexer token used - * to match this rule. This is *not* the look-ahead token, but the last token - * that's actually part of this rule. - * - * Formulated another way, `yytext` is the value of the token immediately preceeding - * the current look-ahead token. - * Caveats apply for rules which don't require look-ahead, such as epsilon rules. - * - * - `yyleng` : ditto as `yytext`, only now for the lexer.yyleng value. - * - * - `yylineno`: ditto as `yytext`, only now for the lexer.yylineno value. - * - * - `yyloc` : ditto as `yytext`, only now for the lexer.yylloc lexer token location info. - * - * - `yystate` : the current parser state number, used internally for dispatching and - * executing the action code chunk matching the rule currently being reduced. - * - * - `yysp` : the current state stack position (a.k.a. 'stack pointer') - * - * This one comes in handy when you are going to do advanced things to the parser - * stacks, all of which are accessible from your action code (see the next entries below). - * - * Also note that you can access this and other stack index values using the new double-hash - * syntax, i.e. `##$ === ##0 === yysp`, while `##1` is the stack index for all things - * related to the first rule term, just like you have `$1`, `@1` and `#1`. - * This is made available to write very advanced grammar action rules, e.g. when you want - * to investigate the parse state stack in your action code, which would, for example, - * be relevant when you wish to implement error diagnostics and reporting schemes similar - * to the work described here: - * - * + Pottier, F., 2016. Reachability and error diagnosis in LR(1) automata. - * In Journées Francophones des Languages Applicatifs. - * - * + Jeffery, C.L., 2003. Generating LR syntax error messages from examples. - * ACM Transactions on Programming Languages and Systems (TOPLAS), 25(5), pp.631–640. - * - * - `yyvstack`: reference to the parser value stack. Also accessed via the `$1` etc. - * constructs. - * - * - `yylstack`: reference to the parser token location stack. Also accessed via - * the `@1` etc. constructs. - * - * - `yystack` : reference to the parser token id stack. Also accessed via the - * `#1` etc. constructs. - * - * Note: this is a bit of a **white lie** as we can statically decode any `#n` reference to - * its numeric token id value, hence that code wouldn't need the `yystack` but *you* might - * want access for your own purposes, such as error analysis as mentioned above! - * - * Note that this stack stores the current stack of *tokens*, that is the sequence of - * already parsed=reduced *nonterminals* (tokens representing rules) and *terminals* - * (lexer tokens *shifted* onto the stack until the rule they belong to is found and - * *reduced*. - * - * - `yysstack`: reference to the parser state stack. This one carries the internal parser - * *states* such as the one in `yystate`, which are used to represent - * the parser state machine in the *parse table*. *Very* *internal* stuff, - * what can I say? If you access this one, you're clearly doing wicked things - * - * - `...` : the extra arguments you specified in the `%parse-param` statement in your - * grammar definition file. - * - * table: [...], - * State transition table - * ---------------------- - * - * index levels are: - * - `state` --> hash table - * - `symbol` --> action (number or array) - * - * If the `action` is an array, these are the elements' meaning: - * - index [0]: 1 = shift, 2 = reduce, 3 = accept - * - index [1]: GOTO `state` - * - * If the `action` is a number, it is the GOTO `state` - * - * defaultActions: {...}, - * - * parseError: function(str, hash, ExceptionClass), - * yyError: function(str, ...), - * yyRecovering: function(), - * yyErrOk: function(), - * yyClearIn: function(), - * - * constructParseErrorInfo: function(error_message, exception_object, expected_token_set, is_recoverable), - * Helper function **which will be set up during the first invocation of the `parse()` method**. - * Produces a new errorInfo 'hash object' which can be passed into `parseError()`. - * See it's use in this parser kernel in many places; example usage: - * - * var infoObj = parser.constructParseErrorInfo('fail!', null, - * parser.collect_expected_token_set(state), true); - * var retVal = parser.parseError(infoObj.errStr, infoObj, parser.JisonParserError); - * - * originalParseError: function(str, hash, ExceptionClass), - * The basic `parseError` handler provided by JISON. - * `cleanupAfterParse()` will clean up and reset `parseError()` to reference this function - * at the end of the `parse()`. - * - * options: { ... parser %options ... }, - * - * parse: function(input[, args...]), - * Parse the given `input` and return the parsed value (or `true` when none was provided by - * the root action, in which case the parser is acting as a *matcher*). - * You MAY use the additional `args...` parameters as per `%parse-param` spec of this grammar: - * these extra `args...` are passed verbatim to the grammar rules' action code. - * - * cleanupAfterParse: function(resultValue, invoke_post_methods, do_not_nuke_errorinfos), - * Helper function **which will be set up during the first invocation of the `parse()` method**. - * This helper API is invoked at the end of the `parse()` call, unless an exception was thrown - * and `%options no-try-catch` has been defined for this grammar: in that case this helper MAY - * be invoked by calling user code to ensure the `post_parse` callbacks are invoked and - * the internal parser gets properly garbage collected under these particular circumstances. - * - * lexer: { - * yy: {...}, A reference to the so-called "shared state" `yy` once - * received via a call to the `.setInput(input, yy)` lexer API. - * EOF: 1, - * ERROR: 2, - * JisonLexerError: function(msg, hash), - * parseError: function(str, hash, ExceptionClass), - * setInput: function(input, [yy]), - * input: function(), - * unput: function(str), - * more: function(), - * reject: function(), - * less: function(n), - * pastInput: function(n), - * upcomingInput: function(n), - * showPosition: function(), - * test_match: function(regex_match_array, rule_index, ...), - * next: function(...), - * lex: function(...), - * begin: function(condition), - * pushState: function(condition), - * popState: function(), - * topState: function(), - * _currentRules: function(), - * stateStackSize: function(), - * cleanupAfterLex: function() - * - * options: { ... lexer %options ... }, - * - * performAction: function(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * rules: [...], - * conditions: {associative list: name ==> set}, - * } - * } - * - * - * token location info (@$, _$, etc.): { - * first_line: n, - * last_line: n, - * first_column: n, - * last_column: n, - * range: [start_number, end_number] - * (where the numbers are indexes into the input string, zero-based) - * } - * - * --- - * - * The `parseError` function receives a 'hash' object with these members for lexer and - * parser errors: - * - * { - * text: (matched text) - * token: (the produced terminal token, if any) - * token_id: (the produced terminal token numeric ID, if any) - * line: (yylineno) - * loc: (yylloc) - * } - * - * parser (grammar) errors will also provide these additional members: - * - * { - * expected: (array describing the set of expected tokens; - * may be UNDEFINED when we cannot easily produce such a set) - * state: (integer (or array when the table includes grammar collisions); - * represents the current internal state of the parser kernel. - * can, for example, be used to pass to the `collect_expected_token_set()` - * API to obtain the expected token set) - * action: (integer; represents the current internal action which will be executed) - * new_state: (integer; represents the next/planned internal state, once the current - * action has executed) - * recoverable: (boolean: TRUE when the parser MAY have an error recovery rule - * available for this particular error) - * state_stack: (array: the current parser LALR/LR internal state stack; this can be used, - * for instance, for advanced error analysis and reporting) - * value_stack: (array: the current parser LALR/LR internal `$$` value stack; this can be used, - * for instance, for advanced error analysis and reporting) - * location_stack: (array: the current parser LALR/LR internal location stack; this can be used, - * for instance, for advanced error analysis and reporting) - * yy: (object: the current parser internal "shared state" `yy` - * as is also available in the rule actions; this can be used, - * for instance, for advanced error analysis and reporting) - * lexer: (reference to the current lexer instance used by the parser) - * parser: (reference to the current parser instance) - * } - * - * while `this` will reference the current parser instance. - * - * When `parseError` is invoked by the lexer, `this` will still reference the related *parser* - * instance, while these additional `hash` fields will also be provided: - * - * { - * lexer: (reference to the current lexer instance which reported the error) - * } - * - * When `parseError` is invoked by the parser due to a **JavaScript exception** being fired - * from either the parser or lexer, `this` will still reference the related *parser* - * instance, while these additional `hash` fields will also be provided: - * - * { - * exception: (reference to the exception thrown) - * } - * - * Please do note that in the latter situation, the `expected` field will be omitted as - * this type of failure is assumed not to be due to *parse errors* but rather due to user - * action code in either parser or lexer failing unexpectedly. - * - * --- - * - * You can specify parser options by setting / modifying the `.yy` object of your Parser instance. - * These options are available: - * - * ### options which are global for all parser instances - * - * Parser.pre_parse: function(yy [, optional parse() args]) - * optional: you can specify a pre_parse() function in the chunk following - * the grammar, i.e. after the last `%%`. - * Parser.post_parse: function(yy, retval [, optional parse() args]) { return retval; } - * optional: you can specify a post_parse() function in the chunk following - * the grammar, i.e. after the last `%%`. When it does not return any value, - * the parser will return the original `retval`. - * - * ### options which can be set up per parser instance - * - * yy: { - * pre_parse: function(yy [, optional parse() args]) - * optional: is invoked before the parse cycle starts (and before the first - * invocation of `lex()`) but immediately after the invocation of - * `parser.pre_parse()`). - * post_parse: function(yy, retval [, optional parse() args]) { return retval; } - * optional: is invoked when the parse terminates due to success ('accept') - * or failure (even when exceptions are thrown). - * `retval` contains the return value to be produced by `Parser.parse()`; - * this function can override the return value by returning another. - * When it does not return any value, the parser will return the original - * `retval`. - * This function is invoked immediately before `Parser.post_parse()`. - * - * parseError: function(str, hash, ExceptionClass) - * optional: overrides the default `parseError` function. - * quoteName: function(name), - * optional: overrides the default `quoteName` function. - * } - * - * parser.lexer.options: { - * pre_lex: function() - * optional: is invoked before the lexer is invoked to produce another token. - * `this` refers to the Lexer object. - * post_lex: function(token) { return token; } - * optional: is invoked when the lexer has produced a token `token`; - * this function can override the returned token value by returning another. - * When it does not return any (truthy) value, the lexer will return - * the original `token`. - * `this` refers to the Lexer object. - * - * ranges: boolean - * optional: `true` ==> token location info will include a .range[] member. - * flex: boolean - * optional: `true` ==> flex-like lexing behaviour where the rules are tested - * exhaustively to find the longest match. - * backtrack_lexer: boolean - * optional: `true` ==> lexer regexes are tested in order and for invoked; - * the lexer terminates the scan when a token is returned by the action code. - * xregexp: boolean - * optional: `true` ==> lexer rule regexes are "extended regex format" requiring the - * `XRegExp` library. When this %option has not been specified at compile time, all lexer - * rule regexes have been written as standard JavaScript RegExp expressions. - * } - */ -var ebnf = (function () { - -// See also: -// http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508 -// but we keep the prototype.constructor and prototype.name assignment lines too for compatibility -// with userland code which might access the derived class in a 'classic' way. -function JisonParserError(msg, hash) { - Object.defineProperty(this, 'name', { - enumerable: false, - writable: false, - value: 'JisonParserError' - }); - - if (msg == null) msg = '???'; - - Object.defineProperty(this, 'message', { - enumerable: false, - writable: true, - value: msg - }); - - this.hash = hash; - - var stacktrace; - if (hash && hash.exception instanceof Error) { - var ex2 = hash.exception; - this.message = ex2.message || msg; - stacktrace = ex2.stack; - } - if (!stacktrace) { - if (Error.hasOwnProperty('captureStackTrace')) { // V8 - Error.captureStackTrace(this, this.constructor); - } else { - stacktrace = (new Error(msg)).stack; - } - } - if (stacktrace) { - Object.defineProperty(this, 'stack', { - enumerable: false, - writable: false, - value: stacktrace - }); - } -} - -if (typeof Object.setPrototypeOf === 'function') { - Object.setPrototypeOf(JisonParserError.prototype, Error.prototype); -} else { - JisonParserError.prototype = Object.create(Error.prototype); -} -JisonParserError.prototype.constructor = JisonParserError; -JisonParserError.prototype.name = 'JisonParserError'; - - - - -// helper: reconstruct the productions[] table -function bp(s) { - var rv = []; - var p = s.pop; - var r = s.rule; - for (var i = 0, l = p.length; i < l; i++) { - rv.push([ - p[i], - r[i] - ]); - } - return rv; -} - - - -// helper: reconstruct the defaultActions[] table -function bda(s) { - var rv = {}; - var d = s.idx; - var g = s.goto; - for (var i = 0, l = d.length; i < l; i++) { - var j = d[i]; - rv[j] = g[i]; - } - return rv; -} - - - -// helper: reconstruct the 'goto' table -function bt(s) { - var rv = []; - var d = s.len; - var y = s.symbol; - var t = s.type; - var a = s.state; - var m = s.mode; - var g = s.goto; - for (var i = 0, l = d.length; i < l; i++) { - var n = d[i]; - var q = {}; - for (var j = 0; j < n; j++) { - var z = y.shift(); - switch (t.shift()) { - case 2: - q[z] = [ - m.shift(), - g.shift() - ]; - break; - - case 0: - q[z] = a.shift(); - break; - - default: - // type === 1: accept - q[z] = [ - 3 - ]; - } - } - rv.push(q); - } - return rv; -} - - - -// helper: runlength encoding with increment step: code, length: step (default step = 0) -// `this` references an array -function s(c, l, a) { - a = a || 0; - for (var i = 0; i < l; i++) { - this.push(c); - c += a; - } -} - -// helper: duplicate sequence from *relative* offset and length. -// `this` references an array -function c(i, l) { - i = this.length - i; - for (l += i; i < l; i++) { - this.push(this[i]); - } -} - -// helper: unpack an array using helpers and data, all passed in an array argument 'a'. -function u(a) { - var rv = []; - for (var i = 0, l = a.length; i < l; i++) { - var e = a[i]; - // Is this entry a helper function? - if (typeof e === 'function') { - i++; - e.apply(rv, a[i]); - } else { - rv.push(e); - } - } - return rv; -} - - -var parser = { - // Code Generator Information Report - // --------------------------------- - // - // Options: - // - // no default action: ............... false - // no try..catch: ................... false - // no default resolve on conflict: false - // on-demand look-ahead: ............ false - // error recovery token skip maximum: 3 - // yyerror in parse actions is: ..... NOT recoverable, - // yyerror in lexer actions and other non-fatal lexer are: - // .................................. NOT recoverable, - // debug grammar/output: ............ false - // has partial LR conflict upgrade: true - // rudimentary token-stack support: false - // parser table compression mode: ... 2 - // export debug tables: ............. false - // export *all* tables: ............. false - // module type: ..................... commonjs - // parser engine type: .............. lalr - // output main() in the module: ..... true - // number of expected conflicts: .... 0 - // - // - // Parser Analysis flags: - // - // all actions are default: ......... false - // uses yyleng: ..................... false - // uses yylineno: ................... false - // uses yytext: ..................... false - // uses yylloc: ..................... false - // uses ParseError API: ............. false - // uses YYERROR: .................... false - // uses YYRECOVERING: ............... false - // uses YYERROK: .................... false - // uses YYCLEARIN: .................. false - // tracks rule values: .............. true - // assigns rule values: ............. true - // uses location tracking: .......... false - // assigns location: ................ false - // uses yystack: .................... false - // uses yysstack: ................... false - // uses yysp: ....................... true - // has error recovery: .............. false - // - // --------- END OF REPORT ----------- - -trace: function no_op_trace() { }, -JisonParserError: JisonParserError, -yy: {}, -options: { - type: "lalr", - hasPartialLrUpgradeOnConflict: true, - errorRecoveryTokenDiscardCount: 3 -}, -symbols_: { - "$accept": 0, - "$end": 1, - "(": 4, - ")": 5, - "*": 6, - "+": 8, - "?": 7, - "ALIAS": 10, - "EOF": 1, - "EPSILON": 9, - "SYMBOL": 11, - "error": 2, - "expression": 17, - "handle": 14, - "handle_list": 13, - "production": 12, - "rule": 15, - "suffix": 18, - "suffixed_expression": 16, - "|": 3 -}, -terminals_: { - 1: "EOF", - 2: "error", - 3: "|", - 4: "(", - 5: ")", - 6: "*", - 7: "?", - 8: "+", - 9: "EPSILON", - 10: "ALIAS", - 11: "SYMBOL" -}, -TERROR: 2, -EOF: 1, - -// internals: defined here so the object *structure* doesn't get modified by parse() et al, -// thus helping JIT compilers like Chrome V8. -originalQuoteName: null, -originalParseError: null, -cleanupAfterParse: null, -constructParseErrorInfo: null, - -__reentrant_call_depth: 0, // INTERNAL USE ONLY -__error_infos: [], // INTERNAL USE ONLY: the set of parseErrorInfo objects created since the last cleanup - -// APIs which will be set up depending on user action code analysis: -//yyRecovering: 0, -//yyErrOk: 0, -//yyClearIn: 0, - -// Helper APIs -// ----------- - -// Helper function which can be overridden by user code later on: put suitable quotes around -// literal IDs in a description string. -quoteName: function parser_quoteName(id_str) { - return '"' + id_str + '"'; -}, - -// Return a more-or-less human-readable description of the given symbol, when available, -// or the symbol itself, serving as its own 'description' for lack of something better to serve up. -// -// Return NULL when the symbol is unknown to the parser. -describeSymbol: function parser_describeSymbol(symbol) { - if (symbol !== this.EOF && this.terminal_descriptions_ && this.terminal_descriptions_[symbol]) { - return this.terminal_descriptions_[symbol]; - } - else if (symbol === this.EOF) { - return 'end of input'; - } - else if (this.terminals_[symbol]) { - return this.quoteName(this.terminals_[symbol]); - } - // Otherwise... this might refer to a RULE token i.e. a non-terminal: see if we can dig that one up. - // - // An example of this may be where a rule's action code contains a call like this: - // - // parser.describeSymbol(#$) - // - // to obtain a human-readable description or name of the current grammar rule. This comes handy in - // error handling action code blocks, for example. - var s = this.symbols_; - for (var key in s) { - if (s[key] === symbol) { - return key; - } - } - return null; -}, - -// Produce a (more or less) human-readable list of expected tokens at the point of failure. -// -// The produced list may contain token or token set descriptions instead of the tokens -// themselves to help turning this output into something that easier to read by humans -// unless `do_not_describe` parameter is set, in which case a list of the raw, *numeric*, -// expected terminals and nonterminals is produced. -// -// The returned list (array) will not contain any duplicate entries. -collect_expected_token_set: function parser_collect_expected_token_set(state, do_not_describe) { - var TERROR = this.TERROR; - var tokenset = []; - var check = {}; - // Has this (error?) state been outfitted with a custom expectations description text for human consumption? - // If so, use that one instead of the less palatable token set. - if (!do_not_describe && this.state_descriptions_ && this.state_descriptions_[state]) { - return [ - this.state_descriptions_[state] - ]; - } - for (var p in this.table[state]) { - p = +p; - if (p !== TERROR) { - var d = do_not_describe ? p : this.describeSymbol(p); - if (d && !check[d]) { - tokenset.push(d); - check[d] = true; // Mark this token description as already mentioned to prevent outputting duplicate entries. - } - } - } - return tokenset; -}, -productions_: bp({ - pop: u([ - 12, - 13, - 13, - s, - [14, 3], - 15, - 15, - 16, - 16, - 17, - 17, - s, - [18, 4] -]), - rule: u([ - 2, - 1, - 3, - 0, - s, - [1, 3], - 2, - 3, - c, - [9, 7] -]) -}), -performAction: function parser__PerformAction(yystate /* action[1] */, yysp, yyvstack) { -/* this == yyval */ -var yy = this.yy; - -switch (yystate) { -case 1: - /*! Production:: production : handle EOF */ - return yyvstack[yysp - 1]; - break; - -case 2: - /*! Production:: handle_list : handle */ -case 7: - /*! Production:: rule : suffixed_expression */ - this.$ = [yyvstack[yysp]]; - break; - -case 3: - /*! Production:: handle_list : handle_list "|" handle */ - yyvstack[yysp - 2].push(yyvstack[yysp]); - break; - -case 4: - /*! Production:: handle : ε */ -case 5: - /*! Production:: handle : EPSILON */ - this.$ = []; - break; - -case 6: - /*! Production:: handle : rule */ - this.$ = yyvstack[yysp]; - break; - -case 8: - /*! Production:: rule : rule suffixed_expression */ - yyvstack[yysp - 1].push(yyvstack[yysp]); - break; - -case 9: - /*! Production:: suffixed_expression : expression suffix ALIAS */ - this.$ = ['xalias', yyvstack[yysp - 1], yyvstack[yysp - 2], yyvstack[yysp]]; - break; - -case 10: - /*! Production:: suffixed_expression : expression suffix */ - if (yyvstack[yysp]) { - this.$ = [yyvstack[yysp], yyvstack[yysp - 1]]; - } else { - this.$ = yyvstack[yysp - 1]; - } - break; - -case 11: - /*! Production:: expression : SYMBOL */ - this.$ = ['symbol', yyvstack[yysp]]; - break; - -case 12: - /*! Production:: expression : "(" handle_list ")" */ - this.$ = ['()', yyvstack[yysp - 1]]; - break; - -} -}, -table: bt({ - len: u([ - 9, - 1, - 1, - 0, - 7, - 0, - 10, - 0, - 10, - 0, - 0, - 6, - s, - [0, 3], - 2, - s, - [0, 3], - 9, - 0 -]), - symbol: u([ - 1, - 4, - 9, - 11, - 12, - s, - [14, 4, 1], - s, - [1, 3], - 3, - 4, - 5, - 11, - c, - [9, 3], - s, - [3, 6, 1], - 10, - 11, - 18, - c, - [9, 3], - 9, - 11, - s, - [13, 5, 1], - c, - [20, 4], - 10, - 11, - 3, - 5, - c, - [18, 5], - c, - [17, 4] -]), - type: u([ - s, - [2, 4], - s, - [0, 5], - 1, - s, - [2, 6], - 0, - 0, - s, - [2, 9], - c, - [10, 6], - s, - [0, 5], - s, - [2, 13], - s, - [0, 4] -]), - state: u([ - 1, - 2, - 4, - 5, - 6, - 10, - 6, - 11, - 15, - 16, - c, - [8, 3], - 20, - c, - [4, 3] -]), - mode: u([ - 2, - s, - [1, 4], - 2, - 2, - 1, - 2, - c, - [5, 3], - c, - [7, 3], - c, - [12, 4], - c, - [13, 4], - c, - [14, 6], - c, - [8, 4], - c, - [5, 4] -]), - goto: u([ - 4, - 8, - 3, - 7, - 9, - 6, - 6, - 8, - 6, - 7, - s, - [13, 4], - 12, - 13, - 14, - 13, - 13, - 4, - 8, - 4, - 3, - 7, - s, - [10, 4], - 17, - 10, - 19, - 18, - c, - [13, 5] -]) -}), -defaultActions: bda({ - idx: u([ - s, - [3, 4, 2], - 10, - 12, - 13, - 14, - 16, - 17, - 18, - 20 -]), - goto: u([ - 5, - 7, - 11, - 1, - 8, - 14, - 15, - 16, - 2, - 9, - 12, - 3 -]) -}), -parseError: function parseError(str, hash, ExceptionClass) { - if (hash.recoverable && typeof this.trace === 'function') { - this.trace(str); - hash.destroy(); // destroy... well, *almost*! - } else { - if (!ExceptionClass) { - ExceptionClass = this.JisonParserError; - } - throw new ExceptionClass(str, hash); - } -}, -parse: function parse(input) { - var self = this, - stack = new Array(128), // token stack: stores token which leads to state at the same index (column storage) - sstack = new Array(128), // state stack: stores states (column storage) - - vstack = new Array(128), // semantic value stack - - table = this.table, - sp = 0; // 'stack pointer': index into the stacks - - var TERROR = this.TERROR, - EOF = this.EOF, - ERROR_RECOVERY_TOKEN_DISCARD_COUNT = (this.options.errorRecoveryTokenDiscardCount | 0) || 3; - var NO_ACTION = [0, table.length /* ensures that anyone using this new state will fail dramatically! */]; - - //this.reductionCount = this.shiftCount = 0; - - var lexer; - if (this.__lexer__) { - lexer = this.__lexer__; - } else { - lexer = this.__lexer__ = Object.create(this.lexer); - } - - var sharedState_yy = { - parseError: null, - quoteName: null, - lexer: null, - parser: null, - pre_parse: null, - post_parse: null - }; - // copy state - for (var k in this.yy) { - if (Object.prototype.hasOwnProperty.call(this.yy, k)) { - sharedState_yy[k] = this.yy[k]; - } - } - - sharedState_yy.lexer = lexer; - sharedState_yy.parser = this; - - - - - - - lexer.setInput(input, sharedState_yy); - - - - vstack[sp] = null; - sstack[sp] = 0; - stack[sp] = 0; - ++sp; - - - // Does the shared state override the default `parseError` that already comes with this instance? - if (typeof sharedState_yy.parseError === 'function') { - this.parseError = function parseErrorAlt(str, hash, ExceptionClass) { - if (!ExceptionClass) { - ExceptionClass = this.JisonParserError; - } - return sharedState_yy.parseError(str, hash, ExceptionClass); - }; - } else { - this.parseError = this.originalParseError; - } - - // Does the shared state override the default `quoteName` that already comes with this instance? - if (typeof sharedState_yy.quoteName === 'function') { - this.quoteName = sharedState_yy.quoteName; - } else { - this.quoteName = this.originalQuoteName; - } - - // set up the cleanup function; make it an API so that external code can re-use this one in case of - // calamities or when the `%options no-try-catch` option has been specified for the grammar, in which - // case this parse() API method doesn't come with a `finally { ... }` block any more! - // - // NOTE: as this API uses parse() as a closure, it MUST be set again on every parse() invocation, - // or else your `sharedState`, etc. references will be *wrong*! - this.cleanupAfterParse = function parser_cleanupAfterParse(resultValue, invoke_post_methods, do_not_nuke_errorinfos) { - var rv; - - if (invoke_post_methods) { - if (sharedState_yy.post_parse) { - rv = sharedState_yy.post_parse.call(this, sharedState_yy, resultValue); - if (typeof rv !== 'undefined') resultValue = rv; - } - if (this.post_parse) { - rv = this.post_parse.call(this, sharedState_yy, resultValue); - if (typeof rv !== 'undefined') resultValue = rv; - } - } - - if (this.__reentrant_call_depth > 1) return resultValue; // do not (yet) kill the sharedState when this is a reentrant run. - - // clean up the lingering lexer structures as well: - if (lexer.cleanupAfterLex) { - lexer.cleanupAfterLex(do_not_nuke_errorinfos); - } - - // prevent lingering circular references from causing memory leaks: - if (sharedState_yy) { - sharedState_yy.parseError = undefined; - sharedState_yy.quoteName = undefined; - sharedState_yy.lexer = undefined; - sharedState_yy.parser = undefined; - if (lexer.yy === sharedState_yy) { - lexer.yy = undefined; - } - } - sharedState_yy = undefined; - this.parseError = this.originalParseError; - this.quoteName = this.originalQuoteName; - - // nuke the vstack[] array at least as that one will still reference obsoleted user values. - // To be safe, we nuke the other internal stack columns as well... - stack.length = 0; // fastest way to nuke an array without overly bothering the GC - sstack.length = 0; - - vstack.length = 0; - sp = 0; - - // nuke the error hash info instances created during this run. - // Userland code must COPY any data/references - // in the error hash instance(s) it is more permanently interested in. - if (!do_not_nuke_errorinfos) { - for (var i = this.__error_infos.length - 1; i >= 0; i--) { - var el = this.__error_infos[i]; - if (el && typeof el.destroy === 'function') { - el.destroy(); - } - } - this.__error_infos.length = 0; - } - - return resultValue; - }; - - // NOTE: as this API uses parse() as a closure, it MUST be set again on every parse() invocation, - // or else your `lexer`, `sharedState`, etc. references will be *wrong*! - this.constructParseErrorInfo = function parser_constructParseErrorInfo(msg, ex, expected, recoverable) { - var pei = { - errStr: msg, - exception: ex, - text: lexer.match, - value: lexer.yytext, - token: this.describeSymbol(symbol) || symbol, - token_id: symbol, - line: lexer.yylineno, - - expected: expected, - recoverable: recoverable, - state: state, - action: action, - new_state: newState, - symbol_stack: stack, - state_stack: sstack, - value_stack: vstack, - - stack_pointer: sp, - yy: sharedState_yy, - lexer: lexer, - parser: this, - - // and make sure the error info doesn't stay due to potential - // ref cycle via userland code manipulations. - // These would otherwise all be memory leak opportunities! - // - // Note that only array and object references are nuked as those - // constitute the set of elements which can produce a cyclic ref. - // The rest of the members is kept intact as they are harmless. - destroy: function destructParseErrorInfo() { - // remove cyclic references added to error info: - // info.yy = null; - // info.lexer = null; - // info.value = null; - // info.value_stack = null; - // ... - var rec = !!this.recoverable; - for (var key in this) { - if (this.hasOwnProperty(key) && typeof key === 'object') { - this[key] = undefined; - } - } - this.recoverable = rec; - } - }; - // track this instance so we can `destroy()` it once we deem it superfluous and ready for garbage collection! - this.__error_infos.push(pei); - return pei; - }; - - - function lex() { - var token = lexer.lex(); - // if token isn't its numeric value, convert - if (typeof token !== 'number') { - token = self.symbols_[token] || token; - } - return token || EOF; - } - - - var symbol = 0; - - var state, action, r, t; - var yyval = { - $: true, - _$: undefined, - yy: sharedState_yy - }; - var p, len, this_production; - - var newState; - var retval = false; - - - try { - this.__reentrant_call_depth++; - - if (this.pre_parse) { - this.pre_parse.call(this, sharedState_yy); - } - if (sharedState_yy.pre_parse) { - sharedState_yy.pre_parse.call(this, sharedState_yy); - } - - newState = sstack[sp - 1]; - for (;;) { - // retrieve state number from top of stack - state = newState; // sstack[sp - 1]; - - // use default actions if available - if (this.defaultActions[state]) { - action = 2; - newState = this.defaultActions[state]; - } else { - // The single `==` condition below covers both these `===` comparisons in a single - // operation: - // - // if (symbol === null || typeof symbol === 'undefined') ... - if (!symbol) { - symbol = lex(); - } - // read action for current state and first input - t = (table[state] && table[state][symbol]) || NO_ACTION; - newState = t[1]; - action = t[0]; - - - - - // handle parse error - if (!action) { - var errStr; - var errSymbolDescr = (this.describeSymbol(symbol) || symbol); - var expected = this.collect_expected_token_set(state); - - // Report error - if (typeof lexer.yylineno === 'number') { - errStr = 'Parse error on line ' + (lexer.yylineno + 1) + ': '; - } else { - errStr = 'Parse error: '; - } - if (lexer.showPosition) { - errStr += '\n' + lexer.showPosition(79 - 10, 10) + '\n'; - } - if (expected.length) { - errStr += 'Expecting ' + expected.join(', ') + ', got unexpected ' + errSymbolDescr; - } else { - errStr += 'Unexpected ' + errSymbolDescr; - } - // we cannot recover from the error! - p = this.constructParseErrorInfo(errStr, null, expected, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - - - } - - - - - - - - - - switch (action) { - // catch misc. parse failures: - default: - // this shouldn't happen, unless resolve defaults are off - if (action instanceof Array) { - p = this.constructParseErrorInfo(('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol), null, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - // Another case of better safe than sorry: in case state transitions come out of another error recovery process - // or a buggy LUT (LookUp Table): - p = this.constructParseErrorInfo('Parsing halted. No viable error recovery approach available due to internal system failure.', null, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - - // shift: - case 1: - //this.shiftCount++; - stack[sp] = symbol; - vstack[sp] = lexer.yytext; - - sstack[sp] = newState; // push state - ++sp; - symbol = 0; - - // Pick up the lexer details for the current symbol as that one is not 'look-ahead' any more: - - - - - - - - - - - - - - - - continue; - - // reduce: - case 2: - //this.reductionCount++; - this_production = this.productions_[newState - 1]; // `this.productions_[]` is zero-based indexed while states start from 1 upwards... - len = this_production[1]; - - - - - - - // Make sure subsequent `$$ = $1` default action doesn't fail - // for rules where len==0 as then there's no $1 (you're reducing an epsilon rule then!) - // - // Also do this to prevent nasty action block codes to *read* `$0` or `$$` - // and *not* get `undefined` as a result for their efforts! - vstack[sp] = undefined; - - // perform semantic action - yyval.$ = vstack[sp - len]; // default to $$ = $1; result must produce `undefined` when len == 0, as then there's no $1 - - - - - - - - - - - r = this.performAction.call(yyval, newState, sp - 1, vstack); - - if (typeof r !== 'undefined') { - retval = r; - break; - } - - // pop off stack - sp -= len; - - // don't overwrite the `symbol` variable: use a local var to speed things up: - var ntsymbol = this_production[0]; // push nonterminal (reduce) - stack[sp] = ntsymbol; - vstack[sp] = yyval.$; - - // goto new state = table[STATE][NONTERMINAL] - newState = table[sstack[sp - 1]][ntsymbol]; - sstack[sp] = newState; - ++sp; - - continue; - - // accept: - case 3: - retval = true; - // Return the `$accept` rule's `$$` result, if available. - // - // Also note that JISON always adds this top-most `$accept` rule (with implicit, - // default, action): - // - // $accept: $end - // %{ $$ = $1; @$ = @1; %} - // - // which, combined with the parse kernel's `$accept` state behaviour coded below, - // will produce the `$$` value output of the rule as the parse result, - // IFF that result is *not* `undefined`. (See also the parser kernel code.) - // - // In code: - // - // %{ - // @$ = @1; // if location tracking support is included - // if (typeof $1 !== 'undefined') - // return $1; - // else - // return true; // the default parse result if the rule actions don't produce anything - // %} - if (typeof yyval.$ !== 'undefined') { - retval = yyval.$; - } - break; - } - - // break out of loop: we accept or fail with error - break; - } - } catch (ex) { - // report exceptions through the parseError callback too, but keep the exception intact - // if it is a known parser or lexer error which has been thrown by parseError() already: - if (ex instanceof this.JisonParserError) { - throw ex; - } - else if (lexer && typeof lexer.JisonLexerError === 'function' && ex instanceof lexer.JisonLexerError) { - throw ex; - } - else { - p = this.constructParseErrorInfo('Parsing aborted due to exception.', ex, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - } - } finally { - retval = this.cleanupAfterParse(retval, true, true); - this.__reentrant_call_depth--; - } - - return retval; -} -}; -parser.originalParseError = parser.parseError; -parser.originalQuoteName = parser.quoteName; - -var XRegExp = require('xregexp'); // for helping out the `%options xregexp` in the lexer; -/* lexer generated by jison-lex 0.3.4-166 */ -/* - * Returns a Lexer object of the following structure: - * - * Lexer: { - * yy: {} The so-called "shared state" or rather the *source* of it; - * the real "shared state" `yy` passed around to - * the rule actions, etc. is a derivative/copy of this one, - * not a direct reference! - * } - * - * Lexer.prototype: { - * yy: {}, - * EOF: 1, - * ERROR: 2, - * - * JisonLexerError: function(msg, hash), - * - * performAction: function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * where `...` denotes the (optional) additional arguments the user passed to - * `lexer.lex(...)` and specified by way of `%parse-param ...` in the **parser** grammar file - * - * The function parameters and `this` have the following value/meaning: - * - `this` : reference to the `lexer` instance. - * - * - `yy` : a reference to the `yy` "shared state" object which was passed to the lexer - * by way of the `lexer.setInput(str, yy)` API before. - * - * - `yy_` : lexer instance reference used internally. - * - * - `$avoiding_name_collisions` : index of the matched lexer rule (regex), used internally. - * - * - `YY_START`: the current lexer "start condition" state. - * - * - `...` : the extra arguments you specified in the `%parse-param` statement in your - * **parser** grammar definition file and which are passed to the lexer via - * its `lexer.lex(...)` API. - * - * parseError: function(str, hash, ExceptionClass), - * - * constructLexErrorInfo: function(error_message, is_recoverable), - * Helper function. - * Produces a new errorInfo 'hash object' which can be passed into `parseError()`. - * See it's use in this lexer kernel in many places; example usage: - * - * var infoObj = lexer.constructParseErrorInfo('fail!', true); - * var retVal = lexer.parseError(infoObj.errStr, infoObj, lexer.JisonLexerError); - * - * options: { ... lexer %options ... }, - * - * lex: function([args...]), - * Produce one token of lexed input, which was passed in earlier via the `lexer.setInput()` API. - * You MAY use the additional `args...` parameters as per `%parse-param` spec of the **parser** grammar: - * these extra `args...` are passed verbatim to the lexer rules' action code. - * - * cleanupAfterLex: function(do_not_nuke_errorinfos), - * Helper function. - * This helper API is invoked when the parse process has completed. This helper may - * be invoked by user code to ensure the internal lexer gets properly garbage collected. - * - * setInput: function(input, [yy]), - * input: function(), - * unput: function(str), - * more: function(), - * reject: function(), - * less: function(n), - * pastInput: function(n), - * upcomingInput: function(n), - * showPosition: function(), - * test_match: function(regex_match_array, rule_index), - * next: function(...), - * lex: function(...), - * begin: function(condition), - * pushState: function(condition), - * popState: function(), - * topState: function(), - * _currentRules: function(), - * stateStackSize: function(), - * - * options: { ... lexer %options ... }, - * - * performAction: function(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * rules: [...], - * conditions: {associative list: name ==> set}, - * } - * - * - * token location info (`yylloc`): { - * first_line: n, - * last_line: n, - * first_column: n, - * last_column: n, - * range: [start_number, end_number] - * (where the numbers are indexes into the input string, zero-based) - * } - * - * --- - * - * The `parseError` function receives a 'hash' object with these members for lexer errors: - * - * { - * text: (matched text) - * token: (the produced terminal token, if any) - * token_id: (the produced terminal token numeric ID, if any) - * line: (yylineno) - * loc: (yylloc) - * recoverable: (boolean: TRUE when the parser MAY have an error recovery rule - * available for this particular error) - * yy: (object: the current parser internal "shared state" `yy` - * as is also available in the rule actions; this can be used, - * for instance, for advanced error analysis and reporting) - * lexer: (reference to the current lexer instance used by the parser) - * } - * - * while `this` will reference the current lexer instance. - * - * When `parseError` is invoked by the lexer, the default implementation will - * attempt to invoke `yy.parser.parseError()`; when this callback is not provided - * it will try to invoke `yy.parseError()` instead. When that callback is also not - * provided, a `JisonLexerError` exception will be thrown containing the error - * message and `hash`, as constructed by the `constructLexErrorInfo()` API. - * - * Note that the lexer's `JisonLexerError` error class is passed via the - * `ExceptionClass` argument, which is invoked to construct the exception - * instance to be thrown, so technically `parseError` will throw the object - * produced by the `new ExceptionClass(str, hash)` JavaScript expression. - * - * --- - * - * You can specify lexer options by setting / modifying the `.options` object of your Lexer instance. - * These options are available: - * - * (Options are permanent.) - * - * yy: { - * parseError: function(str, hash, ExceptionClass) - * optional: overrides the default `parseError` function. - * } - * - * lexer.options: { - * pre_lex: function() - * optional: is invoked before the lexer is invoked to produce another token. - * `this` refers to the Lexer object. - * post_lex: function(token) { return token; } - * optional: is invoked when the lexer has produced a token `token`; - * this function can override the returned token value by returning another. - * When it does not return any (truthy) value, the lexer will return - * the original `token`. - * `this` refers to the Lexer object. - * - * WARNING: the next set of options are not meant to be changed. They echo the abilities of - * the lexer as per when it was compiled! - * - * ranges: boolean - * optional: `true` ==> token location info will include a .range[] member. - * flex: boolean - * optional: `true` ==> flex-like lexing behaviour where the rules are tested - * exhaustively to find the longest match. - * backtrack_lexer: boolean - * optional: `true` ==> lexer regexes are tested in order and for invoked; - * the lexer terminates the scan when a token is returned by the action code. - * xregexp: boolean - * optional: `true` ==> lexer rule regexes are "extended regex format" requiring the - * `XRegExp` library. When this %option has not been specified at compile time, all lexer - * rule regexes have been written as standard JavaScript RegExp expressions. - * } - */ - - -var lexer = (function () { -// See also: -// http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508 -// but we keep the prototype.constructor and prototype.name assignment lines too for compatibility -// with userland code which might access the derived class in a 'classic' way. -function JisonLexerError(msg, hash) { - Object.defineProperty(this, 'name', { - enumerable: false, - writable: false, - value: 'JisonLexerError' - }); - - if (msg == null) msg = '???'; - - Object.defineProperty(this, 'message', { - enumerable: false, - writable: true, - value: msg - }); - - this.hash = hash; - - var stacktrace; - if (hash && hash.exception instanceof Error) { - var ex2 = hash.exception; - this.message = ex2.message || msg; - stacktrace = ex2.stack; - } - if (!stacktrace) { - if (Error.hasOwnProperty('captureStackTrace')) { // V8 - Error.captureStackTrace(this, this.constructor); - } else { - stacktrace = (new Error(msg)).stack; - } - } - if (stacktrace) { - Object.defineProperty(this, 'stack', { - enumerable: false, - writable: false, - value: stacktrace - }); - } -} - -if (typeof Object.setPrototypeOf === 'function') { - Object.setPrototypeOf(JisonLexerError.prototype, Error.prototype); -} else { - JisonLexerError.prototype = Object.create(Error.prototype); -} -JisonLexerError.prototype.constructor = JisonLexerError; -JisonLexerError.prototype.name = 'JisonLexerError'; - - - - -var lexer = { - - // Code Generator Information Report - // --------------------------------- - // - // Options: - // - // backtracking: .................... false - // location.ranges: ................. true - // location line+column tracking: ... true - // - // - // Forwarded Parser Analysis flags: - // - // uses yyleng: ..................... false - // uses yylineno: ................... false - // uses yytext: ..................... false - // uses yylloc: ..................... false - // uses lexer values: ............... true / true - // location tracking: ............... false - // location assignment: ............. false - // - // - // Lexer Analysis flags: - // - // uses yyleng: ..................... undefined - // uses yylineno: ................... undefined - // uses yytext: ..................... undefined - // uses yylloc: ..................... undefined - // uses ParseError API: ............. undefined - // uses location tracking & editing: undefined - // uses more() API: ................. undefined - // uses unput() API: ................ undefined - // uses reject() API: ............... undefined - // uses less() API: ................. undefined - // uses display APIs pastInput(), upcomingInput(), showPosition(): - // ............................. undefined - // uses describeYYLLOC() API: ....... undefined - // - // --------- END OF REPORT ----------- - - - EOF: 1, - ERROR: 2, - - // JisonLexerError: JisonLexerError, /// <-- injected by the code generator - - // options: {}, /// <-- injected by the code generator - - // yy: ..., /// <-- injected by setInput() - - __currentRuleSet__: null, /// <-- internal rule set cache for the current lexer state - - __error_infos: [], /// INTERNAL USE ONLY: the set of lexErrorInfo objects created since the last cleanup - - __decompressed: false, /// INTERNAL USE ONLY: mark whether the lexer instance has been 'unfolded' completely and is now ready for use - - done: false, /// INTERNAL USE ONLY - _backtrack: false, /// INTERNAL USE ONLY - _input: '', /// INTERNAL USE ONLY - _more: false, /// INTERNAL USE ONLY - _signaled_error_token: false, /// INTERNAL USE ONLY - - conditionStack: [], /// INTERNAL USE ONLY; managed via `pushState()`, `popState()`, `topState()` and `stateStackSize()` - - match: '', /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks input which has been matched so far for the lexer token under construction. `match` is identical to `yytext` except that this one still contains the matched input string after `lexer.performAction()` has been invoked, where userland code MAY have changed/replaced the `yytext` value entirely! - matched: '', /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks entire input which has been matched so far - matches: false, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks RE match result for last (successful) match attempt - yytext: '', /// ADVANCED USE ONLY: tracks input which has been matched so far for the lexer token under construction; this value is transferred to the parser as the 'token value' when the parser consumes the lexer token produced through a call to the `lex()` API. - offset: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks the 'cursor position' in the input string, i.e. the number of characters matched so far - yyleng: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: length of matched input for the token under construction (`yytext`) - yylineno: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: 'line number' at which the token under construction is located - yylloc: null, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks location info (lines + columns) for the token under construction - - /** - INTERNAL USE: construct a suitable error info hash object instance for `parseError`. - - @public - @this {RegExpLexer} - */ - constructLexErrorInfo: function lexer_constructLexErrorInfo(msg, recoverable) { - /** @constructor */ - var pei = { - errStr: msg, - recoverable: !!recoverable, - text: this.match, // This one MAY be empty; userland code should use the `upcomingInput` API to obtain more text which follows the 'lexer cursor position'... - token: null, - line: this.yylineno, - loc: this.yylloc, - yy: this.yy, - lexer: this, - - /** - and make sure the error info doesn't stay due to potential - ref cycle via userland code manipulations. - These would otherwise all be memory leak opportunities! - - Note that only array and object references are nuked as those - constitute the set of elements which can produce a cyclic ref. - The rest of the members is kept intact as they are harmless. - - @public - @this {LexErrorInfo} - */ - destroy: function destructLexErrorInfo() { - // remove cyclic references added to error info: - // info.yy = null; - // info.lexer = null; - // ... - var rec = !!this.recoverable; - for (var key in this) { - if (this.hasOwnProperty(key) && typeof key === 'object') { - this[key] = undefined; - } - } - this.recoverable = rec; - } - }; - // track this instance so we can `destroy()` it once we deem it superfluous and ready for garbage collection! - this.__error_infos.push(pei); - return pei; - }, - - /** - handler which is invoked when a lexer error occurs. - - @public - @this {RegExpLexer} - */ - parseError: function lexer_parseError(str, hash, ExceptionClass) { - if (!ExceptionClass) { - ExceptionClass = this.JisonLexerError; - } - if (this.yy.parser && typeof this.yy.parser.parseError === 'function') { - return this.yy.parser.parseError(str, hash, ExceptionClass) || this.ERROR; - } else if (typeof this.yy.parseError === 'function') { - return this.yy.parseError(str, hash, ExceptionClass) || this.ERROR; - } else { - throw new ExceptionClass(str, hash); - } - }, - - /** - method which implements `yyerror(str, ...args)` functionality for use inside lexer actions. - - @public - @this {RegExpLexer} - */ - yyerror: function yyError(str /*, ...args */) { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': ' + str, this.options.lexerErrorsAreRecoverable); - - // Add any extra args to the hash under the name `extra_error_attributes`: - var args = Array.prototype.slice.call(arguments, 1); - if (args.length) { - hash.extra_error_attributes = args; - } - - return (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - }, - - /** - final cleanup function for when we have completed lexing the input; - make it an API so that external code can use this one once userland - code has decided it's time to destroy any lingering lexer error - hash object instances and the like: this function helps to clean - up these constructs, which *may* carry cyclic references which would - otherwise prevent the instances from being properly and timely - garbage-collected, i.e. this function helps prevent memory leaks! - - @public - @this {RegExpLexer} - */ - cleanupAfterLex: function lexer_cleanupAfterLex(do_not_nuke_errorinfos) { - var rv; - - // prevent lingering circular references from causing memory leaks: - this.setInput('', {}); - - // nuke the error hash info instances created during this run. - // Userland code must COPY any data/references - // in the error hash instance(s) it is more permanently interested in. - if (!do_not_nuke_errorinfos) { - for (var i = this.__error_infos.length - 1; i >= 0; i--) { - var el = this.__error_infos[i]; - if (el && typeof el.destroy === 'function') { - el.destroy(); - } - } - this.__error_infos.length = 0; - } - - return this; - }, - - /** - clear the lexer token context; intended for internal use only - - @public - @this {RegExpLexer} - */ - clear: function lexer_clear() { - this.yytext = ''; - this.yyleng = 0; - this.match = ''; - this.matches = false; - this._more = false; - this._backtrack = false; - - var col = this.yylloc ? this.yylloc.last_column : 0; - this.yylloc = { - first_line: this.yylineno + 1, - first_column: col, - last_line: this.yylineno + 1, - last_column: col, - - range: (this.options.ranges ? [this.offset, this.offset] : undefined) - }; - }, - - /** - resets the lexer, sets new input - - @public - @this {RegExpLexer} - */ - setInput: function lexer_setInput(input, yy) { - this.yy = yy || this.yy || {}; - - // also check if we've fully initialized the lexer instance, - // including expansion work to be done to go from a loaded - // lexer to a usable lexer: - if (!this.__decompressed) { - // step 1: decompress the regex list: - var rules = this.rules; - for (var i = 0, len = rules.length; i < len; i++) { - var rule_re = rules[i]; - - // compression: is the RE an xref to another RE slot in the rules[] table? - if (typeof rule_re === 'number') { - rules[i] = rules[rule_re]; - } - } - - // step 2: unfold the conditions[] set to make these ready for use: - var conditions = this.conditions; - for (var k in conditions) { - var spec = conditions[k]; - - var rule_ids = spec.rules; - - var len = rule_ids.length; - var rule_regexes = new Array(len + 1); // slot 0 is unused; we use a 1-based index approach here to keep the hottest code in `lexer_next()` fast and simple! - var rule_new_ids = new Array(len + 1); - - for (var i = 0; i < len; i++) { - var idx = rule_ids[i]; - var rule_re = rules[idx]; - rule_regexes[i + 1] = rule_re; - rule_new_ids[i + 1] = idx; - } - - spec.rules = rule_new_ids; - spec.__rule_regexes = rule_regexes; - spec.__rule_count = len; - } - - this.__decompressed = true; - } - - this._input = input || ''; - this.clear(); - this._signaled_error_token = false; - this.done = false; - this.yylineno = 0; - this.matched = ''; - this.conditionStack = ['INITIAL']; - this.__currentRuleSet__ = null; - this.yylloc = { - first_line: 1, - first_column: 0, - last_line: 1, - last_column: 0, - - range: (this.options.ranges ? [0, 0] : undefined) - }; - this.offset = 0; - return this; - }, - - /** - push a new input into the lexer and activate it: - the old input position is stored and will be resumed - once this new input has been consumed. - - Use this API to help implement C-preprocessor-like - `#include` statements. - - Available options: - - - `emit_EOF_at_end` : {int} the `EOF`-like token to emit - when the new input is consumed: use - this to mark the end of the new input - in the parser grammar. zero/falsey - token value means no end marker token - will be emitted before the lexer - resumes reading from the previous input. - - @public - @this {RegExpLexer} - */ - pushInput: function lexer_pushInput(input, label, options) { - options = options || {}; - - this._input = input || ''; - this.clear(); - // this._signaled_error_token = false; - this.done = false; - this.yylineno = 0; - this.matched = ''; - // this.conditionStack = ['INITIAL']; - // this.__currentRuleSet__ = null; - this.yylloc = { - first_line: 1, - first_column: 0, - last_line: 1, - last_column: 0, - - range: (this.options.ranges ? [0, 0] : undefined) - }; - this.offset = 0; - return this; - }, - - /** - consumes and returns one char from the input - - @public - @this {RegExpLexer} - */ - input: function lexer_input() { - if (!this._input) { - //this.done = true; -- don't set `done` as we want the lex()/next() API to be able to produce one custom EOF token match after this anyhow. (lexer can match special <> tokens and perform user action code for a <> match, but only does so *once*) - return null; - } - var ch = this._input[0]; - this.yytext += ch; - this.yyleng++; - this.offset++; - this.match += ch; - this.matched += ch; - // Count the linenumber up when we hit the LF (or a stand-alone CR). - // On CRLF, the linenumber is incremented when you fetch the CR or the CRLF combo - // and we advance immediately past the LF as well, returning both together as if - // it was all a single 'character' only. - var slice_len = 1; - var lines = false; - if (ch === '\n') { - lines = true; - } else if (ch === '\r') { - lines = true; - var ch2 = this._input[1]; - if (ch2 === '\n') { - slice_len++; - ch += ch2; - this.yytext += ch2; - this.yyleng++; - this.offset++; - this.match += ch2; - this.matched += ch2; - if (this.options.ranges) { - this.yylloc.range[1]++; - } - } - } - if (lines) { - this.yylineno++; - this.yylloc.last_line++; - this.yylloc.last_column = 0; - } else { - this.yylloc.last_column++; - } - if (this.options.ranges) { - this.yylloc.range[1]++; - } - - this._input = this._input.slice(slice_len); - return ch; - }, - - /** - unshifts one char (or an entire string) into the input - - @public - @this {RegExpLexer} - */ - unput: function lexer_unput(ch) { - var len = ch.length; - var lines = ch.split(/(?:\r\n?|\n)/g); - - this._input = ch + this._input; - this.yytext = this.yytext.substr(0, this.yytext.length - len); - this.yyleng = this.yytext.length; - this.offset -= len; - this.match = this.match.substr(0, this.match.length - len); - this.matched = this.matched.substr(0, this.matched.length - len); - - if (lines.length > 1) { - this.yylineno -= lines.length - 1; - - this.yylloc.last_line = this.yylineno + 1; - var pre = this.match; - var pre_lines = pre.split(/(?:\r\n?|\n)/g); - if (pre_lines.length === 1) { - pre = this.matched; - pre_lines = pre.split(/(?:\r\n?|\n)/g); - } - this.yylloc.last_column = pre_lines[pre_lines.length - 1].length; - } else { - this.yylloc.last_column -= len; - } - - if (this.options.ranges) { - this.yylloc.range[1] = this.yylloc.range[0] + this.yyleng; - } - this.done = false; - return this; - }, - - /** - cache matched text and append it on next action - - @public - @this {RegExpLexer} - */ - more: function lexer_more() { - this._more = true; - return this; - }, - - /** - signal the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead. - - @public - @this {RegExpLexer} - */ - reject: function lexer_reject() { - if (this.options.backtrack_lexer) { - this._backtrack = true; - } else { - // when the `parseError()` call returns, we MUST ensure that the error is registered. - // We accomplish this by signaling an 'error' token to be produced for the current - // `.lex()` run. - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).' + pos_str, false); - this._signaled_error_token = (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - } - return this; - }, - - /** - retain first n characters of the match - - @public - @this {RegExpLexer} - */ - less: function lexer_less(n) { - return this.unput(this.match.slice(n)); - }, - - /** - return (part of the) already matched input, i.e. for error messages. - - Limit the returned string length to `maxSize` (default: 20). - - Limit the returned string to the `maxLines` number of lines of input (default: 1). - - Negative limit values equal *unlimited*. - - @public - @this {RegExpLexer} - */ - pastInput: function lexer_pastInput(maxSize, maxLines) { - var past = this.matched.substring(0, this.matched.length - this.match.length); - if (maxSize < 0) - maxSize = past.length; - else if (!maxSize) - maxSize = 20; - if (maxLines < 0) - maxLines = past.length; // can't ever have more input lines than this! - else if (!maxLines) - maxLines = 1; - // `substr` anticipation: treat \r\n as a single character and take a little - // more than necessary so that we can still properly check against maxSize - // after we've transformed and limited the newLines in here: - past = past.substr(-maxSize * 2 - 2); - // now that we have a significantly reduced string to process, transform the newlines - // and chop them, then limit them: - var a = past.replace(/\r\n|\r/g, '\n').split('\n'); - a = a.slice(-maxLines); - past = a.join('\n'); - // When, after limiting to maxLines, we still have too much to return, - // do add an ellipsis prefix... - if (past.length > maxSize) { - past = '...' + past.substr(-maxSize); - } - return past; - }, - - /** - return (part of the) upcoming input, i.e. for error messages. - - Limit the returned string length to `maxSize` (default: 20). - - Limit the returned string to the `maxLines` number of lines of input (default: 1). - - Negative limit values equal *unlimited*. - - @public - @this {RegExpLexer} - */ - upcomingInput: function lexer_upcomingInput(maxSize, maxLines) { - var next = this.match; - if (maxSize < 0) - maxSize = next.length + this._input.length; - else if (!maxSize) - maxSize = 20; - if (maxLines < 0) - maxLines = maxSize; // can't ever have more input lines than this! - else if (!maxLines) - maxLines = 1; - // `substring` anticipation: treat \r\n as a single character and take a little - // more than necessary so that we can still properly check against maxSize - // after we've transformed and limited the newLines in here: - if (next.length < maxSize * 2 + 2) { - next += this._input.substring(0, maxSize * 2 + 2); // substring is faster on Chrome/V8 - } - // now that we have a significantly reduced string to process, transform the newlines - // and chop them, then limit them: - var a = next.replace(/\r\n|\r/g, '\n').split('\n'); - a = a.slice(0, maxLines); - next = a.join('\n'); - // When, after limiting to maxLines, we still have too much to return, - // do add an ellipsis postfix... - if (next.length > maxSize) { - next = next.substring(0, maxSize) + '...'; - } - return next; - }, - - /** - return a string which displays the character position where the lexing error occurred, i.e. for error messages - - @public - @this {RegExpLexer} - */ - showPosition: function lexer_showPosition(maxPrefix, maxPostfix) { - var pre = this.pastInput(maxPrefix).replace(/\s/g, ' '); - var c = new Array(pre.length + 1).join('-'); - return pre + this.upcomingInput(maxPostfix).replace(/\s/g, ' ') + '\n' + c + '^'; - }, - - /** - helper function, used to produce a human readable description as a string, given - the input `yylloc` location object. - - Set `display_range_too` to TRUE to include the string character index position(s) - in the description if the `yylloc.range` is available. - - @public - @this {RegExpLexer} - */ - describeYYLLOC: function lexer_describe_yylloc(yylloc, display_range_too) { - var l1 = yylloc.first_line; - var l2 = yylloc.last_line; - var o1 = yylloc.first_column; - var o2 = yylloc.last_column; - var dl = l2 - l1; - var d_o = o2 - o1; - var rv; - if (dl === 0) { - rv = 'line ' + l1 + ', '; - if (d_o === 1) { - rv += 'column ' + o1; - } else { - rv += 'columns ' + o1 + ' .. ' + o2; - } - } else { - rv = 'lines ' + l1 + '(column ' + o1 + ') .. ' + l2 + '(column ' + o2 + ')'; - } - if (yylloc.range && display_range_too) { - var r1 = yylloc.range[0]; - var r2 = yylloc.range[1] - 1; - if (r2 === r1) { - rv += ' {String Offset: ' + r1 + '}'; - } else { - rv += ' {String Offset range: ' + r1 + ' .. ' + r2 + '}'; - } - } - return rv; - // return JSON.stringify(yylloc); - }, - - /** - test the lexed token: return FALSE when not a match, otherwise return token. - - `match` is supposed to be an array coming out of a regex match, i.e. `match[0]` - contains the actually matched text string. - - Also move the input cursor forward and update the match collectors: - - - `yytext` - - `yyleng` - - `match` - - `matches` - - `yylloc` - - `offset` - - @public - @this {RegExpLexer} - */ - test_match: function lexer_test_match(match, indexed_rule) { - var token, - lines, - backup, - match_str, - match_str_len; - - if (this.options.backtrack_lexer) { - // save context - backup = { - yylineno: this.yylineno, - yylloc: { - first_line: this.yylloc.first_line, - last_line: this.yylloc.last_line, - first_column: this.yylloc.first_column, - last_column: this.yylloc.last_column, - - range: (this.options.ranges ? this.yylloc.range.slice(0) : undefined) - }, - yytext: this.yytext, - match: this.match, - matches: this.matches, - matched: this.matched, - yyleng: this.yyleng, - offset: this.offset, - _more: this._more, - _input: this._input, - //_signaled_error_token: this._signaled_error_token, - yy: this.yy, - conditionStack: this.conditionStack.slice(0), - done: this.done - }; - } - - match_str = match[0]; - match_str_len = match_str.length; - // if (match_str.indexOf('\n') !== -1 || match_str.indexOf('\r') !== -1) { - lines = match_str.split(/(?:\r\n?|\n)/g); - if (lines.length > 1) { - this.yylineno += lines.length - 1; - - this.yylloc.last_line = this.yylineno + 1, - this.yylloc.last_column = lines[lines.length - 1].length; - } else { - this.yylloc.last_column += match_str_len; - } - // } - this.yytext += match_str; - this.match += match_str; - this.matches = match; - this.yyleng = this.yytext.length; - if (this.options.ranges) { - this.yylloc.range[1] += match_str_len; - } - // previous lex rules MAY have invoked the `more()` API rather than producing a token: - // those rules will already have moved this `offset` forward matching their match lengths, - // hence we must only add our own match length now: - this.offset += match_str_len; - this._more = false; - this._backtrack = false; - this._input = this._input.slice(match_str_len); - this.matched += match_str; - - // calling this method: - // - // function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START) {...} - token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1] /* = YY_START */); - // otherwise, when the action codes are all simple return token statements: - //token = this.simpleCaseActionClusters[indexed_rule]; - - if (this.done && this._input) { - this.done = false; - } - if (token) { - return token; - } else if (this._backtrack) { - // recover context - for (var k in backup) { - this[k] = backup[k]; - } - this.__currentRuleSet__ = null; - return false; // rule action called reject() implying the next rule should be tested instead. - } else if (this._signaled_error_token) { - // produce one 'error' token as `.parseError()` in `reject()` did not guarantee a failure signal by throwing an exception! - token = this._signaled_error_token; - this._signaled_error_token = false; - return token; - } - return false; - }, - - /** - return next match in input - - @public - @this {RegExpLexer} - */ - next: function lexer_next() { - if (this.done) { - this.clear(); - return this.EOF; - } - if (!this._input) { - this.done = true; - } - - var token, - match, - tempMatch, - index; - if (!this._more) { - this.clear(); - } - var spec = this.__currentRuleSet__; - if (!spec) { - // Update the ruleset cache as we apparently encountered a state change or just started lexing. - // The cache is set up for fast lookup -- we assume a lexer will switch states much less often than it will - // invoke the `lex()` token-producing API and related APIs, hence caching the set for direct access helps - // speed up those activities a tiny bit. - spec = this.__currentRuleSet__ = this._currentRules(); - // Check whether a *sane* condition has been pushed before: this makes the lexer robust against - // user-programmer bugs such as https://github.com/zaach/jison-lex/issues/19 - if (!spec || !spec.rules) { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Internal lexer engine error' + lineno_msg + ': The lex grammar programmer pushed a non-existing condition name "' + this.topState() + '"; this is a fatal error and should be reported to the application programmer team!' + pos_str, false); - // produce one 'error' token until this situation has been resolved, most probably by parse termination! - return (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - } - } - - var rule_ids = spec.rules; - //var dispatch = spec.__dispatch_lut; - var regexes = spec.__rule_regexes; - var len = spec.__rule_count; - - // Note: the arrays are 1-based, while `len` itself is a valid index, - // hence the non-standard less-or-equal check in the next loop condition! - for (var i = 1; i <= len; i++) { - tempMatch = this._input.match(regexes[i]); - if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { - match = tempMatch; - index = i; - if (this.options.backtrack_lexer) { - token = this.test_match(tempMatch, rule_ids[i]); - if (token !== false) { - return token; - } else if (this._backtrack) { - match = undefined; - continue; // rule action called reject() implying a rule MISmatch. - } else { - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - } else if (!this.options.flex) { - break; - } - } - } - if (match) { - token = this.test_match(match, rule_ids[index]); - if (token !== false) { - return token; - } - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - if (!this._input) { - this.done = true; - this.clear(); - return this.EOF; - } else { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': Unrecognized text.' + pos_str, this.options.lexerErrorsAreRecoverable); - token = (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - if (token === this.ERROR) { - // we can try to recover from a lexer error that `parseError()` did not 'recover' for us - // by moving forward at least one character at a time: - if (!this.match.length) { - this.input(); - } - } - return token; - } - }, - - /** - return next match that has a token - - @public - @this {RegExpLexer} - */ - lex: function lexer_lex() { - var r; - // allow the PRE/POST handlers set/modify the return token for maximum flexibility of the generated lexer: - if (typeof this.options.pre_lex === 'function') { - r = this.options.pre_lex.call(this); - } - while (!r) { - r = this.next(); - } - if (typeof this.options.post_lex === 'function') { - // (also account for a userdef function which does not return any value: keep the token as is) - r = this.options.post_lex.call(this, r) || r; - } - return r; - }, - - /** - backwards compatible alias for `pushState()`; - the latter is symmetrical with `popState()` and we advise to use - those APIs in any modern lexer code, rather than `begin()`. - - @public - @this {RegExpLexer} - */ - begin: function lexer_begin(condition) { - return this.pushState(condition); - }, - - /** - activates a new lexer condition state (pushes the new lexer condition state onto the condition stack) - - @public - @this {RegExpLexer} - */ - pushState: function lexer_pushState(condition) { - this.conditionStack.push(condition); - this.__currentRuleSet__ = null; - return this; - }, - - /** - pop the previously active lexer condition state off the condition stack - - @public - @this {RegExpLexer} - */ - popState: function lexer_popState() { - var n = this.conditionStack.length - 1; - if (n > 0) { - this.__currentRuleSet__ = null; - return this.conditionStack.pop(); - } else { - return this.conditionStack[0]; - } - }, - - /** - return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available - - @public - @this {RegExpLexer} - */ - topState: function lexer_topState(n) { - n = this.conditionStack.length - 1 - Math.abs(n || 0); - if (n >= 0) { - return this.conditionStack[n]; - } else { - return 'INITIAL'; - } - }, - - /** - (internal) determine the lexer rule set which is active for the currently active lexer condition state - - @public - @this {RegExpLexer} - */ - _currentRules: function lexer__currentRules() { - if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) { - return this.conditions[this.conditionStack[this.conditionStack.length - 1]]; - } else { - return this.conditions['INITIAL']; - } - }, - - /** - return the number of states currently on the stack - - @public - @this {RegExpLexer} - */ - stateStackSize: function lexer_stateStackSize() { - return this.conditionStack.length; - }, - options: { - xregexp: true, - ranges: true, - trackPosition: true, - easy_keyword_rules: true -}, - JisonLexerError: JisonLexerError, - performAction: function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START) { - -var YYSTATE = YY_START; -switch($avoiding_name_collisions) { -case 0 : -/*! Conditions:: INITIAL */ -/*! Rule:: \s+ */ - /* skip whitespace */ -break; -case 4 : -/*! Conditions:: INITIAL */ -/*! Rule:: \[{ID}\] */ - yy_.yytext = this.matches[1]; return 10; -break; -default: - return this.simpleCaseActionClusters[$avoiding_name_collisions]; -} -}, - simpleCaseActionClusters: { - - /*! Conditions:: INITIAL */ - /*! Rule:: {ID} */ - 1 : 11, - /*! Conditions:: INITIAL */ - /*! Rule:: \$end\b */ - 2 : 11, - /*! Conditions:: INITIAL */ - /*! Rule:: \$eof\b */ - 3 : 11, - /*! Conditions:: INITIAL */ - /*! Rule:: %empty\b */ - 5 : 9, - /*! Conditions:: INITIAL */ - /*! Rule:: %epsilon\b */ - 6 : 9, - /*! Conditions:: INITIAL */ - /*! Rule:: \u0190 */ - 7 : 9, - /*! Conditions:: INITIAL */ - /*! Rule:: \u025B */ - 8 : 9, - /*! Conditions:: INITIAL */ - /*! Rule:: \u03B5 */ - 9 : 9, - /*! Conditions:: INITIAL */ - /*! Rule:: \u03F5 */ - 10 : 9, - /*! Conditions:: INITIAL */ - /*! Rule:: '{QUOTED_STRING_CONTENT}' */ - 11 : 11, - /*! Conditions:: INITIAL */ - /*! Rule:: "{DOUBLEQUOTED_STRING_CONTENT}" */ - 12 : 11, - /*! Conditions:: INITIAL */ - /*! Rule:: \. */ - 13 : 11, - /*! Conditions:: INITIAL */ - /*! Rule:: \( */ - 14 : 4, - /*! Conditions:: INITIAL */ - /*! Rule:: \) */ - 15 : 5, - /*! Conditions:: INITIAL */ - /*! Rule:: \* */ - 16 : 6, - /*! Conditions:: INITIAL */ - /*! Rule:: \? */ - 17 : 7, - /*! Conditions:: INITIAL */ - /*! Rule:: \| */ - 18 : 3, - /*! Conditions:: INITIAL */ - /*! Rule:: \+ */ - 19 : 8, - /*! Conditions:: INITIAL */ - /*! Rule:: $ */ - 20 : 1 -}, - rules: [ - /^(?:\s+)/, -new XRegExp("^(?:([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}_])*))", ""), -/^(?:\$end\b)/, -/^(?:\$eof\b)/, -new XRegExp("^(?:\\[([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}_])*)\\])", ""), -/^(?:%empty\b)/, -/^(?:%epsilon\b)/, -/^(?:\u0190)/, -/^(?:\u025B)/, -/^(?:\u03B5)/, -/^(?:\u03F5)/, -/^(?:'((?:\\'|\\[^']|[^'\\])*)')/, -/^(?:"((?:\\"|\\[^"]|[^"\\])*)")/, -/^(?:\.)/, -/^(?:\()/, -/^(?:\))/, -/^(?:\*)/, -/^(?:\?)/, -/^(?:\|)/, -/^(?:\+)/, -/^(?:$)/ - ], - conditions: { - "INITIAL": { - rules: [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20 - ], - inclusive: true - } -} -}; - - - - -return lexer; -})(); -parser.lexer = lexer; - -function Parser() { - this.yy = {}; -} -Parser.prototype = parser; -parser.Parser = Parser; - -return new Parser(); -})(); - - - - -if (typeof require !== 'undefined' && typeof exports !== 'undefined') { - exports.parser = ebnf; - exports.Parser = ebnf.Parser; - exports.parse = function () { - return ebnf.parse.apply(ebnf, arguments); - }; - -} diff --git a/modules/ebnf-parser b/modules/ebnf-parser index 5908e671c..f4c0376e4 160000 --- a/modules/ebnf-parser +++ b/modules/ebnf-parser @@ -1 +1 @@ -Subproject commit 5908e671c4bf87a902dcb3064dcedb554bc02cbc +Subproject commit f4c0376e43db619e95c367a97c5b9f3574396fa5 diff --git a/modules/jison-lex b/modules/jison-lex index 57dacf6dc..91c5b4fa5 160000 --- a/modules/jison-lex +++ b/modules/jison-lex @@ -1 +1 @@ -Subproject commit 57dacf6dc121b0c188e4652373a63ee2ef419ef3 +Subproject commit 91c5b4fa5a07108d2276c2b2e4c54bfe55657ce2 diff --git a/modules/jison2json b/modules/jison2json index 32a95e7c6..cea0e7d98 160000 --- a/modules/jison2json +++ b/modules/jison2json @@ -1 +1 @@ -Subproject commit 32a95e7c6515ed05bf4895c010d71420ab982cfd +Subproject commit cea0e7d9837249f94de6daad49420d2d34101597 diff --git a/modules/lex-parser b/modules/lex-parser index 61cfbb726..b7b59fbb7 160000 --- a/modules/lex-parser +++ b/modules/lex-parser @@ -1 +1 @@ -Subproject commit 61cfbb726787d93e025adc0c510e516c23cbcf00 +Subproject commit b7b59fbb75420286cfb2784c0e7e4d48747d83f4 diff --git a/package-lock.json b/package-lock.json index 555038018..89254cc8b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -841,7 +841,7 @@ "dev": true }, "ebnf-parser": { - "version": "github:GerHobbelt/ebnf-parser#a124c5e4ae4163e446a780dcea59791d077ec4f0" + "version": "github:GerHobbelt/ebnf-parser#5908e671c4bf87a902dcb3064dcedb554bc02cbc" }, "elliptic": { "version": "6.4.0", @@ -1206,7 +1206,7 @@ "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=" }, "jison-lex": { - "version": "github:GerHobbelt/jison-lex#82045ca40ec0475708cb12708005e701556f66c9", + "version": "github:GerHobbelt/jison-lex#57dacf6dc121b0c188e4652373a63ee2ef419ef3", "dependencies": { "json5": { "version": "0.5.1", @@ -1313,7 +1313,7 @@ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=" }, "lex-parser": { - "version": "github:GerHobbelt/lex-parser#5099fa73b48fba7339925db7eb0cb3fcecb57c55" + "version": "github:GerHobbelt/lex-parser#61cfbb726787d93e025adc0c510e516c23cbcf00" }, "lexical-scope": { "version": "1.2.0", diff --git a/web/content/assets/js/calculator.js b/web/content/assets/js/calculator.js deleted file mode 100644 index 4b3c45c67..000000000 --- a/web/content/assets/js/calculator.js +++ /dev/null @@ -1,2650 +0,0 @@ -/* parser generated by jison 0.4.18-184 */ - -/* - * Returns a Parser object of the following structure: - * - * Parser: { - * yy: {} The so-called "shared state" or rather the *source* of it; - * the real "shared state" `yy` passed around to - * the rule actions, etc. is a derivative/copy of this one, - * not a direct reference! - * } - * - * Parser.prototype: { - * yy: {}, - * EOF: 1, - * TERROR: 2, - * - * trace: function(errorMessage, ...), - * - * JisonParserError: function(msg, hash), - * - * quoteName: function(name), - * Helper function which can be overridden by user code later on: put suitable - * quotes around literal IDs in a description string. - * - * originalQuoteName: function(name), - * The basic quoteName handler provided by JISON. - * `cleanupAfterParse()` will clean up and reset `quoteName()` to reference this function - * at the end of the `parse()`. - * - * describeSymbol: function(symbol), - * Return a more-or-less human-readable description of the given symbol, when - * available, or the symbol itself, serving as its own 'description' for lack - * of something better to serve up. - * - * Return NULL when the symbol is unknown to the parser. - * - * symbols_: {associative list: name ==> number}, - * terminals_: {associative list: number ==> name}, - * nonterminals: {associative list: rule-name ==> {associative list: number ==> rule-alt}}, - * terminal_descriptions_: (if there are any) {associative list: number ==> description}, - * productions_: [...], - * - * performAction: function parser__performAction(yytext, yyleng, yylineno, yyloc, yystate, yysp, yyvstack, yylstack, yystack, yysstack, ...), - * where `...` denotes the (optional) additional arguments the user passed to - * `parser.parse(str, ...)` and specified by way of `%parse-param ...` in the grammar file - * - * The function parameters and `this` have the following value/meaning: - * - `this` : reference to the `yyval` internal object, which has members (`$` and `_$`) - * to store/reference the rule value `$$` and location info `@$`. - * - * One important thing to note about `this` a.k.a. `yyval`: every *reduce* action gets - * to see the same object via the `this` reference, i.e. if you wish to carry custom - * data from one reduce action through to the next within a single parse run, then you - * may get nasty and use `yyval` a.k.a. `this` for storing you own semi-permanent data. - * - * - `yytext` : reference to the lexer value which belongs to the last lexer token used - * to match this rule. This is *not* the look-ahead token, but the last token - * that's actually part of this rule. - * - * Formulated another way, `yytext` is the value of the token immediately preceeding - * the current look-ahead token. - * Caveats apply for rules which don't require look-ahead, such as epsilon rules. - * - * - `yyleng` : ditto as `yytext`, only now for the lexer.yyleng value. - * - * - `yylineno`: ditto as `yytext`, only now for the lexer.yylineno value. - * - * - `yyloc` : ditto as `yytext`, only now for the lexer.yylloc lexer token location info. - * - * - `yystate` : the current parser state number, used internally for dispatching and - * executing the action code chunk matching the rule currently being reduced. - * - * - `yysp` : the current state stack position (a.k.a. 'stack pointer') - * - * This one comes in handy when you are going to do advanced things to the parser - * stacks, all of which are accessible from your action code (see the next entries below). - * - * Also note that you can access this and other stack index values using the new double-hash - * syntax, i.e. `##$ === ##0 === yysp`, while `##1` is the stack index for all things - * related to the first rule term, just like you have `$1`, `@1` and `#1`. - * This is made available to write very advanced grammar action rules, e.g. when you want - * to investigate the parse state stack in your action code, which would, for example, - * be relevant when you wish to implement error diagnostics and reporting schemes similar - * to the work described here: - * - * + Pottier, F., 2016. Reachability and error diagnosis in LR(1) automata. - * In Journées Francophones des Languages Applicatifs. - * - * + Jeffery, C.L., 2003. Generating LR syntax error messages from examples. - * ACM Transactions on Programming Languages and Systems (TOPLAS), 25(5), pp.631–640. - * - * - `yyvstack`: reference to the parser value stack. Also accessed via the `$1` etc. - * constructs. - * - * - `yylstack`: reference to the parser token location stack. Also accessed via - * the `@1` etc. constructs. - * - * - `yystack` : reference to the parser token id stack. Also accessed via the - * `#1` etc. constructs. - * - * Note: this is a bit of a **white lie** as we can statically decode any `#n` reference to - * its numeric token id value, hence that code wouldn't need the `yystack` but *you* might - * want access for your own purposes, such as error analysis as mentioned above! - * - * Note that this stack stores the current stack of *tokens*, that is the sequence of - * already parsed=reduced *nonterminals* (tokens representing rules) and *terminals* - * (lexer tokens *shifted* onto the stack until the rule they belong to is found and - * *reduced*. - * - * - `yysstack`: reference to the parser state stack. This one carries the internal parser - * *states* such as the one in `yystate`, which are used to represent - * the parser state machine in the *parse table*. *Very* *internal* stuff, - * what can I say? If you access this one, you're clearly doing wicked things - * - * - `...` : the extra arguments you specified in the `%parse-param` statement in your - * grammar definition file. - * - * table: [...], - * State transition table - * ---------------------- - * - * index levels are: - * - `state` --> hash table - * - `symbol` --> action (number or array) - * - * If the `action` is an array, these are the elements' meaning: - * - index [0]: 1 = shift, 2 = reduce, 3 = accept - * - index [1]: GOTO `state` - * - * If the `action` is a number, it is the GOTO `state` - * - * defaultActions: {...}, - * - * parseError: function(str, hash, ExceptionClass), - * yyError: function(str, ...), - * yyRecovering: function(), - * yyErrOk: function(), - * yyClearIn: function(), - * - * constructParseErrorInfo: function(error_message, exception_object, expected_token_set, is_recoverable), - * Helper function **which will be set up during the first invocation of the `parse()` method**. - * Produces a new errorInfo 'hash object' which can be passed into `parseError()`. - * See it's use in this parser kernel in many places; example usage: - * - * var infoObj = parser.constructParseErrorInfo('fail!', null, - * parser.collect_expected_token_set(state), true); - * var retVal = parser.parseError(infoObj.errStr, infoObj, parser.JisonParserError); - * - * originalParseError: function(str, hash, ExceptionClass), - * The basic `parseError` handler provided by JISON. - * `cleanupAfterParse()` will clean up and reset `parseError()` to reference this function - * at the end of the `parse()`. - * - * options: { ... parser %options ... }, - * - * parse: function(input[, args...]), - * Parse the given `input` and return the parsed value (or `true` when none was provided by - * the root action, in which case the parser is acting as a *matcher*). - * You MAY use the additional `args...` parameters as per `%parse-param` spec of this grammar: - * these extra `args...` are passed verbatim to the grammar rules' action code. - * - * cleanupAfterParse: function(resultValue, invoke_post_methods, do_not_nuke_errorinfos), - * Helper function **which will be set up during the first invocation of the `parse()` method**. - * This helper API is invoked at the end of the `parse()` call, unless an exception was thrown - * and `%options no-try-catch` has been defined for this grammar: in that case this helper MAY - * be invoked by calling user code to ensure the `post_parse` callbacks are invoked and - * the internal parser gets properly garbage collected under these particular circumstances. - * - * lexer: { - * yy: {...}, A reference to the so-called "shared state" `yy` once - * received via a call to the `.setInput(input, yy)` lexer API. - * EOF: 1, - * ERROR: 2, - * JisonLexerError: function(msg, hash), - * parseError: function(str, hash, ExceptionClass), - * setInput: function(input, [yy]), - * input: function(), - * unput: function(str), - * more: function(), - * reject: function(), - * less: function(n), - * pastInput: function(n), - * upcomingInput: function(n), - * showPosition: function(), - * test_match: function(regex_match_array, rule_index, ...), - * next: function(...), - * lex: function(...), - * begin: function(condition), - * pushState: function(condition), - * popState: function(), - * topState: function(), - * _currentRules: function(), - * stateStackSize: function(), - * cleanupAfterLex: function() - * - * options: { ... lexer %options ... }, - * - * performAction: function(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * rules: [...], - * conditions: {associative list: name ==> set}, - * } - * } - * - * - * token location info (@$, _$, etc.): { - * first_line: n, - * last_line: n, - * first_column: n, - * last_column: n, - * range: [start_number, end_number] - * (where the numbers are indexes into the input string, zero-based) - * } - * - * --- - * - * The `parseError` function receives a 'hash' object with these members for lexer and - * parser errors: - * - * { - * text: (matched text) - * token: (the produced terminal token, if any) - * token_id: (the produced terminal token numeric ID, if any) - * line: (yylineno) - * loc: (yylloc) - * } - * - * parser (grammar) errors will also provide these additional members: - * - * { - * expected: (array describing the set of expected tokens; - * may be UNDEFINED when we cannot easily produce such a set) - * state: (integer (or array when the table includes grammar collisions); - * represents the current internal state of the parser kernel. - * can, for example, be used to pass to the `collect_expected_token_set()` - * API to obtain the expected token set) - * action: (integer; represents the current internal action which will be executed) - * new_state: (integer; represents the next/planned internal state, once the current - * action has executed) - * recoverable: (boolean: TRUE when the parser MAY have an error recovery rule - * available for this particular error) - * state_stack: (array: the current parser LALR/LR internal state stack; this can be used, - * for instance, for advanced error analysis and reporting) - * value_stack: (array: the current parser LALR/LR internal `$$` value stack; this can be used, - * for instance, for advanced error analysis and reporting) - * location_stack: (array: the current parser LALR/LR internal location stack; this can be used, - * for instance, for advanced error analysis and reporting) - * yy: (object: the current parser internal "shared state" `yy` - * as is also available in the rule actions; this can be used, - * for instance, for advanced error analysis and reporting) - * lexer: (reference to the current lexer instance used by the parser) - * parser: (reference to the current parser instance) - * } - * - * while `this` will reference the current parser instance. - * - * When `parseError` is invoked by the lexer, `this` will still reference the related *parser* - * instance, while these additional `hash` fields will also be provided: - * - * { - * lexer: (reference to the current lexer instance which reported the error) - * } - * - * When `parseError` is invoked by the parser due to a **JavaScript exception** being fired - * from either the parser or lexer, `this` will still reference the related *parser* - * instance, while these additional `hash` fields will also be provided: - * - * { - * exception: (reference to the exception thrown) - * } - * - * Please do note that in the latter situation, the `expected` field will be omitted as - * this type of failure is assumed not to be due to *parse errors* but rather due to user - * action code in either parser or lexer failing unexpectedly. - * - * --- - * - * You can specify parser options by setting / modifying the `.yy` object of your Parser instance. - * These options are available: - * - * ### options which are global for all parser instances - * - * Parser.pre_parse: function(yy [, optional parse() args]) - * optional: you can specify a pre_parse() function in the chunk following - * the grammar, i.e. after the last `%%`. - * Parser.post_parse: function(yy, retval [, optional parse() args]) { return retval; } - * optional: you can specify a post_parse() function in the chunk following - * the grammar, i.e. after the last `%%`. When it does not return any value, - * the parser will return the original `retval`. - * - * ### options which can be set up per parser instance - * - * yy: { - * pre_parse: function(yy [, optional parse() args]) - * optional: is invoked before the parse cycle starts (and before the first - * invocation of `lex()`) but immediately after the invocation of - * `parser.pre_parse()`). - * post_parse: function(yy, retval [, optional parse() args]) { return retval; } - * optional: is invoked when the parse terminates due to success ('accept') - * or failure (even when exceptions are thrown). - * `retval` contains the return value to be produced by `Parser.parse()`; - * this function can override the return value by returning another. - * When it does not return any value, the parser will return the original - * `retval`. - * This function is invoked immediately before `Parser.post_parse()`. - * - * parseError: function(str, hash, ExceptionClass) - * optional: overrides the default `parseError` function. - * quoteName: function(name), - * optional: overrides the default `quoteName` function. - * } - * - * parser.lexer.options: { - * pre_lex: function() - * optional: is invoked before the lexer is invoked to produce another token. - * `this` refers to the Lexer object. - * post_lex: function(token) { return token; } - * optional: is invoked when the lexer has produced a token `token`; - * this function can override the returned token value by returning another. - * When it does not return any (truthy) value, the lexer will return - * the original `token`. - * `this` refers to the Lexer object. - * - * ranges: boolean - * optional: `true` ==> token location info will include a .range[] member. - * flex: boolean - * optional: `true` ==> flex-like lexing behaviour where the rules are tested - * exhaustively to find the longest match. - * backtrack_lexer: boolean - * optional: `true` ==> lexer regexes are tested in order and for invoked; - * the lexer terminates the scan when a token is returned by the action code. - * xregexp: boolean - * optional: `true` ==> lexer rule regexes are "extended regex format" requiring the - * `XRegExp` library. When this %option has not been specified at compile time, all lexer - * rule regexes have been written as standard JavaScript RegExp expressions. - * } - */ -var calculator = (function () { - -// See also: -// http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508 -// but we keep the prototype.constructor and prototype.name assignment lines too for compatibility -// with userland code which might access the derived class in a 'classic' way. -function JisonParserError(msg, hash) { - Object.defineProperty(this, 'name', { - enumerable: false, - writable: false, - value: 'JisonParserError' - }); - - if (msg == null) msg = '???'; - - Object.defineProperty(this, 'message', { - enumerable: false, - writable: true, - value: msg - }); - - this.hash = hash; - - var stacktrace; - if (hash && hash.exception instanceof Error) { - var ex2 = hash.exception; - this.message = ex2.message || msg; - stacktrace = ex2.stack; - } - if (!stacktrace) { - if (Error.hasOwnProperty('captureStackTrace')) { // V8 - Error.captureStackTrace(this, this.constructor); - } else { - stacktrace = (new Error(msg)).stack; - } - } - if (stacktrace) { - Object.defineProperty(this, 'stack', { - enumerable: false, - writable: false, - value: stacktrace - }); - } -} - -if (typeof Object.setPrototypeOf === 'function') { - Object.setPrototypeOf(JisonParserError.prototype, Error.prototype); -} else { - JisonParserError.prototype = Object.create(Error.prototype); -} -JisonParserError.prototype.constructor = JisonParserError; -JisonParserError.prototype.name = 'JisonParserError'; - - - - -// helper: reconstruct the productions[] table -function bp(s) { - var rv = []; - var p = s.pop; - var r = s.rule; - for (var i = 0, l = p.length; i < l; i++) { - rv.push([ - p[i], - r[i] - ]); - } - return rv; -} - - - -// helper: reconstruct the defaultActions[] table -function bda(s) { - var rv = {}; - var d = s.idx; - var g = s.goto; - for (var i = 0, l = d.length; i < l; i++) { - var j = d[i]; - rv[j] = g[i]; - } - return rv; -} - - - -// helper: reconstruct the 'goto' table -function bt(s) { - var rv = []; - var d = s.len; - var y = s.symbol; - var t = s.type; - var a = s.state; - var m = s.mode; - var g = s.goto; - for (var i = 0, l = d.length; i < l; i++) { - var n = d[i]; - var q = {}; - for (var j = 0; j < n; j++) { - var z = y.shift(); - switch (t.shift()) { - case 2: - q[z] = [ - m.shift(), - g.shift() - ]; - break; - - case 0: - q[z] = a.shift(); - break; - - default: - // type === 1: accept - q[z] = [ - 3 - ]; - } - } - rv.push(q); - } - return rv; -} - - - -// helper: runlength encoding with increment step: code, length: step (default step = 0) -// `this` references an array -function s(c, l, a) { - a = a || 0; - for (var i = 0; i < l; i++) { - this.push(c); - c += a; - } -} - -// helper: duplicate sequence from *relative* offset and length. -// `this` references an array -function c(i, l) { - i = this.length - i; - for (l += i; i < l; i++) { - this.push(this[i]); - } -} - -// helper: unpack an array using helpers and data, all passed in an array argument 'a'. -function u(a) { - var rv = []; - for (var i = 0, l = a.length; i < l; i++) { - var e = a[i]; - // Is this entry a helper function? - if (typeof e === 'function') { - i++; - e.apply(rv, a[i]); - } else { - rv.push(e); - } - } - return rv; -} - - -var parser = { - // Code Generator Information Report - // --------------------------------- - // - // Options: - // - // no default action: ............... false - // no try..catch: ................... false - // no default resolve on conflict: false - // on-demand look-ahead: ............ false - // error recovery token skip maximum: 3 - // yyerror in parse actions is: ..... NOT recoverable, - // yyerror in lexer actions and other non-fatal lexer are: - // .................................. NOT recoverable, - // debug grammar/output: ............ false - // has partial LR conflict upgrade: true - // rudimentary token-stack support: false - // parser table compression mode: ... 2 - // export debug tables: ............. false - // export *all* tables: ............. false - // module type: ..................... commonjs - // parser engine type: .............. lalr - // output main() in the module: ..... true - // number of expected conflicts: .... 0 - // - // - // Parser Analysis flags: - // - // all actions are default: ......... false - // uses yyleng: ..................... false - // uses yylineno: ................... false - // uses yytext: ..................... true - // uses yylloc: ..................... false - // uses ParseError API: ............. false - // uses YYERROR: .................... false - // uses YYRECOVERING: ............... false - // uses YYERROK: .................... false - // uses YYCLEARIN: .................. false - // tracks rule values: .............. true - // assigns rule values: ............. true - // uses location tracking: .......... false - // assigns location: ................ false - // uses yystack: .................... false - // uses yysstack: ................... false - // uses yysp: ....................... true - // has error recovery: .............. false - // - // --------- END OF REPORT ----------- - -trace: function no_op_trace() { }, -JisonParserError: JisonParserError, -yy: {}, -options: { - type: "lalr", - hasPartialLrUpgradeOnConflict: true, - errorRecoveryTokenDiscardCount: 3 -}, -symbols_: { - "!": 8, - "$accept": 0, - "$end": 1, - "%": 9, - "(": 10, - ")": 11, - "*": 5, - "+": 3, - "-": 4, - "/": 6, - "E": 12, - "EOF": 1, - "NUMBER": 13, - "PI": 14, - "^": 7, - "e": 16, - "error": 2, - "expressions": 15 -}, -terminals_: { - 1: "EOF", - 2: "error", - 3: "+", - 4: "-", - 5: "*", - 6: "/", - 7: "^", - 8: "!", - 9: "%", - 10: "(", - 11: ")", - 12: "E", - 13: "NUMBER", - 14: "PI" -}, -TERROR: 2, -EOF: 1, - -// internals: defined here so the object *structure* doesn't get modified by parse() et al, -// thus helping JIT compilers like Chrome V8. -originalQuoteName: null, -originalParseError: null, -cleanupAfterParse: null, -constructParseErrorInfo: null, - -__reentrant_call_depth: 0, // INTERNAL USE ONLY -__error_infos: [], // INTERNAL USE ONLY: the set of parseErrorInfo objects created since the last cleanup - -// APIs which will be set up depending on user action code analysis: -//yyRecovering: 0, -//yyErrOk: 0, -//yyClearIn: 0, - -// Helper APIs -// ----------- - -// Helper function which can be overridden by user code later on: put suitable quotes around -// literal IDs in a description string. -quoteName: function parser_quoteName(id_str) { - return '"' + id_str + '"'; -}, - -// Return a more-or-less human-readable description of the given symbol, when available, -// or the symbol itself, serving as its own 'description' for lack of something better to serve up. -// -// Return NULL when the symbol is unknown to the parser. -describeSymbol: function parser_describeSymbol(symbol) { - if (symbol !== this.EOF && this.terminal_descriptions_ && this.terminal_descriptions_[symbol]) { - return this.terminal_descriptions_[symbol]; - } - else if (symbol === this.EOF) { - return 'end of input'; - } - else if (this.terminals_[symbol]) { - return this.quoteName(this.terminals_[symbol]); - } - // Otherwise... this might refer to a RULE token i.e. a non-terminal: see if we can dig that one up. - // - // An example of this may be where a rule's action code contains a call like this: - // - // parser.describeSymbol(#$) - // - // to obtain a human-readable description or name of the current grammar rule. This comes handy in - // error handling action code blocks, for example. - var s = this.symbols_; - for (var key in s) { - if (s[key] === symbol) { - return key; - } - } - return null; -}, - -// Produce a (more or less) human-readable list of expected tokens at the point of failure. -// -// The produced list may contain token or token set descriptions instead of the tokens -// themselves to help turning this output into something that easier to read by humans -// unless `do_not_describe` parameter is set, in which case a list of the raw, *numeric*, -// expected terminals and nonterminals is produced. -// -// The returned list (array) will not contain any duplicate entries. -collect_expected_token_set: function parser_collect_expected_token_set(state, do_not_describe) { - var TERROR = this.TERROR; - var tokenset = []; - var check = {}; - // Has this (error?) state been outfitted with a custom expectations description text for human consumption? - // If so, use that one instead of the less palatable token set. - if (!do_not_describe && this.state_descriptions_ && this.state_descriptions_[state]) { - return [ - this.state_descriptions_[state] - ]; - } - for (var p in this.table[state]) { - p = +p; - if (p !== TERROR) { - var d = do_not_describe ? p : this.describeSymbol(p); - if (d && !check[d]) { - tokenset.push(d); - check[d] = true; // Mark this token description as already mentioned to prevent outputting duplicate entries. - } - } - } - return tokenset; -}, -productions_: bp({ - pop: u([ - 15, - s, - [16, 12] -]), - rule: u([ - 2, - s, - [3, 5], - s, - [2, 3], - 3, - s, - [1, 3] -]) -}), -performAction: function parser__PerformAction(yytext, yystate /* action[1] */, yysp, yyvstack) { -/* this == yyval */ -var yy = this.yy; - -switch (yystate) { -case 1: - /*! Production:: expressions : e EOF */ - typeof console !== 'undefined' ? console.log(yyvstack[yysp - 1]) : print(yyvstack[yysp - 1]); - return yyvstack[yysp - 1]; - break; - -case 2: - /*! Production:: e : e "+" e */ - this.$ = yyvstack[yysp - 2] + yyvstack[yysp]; - break; - -case 3: - /*! Production:: e : e "-" e */ - this.$ = yyvstack[yysp - 2] - yyvstack[yysp]; - break; - -case 4: - /*! Production:: e : e "*" e */ - this.$ = yyvstack[yysp - 2] * yyvstack[yysp]; - break; - -case 5: - /*! Production:: e : e "/" e */ - this.$ = yyvstack[yysp - 2] / yyvstack[yysp]; - break; - -case 6: - /*! Production:: e : e "^" e */ - this.$ = Math.pow(yyvstack[yysp - 2], yyvstack[yysp]); - break; - -case 7: - /*! Production:: e : e "!" */ - this.$ = (function fact(n) { - return n == 0 ? 1 : fact(n - 1) * n; - })(yyvstack[yysp - 1]); - break; - -case 8: - /*! Production:: e : e "%" */ - this.$ = yyvstack[yysp - 1] / 100; - break; - -case 9: - /*! Production:: e : "-" e */ - this.$ = -yyvstack[yysp]; - break; - -case 10: - /*! Production:: e : "(" e ")" */ - this.$ = yyvstack[yysp - 1]; - break; - -case 11: - /*! Production:: e : NUMBER */ - this.$ = Number(yytext); - break; - -case 12: - /*! Production:: e : E */ - this.$ = Math.E; - break; - -case 13: - /*! Production:: e : PI */ - this.$ = Math.PI; - break; - -} -}, -table: bt({ - len: u([ - 7, - 1, - 8, - 6, - 6, - s, - [0, 4], - s, - [6, 5], - s, - [0, 3], - 8, - s, - [9, 5], - 0 -]), - symbol: u([ - 4, - 10, - s, - [12, 5, 1], - 1, - 1, - s, - [3, 7, 1], - c, - [16, 5], - 16, - c, - [6, 36], - c, - [49, 7], - 11, - c, - [58, 8], - c, - [9, 37] -]), - type: u([ - s, - [2, 5], - 0, - 0, - 1, - s, - [2, 13], - 0, - c, - [6, 41], - s, - [2, 48] -]), - state: u([ - 1, - 2, - s, - [16, 7, 1] -]), - mode: u([ - s, - [1, 56], - s, - [2, 3], - c, - [8, 8], - c, - [9, 10], - c, - [11, 5], - c, - [9, 15], - c, - [10, 3], - 2 -]), - goto: u([ - 3, - 4, - 6, - 5, - s, - [7, 9, 1], - c, - [13, 5], - c, - [5, 30], - c, - [42, 7], - 23, - s, - [2, 3], - c, - [9, 5], - 2, - s, - [3, 3], - c, - [60, 7], - s, - [4, 4], - c, - [9, 3], - 4, - s, - [5, 5], - c, - [9, 3], - 5, - s, - [6, 6], - 14, - 15, - 6 -]) -}), -defaultActions: bda({ - idx: u([ - s, - [5, 4, 1], - 14, - 15, - 16, - 23 -]), - goto: u([ - 11, - 12, - 13, - 1, - s, - [7, 4, 1] -]) -}), -parseError: function parseError(str, hash, ExceptionClass) { - if (hash.recoverable && typeof this.trace === 'function') { - this.trace(str); - hash.destroy(); // destroy... well, *almost*! - } else { - if (!ExceptionClass) { - ExceptionClass = this.JisonParserError; - } - throw new ExceptionClass(str, hash); - } -}, -parse: function parse(input) { - var self = this, - stack = new Array(128), // token stack: stores token which leads to state at the same index (column storage) - sstack = new Array(128), // state stack: stores states (column storage) - - vstack = new Array(128), // semantic value stack - - table = this.table, - sp = 0; // 'stack pointer': index into the stacks - - var TERROR = this.TERROR, - EOF = this.EOF, - ERROR_RECOVERY_TOKEN_DISCARD_COUNT = (this.options.errorRecoveryTokenDiscardCount | 0) || 3; - var NO_ACTION = [0, table.length /* ensures that anyone using this new state will fail dramatically! */]; - - //this.reductionCount = this.shiftCount = 0; - - var lexer; - if (this.__lexer__) { - lexer = this.__lexer__; - } else { - lexer = this.__lexer__ = Object.create(this.lexer); - } - - var sharedState_yy = { - parseError: null, - quoteName: null, - lexer: null, - parser: null, - pre_parse: null, - post_parse: null - }; - // copy state - for (var k in this.yy) { - if (Object.prototype.hasOwnProperty.call(this.yy, k)) { - sharedState_yy[k] = this.yy[k]; - } - } - - sharedState_yy.lexer = lexer; - sharedState_yy.parser = this; - - - - - - - lexer.setInput(input, sharedState_yy); - - - - vstack[sp] = null; - sstack[sp] = 0; - stack[sp] = 0; - ++sp; - - var yytext = lexer.yytext; - - - // Does the shared state override the default `parseError` that already comes with this instance? - if (typeof sharedState_yy.parseError === 'function') { - this.parseError = function parseErrorAlt(str, hash, ExceptionClass) { - if (!ExceptionClass) { - ExceptionClass = this.JisonParserError; - } - return sharedState_yy.parseError(str, hash, ExceptionClass); - }; - } else { - this.parseError = this.originalParseError; - } - - // Does the shared state override the default `quoteName` that already comes with this instance? - if (typeof sharedState_yy.quoteName === 'function') { - this.quoteName = sharedState_yy.quoteName; - } else { - this.quoteName = this.originalQuoteName; - } - - // set up the cleanup function; make it an API so that external code can re-use this one in case of - // calamities or when the `%options no-try-catch` option has been specified for the grammar, in which - // case this parse() API method doesn't come with a `finally { ... }` block any more! - // - // NOTE: as this API uses parse() as a closure, it MUST be set again on every parse() invocation, - // or else your `sharedState`, etc. references will be *wrong*! - this.cleanupAfterParse = function parser_cleanupAfterParse(resultValue, invoke_post_methods, do_not_nuke_errorinfos) { - var rv; - - if (invoke_post_methods) { - if (sharedState_yy.post_parse) { - rv = sharedState_yy.post_parse.call(this, sharedState_yy, resultValue); - if (typeof rv !== 'undefined') resultValue = rv; - } - if (this.post_parse) { - rv = this.post_parse.call(this, sharedState_yy, resultValue); - if (typeof rv !== 'undefined') resultValue = rv; - } - } - - if (this.__reentrant_call_depth > 1) return resultValue; // do not (yet) kill the sharedState when this is a reentrant run. - - // clean up the lingering lexer structures as well: - if (lexer.cleanupAfterLex) { - lexer.cleanupAfterLex(do_not_nuke_errorinfos); - } - - // prevent lingering circular references from causing memory leaks: - if (sharedState_yy) { - sharedState_yy.parseError = undefined; - sharedState_yy.quoteName = undefined; - sharedState_yy.lexer = undefined; - sharedState_yy.parser = undefined; - if (lexer.yy === sharedState_yy) { - lexer.yy = undefined; - } - } - sharedState_yy = undefined; - this.parseError = this.originalParseError; - this.quoteName = this.originalQuoteName; - - // nuke the vstack[] array at least as that one will still reference obsoleted user values. - // To be safe, we nuke the other internal stack columns as well... - stack.length = 0; // fastest way to nuke an array without overly bothering the GC - sstack.length = 0; - - vstack.length = 0; - sp = 0; - - // nuke the error hash info instances created during this run. - // Userland code must COPY any data/references - // in the error hash instance(s) it is more permanently interested in. - if (!do_not_nuke_errorinfos) { - for (var i = this.__error_infos.length - 1; i >= 0; i--) { - var el = this.__error_infos[i]; - if (el && typeof el.destroy === 'function') { - el.destroy(); - } - } - this.__error_infos.length = 0; - } - - return resultValue; - }; - - // NOTE: as this API uses parse() as a closure, it MUST be set again on every parse() invocation, - // or else your `lexer`, `sharedState`, etc. references will be *wrong*! - this.constructParseErrorInfo = function parser_constructParseErrorInfo(msg, ex, expected, recoverable) { - var pei = { - errStr: msg, - exception: ex, - text: lexer.match, - value: lexer.yytext, - token: this.describeSymbol(symbol) || symbol, - token_id: symbol, - line: lexer.yylineno, - - expected: expected, - recoverable: recoverable, - state: state, - action: action, - new_state: newState, - symbol_stack: stack, - state_stack: sstack, - value_stack: vstack, - - stack_pointer: sp, - yy: sharedState_yy, - lexer: lexer, - parser: this, - - // and make sure the error info doesn't stay due to potential - // ref cycle via userland code manipulations. - // These would otherwise all be memory leak opportunities! - // - // Note that only array and object references are nuked as those - // constitute the set of elements which can produce a cyclic ref. - // The rest of the members is kept intact as they are harmless. - destroy: function destructParseErrorInfo() { - // remove cyclic references added to error info: - // info.yy = null; - // info.lexer = null; - // info.value = null; - // info.value_stack = null; - // ... - var rec = !!this.recoverable; - for (var key in this) { - if (this.hasOwnProperty(key) && typeof key === 'object') { - this[key] = undefined; - } - } - this.recoverable = rec; - } - }; - // track this instance so we can `destroy()` it once we deem it superfluous and ready for garbage collection! - this.__error_infos.push(pei); - return pei; - }; - - - function lex() { - var token = lexer.lex(); - // if token isn't its numeric value, convert - if (typeof token !== 'number') { - token = self.symbols_[token] || token; - } - return token || EOF; - } - - - var symbol = 0; - - var state, action, r, t; - var yyval = { - $: true, - _$: undefined, - yy: sharedState_yy - }; - var p, len, this_production; - - var newState; - var retval = false; - - - try { - this.__reentrant_call_depth++; - - if (this.pre_parse) { - this.pre_parse.call(this, sharedState_yy); - } - if (sharedState_yy.pre_parse) { - sharedState_yy.pre_parse.call(this, sharedState_yy); - } - - newState = sstack[sp - 1]; - for (;;) { - // retrieve state number from top of stack - state = newState; // sstack[sp - 1]; - - // use default actions if available - if (this.defaultActions[state]) { - action = 2; - newState = this.defaultActions[state]; - } else { - // The single `==` condition below covers both these `===` comparisons in a single - // operation: - // - // if (symbol === null || typeof symbol === 'undefined') ... - if (!symbol) { - symbol = lex(); - } - // read action for current state and first input - t = (table[state] && table[state][symbol]) || NO_ACTION; - newState = t[1]; - action = t[0]; - - - - - // handle parse error - if (!action) { - var errStr; - var errSymbolDescr = (this.describeSymbol(symbol) || symbol); - var expected = this.collect_expected_token_set(state); - - // Report error - if (typeof lexer.yylineno === 'number') { - errStr = 'Parse error on line ' + (lexer.yylineno + 1) + ': '; - } else { - errStr = 'Parse error: '; - } - if (lexer.showPosition) { - errStr += '\n' + lexer.showPosition(79 - 10, 10) + '\n'; - } - if (expected.length) { - errStr += 'Expecting ' + expected.join(', ') + ', got unexpected ' + errSymbolDescr; - } else { - errStr += 'Unexpected ' + errSymbolDescr; - } - // we cannot recover from the error! - p = this.constructParseErrorInfo(errStr, null, expected, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - - - } - - - - - - - - - - switch (action) { - // catch misc. parse failures: - default: - // this shouldn't happen, unless resolve defaults are off - if (action instanceof Array) { - p = this.constructParseErrorInfo(('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol), null, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - // Another case of better safe than sorry: in case state transitions come out of another error recovery process - // or a buggy LUT (LookUp Table): - p = this.constructParseErrorInfo('Parsing halted. No viable error recovery approach available due to internal system failure.', null, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - - // shift: - case 1: - //this.shiftCount++; - stack[sp] = symbol; - vstack[sp] = lexer.yytext; - - sstack[sp] = newState; // push state - ++sp; - symbol = 0; - - // Pick up the lexer details for the current symbol as that one is not 'look-ahead' any more: - - yytext = lexer.yytext; - - - - - - - - - - - - - - continue; - - // reduce: - case 2: - //this.reductionCount++; - this_production = this.productions_[newState - 1]; // `this.productions_[]` is zero-based indexed while states start from 1 upwards... - len = this_production[1]; - - - - - - - // Make sure subsequent `$$ = $1` default action doesn't fail - // for rules where len==0 as then there's no $1 (you're reducing an epsilon rule then!) - // - // Also do this to prevent nasty action block codes to *read* `$0` or `$$` - // and *not* get `undefined` as a result for their efforts! - vstack[sp] = undefined; - - // perform semantic action - yyval.$ = vstack[sp - len]; // default to $$ = $1; result must produce `undefined` when len == 0, as then there's no $1 - - - - - - - - - - - r = this.performAction.call(yyval, yytext, newState, sp - 1, vstack); - - if (typeof r !== 'undefined') { - retval = r; - break; - } - - // pop off stack - sp -= len; - - // don't overwrite the `symbol` variable: use a local var to speed things up: - var ntsymbol = this_production[0]; // push nonterminal (reduce) - stack[sp] = ntsymbol; - vstack[sp] = yyval.$; - - // goto new state = table[STATE][NONTERMINAL] - newState = table[sstack[sp - 1]][ntsymbol]; - sstack[sp] = newState; - ++sp; - - continue; - - // accept: - case 3: - retval = true; - // Return the `$accept` rule's `$$` result, if available. - // - // Also note that JISON always adds this top-most `$accept` rule (with implicit, - // default, action): - // - // $accept: $end - // %{ $$ = $1; @$ = @1; %} - // - // which, combined with the parse kernel's `$accept` state behaviour coded below, - // will produce the `$$` value output of the rule as the parse result, - // IFF that result is *not* `undefined`. (See also the parser kernel code.) - // - // In code: - // - // %{ - // @$ = @1; // if location tracking support is included - // if (typeof $1 !== 'undefined') - // return $1; - // else - // return true; // the default parse result if the rule actions don't produce anything - // %} - if (typeof yyval.$ !== 'undefined') { - retval = yyval.$; - } - break; - } - - // break out of loop: we accept or fail with error - break; - } - } catch (ex) { - // report exceptions through the parseError callback too, but keep the exception intact - // if it is a known parser or lexer error which has been thrown by parseError() already: - if (ex instanceof this.JisonParserError) { - throw ex; - } - else if (lexer && typeof lexer.JisonLexerError === 'function' && ex instanceof lexer.JisonLexerError) { - throw ex; - } - else { - p = this.constructParseErrorInfo('Parsing aborted due to exception.', ex, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - } - } finally { - retval = this.cleanupAfterParse(retval, true, true); - this.__reentrant_call_depth--; - } - - return retval; -} -}; -parser.originalParseError = parser.parseError; -parser.originalQuoteName = parser.quoteName; - - -/* lexer generated by jison-lex 0.3.4-166 */ -/* - * Returns a Lexer object of the following structure: - * - * Lexer: { - * yy: {} The so-called "shared state" or rather the *source* of it; - * the real "shared state" `yy` passed around to - * the rule actions, etc. is a derivative/copy of this one, - * not a direct reference! - * } - * - * Lexer.prototype: { - * yy: {}, - * EOF: 1, - * ERROR: 2, - * - * JisonLexerError: function(msg, hash), - * - * performAction: function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * where `...` denotes the (optional) additional arguments the user passed to - * `lexer.lex(...)` and specified by way of `%parse-param ...` in the **parser** grammar file - * - * The function parameters and `this` have the following value/meaning: - * - `this` : reference to the `lexer` instance. - * - * - `yy` : a reference to the `yy` "shared state" object which was passed to the lexer - * by way of the `lexer.setInput(str, yy)` API before. - * - * - `yy_` : lexer instance reference used internally. - * - * - `$avoiding_name_collisions` : index of the matched lexer rule (regex), used internally. - * - * - `YY_START`: the current lexer "start condition" state. - * - * - `...` : the extra arguments you specified in the `%parse-param` statement in your - * **parser** grammar definition file and which are passed to the lexer via - * its `lexer.lex(...)` API. - * - * parseError: function(str, hash, ExceptionClass), - * - * constructLexErrorInfo: function(error_message, is_recoverable), - * Helper function. - * Produces a new errorInfo 'hash object' which can be passed into `parseError()`. - * See it's use in this lexer kernel in many places; example usage: - * - * var infoObj = lexer.constructParseErrorInfo('fail!', true); - * var retVal = lexer.parseError(infoObj.errStr, infoObj, lexer.JisonLexerError); - * - * options: { ... lexer %options ... }, - * - * lex: function([args...]), - * Produce one token of lexed input, which was passed in earlier via the `lexer.setInput()` API. - * You MAY use the additional `args...` parameters as per `%parse-param` spec of the **parser** grammar: - * these extra `args...` are passed verbatim to the lexer rules' action code. - * - * cleanupAfterLex: function(do_not_nuke_errorinfos), - * Helper function. - * This helper API is invoked when the parse process has completed. This helper may - * be invoked by user code to ensure the internal lexer gets properly garbage collected. - * - * setInput: function(input, [yy]), - * input: function(), - * unput: function(str), - * more: function(), - * reject: function(), - * less: function(n), - * pastInput: function(n), - * upcomingInput: function(n), - * showPosition: function(), - * test_match: function(regex_match_array, rule_index), - * next: function(...), - * lex: function(...), - * begin: function(condition), - * pushState: function(condition), - * popState: function(), - * topState: function(), - * _currentRules: function(), - * stateStackSize: function(), - * - * options: { ... lexer %options ... }, - * - * performAction: function(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * rules: [...], - * conditions: {associative list: name ==> set}, - * } - * - * - * token location info (`yylloc`): { - * first_line: n, - * last_line: n, - * first_column: n, - * last_column: n, - * range: [start_number, end_number] - * (where the numbers are indexes into the input string, zero-based) - * } - * - * --- - * - * The `parseError` function receives a 'hash' object with these members for lexer errors: - * - * { - * text: (matched text) - * token: (the produced terminal token, if any) - * token_id: (the produced terminal token numeric ID, if any) - * line: (yylineno) - * loc: (yylloc) - * recoverable: (boolean: TRUE when the parser MAY have an error recovery rule - * available for this particular error) - * yy: (object: the current parser internal "shared state" `yy` - * as is also available in the rule actions; this can be used, - * for instance, for advanced error analysis and reporting) - * lexer: (reference to the current lexer instance used by the parser) - * } - * - * while `this` will reference the current lexer instance. - * - * When `parseError` is invoked by the lexer, the default implementation will - * attempt to invoke `yy.parser.parseError()`; when this callback is not provided - * it will try to invoke `yy.parseError()` instead. When that callback is also not - * provided, a `JisonLexerError` exception will be thrown containing the error - * message and `hash`, as constructed by the `constructLexErrorInfo()` API. - * - * Note that the lexer's `JisonLexerError` error class is passed via the - * `ExceptionClass` argument, which is invoked to construct the exception - * instance to be thrown, so technically `parseError` will throw the object - * produced by the `new ExceptionClass(str, hash)` JavaScript expression. - * - * --- - * - * You can specify lexer options by setting / modifying the `.options` object of your Lexer instance. - * These options are available: - * - * (Options are permanent.) - * - * yy: { - * parseError: function(str, hash, ExceptionClass) - * optional: overrides the default `parseError` function. - * } - * - * lexer.options: { - * pre_lex: function() - * optional: is invoked before the lexer is invoked to produce another token. - * `this` refers to the Lexer object. - * post_lex: function(token) { return token; } - * optional: is invoked when the lexer has produced a token `token`; - * this function can override the returned token value by returning another. - * When it does not return any (truthy) value, the lexer will return - * the original `token`. - * `this` refers to the Lexer object. - * - * WARNING: the next set of options are not meant to be changed. They echo the abilities of - * the lexer as per when it was compiled! - * - * ranges: boolean - * optional: `true` ==> token location info will include a .range[] member. - * flex: boolean - * optional: `true` ==> flex-like lexing behaviour where the rules are tested - * exhaustively to find the longest match. - * backtrack_lexer: boolean - * optional: `true` ==> lexer regexes are tested in order and for invoked; - * the lexer terminates the scan when a token is returned by the action code. - * xregexp: boolean - * optional: `true` ==> lexer rule regexes are "extended regex format" requiring the - * `XRegExp` library. When this %option has not been specified at compile time, all lexer - * rule regexes have been written as standard JavaScript RegExp expressions. - * } - */ - - -var lexer = (function () { -// See also: -// http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508 -// but we keep the prototype.constructor and prototype.name assignment lines too for compatibility -// with userland code which might access the derived class in a 'classic' way. -function JisonLexerError(msg, hash) { - Object.defineProperty(this, 'name', { - enumerable: false, - writable: false, - value: 'JisonLexerError' - }); - - if (msg == null) msg = '???'; - - Object.defineProperty(this, 'message', { - enumerable: false, - writable: true, - value: msg - }); - - this.hash = hash; - - var stacktrace; - if (hash && hash.exception instanceof Error) { - var ex2 = hash.exception; - this.message = ex2.message || msg; - stacktrace = ex2.stack; - } - if (!stacktrace) { - if (Error.hasOwnProperty('captureStackTrace')) { // V8 - Error.captureStackTrace(this, this.constructor); - } else { - stacktrace = (new Error(msg)).stack; - } - } - if (stacktrace) { - Object.defineProperty(this, 'stack', { - enumerable: false, - writable: false, - value: stacktrace - }); - } -} - -if (typeof Object.setPrototypeOf === 'function') { - Object.setPrototypeOf(JisonLexerError.prototype, Error.prototype); -} else { - JisonLexerError.prototype = Object.create(Error.prototype); -} -JisonLexerError.prototype.constructor = JisonLexerError; -JisonLexerError.prototype.name = 'JisonLexerError'; - - - - -var lexer = { - - // Code Generator Information Report - // --------------------------------- - // - // Options: - // - // backtracking: .................... false - // location.ranges: ................. false - // location line+column tracking: ... true - // - // - // Forwarded Parser Analysis flags: - // - // uses yyleng: ..................... false - // uses yylineno: ................... false - // uses yytext: ..................... true - // uses yylloc: ..................... false - // uses lexer values: ............... true / true - // location tracking: ............... false - // location assignment: ............. false - // - // - // Lexer Analysis flags: - // - // uses yyleng: ..................... undefined - // uses yylineno: ................... undefined - // uses yytext: ..................... undefined - // uses yylloc: ..................... undefined - // uses ParseError API: ............. undefined - // uses location tracking & editing: undefined - // uses more() API: ................. undefined - // uses unput() API: ................ undefined - // uses reject() API: ............... undefined - // uses less() API: ................. undefined - // uses display APIs pastInput(), upcomingInput(), showPosition(): - // ............................. undefined - // uses describeYYLLOC() API: ....... undefined - // - // --------- END OF REPORT ----------- - - - EOF: 1, - ERROR: 2, - - // JisonLexerError: JisonLexerError, /// <-- injected by the code generator - - // options: {}, /// <-- injected by the code generator - - // yy: ..., /// <-- injected by setInput() - - __currentRuleSet__: null, /// <-- internal rule set cache for the current lexer state - - __error_infos: [], /// INTERNAL USE ONLY: the set of lexErrorInfo objects created since the last cleanup - - __decompressed: false, /// INTERNAL USE ONLY: mark whether the lexer instance has been 'unfolded' completely and is now ready for use - - done: false, /// INTERNAL USE ONLY - _backtrack: false, /// INTERNAL USE ONLY - _input: '', /// INTERNAL USE ONLY - _more: false, /// INTERNAL USE ONLY - _signaled_error_token: false, /// INTERNAL USE ONLY - - conditionStack: [], /// INTERNAL USE ONLY; managed via `pushState()`, `popState()`, `topState()` and `stateStackSize()` - - match: '', /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks input which has been matched so far for the lexer token under construction. `match` is identical to `yytext` except that this one still contains the matched input string after `lexer.performAction()` has been invoked, where userland code MAY have changed/replaced the `yytext` value entirely! - matched: '', /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks entire input which has been matched so far - matches: false, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks RE match result for last (successful) match attempt - yytext: '', /// ADVANCED USE ONLY: tracks input which has been matched so far for the lexer token under construction; this value is transferred to the parser as the 'token value' when the parser consumes the lexer token produced through a call to the `lex()` API. - offset: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks the 'cursor position' in the input string, i.e. the number of characters matched so far - yyleng: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: length of matched input for the token under construction (`yytext`) - yylineno: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: 'line number' at which the token under construction is located - yylloc: null, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks location info (lines + columns) for the token under construction - - /** - INTERNAL USE: construct a suitable error info hash object instance for `parseError`. - - @public - @this {RegExpLexer} - */ - constructLexErrorInfo: function lexer_constructLexErrorInfo(msg, recoverable) { - /** @constructor */ - var pei = { - errStr: msg, - recoverable: !!recoverable, - text: this.match, // This one MAY be empty; userland code should use the `upcomingInput` API to obtain more text which follows the 'lexer cursor position'... - token: null, - line: this.yylineno, - loc: this.yylloc, - yy: this.yy, - lexer: this, - - /** - and make sure the error info doesn't stay due to potential - ref cycle via userland code manipulations. - These would otherwise all be memory leak opportunities! - - Note that only array and object references are nuked as those - constitute the set of elements which can produce a cyclic ref. - The rest of the members is kept intact as they are harmless. - - @public - @this {LexErrorInfo} - */ - destroy: function destructLexErrorInfo() { - // remove cyclic references added to error info: - // info.yy = null; - // info.lexer = null; - // ... - var rec = !!this.recoverable; - for (var key in this) { - if (this.hasOwnProperty(key) && typeof key === 'object') { - this[key] = undefined; - } - } - this.recoverable = rec; - } - }; - // track this instance so we can `destroy()` it once we deem it superfluous and ready for garbage collection! - this.__error_infos.push(pei); - return pei; - }, - - /** - handler which is invoked when a lexer error occurs. - - @public - @this {RegExpLexer} - */ - parseError: function lexer_parseError(str, hash, ExceptionClass) { - if (!ExceptionClass) { - ExceptionClass = this.JisonLexerError; - } - if (this.yy.parser && typeof this.yy.parser.parseError === 'function') { - return this.yy.parser.parseError(str, hash, ExceptionClass) || this.ERROR; - } else if (typeof this.yy.parseError === 'function') { - return this.yy.parseError(str, hash, ExceptionClass) || this.ERROR; - } else { - throw new ExceptionClass(str, hash); - } - }, - - /** - method which implements `yyerror(str, ...args)` functionality for use inside lexer actions. - - @public - @this {RegExpLexer} - */ - yyerror: function yyError(str /*, ...args */) { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': ' + str, this.options.lexerErrorsAreRecoverable); - - // Add any extra args to the hash under the name `extra_error_attributes`: - var args = Array.prototype.slice.call(arguments, 1); - if (args.length) { - hash.extra_error_attributes = args; - } - - return (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - }, - - /** - final cleanup function for when we have completed lexing the input; - make it an API so that external code can use this one once userland - code has decided it's time to destroy any lingering lexer error - hash object instances and the like: this function helps to clean - up these constructs, which *may* carry cyclic references which would - otherwise prevent the instances from being properly and timely - garbage-collected, i.e. this function helps prevent memory leaks! - - @public - @this {RegExpLexer} - */ - cleanupAfterLex: function lexer_cleanupAfterLex(do_not_nuke_errorinfos) { - var rv; - - // prevent lingering circular references from causing memory leaks: - this.setInput('', {}); - - // nuke the error hash info instances created during this run. - // Userland code must COPY any data/references - // in the error hash instance(s) it is more permanently interested in. - if (!do_not_nuke_errorinfos) { - for (var i = this.__error_infos.length - 1; i >= 0; i--) { - var el = this.__error_infos[i]; - if (el && typeof el.destroy === 'function') { - el.destroy(); - } - } - this.__error_infos.length = 0; - } - - return this; - }, - - /** - clear the lexer token context; intended for internal use only - - @public - @this {RegExpLexer} - */ - clear: function lexer_clear() { - this.yytext = ''; - this.yyleng = 0; - this.match = ''; - this.matches = false; - this._more = false; - this._backtrack = false; - - var col = this.yylloc ? this.yylloc.last_column : 0; - this.yylloc = { - first_line: this.yylineno + 1, - first_column: col, - last_line: this.yylineno + 1, - last_column: col, - - range: (this.options.ranges ? [this.offset, this.offset] : undefined) - }; - }, - - /** - resets the lexer, sets new input - - @public - @this {RegExpLexer} - */ - setInput: function lexer_setInput(input, yy) { - this.yy = yy || this.yy || {}; - - // also check if we've fully initialized the lexer instance, - // including expansion work to be done to go from a loaded - // lexer to a usable lexer: - if (!this.__decompressed) { - // step 1: decompress the regex list: - var rules = this.rules; - for (var i = 0, len = rules.length; i < len; i++) { - var rule_re = rules[i]; - - // compression: is the RE an xref to another RE slot in the rules[] table? - if (typeof rule_re === 'number') { - rules[i] = rules[rule_re]; - } - } - - // step 2: unfold the conditions[] set to make these ready for use: - var conditions = this.conditions; - for (var k in conditions) { - var spec = conditions[k]; - - var rule_ids = spec.rules; - - var len = rule_ids.length; - var rule_regexes = new Array(len + 1); // slot 0 is unused; we use a 1-based index approach here to keep the hottest code in `lexer_next()` fast and simple! - var rule_new_ids = new Array(len + 1); - - for (var i = 0; i < len; i++) { - var idx = rule_ids[i]; - var rule_re = rules[idx]; - rule_regexes[i + 1] = rule_re; - rule_new_ids[i + 1] = idx; - } - - spec.rules = rule_new_ids; - spec.__rule_regexes = rule_regexes; - spec.__rule_count = len; - } - - this.__decompressed = true; - } - - this._input = input || ''; - this.clear(); - this._signaled_error_token = false; - this.done = false; - this.yylineno = 0; - this.matched = ''; - this.conditionStack = ['INITIAL']; - this.__currentRuleSet__ = null; - this.yylloc = { - first_line: 1, - first_column: 0, - last_line: 1, - last_column: 0, - - range: (this.options.ranges ? [0, 0] : undefined) - }; - this.offset = 0; - return this; - }, - - /** - push a new input into the lexer and activate it: - the old input position is stored and will be resumed - once this new input has been consumed. - - Use this API to help implement C-preprocessor-like - `#include` statements. - - Available options: - - - `emit_EOF_at_end` : {int} the `EOF`-like token to emit - when the new input is consumed: use - this to mark the end of the new input - in the parser grammar. zero/falsey - token value means no end marker token - will be emitted before the lexer - resumes reading from the previous input. - - @public - @this {RegExpLexer} - */ - pushInput: function lexer_pushInput(input, label, options) { - options = options || {}; - - this._input = input || ''; - this.clear(); - // this._signaled_error_token = false; - this.done = false; - this.yylineno = 0; - this.matched = ''; - // this.conditionStack = ['INITIAL']; - // this.__currentRuleSet__ = null; - this.yylloc = { - first_line: 1, - first_column: 0, - last_line: 1, - last_column: 0, - - range: (this.options.ranges ? [0, 0] : undefined) - }; - this.offset = 0; - return this; - }, - - /** - consumes and returns one char from the input - - @public - @this {RegExpLexer} - */ - input: function lexer_input() { - if (!this._input) { - //this.done = true; -- don't set `done` as we want the lex()/next() API to be able to produce one custom EOF token match after this anyhow. (lexer can match special <> tokens and perform user action code for a <> match, but only does so *once*) - return null; - } - var ch = this._input[0]; - this.yytext += ch; - this.yyleng++; - this.offset++; - this.match += ch; - this.matched += ch; - // Count the linenumber up when we hit the LF (or a stand-alone CR). - // On CRLF, the linenumber is incremented when you fetch the CR or the CRLF combo - // and we advance immediately past the LF as well, returning both together as if - // it was all a single 'character' only. - var slice_len = 1; - var lines = false; - if (ch === '\n') { - lines = true; - } else if (ch === '\r') { - lines = true; - var ch2 = this._input[1]; - if (ch2 === '\n') { - slice_len++; - ch += ch2; - this.yytext += ch2; - this.yyleng++; - this.offset++; - this.match += ch2; - this.matched += ch2; - if (this.options.ranges) { - this.yylloc.range[1]++; - } - } - } - if (lines) { - this.yylineno++; - this.yylloc.last_line++; - this.yylloc.last_column = 0; - } else { - this.yylloc.last_column++; - } - if (this.options.ranges) { - this.yylloc.range[1]++; - } - - this._input = this._input.slice(slice_len); - return ch; - }, - - /** - unshifts one char (or an entire string) into the input - - @public - @this {RegExpLexer} - */ - unput: function lexer_unput(ch) { - var len = ch.length; - var lines = ch.split(/(?:\r\n?|\n)/g); - - this._input = ch + this._input; - this.yytext = this.yytext.substr(0, this.yytext.length - len); - this.yyleng = this.yytext.length; - this.offset -= len; - this.match = this.match.substr(0, this.match.length - len); - this.matched = this.matched.substr(0, this.matched.length - len); - - if (lines.length > 1) { - this.yylineno -= lines.length - 1; - - this.yylloc.last_line = this.yylineno + 1; - var pre = this.match; - var pre_lines = pre.split(/(?:\r\n?|\n)/g); - if (pre_lines.length === 1) { - pre = this.matched; - pre_lines = pre.split(/(?:\r\n?|\n)/g); - } - this.yylloc.last_column = pre_lines[pre_lines.length - 1].length; - } else { - this.yylloc.last_column -= len; - } - - if (this.options.ranges) { - this.yylloc.range[1] = this.yylloc.range[0] + this.yyleng; - } - this.done = false; - return this; - }, - - /** - cache matched text and append it on next action - - @public - @this {RegExpLexer} - */ - more: function lexer_more() { - this._more = true; - return this; - }, - - /** - signal the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead. - - @public - @this {RegExpLexer} - */ - reject: function lexer_reject() { - if (this.options.backtrack_lexer) { - this._backtrack = true; - } else { - // when the `parseError()` call returns, we MUST ensure that the error is registered. - // We accomplish this by signaling an 'error' token to be produced for the current - // `.lex()` run. - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).' + pos_str, false); - this._signaled_error_token = (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - } - return this; - }, - - /** - retain first n characters of the match - - @public - @this {RegExpLexer} - */ - less: function lexer_less(n) { - return this.unput(this.match.slice(n)); - }, - - /** - return (part of the) already matched input, i.e. for error messages. - - Limit the returned string length to `maxSize` (default: 20). - - Limit the returned string to the `maxLines` number of lines of input (default: 1). - - Negative limit values equal *unlimited*. - - @public - @this {RegExpLexer} - */ - pastInput: function lexer_pastInput(maxSize, maxLines) { - var past = this.matched.substring(0, this.matched.length - this.match.length); - if (maxSize < 0) - maxSize = past.length; - else if (!maxSize) - maxSize = 20; - if (maxLines < 0) - maxLines = past.length; // can't ever have more input lines than this! - else if (!maxLines) - maxLines = 1; - // `substr` anticipation: treat \r\n as a single character and take a little - // more than necessary so that we can still properly check against maxSize - // after we've transformed and limited the newLines in here: - past = past.substr(-maxSize * 2 - 2); - // now that we have a significantly reduced string to process, transform the newlines - // and chop them, then limit them: - var a = past.replace(/\r\n|\r/g, '\n').split('\n'); - a = a.slice(-maxLines); - past = a.join('\n'); - // When, after limiting to maxLines, we still have too much to return, - // do add an ellipsis prefix... - if (past.length > maxSize) { - past = '...' + past.substr(-maxSize); - } - return past; - }, - - /** - return (part of the) upcoming input, i.e. for error messages. - - Limit the returned string length to `maxSize` (default: 20). - - Limit the returned string to the `maxLines` number of lines of input (default: 1). - - Negative limit values equal *unlimited*. - - @public - @this {RegExpLexer} - */ - upcomingInput: function lexer_upcomingInput(maxSize, maxLines) { - var next = this.match; - if (maxSize < 0) - maxSize = next.length + this._input.length; - else if (!maxSize) - maxSize = 20; - if (maxLines < 0) - maxLines = maxSize; // can't ever have more input lines than this! - else if (!maxLines) - maxLines = 1; - // `substring` anticipation: treat \r\n as a single character and take a little - // more than necessary so that we can still properly check against maxSize - // after we've transformed and limited the newLines in here: - if (next.length < maxSize * 2 + 2) { - next += this._input.substring(0, maxSize * 2 + 2); // substring is faster on Chrome/V8 - } - // now that we have a significantly reduced string to process, transform the newlines - // and chop them, then limit them: - var a = next.replace(/\r\n|\r/g, '\n').split('\n'); - a = a.slice(0, maxLines); - next = a.join('\n'); - // When, after limiting to maxLines, we still have too much to return, - // do add an ellipsis postfix... - if (next.length > maxSize) { - next = next.substring(0, maxSize) + '...'; - } - return next; - }, - - /** - return a string which displays the character position where the lexing error occurred, i.e. for error messages - - @public - @this {RegExpLexer} - */ - showPosition: function lexer_showPosition(maxPrefix, maxPostfix) { - var pre = this.pastInput(maxPrefix).replace(/\s/g, ' '); - var c = new Array(pre.length + 1).join('-'); - return pre + this.upcomingInput(maxPostfix).replace(/\s/g, ' ') + '\n' + c + '^'; - }, - - /** - helper function, used to produce a human readable description as a string, given - the input `yylloc` location object. - - Set `display_range_too` to TRUE to include the string character index position(s) - in the description if the `yylloc.range` is available. - - @public - @this {RegExpLexer} - */ - describeYYLLOC: function lexer_describe_yylloc(yylloc, display_range_too) { - var l1 = yylloc.first_line; - var l2 = yylloc.last_line; - var o1 = yylloc.first_column; - var o2 = yylloc.last_column; - var dl = l2 - l1; - var d_o = o2 - o1; - var rv; - if (dl === 0) { - rv = 'line ' + l1 + ', '; - if (d_o === 1) { - rv += 'column ' + o1; - } else { - rv += 'columns ' + o1 + ' .. ' + o2; - } - } else { - rv = 'lines ' + l1 + '(column ' + o1 + ') .. ' + l2 + '(column ' + o2 + ')'; - } - if (yylloc.range && display_range_too) { - var r1 = yylloc.range[0]; - var r2 = yylloc.range[1] - 1; - if (r2 === r1) { - rv += ' {String Offset: ' + r1 + '}'; - } else { - rv += ' {String Offset range: ' + r1 + ' .. ' + r2 + '}'; - } - } - return rv; - // return JSON.stringify(yylloc); - }, - - /** - test the lexed token: return FALSE when not a match, otherwise return token. - - `match` is supposed to be an array coming out of a regex match, i.e. `match[0]` - contains the actually matched text string. - - Also move the input cursor forward and update the match collectors: - - - `yytext` - - `yyleng` - - `match` - - `matches` - - `yylloc` - - `offset` - - @public - @this {RegExpLexer} - */ - test_match: function lexer_test_match(match, indexed_rule) { - var token, - lines, - backup, - match_str, - match_str_len; - - if (this.options.backtrack_lexer) { - // save context - backup = { - yylineno: this.yylineno, - yylloc: { - first_line: this.yylloc.first_line, - last_line: this.yylloc.last_line, - first_column: this.yylloc.first_column, - last_column: this.yylloc.last_column, - - range: (this.options.ranges ? this.yylloc.range.slice(0) : undefined) - }, - yytext: this.yytext, - match: this.match, - matches: this.matches, - matched: this.matched, - yyleng: this.yyleng, - offset: this.offset, - _more: this._more, - _input: this._input, - //_signaled_error_token: this._signaled_error_token, - yy: this.yy, - conditionStack: this.conditionStack.slice(0), - done: this.done - }; - } - - match_str = match[0]; - match_str_len = match_str.length; - // if (match_str.indexOf('\n') !== -1 || match_str.indexOf('\r') !== -1) { - lines = match_str.split(/(?:\r\n?|\n)/g); - if (lines.length > 1) { - this.yylineno += lines.length - 1; - - this.yylloc.last_line = this.yylineno + 1, - this.yylloc.last_column = lines[lines.length - 1].length; - } else { - this.yylloc.last_column += match_str_len; - } - // } - this.yytext += match_str; - this.match += match_str; - this.matches = match; - this.yyleng = this.yytext.length; - if (this.options.ranges) { - this.yylloc.range[1] += match_str_len; - } - // previous lex rules MAY have invoked the `more()` API rather than producing a token: - // those rules will already have moved this `offset` forward matching their match lengths, - // hence we must only add our own match length now: - this.offset += match_str_len; - this._more = false; - this._backtrack = false; - this._input = this._input.slice(match_str_len); - this.matched += match_str; - - // calling this method: - // - // function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START) {...} - token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1] /* = YY_START */); - // otherwise, when the action codes are all simple return token statements: - //token = this.simpleCaseActionClusters[indexed_rule]; - - if (this.done && this._input) { - this.done = false; - } - if (token) { - return token; - } else if (this._backtrack) { - // recover context - for (var k in backup) { - this[k] = backup[k]; - } - this.__currentRuleSet__ = null; - return false; // rule action called reject() implying the next rule should be tested instead. - } else if (this._signaled_error_token) { - // produce one 'error' token as `.parseError()` in `reject()` did not guarantee a failure signal by throwing an exception! - token = this._signaled_error_token; - this._signaled_error_token = false; - return token; - } - return false; - }, - - /** - return next match in input - - @public - @this {RegExpLexer} - */ - next: function lexer_next() { - if (this.done) { - this.clear(); - return this.EOF; - } - if (!this._input) { - this.done = true; - } - - var token, - match, - tempMatch, - index; - if (!this._more) { - this.clear(); - } - var spec = this.__currentRuleSet__; - if (!spec) { - // Update the ruleset cache as we apparently encountered a state change or just started lexing. - // The cache is set up for fast lookup -- we assume a lexer will switch states much less often than it will - // invoke the `lex()` token-producing API and related APIs, hence caching the set for direct access helps - // speed up those activities a tiny bit. - spec = this.__currentRuleSet__ = this._currentRules(); - // Check whether a *sane* condition has been pushed before: this makes the lexer robust against - // user-programmer bugs such as https://github.com/zaach/jison-lex/issues/19 - if (!spec || !spec.rules) { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Internal lexer engine error' + lineno_msg + ': The lex grammar programmer pushed a non-existing condition name "' + this.topState() + '"; this is a fatal error and should be reported to the application programmer team!' + pos_str, false); - // produce one 'error' token until this situation has been resolved, most probably by parse termination! - return (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - } - } - - var rule_ids = spec.rules; - //var dispatch = spec.__dispatch_lut; - var regexes = spec.__rule_regexes; - var len = spec.__rule_count; - - // Note: the arrays are 1-based, while `len` itself is a valid index, - // hence the non-standard less-or-equal check in the next loop condition! - for (var i = 1; i <= len; i++) { - tempMatch = this._input.match(regexes[i]); - if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { - match = tempMatch; - index = i; - if (this.options.backtrack_lexer) { - token = this.test_match(tempMatch, rule_ids[i]); - if (token !== false) { - return token; - } else if (this._backtrack) { - match = undefined; - continue; // rule action called reject() implying a rule MISmatch. - } else { - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - } else if (!this.options.flex) { - break; - } - } - } - if (match) { - token = this.test_match(match, rule_ids[index]); - if (token !== false) { - return token; - } - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - if (!this._input) { - this.done = true; - this.clear(); - return this.EOF; - } else { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': Unrecognized text.' + pos_str, this.options.lexerErrorsAreRecoverable); - token = (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - if (token === this.ERROR) { - // we can try to recover from a lexer error that `parseError()` did not 'recover' for us - // by moving forward at least one character at a time: - if (!this.match.length) { - this.input(); - } - } - return token; - } - }, - - /** - return next match that has a token - - @public - @this {RegExpLexer} - */ - lex: function lexer_lex() { - var r; - // allow the PRE/POST handlers set/modify the return token for maximum flexibility of the generated lexer: - if (typeof this.options.pre_lex === 'function') { - r = this.options.pre_lex.call(this); - } - while (!r) { - r = this.next(); - } - if (typeof this.options.post_lex === 'function') { - // (also account for a userdef function which does not return any value: keep the token as is) - r = this.options.post_lex.call(this, r) || r; - } - return r; - }, - - /** - backwards compatible alias for `pushState()`; - the latter is symmetrical with `popState()` and we advise to use - those APIs in any modern lexer code, rather than `begin()`. - - @public - @this {RegExpLexer} - */ - begin: function lexer_begin(condition) { - return this.pushState(condition); - }, - - /** - activates a new lexer condition state (pushes the new lexer condition state onto the condition stack) - - @public - @this {RegExpLexer} - */ - pushState: function lexer_pushState(condition) { - this.conditionStack.push(condition); - this.__currentRuleSet__ = null; - return this; - }, - - /** - pop the previously active lexer condition state off the condition stack - - @public - @this {RegExpLexer} - */ - popState: function lexer_popState() { - var n = this.conditionStack.length - 1; - if (n > 0) { - this.__currentRuleSet__ = null; - return this.conditionStack.pop(); - } else { - return this.conditionStack[0]; - } - }, - - /** - return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available - - @public - @this {RegExpLexer} - */ - topState: function lexer_topState(n) { - n = this.conditionStack.length - 1 - Math.abs(n || 0); - if (n >= 0) { - return this.conditionStack[n]; - } else { - return 'INITIAL'; - } - }, - - /** - (internal) determine the lexer rule set which is active for the currently active lexer condition state - - @public - @this {RegExpLexer} - */ - _currentRules: function lexer__currentRules() { - if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) { - return this.conditions[this.conditionStack[this.conditionStack.length - 1]]; - } else { - return this.conditions['INITIAL']; - } - }, - - /** - return the number of states currently on the stack - - @public - @this {RegExpLexer} - */ - stateStackSize: function lexer_stateStackSize() { - return this.conditionStack.length; - }, - options: { - trackPosition: true -}, - JisonLexerError: JisonLexerError, - performAction: function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START) { - -var YYSTATE = YY_START; -switch($avoiding_name_collisions) { -case 0 : -/*! Conditions:: INITIAL */ -/*! Rule:: \s+ */ - /* skip whitespace */ -break; -default: - return this.simpleCaseActionClusters[$avoiding_name_collisions]; -} -}, - simpleCaseActionClusters: { - - /*! Conditions:: INITIAL */ - /*! Rule:: [0-9]+(\.[0-9]+)?\b */ - 1 : 13, - /*! Conditions:: INITIAL */ - /*! Rule:: \* */ - 2 : 5, - /*! Conditions:: INITIAL */ - /*! Rule:: \/ */ - 3 : 6, - /*! Conditions:: INITIAL */ - /*! Rule:: - */ - 4 : 4, - /*! Conditions:: INITIAL */ - /*! Rule:: \+ */ - 5 : 3, - /*! Conditions:: INITIAL */ - /*! Rule:: \^ */ - 6 : 7, - /*! Conditions:: INITIAL */ - /*! Rule:: ! */ - 7 : 8, - /*! Conditions:: INITIAL */ - /*! Rule:: % */ - 8 : 9, - /*! Conditions:: INITIAL */ - /*! Rule:: \( */ - 9 : 10, - /*! Conditions:: INITIAL */ - /*! Rule:: \) */ - 10 : 11, - /*! Conditions:: INITIAL */ - /*! Rule:: PI */ - 11 : 14, - /*! Conditions:: INITIAL */ - /*! Rule:: E */ - 12 : 12, - /*! Conditions:: INITIAL */ - /*! Rule:: $ */ - 13 : 1, - /*! Conditions:: INITIAL */ - /*! Rule:: . */ - 14 : 'INVALID' -}, - rules: [ - /^(?:\s+)/, -/^(?:\d+(\.\d+)?\b)/, -/^(?:\*)/, -/^(?:\/)/, -/^(?:-)/, -/^(?:\+)/, -/^(?:\^)/, -/^(?:!)/, -/^(?:%)/, -/^(?:\()/, -/^(?:\))/, -/^(?:PI)/, -/^(?:E)/, -/^(?:$)/, -/^(?:.)/ - ], - conditions: { - "INITIAL": { - rules: [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14 - ], - inclusive: true - } -} -}; - - - - -return lexer; -})(); -parser.lexer = lexer; - -function Parser() { - this.yy = {}; -} -Parser.prototype = parser; -parser.Parser = Parser; - -return new Parser(); -})(); - - - - -if (typeof require !== 'undefined' && typeof exports !== 'undefined') { - exports.parser = calculator; - exports.Parser = calculator.Parser; - exports.parse = function () { - return calculator.parse.apply(calculator, arguments); - }; - -} diff --git a/web/content/assets/js/jison.js b/web/content/assets/js/jison.js deleted file mode 100644 index cd311e658..000000000 --- a/web/content/assets/js/jison.js +++ /dev/null @@ -1,31778 +0,0 @@ -(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o -// MIT Licensed - -var typal = require('./util/typal').typal; -var Set = require('./util/set').Set; -var Lexer = require('./util/regexp-lexer.js'); -var ebnfParser = require('./util/ebnf-parser.js'); -var lexParser = require('./util/lex-parser.js'); -var code_exec = require('./util/safe-code-exec-and-diag.js'); -var XRegExp = require('xregexp'); -//var recast = require('recast'); -//var codeShift = require('jscodeshift'); -var json5 = require('json5'); -var assert = require('assert'); - - -var version = require('../package.json').version; - -var devDebug = 0; - -var Jison = exports.Jison = exports; -Jison.version = version; - -// see also ./lib/cli.js -const defaultJisonOptions = { - moduleType: 'commonjs', - debug: false, - enableDebugLogs: false, - numExpectedConflictStates: 0, - json: false, - type: 'lalr', // CLI: --parserType option - compressTables: 2, // 0, 1, 2 - outputDebugTables: false, - noDefaultResolve: false, - noDefaultAction: false, - noTryCatch: false, - hasPartialLrUpgradeOnConflict: true, - errorRecoveryTokenDiscardCount: 3, - exportAllTables: false, - noMain: false, // CLI: not:(--main option) - tokenStack: false, - dumpSourceCodeOnFailure: true, - throwErrorOnCompileFailure: true, - - moduleName: undefined, - defaultModuleName: 'parser', - file: undefined, - outfile: undefined, - inputPath: undefined, - inputFilename: undefined, - lexfile: undefined, - warn_cb: undefined, // function(msg) | true (= use Jison.Print) | false (= throw Exception) - - parseParams: undefined, - parserErrorsAreRecoverable: false, - lexerErrorsAreRecoverable: false, - ranges: undefined, - showSource: false, -}; - -Jison.defaultJisonOptions = defaultJisonOptions; - -// Convert dashed option keys to Camel Case, e.g. `camelCase('camels-have-one-hump')` => `'camelsHaveOneHump'` -function camelCase(s) { - // Convert first character to lowercase - return s.replace(/^\w/, function (match) { - return match.toLowerCase(); - }) - .replace(/-\w/g, function (match) { - return match.charAt(1).toUpperCase(); - }); -} - -// Merge sets of options. -// -// Convert alternative jison option names to their base option. -// -// The *last* option set which overrides the default wins, where 'override' is -// defined as specifying a not-undefined value which is not equal to the -// default value. -// -// Return a fresh set of options. -function mkStdOptions(/*args...*/) { - var h = Object.prototype.hasOwnProperty; - - if (devDebug > 3) { - Jison.print('mkStdOptions:\n', arguments); - } - - // clone defaults, so we do not modify those constants. - var opts = {}; - var o = Jison.defaultJisonOptions; - - for (var p in o) { - if (h.call(o, p) && typeof o[p] !== 'undefined') { - opts[p] = o[p]; - } - } - - for (var i = 0, len = arguments.length; i < len; i++) { - o = arguments[i]; - - // clone input (while camel-casing the options), so we do not modify those either. - var o2 = {}; - - for (var p in o) { - if (h.call(o, p) && typeof o[p] !== 'undefined') { - o2[camelCase(p)] = o[p]; - } - } - - // now clean them options up: - if (typeof o2.main !== 'undefined') { - o2.noMain = !o2.main; - } - if (typeof o2.hasDefaultResolve !== 'undefined') { - o2.noDefaultResolve = !o2.hasDefaultResolve; - } - if (typeof o2.hasDefaultAction !== 'undefined') { - o2.noDefaultAction = !o2.hasDefaultAction; - } - if (typeof o2.hasTryCatch !== 'undefined') { - o2.noTryCatch = !o2.hasTryCatch; - } - if (typeof o2.parserType !== 'undefined') { - o2.type = o2.parserType; - } - - delete o2.parserType; - delete o2.main; - delete o2.hasDefaultResolve; - delete o2.hasDefaultAction; - delete o2.hasTryCatch; - - // special check for `moduleName` to ensure we detect the 'default' moduleName entering from the CLI - // NOT overriding the moduleName set in the grammar definition file via an `%options` entry: - if (o2.moduleName === o2.defaultModuleName) { - delete o2.moduleName; - } - - // now see if we have an overriding option here: - for (var p in o2) { - if (h.call(o2, p)) { - if (o2[p] !== undefined && o2[p] !== Jison.defaultJisonOptions[p]) { - opts[p] = o2[p]; - } - } - } - } - - return opts; -} - - -// Autodetect if the input grammar and optional lexer spec is in JSON or JISON -// format when the `options.json` flag is `true`. -// -// Produce the JSON parse result when these are JSON formatted already as that -// would save us the trouble of doing this again, anywhere else in the JISON -// compiler/generator. -// -// Otherwise return the *parsed* grammar and optional lexer specs as they have -// been processed through EBNFParser and LEXParser respectively. -function autodetectAndConvertToJSONformat(grammar, optionalLexerSection, options) { - var chk_g = null; - var chk_l = null; - var ex1, err; - - if (typeof grammar === 'string') { - if (options.json) { - try { - chk_g = json5.parse(grammar); - - // When JSON5-based parsing of the grammar succeeds, this implies the grammar is specified in `JSON mode` - // *OR* there's a JSON/JSON5 format error in the input: - } catch (e) { - ex1 = e; - } - } - if (!chk_g) { - try { - chk_g = ebnfParser.parse(grammar, options); - } catch (e) { - if (options.json) { - err = new Error('Could not parse jison grammar in JSON AUTODETECT mode\nError: ' + ex1.message + ' (' + e.message + ')'); - err.secondary_exception = e; - err.stack = ex1.stack; - } else { - err = new Error('Could not parse jison grammar\nError: ' + e.message); - err.stack = e.stack; - } - throw err; - } - } - - // Save time! Don't reparse the entire grammar *again* inside the code generators when that's not necessary: - // if (chk_g) { - // grammar = chk_g; - // } - } else { - chk_g = grammar; - } - - // Now the same treatment for the lexer: - if (chk_g && optionalLexerSection) { - if (chk_g.lex) { - throw new Error('Cannot invoke with both a lexer section in the grammar input and a separate lexer input at the same time!'); - } - - if (typeof optionalLexerSection === 'string') { - if (options.json) { - try { - chk_l = json5.parse(optionalLexerSection); - - // When JSON5-based parsing of the lexer spec succeeds, this implies the lexer spec is specified in `JSON mode` - // *OR* there's a JSON/JSON5 format error in the input: - } catch (e) { - ex1 = e; - } - } - if (!chk_l) { - // // WARNING: the lexer may receive options specified in the **grammar spec file**, - // // hence we should mix the options to ensure the lexParser always - // // receives the full set! - // // - // // make sure all options are 'standardized' before we go and mix them together: - // options = mkStdOptions(grammar.options, options); - try { - chk_l = lexParser.parse(optionalLexerSection, options); - } catch (e) { - if (options.json) { - err = new Error('Could not parse lexer spec in JSON AUTODETECT mode\nError: ' + ex1.message + ' (' + e.message + ')'); - err.secondary_exception = e; - err.stack = ex1.stack; - } else { - err = new Error('Could not parse lexer spec\nError: ' + e.message); - err.stack = e.stack; - } - throw err; - } - } - } else { - chk_l = optionalLexerSection; - } - - // Save time! Don't reparse the entire lexer spec *again* inside the code generators when that's not necessary: - if (chk_l) { - chk_g.lex = chk_l; - } - } - - return chk_g; -} - -Jison.mkStdOptions = mkStdOptions; -Jison.camelCase = camelCase; -Jison.autodetectAndConvertToJSONformat = autodetectAndConvertToJSONformat; - -// detect print -if (typeof console !== 'undefined' && console.log) { - // wrap console.log to prevent 'Illegal Invocation' exceptions when Jison.print() is used, e.g. - // in the web tryout pages where this code is employed. - Jison.print = function console_log(/* ... */) { - var args = Array.prototype.slice.call(arguments, 0); - console.log.apply(console, args); - }; -} else if (typeof puts !== 'undefined') { - Jison.print = function puts_print() { - puts([].join.call(arguments, ' ')); - }; -} else if (typeof print !== 'undefined') { - Jison.print = print; -} else { - Jison.print = function no_op_print() {}; -} - -Jison.Parser = (function () { - -// iterator utility -function each(obj, func) { - if (typeof obj.forEach === 'function') { - obj.forEach(func); - } else { - var p; - for (p in obj) { - if (obj.hasOwnProperty(p)) { - func.call(obj, obj[p], p, obj); - } - } - } -} - -// This was Set.union() but it's not about *Set* at all: it is purely *Array* oriented! -function union(a, b) { - assert(Array.isArray(a)); - assert(Array.isArray(b)); - // Naive indexOf()-based scanning delivers a faster union() - // (which takes the brunt of the load for large grammars): - // for examples/jscore this drops 13.2 seconds down to - // 8.9 seconds total time spent in the generator! - // - // The idea there was that the FIRST/FOLLOW sets are generally - // quite small; bad cases could run this up to > 128 entries - // to scan through, but overall the FIRST and FOLLOW sets will - // be a few tens of entries at best, and thus it was expected - // that a naive scan would be faster than hash-object creation - // and O(1) checking that hash... Turns out I was right. - // - // The 'arbitrary' threshold of 52 entries in the array to check - // against is probably at or near the worst-case FIRST/FOLLOW set - // site for this jscore grammar as the naive scan consistently - // outperformed the old smarter hash-object code for smaller - // thresholds (10, 20, 32, 42!) - var k, len; - - if (a.length > 52) { - var ar = {}; - for (k = 0, len = a.length; k < len; k++) { - ar[a[k]] = true; - } - for (k = 0, len = b.length; k < len; k++) { - if (!ar[b[k]]) { - a.push(b[k]); - } - } - } else { - var bn = []; - for (k = 0, len = b.length; k < len; k++) { - if (a.indexOf(b[k]) < 0) { - bn.push(b[k]); - } - } - a = a.concat(bn); - } - return a; -} - -// HELPER FUNCTION: print the function in source code form, properly indented. -function printFunctionSourceCode(f) { - return String(f).replace(/^ /gm, ''); -} -function printFunctionSourceCodeContainer(f) { - return String(f).replace(/^ /gm, '').replace(/^ /gm, '').replace(/function [^\{]+\{/, '').replace(/\}$/, ''); -} - - -var Nonterminal = typal.construct({ - constructor: function Nonterminal(symbol) { - this.symbol = symbol; - this.productions = new Set(); - this.first = []; - this.follows = []; - this.nullable = false; - }, - toString: function Nonterminal_toString() { - var str = this.symbol; - var attr_str = []; - - if (this.nullable) { - attr_str.push('nullable'); - } - - if (attr_str.length) { - str += ' [' + attr_str.join(' ') + ']'; - } - str += '\n Firsts: [' + this.first.join('] [') + ']'; - str += '\n Follows: [' + this.follows.join('] [') + ']'; - str += '\n Productions:\n ' + this.productions.join('\n '); - - return str; - } -}); - -var Production = typal.construct({ - constructor: function Production(symbol, handle, id, handle_aliases, handle_action) { - this.symbol = symbol; - this.handle = handle; - this.nullable = false; - this.id = id; - this.aliases = handle_aliases; - this.action = handle_action; - this.first = []; - this.follows = []; - this.precedence = 0; - this.reachable = false; - }, - toString: function Production_toString() { - var str = this.symbol; - - var attr_str = []; - - if (this.nullable) { - attr_str.push('~'); - } - if (this.precedence) { - attr_str.push('@' + this.precedence); - } - if (!this.reachable) { - attr_str.push('*RIP*'); - } - - if (attr_str.length) { - str += '[' + attr_str.join(' ') + ']'; - } - str += ' -> ' + this.handle.join(' '); - - return str; - }, - describe: function Production_describe() { - var str = this.symbol; - - var attr_str = []; - - if (this.nullable) { - attr_str.push('nullable'); - } - if (this.precedence) { - attr_str.push('precedence: ' + this.precedence); - } - - if (attr_str.length) { - str += ' [' + attr_str.join(' ') + ']'; - } - str += '\n Firsts: [' + this.first.join('] [') + ']'; - str += '\n --> ' + this.handle.join(' '); - - return str; - } -}); - - - -var generator = typal.beget(); - -// `optionalLexerSection` is an optional {String} argument, specifying the lexer rules. -// May only be specified when the specified `grammar` also is a yet-unparsed -// {String} defining the grammar. -// -// Hence these invocations are legal: -// -// - `Generator("String")` -// --> `String` contains entire grammar, including -// optional `%lex` lexer rules section -// -// -// - `Generator("String-1", "String-2")` -// --> The `String-1` string contains grammar, *excluding* `%lex` lexer rules section, -// while the `String-2` string contains the `%lex` lexer rules section -// -// -// - `Generator("String", {Options})` -// --> `String` contains entire grammar, including -// optional `%lex` lexer rules section -// -// The `Options` object specifies the desired jison options' settings. -// -// -// - `Generator("String", NULL, {Options})` -// --> `String` contains entire grammar, including -// optional `%lex` lexer rules section -// -// The `Options` object specifies the desired jison options' settings. -// -// -// - `Generator("String-1", "String-2", {Options})` -// --> The `String-1` string contains grammar, *excluding* `%lex` lexer rules section, -// while the `String-2` string contains the `%lex` lexer rules section -// -// The `Options` object specifies the desired jison options' settings. -// -// -// - `Generator({Grammar})` -// --> The `Grammar` object contains the entire grammar as an already parsed *structure*, -// including optional `%lex` lexer rules section in its `.lex` member. -// -// -// - `Generator({Grammar}, {Options})` -// --> The `Grammar` object contains the entire grammar as an already parsed *structure*, -// including optional `%lex` lexer rules section in its `.lex` member. -// -// The `Options` object specifies the desired jison options' settings. -// -// -// - `Generator({Grammar}, NULL, {Options})` -// --> The `Grammar` object contains the entire grammar as an already parsed *structure*, -// including optional `%lex` lexer rules section in its `.lex` member. -// -// The `Options` object specifies the desired jison options' settings. -// -// -// - `Generator({Grammar}, "String-2")` -// --> The `Grammar` object contains grammar, *excluding* `%lex` lexer rules section, -// while the `String-2` string contains the `%lex` lexer rules section -// -// -// - `Generator({Grammar}, "String-2", {Options})` -// --> The `Grammar` object contains grammar, *excluding* `%lex` lexer rules section, -// while the `String-2` string contains the `%lex` lexer rules section -// -// The `Options` object specifies the desired jison options' settings. -// -// -// Any other arguments / arguments' types sequence is illegal. -// -generator.constructor = function Jison_Generator(grammar, optionalLexerSection, options) { - var err; - - // pick the correct argument for the `options` for this call: - if (!options && optionalLexerSection && typeof optionalLexerSection !== 'string') { - options = optionalLexerSection; - optionalLexerSection = null; - } - // and standardize it: - options = mkStdOptions(options); - - grammar = autodetectAndConvertToJSONformat(grammar, optionalLexerSection, options); - - // make sure all options are 'standardized' before we go and mix them together: - options = mkStdOptions(grammar.options, options); - - this.terms = {}; - this.operators = {}; - this.productions = []; - this.conflicts = 0; - this.new_conflicts_found_this_round = 0; - this.conflicting_states = []; - this.resolutions = []; - this.conflict_productions_LU = {}; - this.conflict_states_LU = {}; - this.conflict_fixing_round = false; - this.parseParams = grammar.parseParams; - this.yy = {}; // accessed as yy free variable in the parser/lexer actions - - // also export the grammar itself *and* the cleaned-up generator options: - this.options = options; - this.grammar = grammar; - - // propagate %parse-params into the lexer! - if (grammar.lex) { - if (!grammar.lex.options) { - grammar.lex.options = {}; - } - if (this.parseParams) { - grammar.lex.options.parseParams = this.parseParams; - } - } - - // calculate the input path; if none is specified, it's the present working directory - var path = require('path'); - var inpath = options.file || options.outfile || './dummy'; - inpath = path.normalize(inpath); - options.inputPath = path.dirname(inpath); - options.inputFilename = path.basename(inpath); - - // source included in semantic action execution scope - if (grammar.actionInclude) { - if (typeof grammar.actionInclude === 'function') { - grammar.actionInclude = String(grammar.actionInclude).replace(/^\s*function \(\) \{/, '').replace(/\}\s*$/, ''); - } - this.actionInclude = grammar.actionInclude; - } - this.moduleInclude = grammar.moduleInclude || ''; - this.moduleInit = grammar.moduleInit || []; - assert(Array.isArray(this.moduleInit)); - - this.DEBUG = !!this.options.debug; - this.enableDebugLogs = options.enableDebugLogs || false; - this.numExpectedConflictStates = options.numExpectedConflictStates || 0; - - if (this.DEBUG) { - this.mix(generatorDebug); // mixin debug methods - - Jison.print('Grammar::OPTIONS:\n', this.options); - } - - this.processGrammar(grammar); - - if (grammar.lex) { - var lexer_options = { - // include the knowledge about which parser/lexer - // features will actually be *used* by the environment: - // - // (this stuff comes straight from the jison Optimization Analysis.) - // - parseActionsAreAllDefault: this.actionsAreAllDefault, - parseActionsUseYYLENG: this.actionsUseYYLENG, - parseActionsUseYYLINENO: this.actionsUseYYLINENO, - parseActionsUseYYTEXT: this.actionsUseYYTEXT, - parseActionsUseYYLOC: this.actionsUseYYLOC, - parseActionsUseParseError: this.actionsUseParseError, - parseActionsUseYYERROR: this.actionsUseYYERROR, - parseActionsUseYYRECOVERING: this.actionsUseYYRECOVERING, - parseActionsUseYYERROK: this.actionsUseYYERROK, - parseActionsUseYYCLEARIN: this.actionsUseYYCLEARIN, - parseActionsUseValueTracking: this.actionsUseValueTracking, - parseActionsUseValueAssignment: this.actionsUseValueAssignment, - parseActionsUseLocationTracking: this.actionsUseLocationTracking, - parseActionsUseLocationAssignment: this.actionsUseLocationAssignment, - parseActionsUseYYSTACK: this.actionsUseYYSTACK, - parseActionsUseYYSSTACK: this.actionsUseYYSSTACK, - parseActionsUseYYSTACKPOINTER: this.actionsUseYYSTACKPOINTER, - parserHasErrorRecovery: this.hasErrorRecovery, - - // and re-use any useful options: - moduleType: this.options.moduleType, - debug: this.options.debug, - enableDebugLogs: this.options.enableDebugLogs, - json: this.options.json, - main: false, - dumpSourceCodeOnFailure: this.options.dumpSourceCodeOnFailure, - throwErrorOnCompileFailure: this.options.throwErrorOnCompileFailure, - moduleName: 'lexer', // this.options.moduleName + '_Lexer', - file: this.options.file, - outfile: this.options.outfile, - inputPath: this.options.inputPath, - inputFilename: this.options.inputFilename, // or should we feed it `this.options.lexfile` instead? - warn_cb: this.options.warn_cb, - parseParams: this.options.parseParams, - xregexp: this.options.xregexp, - parserErrorsAreRecoverable: this.options.parserErrorsAreRecoverable, - lexerErrorsAreRecoverable: this.options.lexerErrorsAreRecoverable, - flex: this.options.flex, - backtrack_lexer: this.options.backtrack_lexer, - ranges: this.options.ranges, - caseInsensitive: this.options.caseInsensitive, - showSource: this.options.showSource, - pre_lex: this.options.pre_lex, - post_lex: this.options.post_lex, - }; - - this.lexer = new Lexer(grammar.lex, null, this.terminals_, lexer_options); - } -}; - -generator.processGrammar = function processGrammarDef(grammar) { - var bnf = grammar.bnf, - tokens = grammar.tokens, - nonterminals = this.nonterminals = {}, - productions = this.productions, - self = this; - - if (!grammar.bnf && grammar.ebnf) { - bnf = grammar.bnf = ebnfParser.transform(grammar.ebnf); - } - if (devDebug) { - Jison.print('processGrammar: ', JSON.stringify({ - bnf: bnf, - tokens: tokens, - productions: productions - }, null, 2)); - } - if (tokens) { - if (typeof tokens === 'string') { - tokens = tokens.trim().split(' '); - } else { - tokens = tokens.slice(0); - } - } - - // did the grammar user also provide a predefined set of symbols to be (re)used with this grammar? - // (This is used when you want to generate multiple lexers and parsers which share a common symbol set - // so as to make the parsers and lexers mutually interchangeable.) - var predefined_symbols = null; - if (grammar.imports) { - var symbols_import = grammar.imports.find(function (el, idx) { - if (el.name === 'symbols') { - return el; - } - return false; - }); - if (symbols_import) { - var fs = require('fs'); - var path = require('path'); - var filepath = path.resolve(symbols_import.path); - - var source = fs.readFileSync(filepath, 'utf8'); - // It's either a JSON file or a JISON generated output file: - // - // symbols_: { - // "symbol": ID, ... - // }, - try { - predefined_symbols = json5.parse(source); - } catch (ex) { - if (devDebug) { - console.error('%import symbols JSON fail: ', ex); - } - try { - var m = /[\r\n]\s*symbols_:\s*(\{[\s\S]*?\}),\s*[\r\n]/.exec(source); - if (m && m[1]) { - source = m[1]; - predefined_symbols = json5.parse(source); - } - } catch (ex) { - if (devDebug) { - console.error('%import symbols JISON output fail: ', ex); - } - throw new Error('Error: `%import symbols ` must point to either a JSON file containing a symbol table (hash table) or a previously generated JISON JavaScript file, which contains such a symbol table. Error message: ' + ex.message); - } - } - - if (!predefined_symbols || typeof predefined_symbols !== 'object') { - throw new Error('Error: `%import symbols ` must point to either a JSON file containing a symbol table (hash table) or a previously generated JISON JavaScript file, which contains such a symbol table.'); - } - - // Make sure all predefined symbols are unique and *numeric* and do not include predefined tokens JISON already defines to a fixed ID on its own: - delete predefined_symbols['$accept']; - delete predefined_symbols['$end']; - delete predefined_symbols['error']; - delete predefined_symbols['$eof']; - delete predefined_symbols['EOF']; - - var symdef_uniq_check = {}; - // Only these symbols are allowed to have the values 1 or 2: - symdef_uniq_check[1] = 'EOF'; - symdef_uniq_check[2] = 'error'; - Object.keys(predefined_symbols).forEach(function cvt_symbol_id_to_numeric(sym) { - var v = predefined_symbols[sym]; - - // Symbol value may be defined as boolean TRUE, in which case we let JISON pick the value for us: - if (v === true) return; - - // Symbol value may be defined as a one-character string: - if (typeof v !== 'number') { - if (typeof v !== 'string' || v.length !== 1) { - throw new Error('Error: `%import symbols `: symbol table contains invalid entry at key \'' + sym + '\': a non-numeric symbol ID value must be a single-character string.'); - } - v = v.charCodeAt(0); - } - v = v | 0; - if (!v || v < 0) { - throw new Error('Error: `%import symbols `: symbol table contains invalid entry at key \'' + sym + '\': a symbol ID value must be an integer value, 3 or greater.'); - } - if (symdef_uniq_check[v]) { - if (symdef_uniq_check[v] !== sym) { - throw new Error('Error: `%import symbols `: symbol table contains duplicate ID values for keys \'' + sym + '\' and \'' + symdef_uniq_check[v] + '\''); - } - } - symdef_uniq_check[v] = sym; - predefined_symbols[sym] = v; - }); - } - } - - var symbols = this.symbols = []; - - // calculate precedence of operators - var operators = this.operators = processOperators(grammar.operators); - - // build productions from CFG and calculate the symbol sets (terminals and nonterminals) and their name-to-ID mappings - this.buildProductions(bnf, productions, nonterminals, symbols, operators, predefined_symbols, grammar.extra_tokens); - - if (devDebug > 1) { - Jison.print('terminals vs tokens: ', this.terminals.length, (tokens && tokens.length), this.terminals, - '\n###################################### TOKENS\n', tokens, - '\n###################################### EXTRA TOKENS\n', grammar.extra_tokens, - '\n###################################### LEX\n', grammar.lex, - '\n###################################### GRAMMAR\n', grammar); - } - if (tokens && this.terminals.length !== tokens.length) { - self.trace('Warning: declared tokens differ from tokens found in rules.'); - self.trace('Terminals: ', this.terminals); - self.trace('Tokens: ', tokens); - } - - // augment the grammar - this.augmentGrammar(grammar); - - // detect unused productions and flag them - this.signalUnusedProductions(); - - // build production action code chunks (originally done in `buildProductions` as a side-effect) - this.buildProductionActions(); -}; - -generator.augmentGrammar = function augmentGrammar(grammar) { - if (this.productions.length === 0) { - throw new Error('Grammar error: must have at least one rule.'); - } - // use specified start symbol, or default to first user defined production - this.startSymbol = grammar.start || grammar.startSymbol || this.productions[0].symbol; - if (!this.nonterminals[this.startSymbol]) { - throw new Error('Grammar error: startSymbol must be a non-terminal found in your grammar.'); - } - //this.EOF = '$end'; // moved to generator.buildProductions() - - // Augment the grammar: - // - // Add the top-most accept rule (and implicit, default, action): - // - // $accept: $end - // %{ $$ = $1; @$ = @1; %} - // - // which, combined with the new parse kernel's `$accept` state behaviour will produce the - // `$$` value output of the rule as the parse result, IFF that result is - // *not* `undefined`. (See also the parser kernel code.) - // - // In code: - // - // %{ - // @$ = @1; - // if (typeof $1 !== 'undefined') - // return $1; - // else - // return true; // the default parse result if the rule actions don't produce anything - // %} - // - var acceptProduction = new Production('$accept', [this.startSymbol, '$end'], 0); - this.productions.unshift(acceptProduction); - - // prepend parser tokens // moved to generator.buildProductions() - //this.symbols.unshift('$accept', this.EOF); - //this.symbols_.$accept = 0; - //this.symbols_[this.EOF] = 1; - //this.terminals.unshift(this.EOF); - - //this.nonterminals.$accept = new Nonterminal('$accept'); - - this.nonterminals.$accept.productions.push(acceptProduction); - - // add follow $ to start symbol - this.nonterminals[this.startSymbol].follows.push(this.EOF); -}; - -// Mark unused productions -generator.signalUnusedProductions = function () { - var mark = {}; - - var productions = this.productions; - var nonterminals = this.nonterminals; - var i, p, len, nt, sym; - - for (i = 0, len = nonterminals.length; i < len; i++) { - nt = nonterminals[i]; - assert(nt.symbol); - mark[nt.symbol] = false; - } - - // scan & mark all visited productions - function traverseGrammar(nt) { - assert(nt); - assert(nt.symbol); - mark[nt.symbol] = true; - - var prods = nt.productions; - assert(prods); - prods.forEach(function (p) { - assert(p.symbol === nt.symbol); - assert(p.handle); - var rhs = p.handle; - if (devDebug > 0) { - Jison.print('traverse / mark: ', nt.symbol, ' --> ', rhs); - } - - for (var j = 0, len = rhs.length; j < len; j++) { - var sym = rhs[j]; - assert(!sym ? !nonterminals[sym] : true); - if (nonterminals[sym] && !mark[sym]) { - traverseGrammar(nonterminals[sym]); - } - } - }); - } - - traverseGrammar(nonterminals['$accept' /* this.startSymbol */ ]); - - // now any production which is not yet marked is *unused*: - for (sym in mark) { - nt = nonterminals[sym]; - assert(nt); - var prods = nt.productions; - assert(prods); - var in_use = mark[sym]; - prods.forEach(function (p) { - assert(p); - if (in_use) { - p.reachable = true; - } else { - p.reachable = false; - } - }); - - if (!in_use) { - // and kill the unused nonterminals: - delete this.nonterminals[sym]; - } - } - - this.unused_productions = productions.filter(function (p) { - return !p.reachable; - }); - - // and kill the unused productions: - this.productions = productions.filter(function (p) { - return p.reachable; - }); -}; - -// set precedence and associativity of operators -function processOperators(ops) { - if (!ops) return {}; - var operators = {}; - for (var i = 0, k, prec; (prec = ops[i]); i++) { - for (k = 1; k < prec.length; k++) { - operators[prec[k]] = { - precedence: i + 1, - assoc: prec[0] - }; - } - } - return operators; -} - -// Detect the indentation of the given sourcecode chunk and shift the chunk to be indented the given number of spaces. -// -// Note that the first line doesn't count as the chunk is very probably trimmed! -function reindentCodeBlock(action, indent_level) { - var width = 0; - var lines = action - .trim() - .split('\n') - // measure the indent: - .map(function checkIndentation(line, idx) { - if (idx === 1) { - // first line didn't matter: reset width to help us find the block indent level: - width = Infinity; - } - if (line.trim() === '') return ''; - - // take out any TABs: turn them into spaces (4 per TAB) - line = line - .replace(/^[ \t]+/, function expandTabs(s) { - return s.replace(/\t/g, ' '); - }); - - var m = /^[ ]+/.exec(line); - if (m) { - width = Math.min(m[0].length, width); - } - - return line; - }) - // remove/adjust the indent: - .map(function checkIndentation(line, idx) { - line = line - .replace(/^[ ]*/, function adjustIndent(s) { - var l = Math.max(s.length - width, 0) + indent_level; - var shift = (new Array(l + 1)).join(' '); - return shift; - }); - return line; - }); - - return lines.join('\n'); -} - - -generator.buildProductions = function buildProductions(bnf, productions, nonterminals, symbols, operators, predefined_symbols, descriptions) { - var self = this; - var prods, symbol, symId; - var productions_ = []; - var symbols_ = {}; - var descriptions_ = {}; - var usedSymbolIds = [/* $accept = 0 */ true, /* $end = 1 */ true, /* error = 2 */ true]; - var usedSymbolIdsLowIndex = 3; - - // set up the required symbols `$accept` and `$end` (a.k.a. EOF) and make sure they occupy the expected slots: - this.EOF = '$end'; - - symbols_.$accept = 0; - symbols_[this.EOF] = 1; - symbols_['$eof'] = 1; // `$eof` is a synonym of `$end` for bison compatibility; this is the only place where two symbol names may map to a single symbol ID number! - symbols_['EOF'] = 1; // `EOF` is a synonym of `$end` for bison compatibility; this is the only place where two symbol names may map to a single symbol ID number! - symbols[0] = '$accept'; - symbols[1] = this.EOF; - - nonterminals.$accept = new Nonterminal('$accept'); - - // always add the error symbol; will be third symbol, or "2": ($accept, $end, error) - symbols_.error = 2; - symbols[2] = 'error'; - - if (predefined_symbols) { - for (symbol in predefined_symbols) { - symId = predefined_symbols[symbol]; - if (symId === true) { - // add symbol to queue which must be assigned a value by JISON; after all the other predefined symbols have been processed. - continue; - } - - // skip $accept, $end and error: - if (symId <= 2) continue; - - // has this ID already been taken? If not, pick this ID, otherwise throw a tantrum. - if (!usedSymbolIds[symId]) { - usedSymbolIds[symId] = true; - symbols_[symbol] = symId; - symbols[symId] = symbol; - } else { - throw new Error('Error: Predefined symbol (imported via `%import symbols`) "' + symbol + '" has an ID ' + symId + ' which is already in use by symbol "' + symbols[symId] + '"'); - } - } - - // preferably assign readable ASCII-range token IDs to tokens added from the predefined list - // but only when maximum table compression isn't demanded: - usedSymbolIdsLowIndex = ((self.options.compressTables | 0) < 2 ? 32 : 3); - for (symbol in predefined_symbols) { - symId = predefined_symbols[symbol]; - addSymbol(symbol); - } - - // reset ID low water mark: nonterminals etc. can be assigned any number, preferably a small/low one! - usedSymbolIdsLowIndex = 3; - } - - if (descriptions) { - self.trace('descriptions obtained from grammar: ', descriptions); - descriptions.forEach(function (tokdef) { - // fields: id, type, value, description - if (tokdef.description && tokdef.id) { - descriptions_[tokdef.id] = tokdef.description; - } - }); - } - - - var hasErrorRecovery = false; // has error recovery - - // Produce the next available unique symbolID: - function getNextSymbolId() { - for (var i = usedSymbolIdsLowIndex; ; i++) { - if (!usedSymbolIds[i]) { - usedSymbolIds[i] = true; - usedSymbolIdsLowIndex = i + 1; - return i; - } - } - } - - function addSymbol(s) { - if (s && !symbols_[s]) { - var i; - - // assign the Unicode codepoint index to single-character symbols, - // but only when maximum table compression isn't demanded: - if (s.length === 1 && (self.options.compressTables | 0) < 2) { - i = s.charCodeAt(0); - // has this ID already been taken? If not, pick this ID. - if (i < 128 /* only allow this within the ASCII range */ && !usedSymbolIds[i]) { - usedSymbolIds[i] = true; - } else { - i = getNextSymbolId(); - } - } else { - // otherwise simply obtain the next available ID number as usual. - i = getNextSymbolId(); - } - symbols_[s] = i; - symbols[i] = s; - } - return symbols_[s] || false; - } - - // `this` is options object with `maxTokenLength` option to guide us which literal tokens we want to process: - function collectLiteralTokensInProduction(handle) { - var r, rhs, i, sym; - - if (devDebug) Jison.print('\ncollectLiteralTokensInProduction: ', symbol, ':', JSON.stringify(handle, null, 2), ' @ options: ', this); - - var maxlen = this.maxTokenLength || Infinity; - - if (handle.constructor === Array) { - var rhs_i; - rhs = (typeof handle[0] === 'string') ? - splitStringIntoSymbols(handle[0]) : - handle[0].slice(0); - - for (i = 0; i < rhs.length; i++) { - sym = rhs[i]; - // check for aliased names, e.g., id[alias] and strip them - rhs_i = sym.match(new XRegExp('\\[[\\p{Alphabetic}_][\\p{Alphabetic}_\\p{Number}]*\\]$')); - if (rhs_i) { - sym = sym.substr(0, sym.length - rhs_i[0].length); - } - - if (!bnf[sym] && sym.length <= maxlen) { - addSymbol(sym); - } - } - } else { - // no action -> don't care about aliases; strip them. - handle = handle.replace(new XRegExp('\\[[\\p{Alphabetic}_][\\p{Alphabetic}_\\p{Number}]*\\]', 'g'), ''); - rhs = splitStringIntoSymbols(handle); - for (i = 0; i < rhs.length; i++) { - sym = rhs[i]; - if (!bnf[sym] && sym.length <= maxlen) { - addSymbol(sym); - } - } - } - } - - // Before we go process the grammar for real, we collect the 'literal' non-terminals and add them to the symbol table - // before all others: this way these tokens have the maximum chance to get assigned their ASCII value as symbol ID, - // which helps debugging/diagnosis of generated grammars. - // (This is why previously we had set `usedSymbolIdsLowIndex` to 127 instead of 3!) - - var prodsLUT = {}; - for (symbol in bnf) { - if (!bnf.hasOwnProperty(symbol)) continue; - - if (typeof bnf[symbol] === 'string') { - prods = bnf[symbol].split(/\s*\|\s*/g); - } else { - prods = bnf[symbol].slice(0); - } - if (devDebug) Jison.print('\ngenerator.buildProductions: ', symbol, JSON.stringify(prods, null, 2)); - - prodsLUT[symbol] = prods; - } - - // First we collect all single-character literal tokens: - for (symbol in prodsLUT) { - if (!prodsLUT.hasOwnProperty(symbol)) continue; - - prods = prodsLUT[symbol]; - prods.forEach(collectLiteralTokensInProduction, { - maxTokenLength: 1 - }); - } - // Next we collect all other literal tokens: - for (symbol in prodsLUT) { - if (!prodsLUT.hasOwnProperty(symbol)) continue; - - prods = prodsLUT[symbol]; - prods.forEach(collectLiteralTokensInProduction, { - maxTokenLength: Infinity - }); - } - - // and now go and process the entire grammar: - // first collect all nonterminals in a symbol table, then build the productions - // for each of those: nonterminals should all have IDs assigned before they - // should be processed as part of a *production* rule, where these MAY be - // referenced: - for (symbol in bnf) { - if (!bnf.hasOwnProperty(symbol)) continue; - - addSymbol(symbol); - nonterminals[symbol] = new Nonterminal(symbol); - } - - // now that we have collected all nonterminals in our symbol table, it's finally - // time to process the productions: - for (symbol in prodsLUT) { - if (!prodsLUT.hasOwnProperty(symbol)) continue; - - prods = prodsLUT[symbol]; - prods.forEach(buildProduction); - } - - var sym, - terms = [], - terms_ = {}; - each(symbols_, function (id, sym) { - // `$eof` and `EOF` are synonyms of `$end` (`$eof` is for bison compatibility); - // this is the only place where two symbol names may map to a single symbol ID number - // and we do not want `$eof`/`EOF` to show up in the symbol tables of generated parsers - // as we use `$end` for that one! - if (!nonterminals[sym] && sym !== '$eof') { - terms.push(sym); - terms_[id] = sym; - } - }); - - this.hasErrorRecovery = hasErrorRecovery; - - this.terminals = terms; - this.terminals_ = terms_; - this.symbols_ = symbols_; - this.descriptions_ = descriptions_; - - this.productions_ = productions_; - assert(this.productions === productions); - - - // Cope with literal symbols in the string, including *significant whitespace* tokens - // as used in a rule like this: `rule: A ' ' B;` which should produce 3 tokens for the - // rhs: ['A', ' ', 'B'] - function splitStringIntoSymbols(rhs) { - // when there's no literal tokens in there, we can fast-track this baby: - rhs = rhs.trim(); - var pos1 = rhs.indexOf("'"); - var pos2 = rhs.indexOf('"'); - if (pos1 < 0 && pos2 < 0) { - return rhs.split(' '); - } - // else: - // - // rhs has at least one literal: we will need to parse the rhs into tokens - // with a little more effort now. - var tokens = []; - while (pos1 >= 0 || pos2 >= 0) { - var pos = pos1; - var marker = "'"; - if (pos < 0) { - assert(pos2 >= 0); - pos = pos2; - marker = '"'; - } else if (pos >= 0 && pos2 >= 0 && pos2 < pos) { - pos = pos2; - marker = '"'; - } - var ls = rhs.substr(0, pos).trim(); - if (ls.length > 0) { - tokens.push.apply(tokens, ls.split(' ')); - } - rhs = rhs.substr(pos + 1); - // now find the matching end marker. - // - // Edge case: token MAY include the ESCAPED MARKER... or other escapes! - // Hence we need to skip over ALL escapes inside the token! - var pos3 = rhs.indexOf('\\'); - pos = rhs.indexOf(marker); - ls = ''; - while (pos3 >= 0 && pos3 < pos) { - ls += rhs.substr(0, pos3 + 2); // chop off entire escape (2 chars) and keep as part of next token - rhs = rhs.substr(pos3 + 2); - pos3 = rhs.indexOf('\\'); - pos = rhs.indexOf(marker); - } - if (pos < 0) { - throw new Error('internal error parsing literal token(s) in grammar rule'); - } - ls += rhs.substr(0, pos); - // check for aliased literals, e.g., `'>'[gt]` and keep it and the alias together - rhs = rhs.substr(pos + 1); - var alias = rhs.match(new XRegExp('^\\[[\\p{Alphabetic}_][\\p{Alphabetic}_\\p{Number}]*\\]')); - if (alias) { - ls += alias[0]; - rhs = rhs.substr(alias[0].length); - } - tokens.push(ls); - - rhs = rhs.trim(); - - pos1 = rhs.indexOf("'"); - pos2 = rhs.indexOf('"'); - } - // Now, outside the loop, we're left with the remainder of the rhs, which does NOT - // contain any literal tokens. - if (rhs.length > 0) { - tokens.push.apply(tokens, rhs.split(' ')); - } - return tokens; - } - - function buildProduction(handle) { - var r, rhs, i, - precedence_override; - aliased = [], - action = null; - - if (devDebug) Jison.print('\nbuildProduction: ', symbol, ':', JSON.stringify(handle, null, 2)); - - if (handle.constructor === Array) { - var rhs_i; - - rhs = (typeof handle[0] === 'string') ? - splitStringIntoSymbols(handle[0]) : - handle[0].slice(0); - - for (i = 0; i < rhs.length; i++) { - // check for aliased names, e.g., id[alias] and strip them - rhs_i = rhs[i].match(new XRegExp('\\[[\\p{Alphabetic}_][\\p{Alphabetic}_\\p{Number}]*\\]$')); - if (rhs_i) { - rhs[i] = rhs[i].substr(0, rhs[i].length - rhs_i[0].length); - rhs_i = rhs_i[0].substr(1, rhs_i[0].length - 2); - aliased[i] = rhs_i; - } else { - aliased[i] = rhs[i]; - } - - if (rhs[i] === 'error') { - hasErrorRecovery = true; - } - assert(bnf[rhs[i]] ? symbols_[rhs[i]] : true, 'all nonterminals must already exist in the symbol table'); - assert(rhs[i] ? symbols_[rhs[i]] : true, 'all symbols (terminals and nonterminals) must already exist in the symbol table'); - //addSymbol(rhs[i]); - } - - assert(handle.length === 3 ? typeof handle[1] === 'string' : true); - if (typeof handle[1] === 'string') { - // semantic action specified - action = handle[1]; - - // precedence specified also - if (handle[2] && operators[handle[2].prec]) { - precedence_override = { - symbol: handle[2].prec, - spec: operators[handle[2].prec] - }; - } - } else { - // only precedence specified - if (operators[handle[1].prec]) { - precedence_override = { - symbol: handle[1].prec, - spec: operators[handle[1].prec] - }; - } - } - } else { - // no action -> don't care about aliases; strip them. - handle = handle.replace(new XRegExp('\\[[\\p{Alphabetic}_][\\p{Alphabetic}_\\p{Number}]*\\]', 'g'), ''); - rhs = splitStringIntoSymbols(handle); - for (i = 0; i < rhs.length; i++) { - if (rhs[i] === 'error') { - hasErrorRecovery = true; - } - assert(bnf[rhs[i]] ? symbols_[rhs[i]] : true, 'all nonterminals must already exist in the symbol table'); - assert(rhs[i] ? symbols_[rhs[i]] : true, 'all symbols (terminals and nonterminals) must already exist in the symbol table'); - //addSymbol(rhs[i]); - } - } - - r = new Production(symbol, rhs, productions.length + 1, aliased, action); - - // set precedence - assert(r.precedence === 0); - if (precedence_override) { - r.precedence = precedence_override.spec.precedence; - } - else { - var prec_symbols = []; - var winning_symbol; - - for (i = r.handle.length - 1; i >= 0; i--) { - if (!(r.handle[i] in nonterminals) && r.handle[i] in operators) { - var old_prec = r.precedence; - var new_prec = operators[r.handle[i]].precedence; - if (old_prec !== 0 && old_prec !== new_prec) { - prec_symbols.push(r.handle[i]); - // Jison.print('precedence set twice: ', old_prec, new_prec, r.handle[i], symbol, handle[0]); - if (new_prec < old_prec) { - winning_symbol = r.handle[i]; - } - else { - // keep previously set precedence: - new_prec = old_prec; - } - } else if (old_prec === 0) { - prec_symbols.push(r.handle[i]); - winning_symbol = r.handle[i]; - // Jison.print('precedence set first time: ', old_prec, r.handle[i], symbol, handle[0]); - } - r.precedence = new_prec; - } - } - - if (prec_symbols.length > 1) { - if (self.DEBUG || 1) { - self.warn('Ambiguous rule precedence in grammar: picking the (highest) precedence from operator "' + winning_symbol + '" for rule "' + symbol + ': ' + r.handle.join(' ') + '" which contains multiple operators with different precedences: {' + prec_symbols.join(', ') + '}'); - } - } - } - - productions.push(r); - productions_.push([symbols_[r.symbol], r.handle[0] === '' ? 0 : r.handle.length]); - nonterminals[symbol].productions.push(r); - } -}; - - -generator.buildProductionActions = function buildProductionActions() { -/* - this.terminals = terms; - this.terminals_ = terms_; - this.symbols_ = symbols_; - this.descriptions_ = descriptions_; - - this.productions_ = productions_; - assert(this.productions === productions); -*/ - var productions = this.productions, - nonterminals = this.nonterminals, - symbols = this.symbols, - operators = this.operators, - self = this; - - var actions = [ - '/* this == yyval */', - 'var yy = this.yy;', // the JS engine itself can go and remove this statement when `yy` turns out to be unused in any action code! - preprocessActionCode(this.actionInclude || ''), - 'switch (yystate) {' - ]; - var actionGroups = {}; // used to combine identical actions into single instances: no use duplicating action code needlessly - var actionGroupValue = {}; // stores the unaltered, expanded, user-defined action code for each action group. - var symbol; - - // Preprocess the action code block before we perform any `$n`, `@n` ,`##n` or `#n` expansions: - // Any comment blocks in there should be kept intact (and not cause trouble either as those comments MAY - // contain `$`, `@`, `##` or `#` prefixed bits which might look like references but aren't!) - // - // Also do NOT replace any $x, @x, ##x or #x macros inside any strings! - // - // Note: - // We also replace '/*' comment markers which may (or may not) be lurking inside other comments. - function preprocessActionCode(s) { - function replace_markers(cmt) { - cmt = cmt - .replace(/##/g, '\x01\x09') - .replace(/#/g, '\x01\x01') - .replace(/\$/g, '\x01\x02') - .replace(/@/g, '\x01\x03') - .replace(/\/\*/g, '\x01\x05') - .replace(/\/\//g, '\x01\x06') - .replace(/\'/g, '\x01\x07') - .replace(/\"/g, '\x01\x08') - // and also whiteout any other macros we're about to expand in there: - .replace(/\bYYABORT\b/g, '\x01\x14') - .replace(/\bYYACCEPT\b/g, '\x01\x15') - .replace(/\byyvstack\b/g, '\x01\x16') - .replace(/\byylstack\b/g, '\x01\x17') - .replace(/\byyerror\b/g, '\x01\x18') - .replace(/\bYYRECOVERING\b/g, '\x01\x19') - .replace(/\byyerrok\b/g, '\x01\x1A') - .replace(/\byyclearin\b/g, '\x01\x1B') - .replace(/\byysp\b/g, '\x01\x1C'); - - return cmt; - } - - s = s - // do not trim any NEWLINES in the action block: - .replace(/^\s+/, '') - .replace(/\s+$/, '') - // unify CR/LF combo's: - .replace(/\r\n|\r/g, '\n') - // replace any '$', '@' and '#' in any C++-style comment line to prevent them from being expanded as if they were part of the action code proper: - .replace(/^\s*\/\/.+$/mg, replace_markers) - // also process any //-comments trailing a line of code: - // (we need to ensure these are real and not a bit of string, - // which leaves those comments that are very hard to correctly - // recognize with a simple regex, e.g. '// this isn't a #666 location ref!': - // we accept that we don't actually *parse* the action block and let these - // slip through... :-( ) - // - // WARNING: without that `\n` inside the regex `[...]` set, the set *will* - // match a NEWLINE and thus *possibly* gobble TWO lines for the price of ONE, - // when the first line is an *empty* comment line, i.e. nothing trailing - // the `//` in there and thus the `[^'"]` regex matching the terminating NL *before* - // the `$` in the regex can get at it. Cave canem therefor! |8-( - .replace(/\/\/[^'"\n]+$/mg, replace_markers) - // now MARK all the not-too-tricky-to-recognize /*...*/ comment blocks and process those! - // (Here again we accept that we don't actually *parse* the action code and - // permit to let some of these slip, i.e. comment blocks which trail - // a line of code and contain string delimiter(s). :-( ) - .replace(/^([^'"\n]*?)\/\*/mg, '$1\x01\x04') // comment starts the line, guaranteed not to be inside a string - .replace(/\/\*([^'"\n]*)$/mg, '\x01\x04$1') // comment does not contain any string sentinel in its first line - .replace(/\/\*([^\/]*?\*\/[^'"\n]*)$/mg, '\x01\x04$1') // comment end marker near end of line and since the end is definitely not inside a string, there's bound to be comment start as well - // and find their END marker: first '*/' found wins! - // (The `[\s\S]` regex expression is a hack to ensure NEWLINES are matched - // by that set as well, i.e. this way we can easily cross line boundaries - // while searching for he end of the multiline comment we're trying to - // dig out by regex matching. Also note that we employ non-aggressive - // matching to ensure the regex matcher will find the FIRST occurrence of - // `*/` and mark that as the end of the regex match!) - .replace(/\x01\x04[\s\S]*?\*\//g, replace_markers) - // Now that we have processed all comments in the code, it's time - // to tackle the strings in the code: any strings must be kept intact - // as well. Regrettably, there's regexes which may carry quotes, - // e.g. `/'/`, and escapes of quotes inside strings, e.g. `'\''`, - // which this a non-trivial task. This is when we reconsider whether - // we should run this stuff through Esprima and deal with that AST - // verbosity instead...? For now, we accept that regexes can screw - // us up, but we can handle strings of any kind, by first taking - // out all explicit `\\` non-escaping characters: - .replace(/\\\\/g, '\x01\x10') - // and then we take out all escaped quotes: - .replace(/\\\'/g, '\x01\x11') - .replace(/\\\"/g, '\x01\x12') - // and to top it off, we also take out any more-or-less basic regexes: - .replace(/\\\//g, '\x01\x13') - - // WARNING: Without that prefix check this would also catch - // `6/7 + $$ + 8/9` as if `/7 + $$ + 8/` would be a regex :-( - // but we need this one to ensure any quotes hiding inside - // any regex in there are caught and marked, e.g. `/'/g`. - // Besides, this regex prefix is constructed to it preventing - // the regex matching a `//....` comment line either! - .replace(/[^_a-zA-Z0-9\$\)\/][\s\n\r]*\/[^\n\/\*][^\n\/]*\//g, replace_markers); - - // ... which leaves us with plain strings of both persuasions to cover - // next: we MUST do both at the same time, though or we'll be caught - // with our pants down in constructs like - // `'"' + $$ + '"'` vs. `"'" + $$ + "'"` - - var dqpos, sqpos, ccmtpos, cppcmtpos, first = -1; - for (var c = 0;; c++) { - first++; - dqpos = s.indexOf('"', first); - sqpos = s.indexOf("'", first); - // also look for remaining comments which contain quotes of any kind, - // as those will not have been caught by the previous global regexes: - ccmtpos = s.indexOf('/*', first); - cppcmtpos = s.indexOf('//', first); - first = s.length; - first = Math.min((dqpos >= 0 ? dqpos : first), (sqpos >= 0 ? sqpos : first), (ccmtpos >= 0 ? ccmtpos : first), (cppcmtpos >= 0 ? cppcmtpos : first)); - // now it matters which one came up first: - if (dqpos === first) { - s = s - .replace(/"[^"\n]*"/, replace_markers); - } else if (sqpos === first) { - s = s - .replace(/'[^'\n]*'/, replace_markers); - } else if (ccmtpos === first) { - s = s - .replace(/\/\*[\s\S]*?\*\//, replace_markers); - } else if (cppcmtpos === first) { - s = s - .replace(/\/\/[^\n]*$/m, replace_markers); - } else { - break; - } - } - // Presto! - return s; - } - - // Postprocess the action code block after we perform any `$n`, `@n`, `##n` or `#n` expansions: - // revert the preprocessing! - function postprocessActionCode(s) { - s = s - // multiline comment start markers: - .replace(/\x01\x04/g, '/*') - .replace(/\x01\x05/g, '/*') - .replace(/\x01\x06/g, '//') - // revert markers: - .replace(/\x01\x01/g, '#') - .replace(/\x01\x02/g, '$') - .replace(/\x01\x03/g, '@') - // and revert the string and regex markers: - .replace(/\x01\x07/g, '\'') - .replace(/\x01\x08/g, '\"') - .replace(/\x01\x09/g, '##') - .replace(/\x01\x10/g, '\\\\') - .replace(/\x01\x11/g, '\\\'') - .replace(/\x01\x12/g, '\\\"') - .replace(/\x01\x13/g, '\\\/') - .replace(/\x01\x14/g, 'YYABORT') - .replace(/\x01\x15/g, 'YYACCEPT') - .replace(/\x01\x16/g, 'yyvstack') - .replace(/\x01\x17/g, 'yylstack') - .replace(/\x01\x18/g, 'yyerror') - .replace(/\x01\x19/g, 'YYRECOVERING') - .replace(/\x01\x1A/g, 'yyerrok') - .replace(/\x01\x1B/g, 'yyclearin') - .replace(/\x01\x1C/g, 'yysp'); - - // And a final, minimal, fixup for the semicolon-lovers -- like me! ;-) - // - // Make sure the last statement is properly semicolon-terminated 99.9% of the time: - s = s - .replace(/[\s\r\n]+$/, '') // trim trailing whitespace and empty lines - .replace(/([^\;}])$/, '$1;'); // append a semicolon to the last statement if it doesn't end with one (or a closing brace, e.g. a function definition) - - return s; - } - - // Strip off any insignificant whitespace from the user code to ensure that - // otherwise identical actions are indeed matched up into a single actionGroup: - function mkHashIndex(s) { - return s.trim() - .replace(/\s+$/mg, '') // strip any trailing whitespace for each line of action code - .replace(/^\s+/mg, ''); // ditto for leading whitespace for each line: we don't care about more or less clean indenting practices in the user code - } - - // and now go and process the entire grammar: - productions.forEach(buildProductionAction); - - for (var hash in actionGroups) { - actions.push([].concat.apply([], actionGroups[hash]).join('') + actionGroupValue[hash] + '\n break;\n'); - } - - actions.push('}'); - - var parameters = 'yytext, yyleng, yylineno, yyloc, yystate /* action[1] */, yysp, yyvstack, yylstack, yystack, yysstack'; - if (this.parseParams) parameters += ', ' + this.parseParams.join(', '); - - this.performAction = [].concat( - 'function parser__PerformAction(' + parameters + ') {', - actions, - '}' - ).join('\n') - .replace(/\bYYABORT\b/g, 'return false') - .replace(/\bYYACCEPT\b/g, 'return true') - - // Replace direct symbol references, e.g. #NUMBER# when there's a `%token NUMBER` for your grammar. - // We allow these tokens to be referenced anywhere in your code as #TOKEN#. - .replace(/#([^#\s\r\n]+)#/g, function (_, sym) { - return provideSymbolAsSourcecode(sym); - }); - - this.actionsUseYYLENG = analyzeFeatureUsage(this.performAction, /\byyleng\b/g, 1); - this.actionsUseYYLINENO = analyzeFeatureUsage(this.performAction, /\byylineno\b/g, 1); - this.actionsUseYYTEXT = analyzeFeatureUsage(this.performAction, /\byytext\b/g, 1); - this.actionsUseYYLOC = analyzeFeatureUsage(this.performAction, /\byyloc\b/g, 1); - this.actionsUseParseError = analyzeFeatureUsage(this.performAction, /\.parseError\b/g, 0); - this.actionsUseYYERROR = analyzeFeatureUsage(this.performAction, /\byyerror\b/g, 0); - this.actionsUseYYRECOVERING = analyzeFeatureUsage(this.performAction, /\bYYRECOVERING\b/g, 0); - this.actionsUseYYERROK = analyzeFeatureUsage(this.performAction, /\byyerrok\b/g, 0); - this.actionsUseYYCLEARIN = analyzeFeatureUsage(this.performAction, /\byyclearin\b/g, 0); - // At this point in time, we have already expanded `$name`, `$$` and `$n` to its `$$[n]` index expression. - // - // Also cannot use regex `\b` with `\$` as the regex doesn't consider the literal `$` to be a *word* character - // hence the *boundary check* `\b` won't deliver as expected. Hence we'll have to wing it but we can, assured - // in the knowledge that the 'sourcecode' we have here is a complete generated *function* which will include - // the `function ` prelude and `}` postlude at least! Hence we can replace `\b` with `[^\w]` and we'll be good. - this.actionsUseValueTracking = analyzeFeatureUsage(this.performAction, /\byyvstack\b/g, 1); - // Ditto for the specific case where we are assigning a value to `$$`, i.e. `this.$`: - this.actionsUseValueAssignment = analyzeFeatureUsage(this.performAction, /\bthis\.\$[^\w]/g, 0); - // Ditto for the expansion of `@name`, `@$` and `@n` to its `yylstack[n]` index expression: - this.actionsUseLocationTracking = analyzeFeatureUsage(this.performAction, /\byylstack\b/g, 1); - // Ditto for the specific case where we are assigning a value to `@$`, i.e. `this._$`: - this.actionsUseLocationAssignment = analyzeFeatureUsage(this.performAction, /\bthis\._\$[^\w]/g, 0); - // Note that the `#name`, `#$` and `#n` constructs are expanded directly to their symbol number without - // the need to use yystack! Hence yystack is only there for very special use action code.) - this.actionsUseYYSTACK = analyzeFeatureUsage(this.performAction, /\byystack\b/g, 1); - // Ditto for yysstack... - this.actionsUseYYSSTACK = analyzeFeatureUsage(this.performAction, /\byysstack\b/g, 1); - this.actionsUseYYSTACKPOINTER = analyzeFeatureUsage(this.performAction, /\byysp\b/g, 1); - - this.performAction = this.performAction - .replace(/\byyerror\b/g, 'yy.parser.yyError') - .replace(/\bYYRECOVERING\b(?:\s*\(\s*\))?/g, 'yy.parser.yyRecovering()') - .replace(/\byyerrok\b(?:\s*\(\s*\))?/g, 'yy.parser.yyErrOk()') - .replace(/\byyclearin\b(?:\s*\(\s*\))?/g, 'yy.parser.yyClearIn()'); - - // Now that we've completed all macro expansions, it's time to execute - // the recovery code, i.e. the postprocess: - this.performAction = postprocessActionCode(this.performAction); - - // Now obtain an *EMPTY* `parser__PerformAction()` to compare against and see if all the work - // actually delivered any code that is important and needs executing. - // If not, we can discard the entire function! - // - // Since we'll recursively invoke this implementation (but with a completely different - // `this` reference!), we MUST check if we're currently already constructing such an - // 'empty call': - var actionsBaseline = ''; - if (!this.__constructing_empty_PerformAction_function) { - var empty_parser = { - __constructing_empty_PerformAction_function: true, - - parseParams: this.parseParams, - moduleInclude: '', - moduleInit: [], - actionInclude: '', - productions: [], - nonterminals: [], - symbols: [], - operators: [], - }; - buildProductionActions.call(empty_parser); - var actionsBaseline = empty_parser.performAction; - - // report whether there are actually any custom actions at all (or any custom actions' prep code); this - // flag will be set when the generated function is essentially *empty*: - this.actionsAreAllDefault = (actionsBaseline.replace(/\s+/g, ' ') === this.performAction.replace(/\s+/g, ' ')); - } - - if (devDebug || this.DEBUG) { - Jison.print('Optimization analysis: ', { - actionsAreAllDefault: this.actionsAreAllDefault, - actionsUseYYLENG: this.actionsUseYYLENG, - actionsUseYYLINENO: this.actionsUseYYLINENO, - actionsUseYYTEXT: this.actionsUseYYTEXT, - actionsUseYYLOC: this.actionsUseYYLOC, - actionsUseParseError: this.actionsUseParseError, - actionsUseYYERROR: this.actionsUseYYERROR, - actionsUseYYRECOVERING: this.actionsUseYYRECOVERING, - actionsUseYYERROK: this.actionsUseYYERROK, - actionsUseYYCLEARIN: this.actionsUseYYCLEARIN, - actionsUseValueTracking: this.actionsUseValueTracking, - actionsUseValueAssignment: this.actionsUseValueAssignment, - actionsUseLocationTracking: this.actionsUseLocationTracking, - actionsUseLocationAssignment: this.actionsUseLocationAssignment, - actionsUseYYSTACK: this.actionsUseYYSTACK, - actionsUseYYSSTACK: this.actionsUseYYSSTACK, - actionsUseYYSTACKPOINTER: this.actionsUseYYSTACKPOINTER, - hasErrorRecovery: this.hasErrorRecovery, - noDefaultAction: this.options.noDefaultAction, - noTryCatch: this.options.noTryCatch, - }); - } - - // And before we leave, as a SIDE EFFECT of this call, we also fixup - // the other code chunks specified in the grammar file: - // - // Replace direct symbol references, e.g. #NUMBER# when there's a `%token NUMBER` for your grammar. - // We allow these tokens to be referenced anywhere in your code as #TOKEN#. - this.moduleInclude = postprocessActionCode( - preprocessActionCode(this.moduleInclude) - .replace(/#([^#\s\r\n]+)#/g, function (_, sym) { - return provideSymbolAsSourcecode(sym); - }) - ); - this.moduleInit.forEach(function (chunk) { - assert(chunk.qualifier); - assert(typeof chunk.include === 'string'); - chunk.include = postprocessActionCode( - preprocessActionCode(chunk.include) - .replace(/#([^#\s\r\n]+)#/g, function (_, sym) { - return provideSymbolAsSourcecode(sym); - }) - ); - }); - - - function analyzeFeatureUsage(sourcecode, feature, threshold) { - var found = sourcecode.match(feature); - return !!(found && found.length > threshold); - } - - // make sure a comment does not contain any embedded '*/' end-of-comment marker - // as that would break the generated code - function postprocessComment(str) { - if (Array.isArray(str)) { - str = str.map(function (_) { - return (_ === '' || _ == null) ? 'ε' : _; - }).join(' '); - } - if (str === '') { - str = 'ε'; - } - str = str.replace(/\*\//g, '*\\/'); // destroy any inner `*/` comment terminator sequence. - return str; - } - - function getSymbolId(s) { - if (s && !self.symbols_[s]) { - throw new Error('Your action code is trying to reference non-existing symbol "' + s + '"'); - } - return self.symbols_[s] || false; - } - - function provideSymbolAsSourcecode(sym) { - var ss = String(sym); - return ' /* ' + postprocessComment(ss) + ' */ ' + getSymbolId(sym); - } - - // helper: convert index string/number to proper JS add/subtract expression - function indexToJsExpr(n, len, rule4msg) { - var v = parseInt(n, 10); - // the usual situation: `$3`; MUST reference an rhs[] element or it will be considered an ERROR: - if (v > 0) { - if (len - v < 0) { - throw new Error('invalid token reference in action code for rule: "' + rule4msg + '"'); - } - v = len - v; - if (v) { - return ' - ' + v; - } - // do not generate code for superfluous `- 0` JS expression: - return ''; - } - // the VERY UNusual situation: `$-1`: referencing *parent* rules' values - if (v < 0) { - return ' - ' + (len - v); - } - // decode error? - if (v !== 0) { - throw new Error('invalid token reference in action code for rule: "' + rule4msg + '"'); - } - // the slightly unusual situation: `$0` (instead of `$$`) - v = len; - if (v) { - return ' - ' + v; - } - // do not generate code for superfluous `- 0` JS expression: - return ''; - } - - function buildProductionAction(handle, index) { - var r, i; - - if (devDebug) Jison.print('\nbuildProductionAction: ', handle.symbol, ':', JSON.stringify(handle, null, 2)); - - if (handle.action) { - var aliased = handle.aliases, - rhs_i; - - var rhs = handle.handle; - var named_token_re = new XRegExp('^[\\p{Alphabetic}_][\\p{Alphabetic}_\\p{Number}]*$'); - - // semantic action specified - var label = [ - 'case ', handle.id, ':', - '\n /*! Production:: ', postprocessComment(handle.symbol), ' : ' - ].concat(postprocessComment(rhs.map(function (sym) { - // check if the symbol is a literal terminal, and if it is, quote it: - if (sym && !self.nonterminals[sym] && !named_token_re.test(sym)) { - return '"' + sym.replace(/["]/g, '\\"') + '"'; - } - return sym; - })), ' */\n'); - var action = preprocessActionCode(handle.action); - var actionHash; - var rule4msg = handle.symbol + ': ' + rhs.join(' '); - - // before anything else, replace direct symbol references, e.g. #NUMBER# when there's a %token NUMBER for your grammar. - // This is done to prevent incorrect expansions where tokens are used in rules as RHS elements: we allow these to - // be referenced as both #TOKEN# and #TOKEN where the first is a literal token/symbol reference (unrelated to its use - // in the rule) and the latter is a reference to the token/symbol being used in the rule. - // - // Here we expand those direct token/symbol references: #TOKEN# - action = action - .replace(/#([^#\s\r\n]+)#/g, function (_, sym) { - return provideSymbolAsSourcecode(sym); - }); - - // replace named semantic values ($nonterminal) - if (action.match(new XRegExp('(?:[$@#]|##)[\\p{Alphabetic}_][\\p{Alphabetic}_\\p{Number}]*'))) { - var count = {}, - names = {}, - donotalias = {}; - - // When the rule is fitted with aliases it doesn't mean that the action code MUST use those: - // we therefor allow access to both the original (non)terminal and the alias. - // - // Also note that each (non)terminal can also be uniquely addressed by [$@] - // where N is a number representing the number of this particular occurrence of the given - // (non)terminal. - // - // For example, given this (intentionally contrived) production: - // elem[alias] elem[another_alias] another_elem[alias] elem[alias] another_elem[another_alias] - // all the items can be accessed as: - // $1 $2 $3 $4 $5 - // $elem1 $elem2 $another_elem1 $elem3 $another_elem2 - // $elem $elem2 $another_elem $elem3 $another_elem2 - // $alias1 $another_alias1 $alias2 $alias3 $another_alias2 - // $alias $another_alias $alias2 $alias3 $another_alias2 - // where each line above is equivalent to the top-most line. Note the numbers postfixed to - // both (non)terminal identifiers and aliases alike and also note alias2 === another_elem1: - // the postfix numbering is independent. - // - // WARNING: this feature is disabled for a term when there already exists an - // (human-defined) *alias* for this term *or* when the numbered auto-alias already - // exists because the user has used it as an alias for another term, e.g. - // - // e: WORD[e1] '=' e '+' e; - // - // would *not* produce the `e1` and `e2` aliases, as `e1` is already defined - // as an explicit alias: adding auto-alias `e1` would then break the system, - // while `e2` would be ambiguous from the human perspective as he *might* then - // expect `e2` and `e3`. - var addName = function addName(s) { - var base = s.replace(/[0-9]+$/, ''); - var dna = donotalias[base]; - - if (names[s]) { - count[s]++; - if (!dna) { - names[s + count[s]] = i + 1; - count[s + count[s]] = 1; - } - } else { - names[s] = i + 1; - count[s] = 1; - if (!dna) { - names[s + count[s]] = i + 1; - count[s + count[s]] = 1; - } - } - }; - - // register the alias/rule name when the real one ends with a number, e.g. `rule5` as - // *blocking* the auto-aliasing process for the term of the same base, e.g. `rule`. - // This will catch the `WORD[e1]` example above too, via `e1` --> `donotalias['e']` - var markBasename = function markBasename(s) { - if (/[0-9]$/.test(s)) { - s = s.replace(/[0-9]+$/, ''); - donotalias[s] = true; - } - }; - - for (i = 0; i < rhs.length; i++) { - // mark both regular and aliased names, e.g., `id[alias1]` and `id1` - rhs_i = aliased[i]; - markBasename(rhs_i); - if (rhs_i !== rhs[i]) { - markBasename(rhs[i]); - } - } - - for (i = 0; i < rhs.length; i++) { - // check for aliased names, e.g., id[alias] - rhs_i = aliased[i]; - addName(rhs_i); - if (rhs_i !== rhs[i]) { - addName(rhs[i]); - } - } - action = action.replace( - new XRegExp('([$@#]|##)([\\p{Alphabetic}_][\\p{Alphabetic}_\\p{Number}]*)', 'g'), function (str, mrkr, pl) { - if (names[pl] && count[pl] !== 1) { - throw new Error('The action block references the ambiguous named alias or term reference "' + pl + '" ' + - 'which is mentioned ' + count[pl] + ' times in production "' + handle.handle + '", implicit and explicit aliases included.\n' + - 'You should either provide unambiguous = uniquely named aliases for these terms or use numeric index references (e.g. `$3`) as a stop-gap in your action code.'); - } - return names[pl] ? mrkr + names[pl] : str; - }); - } - action = action - // replace references to `$$` with `this.$`, `@$` with `this._$` and `#$` with the token ID of the current rule - .replace(/\$\$/g, 'this.$') - .replace(/@\$/g, 'this._$') - .replace(/#\$/g, function (_) { - return provideSymbolAsSourcecode(symbol); - }) - // replace semantic value references ($n) with stack value (stack[n]) - .replace(/\$(-?\d+)\b/g, function (_, n) { - return 'yyvstack[yysp' + indexToJsExpr(n, rhs.length, rule4msg) + ']'; - }) - // same as above for location references (@n) - .replace(/@(-?\d+)\b/g, function (_, n) { - return 'yylstack[yysp' + indexToJsExpr(n, rhs.length, rule4msg) + ']'; - }) - // same as above for positional value references (##n): these represent stack indexes - .replace(/##(-?\d+)\b/g, function (_, n) { - return '(yysp' + indexToJsExpr(n, rhs.length, rule4msg) + ')'; - }) - .replace(/##\$/g, function (_) { - return 'yysp'; - }) - // same as above for token ID references (#n) - .replace(/#(-?\d+)\b/g, function (_, n) { - var i = parseInt(n, 10) - 1; - if (!rhs[i]) { - throw new Error('invalid token location reference in action code for rule: "' + rule4msg + '" - location reference: "' + _ + '"'); - } - return provideSymbolAsSourcecode(rhs[i]); - }); - - action = reindentCodeBlock(action, 4); - - actionHash = mkHashIndex(action); - - // Delay running the postprocess (restore) process until we've done ALL macro expansions: - //action = postprocessActionCode(action); - - if (actionHash in actionGroups) { - actionGroups[actionHash].push(label); - } else { - actionGroups[actionHash] = [label]; - actionGroupValue[actionHash] = action; - } - } - } -}; - - - -generator.createParser = function createParser() { - throw new Error('Calling abstract method.'); -}; - -generator.createLexer = function createLexer() { - throw new Error('Calling abstract method.'); -}; - -// no-op. implemented in debug mixin -generator.trace = function no_op_trace() { }; - -generator.warn = function warn() { - var args = Array.prototype.slice.call(arguments, 0); - Jison.print.call(null, args.join('')); -}; - -generator.error = function error(msg) { - throw new Error(msg); -}; - -// Report a few things about the grammar: -// -// - unused rules -// - stats: -// + production count (-> parser table size indicator) -// + state count (-> parser table size indicator) -// -generator.reportGrammarInformation = function reportGrammarInformation() { - if (this.unused_productions.length) { - this.warn('\nUnused productions in your grammar:\n ' + this.unused_productions.join('\n ') + '\n\n'); - } - - if (!this.options.reportStats) { - return; - } - - // nonterminals = this.nonterminals, - // operators = this.operators, - // this.table - // this.states - // this.defaultActions - // this.productions, - // this.terms = {}; - // this.operators = {}; - // this.productions = []; - // this.conflicts = 0; - // this.new_conflicts_found_this_round = 0; - // this.conflicting_states = []; - // this.resolutions = []; - // this.options = options; - // this.parseParams = grammar.parseParams; - // exportDest.parseTable = this.table; - // exportDest.defaultParseActions = this.defaultActions; - // exportDest.parseProductions = this.productions_; - - // TODO: the next bit of code is LR type specific: refactor into a - // LR specific mixin class later on, so that we can have another - // implementation/report for LL and PEG type grammars. - - var rows = 0, cols = 0; - var colmarks = {}; - var i, j, len; - - for (i = 0, len = this.table.length; i < len; i++) { - rows++; - for (j in this.table[i]) { - if (!colmarks[j]) { - colmarks[j] = true; - cols++; - } - } - } - var defrows = 0; - var rowmarks = {}; - for (j in this.defaultActions) { - if (!rowmarks[j]) { - rowmarks[j] = true; - defrows++; - } - } - - var ntc = 0; - for (var nt in this.nonterminals) { - ntc++; - } - - if (devDebug > 3) Jison.print('LALR parse table: ', { - table: this.table, - defaultActions: this.defaultActions - }); - - this.warn('Number of productions in parser:........ ' + this.productions_.length); - this.warn('Number of non-terminals in grammar:..... ' + ntc); - this.warn('Number of states:....................... ' + this.states.size()); - this.warn('Number of rows (states) in table:....... ' + this.table.length); - this.warn('Number of rows in table:................ ' + rows); - this.warn('Number of columns in table:............. ' + cols); - this.warn('Number of defaulted rows in table:...... ' + defrows); - this.warn('Number of unresolvable conflicts:....... ' + this.conflicts); - this.warn('\n'); -}; - - -// Generator debug mixin - -var generatorDebug = { - trace: function debug_trace() { - if (typeof Jison !== 'undefined' && Jison.print) { - Jison.print.apply(null, arguments); - } else if (typeof print !== 'undefined') { - print.apply(null, arguments); - } else if (typeof console !== 'undefined' && console.log) { - console.log.apply(null, arguments); - } - }, - beforeprocessGrammar: function () { - this.trace('Processing grammar.'); - }, - afteraugmentGrammar: function () { - var trace = this.trace; - trace('\nSymbols:\n'); - each(this.symbols, function (sym, i) { - trace(sym + '(' + i + ')'); - }); - trace('\n'); - } -}; - - - -/* - * Mixin for common behaviors of lookahead parsers - */ -var lookaheadMixin = {}; - -lookaheadMixin.computeLookaheads = function computeLookaheads() { - if (this.DEBUG) { - this.mix(lookaheadDebug); // mixin debug methods - } - - this.computeLookaheads = function () {}; - this.nullableSets(); - this.firstSets(); - this.followSets(); -}; - -lookaheadMixin.displayFollowSets = function displayFollowSets() { - var self = this; - var symfollowdbg = {}; - this.productions.forEach(function Follow_prod_forEach_debugOut(production, k) { - // self.trace('Symbol/Follows: ', 'prod:' + k, ':', production.symbol, ' :: ', production.handle.join(' '), ' --> ', self.nonterminals[production.symbol].follows.join(', ')); - var key = ['prod-', k, ': ', production.symbol, ' := ', production.handle.join(' ')].join(''); - var flw = '[' + self.nonterminals[production.symbol].follows.join('] [') + ']'; - if (!symfollowdbg[flw]) { - symfollowdbg[flw] = {}; - } - if (!symfollowdbg[flw][key]) { - symfollowdbg[flw][key] = 1; - } else { - assert(0); - symfollowdbg[flw][key]++; - } - }); - for (var l in symfollowdbg) { - var lst = []; - for (var k in symfollowdbg[l]) { - lst.push(k); - } - self.trace('Symbol/Follows:\n ', lst.join('\n '), ' -->\n ', l); - } -}; - -// calculate follow sets based on first and nullable -lookaheadMixin.followSets = function followSets() { - var productions = this.productions, - nonterminals = this.nonterminals, - self = this, - cont = true, - count = 0; - - // loop until no further changes have been made - while (cont) { - cont = false; - count++; - - productions.forEach(function Follow_prod_forEach(production, k) { - if (devDebug > 3) Jison.print('Symbol/Follows: ', 'round:' + count, 'prod:' + k, ':', production.symbol, ' --> ', nonterminals[production.symbol].follows.join(', ')); - - // q is used in Simple LALR algorithm determine follows in context - var q; - var ctx = !!self.go_; - - for (var i = 0, t; (t = production.handle[i]); ++i) { - if (!nonterminals[t]) continue; - - // for Simple LALR algorithm, self.go_ checks if - if (ctx) { - q = self.go_(production.symbol, production.handle.slice(0, i)); - } - var bool = (!ctx || q === self.nterms_[t]); - var set; - - if (i === production.handle.length - 1 && bool) { - set = nonterminals[production.symbol].follows; - } else { - var part = production.handle.slice(i + 1); - - set = self.first(part); - if (self.nullable(part) && bool) { - assert(nonterminals[production.symbol].follows); - set.push.apply(set, nonterminals[production.symbol].follows); - } - } - var follows = nonterminals[t].follows; - var oldcount = follows.length; - follows = union(follows, set); - if (oldcount !== follows.length) { - cont = true; - } - nonterminals[t].follows = follows; - } - }); - } - - if (devDebug || this.DEBUG) { - this.displayFollowSets(); - } -}; - -// return the FIRST set of a symbol or series of symbols -lookaheadMixin.first = function first(symbol) { - // epsilon - if (symbol === '') { - return []; - // RHS - } else if (symbol instanceof Array) { - var firsts = []; - for (var i = 0, t; (t = symbol[i]); ++i) { - if (!this.nonterminals[t]) { - if (firsts.indexOf(t) === -1) { - firsts.push(t); - } - } else { - firsts = union(firsts, this.nonterminals[t].first); - } - if (!this.nullable(t)) - break; - } - return firsts; - // terminal - } else if (!this.nonterminals[symbol]) { - return [symbol]; - // nonterminal - } else { - return this.nonterminals[symbol].first; - } -}; - -// fixed-point calculation of FIRST sets -lookaheadMixin.firstSets = function firstSets() { - var productions = this.productions, - nonterminals = this.nonterminals, - self = this, - cont = true, - symbol, firsts; - - // loop until no further changes have been made - while (cont) { - cont = false; - - productions.forEach(function FirstSets_forEach(production, k) { - var firsts = self.first(production.handle); - if (firsts.length !== production.first.length) { - production.first = firsts; - cont = true; - } - }); - - for (symbol in nonterminals) { - firsts = []; - nonterminals[symbol].productions.forEach(function FirstSets_forEachNonTerm(production) { - firsts = union(firsts, production.first); - }); - if (firsts.length !== nonterminals[symbol].first.length) { - nonterminals[symbol].first = firsts; - cont = true; - } - } - } -}; - -// fixed-point calculation of NULLABLE -lookaheadMixin.nullableSets = function nullableSets() { - var nonterminals = this.nonterminals, - self = this, - cont = true; - - // loop until no further changes have been made - while (cont) { - cont = false; - - // check if each production is nullable - this.productions.forEach(function isEachProductionNullable(production, k) { - if (!production.nullable) { - for (var i = 0, n = 0, t; (t = production.handle[i]); ++i) { - if (self.nullable(t)) n++; - } - if (n === i) { // production is nullable if all tokens are nullable - production.nullable = cont = true; - } - } - }); - - // check if each symbol is nullable - for (var symbol in nonterminals) { - if (!this.nullable(symbol)) { - for (var i = 0, production; (production = nonterminals[symbol].productions.item(i)); i++) { - if (production.nullable) { - nonterminals[symbol].nullable = cont = true; - } - } - } - } - } -}; - -// check if a token or series of tokens is nullable -lookaheadMixin.nullable = function nullable(symbol) { - // epsilon - if (symbol === '') { - return true; - // RHS - } else if (symbol instanceof Array) { - for (var i = 0, t; (t = symbol[i]); ++i) { - if (!this.nullable(t)) { - return false; - } - } - return true; - // terminal - } else if (!this.nonterminals[symbol]) { - return false; - // nonterminal - } else { - return this.nonterminals[symbol].nullable; - } -}; - - -// lookahead debug mixin -var lookaheadDebug = { - beforenullableSets: function () { - this.trace('Computing Nullable sets.'); - }, - beforefirstSets: function () { - this.trace('Computing First sets.'); - }, - beforefollowSets: function () { - this.trace('Computing Follow sets.'); - }, - afterfollowSets: function () { - var trace = this.trace; - trace('\nNonterminals:\n'); - each(this.nonterminals, function (nt, t) { - trace(nt.toString(), '\n'); - }); - trace('\n'); - } -}; - -/* - * Mixin for common LR parser behavior - */ -var lrGeneratorMixin = {}; - - -// LR state machine actions: -const NONASSOC = 0; -const SHIFT = 1; // shift -const REDUCE = 2; // reduce -const ACCEPT = 3; // accept - - -lrGeneratorMixin.buildTable = function buildTable() { - if (this.DEBUG) { - this.mix(lrGeneratorDebug); // mixin debug methods - } - - this.states = this.canonicalCollection(); - - if (devDebug || this.DEBUG) { - Jison.print('\n-------------------------------------------\nSymbol/Follow sets AFTER canonicalCollection:'); - this.displayFollowSets(); - Jison.print('\n'); - } - - this.table = this.parseTable(this.states); - - if (devDebug || this.DEBUG) { - Jison.print('\n-------------------------------------------\nSymbol/Follow sets AFTER parseTable:'); - this.displayFollowSets(); - Jison.print('\n'); - } - - this.defaultActions = findDefaults(this.table, this.hasErrorRecovery); - cleanupTable(this.table); - - traceStates(this.trace, this.states, 'at the end of LR::buildTable(), after cleanupTable()'); -}; - -lrGeneratorMixin.Item = typal.construct({ - constructor: function Item(production, dotPosition, followSet, predecessor) { - this.production = production; - this.dotPosition = dotPosition || 0; - this.follows = followSet || []; - this.predecessor = predecessor; - this.id = production.id + '#' + this.dotPosition; - this.markedSymbol = this.production.handle[this.dotPosition]; - }, - remainingHandle: function () { - return this.production.handle.slice(this.dotPosition + 1); - }, - eq: function (e) { - return e.id === this.id; - }, - handleToString: function () { - var handle = this.production.handle.slice(0); - handle[this.dotPosition] = '.' + (handle[this.dotPosition] || ''); - return handle.join(' '); - }, - toString: function () { - var temp = this.production.handle.slice(0); - temp[this.dotPosition] = '.' + (temp[this.dotPosition] || ''); - var s = this.production.symbol + ' -> ' + temp.join(' '); - var padlen = Math.max(4, 40 - s.length); - var pad = new Array(padlen); - if (this.follows.length) { - s += pad.join(' ') + '#lookaheads= [' + this.follows.join('] [') + ']'; - pad = new Array(2); - } - if (this.reductions && this.reductions.length) { - s += pad.join(' ') + '#reductions= [' + this.reductions.join('] [') + ']'; - pad = new Array(2); - } - return s; - } -}); - -lrGeneratorMixin.ItemSet = Set.prototype.construct({ - afterconstructor: function () { - this.reductions = []; - this.goes = {}; - this.edges = {}; - this.shifts = false; - this.inadequate = false; - this.hash_ = {}; - for (var i = this._items.length - 1; i >= 0; i--) { - this.hash_[this._items[i].id] = true; //i; - } - }, - concat: function concat(set) { - var a = set._items || set; - for (var i = a.length - 1; i >= 0; i--) { - this.hash_[a[i].id] = true; - } - this._items.push.apply(this._items, a); - return this; - }, - push: function (item) { - this.hash_[item.id] = true; - return this._items.push(item); - }, - contains: function (item) { - return this.hash_[item.id]; - }, - valueOf: function toValue() { - var v = this._items.map(function (a) { return a.id; }).sort().join('|'); - this.valueOf = function valueOf_inner() { return v; }; - return v; - } -}); - -lrGeneratorMixin.closureOperation = function closureOperation(itemSet) { - var closureSet = new this.ItemSet(); - var self = this; - - var set = itemSet, - itemQueue, - syms = {}; - - do { - itemQueue = new Set(); - closureSet = closureSet.concat(set); - set.forEach(function CO_set_forEach(item) { - var symbol = item.markedSymbol; - - // if token is a non-terminal, recursively add closures - if (symbol && self.nonterminals[symbol]) { - if (!syms[symbol]) { - self.nonterminals[symbol].productions.forEach(function CO_nt_forEach(production) { - var newItem = new self.Item(production, 0); - if (!closureSet.contains(newItem)) { - itemQueue.push(newItem); - } - }); - syms[symbol] = true; - } - } else if (!symbol) { - // reduction - closureSet.reductions.push(item); - closureSet.inadequate = closureSet.reductions.length > 1 || closureSet.shifts; - } else { - // shift - closureSet.shifts = true; - closureSet.inadequate = closureSet.reductions.length > 0; - } - }); - - set = itemQueue; - } while (!itemQueue.isEmpty()); - - return closureSet; -}; - -lrGeneratorMixin.gotoOperation = function gotoOperation(itemSet, symbol) { - var gotoSet = new this.ItemSet(), - self = this; - - itemSet.forEach(function goto_forEach(item, n) { - if (item.markedSymbol === symbol) { - gotoSet.push(new self.Item(item.production, item.dotPosition + 1, item.follows, n)); - } - }); - - return gotoSet; -}; - -/* - * Create unique set of item sets - */ -lrGeneratorMixin.canonicalCollection = function canonicalCollection() { - var item1 = new this.Item(this.productions[0], 0, [this.EOF]); - var firstStateNoClosure = new this.ItemSet(item1), - firstState = this.closureOperation(firstStateNoClosure), - states = new Set(firstState), - marked = 0, - self = this, - itemSet, - markedSymbols; - - states.has = {}; - states.has[firstStateNoClosure.valueOf()] = 0; - - if (devDebug > 0) Jison.print('canonicalCollection: ', states.has); - - while (marked !== states.size()) { - itemSet = states.item(marked); - markedSymbols = {}; - itemSet.forEach(function CC_itemSet_forEach(item) { - if (item.markedSymbol && !markedSymbols[item.markedSymbol] && item.markedSymbol !== self.EOF) { - markedSymbols[item.markedSymbol] = true; - self.canonicalCollectionInsert(item.markedSymbol, itemSet, states, marked); - } - }); - marked++; - } - - return states; -}; - -// Pushes a unique state into the queue. Some parsing algorithms may perform additional operations -lrGeneratorMixin.canonicalCollectionInsert = function canonicalCollectionInsert(symbol, itemSet, states, stateNum) { - var g = this.gotoOperation(itemSet, symbol), - state = states.has[g.valueOf()]; - - if (state !== undefined) { - itemSet.edges[symbol] = state; // store goto transition for table - states.item(state).predecessors[symbol].push(stateNum); - } else { - // add g to queue if not empty or duplicate - if (!g.isEmpty()) { - states.has[g.valueOf()] = states.size(); - g = this.closureOperation(g); - if (!g.predecessors) { - g.predecessors = {}; - } - itemSet.edges[symbol] = states.size(); // store goto transition for table - states.push(g); - g.predecessors[symbol] = [stateNum]; - } - } -}; - -lrGeneratorMixin.parseTable = function lrParseTable(itemSets) { - var states = [], - nonterminals = this.nonterminals, - operators = this.operators, - conflictedStates = {}, // set of [state, token] tuples - self = this; - - // for each item set - itemSets.forEach(function parseTableItem(itemSet, k) { - k = +k; - var state = states[k] = {}; - var action, stackSymbol; - - // set shift and goto actions - for (stackSymbol in itemSet.edges) { - itemSet.forEach(function findShiftAndGotoActions(item, j) { - // find shift and goto actions - if (item.markedSymbol === stackSymbol) { - var gotoState = itemSet.edges[stackSymbol]; - assert(gotoState); - if (nonterminals[stackSymbol]) { - // store state to go to after a reduce - //self.trace(k, stackSymbol, 'g' + gotoState); - state[self.symbols_[stackSymbol]] = gotoState; - } else { - //self.trace(k, stackSymbol, 's' + gotoState); - state[self.symbols_[stackSymbol]] = [SHIFT, gotoState]; - } - } - }); - } - - // set accept action - itemSet.forEach(function setAcceptAction(item, j) { - if (item.markedSymbol === self.EOF) { - // accept - state[self.symbols_[self.EOF]] = [ACCEPT]; - } - }); - - var allterms = self.lookAheads ? false : self.terminals; - - // set reductions and resolve potential conflicts - itemSet.reductions.forEach(function calcReduction(item, j) { - // if parser uses lookahead, only enumerate those terminals - var terminals = allterms || self.lookAheads(itemSet, item); - - terminals.forEach(function (stackSymbol) { - action = state[self.symbols_[stackSymbol]]; - var op = operators[stackSymbol]; - - // Reading a terminal and current position is at the end of a production, try to reduce - if (action) { - var sol = resolveConflict(item.production, op, [REDUCE, item.production.id], action[0] instanceof Array ? action[0] : action); - self.resolutions.push([k, stackSymbol, sol]); - if (sol.bydefault) { - self.conflicts++; - - if (self.conflict_fixing_round && self.options.hasPartialLrUpgradeOnConflict) { - // have we encountered a *new* conflict, compared to previous rounds? - if (!self.conflict_productions_LU[item.production.id]) { - self.new_conflicts_found_this_round++; - // and we RESET the `conflict_fixing_round` flag to signal that - // this round needs another one to attempt a *complete* fix - // of the grammar. - // - // This little act also conveniently helps to manage the - // *finity* of the big parsetable production loop, which - // wraps around all this work (and more). - self.conflict_fixing_round = false; - if (self.enableDebugLogs) { - self.warn('RESET conflict fixing: we need another round to see us through...'); - } - } - } - if (!self.conflict_fixing_round && self.options.hasPartialLrUpgradeOnConflict) { - self.conflict_productions_LU[item.production.id] = true; - self.conflict_states_LU[k] = true; - - if (devDebug > 4) Jison.print('Registering conflict: ', { - prod_id: item.production.id, - stateNum: k, - state: state, - production: item.production - }); - } - - if (self.enableDebugLogs) { - self.warn('Conflict in grammar: multiple actions possible when lookahead token is ', stackSymbol, ' in state ', k, '\n- ', printAction(sol.r, self), '\n- ', printAction(sol.s, self), '\n (', sol.msg, ')'); - } - conflictedStates[k] = { - reduction: item, - symbol: stackSymbol, - resolution: sol, - state: k - }; - - if (self.options.noDefaultResolve) { - if (!(action[0] instanceof Array)) { - action = [action]; - } - action.push(sol.r); - } - } else { - action = sol.action; - } - } else { - action = [REDUCE, item.production.id]; - } - if (action && action.length) { - state[self.symbols_[stackSymbol]] = action; - } else if (action === NONASSOC) { - state[self.symbols_[stackSymbol]] = NONASSOC; - // ^- Can't delete this node right away as it will influence - // `findDefaults()` decision-making process adversely when this state is - // not visible at that time. Hence we defer cleanup to the function - // `cleanupTable()` which will be invoked at the very end: the NONASSOC - // transition signals a transition into an ERROR state and we don't care - // for the explicit zero(0) to be present in our table as anything - // 'falsey' as an action code will be considered an error state in - // the parser and not having these zeroes around keeps the table small(er). - } - }); - }); - }); - - self.conflicting_states = conflictedStates; - - if (self.conflicts > 0) { - if (this.numExpectedConflictStates !== self.conflicts || self.enableDebugLogs) { - self.warn('\nStates with conflicts:'); - each(conflictedStates, function report_conflict_state(val, state) { - self.warn('\nState ' + state, ' (' + val.symbol + ' @ ' + val.reduction.production.symbol + ' -> ' + val.reduction.handleToString() + ')\n'); - self.warn(' ', itemSets.item(state).join('\n ')); - }); - self.warn('\n'); - } - } - - return states; -}; - -// find states with only one action: a reduction. -// -// Note: only the state columns for EOF/ERROR/terminals are relevant here as those -// columns are the only ones ever visited by the table lookup code at the top -// of the loop in the parse kernel as the `symbol` index used there cannot ever -// contain a *nonterminal* value! -// -// The nonterminals are recognizable in the table by having numeric entries, rather -// than 1-or-2-element array values, as they only store a GOTO state. -// -// --- -// -// Another 'default' is when all listed terminals all point to the exact same reduce state; -// only this time we are careful about the TERROR symbol as a state carrying that one -// is an explicitly encoded error recovery rule and should remain as-is. -function findDefaults(states, hasErrorRecovery) { - var defaults = {}; - states.forEach(function (state, k) { - var act, sym, st, def; - var i = 0; - var gotos = {}; - - for (sym in state) { - assert({}.hasOwnProperty.call(state, sym)); // it this isn't true, the last part of this function won't work! - // keep state rows where there's an error recovery state: - if (sym === 2 /* TERROR */) { - return; - } - st = state[sym]; - if (typeof st !== 'number') { - if (st[0] !== REDUCE) { - // not a reduce action: forget about this row! - return; - } - var go = st[1]; - if (!gotos[go]) { - gotos[go] = true; - i++; - act = sym; - } - } else if (st === NONASSOC) { - // forget about this row: it's a state where we should kick up an error - // because you're trying to get associativity going where there is none! - return; - } - } - - if (i === 1) { - // only one action in state and it's a reduction; hence we only need to store the new (goto production) state: - defaults[k] = state[act][1]; - - // ... and nuke the entry/entries in the parse table to save space in the generated output: we won't be needing - // it any more! But make sure we keep the slots for the nonterminal symbols, so only nuke the *terminal* entries! - // - // Aber Oh-ho! The table[] entries themselves *are* used: they are needed by - // the error recovery code to decide, when SHIFTING, if the ERROR token would - // improve (fix) matters when it is treated as an *inserted* token. This code - // is therefor not executed then! - // - // ... hence we only nuke these table entries (as that makes for a smaller table --> smaller parser file) - // when there's no error recovery code included in the generated parser: - if (!hasErrorRecovery) { - for (sym in state) { - st = state[sym]; - if (typeof st !== 'number') { - delete state[sym]; - } - } - } - } - }); - - return defaults; -} - -// Remove all NONASSOC state transitions from the generated table now that we don't need them any longer -function cleanupTable(table) { - table.forEach(function (state, k) { - var symbol; - - for (symbol in state) { - if (state[symbol] === NONASSOC) { - delete state[symbol]; - } - } - }); -} - -// resolves shift-reduce and reduce-reduce conflicts -function resolveConflict(production, op, reduce, shift) { - var sln = { - production: production, - operator: op, - r: reduce, - s: shift, - - msg: null, - action: null, - bydefault: false - }; - - if (shift[0] === REDUCE) { - sln.msg = 'Resolved R/R conflict: use first production declared in grammar.'; - sln.action = shift[1] < reduce[1] ? shift : reduce; - if (shift[1] !== reduce[1]) sln.bydefault = true; - return sln; - } - - if (production.precedence === 0 || !op) { - sln.msg = 'Resolved S/R conflict: shift by default.'; - sln.bydefault = true; - sln.action = shift; - } else if (production.precedence < op.precedence) { - sln.msg = 'Resolved S/R conflict: shift for higher precedent operator.'; - sln.action = shift; - } else if (production.precedence === op.precedence) { - if (op.assoc === 'right') { - sln.msg = 'Resolved S/R conflict: shift for right associative operator.'; - sln.action = shift; - } else if (op.assoc === 'left') { - sln.msg = 'Resolved S/R conflict: reduce for left associative operator.'; - sln.action = reduce; - } else if (op.assoc === 'nonassoc') { - sln.msg = 'Resolved S/R conflict: no action for non-associative operator.'; - sln.action = NONASSOC; - } - } else { - sln.msg = 'Resolved conflict: reduce for higher precedent production.'; - sln.action = reduce; - } - - return sln; -} - -/* - * Mixin for common LR/LL/*any* parser behavior - */ -var generatorMixin = {}; - -// internal helper function: -generatorMixin.__prepareOptions = function parser___prepare_Options(opt) { - opt = mkStdOptions(this.options, opt); - this.options = opt; - this.DEBUG = !!opt.debug; - if (devDebug > 3) { - Jison.print('GENERATE::OPTIONS:\n', this.options); - } - - // check for illegal identifier - if (!opt.moduleName || !opt.moduleName.match(/^[a-zA-Z_$][a-zA-Z0-9_$\.]*?[a-zA-Z0-9_$]$/)) { - if (opt.moduleName) { - var msg = 'WARNING: The specified moduleName "' + opt.moduleName + '" is illegal (only characters [a-zA-Z0-9_$] and "." dot are accepted); using the default moduleName "parser" instead.'; - if (typeof opt.warn_cb === 'function') { - opt.warn_cb(msg); - } else if (opt.warn_cb) { - Jison.print(msg); - } else { - // do not treat as warning; barf hairball instead so that this oddity gets noticed right away! - throw new Error(msg); - } - } - opt.moduleName = opt.defaultModuleName; - } - return opt; -}; - -generatorMixin.generateGenericHeaderComment = function generateGenericHeaderComment() { - var out = '/* parser generated by jison ' + version + ' */\n' - + ` -/* - * Returns a Parser object of the following structure: - * - * Parser: { - * yy: {} The so-called "shared state" or rather the *source* of it; - * the real "shared state" \`yy\` passed around to - * the rule actions, etc. is a derivative/copy of this one, - * not a direct reference! - * } - * - * Parser.prototype: { - * yy: {}, - * EOF: 1, - * TERROR: 2, - * - * trace: function(errorMessage, ...), - * - * JisonParserError: function(msg, hash), - * - * quoteName: function(name), - * Helper function which can be overridden by user code later on: put suitable - * quotes around literal IDs in a description string. - * - * originalQuoteName: function(name), - * The basic quoteName handler provided by JISON. - * \`cleanupAfterParse()\` will clean up and reset \`quoteName()\` to reference this function - * at the end of the \`parse()\`. - * - * describeSymbol: function(symbol), - * Return a more-or-less human-readable description of the given symbol, when - * available, or the symbol itself, serving as its own 'description' for lack - * of something better to serve up. - * - * Return NULL when the symbol is unknown to the parser. - * - * symbols_: {associative list: name ==> number}, - * terminals_: {associative list: number ==> name}, - * nonterminals: {associative list: rule-name ==> {associative list: number ==> rule-alt}}, - * terminal_descriptions_: (if there are any) {associative list: number ==> description}, - * productions_: [...], - * - * performAction: function parser__performAction(yytext, yyleng, yylineno, yyloc, yystate, yysp, yyvstack, yylstack, yystack, yysstack, ...), - * where \`...\` denotes the (optional) additional arguments the user passed to - * \`parser.parse(str, ...)\` and specified by way of \`%parse-param ...\` in the grammar file - * - * The function parameters and \`this\` have the following value/meaning: - * - \`this\` : reference to the \`yyval\` internal object, which has members (\`$\` and \`_$\`) - * to store/reference the rule value \`$$\` and location info \`@$\`. - * - * One important thing to note about \`this\` a.k.a. \`yyval\`: every *reduce* action gets - * to see the same object via the \`this\` reference, i.e. if you wish to carry custom - * data from one reduce action through to the next within a single parse run, then you - * may get nasty and use \`yyval\` a.k.a. \`this\` for storing you own semi-permanent data. - * - * - \`yytext\` : reference to the lexer value which belongs to the last lexer token used - * to match this rule. This is *not* the look-ahead token, but the last token - * that's actually part of this rule. - * - * Formulated another way, \`yytext\` is the value of the token immediately preceeding - * the current look-ahead token. - * Caveats apply for rules which don't require look-ahead, such as epsilon rules. - * - * - \`yyleng\` : ditto as \`yytext\`, only now for the lexer.yyleng value. - * - * - \`yylineno\`: ditto as \`yytext\`, only now for the lexer.yylineno value. - * - * - \`yyloc\` : ditto as \`yytext\`, only now for the lexer.yylloc lexer token location info. - * - * - \`yystate\` : the current parser state number, used internally for dispatching and - * executing the action code chunk matching the rule currently being reduced. - * - * - \`yysp\` : the current state stack position (a.k.a. 'stack pointer') - * - * This one comes in handy when you are going to do advanced things to the parser - * stacks, all of which are accessible from your action code (see the next entries below). - * - * Also note that you can access this and other stack index values using the new double-hash - * syntax, i.e. \`##$ === ##0 === yysp\`, while \`##1\` is the stack index for all things - * related to the first rule term, just like you have \`$1\`, \`@1\` and \`#1\`. - * This is made available to write very advanced grammar action rules, e.g. when you want - * to investigate the parse state stack in your action code, which would, for example, - * be relevant when you wish to implement error diagnostics and reporting schemes similar - * to the work described here: - * - * + Pottier, F., 2016. Reachability and error diagnosis in LR(1) automata. - * In Journées Francophones des Languages Applicatifs. - * - * + Jeffery, C.L., 2003. Generating LR syntax error messages from examples. - * ACM Transactions on Programming Languages and Systems (TOPLAS), 25(5), pp.631–640. - * - * - \`yyvstack\`: reference to the parser value stack. Also accessed via the \`$1\` etc. - * constructs. - * - * - \`yylstack\`: reference to the parser token location stack. Also accessed via - * the \`@1\` etc. constructs. - * - * - \`yystack\` : reference to the parser token id stack. Also accessed via the - * \`#1\` etc. constructs. - * - * Note: this is a bit of a **white lie** as we can statically decode any \`#n\` reference to - * its numeric token id value, hence that code wouldn't need the \`yystack\` but *you* might - * want access for your own purposes, such as error analysis as mentioned above! - * - * Note that this stack stores the current stack of *tokens*, that is the sequence of - * already parsed=reduced *nonterminals* (tokens representing rules) and *terminals* - * (lexer tokens *shifted* onto the stack until the rule they belong to is found and - * *reduced*. - * - * - \`yysstack\`: reference to the parser state stack. This one carries the internal parser - * *states* such as the one in \`yystate\`, which are used to represent - * the parser state machine in the *parse table*. *Very* *internal* stuff, - * what can I say? If you access this one, you're clearly doing wicked things - * - * - \`...\` : the extra arguments you specified in the \`%parse-param\` statement in your - * grammar definition file. - * - * table: [...], - * State transition table - * ---------------------- - * - * index levels are: - * - \`state\` --> hash table - * - \`symbol\` --> action (number or array) - * - * If the \`action\` is an array, these are the elements' meaning: - * - index [0]: 1 = shift, 2 = reduce, 3 = accept - * - index [1]: GOTO \`state\` - * - * If the \`action\` is a number, it is the GOTO \`state\` - * - * defaultActions: {...}, - * - * parseError: function(str, hash, ExceptionClass), - * yyError: function(str, ...), - * yyRecovering: function(), - * yyErrOk: function(), - * yyClearIn: function(), - * - * constructParseErrorInfo: function(error_message, exception_object, expected_token_set, is_recoverable), - * Helper function **which will be set up during the first invocation of the \`parse()\` method**. - * Produces a new errorInfo 'hash object' which can be passed into \`parseError()\`. - * See it's use in this parser kernel in many places; example usage: - * - * var infoObj = parser.constructParseErrorInfo('fail!', null, - * parser.collect_expected_token_set(state), true); - * var retVal = parser.parseError(infoObj.errStr, infoObj, parser.JisonParserError); - * - * originalParseError: function(str, hash, ExceptionClass), - * The basic \`parseError\` handler provided by JISON. - * \`cleanupAfterParse()\` will clean up and reset \`parseError()\` to reference this function - * at the end of the \`parse()\`. - * - * options: { ... parser %options ... }, - * - * parse: function(input[, args...]), - * Parse the given \`input\` and return the parsed value (or \`true\` when none was provided by - * the root action, in which case the parser is acting as a *matcher*). - * You MAY use the additional \`args...\` parameters as per \`%parse-param\` spec of this grammar: - * these extra \`args...\` are passed verbatim to the grammar rules' action code. - * - * cleanupAfterParse: function(resultValue, invoke_post_methods, do_not_nuke_errorinfos), - * Helper function **which will be set up during the first invocation of the \`parse()\` method**. - * This helper API is invoked at the end of the \`parse()\` call, unless an exception was thrown - * and \`%options no-try-catch\` has been defined for this grammar: in that case this helper MAY - * be invoked by calling user code to ensure the \`post_parse\` callbacks are invoked and - * the internal parser gets properly garbage collected under these particular circumstances. - * - * lexer: { - * yy: {...}, A reference to the so-called "shared state" \`yy\` once - * received via a call to the \`.setInput(input, yy)\` lexer API. - * EOF: 1, - * ERROR: 2, - * JisonLexerError: function(msg, hash), - * parseError: function(str, hash, ExceptionClass), - * setInput: function(input, [yy]), - * input: function(), - * unput: function(str), - * more: function(), - * reject: function(), - * less: function(n), - * pastInput: function(n), - * upcomingInput: function(n), - * showPosition: function(), - * test_match: function(regex_match_array, rule_index, ...), - * next: function(...), - * lex: function(...), - * begin: function(condition), - * pushState: function(condition), - * popState: function(), - * topState: function(), - * _currentRules: function(), - * stateStackSize: function(), - * cleanupAfterLex: function() - * - * options: { ... lexer %options ... }, - * - * performAction: function(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * rules: [...], - * conditions: {associative list: name ==> set}, - * } - * } - * - * - * token location info (@$, _$, etc.): { - * first_line: n, - * last_line: n, - * first_column: n, - * last_column: n, - * range: [start_number, end_number] - * (where the numbers are indexes into the input string, zero-based) - * } - * - * --- - * - * The \`parseError\` function receives a 'hash' object with these members for lexer and - * parser errors: - * - * { - * text: (matched text) - * token: (the produced terminal token, if any) - * token_id: (the produced terminal token numeric ID, if any) - * line: (yylineno) - * loc: (yylloc) - * } - * - * parser (grammar) errors will also provide these additional members: - * - * { - * expected: (array describing the set of expected tokens; - * may be UNDEFINED when we cannot easily produce such a set) - * state: (integer (or array when the table includes grammar collisions); - * represents the current internal state of the parser kernel. - * can, for example, be used to pass to the \`collect_expected_token_set()\` - * API to obtain the expected token set) - * action: (integer; represents the current internal action which will be executed) - * new_state: (integer; represents the next/planned internal state, once the current - * action has executed) - * recoverable: (boolean: TRUE when the parser MAY have an error recovery rule - * available for this particular error) - * state_stack: (array: the current parser LALR/LR internal state stack; this can be used, - * for instance, for advanced error analysis and reporting) - * value_stack: (array: the current parser LALR/LR internal \`$$\` value stack; this can be used, - * for instance, for advanced error analysis and reporting) - * location_stack: (array: the current parser LALR/LR internal location stack; this can be used, - * for instance, for advanced error analysis and reporting) - * yy: (object: the current parser internal "shared state" \`yy\` - * as is also available in the rule actions; this can be used, - * for instance, for advanced error analysis and reporting) - * lexer: (reference to the current lexer instance used by the parser) - * parser: (reference to the current parser instance) - * } - * - * while \`this\` will reference the current parser instance. - * - * When \`parseError\` is invoked by the lexer, \`this\` will still reference the related *parser* - * instance, while these additional \`hash\` fields will also be provided: - * - * { - * lexer: (reference to the current lexer instance which reported the error) - * } - * - * When \`parseError\` is invoked by the parser due to a **JavaScript exception** being fired - * from either the parser or lexer, \`this\` will still reference the related *parser* - * instance, while these additional \`hash\` fields will also be provided: - * - * { - * exception: (reference to the exception thrown) - * } - * - * Please do note that in the latter situation, the \`expected\` field will be omitted as - * this type of failure is assumed not to be due to *parse errors* but rather due to user - * action code in either parser or lexer failing unexpectedly. - * - * --- - * - * You can specify parser options by setting / modifying the \`.yy\` object of your Parser instance. - * These options are available: - * - * ### options which are global for all parser instances - * - * Parser.pre_parse: function(yy [, optional parse() args]) - * optional: you can specify a pre_parse() function in the chunk following - * the grammar, i.e. after the last \`%%\`. - * Parser.post_parse: function(yy, retval [, optional parse() args]) { return retval; } - * optional: you can specify a post_parse() function in the chunk following - * the grammar, i.e. after the last \`%%\`. When it does not return any value, - * the parser will return the original \`retval\`. - * - * ### options which can be set up per parser instance - * - * yy: { - * pre_parse: function(yy [, optional parse() args]) - * optional: is invoked before the parse cycle starts (and before the first - * invocation of \`lex()\`) but immediately after the invocation of - * \`parser.pre_parse()\`). - * post_parse: function(yy, retval [, optional parse() args]) { return retval; } - * optional: is invoked when the parse terminates due to success ('accept') - * or failure (even when exceptions are thrown). - * \`retval\` contains the return value to be produced by \`Parser.parse()\`; - * this function can override the return value by returning another. - * When it does not return any value, the parser will return the original - * \`retval\`. - * This function is invoked immediately before \`Parser.post_parse()\`. - * - * parseError: function(str, hash, ExceptionClass) - * optional: overrides the default \`parseError\` function. - * quoteName: function(name), - * optional: overrides the default \`quoteName\` function. - * } - * - * parser.lexer.options: { - * pre_lex: function() - * optional: is invoked before the lexer is invoked to produce another token. - * \`this\` refers to the Lexer object. - * post_lex: function(token) { return token; } - * optional: is invoked when the lexer has produced a token \`token\`; - * this function can override the returned token value by returning another. - * When it does not return any (truthy) value, the lexer will return - * the original \`token\`. - * \`this\` refers to the Lexer object. - * - * ranges: boolean - * optional: \`true\` ==> token location info will include a .range[] member. - * flex: boolean - * optional: \`true\` ==> flex-like lexing behaviour where the rules are tested - * exhaustively to find the longest match. - * backtrack_lexer: boolean - * optional: \`true\` ==> lexer regexes are tested in order and for invoked; - * the lexer terminates the scan when a token is returned by the action code. - * xregexp: boolean - * optional: \`true\` ==> lexer rule regexes are "extended regex format" requiring the - * \`XRegExp\` library. When this %option has not been specified at compile time, all lexer - * rule regexes have been written as standard JavaScript RegExp expressions. - * } - */ -`; - - return out; -}; - -generatorMixin.generate = function parser_generate(opt) { - opt = this.__prepareOptions(opt); - - var code = ''; - - switch (opt.moduleType) { - case 'js': - code = this.generateModule(opt); - break; - case 'amd': - code = this.generateAMDModule(opt); - break; - case 'es': - code = this.generateESModule(opt); - break; - case 'commonjs': - default: - code = this.generateCommonJSModule(opt); - break; - } - - return code; -}; - - -generatorMixin.generateAMDModule = function generateAMDModule(opt) { - opt = this.__prepareOptions(opt); - - var module = this.generateModule_(); - var out = [ - this.generateGenericHeaderComment(), - '', - 'define(function (require) {', - module.commonCode, - '', - 'var parser = ' + module.moduleCode, - module.modulePostlude, - '', - this.moduleInclude - ]; - if (this.lexer && this.lexer.generateModule) { - out.push(this.lexer.generateModule()); - out.push('parser.lexer = lexer;'); - } - out.push('return parser;'); - out.push('});'); - - return out.join('\n') + '\n'; -}; - -lrGeneratorMixin.generateESModule = function generateESModule(opt) { - opt = this.__prepareOptions(opt); - - var module = this.generateModule_(); - var out = [ - this.generateGenericHeaderComment(), - '', - module.commonCode, - '', - 'var parser = ' + module.moduleCode, - module.modulePostlude, - '', - this.moduleInclude - ]; - if (this.lexer && this.lexer.generateModule) { - out.push(this.lexer.generateModule()); - out.push('parser.lexer = lexer;'); - } - out.push('function Parser() { this.yy = {} };'); - out.push('Parser.prototype = parser;'); - out.push('parser.Parser = Parser;'); - out.push('export {parser, Parser};'); - - return out.join('\n') + '\n'; -}; - -generatorMixin.generateCommonJSModule = function generateCommonJSModule(opt) { - opt = this.__prepareOptions(opt); - - var moduleName = opt.moduleName; - var main = []; - if (!opt.noMain) { - main = main.concat([ - ' exports.main = ' + String(opt.moduleMain || commonjsMain) + ';', - ' if (typeof module !== \'undefined\' && require.main === module) {', - ' exports.main(process.argv.slice(1));', - ' }' - ]); - } - var out = [ - this.generateModule(opt), - '', - '', - 'if (typeof require !== \'undefined\' && typeof exports !== \'undefined\') {', - ' exports.parser = ' + moduleName + ';', - ' exports.Parser = ' + moduleName + '.Parser;', - ' exports.parse = function () {', - ' return ' + moduleName + '.parse.apply(' + moduleName + ', arguments);', - ' };', - main.join('\n'), - '}' - ]; - return out.join('\n') + '\n'; -}; - -generatorMixin.generateModule = function generateModule(opt) { - opt = this.__prepareOptions(opt); - - var moduleName = opt.moduleName; - var out = this.generateGenericHeaderComment(); - - var self = this; - var _generateNamespace = function (namespaces, previousNamespace, callback) { - var subModuleName = namespaces.shift(); - if (subModuleName != null) { - var moduleName = previousNamespace == null ? subModuleName : previousNamespace + '.' + subModuleName; - if (namespaces.length > 0) { - return 'var ' + subModuleName + ';\n' - + '(function (' + subModuleName + ') {\n' - + _generateNamespace(namespaces, subModuleName, callback) - + '\n})(' + subModuleName + (previousNamespace == null ? '' : ' = ' + moduleName) + ' || (' + moduleName + ' = {}));\n'; - } - return callback(moduleName); - } - return ''; - }; - - out += _generateNamespace(moduleName.split('.'), null, function _generateNamespace_cb(moduleName) { - return (moduleName.match(/\./) ? moduleName : 'var ' + moduleName) + - ' = ' + self.generateModuleExpr() + '\n'; - }); - - return out; -}; - - -generatorMixin.generateModuleExpr = function generateModuleExpr() { - var out; - var module = this.generateModule_(); - - out = [ - '(function () {', - module.commonCode, - '', - 'var parser = ' + module.moduleCode, - module.modulePostlude, - '', - this.moduleInclude - ]; - if (this.lexer && this.lexer.generateModule) { - out.push(this.lexer.generateModule()); - out.push('parser.lexer = lexer;'); - } - out = out.concat(['', - 'function Parser() {', - ' this.yy = {};', - '}', - 'Parser.prototype = parser;', - 'parser.Parser = Parser;', - '', - 'return new Parser();', - '})();' - ]); - return out.join('\n') + '\n'; -}; - -function removeUnusedKernelFeatures(parseFn, info) { - var actionFn = info.performAction; - - if (info.actionsAreAllDefault) { - // in this case, there's no need to call the parseAction function at all: - // it is functionally empty anyway. - actionFn = ''; - - // remove: - // - // r = this.performAction.call(yyval, ...); - // - // if (typeof r !== 'undefined') { - // retval = r; - // break; - // } - // - - parseFn = parseFn - .replace(/\s+r = this\.performAction\.call[^)]+\)\;/g, '') - .replace(/\s+if \(typeof r !== 'undefined'\) \{[^}]+\}/g, ''); - } - - if (!info.actionsUseYYTEXT) { - // Wait with this bit of cleanup until the very end to help keep the - // other cleanup/optimization options below that much simpler to code: - if (0) { - actionFn = actionFn - .replace(/\(\byytext\b(,\s*)?/g, '('); - } - - // kill the passing of the local variable as a parameter, - // its use in an assignment and its declaration: - parseFn = parseFn - .replace(/, yytext\b/g, '') - .replace(/^.*[^.]\byytext = .+$/gm, '') - .replace(/^.+ = yytext\b.+$/gm, ''); - } - - if (!info.actionsUseYYLENG) { - actionFn = actionFn - .replace(/, yyleng\b/g, ''); - - // remove: - // - // if (typeof lexer.yyleng === 'undefined') { - // lexer.yyleng = 0; - // } - // var yyleng = lexer.yyleng; - // ... - - parseFn = parseFn - .replace(/, yyleng\b/g, '') - .replace(/\s+if\b.*?\.yyleng\b.*?\{[^}]+\}/g, '\n') - .replace(/^.*?\byyleng\b.*?=.*?\byyleng\b.*?$/gm, ''); - } - - if (!info.actionsUseYYLINENO) { - // The error handling code inside the kernel still uses this one, but only straight off the lexer - // so we can kill the local var and its usage at least: - actionFn = actionFn - .replace(/, yylineno\b/g, ''); - - // remove: - // - // var yylineno = lexer.yylineno; - // ... - - parseFn = parseFn - .replace(/, yylineno\b/g, '') - .replace(/^.*?\byylineno\b.*?=.*?\byylineno\b.*?$/gm, ''); - } - - if (!info.actionsUseYYSTACK) { - actionFn = actionFn - .replace(/, yystack\b/g, ''); - - parseFn = parseFn - .replace(/, stack\b/g, ''); - } - - if (!info.actionsUseYYSSTACK) { - actionFn = actionFn - .replace(/, yysstack\b/g, ''); - - parseFn = parseFn - .replace(/, sstack\b/g, ''); - } - - if (!info.actionsUseYYLOC && !info.actionsUseLocationTracking && !info.actionsUseLocationAssignment) { - actionFn = actionFn - .replace(/\byyloc, (.*?), yylstack\b/g, '$1'); - - // remove: - // - // var ranges = lexer.options && lexer.options.ranges; - // ... - // var yyloc = lexer.yylloc || {}; - // lstack[sp] = yyloc; - // ... - // lstack[sp] = lexer.yylloc || {}; - // ... - // // default location, uses first token for firsts, last for lasts - // yyval._$ = { - // first_line: lstack[lstack_begin].first_line, - // last_line: lstack[lstack_end].last_line, - // first_column: lstack[lstack_begin].first_column, - // last_column: lstack[lstack_end].last_column - // }; - // if (ranges) { - // yyval._$.range = [lstack[lstack_begin].range[0], lstack[lstack_end].range[1]]; - // } - // ... - - parseFn = parseFn - .replace(/\byyloc, (.*?), lstack\b/g, '$1') - .replace(/\s+if\b.*?\.yylloc\b.*?\{[^}]+\{\s*\}[^}]+\}[^;]+;/g, '\n\n\n\n\n') - .replace(/\s*\/\/ default location,[^\n]+/g, '\n') - .replace(/\s+yyval\._\$\s*=\s*\{[^}]+\}[^\{\}]+\{[^}]+\}/g, '\n\n\n\n\n\n\n\n\n') - .replace(/^\s*var\s+ranges\s+=\s+lexer\.options\s+.*$/gm, '') - .replace(/^.*?\blstack\b.*$/gm, '') - .replace(/^.*?\blstack_[a-z]+.*$/gm, '') - .replace(/^.*?\byyloc\b.*?$/gm, '') - .replace(/^.*?\byylloc\b.*?$/gm, '') - .replace(/^\s*_\$:\s+undefined\s*$/gm, ''); - } - - if (!info.actionsUseValueTracking && !info.actionsUseValueAssignment) { - actionFn = actionFn - .replace(/, yyvstack\b/g, ''); - - parseFn = parseFn - .replace(/, vstack\b/g, ''); - - // kill *all* value tracking when there's also no *implicit* `$$ = $1` action any more: - if (info.options.noDefaultAction) { - // remove: - // - // // Make sure subsequent `$$ = $1` bla bla bla ... - // vstack[sp] = undefined; - // ...up to: - // yyval.$ = vstack[sp - len]; // default to $$ = $1 - // - // ... and other lines using `vstack[xyz...]` ... - - parseFn = parseFn - .replace(/\s+\/\/ Make sure subsequent `\$\$ = \$1` \n\s+yyval\.\$ = vstack\[sp - [^\n]+\n/g, '\n\n') - .replace(/^.*?\bvstack\b.*$/gm, ''); - - // also nuke all `yyval`-related code as we know, when this set of - // features is set, that the grammar doesn't produce any value: - // we are looking at a *matcher*, rather than a *parser*! - // - // remove - // - // // Return the `$accept` rule's `$$` result, if available. - // // ... - // if (typeof yyval.$ !== 'undefined') { - // retval = yyval.$; - // } - // - // bit keep the yyval declaration as either location tracking MAY - // still be employed by the grammar OR the grammar uses advanced - // code which uses `yyval` as a run-time store which carries data - // across multiple reduction calls to `performAction`, as per - // the suggestion in the document comment for the grammar: - // - // > - // > One important thing to note about `this` a.k.a. `yyval`: ... - // > - parseFn = parseFn - .replace(/\s+\/\/ Return the \`\$accept\` rule's \`\$\$\` result[\s\S]+?if \(typeof yyval\.\$ !== 'undefined'\)[^\}]+\}[^\n]*\n/g, '\n\n\n\n\n\n'); - - // ... unless, of course, when there's no `performAction()` call - // at all! Then the `yyval` declaration can safely be discarded - // as well. - if (info.actionsAreAllDefault) { - // remove - // - // var yyval = { - // $: true, - // _$: undefined, - // yy: sharedState_yy - // }; - parseFn = parseFn - .replace(/\s+var yyval =[\s\S]+?\};[^\n]*\n/g, '\n\n\n\n\n\n'); - } - } - } - - if (!info.DEBUG) { - // When 'debug mode' hasn't been turned on during parser generation, - // then we don't allow it at all: this gives us faster production parsers. - // - // When you want debug output at parse run-time, then you MUST produce a parser - // with either the - // %debug - // option set or by invoking JISON with the debug flag `-t`. - - // remove: - // - // var yydebug = false; - // ... and delete yydebug function definition ... - // ... - // if (yydebug) yydebug(...); - - parseFn = parseFn - .replace(/\s+var yydebug = [\s\S]+?self\.trace[\s\S]+?};[^}]+}/g, '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n') - // strip single-line `yydebug(...);` statements - .replace(/^.*?\byydebug\b[^;]+?\);[^\r\n]*?$/gm, '') - // strip multi-line `if (debug) yydebug(..., {...});` statements - .replace(/\n\s+if\s+\(yydebug\)\s+yydebug\([^]+?\}\);[^\r\n]*?/g, '\n\n\n\n\n\n\n\n\n'); - } - - if (!info.actionsUseYYERROK && !info.actionsUseYYRECOVERING && !info.actionsUseYYCLEARIN && !info.actionsUseYYERROR) { - /* - * Kill long multi-line comment about yyerror + YYRECOVERING + yyerrok + yyclearin before this code: - * - * if (this.yyError) { - * ... - */ - parseFn = parseFn - .replace(/\s+\/\/.*setup `yyError`, `YYRECOVERING`, `yyErrOk` and `yyClearIn` functions[^\0]+?\n\s+if \(/g, '\n\n\n\n\n if ('); - } - - if (!info.actionsUseYYERROR) { - /* - * Kill this code: - * - * if (this.yyError) { - * this.yyError = function yyError(str) { - * ... - * }; - * } - */ - parseFn = parseFn - .replace(/\s+if \(this\.yyError\) \{[^\0]+?\};\n\s+\}\n/g, '\n\n\n\n\n'); - } - - if (!info.actionsUseYYRECOVERING) { - /* - * Kill this code: - * - * if (this.yyRecovering) { - * this.yyRecovering = function yyRecovering() { - * return recovering; - * }; - * } - */ - parseFn = parseFn - .replace(/\s+if \(this\.yyRecovering\) \{[^\0]+?\};\n\s+\}\n/g, '\n\n\n\n\n'); - } - - if (!info.actionsUseYYERROK) { - /* - * Kill this code: - * - * if (this.yyErrOk) { - * this.yyErrOk = function yyErrOk() { - * recovering = 0; - * }; - * } - */ - parseFn = parseFn - .replace(/\s+if \(this\.yyErrOk\) \{[^\0]+?\};\n\s+\}\n/g, '\n\n\n\n\n'); - } - - if (!info.actionsUseYYCLEARIN) { - parseFn = parseFn - .replace(/\s+if \(this\.yyClearIn\) \{[^\0]+?[^{]\};\n\s+\}\n/g, '\n\n\n\n\n\n'); - } - - if (info.options.noDefaultAction) { - /* - * Kill this code: - * - * // perform semantic action - * yyval.$ = vstack[sp - len]; // default to $$ = $1 - * - * and this bit of comment: - * - * // Make sure subsequent `$$ = $1` default action doesn't fail - * // for rules where len==0 as then there's no $1 (you're reducing an epsilon rule then!) - * // - * // Also do this ... - */ - parseFn = parseFn - .replace(/\s+\/\/ perform semantic action\n\s+yyval\.\$ = vstack\[sp - [^\n]+\n/g, '\n\n') - .replace(/\s+\/\/ Make sure subsequent `\$\$ = \$1` default action doesn't fail\n[^\n]+\n[^\n]+\n(\s+\/\/ )Also do this/g, '\n\n\n\n\n$1Do this'); - - if (info.options.noTryCatch) { - /* - * This is a very performance-oriented setting and does not care if the - * userland code for the grammar rules is flaky. - * Kill this protection code: - * - * // Do this to prevent ... - * vstack[sp] = undefined; - */ - parseFn = parseFn - .replace(/\s+\/\/ Do this to prevent nasty action block codes[^\n]+\n\s+vstack\[sp\] = undefined;[^\n]+\n/g, '\n\n'); - } - } - - if (info.options.noTryCatch) { - /* - * Kill this code: - * - * try { - * for (;;) { - * ... keep this stuff ... - * } catch (ex) { - * ... remove this stuff ... - * } finally { - * retval = this.cleanupAfterParse(retval, true, true); // <-- keep this line - * } - * - * and also remove any re-entrant parse() call support: - * - * ... __reentrant_call_depth ... - */ - parseFn = parseFn - .replace(/\s+try \{([\s\S]+?)\} catch \(ex\) \{[\s\S]+?\} finally \{([^\}]+)\}/, function replace_noTryCatch(m, p1, p2) { - p1 = p1.replace(/^ /mg, ' '); - p2 = p2.replace(/^ /mg, ' '); - return '\n' + p1 + '\n // ... AND FINALLY ...\n' + p2; - }) - .replace(/^[^\n]+\b__reentrant_call_depth\b[^\n]+$/gm, '\n'); - } - - if (!info.actionsUseYYTEXT) { - // See the comment for the same section near the start of this function: - // - // Wait with this bit of cleanup until the very end to help keep the - // other cleanup/optimization options below that much simpler to code: - actionFn = actionFn - .replace(/\(\byytext\b(,\s*)?/g, '('); - } - - info.performAction = actionFn; - - return parseFn; -} - -// Fill in the optional, extra parse parameters (`%parse-param ...`) -// in the generated parser. -// -// See for important context: -// -// https://github.com/zaach/jison/pull/332 -function expandParseArguments(parseFn, self) { - var arglist = self.parseParams; - - if (!arglist) { - parseFn = parseFn.replace(/, parseParams\b/g, ''); - parseFn = parseFn.replace(/\bparseParams\b/g, ''); - } else { - parseFn = parseFn.replace(/, parseParams\b/g, ', ' + arglist.join(', ')); - parseFn = parseFn.replace(/\bparseParams\b/g, arglist.join(', ')); - } - return parseFn; -} - - -function pickOneOfTwoCodeAlternatives(parseFn, pick_A_not_B, A_start_marker, B_start_marker, end_marker) { - // Notes: - // 1) we use the special /[^\0]*/ regex set as that one will also munch newlines, etc. - // while the obvious /.*/ does not as '.' doesn't eat the newlines. - return parseFn.replace(new RegExp('(' + A_start_marker + '[^\\n]*\\n)([^\\0]*?)(' + B_start_marker + '[^\\n]*\\n)([^\\0]*?)(' + end_marker + '[^\\n]*\\n)', 'g'), function pick_code_alt(str, mA, cA, mB, cB, mE) { - if (pick_A_not_B) { - return cA; - } - return cB; - }); -} - -function addOrRemoveTokenStack(fn, wantTokenStack) { - var parseFn = fn; - // We don't use the Esprima+Escodegen toolchain as those loose the code comments easily; - // instead we just chop the code using labels as sentinels for our chopping-it-up regexes: - // - // if (wantTokenStack) { - // try { - // var ast = esprima.parse(parseFn); - // var stackAst = esprima.parse(String(tokenStackLex)).body[0]; - // stackAst.id.name = 'lex'; - // - // var labeled = JSONSelect.match(':has(:root > .label > .name:val("_token_stack"))', ast); - // - // labeled[0].body = stackAst; - // - // return escodegen.generate(ast); - // } catch (e) { - // return parseFn; - // } - // } else { - // // remove the line: - // // tstack = [], // token stack - // parseFn = parseFn.replace(/tstack = .*$/m, ''); - // return parseFn; - // } - parseFn = pickOneOfTwoCodeAlternatives(parseFn, !wantTokenStack, '//_lexer_without_token_stack:', '//_lexer_with_token_stack:', '//_lexer_with_token_stack_end:'); - // and some post-coital touch-ups: - if (wantTokenStack) { - // And rename the `tokenStackLex` function to become the new `lex`: - return parseFn.replace(/\btokenStackLex\b/g, 'lex'); - } else { - // Also nuke the support declaration statement: - // tstack = [], - return parseFn.replace(/^\s*tstack\b.*$/gm, ''); - } -} - -// returns parse function with/without error recovery code -function pickErrorHandlingChunk(fn, hasErrorRecovery) { - var parseFn = fn; - - // We don't use the Esprima+Escodegen toolchain as those loose the code comments easily; - // instead we just chop the code using labels as sentinels for our chopping-it-up regexes: - // try { - // var ast = esprima.parse(parseFn); - - // var labeled = JSONSelect.match(':has(:root > .label > .name:val("' + - // (!hasErrorRecovery ? '_handle_error_with_recovery' : '_handle_error_no_recovery') + - // '"))', ast); - // Jison.print('labeled: ', labeled); - // assert(labeled[0].body.type === 'IfStatement'); - // labeled[0].body.type = 'DebuggerStatement'; - // Jison.print('patched::labeled: ', labeled); - - // return escodegen.generate(ast); - // } catch (e) { - // return parseFn; - // } - parseFn = pickOneOfTwoCodeAlternatives(parseFn, hasErrorRecovery, '//_handle_error_with_recovery:', '//_handle_error_no_recovery:', '//_handle_error_end_of_section:'); - // and some post-coital touch-ups: - if (!hasErrorRecovery) { - // Also nuke the support declaration statement: - // var recovering = 0; - // and the recovery support statements: - // if (recovering > 0) { - // recovering--; - // } - // and these yydebug particles: - // , recovering: recovering - parseFn = parseFn - .replace(/^\s*var recovering.*$/gm, '') - .replace(/, recovering: recovering/g, '') - .replace(/[ \t]*if \(recovering[^\)]+\) \{[^\0]+?\}\n/g, '\n\n\n\n\n') - // And nuke the preErrorSymbol code as it is unused when there's no error recovery - // if (!preErrorSymbol) { - // ... keep this chunk ... - // } else { - // ... KILL this chunk ... - // } - .replace(/\s+if[^a-z]+preErrorSymbol.*?\{\s*\/\/[^\n]+([\s\S]+?)\} else \{[\s\S]+?\}\n\s+\}\n/g, '\n$1\n\n\n\n') - .replace(/^\s+(?:var )?preErrorSymbol = .*$/gm, '') - // And nuke the support declaration statement: - // var lastEofErrorStateDepth = 0; - .replace(/^\s*var lastEofErrorStateDepth.*$/gm, ''); - } - return parseFn; -} - -// Generates the code of the parser module, which consists of two parts: -// - module.commonCode: initialization code that should be placed before the module -// - module.moduleCode: code that creates the module object -lrGeneratorMixin.generateModule_ = function generateModule_() { - var parseFn = String(parser.parse); - parseFn = pickErrorHandlingChunk(parseFn, this.hasErrorRecovery); - - parseFn = addOrRemoveTokenStack(parseFn, this.options.tokenStack); - - parseFn = removeUnusedKernelFeatures(parseFn, this); - - parseFn = expandParseArguments(parseFn, this); - - var errorClassCode = this.generateErrorClass(); - - // set up the 'option' `exportAllTables` as a hash object for returning - // all generated tables to the caller - var exportDest = this.options.exportAllTables; - if (!exportDest || typeof exportDest !== 'object') { - exportDest = { - enabled: !!exportDest - }; - } else { - exportDest.enabled = true; - } - - // store the parse tables: - exportDest.parseTable = this.table; - exportDest.defaultParseActions = this.defaultActions; - exportDest.parseProductions = this.productions_; - - var tableCode; - switch (this.options.compressTables | 0) { - case 0: // no compression - tableCode = this.generateTableCode0(this.table, this.defaultActions, this.productions_); - break; - - default: - case 1: // default: vanilla JISON table compression = run-length encoding - tableCode = this.generateTableCode1(this.table, this.defaultActions, this.productions_); - break; - - case 2: // column-mode compression - // this compression method corrupts the table when this option is turned on (and one or more conflicts occur) - if (this.options.noDefaultResolve && this.conflicts > 0) { - tableCode = this.generateTableCode1(this.table, this.defaultActions, this.productions_); - } else { - tableCode = this.generateTableCode2(this.table, this.defaultActions, this.productions_); - } - break; - } - - // Generate the initialization code - var initCode = this.moduleInit.slice(0); - - function popInitCodeSection(section) { - var rv = []; - for (var i = 0, len = initCode.length; i < len; i++) { - var m = initCode[i]; - if (!m) continue; - if (m.qualifier === section || !section) { - rv.push(m.include); - delete initCode[i]; - } - } - return rv; - } - - var commonCode = [].concat( - popInitCodeSection('required'), - errorClassCode.commonCode, - errorClassCode.moduleCode, - popInitCodeSection(), - tableCode.commonCode - ); - - - - // sort hash table by key to produce a nicer output: - function produceSymbolTable(tbl) { - var a = Object.keys(tbl); - a.sort(); - var nt = {}; - var k; - for (var i = 0, len = a.length; i < len; i++) { - k = a[i]; - // `$eof` and `EOF` are synonyms of `$end` (`$eof` is for bison compatibility); - // this is the only place where two symbol names may map to a single symbol ID number - // and we do not want `$eof`/`EOF` to show up in the symbol tables of generated parsers - // as we use `$end` for that one! - if (k !== '$eof') { - nt[k] = tbl[k]; - } - } - return nt; - } - - // swap key and value and then sort hash table by key to produce a nicer output: - function produceTerminalTable(tbl) { - var a = Object.keys(tbl); - var nt = {}; - var k, v; - for (var i = 0, len = a.length; i < len; i++) { - k = a[i]; - v = tbl[k]; - nt[v] = +k; // convert numeric key back to number type; all terminals have numeric keys - } - return produceSymbolTable(nt); - } - - function produceProductionsForDebugging(options, symbols, base) { - function get_orig_symbol(s) { - var a = s.split(':'); - if (a.length === 1 || a[0] === '') { - return { - state: -1, - symbol: s - }; - } - var state = a[0]; - a.shift(); - return { - state: +state, - symbol: a.join(':'), - }; - } - function get_orig_symbol_set(arr) { - var rv = {}; - for (var i = 0, len = arr.length; i < len; i++) { - var item = arr[i]; - var symbol = get_orig_symbol(item); - rv[symbol.symbol] = symbol.state; - } - return Object.keys(rv); - } - - var tbl = this.nonterminals; - var sym = this.symbols_ || symbols; - - if (!options.outputDebugTables && !options.exportAllTables) { - return undefined; - } - - var prods = { - ids: {}, - states: {}, - rules: {}, - nonterminals: {}, - symbols: {}, - first: {}, - follows: {}, - }; - - var self = this; - this.productions.forEach(function Follow_prod_forEach_genDebugTable(production, k) { - // self.trace('Symbol/Follows: ', 'prod:' + k, ':', production.symbol, ' :: ', production.handle.join(' '), ' --> ', nonterminals[production.symbol].follows.join(', ')); - var nonterm = production.symbol; - prods.states[k] = nonterm; - prods.ids[nonterm] = sym[nonterm]; - - var lst = prods.rules[nonterm] || {}; - lst[k] = gen_lalr_states_production(production, k, false, k, true); - prods.rules[nonterm] = lst; - }); - - function gen_nonterminal(nt) { - var l = nt.productions._items; - var lst = l.map(function (p, i) { - return gen_lalr_states_production(p, i, false, false, false); - }); - var rv = { - symbol: nt.symbol, - productions: lst, - first: nt.first, - base_first: get_orig_symbol_set(nt.first), - follows: nt.follows, - base_follows: get_orig_symbol_set(nt.follows), - nullable: nt.nullable, - }; - - // clean up structure: ditch superfluous elements: - if (rv.base_first.join(' ') === rv.first.join(' ')) { - delete rv.base_first; - } - if (rv.base_follows.join(' ') === rv.follows.join(' ')) { - delete rv.base_follows; - } - - return rv; - } - - for (var key in tbl) { - prods.nonterminals[key] = gen_nonterminal(tbl[key]); - } - - if (this.nterms_) { - prods.nterms_ = this.nterms_; - } - - function gen_lalr_states_production(production, index, dotPosition, state, patch_base) { - var nonterm = production.symbol; - var hlen = production.handle.length; - var rulestr = production.handle.map(function (t, idx) { - if (!t) { - t = '%epsilon'; - } - // `$eof` and `EOF` are synonyms of `$end` ('$eof' is for bison compatibility); - // this is the only place where two symbol names may map to a single symbol ID number - // and we do not want `$eof`/`EOF` to show up in the symbol tables of generated parsers - // as we use `$end` for that one! - if (t === '$eof') { - t = '$end'; - } - - if (dotPosition === idx) { - t = '⬤' + t; - } - return t; - }).join(' '); - if (dotPosition === hlen) { - rulestr += ' ⬤'; - } - - var base_rulestr = production.handle.map(function (t) { - if (!t) { - t = '%epsilon'; - } - t = get_orig_symbol(t).symbol; - // `$eof` and `EOF` are synonyms of `$end` ('$eof' is for bison compatibility); - // this is the only place where two symbol names may map to a single symbol ID number - // and we do not want `$eof`/`EOF` to show up in the symbol tables of generated parsers - // as we use `$end` for that one! - if (t === '$eof') { - t = '$end'; - } - return t; - }).join(' '); - - var rv = { - symbol: nonterm, - base_symbol: get_orig_symbol(nonterm).symbol, - handle: rulestr, - base_handle: base_rulestr, - nullable: production.nullable, - id: production.id, - index: index, - state: (state !== false ? state : -1), - base_state: -1, - first: production.first, - base_first: get_orig_symbol_set(production.first), - follows: production.follows, - base_follows: get_orig_symbol_set(production.follows), - precedence: production.precedence, - reachable: production.reachable - }; - - // Determine state for given production, if it's not a production that's listed as part of a state: - var chk, idx; - var lst = prods.rules[nonterm]; - chk = rv.symbol + ' : ' + rv.handle; - for (idx in lst) { - idx = +idx; - var p = lst[idx]; - if (p) { - if (p.symbol + ' : ' + p.handle === chk) { - assert(rv.state === -1); - rv.state = idx; - break; - } - } - } - - // Try to reference base productions from newg child productions and vice versa: - chk = rv.base_symbol + ' : ' + rv.base_handle; - if (base && base.rules) { - var pr = base.rules[rv.base_symbol]; - for (idx in pr) { - var bprod = pr[idx]; - if (bprod.symbol + ' : ' + bprod.handle === chk) { - assert(rv.base_state === -1); - rv.base_state = bprod.state; - if (patch_base) { - bprod.newg_states = (bprod.newg_states || []); - bprod.newg_states.push(rv.index); - } - break; - } - } - } - - // clean up structure: ditch superfluous elements: - if (rv.base_symbol === rv.symbol) { - delete rv.base_symbol; - } - if (rv.base_handle === rv.handle) { - delete rv.base_handle; - } - if (rv.base_first.join(' ') === rv.first.join(' ')) { - delete rv.base_first; - } - if (rv.base_follows.join(' ') === rv.follows.join(' ')) { - delete rv.base_follows; - } - if (rv.base_state === -1) { - delete rv.base_state; - } - return rv; - } - - if (this.states) { - prods.lalr_states = []; - this.states.forEach(function traverse_states(state, i) { - //assert(state.inadequate ? this.inadequate : true); - state.forEach(function traverse_state(item, j) { - // is this a REDUCE state? - var nterm_first = self.nonterminals[item.production.symbol].first; - var rv = { - state: i, - item_index: j, - is_reduce_state: (item.dotPosition === item.production.handle.length), - dot_position: item.dotPosition, - state_inadequate: state.inadequate ? true : undefined, - item_inadequate: item.inadequate ? true : undefined, - production: gen_lalr_states_production(item.production, j, item.dotPosition, i, true), - follows: item.follows, - base_follows: get_orig_symbol_set(item.follows), - nterm_first: nterm_first, - base_nterm_first: get_orig_symbol_set(nterm_first), - prod_first: item.production.first, - base_prod_first: get_orig_symbol_set(item.production.first), - }; - - // clean up structure: ditch superfluous elements: - if (rv.base_follows.join(' ') === rv.follows.join(' ')) { - delete rv.base_follows; - } - if (rv.base_nterm_first.join(' ') === rv.nterm_first.join(' ')) { - delete rv.base_nterm_first; - } - if (rv.base_prod_first.join(' ') === rv.prod_first.join(' ')) { - delete rv.base_prod_first; - } - - prods.lalr_states.push(rv); - }); - }); - } - - var nt = tbl; - var sbn; - for (sbn in nt) { - var orig_symbol = get_orig_symbol(sbn); - var item = nt[sbn]; - var firsts = item.first; - var follows = item.follows; - if (!prods.symbols[orig_symbol.symbol]) { - prods.symbols[orig_symbol.symbol] = orig_symbol.state; - } - if (!prods.first[orig_symbol.symbol]) { - prods.first[orig_symbol.symbol] = firsts; - } else { - prods.first[orig_symbol.symbol] = prods.first[orig_symbol.symbol].concat(firsts); - } - if (!prods.follows[orig_symbol.symbol]) { - prods.follows[orig_symbol.symbol] = follows; - } else { - prods.follows[orig_symbol.symbol] = prods.follows[orig_symbol.symbol].concat(follows); - } - } - for (sbn in prods.first) { - prods.first[sbn] = get_orig_symbol_set(prods.first[sbn]); - } - for (sbn in prods.follows) { - prods.follows[sbn] = get_orig_symbol_set(prods.follows[sbn]); - } - - if (this.newg) { - prods.newg = produceProductionsForDebugging.call(this.newg, options, sym, prods); - } - return prods; - } - - function produceTerminalDescriptions(tbl, sym) { - var rv = {}; - var count = 0; - for (var k in tbl) { - var descr = tbl[k]; - var id = sym[k]; - if (id && descr && descr !== id) { - rv[id] = descr; - count++; - } - } - return (count ? rv : undefined); - } - - function produceOptions(opts) { - var obj = {}; - var do_not_pass = { - type: 0, // CLI: --parserType option - debug: !opts.debug, // do not include this item when it is FALSE as there's no debug tracing built into the generated grammar anyway! - enableDebugLogs: 1, - numExpectedConflictStates: 1, - dumpSourceCodeOnFailure: 1, - throwErrorOnCompileFailure: 1, - json: 1, - _: 1, - noMain: 1, - noDefaultResolve: 1, - noDefaultAction: 1, - noTryCatch: 1, - hasPartialLrUpgradeOnConflict: 0, - compressTables: 1, - outputDebugTables: 1, - reportStats: 1, - file: 1, - outfile: 1, - inputPath: 1, - inputFilename: 1, - lexfile: 1, - defaultModuleName: 1, - moduleName: 1, - moduleType: 1, - exportAllTables: 1, - tokenStack: 1, - parserErrorsAreRecoverable: 0, - lexerErrorsAreRecoverable: 1, - showSource: 1, - - errorRecoveryTokenDiscardCount: 0, - - warn_cb: 0, // function(msg) | true (= use Jison.Print) | false (= throw Exception) - - parseParams: 1, - ranges: 0, - }; - for (var k in opts) { - if (!do_not_pass[k] && opts[k] != null && opts[k] !== false) { - // make sure numeric values are encoded as numeric, the rest as boolean/string. - if (typeof opts[k] === 'string') { - var f = parseFloat(opts[k]); - if (f == opts[k]) { - obj[k] = f; - continue; - } - } - obj[k] = opts[k]; - } - } - - // And now some options which should receive some special processing: - if (!obj.hasPartialLrUpgradeOnConflict) { - // only list this option when it's actually TRUE: - delete obj.hasPartialLrUpgradeOnConflict; - } - - var pre = obj.pre_parse; - var post = obj.post_parse; - // since JSON cannot encode functions, we'll have to do it manually at run-time, i.e. later on: - obj.pre_parse = (pre ? true : undefined); - obj.post_parse = (post ? true : undefined); - - var js = JSON.stringify(obj, null, 2); - - js = js.replace(new XRegExp(' "([\\p{Alphabetic}_][\\p{Alphabetic}_\\p{Number}]*)": ', 'g'), ' $1: '); - js = js.replace(/^( +)pre_parse: true,$/gm, '$1pre_parse: ' + String(pre) + ','); - js = js.replace(/^( +)post_parse: true,$/gm, '$1post_parse: ' + String(post) + ','); - return js; - } - - - // Generate the module creation code - var termDescrs = produceTerminalDescriptions(this.descriptions_, this.symbols_); - exportDest.terminalDescriptions = termDescrs; - var descrLst = JSON.stringify(termDescrs, null, 2); - if (descrLst) { - descrLst = descrLst.replace(/"([0-9]+)":/g, '$1:'); - } - - var rules4Dbg = produceProductionsForDebugging.call(this, this.options); - exportDest.parseRules = rules4Dbg; - var rulesLst = (this.options.outputDebugTables ? JSON.stringify(rules4Dbg, null, 2) : undefined); - if (rulesLst) { - rulesLst = rulesLst.replace(/"([0-9]+)":/g, '$1:').replace(/^(\s+)"([a-z_][a-z_0-9]*)":/gmi, '$1$2:'); - } - - var symbolTable = produceSymbolTable(this.symbols_); - exportDest.symbolTable = symbolTable; - - // produce a hash lookup table from the terminal set - exportDest.terminalTable = produceTerminalTable(this.terminals_); - - if (this.options.exportAllTables) { - this.options.exportAllTables = exportDest; - } - - var moduleCode = `{ - // Code Generator Information Report - // --------------------------------- - // - // Options: - // - // no default action: ............... ${this.options.noDefaultAction} - // no try..catch: ................... ${this.options.noTryCatch} - // no default resolve on conflict: ${this.options.noDefaultResolve} - // on-demand look-ahead: ............ ${this.onDemandLookahead} - // error recovery token skip maximum: ${this.options.errorRecoveryTokenDiscardCount} - // yyerror in parse actions is: ..... ${this.options.parserErrorsAreRecoverable ? 'recoverable' : 'NOT recoverable'}, - // yyerror in lexer actions and other non-fatal lexer are: - // .................................. ${this.options.lexerErrorsAreRecoverable ? 'recoverable' : 'NOT recoverable'}, - // debug grammar/output: ............ ${this.options.debug} - // has partial LR conflict upgrade: ${this.options.hasPartialLrUpgradeOnConflict} - // rudimentary token-stack support: ${this.options.tokenStack} - // parser table compression mode: ... ${this.options.compressTables} - // export debug tables: ............. ${this.options.outputDebugTables} - // export *all* tables: ............. ${this.options.exportAllTables} - // module type: ..................... ${this.options.moduleType} - // parser engine type: .............. ${this.options.type} - // output main() in the module: ..... ${this.options.noMain} - // number of expected conflicts: .... ${this.options.numExpectedConflictStates} - // - // - // Parser Analysis flags: - // - // all actions are default: ......... ${this.actionsAreAllDefault} - // uses yyleng: ..................... ${this.actionsUseYYLENG} - // uses yylineno: ................... ${this.actionsUseYYLINENO} - // uses yytext: ..................... ${this.actionsUseYYTEXT} - // uses yylloc: ..................... ${this.actionsUseYYLOC} - // uses ParseError API: ............. ${this.actionsUseParseError} - // uses YYERROR: .................... ${this.actionsUseYYERROR} - // uses YYRECOVERING: ............... ${this.actionsUseYYRECOVERING} - // uses YYERROK: .................... ${this.actionsUseYYERROK} - // uses YYCLEARIN: .................. ${this.actionsUseYYCLEARIN} - // tracks rule values: .............. ${this.actionsUseValueTracking} - // assigns rule values: ............. ${this.actionsUseValueAssignment} - // uses location tracking: .......... ${this.actionsUseLocationTracking} - // assigns location: ................ ${this.actionsUseLocationAssignment} - // uses yystack: .................... ${this.actionsUseYYSTACK} - // uses yysstack: ................... ${this.actionsUseYYSSTACK} - // uses yysp: ....................... ${this.actionsUseYYSTACKPOINTER} - // has error recovery: .............. ${this.hasErrorRecovery} - // - // --------- END OF REPORT ----------- - -`; - moduleCode += [ - 'trace: ' + String(this.trace || parser.trace), - 'JisonParserError: JisonParserError', - 'yy: {}', - 'options: ' + produceOptions(this.options), - 'symbols_: ' + JSON.stringify(symbolTable, null, 2), - 'terminals_: ' + JSON.stringify(this.terminals_, null, 2).replace(/"([0-9]+)":/g, '$1:'), - ].concat( - rulesLst ? - 'nonterminals_: ' + rulesLst : - [] - ).concat( - descrLst ? - 'terminal_descriptions_: ' + descrLst : - [] - ).concat([ - String(define_parser_APIs_1) - .replace(/^[\s\S]+?return \{/, '') - .replace(/\};[s\r\n]+\}\s*$/, '') - .replace(/^ /mg, '') - .trim(), - 'productions_: ' + tableCode.productionsCode - ]).concat( - String(this.performAction).trim() !== '' ? - 'performAction: ' + String(this.performAction) : - [] - ).concat([ - 'table: ' + tableCode.tableCode, - 'defaultActions: ' + tableCode.defaultActionsCode, - 'parseError: ' + String(this.parseError || parser.parseError), - 'parse: ' + parseFn - ]).concat( - this.actionsUseYYERROR ? - 'yyError: 1' : - [] - ).concat( - this.actionsUseYYRECOVERING ? - 'yyRecovering: 1' : - [] - ).concat( - this.actionsUseYYERROK ? - 'yyErrOk: 1' : - [] - ).concat( - this.actionsUseYYCLEARIN ? - 'yyClearIn: 1' : - [] - ).join(',\n'); - moduleCode += '\n};'; - - return { - commonCode: commonCode.join('\n'), - moduleCode: moduleCode, - modulePostlude: [ - 'parser.originalParseError = parser.parseError;', - 'parser.originalQuoteName = parser.quoteName;', - ].join('\n') - }; -}; - -lrGeneratorMixin.generateErrorClass = function () { - // See also: - // http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508 - // but we keep the prototype.constructor and prototype.name assignment lines too for compatibility - // with userland code which might access the derived class in a 'classic' way. - function JisonParserError(msg, hash) { - Object.defineProperty(this, 'name', { - enumerable: false, - writable: false, - value: 'JisonParserError' - }); - - if (msg == null) msg = '???'; - - Object.defineProperty(this, 'message', { - enumerable: false, - writable: true, - value: msg - }); - - this.hash = hash; - - var stacktrace; - if (hash && hash.exception instanceof Error) { - var ex2 = hash.exception; - this.message = ex2.message || msg; - stacktrace = ex2.stack; - } - if (!stacktrace) { - if (Error.hasOwnProperty('captureStackTrace')) { // V8 - Error.captureStackTrace(this, this.constructor); - } else { - stacktrace = (new Error(msg)).stack; - } - } - if (stacktrace) { - Object.defineProperty(this, 'stack', { - enumerable: false, - writable: false, - value: stacktrace - }); - } - } - - // wrap this init code in a function so we can String(function)-dump it into the generated - // output: that way we only have to write this code *once*! - function __extra_code__() { - if (typeof Object.setPrototypeOf === 'function') { - Object.setPrototypeOf(JisonParserError.prototype, Error.prototype); - } else { - JisonParserError.prototype = Object.create(Error.prototype); - } - JisonParserError.prototype.constructor = JisonParserError; - JisonParserError.prototype.name = 'JisonParserError'; - } - __extra_code__(); - - var prelude = [ - '// See also:', - '// http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508', - '// but we keep the prototype.constructor and prototype.name assignment lines too for compatibility', - '// with userland code which might access the derived class in a \'classic\' way.', - printFunctionSourceCode(JisonParserError), - printFunctionSourceCodeContainer(__extra_code__), - '', - ]; - - return { - commonCode: '', - moduleCode: prelude.join('\n') - }; -}; - -// Generate code that represents the specified parser table -lrGeneratorMixin.generateTableCode0 = function (table, defaultActions, productions) { - var tableCode = JSON.stringify(table, null, 2); - var defaultActionsCode = JSON.stringify(defaultActions, null, 2).replace(/"([0-9]+)":/g, '$1:'); - var productionsCode = JSON.stringify(productions, null, 2); - - // Don't surround numerical property name numbers in quotes - tableCode = tableCode.replace(/"([0-9]+)"(?=:)/g, '$1'); - - var prelude = []; - - // Return the variable initialization code and the table code - return { - commonCode: prelude.join('\n'), - tableCode: tableCode, - defaultActionsCode: defaultActionsCode, - productionsCode: productionsCode - }; -}; - -// Generate code that represents the specified parser table -lrGeneratorMixin.generateTableCode1 = function (table, defaultActions, productions) { - var tableCode = JSON.stringify(table, null, 2); - var defaultActionsCode = JSON.stringify(defaultActions, null, 2).replace(/"([0-9]+)":/g, '$1:'); - var productionsCode = JSON.stringify(productions, null, 2); - var usesCompressor = false; - - // Don't surround numerical property name numbers in quotes - tableCode = tableCode.replace(/"([0-9]+)"(?=:)/g, '$1'); - - // Replace objects with several identical values by function calls - // e.g., { 1: [6, 7]; 3: [6, 7], 4: [6, 7], 5: 8 } = x([1, 3, 4], [6, 7], { 5: 8 }) - tableCode = tableCode.replace(/\{[\s\r\n]*\d+:[^\}]+,[\s\r\n]*\d+:[^\}]+\}/g, function (object) { - // Find the value that occurs with the highest number of keys - var value, frequentValue, key, - keys = {}, - keyCount, - maxKeyCount = 0, - keyValue, - keyValues = [], - keyValueMatcher = /(\d+):[\s\r\n]*([^:\}]+)(?=,[\s\r\n]*\d+:|\})/g; - - while ((keyValue = keyValueMatcher.exec(object))) { - // For each value, store the keys where that value occurs - key = keyValue[1]; - value = keyValue[2].trim(); - keyCount = 1; - - if (!(value in keys)) { - keys[value] = [key]; - } else { - keyCount = keys[value].push(key); - } - // Remember this value if it is the most frequent one - if (keyCount > maxKeyCount) { - maxKeyCount = keyCount; - frequentValue = value; - } - } - // Construct the object with a function call if the most frequent value occurs multiple times - if (maxKeyCount > 1) { - // Collect all non-frequent values into a remainder object - for (value in keys) { - if (value !== frequentValue) { - for (var k = keys[value], i = 0, l = k.length; i < l; i++) { - keyValues.push(k[i] + ':' + value); - } - } - } - keyValues = keyValues.length ? ', {' + keyValues.join(',') + '}' : ''; - // Create the function call `x(keys, value, remainder)` - object = 'x([' + keys[frequentValue].join(',') + '], ' + frequentValue + keyValues + ')'; - usesCompressor = true; - } - return object; - }); - - // Count occurrences of number lists - var list; - var lists = {}; - var listMatcher = /\[[0-9,]+\]/g; - var frequentLists = []; - - while ((list = listMatcher.exec(tableCode))) { - lists[list] = (lists[list] || 0) + 1; - } - - // Replace frequently occurring number lists with variables - tableCode = tableCode.replace(listMatcher, function (list) { - var listId = lists[list]; - // If listId is a number, it represents the list's occurrence frequency - if (typeof listId === 'number') { - // If the list does not occur frequently, represent it by the list - if (listId === 1) { - lists[list] = listId = list; - // If the list occurs frequently, represent it by a newly assigned variable - } else { - lists[list] = listId = 'u[' + frequentLists.length + ']'; - frequentLists.push(list); - } - } - return listId; - }); - - var prelude = []; - - // Only include the expender function when it's actually used - // (tiny grammars don't have much state duplication, so this shaves off - // another couple bytes off the generated output) - if (usesCompressor) { - prelude.push(createObjectCode.toString().replace('createObjectCode', 'x')); - prelude.push(''); - } - - if (frequentLists.length > 0) { - prelude.push('var u = [\n ' + frequentLists.join(',\n ') + '\n];'); - prelude.push(''); - } - - // Return the variable initialization code and the table code - return { - commonCode: prelude.join('\n'), - tableCode: tableCode, - defaultActionsCode: defaultActionsCode, - productionsCode: productionsCode - }; -}; - -// Function that extends an object with the given value for all given keys -// e.g., x([1, 3, 4], [6, 7], { x: 1, y: 2 }) = { 1: [6, 7]; 3: [6, 7], 4: [6, 7], x: 1, y: 2 } -function createObjectCode(k, v, o) { - o = o || {}; - for (var l = k.length; l--; ) { - o[k[l]] = v; - } - return o; -} - -// Generate code that represents the specified parser table -lrGeneratorMixin.generateTableCode2 = function (table, defaultActions, productions) { - assert(!(this.options.noDefaultResolve && this.conflicts > 0)); // this compression method corrupts the table when this option is turned on (and one or more conflicts occur) - - var tableCode = JSON.stringify(table, null, 2); - var defaultActionsCode = JSON.stringify(defaultActions, null, 2).replace(/"([0-9]+)":/g, '$1:'); - var productionsCode = JSON.stringify(productions, null, 2); - - // We know a couple of things about the parse table: - // - // - The first level is an array with continuous indexes - // - Each entry of the array is an object which contains a series of numeric states as a hash table - // - Each 'hash table' entry is either a state number or a 2-element array - // - // So we can start by encoding the table 'vertically', i.e. by column rather than by row, - // and then provide a bit of code to transform that series of arrays to the real parse table - // at run time. - // We can encode the columns by encoding the array-or-number aspect as a separate column, - // while encoding the size of each hash table in yet another column: number of entries per state. - // Then thanks to that length info, plus the 'is this hash-table entry going to be a number or an array' flag column, - // we can transform those back to what we need at run-time. - // - // Meanwhile, we can inspect each of the columns and see if we can compress them. - // - // Of course the flags array is compressible as it's only 1 bit per entry, but there's sure to - // be more compression goodies to be had in there, such as run-length encoding and maybe - // delta-encoding of the hashtable indexes themselves. - // - // - - // Don't surround numerical property name numbers in quotes - tableCode = tableCode.replace(/"([0-9]+)"(?=:)/g, '$1'); - - - - - function reportColumnsForCompression(def_arr) { - var i, key, len; - var report = []; - - len = 0; - for (key in def_arr) { - len = Math.max(len, def_arr[key].length); - } - - var col_width = 6; - var col_delta_width = 4; - - function clip(val, width) { - var s = ' ' + val; - s = s.substr(s.length - width); - return s; - } - - var track_prev4delta = {}; - var c, delta, val, delta_val; - var line = []; - line.push('║'); - for (c in def_arr) { - key = clip(c, col_width); - delta = clip('∆', col_delta_width); - line.push(key); - line.push('┊'); - line.push(delta); - line.push('║'); - - track_prev4delta[c] = 10000000; - } - report.push(line.join('')); - - for (i = 0; i < len; i++) { - line = []; - line.push('║'); - - for (c in def_arr) { - var tbl = def_arr[c]; - if (tbl.length > i) { - val = tbl[i] || 0; - - delta_val = val - track_prev4delta[c]; - // negative deltas are jumps: don't treat those as delta but as absolute value, sign-flipped: - if (delta_val < 0) { - delta_val = -val - 1; // so that absolute 0 becomes -1, so it can be recognized from delta=0 ('no change') - } - track_prev4delta[c] = val; - } else { - val = '.'; - delta_val = '.'; - delta_val2 = '.'; - } - - key = clip(val, col_width); - delta = clip(delta_val, col_delta_width); - line.push(key); - line.push('┊'); - line.push(delta); - line.push('║'); - } - report.push(line.join('')); - } - - return '\n\n\n// ------------------------------\n\n\n// ' + report.join('\n// ') + '\n\n\n// ------------------\n\n\n'; - } - - - // table is array of 1/2-len arrays: - function analyzeTableForCompression(table) { - // column: productions' row length - var len_col = []; - // column: productions' shift size / action column - var pop_col = []; - // column: rule number for each slot ('rule'): - var rule_col = []; - - var i; - var row_count = table.length; - for (i = 0; i < row_count; i++) { - var prod = table[i]; - - len_col.push(prod.length); - assert(prod.length <= 2); - assert(prod.length > 0); - // and the special knowledge about the productions[] table: - assert(prod.length === 2); - pop_col.push(prod[0]); - rule_col.push(prod[1]); - } - - var def_arr = { - 'len': len_col, - 'pop': pop_col, - 'rule': rule_col, - }; - return def_arr; - } - - - - - // table is hash of 1/2-len arrays: - function analyzeSetForCompression(table) { - // column: row index - var idx_col = []; - // column: productions' row length - var len_col = []; - // column: REDUCE productions' goto column - var goto_col = []; - - var i; - var row_count = 0; - for (i in table) { - i = +i; - var prod = table[i]; - row_count++; - idx_col.push(i); - - // and the special knowledge about the defaultActions[] table: - assert(typeof prod === 'number'); - goto_col.push(prod); - } - - var def_arr = { - 'idx': idx_col, - 'goto': goto_col, - }; - return def_arr; - } - - - - function analyzeGotoTableForCompression(table) { - // column: number of symbol hash entries per state slot ('length'): - var len_col = []; - // column: symbol hash entry key for each slot ('symbol'): - var symbol_col = []; - // column: symbol hash entry value type: number (0) or array (array.length) ('type'): - var type_col = []; - // column: symbol hash entry value if single GOTO state number ('state'): - var state_col = []; - // column: symbol hash entry mode value if array slot type (reduce/shift/accept): - var mode_col = []; - // column: symbol hash entry goto state value if array slot type: - var goto_col = []; - // // column: merged: state_col + goto_col: - // var next_col = []; - - var row_count = table.length; - for (var state = 0; state < row_count; state++) { - var hashtable = table[state]; - var count = 0; - var symbol; - for (symbol in hashtable) { - symbol = +symbol; - symbol_col.push(symbol); - - var slot = hashtable[symbol]; - if (slot && slot.length) { - // array type slot: - assert(slot.length === 2 || slot.length === 1); - assert(slot.length === 1 ? slot[0] === 3 /* $accept */ : true); - type_col.push(slot.length); - if (slot.length > 1) { - mode_col.push(slot[0]); - goto_col.push(slot[1]); - //next_col.push(slot[1]); - } - } else if (slot) { - // number type slot: - type_col.push(0); - state_col.push(slot); - //next_col.push(slot); - } else { - assert(0); - type_col.push(666); - state_col.push((typeof slot) + state + '/' + symbol); - //next_col.push((typeof slot) + state + '/' + symbol); - } - count++; - } - len_col.push(count); - } - - var def_arr = { - 'len': len_col, - 'symbol': symbol_col, - 'type': type_col, - 'state': state_col, - 'mode': mode_col, - 'goto': goto_col, - //'next': next_col, - }; - return def_arr; - } - - - var has_compressed_a_table = false; - - - function generateColumn(name, col) { - var rv = []; - var i, j, len, l; - - for (i = 0, len = col.length; i < len; i++) { - // try basic run-length encoding first: - var v = col[i]; - - for (j = i + 1; j < len; j++) { - if (col[j] !== v) { - break; - } - } - var runlength = j - i; - - // try stepped run-length encoding next: - var delta = col[i + 1] - v; - var steplength = 0; - - // we don't want to replicate the runlength result, so only look for a match - // when delta !== 0: - if (delta !== 0) { - for (j = i + 2; j < len; j++) { - if (col[j] - col[j - 1] !== delta) { - break; - } - } - steplength = j - i; - } - - // try to match the pattern in history: - var best_pos = 0; - var best_len = 0; - var upper_bound = i - 2; - for (j = 0; j < upper_bound; j++) { - for (l = 0; col[j + l] === col[i + l]; l++) { - // No need to check for: - // if (j + l === i) break; - // because we know how the c() helper function will regenerate - // this pattern: it is perfectly fine to overlap on itself: we always - // have an offset of relative -1 or more, so we can encode runlength - // patterns as duplicates this way too: - // [4, c(0, 7)] (note the written offset is 0!) - // will output an sequence of 7+1 '4' values: one '4' and then 7 more. - // - // Encoding such a pattern as direct runlength `s(4, 8)` is cheaper - // though. Hence we loop until `i - 2`: we want to find ABABABAB... - // patterns, but no AAAAAA... patterns here. - } - - // We want the nearest offset for the longest pattern: - if (l >= best_len) { - best_len = l; - best_pos = i - j; - } - } - - // weight our options now: - var gain = [ - runlength - 2, - steplength - 3, - best_len - 2 - ]; - var optimum_gain = Math.max.apply(null, gain); - if (optimum_gain <= 0) { - rv.push(v); - } - else if (optimum_gain === gain[0]) { - rv.push('s', '[' + v + ', ' + runlength + ']'); - i += runlength - 1; - } - else if (optimum_gain === gain[1]) { - rv.push('s', '[' + v + ', ' + steplength + ', ' + delta + ']'); - i += steplength - 1; - } - else if (optimum_gain === gain[2]) { - rv.push('c', '[' + best_pos + ', ' + best_len + ']'); - i += best_len - 1; - } - else { - rv.push(v); - //assert(0); // should never get here! - } - - if (optimum_gain > 0) { - has_compressed_a_table = true; - } - } - - var code = [ - ' ', name, ': ', - 'u([', - '\n ', - rv.join(',\n '), // JSON.stringify(col, null, 2), - '\n', - '])' - ].join(''); - return code; - } - - - function generateCompressedTable(def_arr) { - var code = [ - 'bp({', - generateColumn('pop', def_arr.pop) + ',', - generateColumn('rule', def_arr.rule), - '})' - ].join('\n'); - return code; - } - - - function generateCompressedSet(def_arr) { - var code = [ - 'bda({', - generateColumn('idx', def_arr.idx) + ',', - generateColumn('goto', def_arr.goto), - '})' - ].join('\n'); - return code; - } - - - function generateCompressedGotoTable(def_arr) { - var code = [ - 'bt({', - generateColumn('len', def_arr.len) + ',', - generateColumn('symbol', def_arr.symbol) + ',', - generateColumn('type', def_arr.type) + ',', - generateColumn('state', def_arr.state) + ',', - generateColumn('mode', def_arr.mode) + ',', - generateColumn('goto', def_arr.goto), - '})' - ].join('\n'); - return code; - } - - - var tableDef = analyzeGotoTableForCompression(table); - var defaultActionsDef = analyzeSetForCompression(defaultActions); - var productionsDef = analyzeTableForCompression(productions); - - - function bp_code_container() { - // helper: reconstruct the productions[] table - function bp(s) { - var rv = []; - var p = s.pop; - var r = s.rule; - for (var i = 0, l = p.length; i < l; i++) { - rv.push([ - p[i], - r[i] - ]); - } - return rv; - } - } - - function bda_code_container() { - // helper: reconstruct the defaultActions[] table - function bda(s) { - var rv = {}; - var d = s.idx; - var g = s.goto; - for (var i = 0, l = d.length; i < l; i++) { - var j = d[i]; - rv[j] = g[i]; - } - return rv; - } - } - - function bt_code_container() { - // helper: reconstruct the 'goto' table - function bt(s) { - var rv = []; - var d = s.len; - var y = s.symbol; - var t = s.type; - var a = s.state; - var m = s.mode; - var g = s.goto; - for (var i = 0, l = d.length; i < l; i++) { - var n = d[i]; - var q = {}; - for (var j = 0; j < n; j++) { - var z = y.shift(); - switch (t.shift()) { - case 2: - q[z] = [ - m.shift(), - g.shift() - ]; - break; - - case 0: - q[z] = a.shift(); - break; - - default: - // type === 1: accept - q[z] = [ - 3 - ]; - } - } - rv.push(q); - } - return rv; - } - } - - function c_s_u_code_container() { - // helper: runlength encoding with increment step: code, length: step (default step = 0) - // `this` references an array - function s(c, l, a) { - a = a || 0; - for (var i = 0; i < l; i++) { - this.push(c); - c += a; - } - } - - // helper: duplicate sequence from *relative* offset and length. - // `this` references an array - function c(i, l) { - i = this.length - i; - for (l += i; i < l; i++) { - this.push(this[i]); - } - } - - // helper: unpack an array using helpers and data, all passed in an array argument 'a'. - function u(a) { - var rv = []; - for (var i = 0, l = a.length; i < l; i++) { - var e = a[i]; - // Is this entry a helper function? - if (typeof e === 'function') { - i++; - e.apply(rv, a[i]); - } else { - rv.push(e); - } - } - return rv; - } - } - - has_compressed_a_table = false; - var tc = generateCompressedGotoTable(tableDef); - var compressGotoTable = has_compressed_a_table; - - has_compressed_a_table = false; - var dac = generateCompressedSet(defaultActionsDef); - var compressDefaultActions = has_compressed_a_table; - - has_compressed_a_table = false; - var pc = generateCompressedTable(productionsDef); - var compressProductions = has_compressed_a_table; - - var compressAnything = (compressProductions || compressDefaultActions || compressGotoTable); - - tableCode = ((this.DEBUG || devDebug) ? reportColumnsForCompression(tableDef) : '') + (compressGotoTable ? tc : tableCode); - defaultActionsCode = ((this.DEBUG || devDebug) ? reportColumnsForCompression(defaultActionsDef) : '') + (compressDefaultActions ? dac : defaultActionsCode); - productionsCode = ((this.DEBUG || devDebug) ? reportColumnsForCompression(productionsDef) : '') + (compressProductions ? pc : productionsCode); - - - var prelude = [ - '', - compressProductions ? printFunctionSourceCodeContainer(bp_code_container) : '', - '', - compressDefaultActions ? printFunctionSourceCodeContainer(bda_code_container) : '', - '', - compressGotoTable ? printFunctionSourceCodeContainer(bt_code_container) : '', - '', - printFunctionSourceCodeContainer(c_s_u_code_container), - ]; - if (!compressAnything) { - prelude = []; - } - - // Return the variable initialization code and the table code - return { - commonCode: prelude.join('\n'), - tableCode: tableCode, - defaultActionsCode: defaultActionsCode, - productionsCode: productionsCode - }; -}; - -// default main method for generated commonjs modules -function commonjsMain(args) { - // When the parser comes with its own `main` function, then use that one: - if (typeof exports.parser.main === 'function') { - return exports.parser.main(args); - } - - var fs = require('fs'); - var path = require('path'); - - if (!args[1]) { - console.log('Usage: ' + path.basename(args[0]) + ' FILE'); - process.exit(1); - } - var source = fs.readFileSync(path.normalize(args[1]), 'utf8'); - var dst = exports.parser.parse(source); - console.log('parser output: ', { - type: typeof dst, - value: dst - }); - var rv = 0; - if (typeof dst === 'number' || typeof dst === 'boolean') { - rv = dst; - } - return dst; -} - -// debug mixin for LR parser generators - -function printAction(a, gen) { - var s = a[0] === SHIFT ? 'shift token (then go to state ' + a[1] + ')' : - a[0] === REDUCE ? 'reduce by rule: ' + gen.productions[a[1]] : - a[0] === ACCEPT ? 'accept' : 'UNDEFINED ACTION: ' + a[0]; - - return s; -} - -function traceStates(trace, states, title) { - trace('Item sets -- ' + title + '\n------'); - - states.forEach(function (state, i) { - trace('\nitem set', i, '\n' + state.join('\n'), '\ntransitions -> ', JSON.stringify(state.edges)); - }); - trace('\n'); -} - -var lrGeneratorDebug = { - beforeparseTable: function () { - this.trace('Building parse table.'); - }, - afterparseTable: function () { - var trace = this.trace; - var self = this; - if (this.conflicts > 0) { - trace('\nConflicts:\n'); - this.resolutions.forEach(function (r, i) { - if (r[2].bydefault) { - trace('Conflict at state: ', r[0], ', token: ', r[1], '\n ', printAction(r[2].r, self), '\n ', printAction(r[2].s, self)); - } - }); - trace('\n' + this.conflicts + ' Conflict(s) found in grammar.'); - } - trace('Done.\n'); - }, - aftercanonicalCollection: function (states /* as produced by `this.canonicalCollection()` */ ) { - traceStates(this.trace, states, 'as produced by LR::canonicalCollection()'); - } -}; - -var parser = typal.beget(); - -generatorMixin.createParser = function createParser() { - var sourcecode = this.generateModuleExpr(); - - var p = code_exec(sourcecode, function generated_code_exec_wrapper(sourcecode) { - //console.log("===============================PARSER TEST CODE\n", sourcecode, "\n=====================END====================\n"); - var rv = eval(sourcecode); - return rv; - }, this.options, "parser"); - - // for debugging - p.productions = this.productions; - p.unused_productions = this.unused_productions; - p.conflicts = this.conflicts; - if (p.conflicts && this.options.hasPartialLrUpgradeOnConflict) { - p.conflicts_have_been_fixed = this.conflict_fixing_round; - p.conflict_productions_LU = this.conflict_productions_LU; - p.conflict_states_LU = this.conflict_states_LU; - } - - var self = this; - function bind(method) { - return function () { - self.lexer = p.lexer; - return method.apply(self, arguments); - }; - } - - // backwards compatibility - p.lexer = this.lexer; - p.generate = bind(this.generate); - p.generateAMDModule = bind(this.generateAMDModule); - p.generateModule = bind(this.generateModule); - p.generateCommonJSModule = bind(this.generateCommonJSModule); - - this.reportGrammarInformation(); - - return p; -}; - -parser.trace = generator.trace; -parser.warn = generator.warn; -parser.error = generator.error; - -function parseError(str, hash, ExceptionClass) { - if (hash.recoverable && typeof this.trace === 'function') { - this.trace(str); - hash.destroy(); // destroy... well, *almost*! - } else { - if (!ExceptionClass) { - ExceptionClass = this.JisonParserError; - } - throw new ExceptionClass(str, hash); - } -} - -parser.parseError = lrGeneratorMixin.parseError = parseError; - -generatorMixin.createLexer = function createLexer(lexerSpec, input, tokens, options) { - // TODO: construct options from generator options: - // lexer_options = ... - var lexer = new Lexer(lexerSpec, input, tokens, options); - - return lexer; -}; - - -// wrapper function so we easily stringify the APIs defined inside to code *with comments* -// in the generated code: -function define_parser_APIs_1() { - return { - TERROR: 2, - EOF: 1, - - // internals: defined here so the object *structure* doesn't get modified by parse() et al, - // thus helping JIT compilers like Chrome V8. - originalQuoteName: null, - originalParseError: null, - cleanupAfterParse: null, - constructParseErrorInfo: null, - - __reentrant_call_depth: 0, // INTERNAL USE ONLY - __error_infos: [], // INTERNAL USE ONLY: the set of parseErrorInfo objects created since the last cleanup - - // APIs which will be set up depending on user action code analysis: - //yyRecovering: 0, - //yyErrOk: 0, - //yyClearIn: 0, - - // Helper APIs - // ----------- - - // Helper function which can be overridden by user code later on: put suitable quotes around - // literal IDs in a description string. - quoteName: function parser_quoteName(id_str) { - return '"' + id_str + '"'; - }, - - // Return a more-or-less human-readable description of the given symbol, when available, - // or the symbol itself, serving as its own 'description' for lack of something better to serve up. - // - // Return NULL when the symbol is unknown to the parser. - describeSymbol: function parser_describeSymbol(symbol) { - if (symbol !== this.EOF && this.terminal_descriptions_ && this.terminal_descriptions_[symbol]) { - return this.terminal_descriptions_[symbol]; - } - else if (symbol === this.EOF) { - return 'end of input'; - } - else if (this.terminals_[symbol]) { - return this.quoteName(this.terminals_[symbol]); - } - // Otherwise... this might refer to a RULE token i.e. a non-terminal: see if we can dig that one up. - // - // An example of this may be where a rule's action code contains a call like this: - // - // parser.describeSymbol(#$) - // - // to obtain a human-readable description or name of the current grammar rule. This comes handy in - // error handling action code blocks, for example. - var s = this.symbols_; - for (var key in s) { - if (s[key] === symbol) { - return key; - } - } - return null; - }, - - // Produce a (more or less) human-readable list of expected tokens at the point of failure. - // - // The produced list may contain token or token set descriptions instead of the tokens - // themselves to help turning this output into something that easier to read by humans - // unless `do_not_describe` parameter is set, in which case a list of the raw, *numeric*, - // expected terminals and nonterminals is produced. - // - // The returned list (array) will not contain any duplicate entries. - collect_expected_token_set: function parser_collect_expected_token_set(state, do_not_describe) { - var TERROR = this.TERROR; - var tokenset = []; - var check = {}; - // Has this (error?) state been outfitted with a custom expectations description text for human consumption? - // If so, use that one instead of the less palatable token set. - if (!do_not_describe && this.state_descriptions_ && this.state_descriptions_[state]) { - return [ - this.state_descriptions_[state] - ]; - } - for (var p in this.table[state]) { - p = +p; - if (p !== TERROR) { - var d = do_not_describe ? p : this.describeSymbol(p); - if (d && !check[d]) { - tokenset.push(d); - check[d] = true; // Mark this token description as already mentioned to prevent outputting duplicate entries. - } - } - } - return tokenset; - } - }; -} - -var api_set = define_parser_APIs_1(); -for (var api in api_set) { - parser[api] = api_set[api]; -} - - -parser.parse = function parse(input, parseParams) { - var self = this, - stack = new Array(128), // token stack: stores token which leads to state at the same index (column storage) - sstack = new Array(128), // state stack: stores states (column storage) - tstack = [], // token stack (only used when `%options token_stack` support has been enabled) - vstack = new Array(128), // semantic value stack - lstack = new Array(128), // location stack - table = this.table, - sp = 0; // 'stack pointer': index into the stacks - - var recovering = 0; // (only used when the grammar contains error recovery rules) - var TERROR = this.TERROR, - EOF = this.EOF, - ERROR_RECOVERY_TOKEN_DISCARD_COUNT = (this.options.errorRecoveryTokenDiscardCount | 0) || 3; - var NO_ACTION = [0, table.length /* ensures that anyone using this new state will fail dramatically! */]; - - //this.reductionCount = this.shiftCount = 0; - - var lexer; - if (this.__lexer__) { - lexer = this.__lexer__; - } else { - lexer = this.__lexer__ = Object.create(this.lexer); - } - - var sharedState_yy = { - parseError: null, - quoteName: null, - lexer: null, - parser: null, - pre_parse: null, - post_parse: null - }; - // copy state - for (var k in this.yy) { - if (Object.prototype.hasOwnProperty.call(this.yy, k)) { - sharedState_yy[k] = this.yy[k]; - } - } - - sharedState_yy.lexer = lexer; - sharedState_yy.parser = this; - - var yydebug = false; - if (this.options.debug) { - yydebug = function yydebug_impl(msg, obj) { - var ref_list; - var ref_names; - function deepClone(from, sub) { - if (sub == null) { - ref_list = []; - ref_names = []; - sub = 'root'; - } - if (typeof from === 'function') return '[Function]'; - if (from == null || typeof from !== 'object') return from; - if (from.constructor !== Object && from.constructor !== Array) { - return from; - } - - for (var i = 0, len = ref_list.length; i < len; i++) { - if (ref_list[i] === from) { - return '[Circular/Xref:' + ref_names[i] + ']'; // circular or cross reference - } - } - ref_list.push(from); - ref_names.push(sub); - - var to = new from.constructor(); - for (var name in from) { - if (name === 'parser') continue; - if (name === 'lexer') continue; - to[name] = deepClone(from[name], name); - } - return to; - } - - obj = obj || {}; - if (obj.symbol) { - obj['local yytext'] = yytext; - obj['lexer.yytext'] = lexer.yytext; - obj['lexer.yylloc'] = lexer.yylloc; - obj['lexer.yyllineno'] = lexer.yyllineno; - } - - // warning: here we fetch from closure (stack et al) - obj.symbol_stack = stack; - obj.state_stack = sstack; - obj.value_stack = vstack; - obj.location_stack = lstack; - obj.stack_pointer = sp; - - // ready the object for printing: - obj = deepClone(obj); - - // wrap try/catch in a function to help the V8 JIT compiler... - function yydebug_cvt(obj) { - var js; - try { - var re1; - if (typeof XRegExp === 'undefined') { - re1 = / \"([a-z_][a-z_0-9. ]*)\": /ig; - } else { - re1 = new XRegExp(' \"([\\p{Alphabetic}_][\\p{Alphabetic}\\p{Number}_. ]*)\": ', 'g'); - } - js = JSON.stringify(obj, null, 2).replace(re1, ' $1: ').replace(/[\n\s]+/g, ' '); - } catch (ex) { - js = String(obj); - } - return js; - } - - self.trace(msg, yydebug_cvt(obj), '\n'); - }; - } - - // *Always* setup `yyError`, `YYRECOVERING`, `yyErrOk` and `yyClearIn` functions as it is paramount - // to have *their* closure match ours -- if we only set them up once, - // any subsequent `parse()` runs will fail in very obscure ways when - // these functions are invoked in the user action code block(s) as - // their closure will still refer to the `parse()` instance which set - // them up. Hence we MUST set them up at the start of every `parse()` run! - if (this.yyError) { - this.yyError = function yyError(str /*, ...args */) { - if (yydebug) yydebug('yyerror: ', { message: str, args: arguments, symbol: symbol, state: state, newState: newState, recovering: recovering, action: action }); - -//_handle_error_with_recovery: // run this code when the grammar includes error recovery rules - - var error_rule_depth = (this.options.parserErrorsAreRecoverable ? locateNearestErrorRecoveryRule(state) : -1); - var expected = this.collect_expected_token_set(state); - var hash = this.constructParseErrorInfo(str, null, expected, (error_rule_depth >= 0)); - -//_handle_error_no_recovery: // run this code when the grammar does not include any error recovery rules - - var hash = this.constructParseErrorInfo(str, null, null, false); - -//_handle_error_end_of_section: // this concludes the error recovery / no error recovery code section choice above - - // Add any extra args to the hash under the name `extra_error_attributes`: - var args = Array.prototype.slice.call(arguments, 1); - if (args.length) { - hash.extra_error_attributes = args; - } - - var r = this.parseError(str, hash, this.JisonParserError); - return r; - }; - } - - if (this.yyRecovering) { - this.yyRecovering = function yyRecovering() { - if (yydebug) yydebug('yyrecovering: ', { symbol: symbol, state: state, newState: newState, recovering: recovering, action: action }); - return recovering; - }; - } - - if (this.yyErrOk) { - this.yyErrOk = function yyErrOk() { - if (yydebug) yydebug('yyerrok: ', { symbol: symbol, state: state, newState: newState, recovering: recovering, action: action }); - recovering = 0; - }; - } - - if (this.yyClearIn) { - this.yyClearIn = function yyClearIn() { - if (yydebug) yydebug('yyclearin: ', { symbol: symbol, newState: newState, recovering: recovering, action: action, preErrorSymbol: preErrorSymbol }); - if (symbol === TERROR) { - symbol = 0; - yytext = null; - yyleng = 0; - yyloc = {}; - } - preErrorSymbol = 0; - }; - } - - lexer.setInput(input, sharedState_yy); - - var yyloc = lexer.yylloc || {}; - lstack[sp] = yyloc; - vstack[sp] = null; - sstack[sp] = 0; - stack[sp] = 0; - ++sp; - - var yytext = lexer.yytext; - var yylineno = lexer.yylineno; - var yyleng = lexer.yyleng; - - var ranges = lexer.options && lexer.options.ranges; - - // Does the shared state override the default `parseError` that already comes with this instance? - if (typeof sharedState_yy.parseError === 'function') { - this.parseError = function parseErrorAlt(str, hash, ExceptionClass) { - if (!ExceptionClass) { - ExceptionClass = this.JisonParserError; - } - return sharedState_yy.parseError(str, hash, ExceptionClass); - }; - } else { - this.parseError = this.originalParseError; - } - - // Does the shared state override the default `quoteName` that already comes with this instance? - if (typeof sharedState_yy.quoteName === 'function') { - this.quoteName = sharedState_yy.quoteName; - } else { - this.quoteName = this.originalQuoteName; - } - - // set up the cleanup function; make it an API so that external code can re-use this one in case of - // calamities or when the `%options no-try-catch` option has been specified for the grammar, in which - // case this parse() API method doesn't come with a `finally { ... }` block any more! - // - // NOTE: as this API uses parse() as a closure, it MUST be set again on every parse() invocation, - // or else your `sharedState`, etc. references will be *wrong*! - this.cleanupAfterParse = function parser_cleanupAfterParse(resultValue, invoke_post_methods, do_not_nuke_errorinfos) { - var rv; - - if (invoke_post_methods) { - if (sharedState_yy.post_parse) { - rv = sharedState_yy.post_parse.call(this, sharedState_yy, resultValue, parseParams); - if (typeof rv !== 'undefined') resultValue = rv; - } - if (this.post_parse) { - rv = this.post_parse.call(this, sharedState_yy, resultValue, parseParams); - if (typeof rv !== 'undefined') resultValue = rv; - } - } - - if (this.__reentrant_call_depth > 1) return resultValue; // do not (yet) kill the sharedState when this is a reentrant run. - - // clean up the lingering lexer structures as well: - if (lexer.cleanupAfterLex) { - lexer.cleanupAfterLex(do_not_nuke_errorinfos); - } - - // prevent lingering circular references from causing memory leaks: - if (sharedState_yy) { - sharedState_yy.parseError = undefined; - sharedState_yy.quoteName = undefined; - sharedState_yy.lexer = undefined; - sharedState_yy.parser = undefined; - if (lexer.yy === sharedState_yy) { - lexer.yy = undefined; - } - } - sharedState_yy = undefined; - this.parseError = this.originalParseError; - this.quoteName = this.originalQuoteName; - - // nuke the vstack[] array at least as that one will still reference obsoleted user values. - // To be safe, we nuke the other internal stack columns as well... - stack.length = 0; // fastest way to nuke an array without overly bothering the GC - sstack.length = 0; - lstack.length = 0; - vstack.length = 0; - sp = 0; - - // nuke the error hash info instances created during this run. - // Userland code must COPY any data/references - // in the error hash instance(s) it is more permanently interested in. - if (!do_not_nuke_errorinfos) { - for (var i = this.__error_infos.length - 1; i >= 0; i--) { - var el = this.__error_infos[i]; - if (el && typeof el.destroy === 'function') { - el.destroy(); - } - } - this.__error_infos.length = 0; - } - - return resultValue; - }; - - // NOTE: as this API uses parse() as a closure, it MUST be set again on every parse() invocation, - // or else your `lexer`, `sharedState`, etc. references will be *wrong*! - this.constructParseErrorInfo = function parser_constructParseErrorInfo(msg, ex, expected, recoverable) { - var pei = { - errStr: msg, - exception: ex, - text: lexer.match, - value: lexer.yytext, - token: this.describeSymbol(symbol) || symbol, - token_id: symbol, - line: lexer.yylineno, - loc: lexer.yylloc || {}, - expected: expected, - recoverable: recoverable, - state: state, - action: action, - new_state: newState, - symbol_stack: stack, - state_stack: sstack, - value_stack: vstack, - location_stack: lstack, - stack_pointer: sp, - yy: sharedState_yy, - lexer: lexer, - parser: this, - - // and make sure the error info doesn't stay due to potential - // ref cycle via userland code manipulations. - // These would otherwise all be memory leak opportunities! - // - // Note that only array and object references are nuked as those - // constitute the set of elements which can produce a cyclic ref. - // The rest of the members is kept intact as they are harmless. - destroy: function destructParseErrorInfo() { - // remove cyclic references added to error info: - // info.yy = null; - // info.lexer = null; - // info.value = null; - // info.value_stack = null; - // ... - var rec = !!this.recoverable; - for (var key in this) { - if (this.hasOwnProperty(key) && typeof key === 'object') { - this[key] = undefined; - } - } - this.recoverable = rec; - } - }; - // track this instance so we can `destroy()` it once we deem it superfluous and ready for garbage collection! - this.__error_infos.push(pei); - return pei; - }; - -//_lexer_without_token_stack: - - function lex(parseParams) { - var token = lexer.lex(parseParams); - // if token isn't its numeric value, convert - if (typeof token !== 'number') { - token = self.symbols_[token] || token; - } - return token || EOF; - } - -//_lexer_with_token_stack: - - // lex function that supports token stacks - function tokenStackLex(parseParams) { - var token; - token = tstack.pop() || lexer.lex(parseParams) || EOF; - // if token isn't its numeric value, convert - if (typeof token !== 'number') { - if (token instanceof Array) { - tstack = token; - token = tstack.pop(); - } - // if token isn't its numeric value, convert - if (typeof token !== 'number') { - token = self.symbols_[token] || token; - } - } - return token || EOF; - } - -//_lexer_with_token_stack_end: - - var symbol = 0; - var preErrorSymbol = 0; - var lastEofErrorStateDepth = 0; - var state, action, r, t; - var yyval = { - $: true, - _$: undefined, - yy: sharedState_yy - }; - var p, len, this_production; - var lstack_begin, lstack_end; - var newState; - var retval = false; - -//_handle_error_with_recovery: // run this code when the grammar includes error recovery rules - - // Return the rule stack depth where the nearest error rule can be found. - // Return -1 when no error recovery rule was found. - function locateNearestErrorRecoveryRule(state) { - var stack_probe = sp - 1; - var depth = 0; - - // try to recover from error - for (;;) { - // check for error recovery rule in this state - if (yydebug) yydebug('locateNearestErrorRecoveryRule #test#: ', { symbol: symbol, state: state, depth: depth, stackidx: sp - 1 - depth, lastidx: lastEofErrorStateDepth }); - var t = table[state][TERROR] || NO_ACTION; - if (t[0]) { - // We need to make sure we're not cycling forever: - // once we hit EOF, even when we `yyerrok()` an error, we must - // prevent the core from running forever, - // e.g. when parent rules are still expecting certain input to - // follow after this, for example when you handle an error inside a set - // of braces which are matched by a parent rule in your grammar. - // - // Hence we require that every error handling/recovery attempt - // *after we've hit EOF* has a diminishing state stack: this means - // we will ultimately have unwound the state stack entirely and thus - // terminate the parse in a controlled fashion even when we have - // very complex error/recovery code interplay in the core + user - // action code blocks: - if (yydebug) yydebug('locateNearestErrorRecoveryRule #found#: ', { symbol: symbol, state: state, depth: depth, stackidx: sp - 1 - depth, lastidx: lastEofErrorStateDepth }); - if (symbol === EOF) { - if (!lastEofErrorStateDepth) { - lastEofErrorStateDepth = sp - 1 - depth; - } else if (lastEofErrorStateDepth <= sp - 1 - depth) { - if (yydebug) yydebug('locateNearestErrorRecoveryRule #skip#: ', { symbol: symbol, state: state, depth: depth, stackidx: sp - 1 - depth, lastidx: lastEofErrorStateDepth }); - --stack_probe; // popStack(1): [symbol, action] - state = sstack[stack_probe]; - ++depth; - continue; - } - } - return depth; - } - if (state === 0 /* $accept rule */ || stack_probe < 1) { - if (yydebug) yydebug('locateNearestErrorRecoveryRule #end=NIL#: ', { symbol: symbol, state: state, depth: depth, stackidx: sp - 1 - depth, lastidx: lastEofErrorStateDepth }); - return -1; // No suitable error recovery rule available. - } - --stack_probe; // popStack(1): [symbol, action] - state = sstack[stack_probe]; - ++depth; - } - } - -//_handle_error_no_recovery: // run this code when the grammar does not include any error recovery rules -//_handle_error_end_of_section: // this concludes the error recovery / no error recovery code section choice above - - try { - this.__reentrant_call_depth++; - - if (this.pre_parse) { - this.pre_parse.call(this, sharedState_yy, parseParams); - } - if (sharedState_yy.pre_parse) { - sharedState_yy.pre_parse.call(this, sharedState_yy, parseParams); - } - - newState = sstack[sp - 1]; - for (;;) { - // retrieve state number from top of stack - state = newState; // sstack[sp - 1]; - - // use default actions if available - if (this.defaultActions[state]) { - action = 2; - newState = this.defaultActions[state]; - } else { - // The single `==` condition below covers both these `===` comparisons in a single - // operation: - // - // if (symbol === null || typeof symbol === 'undefined') ... - if (!symbol) { - symbol = lex(parseParams); - } - // read action for current state and first input - t = (table[state] && table[state][symbol]) || NO_ACTION; - newState = t[1]; - action = t[0]; - - if (yydebug) yydebug('after FETCH/LEX: ', { symbol: symbol, newState: newState, recovering: recovering, action: action }); - -//_handle_error_with_recovery: // run this code when the grammar includes error recovery rules - - // handle parse error - if (!action) { - // first see if there's any chance at hitting an error recovery rule: - var error_rule_depth = locateNearestErrorRecoveryRule(state); - var errStr = null; - var errSymbolDescr = (this.describeSymbol(symbol) || symbol); - var expected = this.collect_expected_token_set(state); - - if (!recovering) { - // Report error - if (typeof lexer.yylineno === 'number') { - errStr = 'Parse error on line ' + (lexer.yylineno + 1) + ': '; - } else { - errStr = 'Parse error: '; - } - if (lexer.showPosition) { - errStr += '\n' + lexer.showPosition(79 - 10, 10) + '\n'; - } - if (expected.length) { - errStr += 'Expecting ' + expected.join(', ') + ', got unexpected ' + errSymbolDescr; - } else { - errStr += 'Unexpected ' + errSymbolDescr; - } - p = this.constructParseErrorInfo(errStr, null, expected, (error_rule_depth >= 0)); - r = this.parseError(p.errStr, p, this.JisonParserError); - - if (yydebug) yydebug('error detected: ', { error_rule_depth: error_rule_depth }); - if (!p.recoverable) { - retval = r; - break; - } else { - // TODO: allow parseError callback to edit symbol and or state at the start of the error recovery process... - } - } - - if (yydebug) yydebug('after ERROR DETECT: ', { error_rule_depth: error_rule_depth }); - - // just recovered from another error - if (recovering === ERROR_RECOVERY_TOKEN_DISCARD_COUNT && error_rule_depth >= 0) { - // only barf a fatal hairball when we're out of look-ahead symbols and none hit a match; - // this DOES discard look-ahead while recovering from an error when said look-ahead doesn't - // suit the error recovery rules... The error HAS been reported already so we're fine with - // throwing away a few items if that is what it takes to match the nearest recovery rule! - if (symbol === EOF || preErrorSymbol === EOF) { - p = this.__error_infos[this.__error_infos.length - 1]; - if (!p) { - p = this.constructParseErrorInfo('Parsing halted while starting to recover from another error.', null, expected, false); - } else { - p.errStr = 'Parsing halted while starting to recover from another error. Previous error which resulted in this fatal result: ' + p.errStr; - p.recoverable = false; - } - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - - // discard current lookahead and grab another - yyleng = lexer.yyleng; - yytext = lexer.yytext; - yylineno = lexer.yylineno; - yyloc = lexer.yylloc || {}; - - symbol = lex(parseParams); - - if (yydebug) yydebug('after ERROR RECOVERY-3: ', { symbol: symbol }); - } - - // try to recover from error - if (error_rule_depth < 0) { - p = this.constructParseErrorInfo((errStr || 'Parsing halted. No suitable error recovery rule available.'), null, expected, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - sp -= error_rule_depth; - - preErrorSymbol = (symbol === TERROR ? 0 : symbol); // save the lookahead token - symbol = TERROR; // insert generic error symbol as new lookahead - // allow N (default: 3) real symbols to be shifted before reporting a new error - recovering = ERROR_RECOVERY_TOKEN_DISCARD_COUNT; - - newState = sstack[sp - 1]; - - if (yydebug) yydebug('after ERROR POP: ', { error_rule_depth: error_rule_depth, symbol: symbol }); - - continue; - } - -//_handle_error_no_recovery: // run this code when the grammar does not include any error recovery rules - - // handle parse error - if (!action) { - var errStr; - var errSymbolDescr = (this.describeSymbol(symbol) || symbol); - var expected = this.collect_expected_token_set(state); - - // Report error - if (typeof lexer.yylineno === 'number') { - errStr = 'Parse error on line ' + (lexer.yylineno + 1) + ': '; - } else { - errStr = 'Parse error: '; - } - if (lexer.showPosition) { - errStr += '\n' + lexer.showPosition(79 - 10, 10) + '\n'; - } - if (expected.length) { - errStr += 'Expecting ' + expected.join(', ') + ', got unexpected ' + errSymbolDescr; - } else { - errStr += 'Unexpected ' + errSymbolDescr; - } - // we cannot recover from the error! - p = this.constructParseErrorInfo(errStr, null, expected, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - -//_handle_error_end_of_section: // this concludes the error recovery / no error recovery code section choice above - - } - - if (yydebug) yydebug('::: action: ' + (action === 1 ? 'shift token ' + symbol + ' (then go to state ' + newState + ')' : action === 2 ? 'reduce by rule: ' + newState + (function __print_rule(nt, state) { - if (!nt || !nt.states || !nt.rules) - return ''; - var rulename = nt.states[state]; - var rulespec = nt.rules[rulename][state]; - return ' (' + rulespec.symbol + ' := ' + rulespec.handle + ')'; - })(this.nonterminals_, newState) : action === 3 ? 'accept' : '???unexpected???'), { action: action, newState: newState, recovering: recovering, symbol: symbol }); - switch (action) { - // catch misc. parse failures: - default: - // this shouldn't happen, unless resolve defaults are off - if (action instanceof Array) { - p = this.constructParseErrorInfo(('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol), null, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - // Another case of better safe than sorry: in case state transitions come out of another error recovery process - // or a buggy LUT (LookUp Table): - p = this.constructParseErrorInfo('Parsing halted. No viable error recovery approach available due to internal system failure.', null, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - - // shift: - case 1: - //this.shiftCount++; - stack[sp] = symbol; - vstack[sp] = lexer.yytext; - lstack[sp] = lexer.yylloc || {}; - sstack[sp] = newState; // push state - ++sp; - symbol = 0; - if (!preErrorSymbol) { // normal execution / no error - // Pick up the lexer details for the current symbol as that one is not 'look-ahead' any more: - yyleng = lexer.yyleng; - yytext = lexer.yytext; - yylineno = lexer.yylineno; - yyloc = lexer.yylloc || {}; - - if (recovering > 0) { - recovering--; - if (yydebug) yydebug('... SHIFT:error rule matching: ', { recovering: recovering, symbol: symbol }); - } - } else { - // error just occurred, resume old lookahead f/ before error, *unless* that drops us straight back into error mode: - symbol = preErrorSymbol; - preErrorSymbol = 0; - if (yydebug) yydebug('... SHIFT:error recovery: ', { recovering: recovering, symbol: symbol }); - // read action for current state and first input - t = (table[newState] && table[newState][symbol]) || NO_ACTION; - if (!t[0] || symbol === TERROR) { - // forget about that symbol and move forward: this wasn't a 'forgot to insert' error type where - // (simple) stuff might have been missing before the token which caused the error we're - // recovering from now... - // - // Also check if the LookAhead symbol isn't the ERROR token we set as part of the error - // recovery, for then this we would we idling (cycling) on the error forever. - // Yes, this does not take into account the possibility that the *lexer* may have - // produced a *new* TERROR token all by itself, but that would be a very peculiar grammar! - if (yydebug) yydebug('... SHIFT:error recovery: re-application of old symbol doesn\'t work: instead, we\'re moving forward now. ', { recovering: recovering, symbol: symbol }); - symbol = 0; - } - } - - continue; - - // reduce: - case 2: - //this.reductionCount++; - this_production = this.productions_[newState - 1]; // `this.productions_[]` is zero-based indexed while states start from 1 upwards... - len = this_production[1]; - lstack_end = sp; - lstack_begin = lstack_end - (len || 1); - lstack_end--; - - if (yydebug) yydebug('~~~ REDUCE: ', { pop_size: len, newState: newState, recovering: recovering, symbol: symbol }); - - // Make sure subsequent `$$ = $1` default action doesn't fail - // for rules where len==0 as then there's no $1 (you're reducing an epsilon rule then!) - // - // Also do this to prevent nasty action block codes to *read* `$0` or `$$` - // and *not* get `undefined` as a result for their efforts! - vstack[sp] = undefined; - - // perform semantic action - yyval.$ = vstack[sp - len]; // default to $$ = $1; result must produce `undefined` when len == 0, as then there's no $1 - - // default location, uses first token for firsts, last for lasts - yyval._$ = { - first_line: lstack[lstack_begin].first_line, - last_line: lstack[lstack_end].last_line, - first_column: lstack[lstack_begin].first_column, - last_column: lstack[lstack_end].last_column - }; - if (ranges) { - yyval._$.range = [lstack[lstack_begin].range[0], lstack[lstack_end].range[1]]; - } - - r = this.performAction.call(yyval, yytext, yyleng, yylineno, yyloc, newState, sp - 1, vstack, lstack, stack, sstack, parseParams); - - if (typeof r !== 'undefined') { - retval = r; - break; - } - - // pop off stack - sp -= len; - - // don't overwrite the `symbol` variable: use a local var to speed things up: - var ntsymbol = this_production[0]; // push nonterminal (reduce) - stack[sp] = ntsymbol; - vstack[sp] = yyval.$; - lstack[sp] = yyval._$; - // goto new state = table[STATE][NONTERMINAL] - newState = table[sstack[sp - 1]][ntsymbol]; - sstack[sp] = newState; - ++sp; - if (yydebug) yydebug('REDUCED: ', { newState: newState, recovering: recovering, symbol: symbol }); - continue; - - // accept: - case 3: - retval = true; - // Return the `$accept` rule's `$$` result, if available. - // - // Also note that JISON always adds this top-most `$accept` rule (with implicit, - // default, action): - // - // $accept: $end - // %{ $$ = $1; @$ = @1; %} - // - // which, combined with the parse kernel's `$accept` state behaviour coded below, - // will produce the `$$` value output of the rule as the parse result, - // IFF that result is *not* `undefined`. (See also the parser kernel code.) - // - // In code: - // - // %{ - // @$ = @1; // if location tracking support is included - // if (typeof $1 !== 'undefined') - // return $1; - // else - // return true; // the default parse result if the rule actions don't produce anything - // %} - if (typeof yyval.$ !== 'undefined') { - retval = yyval.$; - } - break; - } - - // break out of loop: we accept or fail with error - break; - } - } catch (ex) { - // report exceptions through the parseError callback too, but keep the exception intact - // if it is a known parser or lexer error which has been thrown by parseError() already: - if (ex instanceof this.JisonParserError) { - throw ex; - } - else if (lexer && typeof lexer.JisonLexerError === 'function' && ex instanceof lexer.JisonLexerError) { - throw ex; - } - else { - p = this.constructParseErrorInfo('Parsing aborted due to exception.', ex, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - } - } finally { - retval = this.cleanupAfterParse(retval, true, true); - this.__reentrant_call_depth--; - } - - return retval; -}; - - -/* - * LR(0) Parser - */ - -var lr0 = generator.beget(lookaheadMixin, generatorMixin, lrGeneratorMixin, { - type: 'LR(0)', - afterconstructor: function lr0_afterconstructor() { - this.buildTable(); - } -}); - -var LR0Generator = exports.LR0Generator = lr0.construct(); - -/* - * Simple LALR(1) - */ - -var lalr = generator.beget(lookaheadMixin, generatorMixin, lrGeneratorMixin, { - type: 'LALR(1)', - - afterconstructor: function lalr_afterconstructor() { - var self = this; - - if (this.DEBUG) { - this.mix(lrGeneratorDebug, lalrGeneratorDebug); // mixin debug methods - } - - for (var round = 1; /* infinite loop if it weren't for the `break`s at the end */ ; round++) { - this.states = this.canonicalCollection(); - - if (this.DEBUG || devDebug) { - Jison.print('\n-------------------------------------------\nSymbol/Follow sets AFTER canonicalCollection:'); - this.displayFollowSets(); - Jison.print('\n'); - } - - this.terms_ = {}; - - var newg = this.newg = typal.beget(lookaheadMixin, { - oldg: this, - trace: this.trace, - nterms_: {}, - DEBUG: false, - go_: function (productionSymbol, productionHandle) { - var stateNum = productionSymbol.split(':')[0]; // grab state # - assert(stateNum == +stateNum); - stateNum = +stateNum; - productionHandle = productionHandle.map(function (rhsElem) { - return rhsElem.slice(rhsElem.indexOf(':') + 1); - }); - return this.oldg.go(stateNum, productionHandle, productionSymbol); - } - }); - newg.nonterminals = {}; - newg.productions = []; - - //this.inadequateStates = []; - - // if true, only lookaheads in inadequate states are computed (faster, larger table) - // if false, lookaheads for all reductions will be computed (slower, smaller table) - // - // WARNING: using this has a negative effect on your error reports: - // a lot of 'expected' symbols are reported which are not in the real FOLLOW set, - // resulting in 'illogical' error messages! - this.onDemandLookahead = !!this.options.onDemandLookahead; - if (devDebug || this.DEBUG) Jison.print('LALR: using on-demand look-ahead: ', (this.onDemandLookahead ? 'yes' : 'no')); - - this.buildNewGrammar(); - - if (devDebug || this.DEBUG) { - Jison.print('\n-------------------------------------------\nSymbol/Follow sets AFTER buildNewGrammar: NEW GRAMMAR'); - newg.displayFollowSets(); - Jison.print('\n-------------------------------------------\nSymbol/Follow sets AFTER buildNewGrammar: ORIGINAL GRAMMAR'); - this.displayFollowSets(); - } - - newg.computeLookaheads(); - - // backprop `nullable` value for each nonterminal and production back to original grammar: - each(newg.nonterminals, function (newg_nt, t) { - // extract original symbol: - var sym; - var a = newg_nt.symbol.split(':'); - if (a.length === 1 || a[0] === '') { - sym = newg_nt.symbol; - } else { - a.shift(); - sym = a.join(':'); - } - if (self.nonterminals[sym] && newg_nt.nullable) { - self.nonterminals[sym].nullable = true; - } else { - //console.error('cannot find symbol ', sym); - } - }); - - if (devDebug || this.DEBUG) { - Jison.print('\n-------------------------------------------\nSymbol/Follow sets AFTER computeLookaheads: NEW GRAMMAR'); - newg.displayFollowSets(); - Jison.print('\n-------------------------------------------\nSymbol/Follow sets AFTER computeLookaheads: ORIGINAL GRAMMAR'); - this.displayFollowSets(); - } - - this.unionLookaheads(); - - if (devDebug || this.DEBUG) { - Jison.print('\n-------------------------------------------\nSymbol/Follow sets AFTER unionLookaheads: NEW GRAMMAR'); - newg.displayFollowSets(); - Jison.print('\n-------------------------------------------\nSymbol/Follow sets AFTER unionLookaheads: ORIGINAL GRAMMAR'); - this.displayFollowSets(); - } - - this.table = this.parseTable(this.states); - - if (devDebug || this.DEBUG) { - Jison.print('\n-------------------------------------------\nSymbol/Follow sets AFTER parseTable: NEW GRAMMAR'); - newg.displayFollowSets(); - Jison.print('\n-------------------------------------------\nSymbol/Follow sets AFTER parseTable: ORIGINAL GRAMMAR'); - this.displayFollowSets(); - } - - // When some productions are flagged as conflicting, we redo the G' generation and consequent union-ing of the productions - // in the `.goes[]` arrays. - // - // Also quit when we're at the end of the conflict resolution round (which is round #2) - if (this.conflicts === 0 || this.conflict_fixing_round || !this.options.hasPartialLrUpgradeOnConflict) { - break; - } - - if (devDebug > 4) { - Jison.print('\n-------------------------------------------\nNew round to fix conflicts? Completed round:', { - round: round, - conflict_fixing_round: this.conflict_fixing_round, - states: this.conflict_states_LU, - productions: this.conflict_productions_LU - }); - } else { - Jison.print('\n' - + '----------------------------------- NOTICE -------------------------------\n' - + 'Attempting to resolve the unresolved conflicts in partial LR mode...\n\n' - + 'When no conflicts are reported in the next round below, your grammar is\n' - + 'accepted as mixed LR/LALR and should work as expected.\n' - + '--------------------------------------------------------------------------\n\n'); - } - - this.conflict_fixing_round = true; - - // and reset the conflict trackers, which we do not use to attempt to fix the conflict in round #2: - this.conflicts = 0; - this.new_conflicts_found_this_round = 0; - this.conflicting_states = []; - this.resolutions = []; - } - - this.defaultActions = findDefaults(this.table, this.hasErrorRecovery); - cleanupTable(this.table); - - traceStates(this.trace, this.states, 'at the end of the LALR constructor, after cleanupTable() and findDefaults()'); - }, - - lookAheads: function LALR_lookaheads(state, item) { - return (this.onDemandLookahead && !state.inadequate) ? this.terminals : item.follows; - }, - - go: function LALR_go(stateNum, productionHandle, productionSymbol) { - assert(typeof stateNum === 'number'); - var endStateNum = stateNum; - for (var i = 0; i < productionHandle.length; i++) { - endStateNum = this.states.item(endStateNum).edges[productionHandle[i]] || endStateNum; - } - if (devDebug > 0) { - Jison.print('GO: ', { - stateNum: stateNum, - symbol: productionSymbol, - endState: endStateNum - }); - } - return endStateNum; - }, - - goPath: function LALR_goPath(stateNum, productionHandle, productionSymbol) { - assert(typeof stateNum === 'number'); - var endStateNum = stateNum, - t, - path = []; - for (var i = 0; i < productionHandle.length; i++) { - t = productionHandle[i] ? endStateNum + ':' + productionHandle[i] /* + ':' + productionSymbol */ : ''; - if (t) { - this.newg.nterms_[t] = endStateNum; - } - path.push(t); - endStateNum = this.states.item(endStateNum).edges[productionHandle[i]] || endStateNum; - assert(t ? this.terms_[t] === undefined || this.terms_[t] === productionHandle[i] : true); - this.terms_[t] = productionHandle[i]; - } - if (devDebug > 0) { - Jison.print('GOPATH: ', { - stateNum: stateNum, - symbol: productionSymbol, - path: path, - endState: endStateNum - }); - } - return { - path: path, - endState: endStateNum - }; - }, - - // every disjoint reduction of a nonterminal becomes a production in G' - buildNewGrammar: function LALR_buildNewGrammar() { - var self = this, - newg = this.newg; - - this.states.forEach(function (state, i) { - i = +i; - state.forEach(function LALR_buildNewHandle(item) { - if (item.dotPosition === 0) { - // new symbols are a combination of state and transition symbol - var symbol = i + ':' + item.production.symbol; - assert(self.terms_[symbol] === undefined || self.terms_[symbol] === item.production.symbol); - self.terms_[symbol] = item.production.symbol; - newg.nterms_[symbol] = i; - if (!newg.nonterminals[symbol]) { - newg.nonterminals[symbol] = new Nonterminal(symbol); - } - var pathInfo = self.goPath(i, item.production.handle, item.production.symbol); - var p = new Production(symbol, pathInfo.path, newg.productions.length); - newg.productions.push(p); - newg.nonterminals[symbol].productions.push(p); - - // store the transition that gets 'backed up to' after reduction on path - var handle = item.production.handle.join(' '); - if (self.conflict_fixing_round && self.conflict_states_LU[i]) { - // handle += ':C' + i; - } - if (self.conflict_fixing_round && self.conflict_productions_LU[item.production.id]) { - handle += ':P' + item.production.id; - } - - var goes = self.states.item(pathInfo.endState).goes; - if (!goes[handle]) { - goes[handle] = []; - } - goes[handle].push(symbol); - - if (devDebug > 2) Jison.print('new production:', { - prod_id: item.production.id, - new_prod_id: p.id, - state: state, - stateNum: i, - production: p, - item_production: item.production, - goes: goes, - handle: handle, - symbol: symbol, - pathInfo: pathInfo - }); - } - }); - // if (state.inadequate) { - // self.inadequateStates.push(i); - // } - }); - }, - - unionLookaheads: function LALR_unionLookaheads() { - var self = this, - newg = this.newg; - // var states = !!this.onDemandLookahead ? this.inadequateStates : this.states; - - this.states.forEach(function union_states_forEach(state, i) { - i = +i; - //assert(state.inadequate ? this.inadequate : true); - var treat_me = (self.onDemandLookahead ? this.inadequate || state.inadequate : true); - if (state.reductions.length && treat_me) { - state.reductions.forEach(function union_reduction_forEach(item) { - var follows = {}; - for (var k = 0; k < item.follows.length; k++) { - follows[item.follows[k]] = true; - } - var handle = item.production.handle.join(' '); - if (self.conflict_fixing_round && self.conflict_states_LU[i]) { - // handle += ':C' + i; - } - if (self.conflict_fixing_round && self.conflict_productions_LU[item.production.id]) { - handle += ':P' + item.production.id; - } - if (!state.goes[handle]) { - state.goes[handle] = []; - } - - if (devDebug > 2) Jison.print('not-yet-unioned item', { - handle: handle, - item: item, - follows: follows, - goes: state.goes, - state: state, - stateNum: i - }); - - state.goes[handle].forEach(function reduction_goes_forEach(symbol) { - newg.nonterminals[symbol].follows.forEach(function goes_follows_forEach(symbol) { - var terminal = self.terms_[symbol]; - if (!follows[terminal]) { - follows[terminal] = true; - - if (devDebug > 2) Jison.print('adding to FOLLOW set (union)', { - terminal: terminal, - nonterminal: symbol, - in_follows: newg.nonterminals[symbol], - out_follows: item.follows - }); - - item.follows.push(terminal); - } - }); - }); - - if (devDebug > 2) Jison.print('unioned item', item); - }); - } - }); - } -}); - -var LALRGenerator = exports.LALRGenerator = lalr.construct(); - -// LALR generator debug mixin - -var lalrGeneratorDebug = { - beforebuildNewGrammar: function () { - this.trace(this.states.size() + ' states.'); - this.trace('Building lookahead grammar.'); - }, - beforeunionLookaheads: function () { - this.trace('Computing lookaheads.'); - }, - afterbuildNewGrammar: function () { - traceStates(this.trace, this.states, 'after LALR::buildNewGrammar()'); - }, - afterunionLookaheads: function () { - traceStates(this.trace, this.states, 'after LALR::unionLookaheads()'); - }, - aftercomputeLookaheads: function () { - traceStates(this.trace, this.states, 'after LALR::computeLookaheads()'); - }, - aftercanonicalCollection: function (states /* as produced by `this.canonicalCollection()` */ ) { - traceStates(this.trace, states, 'as produced by LALR::canonicalCollection()'); - } -}; - -/* - * Lookahead parser definitions - * - * Define base type - */ -var lrLookaheadGenerator = generator.beget(lookaheadMixin, generatorMixin, lrGeneratorMixin, { - afterconstructor: function lr_aftercontructor() { - this.computeLookaheads(); - - if (devDebug || this.DEBUG) { - Jison.print('\n-------------------------------------------\nSymbol/Follow sets AFTER computeLookaheads:'); - this.displayFollowSets(); - Jison.print('\n'); - } - - this.buildTable(); - } -}); - -/* - * SLR Parser - */ -var SLRGenerator = exports.SLRGenerator = lrLookaheadGenerator.construct({ - type: 'SLR(1)', - - lookAheads: function SLR_lookAhead(state, item) { - return this.nonterminals[item.production.symbol].follows; - } -}); - - -/* - * LR(1) Parser - */ -var lr1 = lrLookaheadGenerator.beget({ - type: 'Canonical LR(1)', - - lookAheads: function LR_lookAheads(state, item) { - return item.follows; - }, - - Item: lrGeneratorMixin.Item.prototype.construct({ - afterconstructor: function () { - this.id = this.production.id + '#' + this.dotPosition + '#' + this.follows.sort().join(','); - }, - eq: function (e) { - return e.id === this.id; - } - }), - - closureOperation: function LR_ClosureOperation(itemSet) { - var closureSet = new this.ItemSet(); - var self = this; - - var set = itemSet, - itemQueue, - syms = {}; - - do { - itemQueue = new Set(); - closureSet = closureSet.concat(set); - set.forEach(function LR_AddItemToClosureSets(item) { - var symbol = item.markedSymbol; - var b, r; - - // if token is a nonterminal, recursively add closures - if (symbol && self.nonterminals[symbol]) { - r = item.remainingHandle(); - b = self.first(r); - if (b.length === 0 || item.production.nullable || self.nullable(r)) { - b = b.concat(item.follows); - } - self.nonterminals[symbol].productions.forEach(function (production) { - var newItem = new self.Item(production, 0, b); - if (!closureSet.contains(newItem) && !itemQueue.contains(newItem)) { - itemQueue.push(newItem); - } - }); - } else if (!symbol) { - // reduction - closureSet.reductions.push(item); - } - }); - - set = itemQueue; - } while (!itemQueue.isEmpty()); - - return closureSet; - } -}); - -var LR1Generator = exports.LR1Generator = lr1.construct(); - -/* - * LL Parser - */ -var ll = generator.beget(lookaheadMixin, generatorMixin, lrGeneratorMixin, { - type: 'LL(1)', - - afterconstructor: function ll_aftercontructor() { - this.computeLookaheads(); - - if (devDebug || this.DEBUG) { - Jison.print('\n-------------------------------------------\nSymbol/Follow sets AFTER computeLookaheads:'); - this.displayFollowSets(); - } - - this.table = this.parseTable(this.productions); - - if (devDebug || this.DEBUG) { - Jison.print('\n-------------------------------------------\nSymbol/Follow sets AFTER parseTable:'); - this.displayFollowSets(); - } - - this.defaultActions = {}; // findDefaults(this.table, this.hasErrorRecovery); - //cleanupTable(this.table); - }, - - parseTable: function ll_ParseTable(productions) { - var table = {}, - symbols_ = this.symbols_, - self = this; - - productions.forEach(function (production, i) { - var row = table[production.symbol] || {}; - var tokens = production.first; - if (self.nullable(production.handle)) { - tokens = union(tokens, self.nonterminals[production.symbol].follows); - } - tokens.forEach(function (token) { - if (row[token]) { - row[token].push(i); - self.conflicts++; - } else { - row[token] = [i]; - } - }); - table[production.symbol] = row; - production.first = tokens; - }); - - return table; - } -}); - -var LLGenerator = exports.LLGenerator = ll.construct(); - -Jison.Generator = function Jison_Generator(grammar, optionalLexerSection, options) { - // pick the correct argument for the `options` for this call: - if (!options && optionalLexerSection && typeof optionalLexerSection !== 'string') { - options = optionalLexerSection; - optionalLexerSection = null; - } - // and standardize it: - options = mkStdOptions(options); - - // Provisionally parse the grammar, really only to obtain the *options.type* - // specified within the grammar, if specified (via `%parser-type`). - // - // Meanwhile, we *auto-detect* if the input is in JSON or JISON format - // and parse the specs, so we don't have to, nor should we have to, do - // *that* activity again in the specific generators below: they all - // share a common grammar+lexer spec format (JSON/JSON5/JISON) which will - // be parsed by `autodetectAndConvertToJSONformat()` right now! - grammar = autodetectAndConvertToJSONformat(grammar, optionalLexerSection, options); - - // make sure all options are 'standardized' before we go and mix them together: - options = mkStdOptions(grammar.options, options); - switch (options.type || '') { - case 'lr0': - options.hasPartialLrUpgradeOnConflict = false; // kill this unsupported option - return new LR0Generator(grammar, null, options); - case 'slr': - options.hasPartialLrUpgradeOnConflict = false; // kill this unsupported option - return new SLRGenerator(grammar, null, options); - case 'lr': - case 'lr1': - options.hasPartialLrUpgradeOnConflict = false; // kill this unsupported option - return new LR1Generator(grammar, null, options); - case 'll': - case 'll1': - options.hasPartialLrUpgradeOnConflict = false; // kill this unsupported option - return new LLGenerator(grammar, null, options); - case 'lalr1': - case 'lalr': - case '': - return new LALRGenerator(grammar, null, options); - default: - throw new Error('Unsupported parser type: ' + options.type); - } -}; - -return function Parser(g, l, options) { - var gen = Jison.Generator(g, l, options); - return gen.createParser(); -}; - -})(); - -}).call(this,require('_process')) -},{"../package.json":23,"./util/ebnf-parser.js":3,"./util/lex-parser.js":5,"./util/regexp-lexer.js":8,"./util/safe-code-exec-and-diag.js":10,"./util/set":11,"./util/typal":13,"_process":18,"assert":14,"fs":15,"json5":16,"path":17,"xregexp":22}],3:[function(require,module,exports){ -var bnf = require("./parser").parser, - ebnf = require("./ebnf-transform"), - jisonlex = require("./lex-parser"); - -exports.parse = function parse(grammar) { - return bnf.parse(grammar); -}; - -exports.transform = ebnf.transform; - -// adds a declaration to the grammar -bnf.yy.addDeclaration = function bnfAddDeclaration(grammar, decl) { - if (decl.start) { - grammar.start = decl.start; - } else if (decl.lex) { - grammar.lex = parseLex(decl.lex.text, decl.lex.position); - } else if (decl.operator) { - if (!grammar.operators) grammar.operators = []; - grammar.operators.push(decl.operator); - } else if (decl.token) { - if (!grammar.extra_tokens) grammar.extra_tokens = []; - grammar.extra_tokens.push(decl.token); - } else if (decl.token_list) { - if (!grammar.extra_tokens) grammar.extra_tokens = []; - decl.token_list.forEach(function (tok) { - grammar.extra_tokens.push(tok); - }); - } else if (decl.parseParams) { - if (!grammar.parseParams) grammar.parseParams = []; - grammar.parseParams = grammar.parseParams.concat(decl.parseParams); - } else if (decl.parserType) { - if (!grammar.options) grammar.options = {}; - grammar.options.type = decl.parserType; - } else if (decl.include) { - if (!grammar.moduleInclude) grammar.moduleInclude = ''; - grammar.moduleInclude += decl.include; - } else if (decl.options) { - if (!grammar.options) grammar.options = {}; - // last occurrence of `%options` wins: - for (var i = 0; i < decl.options.length; i++) { - grammar.options[decl.options[i][0]] = decl.options[i][1]; - } - } else if (decl.unknownDecl) { - if (!grammar.unknownDecls) grammar.unknownDecls = []; - grammar.unknownDecls.push(decl.unknownDecl); - } else if (decl.imports) { - if (!grammar.imports) grammar.imports = []; - grammar.imports.push(decl.imports); - } else if (decl.actionInclude) { - if (!grammar.actionInclude) { - grammar.actionInclude = ''; - } - grammar.actionInclude += decl.actionInclude; - } else if (decl.initCode) { - if (!grammar.moduleInit) { - grammar.moduleInit = []; - } - grammar.moduleInit.push(decl.initCode); // {qualifier: , include: } - } -}; - -// parse an embedded lex section -var parseLex = function bnfParseLex(text, position) { - text = text.replace(/(?:^%lex)|(?:\/lex$)/g, ''); - // We want the lex input to start at the given 'position', if any, - // so that error reports will produce a line number and character index - // which matches the original input file: - position = position || {}; - position.range = position.range || []; - var l = position.first_line | 0; - var c = position.range[0] | 0; - var prelude = ''; - if (l > 1) { - prelude += (new Array(l)).join('\n'); - c -= prelude.length; - } - if (c > 3) { - prelude = '// ' + (new Array(c - 3)).join('.') + prelude; - } - return jisonlex.parse(prelude + text); -}; - -},{"./ebnf-transform":4,"./lex-parser":5,"./parser":7}],4:[function(require,module,exports){ -var EBNF = (function () { - var parser = require('./transform-parser.js'); - var XRegExp = require('xregexp'); - //var assert = require('assert'); - - var devDebug = 0; - - function generatePushAction(handle, offset) { - var terms = handle.terms; - var rv = []; - - for (var i = 0, len = terms.length; i < len; i++) { - rv.push('$' + (i + offset)); - } - rv = rv.join(', '); - // and make sure we contain a term series unambiguously, i.e. anything more complex than - // a single term inside an EBNF check is produced as an array so we can differentiate - // between */+/? EBNF operator results and groups of tokens per individual match. - if (len > 1) { - rv = '[' + rv + ']'; - } - return rv; - } - - function transformExpression(e, opts, emit) { - var type = e[0], - value = e[1], - name = false, - has_transformed = 0; - var list, n; - - if (type === 'xalias') { - type = e[1]; - value = e[2]; - name = e[3]; - if (type) { - e = e.slice(1); - } else { - e = value; - type = e[0]; - value = e[1]; - } - if (devDebug > 3) console.log('xalias: ', e, type, value, name); - } - - if (type === 'symbol') { - // if (e[1][0] === '\\') { - // n = e[1][1]; - // } - // else if (e[1][0] === '\'') { - // n = e[1].substring(1, e[1].length - 1); - // } - // else if (e[1][0] === '"') { - // n = e[1].substring(1, e[1].length - 1); - // } - // else { - n = e[1]; - // } - if (devDebug > 2) console.log('symbol EMIT: ', n + (name ? '[' + name + ']' : '')); - emit(n + (name ? '[' + name + ']' : '')); - } else if (type === '+') { - if (!name) { - name = opts.production + '_repetition_plus' + opts.repid++; - } - if (devDebug > 2) console.log('+ EMIT name: ', name); - emit(name); - - has_transformed = 1; - - opts = optsForProduction(name, opts.grammar); - list = transformExpressionList([value], opts); - opts.grammar[name] = [ - [ - list.fragment, - '$$ = [' + generatePushAction(list, 1) + '];' - ], - [ - name + ' ' + list.fragment, - '$1.push(' + generatePushAction(list, 2) + ');\n$$ = $1;' - ] - ]; - } else if (type === '*') { - if (!name) { - name = opts.production + '_repetition' + opts.repid++; - } - if (devDebug > 2) console.log('* EMIT name: ', name); - emit(name); - - has_transformed = 1; - - opts = optsForProduction(name, opts.grammar); - list = transformExpressionList([value], opts); - opts.grammar[name] = [ - [ - '', - '$$ = [];' - ], - [ - name + ' ' + list.fragment, - '$1.push(' + generatePushAction(list, 2) + ');\n$$ = $1;' - ] - ]; - } else if (type === '?') { - if (!name) { - name = opts.production + '_option' + opts.optid++; - } - if (devDebug > 2) console.log('? EMIT name: ', name); - emit(name); - - has_transformed = 1; - - opts = optsForProduction(name, opts.grammar); - list = transformExpressionList([value], opts); - // you want to be able to check if 0 or 1 occurrences were recognized: since jison - // by default *copies* the lexer token value, i.e. `$$ = $1` is the (optional) default action, - // we will need to set the action up explicitly in case of the 0-count match: - // `$$ = undefined`. - // - // Note that we MUST return an array as the - // '1 occurrence' match CAN carry multiple terms, e.g. in constructs like - // `(T1 T2 T3)?`. - opts.grammar[name] = [ - [ - '', - '$$ = undefined;' - ], - [ - list.fragment, - '$$ = ' + generatePushAction(list, 1) + ';' - ] - ]; - } else if (type === '()') { - if (value.length === 1 && !name) { - list = transformExpressionList(value[0], opts); - if (list.first_transformed_term_index) { - has_transformed = list.first_transformed_term_index; - } - if (devDebug > 2) console.log('group EMIT len=1: ', list); - emit(list); - } else { - if (!name) { - name = opts.production + '_group' + opts.groupid++; - } - if (devDebug > 2) console.log('group EMIT name: ', name); - emit(name); - - has_transformed = 1; - - opts = optsForProduction(name, opts.grammar); - opts.grammar[name] = value.map(function (handle) { - var list = transformExpressionList(handle, opts); - return [ - list.fragment, - '$$ = ' + generatePushAction(list, 1) + ';' - ]; - }); - } - } - - return has_transformed; - } - - function transformExpressionList(list, opts) { - var first_transformed_term_index = false; - var terms = list.reduce(function (tot, e) { - var ci = tot.length; - - var has_transformed = transformExpression(e, opts, function (name) { - if (name.terms) { - tot.push.apply(tot, name.terms); - } else { - tot.push(name); - } - }); - - if (has_transformed) { - first_transformed_term_index = ci + has_transformed; - } - return tot; - }, []); - - return { - fragment: terms.join(' '), - terms: terms, - first_transformed_term_index: first_transformed_term_index // 1-based index - }; - } - - function optsForProduction(id, grammar) { - return { - production: id, - repid: 1, - groupid: 1, - optid: 1, - grammar: grammar - }; - } - - function transformProduction(id, production, grammar) { - var transform_opts = optsForProduction(id, grammar); - return production.map(function (handle) { - var action = null, - opts = null; - var i, len, n; - - if (typeof handle !== 'string') { - action = handle[1]; - opts = handle[2]; - handle = handle[0]; - } - var expressions = parser.parse(handle); - - if (devDebug > 1) console.log('\n================\nEBNF transform expressions:\n ', handle, opts, JSON.stringify(expressions, null, 2)); - - var list = transformExpressionList(expressions, transform_opts); - - var ret = [list.fragment]; - if (action) { - // make sure the action doesn't address any inner items. - if (list.first_transformed_term_index) { - var rhs = list.fragment; - // seek out all names and aliases; strip out literal tokens first as those cannot serve as $names: - var alist = list.terms; // rhs.replace(/'[^']+'/g, '~').replace(/"[^"]+"/g, '~').split(' '); - // we also know at which index the first transformation occurred: - var first_index = list.first_transformed_term_index - 1; - if (devDebug > 2) console.log('alist ~ rhs rule terms: ', alist, rhs); - - var alias_re = new XRegExp('\\[[\\p{Alphabetic}_][\\p{Alphabetic}\\p{Number}_]*\\]'); - var term_re = new XRegExp('^[\\p{Alphabetic}_][\\p{Alphabetic}\\p{Number}_]*$'); - // and collect the PERMITTED aliases: the names of the terms and all the remaining aliases - var good_aliases = {}; - var alias_cnt = {}; - var donotalias = {}; - - // WARNING: this replicates the knowledge/code of jison.js::addName() - var addName = function addNameEBNF(s, i) { - var base = s.replace(/[0-9]+$/, ''); - var dna = donotalias[base]; - - if (good_aliases[s]) { - alias_cnt[s]++; - if (!dna) { - good_aliases[s + alias_cnt[s]] = i + 1; - alias_cnt[s + alias_cnt[s]] = 1; - } - } else { - good_aliases[s] = i + 1; - alias_cnt[s] = 1; - if (!dna) { - good_aliases[s + alias_cnt[s]] = i + 1; - alias_cnt[s + alias_cnt[s]] = 1; - } - } - }; - - // WARNING: this replicates the knowledge/code of jison.js::markBasename() - var markBasename = function markBasenameEBNF(s) { - if (/[0-9]$/.test(s)) { - s = s.replace(/[0-9]+$/, ''); - donotalias[s] = true; - } - }; - - // mark both regular and aliased names, e.g., `id[alias1]` and `id1` - // - // WARNING: this replicates the knowledge/code of jison.js::markBasename()+addName() usage - for (i = 0, len = alist.length; i < len; i++) { - var term = alist[i]; - var alias = term.match(alias_re); - if (alias) { - markBasename(alias[0].substr(1, alias[0].length - 2)); - term = term.replace(alias_re, ''); - } - if (term.match(term_re)) { - markBasename(term); - } - } - // then check & register both regular and aliased names, e.g., `id[alias1]` and `id1` - for (i = 0, len = alist.length; i < len; i++) { - var term = alist[i]; - var alias = term.match(alias_re); - if (alias) { - addName(alias[0].substr(1, alias[0].length - 2), i); - term = term.replace(alias_re, ''); - } - if (term.match(term_re)) { - addName(term, i); - } - } - if (devDebug > 2) console.log('good_aliases: ', { - donotalias: donotalias, - good_aliases: good_aliases, - alias_cnt: alias_cnt, - }); - - // now scan the action for all named and numeric semantic values ($nonterminal / $1) - var nameref_re = new XRegExp('[$@][\\p{Alphabetic}_][\\p{Alphabetic}\\p{Number}_]*\\b', 'g'); - var named_spots = nameref_re.exec(action); - var numbered_spots = action.match(/[$@][0-9]+\b/g); - var max_term_index = list.terms.length; - if (devDebug > 2) console.log('ACTION named_spots: ', named_spots); - if (devDebug > 2) console.log('ACTION numbered_spots: ', numbered_spots); - - // loop through the XRegExp alias regex matches in `action` - while (named_spots) { - n = named_spots[0].substr(1); - if (!good_aliases[n]) { - throw new Error('The action block references the named alias "' + n + '" ' + - 'which is not available in production "' + handle + '"; ' + - 'it probably got removed by the EBNF rule rewrite process.\n' + - 'Be reminded that you cannot reference sub-elements within EBNF */+/? groups, ' + - 'only the outer-most EBNF group alias will remain available at all times ' + - 'due to the EBNF-to-BNF rewrite process.'); - } - - if (alias_cnt[n] !== 1) { - throw new Error('The action block references the ambiguous named alias or term reference "' + n + '" ' + - 'which is mentioned ' + alias_cnt[n] + ' times in production "' + handle + '", implicit and explicit aliases included.\n' + - 'You should either provide unambiguous = uniquely named aliases for these terms or use numeric index references (e.g. `$3`) as a stop-gap in your action code.\n' + - 'Be reminded that you cannot reference sub-elements within EBNF */+/? groups, ' + - 'only the outer-most EBNF group alias will remain available at all times ' + - 'due to the EBNF-to-BNF rewrite process.'); - } - //assert(good_aliases[n] <= max_term_index, 'max term index'); - - named_spots = nameref_re.exec(action); - } - if (numbered_spots) { - for (i = 0, len = numbered_spots.length; i < len; i++) { - n = parseInt(numbered_spots[i].substr(1)); - if (n > max_term_index) { - /* @const */ var n_suffixes = [ 'st', 'nd', 'rd', 'th' ]; - throw new Error('The action block references the ' + n + n_suffixes[Math.max(0, Math.min(3, n - 1))] + ' term, ' + - 'which is not available in production "' + handle + '"; ' + - 'Be reminded that you cannot reference sub-elements within EBNF */+/? groups, ' + - 'only the outer-most EBNF group alias will remain available at all times ' + - 'due to the EBNF-to-BNF rewrite process.'); - } - } - } - } - ret.push(action); - } - if (opts) { - ret.push(opts); - } - if (devDebug > 1) console.log('\n\nEBNF tx result:\n ', JSON.stringify(list, null, 2), JSON.stringify(ret, null, 2)); - - if (ret.length === 1) { - return ret[0]; - } else { - return ret; - } - }); - }; - - function transformGrammar(grammar) { - Object.keys(grammar).forEach(function transformGrammarForKey(id) { - grammar[id] = transformProduction(id, grammar[id], grammar); - }); - }; - - return { - transform: function (ebnf) { - if (devDebug > 0) console.log('EBNF:\n ', JSON.stringify(ebnf, null, 2)); - transformGrammar(ebnf); - if (devDebug > 0) console.log('\n\nEBNF after transformation:\n ', JSON.stringify(ebnf, null, 2)); - return ebnf; - } - }; -})(); - -exports.transform = EBNF.transform; - - -},{"./transform-parser.js":12,"xregexp":22}],5:[function(require,module,exports){ -/* parser generated by jison 0.4.18-184 */ - -/* - * Returns a Parser object of the following structure: - * - * Parser: { - * yy: {} The so-called "shared state" or rather the *source* of it; - * the real "shared state" `yy` passed around to - * the rule actions, etc. is a derivative/copy of this one, - * not a direct reference! - * } - * - * Parser.prototype: { - * yy: {}, - * EOF: 1, - * TERROR: 2, - * - * trace: function(errorMessage, ...), - * - * JisonParserError: function(msg, hash), - * - * quoteName: function(name), - * Helper function which can be overridden by user code later on: put suitable - * quotes around literal IDs in a description string. - * - * originalQuoteName: function(name), - * The basic quoteName handler provided by JISON. - * `cleanupAfterParse()` will clean up and reset `quoteName()` to reference this function - * at the end of the `parse()`. - * - * describeSymbol: function(symbol), - * Return a more-or-less human-readable description of the given symbol, when - * available, or the symbol itself, serving as its own 'description' for lack - * of something better to serve up. - * - * Return NULL when the symbol is unknown to the parser. - * - * symbols_: {associative list: name ==> number}, - * terminals_: {associative list: number ==> name}, - * nonterminals: {associative list: rule-name ==> {associative list: number ==> rule-alt}}, - * terminal_descriptions_: (if there are any) {associative list: number ==> description}, - * productions_: [...], - * - * performAction: function parser__performAction(yytext, yyleng, yylineno, yyloc, yystate, yysp, yyvstack, yylstack, yystack, yysstack, ...), - * where `...` denotes the (optional) additional arguments the user passed to - * `parser.parse(str, ...)` and specified by way of `%parse-param ...` in the grammar file - * - * The function parameters and `this` have the following value/meaning: - * - `this` : reference to the `yyval` internal object, which has members (`$` and `_$`) - * to store/reference the rule value `$$` and location info `@$`. - * - * One important thing to note about `this` a.k.a. `yyval`: every *reduce* action gets - * to see the same object via the `this` reference, i.e. if you wish to carry custom - * data from one reduce action through to the next within a single parse run, then you - * may get nasty and use `yyval` a.k.a. `this` for storing you own semi-permanent data. - * - * - `yytext` : reference to the lexer value which belongs to the last lexer token used - * to match this rule. This is *not* the look-ahead token, but the last token - * that's actually part of this rule. - * - * Formulated another way, `yytext` is the value of the token immediately preceeding - * the current look-ahead token. - * Caveats apply for rules which don't require look-ahead, such as epsilon rules. - * - * - `yyleng` : ditto as `yytext`, only now for the lexer.yyleng value. - * - * - `yylineno`: ditto as `yytext`, only now for the lexer.yylineno value. - * - * - `yyloc` : ditto as `yytext`, only now for the lexer.yylloc lexer token location info. - * - * - `yystate` : the current parser state number, used internally for dispatching and - * executing the action code chunk matching the rule currently being reduced. - * - * - `yysp` : the current state stack position (a.k.a. 'stack pointer') - * - * This one comes in handy when you are going to do advanced things to the parser - * stacks, all of which are accessible from your action code (see the next entries below). - * - * Also note that you can access this and other stack index values using the new double-hash - * syntax, i.e. `##$ === ##0 === yysp`, while `##1` is the stack index for all things - * related to the first rule term, just like you have `$1`, `@1` and `#1`. - * This is made available to write very advanced grammar action rules, e.g. when you want - * to investigate the parse state stack in your action code, which would, for example, - * be relevant when you wish to implement error diagnostics and reporting schemes similar - * to the work described here: - * - * + Pottier, F., 2016. Reachability and error diagnosis in LR(1) automata. - * In Journées Francophones des Languages Applicatifs. - * - * + Jeffery, C.L., 2003. Generating LR syntax error messages from examples. - * ACM Transactions on Programming Languages and Systems (TOPLAS), 25(5), pp.631–640. - * - * - `yyvstack`: reference to the parser value stack. Also accessed via the `$1` etc. - * constructs. - * - * - `yylstack`: reference to the parser token location stack. Also accessed via - * the `@1` etc. constructs. - * - * - `yystack` : reference to the parser token id stack. Also accessed via the - * `#1` etc. constructs. - * - * Note: this is a bit of a **white lie** as we can statically decode any `#n` reference to - * its numeric token id value, hence that code wouldn't need the `yystack` but *you* might - * want access for your own purposes, such as error analysis as mentioned above! - * - * Note that this stack stores the current stack of *tokens*, that is the sequence of - * already parsed=reduced *nonterminals* (tokens representing rules) and *terminals* - * (lexer tokens *shifted* onto the stack until the rule they belong to is found and - * *reduced*. - * - * - `yysstack`: reference to the parser state stack. This one carries the internal parser - * *states* such as the one in `yystate`, which are used to represent - * the parser state machine in the *parse table*. *Very* *internal* stuff, - * what can I say? If you access this one, you're clearly doing wicked things - * - * - `...` : the extra arguments you specified in the `%parse-param` statement in your - * grammar definition file. - * - * table: [...], - * State transition table - * ---------------------- - * - * index levels are: - * - `state` --> hash table - * - `symbol` --> action (number or array) - * - * If the `action` is an array, these are the elements' meaning: - * - index [0]: 1 = shift, 2 = reduce, 3 = accept - * - index [1]: GOTO `state` - * - * If the `action` is a number, it is the GOTO `state` - * - * defaultActions: {...}, - * - * parseError: function(str, hash, ExceptionClass), - * yyError: function(str, ...), - * yyRecovering: function(), - * yyErrOk: function(), - * yyClearIn: function(), - * - * constructParseErrorInfo: function(error_message, exception_object, expected_token_set, is_recoverable), - * Helper function **which will be set up during the first invocation of the `parse()` method**. - * Produces a new errorInfo 'hash object' which can be passed into `parseError()`. - * See it's use in this parser kernel in many places; example usage: - * - * var infoObj = parser.constructParseErrorInfo('fail!', null, - * parser.collect_expected_token_set(state), true); - * var retVal = parser.parseError(infoObj.errStr, infoObj, parser.JisonParserError); - * - * originalParseError: function(str, hash, ExceptionClass), - * The basic `parseError` handler provided by JISON. - * `cleanupAfterParse()` will clean up and reset `parseError()` to reference this function - * at the end of the `parse()`. - * - * options: { ... parser %options ... }, - * - * parse: function(input[, args...]), - * Parse the given `input` and return the parsed value (or `true` when none was provided by - * the root action, in which case the parser is acting as a *matcher*). - * You MAY use the additional `args...` parameters as per `%parse-param` spec of this grammar: - * these extra `args...` are passed verbatim to the grammar rules' action code. - * - * cleanupAfterParse: function(resultValue, invoke_post_methods, do_not_nuke_errorinfos), - * Helper function **which will be set up during the first invocation of the `parse()` method**. - * This helper API is invoked at the end of the `parse()` call, unless an exception was thrown - * and `%options no-try-catch` has been defined for this grammar: in that case this helper MAY - * be invoked by calling user code to ensure the `post_parse` callbacks are invoked and - * the internal parser gets properly garbage collected under these particular circumstances. - * - * lexer: { - * yy: {...}, A reference to the so-called "shared state" `yy` once - * received via a call to the `.setInput(input, yy)` lexer API. - * EOF: 1, - * ERROR: 2, - * JisonLexerError: function(msg, hash), - * parseError: function(str, hash, ExceptionClass), - * setInput: function(input, [yy]), - * input: function(), - * unput: function(str), - * more: function(), - * reject: function(), - * less: function(n), - * pastInput: function(n), - * upcomingInput: function(n), - * showPosition: function(), - * test_match: function(regex_match_array, rule_index, ...), - * next: function(...), - * lex: function(...), - * begin: function(condition), - * pushState: function(condition), - * popState: function(), - * topState: function(), - * _currentRules: function(), - * stateStackSize: function(), - * cleanupAfterLex: function() - * - * options: { ... lexer %options ... }, - * - * performAction: function(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * rules: [...], - * conditions: {associative list: name ==> set}, - * } - * } - * - * - * token location info (@$, _$, etc.): { - * first_line: n, - * last_line: n, - * first_column: n, - * last_column: n, - * range: [start_number, end_number] - * (where the numbers are indexes into the input string, zero-based) - * } - * - * --- - * - * The `parseError` function receives a 'hash' object with these members for lexer and - * parser errors: - * - * { - * text: (matched text) - * token: (the produced terminal token, if any) - * token_id: (the produced terminal token numeric ID, if any) - * line: (yylineno) - * loc: (yylloc) - * } - * - * parser (grammar) errors will also provide these additional members: - * - * { - * expected: (array describing the set of expected tokens; - * may be UNDEFINED when we cannot easily produce such a set) - * state: (integer (or array when the table includes grammar collisions); - * represents the current internal state of the parser kernel. - * can, for example, be used to pass to the `collect_expected_token_set()` - * API to obtain the expected token set) - * action: (integer; represents the current internal action which will be executed) - * new_state: (integer; represents the next/planned internal state, once the current - * action has executed) - * recoverable: (boolean: TRUE when the parser MAY have an error recovery rule - * available for this particular error) - * state_stack: (array: the current parser LALR/LR internal state stack; this can be used, - * for instance, for advanced error analysis and reporting) - * value_stack: (array: the current parser LALR/LR internal `$$` value stack; this can be used, - * for instance, for advanced error analysis and reporting) - * location_stack: (array: the current parser LALR/LR internal location stack; this can be used, - * for instance, for advanced error analysis and reporting) - * yy: (object: the current parser internal "shared state" `yy` - * as is also available in the rule actions; this can be used, - * for instance, for advanced error analysis and reporting) - * lexer: (reference to the current lexer instance used by the parser) - * parser: (reference to the current parser instance) - * } - * - * while `this` will reference the current parser instance. - * - * When `parseError` is invoked by the lexer, `this` will still reference the related *parser* - * instance, while these additional `hash` fields will also be provided: - * - * { - * lexer: (reference to the current lexer instance which reported the error) - * } - * - * When `parseError` is invoked by the parser due to a **JavaScript exception** being fired - * from either the parser or lexer, `this` will still reference the related *parser* - * instance, while these additional `hash` fields will also be provided: - * - * { - * exception: (reference to the exception thrown) - * } - * - * Please do note that in the latter situation, the `expected` field will be omitted as - * this type of failure is assumed not to be due to *parse errors* but rather due to user - * action code in either parser or lexer failing unexpectedly. - * - * --- - * - * You can specify parser options by setting / modifying the `.yy` object of your Parser instance. - * These options are available: - * - * ### options which are global for all parser instances - * - * Parser.pre_parse: function(yy [, optional parse() args]) - * optional: you can specify a pre_parse() function in the chunk following - * the grammar, i.e. after the last `%%`. - * Parser.post_parse: function(yy, retval [, optional parse() args]) { return retval; } - * optional: you can specify a post_parse() function in the chunk following - * the grammar, i.e. after the last `%%`. When it does not return any value, - * the parser will return the original `retval`. - * - * ### options which can be set up per parser instance - * - * yy: { - * pre_parse: function(yy [, optional parse() args]) - * optional: is invoked before the parse cycle starts (and before the first - * invocation of `lex()`) but immediately after the invocation of - * `parser.pre_parse()`). - * post_parse: function(yy, retval [, optional parse() args]) { return retval; } - * optional: is invoked when the parse terminates due to success ('accept') - * or failure (even when exceptions are thrown). - * `retval` contains the return value to be produced by `Parser.parse()`; - * this function can override the return value by returning another. - * When it does not return any value, the parser will return the original - * `retval`. - * This function is invoked immediately before `Parser.post_parse()`. - * - * parseError: function(str, hash, ExceptionClass) - * optional: overrides the default `parseError` function. - * quoteName: function(name), - * optional: overrides the default `quoteName` function. - * } - * - * parser.lexer.options: { - * pre_lex: function() - * optional: is invoked before the lexer is invoked to produce another token. - * `this` refers to the Lexer object. - * post_lex: function(token) { return token; } - * optional: is invoked when the lexer has produced a token `token`; - * this function can override the returned token value by returning another. - * When it does not return any (truthy) value, the lexer will return - * the original `token`. - * `this` refers to the Lexer object. - * - * ranges: boolean - * optional: `true` ==> token location info will include a .range[] member. - * flex: boolean - * optional: `true` ==> flex-like lexing behaviour where the rules are tested - * exhaustively to find the longest match. - * backtrack_lexer: boolean - * optional: `true` ==> lexer regexes are tested in order and for invoked; - * the lexer terminates the scan when a token is returned by the action code. - * xregexp: boolean - * optional: `true` ==> lexer rule regexes are "extended regex format" requiring the - * `XRegExp` library. When this %option has not been specified at compile time, all lexer - * rule regexes have been written as standard JavaScript RegExp expressions. - * } - */ -var lexParser = (function () { - -// See also: -// http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508 -// but we keep the prototype.constructor and prototype.name assignment lines too for compatibility -// with userland code which might access the derived class in a 'classic' way. -function JisonParserError(msg, hash) { - Object.defineProperty(this, 'name', { - enumerable: false, - writable: false, - value: 'JisonParserError' - }); - - if (msg == null) msg = '???'; - - Object.defineProperty(this, 'message', { - enumerable: false, - writable: true, - value: msg - }); - - this.hash = hash; - - var stacktrace; - if (hash && hash.exception instanceof Error) { - var ex2 = hash.exception; - this.message = ex2.message || msg; - stacktrace = ex2.stack; - } - if (!stacktrace) { - if (Error.hasOwnProperty('captureStackTrace')) { // V8 - Error.captureStackTrace(this, this.constructor); - } else { - stacktrace = (new Error(msg)).stack; - } - } - if (stacktrace) { - Object.defineProperty(this, 'stack', { - enumerable: false, - writable: false, - value: stacktrace - }); - } -} - -if (typeof Object.setPrototypeOf === 'function') { - Object.setPrototypeOf(JisonParserError.prototype, Error.prototype); -} else { - JisonParserError.prototype = Object.create(Error.prototype); -} -JisonParserError.prototype.constructor = JisonParserError; -JisonParserError.prototype.name = 'JisonParserError'; - - - - -// helper: reconstruct the productions[] table -function bp(s) { - var rv = []; - var p = s.pop; - var r = s.rule; - for (var i = 0, l = p.length; i < l; i++) { - rv.push([ - p[i], - r[i] - ]); - } - return rv; -} - - - -// helper: reconstruct the defaultActions[] table -function bda(s) { - var rv = {}; - var d = s.idx; - var g = s.goto; - for (var i = 0, l = d.length; i < l; i++) { - var j = d[i]; - rv[j] = g[i]; - } - return rv; -} - - - -// helper: reconstruct the 'goto' table -function bt(s) { - var rv = []; - var d = s.len; - var y = s.symbol; - var t = s.type; - var a = s.state; - var m = s.mode; - var g = s.goto; - for (var i = 0, l = d.length; i < l; i++) { - var n = d[i]; - var q = {}; - for (var j = 0; j < n; j++) { - var z = y.shift(); - switch (t.shift()) { - case 2: - q[z] = [ - m.shift(), - g.shift() - ]; - break; - - case 0: - q[z] = a.shift(); - break; - - default: - // type === 1: accept - q[z] = [ - 3 - ]; - } - } - rv.push(q); - } - return rv; -} - - - -// helper: runlength encoding with increment step: code, length: step (default step = 0) -// `this` references an array -function s(c, l, a) { - a = a || 0; - for (var i = 0; i < l; i++) { - this.push(c); - c += a; - } -} - -// helper: duplicate sequence from *relative* offset and length. -// `this` references an array -function c(i, l) { - i = this.length - i; - for (l += i; i < l; i++) { - this.push(this[i]); - } -} - -// helper: unpack an array using helpers and data, all passed in an array argument 'a'. -function u(a) { - var rv = []; - for (var i = 0, l = a.length; i < l; i++) { - var e = a[i]; - // Is this entry a helper function? - if (typeof e === 'function') { - i++; - e.apply(rv, a[i]); - } else { - rv.push(e); - } - } - return rv; -} - - -var parser = { - // Code Generator Information Report - // --------------------------------- - // - // Options: - // - // no default action: ............... false - // no try..catch: ................... false - // no default resolve on conflict: false - // on-demand look-ahead: ............ false - // error recovery token skip maximum: 3 - // yyerror in parse actions is: ..... NOT recoverable, - // yyerror in lexer actions and other non-fatal lexer are: - // .................................. NOT recoverable, - // debug grammar/output: ............ false - // has partial LR conflict upgrade: true - // rudimentary token-stack support: false - // parser table compression mode: ... 2 - // export debug tables: ............. false - // export *all* tables: ............. false - // module type: ..................... commonjs - // parser engine type: .............. lalr - // output main() in the module: ..... true - // number of expected conflicts: .... 0 - // - // - // Parser Analysis flags: - // - // all actions are default: ......... false - // uses yyleng: ..................... false - // uses yylineno: ................... false - // uses yytext: ..................... false - // uses yylloc: ..................... false - // uses ParseError API: ............. false - // uses YYERROR: .................... true - // uses YYRECOVERING: ............... false - // uses YYERROK: .................... false - // uses YYCLEARIN: .................. false - // tracks rule values: .............. true - // assigns rule values: ............. true - // uses location tracking: .......... false - // assigns location: ................ false - // uses yystack: .................... false - // uses yysstack: ................... false - // uses yysp: ....................... true - // has error recovery: .............. true - // - // --------- END OF REPORT ----------- - -trace: function no_op_trace() { }, -JisonParserError: JisonParserError, -yy: {}, -options: { - type: "lalr", - hasPartialLrUpgradeOnConflict: true, - errorRecoveryTokenDiscardCount: 3 -}, -symbols_: { - "$": 17, - "$accept": 0, - "$end": 1, - "%%": 19, - "(": 10, - ")": 11, - "*": 7, - "+": 12, - ",": 8, - ".": 15, - "/": 14, - "/!": 28, - "<": 5, - "=": 18, - ">": 6, - "?": 13, - "ACTION": 23, - "ACTION_BODY": 26, - "CHARACTER_LIT": 36, - "CODE": 43, - "EOF": 1, - "ESCAPE_CHAR": 33, - "INCLUDE": 41, - "NAME": 20, - "NAME_BRACE": 29, - "OPTIONS": 37, - "OPTIONS_END": 38, - "OPTION_STRING_VALUE": 39, - "OPTION_VALUE": 40, - "PATH": 42, - "RANGE_REGEX": 34, - "REGEX_SET": 32, - "REGEX_SET_END": 31, - "REGEX_SET_START": 30, - "SPECIAL_GROUP": 27, - "START_COND": 25, - "START_EXC": 22, - "START_INC": 21, - "STRING_LIT": 35, - "UNKNOWN_DECL": 24, - "^": 16, - "action": 55, - "action_body": 57, - "action_comments_body": 58, - "any_group_regex": 67, - "definition": 48, - "definitions": 47, - "error": 2, - "escape_char": 70, - "extra_lexer_module_code": 76, - "include_macro_code": 77, - "init": 46, - "lex": 44, - "module_code_chunk": 78, - "name_expansion": 66, - "name_list": 60, - "names_exclusive": 50, - "names_inclusive": 49, - "nonempty_regex_list": 63, - "option": 75, - "option_list": 74, - "optional_module_code_chunk": 79, - "options": 73, - "range_regex": 71, - "regex": 61, - "regex_base": 65, - "regex_concat": 64, - "regex_list": 62, - "regex_set": 68, - "regex_set_atom": 69, - "rule": 54, - "rule_block": 53, - "rules": 51, - "rules_and_epilogue": 45, - "rules_collective": 52, - "start_conditions": 59, - "string": 72, - "unbracketed_action_body": 56, - "{": 3, - "|": 9, - "}": 4 -}, -terminals_: { - 1: "EOF", - 2: "error", - 3: "{", - 4: "}", - 5: "<", - 6: ">", - 7: "*", - 8: ",", - 9: "|", - 10: "(", - 11: ")", - 12: "+", - 13: "?", - 14: "/", - 15: ".", - 16: "^", - 17: "$", - 18: "=", - 19: "%%", - 20: "NAME", - 21: "START_INC", - 22: "START_EXC", - 23: "ACTION", - 24: "UNKNOWN_DECL", - 25: "START_COND", - 26: "ACTION_BODY", - 27: "SPECIAL_GROUP", - 28: "/!", - 29: "NAME_BRACE", - 30: "REGEX_SET_START", - 31: "REGEX_SET_END", - 32: "REGEX_SET", - 33: "ESCAPE_CHAR", - 34: "RANGE_REGEX", - 35: "STRING_LIT", - 36: "CHARACTER_LIT", - 37: "OPTIONS", - 38: "OPTIONS_END", - 39: "OPTION_STRING_VALUE", - 40: "OPTION_VALUE", - 41: "INCLUDE", - 42: "PATH", - 43: "CODE" -}, -TERROR: 2, -EOF: 1, - -// internals: defined here so the object *structure* doesn't get modified by parse() et al, -// thus helping JIT compilers like Chrome V8. -originalQuoteName: null, -originalParseError: null, -cleanupAfterParse: null, -constructParseErrorInfo: null, - -__reentrant_call_depth: 0, // INTERNAL USE ONLY -__error_infos: [], // INTERNAL USE ONLY: the set of parseErrorInfo objects created since the last cleanup - -// APIs which will be set up depending on user action code analysis: -//yyRecovering: 0, -//yyErrOk: 0, -//yyClearIn: 0, - -// Helper APIs -// ----------- - -// Helper function which can be overridden by user code later on: put suitable quotes around -// literal IDs in a description string. -quoteName: function parser_quoteName(id_str) { - return '"' + id_str + '"'; -}, - -// Return a more-or-less human-readable description of the given symbol, when available, -// or the symbol itself, serving as its own 'description' for lack of something better to serve up. -// -// Return NULL when the symbol is unknown to the parser. -describeSymbol: function parser_describeSymbol(symbol) { - if (symbol !== this.EOF && this.terminal_descriptions_ && this.terminal_descriptions_[symbol]) { - return this.terminal_descriptions_[symbol]; - } - else if (symbol === this.EOF) { - return 'end of input'; - } - else if (this.terminals_[symbol]) { - return this.quoteName(this.terminals_[symbol]); - } - // Otherwise... this might refer to a RULE token i.e. a non-terminal: see if we can dig that one up. - // - // An example of this may be where a rule's action code contains a call like this: - // - // parser.describeSymbol(#$) - // - // to obtain a human-readable description or name of the current grammar rule. This comes handy in - // error handling action code blocks, for example. - var s = this.symbols_; - for (var key in s) { - if (s[key] === symbol) { - return key; - } - } - return null; -}, - -// Produce a (more or less) human-readable list of expected tokens at the point of failure. -// -// The produced list may contain token or token set descriptions instead of the tokens -// themselves to help turning this output into something that easier to read by humans -// unless `do_not_describe` parameter is set, in which case a list of the raw, *numeric*, -// expected terminals and nonterminals is produced. -// -// The returned list (array) will not contain any duplicate entries. -collect_expected_token_set: function parser_collect_expected_token_set(state, do_not_describe) { - var TERROR = this.TERROR; - var tokenset = []; - var check = {}; - // Has this (error?) state been outfitted with a custom expectations description text for human consumption? - // If so, use that one instead of the less palatable token set. - if (!do_not_describe && this.state_descriptions_ && this.state_descriptions_[state]) { - return [ - this.state_descriptions_[state] - ]; - } - for (var p in this.table[state]) { - p = +p; - if (p !== TERROR) { - var d = do_not_describe ? p : this.describeSymbol(p); - if (d && !check[d]) { - tokenset.push(d); - check[d] = true; // Mark this token description as already mentioned to prevent outputting duplicate entries. - } - } - } - return tokenset; -}, -productions_: bp({ - pop: u([ - 44, - 44, - s, - [45, 3], - 46, - 47, - 47, - s, - [48, 9], - 49, - 49, - 50, - 50, - 51, - 51, - s, - [52, 3], - 53, - 53, - 54, - s, - [55, 4], - 56, - 56, - s, - [57, 3], - 58, - 58, - s, - [59, 4], - 60, - 60, - 61, - 62, - 62, - s, - [63, 3], - 64, - 64, - s, - [65, 17], - 66, - 67, - 67, - 68, - 68, - 69, - s, - [69, 4, 1], - 72, - 73, - 74, - 74, - s, - [75, 4], - 76, - 76, - 77, - 77, - 78, - 78, - 79, - 79 -]), - rule: u([ - s, - [4, 3], - 2, - 0, - 0, - 2, - 0, - s, - [2, 3], - 3, - 3, - s, - [1, 5], - 2, - 1, - 2, - c, - [15, 3], - c, - [23, 4], - c, - [18, 6], - 2, - 1, - 5, - 4, - c, - [11, 4], - 3, - 0, - 1, - c, - [15, 3], - 0, - 3, - c, - [32, 3], - 1, - s, - [3, 4], - s, - [2, 5], - c, - [12, 3], - s, - [1, 6], - c, - [16, 3], - c, - [10, 8], - c, - [9, 3], - s, - [3, 3], - c, - [8, 3], - c, - [30, 4], - 0 -]) -}), -performAction: function parser__PerformAction(yystate /* action[1] */, yysp, yyvstack) { -/* this == yyval */ -var yy = this.yy; - -switch (yystate) { -case 1: - /*! Production:: lex : init definitions rules_and_epilogue EOF */ - this.$ = yyvstack[yysp - 1]; - this.$.macros = yyvstack[yysp - 2].macros; - this.$.startConditions = yyvstack[yysp - 2].startConditions; - this.$.unknownDecls = yyvstack[yysp - 2].unknownDecls; - // if there are any options, add them all, otherwise set options to NULL: - // can't check for 'empty object' by `if (yy.options) ...` so we do it this way: - for (var k in yy.options) { - this.$.options = yy.options; - break; - } - if (yy.actionInclude) { - var asrc = yy.actionInclude.join('\n\n'); - // Only a non-empty action code chunk should actually make it through: - if (asrc.trim() !== '') { - this.$.actionInclude = asrc; - } - } - delete yy.options; - delete yy.actionInclude; - return this.$; - break; - -case 2: - /*! Production:: lex : init definitions error EOF */ - yy.parser.yyError("Maybe you did not correctly separate the lexer sections with a '%%' on an otherwise empty line? The lexer spec file should have this structure: definitions %% rules [%% extra_module_code]"); - break; - -case 3: - /*! Production:: rules_and_epilogue : "%%" rules "%%" extra_lexer_module_code */ - if (yyvstack[yysp] && yyvstack[yysp].trim() !== '') { - this.$ = { rules: yyvstack[yysp - 2], moduleInclude: yyvstack[yysp] }; - } else { - this.$ = { rules: yyvstack[yysp - 2] }; - } - break; - -case 4: - /*! Production:: rules_and_epilogue : "%%" rules */ - this.$ = { rules: yyvstack[yysp] }; - break; - -case 5: - /*! Production:: rules_and_epilogue : ε */ - this.$ = { rules: [] }; - break; - -case 6: - /*! Production:: init : ε */ - yy.actionInclude = []; - if (!yy.options) yy.options = {}; - break; - -case 7: - /*! Production:: definitions : definition definitions */ - this.$ = yyvstack[yysp]; - if (yyvstack[yysp - 1] != null) { - if ('length' in yyvstack[yysp - 1]) { - this.$.macros[yyvstack[yysp - 1][0]] = yyvstack[yysp - 1][1]; - } else if (yyvstack[yysp - 1].type === 'names') { - for (var name in yyvstack[yysp - 1].names) { - this.$.startConditions[name] = yyvstack[yysp - 1].names[name]; - } - } else if (yyvstack[yysp - 1].type === 'unknown') { - this.$.unknownDecls.push(yyvstack[yysp - 1].body); - } - } - break; - -case 8: - /*! Production:: definitions : ε */ - this.$ = { - macros: {}, // { hash table } - startConditions: {}, // { hash table } - unknownDecls: [] // [ array of [key,value] pairs } - }; - break; - -case 9: - /*! Production:: definition : NAME regex */ -case 29: - /*! Production:: rule : regex action */ - this.$ = [yyvstack[yysp - 1], yyvstack[yysp]]; - break; - -case 10: - /*! Production:: definition : START_INC names_inclusive */ -case 11: - /*! Production:: definition : START_EXC names_exclusive */ -case 32: - /*! Production:: action : unbracketed_action_body */ -case 33: - /*! Production:: action : include_macro_code */ -case 36: - /*! Production:: action_body : action_comments_body */ -case 79: - /*! Production:: escape_char : ESCAPE_CHAR */ -case 80: - /*! Production:: range_regex : RANGE_REGEX */ -case 90: - /*! Production:: extra_lexer_module_code : optional_module_code_chunk */ -case 94: - /*! Production:: module_code_chunk : CODE */ -case 96: - /*! Production:: optional_module_code_chunk : module_code_chunk */ - this.$ = yyvstack[yysp]; - break; - -case 12: - /*! Production:: definition : "{" action_body "}" */ - yy.actionInclude.push(yyvstack[yysp - 1]); this.$ = null; - break; - -case 13: - /*! Production:: definition : "{" action_body error */ - var l = yyvstack[yysp - 1].split('\n'); - var ab = l.slice(0, 10).join('\n'); - yy.parser.yyError("Seems you did not correctly bracket the lexer 'preparatory' action block in curly braces: '{ ... }'. Offending action body:\n" + ab); - break; - -case 14: - /*! Production:: definition : ACTION */ -case 15: - /*! Production:: definition : include_macro_code */ - yy.actionInclude.push(yyvstack[yysp]); this.$ = null; - break; - -case 16: - /*! Production:: definition : options */ - this.$ = null; - break; - -case 17: - /*! Production:: definition : UNKNOWN_DECL */ - this.$ = {type: 'unknown', body: yyvstack[yysp]}; - break; - -case 18: - /*! Production:: names_inclusive : START_COND */ - this.$ = {type: 'names', names: {}}; this.$.names[yyvstack[yysp]] = 0; - break; - -case 19: - /*! Production:: names_inclusive : names_inclusive START_COND */ - this.$ = yyvstack[yysp - 1]; this.$.names[yyvstack[yysp]] = 0; - break; - -case 20: - /*! Production:: names_exclusive : START_COND */ - this.$ = {type: 'names', names: {}}; this.$.names[yyvstack[yysp]] = 1; - break; - -case 21: - /*! Production:: names_exclusive : names_exclusive START_COND */ - this.$ = yyvstack[yysp - 1]; this.$.names[yyvstack[yysp]] = 1; - break; - -case 22: - /*! Production:: rules : rules rules_collective */ - this.$ = yyvstack[yysp - 1].concat(yyvstack[yysp]); - break; - -case 23: - /*! Production:: rules : ε */ -case 28: - /*! Production:: rule_block : ε */ - this.$ = []; - break; - -case 24: - /*! Production:: rules_collective : start_conditions rule */ - if (yyvstack[yysp - 1]) { - yyvstack[yysp].unshift(yyvstack[yysp - 1]); - } - this.$ = [yyvstack[yysp]]; - break; - -case 25: - /*! Production:: rules_collective : start_conditions "{" rule_block "}" */ - if (yyvstack[yysp - 3]) { - yyvstack[yysp - 1].forEach(function (d) { - d.unshift(yyvstack[yysp - 3]); - }); - } - this.$ = yyvstack[yysp - 1]; - break; - -case 26: - /*! Production:: rules_collective : start_conditions "{" rule_block error */ - if (yyvstack[yysp - 3]) { - yyvstack[yysp - 1].forEach(function (d) { - d.unshift(yyvstack[yysp - 3]); - }); - } - yy.parser.yyError("Seems you did not correctly bracket a lexer rule set inside the start condition <" + yyvstack[yysp - 3].join(',') + "> { rules... } as a terminating curly brace '}' could not be found.", yyvstack[yysp - 1]); - break; - -case 27: - /*! Production:: rule_block : rule_block rule */ - this.$ = $rules; this.$.push(yyvstack[yysp]); - break; - -case 30: - /*! Production:: action : "{" action_body "}" */ -case 41: - /*! Production:: start_conditions : "<" name_list ">" */ - this.$ = yyvstack[yysp - 1]; - break; - -case 31: - /*! Production:: action : "{" action_body error */ - var l = yyvstack[yysp - 1].split('\n'); - var ab = l.slice(0, 10).join('\n'); - yy.parser.yyError("Seems you did not correctly bracket a lexer rule action block in curly braces: '{ ... }'. Offending action body:\n" + ab); - break; - -case 35: - /*! Production:: unbracketed_action_body : unbracketed_action_body ACTION */ - this.$ = yyvstack[yysp - 1] + '\n' + yyvstack[yysp]; - break; - -case 37: - /*! Production:: action_body : action_body "{" action_body "}" action_comments_body */ - this.$ = yyvstack[yysp - 4] + yyvstack[yysp - 3] + yyvstack[yysp - 2] + yyvstack[yysp - 1] + yyvstack[yysp]; - break; - -case 38: - /*! Production:: action_body : action_body "{" action_body error */ - var l = yyvstack[yysp - 1].split('\n'); - var ab = l.slice(0, 10).join('\n'); - yy.parser.yyError("Seems you did not correctly match curly braces '{ ... }' in a lexer rule action block. Offending action body part:\n" + ab); - break; - -case 39: - /*! Production:: action_comments_body : ε */ -case 49: - /*! Production:: regex_list : ε */ -case 97: - /*! Production:: optional_module_code_chunk : ε */ - this.$ = ''; - break; - -case 40: - /*! Production:: action_comments_body : action_comments_body ACTION_BODY */ -case 53: - /*! Production:: regex_concat : regex_concat regex_base */ -case 65: - /*! Production:: regex_base : regex_base range_regex */ -case 75: - /*! Production:: regex_set : regex_set_atom regex_set */ -case 95: - /*! Production:: module_code_chunk : module_code_chunk CODE */ - this.$ = yyvstack[yysp - 1] + yyvstack[yysp]; - break; - -case 42: - /*! Production:: start_conditions : "<" name_list error */ - var l = yyvstack[yysp - 1]; - var ab = l.slice(0, 10).join(',').replace(/[\s\r\n]/g, ' '); - yy.parser.yyError("Seems you did not correctly terminate the start condition set <" + ab + ",???> with a terminating '>'"); - break; - -case 43: - /*! Production:: start_conditions : "<" "*" ">" */ - this.$ = ['*']; - break; - -case 45: - /*! Production:: name_list : NAME */ - this.$ = [yyvstack[yysp]]; - break; - -case 46: - /*! Production:: name_list : name_list "," NAME */ - this.$ = yyvstack[yysp - 2]; this.$.push(yyvstack[yysp]); - break; - -case 47: - /*! Production:: regex : nonempty_regex_list */ - // Detect if the regex ends with a pure (Unicode) word; - // we *do* consider escaped characters which are 'alphanumeric' - // to be equivalent to their non-escaped version, hence these are - // all valid 'words' for the 'easy keyword rules' option: - // - // - hello_kitty - // - γεια_σου_γατούλα - // - \u03B3\u03B5\u03B9\u03B1_\u03C3\u03BF\u03C5_\u03B3\u03B1\u03C4\u03BF\u03CD\u03BB\u03B1 - // - // http://stackoverflow.com/questions/7885096/how-do-i-decode-a-string-with-escaped-unicode#12869914 - // - // As we only check the *tail*, we also accept these as - // 'easy keywords': - // - // - %options - // - %foo-bar - // - +++a:b:c1 - // - // Note the dash in that last example: there the code will consider - // `bar` to be the keyword, which is fine with us as we're only - // interested in the trailing boundary and patching that one for - // the `easy_keyword_rules` option. - this.$ = yyvstack[yysp]; - if (yy.options.easy_keyword_rules) { - // We need to 'protect' `eval` here as keywords are allowed - // to contain double-quotes and other leading cruft. - // `eval` *does* gobble some escapes (such as `\b`) but - // we protect against that through a simple replace regex: - // we're not interested in the special escapes' exact value - // anyway. - // It will also catch escaped escapes (`\\`), which are not - // word characters either, so no need to worry about - // `eval(str)` 'correctly' converting convoluted constructs - // like '\\\\\\\\\\b' in here. - this.$ = this.$ - .replace(/\\\\/g, '.') - .replace(/"/g, '.') - .replace(/\\c[A-Z]/g, '.') - .replace(/\\[^xu0-9]/g, '.'); - - try { - // Convert Unicode escapes and other escapes to their literal characters - // BEFORE we go and check whether this item is subject to the - // `easy_keyword_rules` option. - this.$ = eval('"' + this.$ + '"'); - } - catch (ex) { - console.warn('easy-keyword-rule FAIL on eval: ', ex); - - // make the next keyword test fail: - this.$ = '.'; - } - // a 'keyword' starts with an alphanumeric character, - // followed by zero or more alphanumerics or digits: - var re = new XRegExp('\\w[\\w\\d]*$'); - if (XRegExp.match(this.$, re)) { - this.$ = yyvstack[yysp] + "\\b"; - } else { - this.$ = yyvstack[yysp]; - } - } - break; - -case 50: - /*! Production:: nonempty_regex_list : regex_concat "|" regex_list */ - this.$ = yyvstack[yysp - 2] + '|' + yyvstack[yysp]; - break; - -case 51: - /*! Production:: nonempty_regex_list : "|" regex_list */ - this.$ = '|' + yyvstack[yysp]; - break; - -case 55: - /*! Production:: regex_base : "(" regex_list ")" */ - this.$ = '(' + yyvstack[yysp - 1] + ')'; - break; - -case 56: - /*! Production:: regex_base : SPECIAL_GROUP regex_list ")" */ - this.$ = yyvstack[yysp - 2] + yyvstack[yysp - 1] + ')'; - break; - -case 57: - /*! Production:: regex_base : "(" regex_list error */ - var l = yyvstack[yysp - 1]; - var ab = l.replace(/[\s\r\n]/g, ' ').substring(0, 32); - yy.parser.yyError("Seems you did not correctly bracket a lex rule regex part in '(...)' braces. Unterminated regex part: (" + ab, yyvstack[yysp - 1]); - break; - -case 58: - /*! Production:: regex_base : SPECIAL_GROUP regex_list error */ - var l = yyvstack[yysp - 1]; - var ab = l.replace(/[\s\r\n]/g, ' ').substring(0, 32); - yy.parser.yyError("Seems you did not correctly bracket a lex rule regex part in '(...)' braces. Unterminated regex part: " + yyvstack[yysp - 2] + ab, yyvstack[yysp - 1]); - break; - -case 59: - /*! Production:: regex_base : regex_base "+" */ - this.$ = yyvstack[yysp - 1] + '+'; - break; - -case 60: - /*! Production:: regex_base : regex_base "*" */ - this.$ = yyvstack[yysp - 1] + '*'; - break; - -case 61: - /*! Production:: regex_base : regex_base "?" */ - this.$ = yyvstack[yysp - 1] + '?'; - break; - -case 62: - /*! Production:: regex_base : "/" regex_base */ - this.$ = '(?=' + yyvstack[yysp] + ')'; - break; - -case 63: - /*! Production:: regex_base : "/!" regex_base */ - this.$ = '(?!' + yyvstack[yysp] + ')'; - break; - -case 67: - /*! Production:: regex_base : "." */ - this.$ = '.'; - break; - -case 68: - /*! Production:: regex_base : "^" */ - this.$ = '^'; - break; - -case 69: - /*! Production:: regex_base : "$" */ - this.$ = '$'; - break; - -case 73: - /*! Production:: any_group_regex : REGEX_SET_START regex_set REGEX_SET_END */ -case 91: - /*! Production:: extra_lexer_module_code : optional_module_code_chunk include_macro_code extra_lexer_module_code */ - this.$ = yyvstack[yysp - 2] + yyvstack[yysp - 1] + yyvstack[yysp]; - break; - -case 74: - /*! Production:: any_group_regex : REGEX_SET_START regex_set error */ - var l = yyvstack[yysp - 1]; - var ab = l.replace(/[\s\r\n]/g, ' ').substring(0, 32); - yy.parser.yyError("Seems you did not correctly bracket a lex rule regex set in '[...]' brackets. Unterminated regex set: " + yyvstack[yysp - 2] + ab, yyvstack[yysp - 1]); - break; - -case 78: - /*! Production:: regex_set_atom : name_expansion */ - if (XRegExp._getUnicodeProperty(yyvstack[yysp].replace(/[{}]/g, '')) - && yyvstack[yysp].toUpperCase() !== yyvstack[yysp] - ) { - // treat this as part of an XRegExp `\p{...}` Unicode 'General Category' Property cf. http://unicode.org/reports/tr18/#Categories - this.$ = yyvstack[yysp]; - } else { - this.$ = yyvstack[yysp]; - } - //console.log("name expansion for: ", { name: $name_expansion, redux: $name_expansion.replace(/[{}]/g, ''), output: $$ }); - break; - -case 81: - /*! Production:: string : STRING_LIT */ - this.$ = prepareString(yyvstack[yysp].substr(1, yyvstack[yysp].length - 2)); - break; - -case 86: - /*! Production:: option : NAME */ - yy.options[yyvstack[yysp]] = true; - break; - -case 87: - /*! Production:: option : NAME "=" OPTION_STRING_VALUE */ - yy.options[yyvstack[yysp - 2]] = yyvstack[yysp]; - break; - -case 88: - /*! Production:: option : NAME "=" OPTION_VALUE */ -case 89: - /*! Production:: option : NAME "=" NAME */ - yy.options[yyvstack[yysp - 2]] = parseValue(yyvstack[yysp]); - break; - -case 92: - /*! Production:: include_macro_code : INCLUDE PATH */ - var fs = require('fs'); - var fileContent = fs.readFileSync(yyvstack[yysp], { encoding: 'utf-8' }); - // And no, we don't support nested '%include': - this.$ = '\n// Included by Jison: ' + yyvstack[yysp] + ':\n\n' + fileContent + '\n\n// End Of Include by Jison: ' + yyvstack[yysp] + '\n\n'; - break; - -case 93: - /*! Production:: include_macro_code : INCLUDE error */ - yy.parser.yyError("%include MUST be followed by a valid file path"); - break; - -} -}, -table: bt({ - len: u([ - 13, - 1, - 15, - 4, - 15, - 21, - 2, - 2, - 6, - s, - [11, 4], - 2, - 3, - 1, - 1, - 18, - 3, - 11, - 11, - 30, - 33, - 30, - 23, - 23, - 17, - 17, - s, - [29, 7], - 31, - 5, - s, - [29, 3], - s, - [12, 4], - 3, - 4, - 27, - 27, - 1, - 4, - c, - [36, 3], - 19, - 33, - 30, - 12, - 12, - s, - [29, 5], - 2, - 2, - 30, - 30, - 2, - 7, - 4, - 4, - 12, - 12, - 11, - 11, - 6, - 4, - 11, - 1, - 3, - 6, - 17, - 23, - 3, - c, - [27, 6], - 29, - 2, - 3, - s, - [2, 3], - 1, - s, - [3, 3], - 17, - 16, - 6, - 3, - 1, - 3, - 5, - 3, - 6, - 3, - 24, - 19, - 6, - 20, - 19, - 20, - 14, - 14, - 1, - 14, - 4, - 1, - 17, - 17, - 15, - 3, - 20, - 3, - 19, - 19 -]), - symbol: u([ - 1, - 2, - 3, - s, - [19, 6, 1], - 37, - 41, - 44, - 46, - 1, - c, - [14, 11], - 47, - 48, - 73, - 77, - 1, - 2, - 19, - 45, - c, - [19, 15], - 9, - 10, - s, - [14, 4, 1], - s, - [27, 4, 1], - 33, - 35, - 36, - 61, - s, - [63, 5, 1], - 70, - 72, - 25, - 49, - 25, - 50, - 2, - 3, - 4, - 26, - 57, - 58, - c, - [46, 11], - c, - [11, 33], - 2, - 42, - 20, - 74, - 75, - s, - [1, 3], - 3, - 5, - c, - [85, 6], - 19, - c, - [86, 7], - 51, - c, - [119, 3], - c, - [61, 25], - 9, - 10, - 11, - c, - [44, 5], - c, - [18, 5], - c, - [49, 7], - 37, - 41, - c, - [134, 5], - c, - [30, 25], - s, - [62, 6, 1], - c, - [33, 5], - 7, - s, - [9, 9, 1], - c, - [36, 11], - s, - [34, 4, 1], - 41, - 71, - 2, - c, - [61, 7], - c, - [55, 7], - c, - [53, 8], - c, - [23, 23], - c, - [263, 12], - c, - [17, 22], - c, - [110, 29], - c, - [29, 197], - s, - [31, 7, 1], - 41, - 29, - 32, - 66, - 68, - 69, - c, - [123, 90], - s, - [19, 7, 1], - c, - [12, 38], - c, - [647, 3], - c, - [650, 4], - s, - [1, 5, 1], - c, - [599, 7], - c, - [525, 14], - 43, - c, - [27, 27], - 38, - 20, - 38, - 74, - 75, - 18, - 20, - 38, - c, - [661, 19], - 52, - 59, - c, - [607, 63], - c, - [30, 3], - 11, - c, - [712, 11], - c, - [12, 12], - c, - [493, 142], - 2, - 11, - 2, - 11, - c, - [203, 33], - c, - [840, 28], - 31, - 2, - 29, - 31, - c, - [530, 4], - c, - [7, 4], - c, - [4, 4], - c, - [439, 33], - c, - [1046, 14], - c, - [1096, 5], - c, - [452, 7], - c, - [21, 8], - 38, - 20, - 39, - 40, - 1, - 41, - 43, - 76, - 78, - 79, - c, - [409, 17], - c, - [405, 3], - c, - [890, 11], - 54, - c, - [1171, 8], - 7, - 20, - 60, - c, - [358, 157], - c, - [387, 30], - 31, - c, - [256, 3], - 20, - c, - [654, 3], - c, - [651, 4], - 41, - 77, - c, - [250, 3], - c, - [3, 4], - c, - [250, 16], - 2, - 4, - c, - [251, 13], - 53, - 3, - 23, - 41, - 55, - 56, - 77, - 2, - 6, - 8, - 6, - c, - [4, 3], - c, - [321, 4], - c, - [326, 4], - c, - [310, 7], - 41, - 43, - c, - [46, 15], - c, - [297, 9], - c, - [783, 12], - c, - [89, 8], - c, - [1483, 8], - c, - [25, 9], - 23, - c, - [26, 7], - c, - [45, 19], - c, - [39, 20], - c, - [404, 14], - c, - [14, 14], - 20, - c, - [15, 14], - c, - [489, 5], - c, - [219, 17], - c, - [236, 32], - c, - [217, 4], - c, - [120, 19], - c, - [248, 3], - c, - [162, 31], - c, - [19, 7] -]), - type: u([ - s, - [2, 11], - 0, - 0, - 1, - c, - [14, 13], - 0, - 0, - c, - [7, 4], - c, - [19, 18], - c, - [17, 14], - c, - [21, 5], - 0, - c, - [40, 6], - c, - [31, 15], - s, - [2, 34], - c, - [49, 21], - c, - [69, 48], - c, - [137, 8], - c, - [30, 30], - c, - [33, 28], - c, - [118, 20], - c, - [53, 23], - c, - [23, 20], - c, - [17, 34], - s, - [2, 224], - c, - [239, 202], - c, - [201, 24], - c, - [607, 80], - c, - [291, 188], - c, - [30, 36], - c, - [530, 61], - c, - [389, 26], - c, - [85, 32], - s, - [0, 9], - c, - [370, 203], - c, - [201, 39], - c, - [1454, 5], - c, - [289, 13], - c, - [395, 28], - c, - [297, 11], - c, - [752, 48], - s, - [2, 195] -]), - state: u([ - s, - [1, 4, 1], - 11, - 10, - 15, - 18, - c, - [5, 3], - 19, - 20, - 21, - 23, - 28, - 29, - 34, - 33, - 40, - 42, - 44, - 45, - 48, - 49, - 53, - 55, - c, - [12, 4], - 56, - 57, - c, - [20, 6], - 61, - 63, - c, - [9, 7], - 64, - c, - [8, 7], - 65, - c, - [5, 4], - 66, - c, - [5, 4], - 70, - 67, - 68, - 78, - 49, - 81, - 82, - 84, - c, - [42, 8], - 61, - 61, - 70, - 91, - 68, - 92, - 45, - 96, - 98, - 97, - 100, - 102, - c, - [82, 7], - 103, - 108, - 110, - 111, - 113, - 114, - 120, - 121, - 98, - 97, - 124, - c, - [19, 8], - 125, - 45 -]), - mode: u([ - s, - [2, 13], - 1, - 2, - s, - [1, 7], - c, - [8, 3], - c, - [14, 11], - s, - [1, 15], - s, - [2, 48], - c, - [53, 50], - c, - [129, 5], - c, - [52, 8], - c, - [118, 12], - c, - [25, 26], - c, - [29, 6], - c, - [71, 15], - c, - [52, 12], - c, - [218, 11], - s, - [1, 35], - s, - [2, 234], - c, - [236, 98], - c, - [97, 24], - c, - [24, 15], - c, - [374, 6], - c, - [142, 55], - c, - [470, 4], - c, - [86, 13], - c, - [72, 11], - c, - [565, 52], - c, - [446, 170], - c, - [310, 9], - c, - [202, 25], - c, - [29, 26], - c, - [310, 5], - c, - [241, 75], - c, - [141, 5], - c, - [80, 18], - c, - [861, 204], - c, - [191, 11], - c, - [236, 21], - c, - [1279, 23], - c, - [245, 14], - c, - [402, 6], - c, - [276, 48], - c, - [425, 76], - c, - [93, 68], - c, - [458, 64] -]), - goto: u([ - s, - [6, 11], - s, - [8, 4], - 5, - 6, - 7, - 9, - 12, - 14, - 13, - 5, - 16, - 17, - c, - [14, 11], - 22, - 24, - 26, - 30, - 31, - 32, - 25, - 27, - 35, - 36, - 39, - 37, - 38, - 41, - 43, - s, - [39, 4], - s, - [14, 11], - s, - [15, 11], - s, - [16, 11], - s, - [17, 11], - 47, - 46, - 50, - 51, - 52, - s, - [23, 17], - s, - [7, 3], - s, - [9, 11], - s, - [47, 11], - s, - [52, 3], - 54, - 24, - 52, - c, - [114, 4], - s, - [52, 6], - c, - [120, 7], - 52, - 52, - s, - [49, 3], - 22, - 24, - 49, - c, - [25, 4], - s, - [49, 6], - c, - [25, 7], - 49, - 49, - s, - [54, 3], - 59, - s, - [54, 3], - 58, - 60, - s, - [54, 15], - 62, - s, - [54, 4], - c, - [52, 8], - c, - [46, 8], - c, - [15, 14], - c, - [218, 12], - c, - [12, 12], - s, - [64, 29], - s, - [66, 29], - s, - [67, 29], - s, - [68, 29], - s, - [69, 29], - s, - [70, 29], - s, - [71, 29], - s, - [72, 31], - 35, - 69, - s, - [81, 29], - s, - [82, 29], - s, - [79, 29], - s, - [10, 9], - 71, - 10, - 10, - s, - [18, 12], - s, - [11, 9], - 72, - 11, - 11, - s, - [20, 12], - 74, - 75, - 73, - s, - [36, 3], - 76, - s, - [92, 27], - s, - [93, 27], - 77, - 50, - 85, - 79, - 86, - 86, - 1, - 2, - 4, - 44, - 83, - s, - [44, 6], - 80, - s, - [44, 7], - c, - [565, 25], - s, - [53, 3], - 59, - s, - [53, 3], - 58, - 60, - s, - [53, 15], - 62, - s, - [53, 4], - s, - [51, 12], - s, - [48, 12], - s, - [59, 29], - s, - [60, 29], - s, - [61, 29], - s, - [65, 29], - s, - [80, 29], - 86, - 85, - 88, - 87, - s, - [62, 3], - 59, - s, - [62, 3], - 58, - 60, - s, - [62, 20], - s, - [63, 3], - 59, - s, - [63, 3], - 58, - 60, - s, - [63, 15], - c, - [25, 4], - 63, - 90, - 89, - 76, - 35, - 76, - 69, - s, - [77, 4], - s, - [78, 4], - s, - [19, 12], - s, - [21, 12], - s, - [12, 11], - s, - [13, 11], - s, - [39, 4], - s, - [40, 4], - s, - [83, 11], - 84, - 95, - 93, - 94, - 97, - 97, - 99, - s, - [22, 17], - 101, - c, - [1089, 13], - 104, - 105, - s, - [50, 12], - s, - [55, 29], - s, - [57, 29], - s, - [56, 29], - s, - [58, 29], - s, - [73, 29], - s, - [74, 29], - 75, - 75, - 107, - 75, - 106, - 87, - 87, - 88, - 88, - 89, - 89, - 3, - 90, - 13, - 96, - 96, - 109, - s, - [94, 3], - s, - [24, 17], - s, - [28, 15], - 112, - 115, - 13, - 117, - 116, - 118, - 119, - s, - [45, 3], - s, - [39, 4], - s, - [38, 3], - c, - [291, 3], - s, - [95, 3], - 123, - 122, - c, - [278, 13], - s, - [29, 19], - s, - [39, 4], - s, - [32, 12], - 126, - s, - [32, 7], - s, - [33, 19], - s, - [34, 20], - s, - [41, 14], - s, - [42, 14], - 127, - s, - [43, 14], - s, - [37, 3], - 76, - 91, - s, - [25, 17], - s, - [26, 17], - s, - [27, 15], - 129, - 75, - 128, - s, - [35, 20], - s, - [46, 3], - s, - [30, 19], - s, - [31, 19] -]) -}), -defaultActions: bda({ - idx: u([ - 0, - s, - [8, 5, 1], - s, - [17, 4, 1], - s, - [28, 8, 1], - 37, - 38, - 39, - 41, - 43, - 46, - 47, - 51, - 52, - s, - [56, 7, 1], - s, - [69, 10, 1], - 81, - s, - [84, 8, 1], - s, - [93, 4, 1], - 99, - 100, - 101, - 105, - 106, - 107, - 109, - 111, - 112, - s, - [114, 4, 1], - 119, - s, - [121, 4, 1], - s, - [126, 4, 1] -]), - goto: u([ - 6, - 39, - s, - [14, 4, 1], - 23, - 7, - 9, - 47, - 64, - s, - [66, 7, 1], - 81, - 82, - 79, - 18, - 20, - 92, - 93, - 1, - 2, - 51, - 48, - 59, - 60, - 61, - 65, - 80, - 77, - 78, - 19, - 21, - 12, - 13, - 39, - 40, - 83, - 84, - 22, - 50, - 55, - 57, - 56, - 58, - 73, - 74, - 75, - 87, - 88, - 89, - 3, - 94, - 24, - 28, - 45, - 39, - 38, - 95, - 29, - 39, - 33, - 34, - 41, - 42, - 43, - 91, - 25, - 26, - 27, - 35, - 46, - 30, - 31 -]) -}), -parseError: function parseError(str, hash, ExceptionClass) { - if (hash.recoverable && typeof this.trace === 'function') { - this.trace(str); - hash.destroy(); // destroy... well, *almost*! - } else { - if (!ExceptionClass) { - ExceptionClass = this.JisonParserError; - } - throw new ExceptionClass(str, hash); - } -}, -parse: function parse(input) { - var self = this, - stack = new Array(128), // token stack: stores token which leads to state at the same index (column storage) - sstack = new Array(128), // state stack: stores states (column storage) - - vstack = new Array(128), // semantic value stack - - table = this.table, - sp = 0; // 'stack pointer': index into the stacks - - var recovering = 0; // (only used when the grammar contains error recovery rules) - var TERROR = this.TERROR, - EOF = this.EOF, - ERROR_RECOVERY_TOKEN_DISCARD_COUNT = (this.options.errorRecoveryTokenDiscardCount | 0) || 3; - var NO_ACTION = [0, table.length /* ensures that anyone using this new state will fail dramatically! */]; - - //this.reductionCount = this.shiftCount = 0; - - var lexer; - if (this.__lexer__) { - lexer = this.__lexer__; - } else { - lexer = this.__lexer__ = Object.create(this.lexer); - } - - var sharedState_yy = { - parseError: null, - quoteName: null, - lexer: null, - parser: null, - pre_parse: null, - post_parse: null - }; - // copy state - for (var k in this.yy) { - if (Object.prototype.hasOwnProperty.call(this.yy, k)) { - sharedState_yy[k] = this.yy[k]; - } - } - - sharedState_yy.lexer = lexer; - sharedState_yy.parser = this; - - - - - - - - - - - - - - - - - - - - // *Always* setup `yyError`, `YYRECOVERING`, `yyErrOk` and `yyClearIn` functions as it is paramount - // to have *their* closure match ours -- if we only set them up once, - // any subsequent `parse()` runs will fail in very obscure ways when - // these functions are invoked in the user action code block(s) as - // their closure will still refer to the `parse()` instance which set - // them up. Hence we MUST set them up at the start of every `parse()` run! - if (this.yyError) { - this.yyError = function yyError(str /*, ...args */) { - - - - var error_rule_depth = (this.options.parserErrorsAreRecoverable ? locateNearestErrorRecoveryRule(state) : -1); - var expected = this.collect_expected_token_set(state); - var hash = this.constructParseErrorInfo(str, null, expected, (error_rule_depth >= 0)); - - - // Add any extra args to the hash under the name `extra_error_attributes`: - var args = Array.prototype.slice.call(arguments, 1); - if (args.length) { - hash.extra_error_attributes = args; - } - - var r = this.parseError(str, hash, this.JisonParserError); - return r; - }; - } - - - - - - - lexer.setInput(input, sharedState_yy); - - - - vstack[sp] = null; - sstack[sp] = 0; - stack[sp] = 0; - ++sp; - - - // Does the shared state override the default `parseError` that already comes with this instance? - if (typeof sharedState_yy.parseError === 'function') { - this.parseError = function parseErrorAlt(str, hash, ExceptionClass) { - if (!ExceptionClass) { - ExceptionClass = this.JisonParserError; - } - return sharedState_yy.parseError(str, hash, ExceptionClass); - }; - } else { - this.parseError = this.originalParseError; - } - - // Does the shared state override the default `quoteName` that already comes with this instance? - if (typeof sharedState_yy.quoteName === 'function') { - this.quoteName = sharedState_yy.quoteName; - } else { - this.quoteName = this.originalQuoteName; - } - - // set up the cleanup function; make it an API so that external code can re-use this one in case of - // calamities or when the `%options no-try-catch` option has been specified for the grammar, in which - // case this parse() API method doesn't come with a `finally { ... }` block any more! - // - // NOTE: as this API uses parse() as a closure, it MUST be set again on every parse() invocation, - // or else your `sharedState`, etc. references will be *wrong*! - this.cleanupAfterParse = function parser_cleanupAfterParse(resultValue, invoke_post_methods, do_not_nuke_errorinfos) { - var rv; - - if (invoke_post_methods) { - if (sharedState_yy.post_parse) { - rv = sharedState_yy.post_parse.call(this, sharedState_yy, resultValue); - if (typeof rv !== 'undefined') resultValue = rv; - } - if (this.post_parse) { - rv = this.post_parse.call(this, sharedState_yy, resultValue); - if (typeof rv !== 'undefined') resultValue = rv; - } - } - - if (this.__reentrant_call_depth > 1) return resultValue; // do not (yet) kill the sharedState when this is a reentrant run. - - // clean up the lingering lexer structures as well: - if (lexer.cleanupAfterLex) { - lexer.cleanupAfterLex(do_not_nuke_errorinfos); - } - - // prevent lingering circular references from causing memory leaks: - if (sharedState_yy) { - sharedState_yy.parseError = undefined; - sharedState_yy.quoteName = undefined; - sharedState_yy.lexer = undefined; - sharedState_yy.parser = undefined; - if (lexer.yy === sharedState_yy) { - lexer.yy = undefined; - } - } - sharedState_yy = undefined; - this.parseError = this.originalParseError; - this.quoteName = this.originalQuoteName; - - // nuke the vstack[] array at least as that one will still reference obsoleted user values. - // To be safe, we nuke the other internal stack columns as well... - stack.length = 0; // fastest way to nuke an array without overly bothering the GC - sstack.length = 0; - - vstack.length = 0; - sp = 0; - - // nuke the error hash info instances created during this run. - // Userland code must COPY any data/references - // in the error hash instance(s) it is more permanently interested in. - if (!do_not_nuke_errorinfos) { - for (var i = this.__error_infos.length - 1; i >= 0; i--) { - var el = this.__error_infos[i]; - if (el && typeof el.destroy === 'function') { - el.destroy(); - } - } - this.__error_infos.length = 0; - } - - return resultValue; - }; - - // NOTE: as this API uses parse() as a closure, it MUST be set again on every parse() invocation, - // or else your `lexer`, `sharedState`, etc. references will be *wrong*! - this.constructParseErrorInfo = function parser_constructParseErrorInfo(msg, ex, expected, recoverable) { - var pei = { - errStr: msg, - exception: ex, - text: lexer.match, - value: lexer.yytext, - token: this.describeSymbol(symbol) || symbol, - token_id: symbol, - line: lexer.yylineno, - - expected: expected, - recoverable: recoverable, - state: state, - action: action, - new_state: newState, - symbol_stack: stack, - state_stack: sstack, - value_stack: vstack, - - stack_pointer: sp, - yy: sharedState_yy, - lexer: lexer, - parser: this, - - // and make sure the error info doesn't stay due to potential - // ref cycle via userland code manipulations. - // These would otherwise all be memory leak opportunities! - // - // Note that only array and object references are nuked as those - // constitute the set of elements which can produce a cyclic ref. - // The rest of the members is kept intact as they are harmless. - destroy: function destructParseErrorInfo() { - // remove cyclic references added to error info: - // info.yy = null; - // info.lexer = null; - // info.value = null; - // info.value_stack = null; - // ... - var rec = !!this.recoverable; - for (var key in this) { - if (this.hasOwnProperty(key) && typeof key === 'object') { - this[key] = undefined; - } - } - this.recoverable = rec; - } - }; - // track this instance so we can `destroy()` it once we deem it superfluous and ready for garbage collection! - this.__error_infos.push(pei); - return pei; - }; - - - function lex() { - var token = lexer.lex(); - // if token isn't its numeric value, convert - if (typeof token !== 'number') { - token = self.symbols_[token] || token; - } - return token || EOF; - } - - - var symbol = 0; - var preErrorSymbol = 0; - var lastEofErrorStateDepth = 0; - var state, action, r, t; - var yyval = { - $: true, - _$: undefined, - yy: sharedState_yy - }; - var p, len, this_production; - - var newState; - var retval = false; - - - // Return the rule stack depth where the nearest error rule can be found. - // Return -1 when no error recovery rule was found. - function locateNearestErrorRecoveryRule(state) { - var stack_probe = sp - 1; - var depth = 0; - - // try to recover from error - for (;;) { - // check for error recovery rule in this state - - var t = table[state][TERROR] || NO_ACTION; - if (t[0]) { - // We need to make sure we're not cycling forever: - // once we hit EOF, even when we `yyerrok()` an error, we must - // prevent the core from running forever, - // e.g. when parent rules are still expecting certain input to - // follow after this, for example when you handle an error inside a set - // of braces which are matched by a parent rule in your grammar. - // - // Hence we require that every error handling/recovery attempt - // *after we've hit EOF* has a diminishing state stack: this means - // we will ultimately have unwound the state stack entirely and thus - // terminate the parse in a controlled fashion even when we have - // very complex error/recovery code interplay in the core + user - // action code blocks: - - if (symbol === EOF) { - if (!lastEofErrorStateDepth) { - lastEofErrorStateDepth = sp - 1 - depth; - } else if (lastEofErrorStateDepth <= sp - 1 - depth) { - - --stack_probe; // popStack(1): [symbol, action] - state = sstack[stack_probe]; - ++depth; - continue; - } - } - return depth; - } - if (state === 0 /* $accept rule */ || stack_probe < 1) { - - return -1; // No suitable error recovery rule available. - } - --stack_probe; // popStack(1): [symbol, action] - state = sstack[stack_probe]; - ++depth; - } - } - - - try { - this.__reentrant_call_depth++; - - if (this.pre_parse) { - this.pre_parse.call(this, sharedState_yy); - } - if (sharedState_yy.pre_parse) { - sharedState_yy.pre_parse.call(this, sharedState_yy); - } - - newState = sstack[sp - 1]; - for (;;) { - // retrieve state number from top of stack - state = newState; // sstack[sp - 1]; - - // use default actions if available - if (this.defaultActions[state]) { - action = 2; - newState = this.defaultActions[state]; - } else { - // The single `==` condition below covers both these `===` comparisons in a single - // operation: - // - // if (symbol === null || typeof symbol === 'undefined') ... - if (!symbol) { - symbol = lex(); - } - // read action for current state and first input - t = (table[state] && table[state][symbol]) || NO_ACTION; - newState = t[1]; - action = t[0]; - - - - - // handle parse error - if (!action) { - // first see if there's any chance at hitting an error recovery rule: - var error_rule_depth = locateNearestErrorRecoveryRule(state); - var errStr = null; - var errSymbolDescr = (this.describeSymbol(symbol) || symbol); - var expected = this.collect_expected_token_set(state); - - if (!recovering) { - // Report error - if (typeof lexer.yylineno === 'number') { - errStr = 'Parse error on line ' + (lexer.yylineno + 1) + ': '; - } else { - errStr = 'Parse error: '; - } - if (lexer.showPosition) { - errStr += '\n' + lexer.showPosition(79 - 10, 10) + '\n'; - } - if (expected.length) { - errStr += 'Expecting ' + expected.join(', ') + ', got unexpected ' + errSymbolDescr; - } else { - errStr += 'Unexpected ' + errSymbolDescr; - } - p = this.constructParseErrorInfo(errStr, null, expected, (error_rule_depth >= 0)); - r = this.parseError(p.errStr, p, this.JisonParserError); - - - if (!p.recoverable) { - retval = r; - break; - } else { - // TODO: allow parseError callback to edit symbol and or state at the start of the error recovery process... - } - } - - - - // just recovered from another error - if (recovering === ERROR_RECOVERY_TOKEN_DISCARD_COUNT && error_rule_depth >= 0) { - // only barf a fatal hairball when we're out of look-ahead symbols and none hit a match; - // this DOES discard look-ahead while recovering from an error when said look-ahead doesn't - // suit the error recovery rules... The error HAS been reported already so we're fine with - // throwing away a few items if that is what it takes to match the nearest recovery rule! - if (symbol === EOF || preErrorSymbol === EOF) { - p = this.__error_infos[this.__error_infos.length - 1]; - if (!p) { - p = this.constructParseErrorInfo('Parsing halted while starting to recover from another error.', null, expected, false); - } else { - p.errStr = 'Parsing halted while starting to recover from another error. Previous error which resulted in this fatal result: ' + p.errStr; - p.recoverable = false; - } - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - - // discard current lookahead and grab another - - - - - - symbol = lex(); - - - } - - // try to recover from error - if (error_rule_depth < 0) { - p = this.constructParseErrorInfo((errStr || 'Parsing halted. No suitable error recovery rule available.'), null, expected, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - sp -= error_rule_depth; - - preErrorSymbol = (symbol === TERROR ? 0 : symbol); // save the lookahead token - symbol = TERROR; // insert generic error symbol as new lookahead - // allow N (default: 3) real symbols to be shifted before reporting a new error - recovering = ERROR_RECOVERY_TOKEN_DISCARD_COUNT; - - newState = sstack[sp - 1]; - - - - continue; - } - - - } - - - - - - - - - - switch (action) { - // catch misc. parse failures: - default: - // this shouldn't happen, unless resolve defaults are off - if (action instanceof Array) { - p = this.constructParseErrorInfo(('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol), null, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - // Another case of better safe than sorry: in case state transitions come out of another error recovery process - // or a buggy LUT (LookUp Table): - p = this.constructParseErrorInfo('Parsing halted. No viable error recovery approach available due to internal system failure.', null, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - - // shift: - case 1: - //this.shiftCount++; - stack[sp] = symbol; - vstack[sp] = lexer.yytext; - - sstack[sp] = newState; // push state - ++sp; - symbol = 0; - if (!preErrorSymbol) { // normal execution / no error - // Pick up the lexer details for the current symbol as that one is not 'look-ahead' any more: - - - - - - if (recovering > 0) { - recovering--; - - } - } else { - // error just occurred, resume old lookahead f/ before error, *unless* that drops us straight back into error mode: - symbol = preErrorSymbol; - preErrorSymbol = 0; - - // read action for current state and first input - t = (table[newState] && table[newState][symbol]) || NO_ACTION; - if (!t[0] || symbol === TERROR) { - // forget about that symbol and move forward: this wasn't a 'forgot to insert' error type where - // (simple) stuff might have been missing before the token which caused the error we're - // recovering from now... - // - // Also check if the LookAhead symbol isn't the ERROR token we set as part of the error - // recovery, for then this we would we idling (cycling) on the error forever. - // Yes, this does not take into account the possibility that the *lexer* may have - // produced a *new* TERROR token all by itself, but that would be a very peculiar grammar! - - symbol = 0; - } - } - - continue; - - // reduce: - case 2: - //this.reductionCount++; - this_production = this.productions_[newState - 1]; // `this.productions_[]` is zero-based indexed while states start from 1 upwards... - len = this_production[1]; - - - - - - - // Make sure subsequent `$$ = $1` default action doesn't fail - // for rules where len==0 as then there's no $1 (you're reducing an epsilon rule then!) - // - // Also do this to prevent nasty action block codes to *read* `$0` or `$$` - // and *not* get `undefined` as a result for their efforts! - vstack[sp] = undefined; - - // perform semantic action - yyval.$ = vstack[sp - len]; // default to $$ = $1; result must produce `undefined` when len == 0, as then there's no $1 - - - - - - - - - - - r = this.performAction.call(yyval, newState, sp - 1, vstack); - - if (typeof r !== 'undefined') { - retval = r; - break; - } - - // pop off stack - sp -= len; - - // don't overwrite the `symbol` variable: use a local var to speed things up: - var ntsymbol = this_production[0]; // push nonterminal (reduce) - stack[sp] = ntsymbol; - vstack[sp] = yyval.$; - - // goto new state = table[STATE][NONTERMINAL] - newState = table[sstack[sp - 1]][ntsymbol]; - sstack[sp] = newState; - ++sp; - - continue; - - // accept: - case 3: - retval = true; - // Return the `$accept` rule's `$$` result, if available. - // - // Also note that JISON always adds this top-most `$accept` rule (with implicit, - // default, action): - // - // $accept: $end - // %{ $$ = $1; @$ = @1; %} - // - // which, combined with the parse kernel's `$accept` state behaviour coded below, - // will produce the `$$` value output of the rule as the parse result, - // IFF that result is *not* `undefined`. (See also the parser kernel code.) - // - // In code: - // - // %{ - // @$ = @1; // if location tracking support is included - // if (typeof $1 !== 'undefined') - // return $1; - // else - // return true; // the default parse result if the rule actions don't produce anything - // %} - if (typeof yyval.$ !== 'undefined') { - retval = yyval.$; - } - break; - } - - // break out of loop: we accept or fail with error - break; - } - } catch (ex) { - // report exceptions through the parseError callback too, but keep the exception intact - // if it is a known parser or lexer error which has been thrown by parseError() already: - if (ex instanceof this.JisonParserError) { - throw ex; - } - else if (lexer && typeof lexer.JisonLexerError === 'function' && ex instanceof lexer.JisonLexerError) { - throw ex; - } - else { - p = this.constructParseErrorInfo('Parsing aborted due to exception.', ex, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - } - } finally { - retval = this.cleanupAfterParse(retval, true, true); - this.__reentrant_call_depth--; - } - - return retval; -}, -yyError: 1 -}; -parser.originalParseError = parser.parseError; -parser.originalQuoteName = parser.quoteName; - -var XRegExp = require('xregexp'); // for helping out the `%options xregexp` in the lexer - -function encodeRE (s) { - return s.replace(/([.*+?^${}()|\[\]\/\\])/g, '\\$1').replace(/\\\\u([a-fA-F0-9]{4})/g, '\\u$1'); -} - -function prepareString (s) { - // unescape slashes - s = s.replace(/\\\\/g, "\\"); - s = encodeRE(s); - return s; -} - -// convert string value to number or boolean value, when possible -// (and when this is more or less obviously the intent) -// otherwise produce the string itself as value. -function parseValue(v) { - if (v === 'false') { - return false; - } - if (v === 'true') { - return true; - } - // http://stackoverflow.com/questions/175739/is-there-a-built-in-way-in-javascript-to-check-if-a-string-is-a-valid-number - // Note that the `v` check ensures that we do not convert `undefined`, `null` and `''` (empty string!) - if (v && !isNaN(v)) { - var rv = +v; - if (isFinite(rv)) { - return rv; - } - } - return v; -} -/* lexer generated by jison-lex 0.3.4-166 */ -/* - * Returns a Lexer object of the following structure: - * - * Lexer: { - * yy: {} The so-called "shared state" or rather the *source* of it; - * the real "shared state" `yy` passed around to - * the rule actions, etc. is a derivative/copy of this one, - * not a direct reference! - * } - * - * Lexer.prototype: { - * yy: {}, - * EOF: 1, - * ERROR: 2, - * - * JisonLexerError: function(msg, hash), - * - * performAction: function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * where `...` denotes the (optional) additional arguments the user passed to - * `lexer.lex(...)` and specified by way of `%parse-param ...` in the **parser** grammar file - * - * The function parameters and `this` have the following value/meaning: - * - `this` : reference to the `lexer` instance. - * - * - `yy` : a reference to the `yy` "shared state" object which was passed to the lexer - * by way of the `lexer.setInput(str, yy)` API before. - * - * - `yy_` : lexer instance reference used internally. - * - * - `$avoiding_name_collisions` : index of the matched lexer rule (regex), used internally. - * - * - `YY_START`: the current lexer "start condition" state. - * - * - `...` : the extra arguments you specified in the `%parse-param` statement in your - * **parser** grammar definition file and which are passed to the lexer via - * its `lexer.lex(...)` API. - * - * parseError: function(str, hash, ExceptionClass), - * - * constructLexErrorInfo: function(error_message, is_recoverable), - * Helper function. - * Produces a new errorInfo 'hash object' which can be passed into `parseError()`. - * See it's use in this lexer kernel in many places; example usage: - * - * var infoObj = lexer.constructParseErrorInfo('fail!', true); - * var retVal = lexer.parseError(infoObj.errStr, infoObj, lexer.JisonLexerError); - * - * options: { ... lexer %options ... }, - * - * lex: function([args...]), - * Produce one token of lexed input, which was passed in earlier via the `lexer.setInput()` API. - * You MAY use the additional `args...` parameters as per `%parse-param` spec of the **parser** grammar: - * these extra `args...` are passed verbatim to the lexer rules' action code. - * - * cleanupAfterLex: function(do_not_nuke_errorinfos), - * Helper function. - * This helper API is invoked when the parse process has completed. This helper may - * be invoked by user code to ensure the internal lexer gets properly garbage collected. - * - * setInput: function(input, [yy]), - * input: function(), - * unput: function(str), - * more: function(), - * reject: function(), - * less: function(n), - * pastInput: function(n), - * upcomingInput: function(n), - * showPosition: function(), - * test_match: function(regex_match_array, rule_index), - * next: function(...), - * lex: function(...), - * begin: function(condition), - * pushState: function(condition), - * popState: function(), - * topState: function(), - * _currentRules: function(), - * stateStackSize: function(), - * - * options: { ... lexer %options ... }, - * - * performAction: function(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * rules: [...], - * conditions: {associative list: name ==> set}, - * } - * - * - * token location info (`yylloc`): { - * first_line: n, - * last_line: n, - * first_column: n, - * last_column: n, - * range: [start_number, end_number] - * (where the numbers are indexes into the input string, zero-based) - * } - * - * --- - * - * The `parseError` function receives a 'hash' object with these members for lexer errors: - * - * { - * text: (matched text) - * token: (the produced terminal token, if any) - * token_id: (the produced terminal token numeric ID, if any) - * line: (yylineno) - * loc: (yylloc) - * recoverable: (boolean: TRUE when the parser MAY have an error recovery rule - * available for this particular error) - * yy: (object: the current parser internal "shared state" `yy` - * as is also available in the rule actions; this can be used, - * for instance, for advanced error analysis and reporting) - * lexer: (reference to the current lexer instance used by the parser) - * } - * - * while `this` will reference the current lexer instance. - * - * When `parseError` is invoked by the lexer, the default implementation will - * attempt to invoke `yy.parser.parseError()`; when this callback is not provided - * it will try to invoke `yy.parseError()` instead. When that callback is also not - * provided, a `JisonLexerError` exception will be thrown containing the error - * message and `hash`, as constructed by the `constructLexErrorInfo()` API. - * - * Note that the lexer's `JisonLexerError` error class is passed via the - * `ExceptionClass` argument, which is invoked to construct the exception - * instance to be thrown, so technically `parseError` will throw the object - * produced by the `new ExceptionClass(str, hash)` JavaScript expression. - * - * --- - * - * You can specify lexer options by setting / modifying the `.options` object of your Lexer instance. - * These options are available: - * - * (Options are permanent.) - * - * yy: { - * parseError: function(str, hash, ExceptionClass) - * optional: overrides the default `parseError` function. - * } - * - * lexer.options: { - * pre_lex: function() - * optional: is invoked before the lexer is invoked to produce another token. - * `this` refers to the Lexer object. - * post_lex: function(token) { return token; } - * optional: is invoked when the lexer has produced a token `token`; - * this function can override the returned token value by returning another. - * When it does not return any (truthy) value, the lexer will return - * the original `token`. - * `this` refers to the Lexer object. - * - * WARNING: the next set of options are not meant to be changed. They echo the abilities of - * the lexer as per when it was compiled! - * - * ranges: boolean - * optional: `true` ==> token location info will include a .range[] member. - * flex: boolean - * optional: `true` ==> flex-like lexing behaviour where the rules are tested - * exhaustively to find the longest match. - * backtrack_lexer: boolean - * optional: `true` ==> lexer regexes are tested in order and for invoked; - * the lexer terminates the scan when a token is returned by the action code. - * xregexp: boolean - * optional: `true` ==> lexer rule regexes are "extended regex format" requiring the - * `XRegExp` library. When this %option has not been specified at compile time, all lexer - * rule regexes have been written as standard JavaScript RegExp expressions. - * } - */ - - -var lexer = (function () { -// See also: -// http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508 -// but we keep the prototype.constructor and prototype.name assignment lines too for compatibility -// with userland code which might access the derived class in a 'classic' way. -function JisonLexerError(msg, hash) { - Object.defineProperty(this, 'name', { - enumerable: false, - writable: false, - value: 'JisonLexerError' - }); - - if (msg == null) msg = '???'; - - Object.defineProperty(this, 'message', { - enumerable: false, - writable: true, - value: msg - }); - - this.hash = hash; - - var stacktrace; - if (hash && hash.exception instanceof Error) { - var ex2 = hash.exception; - this.message = ex2.message || msg; - stacktrace = ex2.stack; - } - if (!stacktrace) { - if (Error.hasOwnProperty('captureStackTrace')) { // V8 - Error.captureStackTrace(this, this.constructor); - } else { - stacktrace = (new Error(msg)).stack; - } - } - if (stacktrace) { - Object.defineProperty(this, 'stack', { - enumerable: false, - writable: false, - value: stacktrace - }); - } -} - -if (typeof Object.setPrototypeOf === 'function') { - Object.setPrototypeOf(JisonLexerError.prototype, Error.prototype); -} else { - JisonLexerError.prototype = Object.create(Error.prototype); -} -JisonLexerError.prototype.constructor = JisonLexerError; -JisonLexerError.prototype.name = 'JisonLexerError'; - - - - -var lexer = { - - // Code Generator Information Report - // --------------------------------- - // - // Options: - // - // backtracking: .................... false - // location.ranges: ................. true - // location line+column tracking: ... true - // - // - // Forwarded Parser Analysis flags: - // - // uses yyleng: ..................... false - // uses yylineno: ................... false - // uses yytext: ..................... false - // uses yylloc: ..................... false - // uses lexer values: ............... true / true - // location tracking: ............... false - // location assignment: ............. false - // - // - // Lexer Analysis flags: - // - // uses yyleng: ..................... undefined - // uses yylineno: ................... undefined - // uses yytext: ..................... undefined - // uses yylloc: ..................... undefined - // uses ParseError API: ............. undefined - // uses location tracking & editing: undefined - // uses more() API: ................. undefined - // uses unput() API: ................ undefined - // uses reject() API: ............... undefined - // uses less() API: ................. undefined - // uses display APIs pastInput(), upcomingInput(), showPosition(): - // ............................. undefined - // uses describeYYLLOC() API: ....... undefined - // - // --------- END OF REPORT ----------- - - - EOF: 1, - ERROR: 2, - - // JisonLexerError: JisonLexerError, /// <-- injected by the code generator - - // options: {}, /// <-- injected by the code generator - - // yy: ..., /// <-- injected by setInput() - - __currentRuleSet__: null, /// <-- internal rule set cache for the current lexer state - - __error_infos: [], /// INTERNAL USE ONLY: the set of lexErrorInfo objects created since the last cleanup - - __decompressed: false, /// INTERNAL USE ONLY: mark whether the lexer instance has been 'unfolded' completely and is now ready for use - - done: false, /// INTERNAL USE ONLY - _backtrack: false, /// INTERNAL USE ONLY - _input: '', /// INTERNAL USE ONLY - _more: false, /// INTERNAL USE ONLY - _signaled_error_token: false, /// INTERNAL USE ONLY - - conditionStack: [], /// INTERNAL USE ONLY; managed via `pushState()`, `popState()`, `topState()` and `stateStackSize()` - - match: '', /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks input which has been matched so far for the lexer token under construction. `match` is identical to `yytext` except that this one still contains the matched input string after `lexer.performAction()` has been invoked, where userland code MAY have changed/replaced the `yytext` value entirely! - matched: '', /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks entire input which has been matched so far - matches: false, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks RE match result for last (successful) match attempt - yytext: '', /// ADVANCED USE ONLY: tracks input which has been matched so far for the lexer token under construction; this value is transferred to the parser as the 'token value' when the parser consumes the lexer token produced through a call to the `lex()` API. - offset: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks the 'cursor position' in the input string, i.e. the number of characters matched so far - yyleng: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: length of matched input for the token under construction (`yytext`) - yylineno: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: 'line number' at which the token under construction is located - yylloc: null, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks location info (lines + columns) for the token under construction - - /** - INTERNAL USE: construct a suitable error info hash object instance for `parseError`. - - @public - @this {RegExpLexer} - */ - constructLexErrorInfo: function lexer_constructLexErrorInfo(msg, recoverable) { - /** @constructor */ - var pei = { - errStr: msg, - recoverable: !!recoverable, - text: this.match, // This one MAY be empty; userland code should use the `upcomingInput` API to obtain more text which follows the 'lexer cursor position'... - token: null, - line: this.yylineno, - loc: this.yylloc, - yy: this.yy, - lexer: this, - - /** - and make sure the error info doesn't stay due to potential - ref cycle via userland code manipulations. - These would otherwise all be memory leak opportunities! - - Note that only array and object references are nuked as those - constitute the set of elements which can produce a cyclic ref. - The rest of the members is kept intact as they are harmless. - - @public - @this {LexErrorInfo} - */ - destroy: function destructLexErrorInfo() { - // remove cyclic references added to error info: - // info.yy = null; - // info.lexer = null; - // ... - var rec = !!this.recoverable; - for (var key in this) { - if (this.hasOwnProperty(key) && typeof key === 'object') { - this[key] = undefined; - } - } - this.recoverable = rec; - } - }; - // track this instance so we can `destroy()` it once we deem it superfluous and ready for garbage collection! - this.__error_infos.push(pei); - return pei; - }, - - /** - handler which is invoked when a lexer error occurs. - - @public - @this {RegExpLexer} - */ - parseError: function lexer_parseError(str, hash, ExceptionClass) { - if (!ExceptionClass) { - ExceptionClass = this.JisonLexerError; - } - if (this.yy.parser && typeof this.yy.parser.parseError === 'function') { - return this.yy.parser.parseError(str, hash, ExceptionClass) || this.ERROR; - } else if (typeof this.yy.parseError === 'function') { - return this.yy.parseError(str, hash, ExceptionClass) || this.ERROR; - } else { - throw new ExceptionClass(str, hash); - } - }, - - /** - method which implements `yyerror(str, ...args)` functionality for use inside lexer actions. - - @public - @this {RegExpLexer} - */ - yyerror: function yyError(str /*, ...args */) { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': ' + str, this.options.lexerErrorsAreRecoverable); - - // Add any extra args to the hash under the name `extra_error_attributes`: - var args = Array.prototype.slice.call(arguments, 1); - if (args.length) { - hash.extra_error_attributes = args; - } - - return (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - }, - - /** - final cleanup function for when we have completed lexing the input; - make it an API so that external code can use this one once userland - code has decided it's time to destroy any lingering lexer error - hash object instances and the like: this function helps to clean - up these constructs, which *may* carry cyclic references which would - otherwise prevent the instances from being properly and timely - garbage-collected, i.e. this function helps prevent memory leaks! - - @public - @this {RegExpLexer} - */ - cleanupAfterLex: function lexer_cleanupAfterLex(do_not_nuke_errorinfos) { - var rv; - - // prevent lingering circular references from causing memory leaks: - this.setInput('', {}); - - // nuke the error hash info instances created during this run. - // Userland code must COPY any data/references - // in the error hash instance(s) it is more permanently interested in. - if (!do_not_nuke_errorinfos) { - for (var i = this.__error_infos.length - 1; i >= 0; i--) { - var el = this.__error_infos[i]; - if (el && typeof el.destroy === 'function') { - el.destroy(); - } - } - this.__error_infos.length = 0; - } - - return this; - }, - - /** - clear the lexer token context; intended for internal use only - - @public - @this {RegExpLexer} - */ - clear: function lexer_clear() { - this.yytext = ''; - this.yyleng = 0; - this.match = ''; - this.matches = false; - this._more = false; - this._backtrack = false; - - var col = this.yylloc ? this.yylloc.last_column : 0; - this.yylloc = { - first_line: this.yylineno + 1, - first_column: col, - last_line: this.yylineno + 1, - last_column: col, - - range: (this.options.ranges ? [this.offset, this.offset] : undefined) - }; - }, - - /** - resets the lexer, sets new input - - @public - @this {RegExpLexer} - */ - setInput: function lexer_setInput(input, yy) { - this.yy = yy || this.yy || {}; - - // also check if we've fully initialized the lexer instance, - // including expansion work to be done to go from a loaded - // lexer to a usable lexer: - if (!this.__decompressed) { - // step 1: decompress the regex list: - var rules = this.rules; - for (var i = 0, len = rules.length; i < len; i++) { - var rule_re = rules[i]; - - // compression: is the RE an xref to another RE slot in the rules[] table? - if (typeof rule_re === 'number') { - rules[i] = rules[rule_re]; - } - } - - // step 2: unfold the conditions[] set to make these ready for use: - var conditions = this.conditions; - for (var k in conditions) { - var spec = conditions[k]; - - var rule_ids = spec.rules; - - var len = rule_ids.length; - var rule_regexes = new Array(len + 1); // slot 0 is unused; we use a 1-based index approach here to keep the hottest code in `lexer_next()` fast and simple! - var rule_new_ids = new Array(len + 1); - - for (var i = 0; i < len; i++) { - var idx = rule_ids[i]; - var rule_re = rules[idx]; - rule_regexes[i + 1] = rule_re; - rule_new_ids[i + 1] = idx; - } - - spec.rules = rule_new_ids; - spec.__rule_regexes = rule_regexes; - spec.__rule_count = len; - } - - this.__decompressed = true; - } - - this._input = input || ''; - this.clear(); - this._signaled_error_token = false; - this.done = false; - this.yylineno = 0; - this.matched = ''; - this.conditionStack = ['INITIAL']; - this.__currentRuleSet__ = null; - this.yylloc = { - first_line: 1, - first_column: 0, - last_line: 1, - last_column: 0, - - range: (this.options.ranges ? [0, 0] : undefined) - }; - this.offset = 0; - return this; - }, - - /** - push a new input into the lexer and activate it: - the old input position is stored and will be resumed - once this new input has been consumed. - - Use this API to help implement C-preprocessor-like - `#include` statements. - - Available options: - - - `emit_EOF_at_end` : {int} the `EOF`-like token to emit - when the new input is consumed: use - this to mark the end of the new input - in the parser grammar. zero/falsey - token value means no end marker token - will be emitted before the lexer - resumes reading from the previous input. - - @public - @this {RegExpLexer} - */ - pushInput: function lexer_pushInput(input, label, options) { - options = options || {}; - - this._input = input || ''; - this.clear(); - // this._signaled_error_token = false; - this.done = false; - this.yylineno = 0; - this.matched = ''; - // this.conditionStack = ['INITIAL']; - // this.__currentRuleSet__ = null; - this.yylloc = { - first_line: 1, - first_column: 0, - last_line: 1, - last_column: 0, - - range: (this.options.ranges ? [0, 0] : undefined) - }; - this.offset = 0; - return this; - }, - - /** - consumes and returns one char from the input - - @public - @this {RegExpLexer} - */ - input: function lexer_input() { - if (!this._input) { - //this.done = true; -- don't set `done` as we want the lex()/next() API to be able to produce one custom EOF token match after this anyhow. (lexer can match special <> tokens and perform user action code for a <> match, but only does so *once*) - return null; - } - var ch = this._input[0]; - this.yytext += ch; - this.yyleng++; - this.offset++; - this.match += ch; - this.matched += ch; - // Count the linenumber up when we hit the LF (or a stand-alone CR). - // On CRLF, the linenumber is incremented when you fetch the CR or the CRLF combo - // and we advance immediately past the LF as well, returning both together as if - // it was all a single 'character' only. - var slice_len = 1; - var lines = false; - if (ch === '\n') { - lines = true; - } else if (ch === '\r') { - lines = true; - var ch2 = this._input[1]; - if (ch2 === '\n') { - slice_len++; - ch += ch2; - this.yytext += ch2; - this.yyleng++; - this.offset++; - this.match += ch2; - this.matched += ch2; - if (this.options.ranges) { - this.yylloc.range[1]++; - } - } - } - if (lines) { - this.yylineno++; - this.yylloc.last_line++; - this.yylloc.last_column = 0; - } else { - this.yylloc.last_column++; - } - if (this.options.ranges) { - this.yylloc.range[1]++; - } - - this._input = this._input.slice(slice_len); - return ch; - }, - - /** - unshifts one char (or an entire string) into the input - - @public - @this {RegExpLexer} - */ - unput: function lexer_unput(ch) { - var len = ch.length; - var lines = ch.split(/(?:\r\n?|\n)/g); - - this._input = ch + this._input; - this.yytext = this.yytext.substr(0, this.yytext.length - len); - this.yyleng = this.yytext.length; - this.offset -= len; - this.match = this.match.substr(0, this.match.length - len); - this.matched = this.matched.substr(0, this.matched.length - len); - - if (lines.length > 1) { - this.yylineno -= lines.length - 1; - - this.yylloc.last_line = this.yylineno + 1; - var pre = this.match; - var pre_lines = pre.split(/(?:\r\n?|\n)/g); - if (pre_lines.length === 1) { - pre = this.matched; - pre_lines = pre.split(/(?:\r\n?|\n)/g); - } - this.yylloc.last_column = pre_lines[pre_lines.length - 1].length; - } else { - this.yylloc.last_column -= len; - } - - if (this.options.ranges) { - this.yylloc.range[1] = this.yylloc.range[0] + this.yyleng; - } - this.done = false; - return this; - }, - - /** - cache matched text and append it on next action - - @public - @this {RegExpLexer} - */ - more: function lexer_more() { - this._more = true; - return this; - }, - - /** - signal the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead. - - @public - @this {RegExpLexer} - */ - reject: function lexer_reject() { - if (this.options.backtrack_lexer) { - this._backtrack = true; - } else { - // when the `parseError()` call returns, we MUST ensure that the error is registered. - // We accomplish this by signaling an 'error' token to be produced for the current - // `.lex()` run. - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).' + pos_str, false); - this._signaled_error_token = (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - } - return this; - }, - - /** - retain first n characters of the match - - @public - @this {RegExpLexer} - */ - less: function lexer_less(n) { - return this.unput(this.match.slice(n)); - }, - - /** - return (part of the) already matched input, i.e. for error messages. - - Limit the returned string length to `maxSize` (default: 20). - - Limit the returned string to the `maxLines` number of lines of input (default: 1). - - Negative limit values equal *unlimited*. - - @public - @this {RegExpLexer} - */ - pastInput: function lexer_pastInput(maxSize, maxLines) { - var past = this.matched.substring(0, this.matched.length - this.match.length); - if (maxSize < 0) - maxSize = past.length; - else if (!maxSize) - maxSize = 20; - if (maxLines < 0) - maxLines = past.length; // can't ever have more input lines than this! - else if (!maxLines) - maxLines = 1; - // `substr` anticipation: treat \r\n as a single character and take a little - // more than necessary so that we can still properly check against maxSize - // after we've transformed and limited the newLines in here: - past = past.substr(-maxSize * 2 - 2); - // now that we have a significantly reduced string to process, transform the newlines - // and chop them, then limit them: - var a = past.replace(/\r\n|\r/g, '\n').split('\n'); - a = a.slice(-maxLines); - past = a.join('\n'); - // When, after limiting to maxLines, we still have too much to return, - // do add an ellipsis prefix... - if (past.length > maxSize) { - past = '...' + past.substr(-maxSize); - } - return past; - }, - - /** - return (part of the) upcoming input, i.e. for error messages. - - Limit the returned string length to `maxSize` (default: 20). - - Limit the returned string to the `maxLines` number of lines of input (default: 1). - - Negative limit values equal *unlimited*. - - @public - @this {RegExpLexer} - */ - upcomingInput: function lexer_upcomingInput(maxSize, maxLines) { - var next = this.match; - if (maxSize < 0) - maxSize = next.length + this._input.length; - else if (!maxSize) - maxSize = 20; - if (maxLines < 0) - maxLines = maxSize; // can't ever have more input lines than this! - else if (!maxLines) - maxLines = 1; - // `substring` anticipation: treat \r\n as a single character and take a little - // more than necessary so that we can still properly check against maxSize - // after we've transformed and limited the newLines in here: - if (next.length < maxSize * 2 + 2) { - next += this._input.substring(0, maxSize * 2 + 2); // substring is faster on Chrome/V8 - } - // now that we have a significantly reduced string to process, transform the newlines - // and chop them, then limit them: - var a = next.replace(/\r\n|\r/g, '\n').split('\n'); - a = a.slice(0, maxLines); - next = a.join('\n'); - // When, after limiting to maxLines, we still have too much to return, - // do add an ellipsis postfix... - if (next.length > maxSize) { - next = next.substring(0, maxSize) + '...'; - } - return next; - }, - - /** - return a string which displays the character position where the lexing error occurred, i.e. for error messages - - @public - @this {RegExpLexer} - */ - showPosition: function lexer_showPosition(maxPrefix, maxPostfix) { - var pre = this.pastInput(maxPrefix).replace(/\s/g, ' '); - var c = new Array(pre.length + 1).join('-'); - return pre + this.upcomingInput(maxPostfix).replace(/\s/g, ' ') + '\n' + c + '^'; - }, - - /** - helper function, used to produce a human readable description as a string, given - the input `yylloc` location object. - - Set `display_range_too` to TRUE to include the string character index position(s) - in the description if the `yylloc.range` is available. - - @public - @this {RegExpLexer} - */ - describeYYLLOC: function lexer_describe_yylloc(yylloc, display_range_too) { - var l1 = yylloc.first_line; - var l2 = yylloc.last_line; - var o1 = yylloc.first_column; - var o2 = yylloc.last_column; - var dl = l2 - l1; - var d_o = o2 - o1; - var rv; - if (dl === 0) { - rv = 'line ' + l1 + ', '; - if (d_o === 1) { - rv += 'column ' + o1; - } else { - rv += 'columns ' + o1 + ' .. ' + o2; - } - } else { - rv = 'lines ' + l1 + '(column ' + o1 + ') .. ' + l2 + '(column ' + o2 + ')'; - } - if (yylloc.range && display_range_too) { - var r1 = yylloc.range[0]; - var r2 = yylloc.range[1] - 1; - if (r2 === r1) { - rv += ' {String Offset: ' + r1 + '}'; - } else { - rv += ' {String Offset range: ' + r1 + ' .. ' + r2 + '}'; - } - } - return rv; - // return JSON.stringify(yylloc); - }, - - /** - test the lexed token: return FALSE when not a match, otherwise return token. - - `match` is supposed to be an array coming out of a regex match, i.e. `match[0]` - contains the actually matched text string. - - Also move the input cursor forward and update the match collectors: - - - `yytext` - - `yyleng` - - `match` - - `matches` - - `yylloc` - - `offset` - - @public - @this {RegExpLexer} - */ - test_match: function lexer_test_match(match, indexed_rule) { - var token, - lines, - backup, - match_str, - match_str_len; - - if (this.options.backtrack_lexer) { - // save context - backup = { - yylineno: this.yylineno, - yylloc: { - first_line: this.yylloc.first_line, - last_line: this.yylloc.last_line, - first_column: this.yylloc.first_column, - last_column: this.yylloc.last_column, - - range: (this.options.ranges ? this.yylloc.range.slice(0) : undefined) - }, - yytext: this.yytext, - match: this.match, - matches: this.matches, - matched: this.matched, - yyleng: this.yyleng, - offset: this.offset, - _more: this._more, - _input: this._input, - //_signaled_error_token: this._signaled_error_token, - yy: this.yy, - conditionStack: this.conditionStack.slice(0), - done: this.done - }; - } - - match_str = match[0]; - match_str_len = match_str.length; - // if (match_str.indexOf('\n') !== -1 || match_str.indexOf('\r') !== -1) { - lines = match_str.split(/(?:\r\n?|\n)/g); - if (lines.length > 1) { - this.yylineno += lines.length - 1; - - this.yylloc.last_line = this.yylineno + 1, - this.yylloc.last_column = lines[lines.length - 1].length; - } else { - this.yylloc.last_column += match_str_len; - } - // } - this.yytext += match_str; - this.match += match_str; - this.matches = match; - this.yyleng = this.yytext.length; - if (this.options.ranges) { - this.yylloc.range[1] += match_str_len; - } - // previous lex rules MAY have invoked the `more()` API rather than producing a token: - // those rules will already have moved this `offset` forward matching their match lengths, - // hence we must only add our own match length now: - this.offset += match_str_len; - this._more = false; - this._backtrack = false; - this._input = this._input.slice(match_str_len); - this.matched += match_str; - - // calling this method: - // - // function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START) {...} - token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1] /* = YY_START */); - // otherwise, when the action codes are all simple return token statements: - //token = this.simpleCaseActionClusters[indexed_rule]; - - if (this.done && this._input) { - this.done = false; - } - if (token) { - return token; - } else if (this._backtrack) { - // recover context - for (var k in backup) { - this[k] = backup[k]; - } - this.__currentRuleSet__ = null; - return false; // rule action called reject() implying the next rule should be tested instead. - } else if (this._signaled_error_token) { - // produce one 'error' token as `.parseError()` in `reject()` did not guarantee a failure signal by throwing an exception! - token = this._signaled_error_token; - this._signaled_error_token = false; - return token; - } - return false; - }, - - /** - return next match in input - - @public - @this {RegExpLexer} - */ - next: function lexer_next() { - if (this.done) { - this.clear(); - return this.EOF; - } - if (!this._input) { - this.done = true; - } - - var token, - match, - tempMatch, - index; - if (!this._more) { - this.clear(); - } - var spec = this.__currentRuleSet__; - if (!spec) { - // Update the ruleset cache as we apparently encountered a state change or just started lexing. - // The cache is set up for fast lookup -- we assume a lexer will switch states much less often than it will - // invoke the `lex()` token-producing API and related APIs, hence caching the set for direct access helps - // speed up those activities a tiny bit. - spec = this.__currentRuleSet__ = this._currentRules(); - // Check whether a *sane* condition has been pushed before: this makes the lexer robust against - // user-programmer bugs such as https://github.com/zaach/jison-lex/issues/19 - if (!spec || !spec.rules) { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Internal lexer engine error' + lineno_msg + ': The lex grammar programmer pushed a non-existing condition name "' + this.topState() + '"; this is a fatal error and should be reported to the application programmer team!' + pos_str, false); - // produce one 'error' token until this situation has been resolved, most probably by parse termination! - return (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - } - } - - var rule_ids = spec.rules; - //var dispatch = spec.__dispatch_lut; - var regexes = spec.__rule_regexes; - var len = spec.__rule_count; - - // Note: the arrays are 1-based, while `len` itself is a valid index, - // hence the non-standard less-or-equal check in the next loop condition! - for (var i = 1; i <= len; i++) { - tempMatch = this._input.match(regexes[i]); - if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { - match = tempMatch; - index = i; - if (this.options.backtrack_lexer) { - token = this.test_match(tempMatch, rule_ids[i]); - if (token !== false) { - return token; - } else if (this._backtrack) { - match = undefined; - continue; // rule action called reject() implying a rule MISmatch. - } else { - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - } else if (!this.options.flex) { - break; - } - } - } - if (match) { - token = this.test_match(match, rule_ids[index]); - if (token !== false) { - return token; - } - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - if (!this._input) { - this.done = true; - this.clear(); - return this.EOF; - } else { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': Unrecognized text.' + pos_str, this.options.lexerErrorsAreRecoverable); - token = (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - if (token === this.ERROR) { - // we can try to recover from a lexer error that `parseError()` did not 'recover' for us - // by moving forward at least one character at a time: - if (!this.match.length) { - this.input(); - } - } - return token; - } - }, - - /** - return next match that has a token - - @public - @this {RegExpLexer} - */ - lex: function lexer_lex() { - var r; - // allow the PRE/POST handlers set/modify the return token for maximum flexibility of the generated lexer: - if (typeof this.options.pre_lex === 'function') { - r = this.options.pre_lex.call(this); - } - while (!r) { - r = this.next(); - } - if (typeof this.options.post_lex === 'function') { - // (also account for a userdef function which does not return any value: keep the token as is) - r = this.options.post_lex.call(this, r) || r; - } - return r; - }, - - /** - backwards compatible alias for `pushState()`; - the latter is symmetrical with `popState()` and we advise to use - those APIs in any modern lexer code, rather than `begin()`. - - @public - @this {RegExpLexer} - */ - begin: function lexer_begin(condition) { - return this.pushState(condition); - }, - - /** - activates a new lexer condition state (pushes the new lexer condition state onto the condition stack) - - @public - @this {RegExpLexer} - */ - pushState: function lexer_pushState(condition) { - this.conditionStack.push(condition); - this.__currentRuleSet__ = null; - return this; - }, - - /** - pop the previously active lexer condition state off the condition stack - - @public - @this {RegExpLexer} - */ - popState: function lexer_popState() { - var n = this.conditionStack.length - 1; - if (n > 0) { - this.__currentRuleSet__ = null; - return this.conditionStack.pop(); - } else { - return this.conditionStack[0]; - } - }, - - /** - return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available - - @public - @this {RegExpLexer} - */ - topState: function lexer_topState(n) { - n = this.conditionStack.length - 1 - Math.abs(n || 0); - if (n >= 0) { - return this.conditionStack[n]; - } else { - return 'INITIAL'; - } - }, - - /** - (internal) determine the lexer rule set which is active for the currently active lexer condition state - - @public - @this {RegExpLexer} - */ - _currentRules: function lexer__currentRules() { - if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) { - return this.conditions[this.conditionStack[this.conditionStack.length - 1]]; - } else { - return this.conditions['INITIAL']; - } - }, - - /** - return the number of states currently on the stack - - @public - @this {RegExpLexer} - */ - stateStackSize: function lexer_stateStackSize() { - return this.conditionStack.length; - }, - options: { - xregexp: true, - ranges: true, - trackPosition: true, - easy_keyword_rules: true -}, - JisonLexerError: JisonLexerError, - performAction: function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START) { - -var YYSTATE = YY_START; -switch($avoiding_name_collisions) { -case 7 : -/*! Conditions:: action */ -/*! Rule:: \{ */ - yy.depth++; return 3; -break; -case 8 : -/*! Conditions:: action */ -/*! Rule:: \} */ - - if (yy.depth == 0) { - this.pushState('trail'); - } else { - yy.depth--; - } - return 4; - -break; -case 10 : -/*! Conditions:: conditions */ -/*! Rule:: > */ - this.popState(); return 6; -break; -case 13 : -/*! Conditions:: rules */ -/*! Rule:: {BR}+ */ - /* empty */ -break; -case 14 : -/*! Conditions:: rules */ -/*! Rule:: {WS}+{BR}+ */ - /* empty */ -break; -case 15 : -/*! Conditions:: rules */ -/*! Rule:: {WS}+ */ - this.pushState('indented'); -break; -case 16 : -/*! Conditions:: rules */ -/*! Rule:: %% */ - this.pushState('code'); return 19; -break; -case 17 : -/*! Conditions:: rules */ -/*! Rule:: {ANY_LITERAL_CHAR}+ */ - - // accept any non-regex, non-lex, non-string-delim, - // non-escape-starter, non-space character as-is - return 36; - -break; -case 20 : -/*! Conditions:: options */ -/*! Rule:: "{DOUBLEQUOTED_STRING_CONTENT}" */ - yy_.yytext = this.matches[1]; return 39; // value is always a string type -break; -case 21 : -/*! Conditions:: options */ -/*! Rule:: '{QUOTED_STRING_CONTENT}' */ - yy_.yytext = this.matches[1]; return 39; // value is always a string type -break; -case 22 : -/*! Conditions:: INITIAL start_condition trail rules macro path options */ -/*! Rule:: \/\/[^\r\n]* */ - /* skip single-line comment */ -break; -case 23 : -/*! Conditions:: INITIAL start_condition trail rules macro path options */ -/*! Rule:: \/\*(.|\n|\r)*?\*\/ */ - /* skip multi-line comment */ -break; -case 25 : -/*! Conditions:: options */ -/*! Rule:: {BR}{WS}+(?=\S) */ - /* skip leading whitespace on the next line of input, when followed by more options */ -break; -case 26 : -/*! Conditions:: options */ -/*! Rule:: {BR} */ - this.popState(); return 38; -break; -case 27 : -/*! Conditions:: options */ -/*! Rule:: {WS}+ */ - /* skip whitespace */ -break; -case 29 : -/*! Conditions:: start_condition */ -/*! Rule:: {BR}+ */ - this.popState(); -break; -case 30 : -/*! Conditions:: start_condition */ -/*! Rule:: {WS}+ */ - /* empty */ -break; -case 31 : -/*! Conditions:: trail */ -/*! Rule:: {WS}*{BR}+ */ - this.pushState('rules'); -break; -case 32 : -/*! Conditions:: indented */ -/*! Rule:: \{ */ - yy.depth = 0; this.pushState('action'); return 3; -break; -case 33 : -/*! Conditions:: indented */ -/*! Rule:: %\{(?:.|{BR})*?%\} */ - this.pushState('trail'); yy_.yytext = yy_.yytext.substr(2, yy_.yyleng - 4); return 23; -break; -case 34 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: %\{(?:.|{BR})*?%\} */ - yy_.yytext = yy_.yytext.substr(2, yy_.yyleng - 4); return 23; -break; -case 35 : -/*! Conditions:: indented */ -/*! Rule:: %include\b */ - - // This is an include instruction in place of an action: - // thanks to the `.+` rule immediately below we need to semi-duplicate - // the `%include` token recognition here vs. the almost-identical rule for the same - // further below. - // There's no real harm as we need to do something special in this case anyway: - // push 2 (two!) conditions. - // - // (Anecdotal: to find that we needed to place this almost-copy here to make the test grammar - // parse correctly took several hours as the debug facilities were - and are - too meager to - // quickly diagnose the problem while we hadn't. So the code got littered with debug prints - // and finally it hit me what the *F* went wrong, after which I saw I needed to add *this* rule!) - - // first push the 'trail' condition which will be the follow-up after we're done parsing the path parameter... - this.pushState('trail'); - // then push the immediate need: the 'path' condition. - this.pushState('path'); - return 41; - -break; -case 36 : -/*! Conditions:: indented */ -/*! Rule:: .* */ - this.popState(); return 23; -break; -case 37 : -/*! Conditions:: INITIAL */ -/*! Rule:: {ID} */ - this.pushState('macro'); return 20; -break; -case 38 : -/*! Conditions:: macro */ -/*! Rule:: {BR}+ */ - this.popState('macro'); -break; -case 39 : -/*! Conditions:: macro */ -/*! Rule:: {ANY_LITERAL_CHAR}+ */ - - // accept any non-regex, non-lex, non-string-delim, - // non-escape-starter, non-space character as-is - return 36; - -break; -case 40 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: {BR}+ */ - /* empty */ -break; -case 41 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: \s+ */ - /* empty */ -break; -case 42 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: "{DOUBLEQUOTED_STRING_CONTENT}" */ - yy_.yytext = yy_.yytext.replace(/\\"/g,'"'); return 35; -break; -case 43 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: '{QUOTED_STRING_CONTENT}' */ - yy_.yytext = yy_.yytext.replace(/\\'/g,"'"); return 35; -break; -case 44 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: \[ */ - this.pushState('set'); return 30; -break; -case 57 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: < */ - this.pushState('conditions'); return 5; -break; -case 58 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: \/! */ - return 28; // treated as `(?!atom)` -break; -case 59 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: \/ */ - return 14; // treated as `(?=atom)` -break; -case 61 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: \\. */ - yy_.yytext = yy_.yytext.replace(/^\\/g, ''); return 33; -break; -case 64 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: %options\b */ - this.pushState('options'); return 37; -break; -case 65 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: %s\b */ - this.pushState('start_condition'); return 21; -break; -case 66 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: %x\b */ - this.pushState('start_condition'); return 22; -break; -case 67 : -/*! Conditions:: INITIAL trail code */ -/*! Rule:: %include\b */ - this.pushState('path'); return 41; -break; -case 68 : -/*! Conditions:: INITIAL rules trail code */ -/*! Rule:: %{NAME}([^\r\n]*) */ - - /* ignore unrecognized decl */ - var l0 = Math.max(0, yy_.yylloc.last_column - yy_.yylloc.first_column); - var l2 = 19; - var l1 = Math.min(79 - 4 - l0 - l2, yy_.yylloc.first_column, 0); - console.warn('LEX: ignoring unsupported lexer option ' + dquote(yy_.yytext) + ' while lexing in ' + this.topState() + ' state:\n' + indent(this.showPosition(l1, l2), 4), this._input, ' /////// ', this.matched); - // this.pushState('options'); - yy_.yytext = [ - this.matches[1], // {NAME} - this.matches[2].trim() // optional value/parameters - ]; - return 24; - -break; -case 69 : -/*! Conditions:: indented trail rules macro INITIAL */ -/*! Rule:: %% */ - this.pushState('rules'); return 19; -break; -case 77 : -/*! Conditions:: set */ -/*! Rule:: \] */ - this.popState('set'); return 31; -break; -case 79 : -/*! Conditions:: code */ -/*! Rule:: [^\r\n]+ */ - return 43; // the bit of CODE just before EOF... -break; -case 80 : -/*! Conditions:: path */ -/*! Rule:: {BR} */ - this.popState(); this.unput(yy_.yytext); -break; -case 81 : -/*! Conditions:: path */ -/*! Rule:: "{DOUBLEQUOTED_STRING_CONTENT}" */ - yy_.yytext = this.matches[1]; this.popState(); return 42; -break; -case 82 : -/*! Conditions:: path */ -/*! Rule:: '{QUOTED_STRING_CONTENT}' */ - yy_.yytext = this.matches[1]; this.popState(); return 42; -break; -case 83 : -/*! Conditions:: path */ -/*! Rule:: {WS}+ */ - // skip whitespace in the line -break; -case 84 : -/*! Conditions:: path */ -/*! Rule:: [^\s\r\n]+ */ - this.popState(); return 42; -break; -case 85 : -/*! Conditions:: macro rules */ -/*! Rule:: . */ - - /* b0rk on bad characters */ - var l0 = Math.max(0, yy_.yylloc.last_column - yy_.yylloc.first_column); - var l2 = 39; - var l1 = Math.min(79 - 4 - l0 - l2, yy_.yylloc.first_column, 0); - var rules = (this.topState() === 'macro' ? 'macro\'s' : this.topState()); - var pos_str = this.showPosition(l1, l2); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n\n Offending input:\n' + indent(pos_str, 4); - } - yy_.yyerror('unsupported lexer input: ' + dquote(yy_.yytext) + ' while lexing ' + rules + '\n (i.e. jison lex regexes).\n\n NOTE: When you want the input ' + dquote(yy_.yytext) + ' to be interpreted as a literal part\n of a lex rule regex, you MUST enclose it in double or single quotes,\n e.g. as shown in this error message just before. If not, then know\n that this is not accepted as a regex operator here in\n jison-lex ' + rules + '.' + pos_str); - -break; -case 86 : -/*! Conditions:: * */ -/*! Rule:: . */ - - /* b0rk on bad characters */ - var l0 = Math.max(0, yy_.yylloc.last_column - yy_.yylloc.first_column); - var l2 = 39; - var l1 = Math.min(79 - 4 - l0 - l2, yy_.yylloc.first_column, 0); - var pos_str = this.showPosition(l1, l2); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n\n Offending input:\n' + indent(pos_str, 4); - } - yy_.yyerror('unsupported lexer input: ' + dquote(yy_.yytext) + ' while lexing in ' + dquote(this.topState()) + ' state.' + pos_str); - -break; -default: - return this.simpleCaseActionClusters[$avoiding_name_collisions]; -} -}, - simpleCaseActionClusters: { - - /*! Conditions:: action */ - /*! Rule:: \/\*(.|\n|\r)*?\*\/ */ - 0 : 26, - /*! Conditions:: action */ - /*! Rule:: \/\/.* */ - 1 : 26, - /*! Conditions:: action */ - /*! Rule:: \/[^ /]*?['"{}][^ ]*?\/ */ - 2 : 26, - /*! Conditions:: action */ - /*! Rule:: "(\\\\|\\"|[^"])*" */ - 3 : 26, - /*! Conditions:: action */ - /*! Rule:: '(\\\\|\\'|[^'])*' */ - 4 : 26, - /*! Conditions:: action */ - /*! Rule:: [/"'][^{}/"']+ */ - 5 : 26, - /*! Conditions:: action */ - /*! Rule:: [^{}/"']+ */ - 6 : 26, - /*! Conditions:: conditions */ - /*! Rule:: {NAME} */ - 9 : 20, - /*! Conditions:: conditions */ - /*! Rule:: , */ - 11 : 8, - /*! Conditions:: conditions */ - /*! Rule:: \* */ - 12 : 7, - /*! Conditions:: options */ - /*! Rule:: {NAME} */ - 18 : 20, - /*! Conditions:: options */ - /*! Rule:: = */ - 19 : 18, - /*! Conditions:: options */ - /*! Rule:: [^\s\r\n]+ */ - 24 : 40, - /*! Conditions:: start_condition */ - /*! Rule:: {ID} */ - 28 : 25, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \| */ - 45 : 9, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \(\?: */ - 46 : 27, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \(\?= */ - 47 : 27, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \(\?! */ - 48 : 27, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \( */ - 49 : 10, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \) */ - 50 : 11, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \+ */ - 51 : 12, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \* */ - 52 : 7, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \? */ - 53 : 13, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \^ */ - 54 : 16, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: , */ - 55 : 8, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: <> */ - 56 : 17, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \\([0-7]{1,3}|[rfntvsSbBwWdD\\*+()${}|[\]\/.^?]|c[A-Z]|x[0-9A-F]{2}|u[a-fA-F0-9]{4}) */ - 60 : 33, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \$ */ - 62 : 17, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \. */ - 63 : 15, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \{\d+(,\s?\d+|,)?\} */ - 70 : 34, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \{{ID}\} */ - 71 : 29, - /*! Conditions:: set options */ - /*! Rule:: \{{ID}\} */ - 72 : 29, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \{ */ - 73 : 3, - /*! Conditions:: indented trail rules macro INITIAL */ - /*! Rule:: \} */ - 74 : 4, - /*! Conditions:: set */ - /*! Rule:: (?:\\\\|\\\]|[^\]{])+ */ - 75 : 32, - /*! Conditions:: set */ - /*! Rule:: \{ */ - 76 : 32, - /*! Conditions:: code */ - /*! Rule:: [^\r\n]*(\r|\n)+ */ - 78 : 43, - /*! Conditions:: * */ - /*! Rule:: $ */ - 87 : 1 -}, - rules: [ - /^(?:\/\*(.|\n|\r)*?\*\/)/, -/^(?:\/\/.*)/, -/^(?:\/[^ \/]*?['"{}][^ ]*?\/)/, -/^(?:"(\\\\|\\"|[^"])*")/, -/^(?:'(\\\\|\\'|[^'])*')/, -/^(?:[\/"'][^{}\/"']+)/, -/^(?:[^{}\/"']+)/, -/^(?:\{)/, -/^(?:\})/, -new XRegExp("^(?:([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}\\-_]*(?:[\\p{Alphabetic}\\p{Number}_]))?))", ""), -/^(?:>)/, -/^(?:,)/, -/^(?:\*)/, -/^(?:(\r\n|\n|\r)+)/, -/^(?:([^\S\n\r])+(\r\n|\n|\r)+)/, -/^(?:([^\S\n\r])+)/, -/^(?:%%)/, -/^(?:([^\s!"$%'-,.\/:-?\[-\^{-}])+)/, -new XRegExp("^(?:([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}\\-_]*(?:[\\p{Alphabetic}\\p{Number}_]))?))", ""), -/^(?:=)/, -/^(?:"((?:\\"|\\[^"]|[^"\\])*)")/, -/^(?:'((?:\\'|\\[^']|[^'\\])*)')/, -/^(?:\/\/[^\r\n]*)/, -/^(?:\/\*(.|\n|\r)*?\*\/)/, -/^(?:\S+)/, -/^(?:(\r\n|\n|\r)([^\S\n\r])+(?=\S))/, -/^(?:(\r\n|\n|\r))/, -/^(?:([^\S\n\r])+)/, -new XRegExp("^(?:([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}_])*))", ""), -/^(?:(\r\n|\n|\r)+)/, -/^(?:([^\S\n\r])+)/, -/^(?:([^\S\n\r])*(\r\n|\n|\r)+)/, -/^(?:\{)/, -/^(?:%\{(?:.|(\r\n|\n|\r))*?%\})/, -/^(?:%\{(?:.|(\r\n|\n|\r))*?%\})/, -/^(?:%include\b)/, -/^(?:.*)/, -new XRegExp("^(?:([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}_])*))", ""), -/^(?:(\r\n|\n|\r)+)/, -/^(?:([^\s!"$%'-,.\/:-?\[-\^{-}])+)/, -/^(?:(\r\n|\n|\r)+)/, -/^(?:\s+)/, -/^(?:"((?:\\"|\\[^"]|[^"\\])*)")/, -/^(?:'((?:\\'|\\[^']|[^'\\])*)')/, -/^(?:\[)/, -/^(?:\|)/, -/^(?:\(\?:)/, -/^(?:\(\?=)/, -/^(?:\(\?!)/, -/^(?:\()/, -/^(?:\))/, -/^(?:\+)/, -/^(?:\*)/, -/^(?:\?)/, -/^(?:\^)/, -/^(?:,)/, -/^(?:<>)/, -/^(?:<)/, -/^(?:\/!)/, -/^(?:\/)/, -/^(?:\\([0-7]{1,3}|[$(-+.\/?BDSW\[-\^bdfnr-tvw{-}]|c[A-Z]|x[\dA-F]{2}|u[\dA-Fa-f]{4}))/, -/^(?:\\.)/, -/^(?:\$)/, -/^(?:\.)/, -/^(?:%options\b)/, -/^(?:%s\b)/, -/^(?:%x\b)/, -/^(?:%include\b)/, -new XRegExp("^(?:%([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}\\-_]*(?:[\\p{Alphabetic}\\p{Number}_]))?)([^\\n\\r]*))", ""), -/^(?:%%)/, -/^(?:\{\d+(,\s?\d+|,)?\})/, -new XRegExp("^(?:\\{([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}_])*)\\})", ""), -new XRegExp("^(?:\\{([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}_])*)\\})", ""), -/^(?:\{)/, -/^(?:\})/, -/^(?:(?:\\\\|\\\]|[^\]{])+)/, -/^(?:\{)/, -/^(?:\])/, -/^(?:[^\r\n]*(\r|\n)+)/, -/^(?:[^\r\n]+)/, -/^(?:(\r\n|\n|\r))/, -/^(?:"((?:\\"|\\[^"]|[^"\\])*)")/, -/^(?:'((?:\\'|\\[^']|[^'\\])*)')/, -/^(?:([^\S\n\r])+)/, -/^(?:\S+)/, -/^(?:.)/, -/^(?:.)/, -/^(?:$)/ - ], - conditions: { - "code": { - rules: [ - 67, - 68, - 78, - 79, - 86, - 87 - ], - inclusive: false - }, - "start_condition": { - rules: [ - 22, - 23, - 28, - 29, - 30, - 86, - 87 - ], - inclusive: false - }, - "options": { - rules: [ - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 72, - 86, - 87 - ], - inclusive: false - }, - "conditions": { - rules: [ - 9, - 10, - 11, - 12, - 86, - 87 - ], - inclusive: false - }, - "action": { - rules: [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 86, - 87 - ], - inclusive: false - }, - "path": { - rules: [ - 22, - 23, - 80, - 81, - 82, - 83, - 84, - 86, - 87 - ], - inclusive: false - }, - "set": { - rules: [ - 72, - 75, - 76, - 77, - 86, - 87 - ], - inclusive: false - }, - "indented": { - rules: [ - 32, - 33, - 34, - 35, - 36, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 69, - 70, - 71, - 73, - 74, - 86, - 87 - ], - inclusive: true - }, - "trail": { - rules: [ - 22, - 23, - 31, - 34, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 73, - 74, - 86, - 87 - ], - inclusive: true - }, - "rules": { - rules: [ - 13, - 14, - 15, - 16, - 17, - 22, - 23, - 34, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 68, - 69, - 70, - 71, - 73, - 74, - 85, - 86, - 87 - ], - inclusive: true - }, - "macro": { - rules: [ - 22, - 23, - 34, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 69, - 70, - 71, - 73, - 74, - 85, - 86, - 87 - ], - inclusive: true - }, - "INITIAL": { - rules: [ - 22, - 23, - 34, - 37, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 73, - 74, - 86, - 87 - ], - inclusive: true - } -} -}; - - -function indent(s, i) { - var a = s.split('\n'); - var pf = (new Array(i + 1)).join(' '); - return pf + a.join('\n' + pf); -} - -// properly quote and escape the given input string -function dquote(s) { - var sq = (s.indexOf('\'') >= 0); - var dq = (s.indexOf('"') >= 0); - if (sq && dq) { - s = s.replace(/"/g, '\\"'); - dq = false; - } - if (dq) { - s = '\'' + s + '\''; - } - else { - s = '"' + s + '"'; - } - return s; -}; - -return lexer; -})(); -parser.lexer = lexer; - -function Parser() { - this.yy = {}; -} -Parser.prototype = parser; -parser.Parser = Parser; - -return new Parser(); -})(); - - - - -if (typeof require !== 'undefined' && typeof exports !== 'undefined') { - exports.parser = lexParser; - exports.Parser = lexParser.Parser; - exports.parse = function () { - return lexParser.parse.apply(lexParser, arguments); - }; - -} - -},{"fs":15,"xregexp":22}],6:[function(require,module,exports){ -module.exports={ - "author": { - "name": "Zach Carter", - "email": "zach@carter.name", - "url": "http://zaa.ch" - }, - "name": "jison-lex", - "description": "lexical analyzer generator used by jison", - "license": "MIT", - "version": "0.3.4-166", - "keywords": [ - "jison", - "parser", - "generator", - "lexer", - "flex", - "tokenizer" - ], - "repository": { - "type": "git", - "url": "git://github.com/zaach/jison-lex.git" - }, - "bugs": { - "email": "jison@librelist.com", - "url": "http://github.com/zaach/jison-lex/issues" - }, - "main": "regexp-lexer", - "bin": "cli.js", - "engines": { - "node": ">=4.0" - }, - "dependencies": { - "lex-parser": "GerHobbelt/lex-parser#master", - "nomnom": "GerHobbelt/nomnom#master", - "xregexp": "GerHobbelt/xregexp#master" - }, - "devDependencies": { - "test": "0.6.0" - }, - "scripts": { - "test": "node tests/all-tests.js" - }, - "directories": { - "lib": "lib", - "tests": "tests" - }, - "homepage": "http://jison.org" -} - -},{}],7:[function(require,module,exports){ -/* parser generated by jison 0.4.18-184 */ - -/* - * Returns a Parser object of the following structure: - * - * Parser: { - * yy: {} The so-called "shared state" or rather the *source* of it; - * the real "shared state" `yy` passed around to - * the rule actions, etc. is a derivative/copy of this one, - * not a direct reference! - * } - * - * Parser.prototype: { - * yy: {}, - * EOF: 1, - * TERROR: 2, - * - * trace: function(errorMessage, ...), - * - * JisonParserError: function(msg, hash), - * - * quoteName: function(name), - * Helper function which can be overridden by user code later on: put suitable - * quotes around literal IDs in a description string. - * - * originalQuoteName: function(name), - * The basic quoteName handler provided by JISON. - * `cleanupAfterParse()` will clean up and reset `quoteName()` to reference this function - * at the end of the `parse()`. - * - * describeSymbol: function(symbol), - * Return a more-or-less human-readable description of the given symbol, when - * available, or the symbol itself, serving as its own 'description' for lack - * of something better to serve up. - * - * Return NULL when the symbol is unknown to the parser. - * - * symbols_: {associative list: name ==> number}, - * terminals_: {associative list: number ==> name}, - * nonterminals: {associative list: rule-name ==> {associative list: number ==> rule-alt}}, - * terminal_descriptions_: (if there are any) {associative list: number ==> description}, - * productions_: [...], - * - * performAction: function parser__performAction(yytext, yyleng, yylineno, yyloc, yystate, yysp, yyvstack, yylstack, yystack, yysstack, ...), - * where `...` denotes the (optional) additional arguments the user passed to - * `parser.parse(str, ...)` and specified by way of `%parse-param ...` in the grammar file - * - * The function parameters and `this` have the following value/meaning: - * - `this` : reference to the `yyval` internal object, which has members (`$` and `_$`) - * to store/reference the rule value `$$` and location info `@$`. - * - * One important thing to note about `this` a.k.a. `yyval`: every *reduce* action gets - * to see the same object via the `this` reference, i.e. if you wish to carry custom - * data from one reduce action through to the next within a single parse run, then you - * may get nasty and use `yyval` a.k.a. `this` for storing you own semi-permanent data. - * - * - `yytext` : reference to the lexer value which belongs to the last lexer token used - * to match this rule. This is *not* the look-ahead token, but the last token - * that's actually part of this rule. - * - * Formulated another way, `yytext` is the value of the token immediately preceeding - * the current look-ahead token. - * Caveats apply for rules which don't require look-ahead, such as epsilon rules. - * - * - `yyleng` : ditto as `yytext`, only now for the lexer.yyleng value. - * - * - `yylineno`: ditto as `yytext`, only now for the lexer.yylineno value. - * - * - `yyloc` : ditto as `yytext`, only now for the lexer.yylloc lexer token location info. - * - * - `yystate` : the current parser state number, used internally for dispatching and - * executing the action code chunk matching the rule currently being reduced. - * - * - `yysp` : the current state stack position (a.k.a. 'stack pointer') - * - * This one comes in handy when you are going to do advanced things to the parser - * stacks, all of which are accessible from your action code (see the next entries below). - * - * Also note that you can access this and other stack index values using the new double-hash - * syntax, i.e. `##$ === ##0 === yysp`, while `##1` is the stack index for all things - * related to the first rule term, just like you have `$1`, `@1` and `#1`. - * This is made available to write very advanced grammar action rules, e.g. when you want - * to investigate the parse state stack in your action code, which would, for example, - * be relevant when you wish to implement error diagnostics and reporting schemes similar - * to the work described here: - * - * + Pottier, F., 2016. Reachability and error diagnosis in LR(1) automata. - * In Journées Francophones des Languages Applicatifs. - * - * + Jeffery, C.L., 2003. Generating LR syntax error messages from examples. - * ACM Transactions on Programming Languages and Systems (TOPLAS), 25(5), pp.631–640. - * - * - `yyvstack`: reference to the parser value stack. Also accessed via the `$1` etc. - * constructs. - * - * - `yylstack`: reference to the parser token location stack. Also accessed via - * the `@1` etc. constructs. - * - * - `yystack` : reference to the parser token id stack. Also accessed via the - * `#1` etc. constructs. - * - * Note: this is a bit of a **white lie** as we can statically decode any `#n` reference to - * its numeric token id value, hence that code wouldn't need the `yystack` but *you* might - * want access for your own purposes, such as error analysis as mentioned above! - * - * Note that this stack stores the current stack of *tokens*, that is the sequence of - * already parsed=reduced *nonterminals* (tokens representing rules) and *terminals* - * (lexer tokens *shifted* onto the stack until the rule they belong to is found and - * *reduced*. - * - * - `yysstack`: reference to the parser state stack. This one carries the internal parser - * *states* such as the one in `yystate`, which are used to represent - * the parser state machine in the *parse table*. *Very* *internal* stuff, - * what can I say? If you access this one, you're clearly doing wicked things - * - * - `...` : the extra arguments you specified in the `%parse-param` statement in your - * grammar definition file. - * - * table: [...], - * State transition table - * ---------------------- - * - * index levels are: - * - `state` --> hash table - * - `symbol` --> action (number or array) - * - * If the `action` is an array, these are the elements' meaning: - * - index [0]: 1 = shift, 2 = reduce, 3 = accept - * - index [1]: GOTO `state` - * - * If the `action` is a number, it is the GOTO `state` - * - * defaultActions: {...}, - * - * parseError: function(str, hash, ExceptionClass), - * yyError: function(str, ...), - * yyRecovering: function(), - * yyErrOk: function(), - * yyClearIn: function(), - * - * constructParseErrorInfo: function(error_message, exception_object, expected_token_set, is_recoverable), - * Helper function **which will be set up during the first invocation of the `parse()` method**. - * Produces a new errorInfo 'hash object' which can be passed into `parseError()`. - * See it's use in this parser kernel in many places; example usage: - * - * var infoObj = parser.constructParseErrorInfo('fail!', null, - * parser.collect_expected_token_set(state), true); - * var retVal = parser.parseError(infoObj.errStr, infoObj, parser.JisonParserError); - * - * originalParseError: function(str, hash, ExceptionClass), - * The basic `parseError` handler provided by JISON. - * `cleanupAfterParse()` will clean up and reset `parseError()` to reference this function - * at the end of the `parse()`. - * - * options: { ... parser %options ... }, - * - * parse: function(input[, args...]), - * Parse the given `input` and return the parsed value (or `true` when none was provided by - * the root action, in which case the parser is acting as a *matcher*). - * You MAY use the additional `args...` parameters as per `%parse-param` spec of this grammar: - * these extra `args...` are passed verbatim to the grammar rules' action code. - * - * cleanupAfterParse: function(resultValue, invoke_post_methods, do_not_nuke_errorinfos), - * Helper function **which will be set up during the first invocation of the `parse()` method**. - * This helper API is invoked at the end of the `parse()` call, unless an exception was thrown - * and `%options no-try-catch` has been defined for this grammar: in that case this helper MAY - * be invoked by calling user code to ensure the `post_parse` callbacks are invoked and - * the internal parser gets properly garbage collected under these particular circumstances. - * - * lexer: { - * yy: {...}, A reference to the so-called "shared state" `yy` once - * received via a call to the `.setInput(input, yy)` lexer API. - * EOF: 1, - * ERROR: 2, - * JisonLexerError: function(msg, hash), - * parseError: function(str, hash, ExceptionClass), - * setInput: function(input, [yy]), - * input: function(), - * unput: function(str), - * more: function(), - * reject: function(), - * less: function(n), - * pastInput: function(n), - * upcomingInput: function(n), - * showPosition: function(), - * test_match: function(regex_match_array, rule_index, ...), - * next: function(...), - * lex: function(...), - * begin: function(condition), - * pushState: function(condition), - * popState: function(), - * topState: function(), - * _currentRules: function(), - * stateStackSize: function(), - * cleanupAfterLex: function() - * - * options: { ... lexer %options ... }, - * - * performAction: function(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * rules: [...], - * conditions: {associative list: name ==> set}, - * } - * } - * - * - * token location info (@$, _$, etc.): { - * first_line: n, - * last_line: n, - * first_column: n, - * last_column: n, - * range: [start_number, end_number] - * (where the numbers are indexes into the input string, zero-based) - * } - * - * --- - * - * The `parseError` function receives a 'hash' object with these members for lexer and - * parser errors: - * - * { - * text: (matched text) - * token: (the produced terminal token, if any) - * token_id: (the produced terminal token numeric ID, if any) - * line: (yylineno) - * loc: (yylloc) - * } - * - * parser (grammar) errors will also provide these additional members: - * - * { - * expected: (array describing the set of expected tokens; - * may be UNDEFINED when we cannot easily produce such a set) - * state: (integer (or array when the table includes grammar collisions); - * represents the current internal state of the parser kernel. - * can, for example, be used to pass to the `collect_expected_token_set()` - * API to obtain the expected token set) - * action: (integer; represents the current internal action which will be executed) - * new_state: (integer; represents the next/planned internal state, once the current - * action has executed) - * recoverable: (boolean: TRUE when the parser MAY have an error recovery rule - * available for this particular error) - * state_stack: (array: the current parser LALR/LR internal state stack; this can be used, - * for instance, for advanced error analysis and reporting) - * value_stack: (array: the current parser LALR/LR internal `$$` value stack; this can be used, - * for instance, for advanced error analysis and reporting) - * location_stack: (array: the current parser LALR/LR internal location stack; this can be used, - * for instance, for advanced error analysis and reporting) - * yy: (object: the current parser internal "shared state" `yy` - * as is also available in the rule actions; this can be used, - * for instance, for advanced error analysis and reporting) - * lexer: (reference to the current lexer instance used by the parser) - * parser: (reference to the current parser instance) - * } - * - * while `this` will reference the current parser instance. - * - * When `parseError` is invoked by the lexer, `this` will still reference the related *parser* - * instance, while these additional `hash` fields will also be provided: - * - * { - * lexer: (reference to the current lexer instance which reported the error) - * } - * - * When `parseError` is invoked by the parser due to a **JavaScript exception** being fired - * from either the parser or lexer, `this` will still reference the related *parser* - * instance, while these additional `hash` fields will also be provided: - * - * { - * exception: (reference to the exception thrown) - * } - * - * Please do note that in the latter situation, the `expected` field will be omitted as - * this type of failure is assumed not to be due to *parse errors* but rather due to user - * action code in either parser or lexer failing unexpectedly. - * - * --- - * - * You can specify parser options by setting / modifying the `.yy` object of your Parser instance. - * These options are available: - * - * ### options which are global for all parser instances - * - * Parser.pre_parse: function(yy [, optional parse() args]) - * optional: you can specify a pre_parse() function in the chunk following - * the grammar, i.e. after the last `%%`. - * Parser.post_parse: function(yy, retval [, optional parse() args]) { return retval; } - * optional: you can specify a post_parse() function in the chunk following - * the grammar, i.e. after the last `%%`. When it does not return any value, - * the parser will return the original `retval`. - * - * ### options which can be set up per parser instance - * - * yy: { - * pre_parse: function(yy [, optional parse() args]) - * optional: is invoked before the parse cycle starts (and before the first - * invocation of `lex()`) but immediately after the invocation of - * `parser.pre_parse()`). - * post_parse: function(yy, retval [, optional parse() args]) { return retval; } - * optional: is invoked when the parse terminates due to success ('accept') - * or failure (even when exceptions are thrown). - * `retval` contains the return value to be produced by `Parser.parse()`; - * this function can override the return value by returning another. - * When it does not return any value, the parser will return the original - * `retval`. - * This function is invoked immediately before `Parser.post_parse()`. - * - * parseError: function(str, hash, ExceptionClass) - * optional: overrides the default `parseError` function. - * quoteName: function(name), - * optional: overrides the default `quoteName` function. - * } - * - * parser.lexer.options: { - * pre_lex: function() - * optional: is invoked before the lexer is invoked to produce another token. - * `this` refers to the Lexer object. - * post_lex: function(token) { return token; } - * optional: is invoked when the lexer has produced a token `token`; - * this function can override the returned token value by returning another. - * When it does not return any (truthy) value, the lexer will return - * the original `token`. - * `this` refers to the Lexer object. - * - * ranges: boolean - * optional: `true` ==> token location info will include a .range[] member. - * flex: boolean - * optional: `true` ==> flex-like lexing behaviour where the rules are tested - * exhaustively to find the longest match. - * backtrack_lexer: boolean - * optional: `true` ==> lexer regexes are tested in order and for invoked; - * the lexer terminates the scan when a token is returned by the action code. - * xregexp: boolean - * optional: `true` ==> lexer rule regexes are "extended regex format" requiring the - * `XRegExp` library. When this %option has not been specified at compile time, all lexer - * rule regexes have been written as standard JavaScript RegExp expressions. - * } - */ -var parser = (function () { - -// See also: -// http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508 -// but we keep the prototype.constructor and prototype.name assignment lines too for compatibility -// with userland code which might access the derived class in a 'classic' way. -function JisonParserError(msg, hash) { - Object.defineProperty(this, 'name', { - enumerable: false, - writable: false, - value: 'JisonParserError' - }); - - if (msg == null) msg = '???'; - - Object.defineProperty(this, 'message', { - enumerable: false, - writable: true, - value: msg - }); - - this.hash = hash; - - var stacktrace; - if (hash && hash.exception instanceof Error) { - var ex2 = hash.exception; - this.message = ex2.message || msg; - stacktrace = ex2.stack; - } - if (!stacktrace) { - if (Error.hasOwnProperty('captureStackTrace')) { // V8 - Error.captureStackTrace(this, this.constructor); - } else { - stacktrace = (new Error(msg)).stack; - } - } - if (stacktrace) { - Object.defineProperty(this, 'stack', { - enumerable: false, - writable: false, - value: stacktrace - }); - } -} - -if (typeof Object.setPrototypeOf === 'function') { - Object.setPrototypeOf(JisonParserError.prototype, Error.prototype); -} else { - JisonParserError.prototype = Object.create(Error.prototype); -} -JisonParserError.prototype.constructor = JisonParserError; -JisonParserError.prototype.name = 'JisonParserError'; - - - - -// helper: reconstruct the productions[] table -function bp(s) { - var rv = []; - var p = s.pop; - var r = s.rule; - for (var i = 0, l = p.length; i < l; i++) { - rv.push([ - p[i], - r[i] - ]); - } - return rv; -} - - - -// helper: reconstruct the defaultActions[] table -function bda(s) { - var rv = {}; - var d = s.idx; - var g = s.goto; - for (var i = 0, l = d.length; i < l; i++) { - var j = d[i]; - rv[j] = g[i]; - } - return rv; -} - - - -// helper: reconstruct the 'goto' table -function bt(s) { - var rv = []; - var d = s.len; - var y = s.symbol; - var t = s.type; - var a = s.state; - var m = s.mode; - var g = s.goto; - for (var i = 0, l = d.length; i < l; i++) { - var n = d[i]; - var q = {}; - for (var j = 0; j < n; j++) { - var z = y.shift(); - switch (t.shift()) { - case 2: - q[z] = [ - m.shift(), - g.shift() - ]; - break; - - case 0: - q[z] = a.shift(); - break; - - default: - // type === 1: accept - q[z] = [ - 3 - ]; - } - } - rv.push(q); - } - return rv; -} - - - -// helper: runlength encoding with increment step: code, length: step (default step = 0) -// `this` references an array -function s(c, l, a) { - a = a || 0; - for (var i = 0; i < l; i++) { - this.push(c); - c += a; - } -} - -// helper: duplicate sequence from *relative* offset and length. -// `this` references an array -function c(i, l) { - i = this.length - i; - for (l += i; i < l; i++) { - this.push(this[i]); - } -} - -// helper: unpack an array using helpers and data, all passed in an array argument 'a'. -function u(a) { - var rv = []; - for (var i = 0, l = a.length; i < l; i++) { - var e = a[i]; - // Is this entry a helper function? - if (typeof e === 'function') { - i++; - e.apply(rv, a[i]); - } else { - rv.push(e); - } - } - return rv; -} - - -var parser = { - // Code Generator Information Report - // --------------------------------- - // - // Options: - // - // no default action: ............... false - // no try..catch: ................... false - // no default resolve on conflict: false - // on-demand look-ahead: ............ false - // error recovery token skip maximum: 3 - // yyerror in parse actions is: ..... NOT recoverable, - // yyerror in lexer actions and other non-fatal lexer are: - // .................................. NOT recoverable, - // debug grammar/output: ............ false - // has partial LR conflict upgrade: true - // rudimentary token-stack support: false - // parser table compression mode: ... 2 - // export debug tables: ............. false - // export *all* tables: ............. false - // module type: ..................... commonjs - // parser engine type: .............. lalr - // output main() in the module: ..... true - // number of expected conflicts: .... 0 - // - // - // Parser Analysis flags: - // - // all actions are default: ......... false - // uses yyleng: ..................... false - // uses yylineno: ................... false - // uses yytext: ..................... false - // uses yylloc: ..................... false - // uses ParseError API: ............. false - // uses YYERROR: .................... true - // uses YYRECOVERING: ............... false - // uses YYERROK: .................... false - // uses YYCLEARIN: .................. false - // tracks rule values: .............. true - // assigns rule values: ............. true - // uses location tracking: .......... true - // assigns location: ................ false - // uses yystack: .................... false - // uses yysstack: ................... false - // uses yysp: ....................... true - // has error recovery: .............. true - // - // --------- END OF REPORT ----------- - -trace: function no_op_trace() { }, -JisonParserError: JisonParserError, -yy: {}, -options: { - type: "lalr", - hasPartialLrUpgradeOnConflict: true, - errorRecoveryTokenDiscardCount: 3 -}, -symbols_: { - "$accept": 0, - "$end": 1, - "%%": 14, - "(": 7, - ")": 8, - "*": 9, - "+": 11, - ":": 4, - ";": 5, - "=": 3, - "?": 10, - "ACTION": 15, - "ACTION_BODY": 41, - "ALIAS": 38, - "ARROW_ACTION": 40, - "CODE": 44, - "DEBUG": 19, - "EOF": 1, - "EPSILON": 37, - "ID": 23, - "IMPORT": 21, - "INCLUDE": 42, - "INIT_CODE": 22, - "INTEGER": 36, - "LEFT": 32, - "LEX_BLOCK": 17, - "NAME": 27, - "NONASSOC": 34, - "OPTIONS": 25, - "OPTIONS_END": 26, - "OPTION_STRING_VALUE": 28, - "OPTION_VALUE": 29, - "PARSER_TYPE": 31, - "PARSE_PARAM": 30, - "PATH": 43, - "PREC": 39, - "RIGHT": 33, - "START": 16, - "STRING": 24, - "TOKEN": 18, - "TOKEN_TYPE": 35, - "UNKNOWN_DECL": 20, - "action": 80, - "action_body": 81, - "action_comments_body": 82, - "action_ne": 79, - "associativity": 58, - "declaration": 49, - "declaration_list": 48, - "error": 2, - "expression": 74, - "extra_parser_module_code": 83, - "full_token_definitions": 60, - "grammar": 66, - "handle": 71, - "handle_action": 70, - "handle_list": 69, - "handle_sublist": 72, - "id": 78, - "id_list": 65, - "import_name": 50, - "import_path": 51, - "include_macro_code": 84, - "module_code_chunk": 85, - "one_full_token": 61, - "operator": 57, - "option": 54, - "option_list": 53, - "optional_action_header_block": 47, - "optional_end_block": 46, - "optional_module_code_chunk": 86, - "optional_token_type": 62, - "options": 52, - "parse_params": 55, - "parser_type": 56, - "prec": 76, - "production": 68, - "production_list": 67, - "spec": 45, - "suffix": 75, - "suffixed_expression": 73, - "symbol": 77, - "token_description": 64, - "token_list": 59, - "token_value": 63, - "{": 12, - "|": 6, - "}": 13 -}, -terminals_: { - 1: "EOF", - 2: "error", - 3: "=", - 4: ":", - 5: ";", - 6: "|", - 7: "(", - 8: ")", - 9: "*", - 10: "?", - 11: "+", - 12: "{", - 13: "}", - 14: "%%", - 15: "ACTION", - 16: "START", - 17: "LEX_BLOCK", - 18: "TOKEN", - 19: "DEBUG", - 20: "UNKNOWN_DECL", - 21: "IMPORT", - 22: "INIT_CODE", - 23: "ID", - 24: "STRING", - 25: "OPTIONS", - 26: "OPTIONS_END", - 27: "NAME", - 28: "OPTION_STRING_VALUE", - 29: "OPTION_VALUE", - 30: "PARSE_PARAM", - 31: "PARSER_TYPE", - 32: "LEFT", - 33: "RIGHT", - 34: "NONASSOC", - 35: "TOKEN_TYPE", - 36: "INTEGER", - 37: "EPSILON", - 38: "ALIAS", - 39: "PREC", - 40: "ARROW_ACTION", - 41: "ACTION_BODY", - 42: "INCLUDE", - 43: "PATH", - 44: "CODE" -}, -TERROR: 2, -EOF: 1, - -// internals: defined here so the object *structure* doesn't get modified by parse() et al, -// thus helping JIT compilers like Chrome V8. -originalQuoteName: null, -originalParseError: null, -cleanupAfterParse: null, -constructParseErrorInfo: null, - -__reentrant_call_depth: 0, // INTERNAL USE ONLY -__error_infos: [], // INTERNAL USE ONLY: the set of parseErrorInfo objects created since the last cleanup - -// APIs which will be set up depending on user action code analysis: -//yyRecovering: 0, -//yyErrOk: 0, -//yyClearIn: 0, - -// Helper APIs -// ----------- - -// Helper function which can be overridden by user code later on: put suitable quotes around -// literal IDs in a description string. -quoteName: function parser_quoteName(id_str) { - return '"' + id_str + '"'; -}, - -// Return a more-or-less human-readable description of the given symbol, when available, -// or the symbol itself, serving as its own 'description' for lack of something better to serve up. -// -// Return NULL when the symbol is unknown to the parser. -describeSymbol: function parser_describeSymbol(symbol) { - if (symbol !== this.EOF && this.terminal_descriptions_ && this.terminal_descriptions_[symbol]) { - return this.terminal_descriptions_[symbol]; - } - else if (symbol === this.EOF) { - return 'end of input'; - } - else if (this.terminals_[symbol]) { - return this.quoteName(this.terminals_[symbol]); - } - // Otherwise... this might refer to a RULE token i.e. a non-terminal: see if we can dig that one up. - // - // An example of this may be where a rule's action code contains a call like this: - // - // parser.describeSymbol(#$) - // - // to obtain a human-readable description or name of the current grammar rule. This comes handy in - // error handling action code blocks, for example. - var s = this.symbols_; - for (var key in s) { - if (s[key] === symbol) { - return key; - } - } - return null; -}, - -// Produce a (more or less) human-readable list of expected tokens at the point of failure. -// -// The produced list may contain token or token set descriptions instead of the tokens -// themselves to help turning this output into something that easier to read by humans -// unless `do_not_describe` parameter is set, in which case a list of the raw, *numeric*, -// expected terminals and nonterminals is produced. -// -// The returned list (array) will not contain any duplicate entries. -collect_expected_token_set: function parser_collect_expected_token_set(state, do_not_describe) { - var TERROR = this.TERROR; - var tokenset = []; - var check = {}; - // Has this (error?) state been outfitted with a custom expectations description text for human consumption? - // If so, use that one instead of the less palatable token set. - if (!do_not_describe && this.state_descriptions_ && this.state_descriptions_[state]) { - return [ - this.state_descriptions_[state] - ]; - } - for (var p in this.table[state]) { - p = +p; - if (p !== TERROR) { - var d = do_not_describe ? p : this.describeSymbol(p); - if (d && !check[d]) { - tokenset.push(d); - check[d] = true; // Mark this token description as already mentioned to prevent outputting duplicate entries. - } - } - } - return tokenset; -}, -productions_: bp({ - pop: u([ - s, - [45, 3], - 46, - 46, - s, - [47, 3], - 48, - 48, - s, - [49, 16], - 50, - 50, - 51, - 51, - 52, - 53, - 53, - s, - [54, 4], - s, - [55, 4, 1], - 58, - 58, - 59, - 59, - 60, - 60, - s, - [61, 3], - 62, - s, - [62, 4, 1], - 65, - 66, - 67, - 67, - 68, - 69, - 69, - 70, - 70, - 71, - 71, - 72, - 72, - 73, - 73, - s, - [74, 4], - s, - [75, 4], - 76, - 76, - 77, - 77, - 78, - s, - [79, 5], - 80, - 80, - s, - [81, 5], - 82, - 82, - 83, - 83, - 84, - 84, - 85, - 85, - 86, - 86 -]), - rule: u([ - 5, - 5, - 3, - 0, - 2, - 0, - s, - [2, 3], - 0, - 2, - 1, - 1, - c, - [3, 3], - s, - [1, 5], - s, - [3, 5], - c, - [9, 5], - c, - [18, 3], - s, - [3, 3], - s, - [2, 3], - s, - [1, 3], - 2, - 1, - 2, - 2, - c, - [11, 3], - 0, - c, - [11, 7], - 1, - 4, - 3, - c, - [32, 3], - 2, - 0, - c, - [6, 4], - c, - [38, 4], - c, - [24, 5], - c, - [5, 4], - c, - [59, 6], - 0, - 0, - 1, - 5, - 4, - 4, - c, - [42, 3], - c, - [36, 3], - c, - [6, 3], - 0 -]) -}), -performAction: function parser__PerformAction(yyloc, yystate /* action[1] */, yysp, yyvstack, yylstack) { -/* this == yyval */ -var yy = this.yy; - -switch (yystate) { -case 1: - /*! Production:: spec : declaration_list "%%" grammar optional_end_block EOF */ - this.$ = yyvstack[yysp - 4]; - if (yyvstack[yysp - 1] && yyvstack[yysp - 1].trim() !== '') { - yy.addDeclaration(this.$, { include: yyvstack[yysp - 1] }); - } - return extend(this.$, yyvstack[yysp - 2]); - break; - -case 2: - /*! Production:: spec : declaration_list "%%" grammar error EOF */ - yy.parser.yyError("Maybe you did not correctly separate trailing code from the grammar rule set with a '%%' marker on an otherwise empty line?"); - break; - -case 3: - /*! Production:: spec : declaration_list error EOF */ - yy.parser.yyError("Maybe you did not correctly separate the parse 'header section' (token definitions, options, lexer spec, etc.) from the grammar rule set with a '%%' on an otherwise empty line?"); - break; - -case 5: - /*! Production:: optional_end_block : "%%" extra_parser_module_code */ -case 38: - /*! Production:: parse_params : PARSE_PARAM token_list */ -case 39: - /*! Production:: parser_type : PARSER_TYPE symbol */ -case 71: - /*! Production:: expression : ID */ -case 81: - /*! Production:: symbol : id */ -case 82: - /*! Production:: symbol : STRING */ -case 83: - /*! Production:: id : ID */ -case 86: - /*! Production:: action_ne : ACTION */ -case 87: - /*! Production:: action_ne : include_macro_code */ -case 89: - /*! Production:: action : action_ne */ -case 92: - /*! Production:: action_body : action_comments_body */ -case 96: - /*! Production:: action_comments_body : ACTION_BODY */ -case 98: - /*! Production:: extra_parser_module_code : optional_module_code_chunk */ -case 102: - /*! Production:: module_code_chunk : CODE */ -case 104: - /*! Production:: optional_module_code_chunk : module_code_chunk */ - this.$ = yyvstack[yysp]; - break; - -case 6: - /*! Production:: optional_action_header_block : ε */ -case 10: - /*! Production:: declaration_list : ε */ - this.$ = {}; - break; - -case 7: - /*! Production:: optional_action_header_block : optional_action_header_block ACTION */ -case 8: - /*! Production:: optional_action_header_block : optional_action_header_block include_macro_code */ - this.$ = yyvstack[yysp - 1]; - yy.addDeclaration(this.$, { actionInclude: yyvstack[yysp] }); - break; - -case 9: - /*! Production:: declaration_list : declaration_list declaration */ - this.$ = yyvstack[yysp - 1]; yy.addDeclaration(this.$, yyvstack[yysp]); - break; - -case 11: - /*! Production:: declaration : START id */ - this.$ = {start: yyvstack[yysp]}; - break; - -case 12: - /*! Production:: declaration : LEX_BLOCK */ - this.$ = {lex: {text: yyvstack[yysp], position: yylstack[yysp]}}; - break; - -case 13: - /*! Production:: declaration : operator */ - this.$ = {operator: yyvstack[yysp]}; - break; - -case 14: - /*! Production:: declaration : TOKEN full_token_definitions */ - this.$ = {token_list: yyvstack[yysp]}; - break; - -case 15: - /*! Production:: declaration : ACTION */ -case 16: - /*! Production:: declaration : include_macro_code */ - this.$ = {include: yyvstack[yysp]}; - break; - -case 17: - /*! Production:: declaration : parse_params */ - this.$ = {parseParams: yyvstack[yysp]}; - break; - -case 18: - /*! Production:: declaration : parser_type */ - this.$ = {parserType: yyvstack[yysp]}; - break; - -case 19: - /*! Production:: declaration : options */ - this.$ = {options: yyvstack[yysp]}; - break; - -case 20: - /*! Production:: declaration : DEBUG */ - this.$ = {options: [['debug', true]]}; - break; - -case 21: - /*! Production:: declaration : UNKNOWN_DECL */ - this.$ = {unknownDecl: yyvstack[yysp]}; - break; - -case 22: - /*! Production:: declaration : IMPORT import_name import_path */ - this.$ = {imports: {name: yyvstack[yysp - 1], path: yyvstack[yysp]}}; - break; - -case 23: - /*! Production:: declaration : IMPORT import_name error */ - yy.parser.yyError("You did not specify a legal file path for the '%import' initialization code statement, which must have the format: '%import qualifier_name file_path'."); - break; - -case 24: - /*! Production:: declaration : IMPORT error import_path */ - yy.parser.yyError("Each '%import'-ed initialization code section must be qualified by a name, e.g. 'required' before the import path itself: '%import qualifier_name file_path'."); - break; - -case 25: - /*! Production:: declaration : INIT_CODE import_name action_ne */ - this.$ = {initCode: {qualifier: yyvstack[yysp - 1], include: yyvstack[yysp]}}; - break; - -case 26: - /*! Production:: declaration : INIT_CODE error action_ne */ - yy.parser.yyError("Each '%code' initialization code section must be qualified by a name, e.g. 'required' before the action code itself: '%code qualifier_name {action code}'."); - break; - -case 31: - /*! Production:: options : OPTIONS option_list OPTIONS_END */ -case 84: - /*! Production:: action_ne : "{" action_body "}" */ - this.$ = yyvstack[yysp - 1]; - break; - -case 32: - /*! Production:: option_list : option_list option */ -case 44: - /*! Production:: token_list : token_list symbol */ -case 55: - /*! Production:: id_list : id_list id */ - this.$ = yyvstack[yysp - 1]; this.$.push(yyvstack[yysp]); - break; - -case 33: - /*! Production:: option_list : option */ -case 45: - /*! Production:: token_list : symbol */ -case 56: - /*! Production:: id_list : id */ -case 62: - /*! Production:: handle_list : handle_action */ - this.$ = [yyvstack[yysp]]; - break; - -case 34: - /*! Production:: option : NAME */ - this.$ = [yyvstack[yysp], true]; - break; - -case 35: - /*! Production:: option : NAME "=" OPTION_STRING_VALUE */ - this.$ = [yyvstack[yysp - 2], yyvstack[yysp]]; - break; - -case 36: - /*! Production:: option : NAME "=" OPTION_VALUE */ -case 37: - /*! Production:: option : NAME "=" NAME */ - this.$ = [yyvstack[yysp - 2], parseValue(yyvstack[yysp])]; - break; - -case 40: - /*! Production:: operator : associativity token_list */ - this.$ = [yyvstack[yysp - 1]]; this.$.push.apply(this.$, yyvstack[yysp]); - break; - -case 41: - /*! Production:: associativity : LEFT */ - this.$ = 'left'; - break; - -case 42: - /*! Production:: associativity : RIGHT */ - this.$ = 'right'; - break; - -case 43: - /*! Production:: associativity : NONASSOC */ - this.$ = 'nonassoc'; - break; - -case 46: - /*! Production:: full_token_definitions : optional_token_type id_list */ - var rv = []; - var lst = yyvstack[yysp]; - for (var i = 0, len = lst.length; i < len; i++) { - var id = lst[i]; - var m = {id: id}; - if (yyvstack[yysp - 1]) { - m.type = yyvstack[yysp - 1]; - } - rv.push(m); - } - this.$ = rv; - break; - -case 47: - /*! Production:: full_token_definitions : optional_token_type one_full_token */ - var m = yyvstack[yysp]; - if (yyvstack[yysp - 1]) { - m.type = yyvstack[yysp - 1]; - } - this.$ = [m]; - break; - -case 48: - /*! Production:: one_full_token : id token_value token_description */ - this.$ = { - id: yyvstack[yysp - 2], - value: yyvstack[yysp - 1] - }; - break; - -case 49: - /*! Production:: one_full_token : id token_description */ - this.$ = { - id: yyvstack[yysp - 1], - description: yyvstack[yysp] - }; - break; - -case 50: - /*! Production:: one_full_token : id token_value */ - this.$ = { - id: yyvstack[yysp - 1], - value: yyvstack[yysp], - description: $token_description - }; - break; - -case 51: - /*! Production:: optional_token_type : ε */ - this.$ = false; - break; - -case 57: - /*! Production:: grammar : optional_action_header_block production_list */ - this.$ = yyvstack[yysp - 1]; - this.$.grammar = yyvstack[yysp]; - break; - -case 58: - /*! Production:: production_list : production_list production */ - this.$ = yyvstack[yysp - 1]; - if (yyvstack[yysp][0] in this.$) { - this.$[yyvstack[yysp][0]] = this.$[yyvstack[yysp][0]].concat(yyvstack[yysp][1]); - } else { - this.$[yyvstack[yysp][0]] = yyvstack[yysp][1]; - } - break; - -case 59: - /*! Production:: production_list : production */ - this.$ = {}; this.$[yyvstack[yysp][0]] = yyvstack[yysp][1]; - break; - -case 60: - /*! Production:: production : id ":" handle_list ";" */ - this.$ = [yyvstack[yysp - 3], yyvstack[yysp - 1]]; - break; - -case 61: - /*! Production:: handle_list : handle_list "|" handle_action */ - this.$ = yyvstack[yysp - 2]; - this.$.push(yyvstack[yysp]); - break; - -case 63: - /*! Production:: handle_action : handle prec action */ - this.$ = [(yyvstack[yysp - 2].length ? yyvstack[yysp - 2].join(' ') : '')]; - if (yyvstack[yysp]) { - this.$.push(yyvstack[yysp]); - } - if (yyvstack[yysp - 1]) { - if (yyvstack[yysp - 2].length === 0) { - yy.parser.yyError('You cannot specify a precedence override for an epsilon (a.k.a. empty) rule!'); - } - this.$.push(yyvstack[yysp - 1]); - } - if (this.$.length === 1) { - this.$ = this.$[0]; - } - break; - -case 64: - /*! Production:: handle_action : EPSILON action */ - this.$ = ['']; - if (yyvstack[yysp]) { - this.$.push(yyvstack[yysp]); - } - if (this.$.length === 1) { - this.$ = this.$[0]; - } - break; - -case 65: - /*! Production:: handle : handle suffixed_expression */ - this.$ = yyvstack[yysp - 1]; - this.$.push(yyvstack[yysp]); - break; - -case 66: - /*! Production:: handle : ε */ - this.$ = []; - break; - -case 67: - /*! Production:: handle_sublist : handle_sublist "|" handle */ - this.$ = yyvstack[yysp - 2]; - this.$.push(yyvstack[yysp].join(' ')); - break; - -case 68: - /*! Production:: handle_sublist : handle */ - this.$ = [yyvstack[yysp].join(' ')]; - break; - -case 69: - /*! Production:: suffixed_expression : expression suffix ALIAS */ - this.$ = yyvstack[yysp - 2] + yyvstack[yysp - 1] + "[" + yyvstack[yysp] + "]"; - break; - -case 70: - /*! Production:: suffixed_expression : expression suffix */ -case 97: - /*! Production:: action_comments_body : action_comments_body ACTION_BODY */ -case 103: - /*! Production:: module_code_chunk : module_code_chunk CODE */ - this.$ = yyvstack[yysp - 1] + yyvstack[yysp]; - break; - -case 72: - /*! Production:: expression : STRING */ - // Re-encode the string *anyway* as it will - // be made part of the rule rhs a.k.a. production (type: *string*) again and we want - // to be able to handle all tokens, including *significant space* - // encoded as literal tokens in a grammar such as this: `rule: A ' ' B`. - if (yyvstack[yysp].indexOf("'") >= 0) { - this.$ = '"' + yyvstack[yysp] + '"'; - } else { - this.$ = "'" + yyvstack[yysp] + "'"; - } - break; - -case 73: - /*! Production:: expression : "(" handle_sublist ")" */ - this.$ = '(' + yyvstack[yysp - 1].join(' | ') + ')'; - break; - -case 74: - /*! Production:: expression : "(" handle_sublist error */ - var l = yyvstack[yysp - 1]; - var ab = l.slice(0, 10).join(' | '); - yy.parser.yyError("Seems you did not correctly bracket a grammar rule sublist in '( ... )' brackets. Offending handle sublist:\n" + ab); - break; - -case 75: - /*! Production:: suffix : ε */ -case 90: - /*! Production:: action : ε */ -case 91: - /*! Production:: action_body : ε */ -case 105: - /*! Production:: optional_module_code_chunk : ε */ - this.$ = ''; - break; - -case 79: - /*! Production:: prec : PREC symbol */ - this.$ = { prec: yyvstack[yysp] }; - break; - -case 80: - /*! Production:: prec : ε */ - this.$ = null; - break; - -case 85: - /*! Production:: action_ne : "{" action_body error */ - var l = yyvstack[yysp - 1].split('\n'); - var ab = l.slice(0, 10).join('\n'); - yy.parser.yyError("Seems you did not correctly bracket a parser rule action block in curly braces: '{ ... }'. Offending action body:\n" + ab); - break; - -case 88: - /*! Production:: action_ne : ARROW_ACTION */ - this.$ = '$$ = ' + yyvstack[yysp]; - break; - -case 93: - /*! Production:: action_body : action_body "{" action_body "}" action_comments_body */ - this.$ = yyvstack[yysp - 4] + yyvstack[yysp - 3] + yyvstack[yysp - 2] + yyvstack[yysp - 1] + yyvstack[yysp]; - break; - -case 94: - /*! Production:: action_body : action_body "{" action_body "}" */ - this.$ = yyvstack[yysp - 3] + yyvstack[yysp - 2] + yyvstack[yysp - 1] + yyvstack[yysp]; - break; - -case 95: - /*! Production:: action_body : action_body "{" action_body error */ - var l = yyvstack[yysp - 1].split('\n'); - var ab = l.slice(0, 10).join('\n'); - yy.parser.yyError("Seems you did not correctly match curly braces '{ ... }' in a parser rule action block. Offending action body part:\n" + ab); - break; - -case 99: - /*! Production:: extra_parser_module_code : optional_module_code_chunk include_macro_code extra_parser_module_code */ - this.$ = yyvstack[yysp - 2] + yyvstack[yysp - 1] + yyvstack[yysp]; - break; - -case 100: - /*! Production:: include_macro_code : INCLUDE PATH */ - var fs = require('fs'); - var fileContent = fs.readFileSync(yyvstack[yysp], { encoding: 'utf-8' }); - // And no, we don't support nested '%include': - this.$ = '\n// Included by Jison: ' + yyvstack[yysp] + ':\n\n' + fileContent + '\n\n// End Of Include by Jison: ' + yyvstack[yysp] + '\n\n'; - break; - -case 101: - /*! Production:: include_macro_code : INCLUDE error */ - yy.parser.yyError("%include MUST be followed by a valid file path"); - break; - -} -}, -table: bt({ - len: u([ - 19, - 1, - 24, - 5, - 1, - 17, - 2, - 17, - 17, - 4, - s, - [17, 7], - 4, - 4, - 5, - 2, - s, - [5, 4, -1], - 2, - 2, - 4, - 7, - 1, - 17, - 25, - 17, - 4, - 1, - 4, - 3, - 7, - 7, - 6, - 6, - 21, - 19, - 23, - 23, - 22, - 22, - 21, - 17, - 3, - 2, - 3, - 1, - 1, - 6, - 6, - 3, - 3, - 4, - 1, - 19, - 17, - 22, - s, - [17, 6], - 6, - s, - [19, 3], - 17, - 19, - 17, - c, - [26, 4], - 1, - s, - [3, 3], - 4, - 14, - 18, - 19, - 17, - 18, - 17, - 3, - 4, - 4, - s, - [2, 3], - 6, - c, - [75, 3], - 13, - 9, - 17, - 19, - 19, - 6, - c, - [74, 3], - 13, - 9, - 12, - 4, - 17, - 16, - 16, - 8, - 2, - 2, - c, - [22, 3], - 6, - s, - [13, 4], - 3, - 8, - 5, - 3, - 12, - 16, - 16, - 7, - 4, - 8 -]), - symbol: u([ - 2, - s, - [14, 9, 1], - 25, - s, - [30, 5, 1], - 42, - 45, - 48, - 1, - c, - [20, 17], - 49, - 52, - s, - [55, 4, 1], - 84, - 15, - 23, - 42, - 47, - 66, - c, - [30, 18], - 23, - 78, - c, - [19, 17], - c, - [36, 18], - 35, - 60, - 62, - c, - [38, 34], - c, - [17, 86], - 23, - 24, - 50, - c, - [4, 4], - 23, - 24, - 59, - 77, - 78, - 2, - 43, - c, - [7, 5], - 23, - 24, - 77, - 78, - 27, - 53, - 54, - 23, - 24, - 23, - 24, - 23, - 24, - c, - [210, 3], - 46, - c, - [219, 3], - 67, - 68, - 78, - 84, - c, - [221, 18], - 2, - 4, - 5, - 6, - 12, - s, - [14, 12, 1], - c, - [23, 5], - 36, - 40, - c, - [227, 19], - 61, - 65, - 78, - 23, - c, - [105, 3], - 51, - c, - [3, 3], - 2, - 12, - 15, - 23, - 24, - c, - [36, 3], - c, - [7, 6], - 12, - 15, - 40, - 42, - 79, - 84, - c, - [6, 6], - c, - [55, 10], - c, - [76, 8], - 42, - c, - [150, 3], - c, - [21, 18], - 2, - c, - [119, 20], - c, - [82, 3], - c, - [23, 22], - 1, - c, - [24, 3], - c, - [23, 10], - c, - [67, 7], - 44, - c, - [22, 22], - c, - [130, 31], - c, - [19, 7], - 26, - 27, - 54, - 26, - 27, - 3, - 26, - 27, - s, - [1, 3], - 42, - 44, - 83, - 85, - 86, - c, - [282, 3], - 23, - 68, - 78, - c, - [295, 3], - c, - [3, 3], - c, - [12, 4], - 4, - c, - [71, 11], - c, - [51, 7], - c, - [519, 28], - c, - [313, 9], - 42, - 63, - 64, - c, - [503, 103], - 12, - 13, - 41, - 81, - 82, - c, - [258, 12], - c, - [346, 10], - c, - [19, 36], - c, - [204, 34], - c, - [36, 18], - 26, - 27, - 27, - 28, - 29, - s, - [1, 4], - 42, - 84, - c, - [310, 3], - c, - [3, 4], - c, - [298, 3], - 5, - 6, - 7, - c, - [519, 4], - 37, - 39, - 40, - 42, - 69, - 70, - 71, - c, - [311, 18], - c, - [18, 10], - c, - [88, 8], - c, - [290, 28], - c, - [124, 25], - c, - [240, 3], - c, - [243, 4], - c, - [4, 4], - 26, - 27, - 26, - 27, - c, - [442, 3], - c, - [440, 6], - 42, - 44, - 5, - 6, - 5, - 6, - c, - [133, 7], - c, - [132, 3], - 73, - 74, - 76, - c, - [580, 3], - c, - [652, 4], - 80, - c, - [653, 11], - c, - [284, 46], - c, - [347, 6], - c, - [6, 3], - 1, - c, - [225, 15], - 70, - 71, - c, - [92, 10], - s, - [5, 4, 1], - c, - [116, 7], - c, - [879, 4], - c, - [16, 5], - s, - [9, 4, 1], - c, - [19, 3], - 38, - c, - [20, 3], - 75, - c, - [17, 16], - c, - [16, 17], - c, - [15, 3], - 23, - 24, - 71, - 72, - c, - [189, 4], - c, - [108, 3], - c, - [198, 6], - c, - [93, 4], - c, - [90, 9], - c, - [54, 9], - c, - [13, 35], - 6, - 8, - c, - [80, 6], - 73, - 74, - c, - [184, 4], - c, - [189, 4], - c, - [161, 12], - c, - [140, 39], - c, - [353, 5], - c, - [71, 7] -]), - type: u([ - s, - [2, 17], - 0, - 0, - 1, - c, - [20, 19], - s, - [0, 5], - c, - [10, 5], - s, - [2, 19], - c, - [20, 20], - c, - [68, 19], - s, - [2, 122], - c, - [186, 5], - c, - [199, 5], - c, - [206, 7], - c, - [143, 5], - c, - [146, 11], - c, - [219, 6], - c, - [163, 63], - c, - [95, 8], - c, - [273, 21], - c, - [123, 8], - c, - [282, 143], - c, - [130, 27], - c, - [20, 11], - c, - [313, 9], - c, - [358, 33], - c, - [520, 142], - c, - [346, 122], - c, - [121, 22], - c, - [610, 39], - c, - [182, 73], - c, - [112, 20], - c, - [20, 9], - c, - [751, 62], - c, - [61, 22], - c, - [92, 25], - c, - [47, 18], - c, - [125, 39], - c, - [451, 80], - c, - [932, 9], - c, - [469, 62], - 0, - 0 -]), - state: u([ - 1, - 2, - 5, - 14, - 12, - 13, - 8, - 19, - 11, - 28, - 27, - 30, - 32, - 33, - 35, - 39, - 41, - 42, - 43, - 47, - 42, - 43, - 48, - 43, - 49, - 50, - 52, - 55, - 58, - 59, - 57, - 61, - 60, - 62, - 63, - 67, - 68, - 71, - 73, - 71, - 74, - 43, - 74, - 43, - 76, - 80, - 82, - 81, - 84, - 59, - 86, - 87, - 88, - 91, - 92, - 97, - 99, - 100, - 101, - 103, - 108, - 82, - 81, - 112, - 114, - 111, - 119, - 118, - 71, - 120, - 92, - 121, - 101, - 119, - 122, - 71, - 123, - 43, - 124, - 129, - 128, - 112, - 114, - 136, - 137, - 112, - 114 -]), - mode: u([ - s, - [2, 17], - s, - [1, 17], - c, - [20, 4], - c, - [38, 18], - s, - [2, 35], - c, - [36, 36], - s, - [2, 84], - c, - [192, 18], - c, - [22, 9], - c, - [87, 61], - c, - [67, 20], - c, - [101, 15], - c, - [18, 5], - s, - [2, 126], - c, - [128, 26], - c, - [26, 4], - c, - [3, 4], - c, - [7, 6], - c, - [415, 12], - c, - [11, 22], - c, - [473, 32], - c, - [233, 107], - c, - [340, 113], - c, - [467, 7], - c, - [299, 7], - c, - [138, 43], - c, - [170, 60], - c, - [877, 19], - c, - [17, 5], - c, - [139, 9], - c, - [10, 7], - c, - [713, 61], - c, - [59, 17], - c, - [17, 6], - c, - [85, 16], - c, - [14, 7], - c, - [107, 53], - c, - [53, 22], - c, - [73, 43], - c, - [65, 5], - c, - [880, 4], - c, - [337, 60], - c, - [67, 7] -]), - goto: u([ - s, - [10, 17], - 4, - 3, - 10, - 6, - 7, - 9, - s, - [15, 4, 1], - 23, - 21, - 22, - 24, - 25, - 26, - 20, - s, - [6, 3], - 29, - s, - [9, 17], - 31, - s, - [12, 17], - s, - [13, 17], - 51, - 34, - s, - [15, 17], - s, - [16, 17], - s, - [17, 17], - s, - [18, 17], - s, - [19, 17], - s, - [20, 17], - s, - [21, 17], - 36, - 37, - 38, - 40, - 37, - 38, - 31, - 44, - 46, - 45, - 31, - 44, - 31, - 44, - 51, - 41, - 41, - 42, - 42, - 43, - 43, - 4, - 53, - 54, - 56, - 31, - 20, - 3, - s, - [11, 17], - s, - [83, 25], - s, - [14, 17], - 31, - 52, - 64, - 65, - 66, - 65, - 66, - s, - [27, 7], - s, - [28, 7], - 69, - 70, - 72, - 20, - c, - [4, 4], - s, - [40, 10], - 31, - 44, - s, - [40, 7], - s, - [45, 19], - s, - [81, 23], - s, - [82, 23], - s, - [100, 22], - s, - [101, 22], - s, - [38, 10], - 31, - 44, - s, - [38, 7], - s, - [39, 17], - 75, - 51, - 33, - 33, - 77, - 34, - 34, - 78, - 79, - 105, - 105, - 83, - s, - [57, 3], - 31, - s, - [7, 3], - s, - [8, 3], - s, - [59, 4], - 85, - s, - [46, 10], - 31, - s, - [46, 7], - s, - [47, 17], - s, - [56, 11], - 90, - s, - [56, 6], - 89, - 56, - s, - [22, 17], - s, - [23, 17], - s, - [29, 17], - s, - [30, 17], - s, - [24, 17], - s, - [25, 17], - s, - [91, 3], - 93, - s, - [86, 19], - s, - [87, 19], - s, - [88, 19], - s, - [26, 17], - s, - [44, 19], - s, - [31, 17], - 32, - 32, - 96, - 94, - 95, - 1, - 2, - 5, - 98, - 20, - 104, - 104, - 98, - s, - [102, 3], - s, - [58, 4], - s, - [66, 7], - 102, - s, - [66, 3], - s, - [55, 18], - s, - [50, 10], - 90, - s, - [50, 7], - s, - [49, 17], - s, - [53, 18], - s, - [54, 17], - 105, - 106, - 104, - s, - [92, 3], - 107, - s, - [96, 4], - 35, - 35, - 36, - 36, - 37, - 37, - c, - [425, 3], - s, - [103, 3], - 109, - 110, - 62, - 62, - 80, - 80, - 117, - 80, - 80, - 115, - 116, - 113, - 80, - 80, - 90, - 90, - c, - [624, 4], - s, - [48, 17], - s, - [84, 19], - s, - [85, 19], - c, - [331, 4], - s, - [97, 4], - 99, - s, - [60, 4], - c, - [210, 11], - c, - [85, 6], - s, - [65, 12], - 31, - 44, - s, - [75, 5], - 125, - 126, - 127, - s, - [75, 8], - s, - [71, 16], - s, - [72, 16], - s, - [66, 6], - 64, - 64, - 89, - 89, - 131, - 106, - 130, - 61, - 61, - 63, - 63, - s, - [79, 6], - s, - [70, 9], - 132, - s, - [70, 3], - s, - [76, 13], - s, - [77, 13], - s, - [78, 13], - 134, - 135, - 133, - 68, - 68, - 117, - 68, - 115, - 116, - s, - [94, 3], - 93, - s, - [95, 3], - s, - [69, 12], - s, - [73, 16], - s, - [74, 16], - s, - [66, 6], - s, - [93, 3], - 107, - 67, - 67, - 117, - 67, - 115, - 116 -]) -}), -defaultActions: bda({ - idx: u([ - 0, - 3, - 5, - 7, - 8, - s, - [10, 7, 1], - 24, - 25, - 26, - s, - [29, 4, 1], - 34, - 37, - 38, - s, - [42, 5, 1], - 48, - 50, - 56, - 57, - 58, - 61, - s, - [63, 6, 1], - s, - [70, 7, 1], - 78, - 79, - 80, - 83, - 84, - 86, - 88, - 89, - 90, - s, - [93, 4, 1], - 98, - 100, - 103, - 104, - 105, - 107, - 108, - 109, - 112, - s, - [115, 5, 1], - 121, - 122, - 123, - 125, - 126, - 127, - s, - [131, 5, 1] -]), - goto: u([ - 10, - 6, - 9, - 12, - 13, - s, - [15, 7, 1], - 41, - 42, - 43, - 3, - 11, - 83, - 14, - 52, - 27, - 28, - 45, - 81, - 82, - 100, - 101, - 39, - 33, - 7, - 8, - 59, - 47, - 22, - 23, - 29, - 30, - 24, - 25, - 86, - 87, - 88, - 26, - 44, - 31, - 32, - 1, - 2, - 5, - 102, - 58, - 55, - 49, - 53, - 54, - 96, - 35, - 36, - 37, - 103, - 62, - 48, - 84, - 85, - 97, - 99, - 60, - 65, - 71, - 72, - 66, - 64, - 89, - 61, - 63, - 79, - 76, - 77, - 78, - 95, - 69, - 73, - 74, - 66 -]) -}), -parseError: function parseError(str, hash, ExceptionClass) { - if (hash.recoverable && typeof this.trace === 'function') { - this.trace(str); - hash.destroy(); // destroy... well, *almost*! - } else { - if (!ExceptionClass) { - ExceptionClass = this.JisonParserError; - } - throw new ExceptionClass(str, hash); - } -}, -parse: function parse(input) { - var self = this, - stack = new Array(128), // token stack: stores token which leads to state at the same index (column storage) - sstack = new Array(128), // state stack: stores states (column storage) - - vstack = new Array(128), // semantic value stack - lstack = new Array(128), // location stack - table = this.table, - sp = 0; // 'stack pointer': index into the stacks - - var recovering = 0; // (only used when the grammar contains error recovery rules) - var TERROR = this.TERROR, - EOF = this.EOF, - ERROR_RECOVERY_TOKEN_DISCARD_COUNT = (this.options.errorRecoveryTokenDiscardCount | 0) || 3; - var NO_ACTION = [0, table.length /* ensures that anyone using this new state will fail dramatically! */]; - - //this.reductionCount = this.shiftCount = 0; - - var lexer; - if (this.__lexer__) { - lexer = this.__lexer__; - } else { - lexer = this.__lexer__ = Object.create(this.lexer); - } - - var sharedState_yy = { - parseError: null, - quoteName: null, - lexer: null, - parser: null, - pre_parse: null, - post_parse: null - }; - // copy state - for (var k in this.yy) { - if (Object.prototype.hasOwnProperty.call(this.yy, k)) { - sharedState_yy[k] = this.yy[k]; - } - } - - sharedState_yy.lexer = lexer; - sharedState_yy.parser = this; - - - - - - - - - - - - - - - - - - - - // *Always* setup `yyError`, `YYRECOVERING`, `yyErrOk` and `yyClearIn` functions as it is paramount - // to have *their* closure match ours -- if we only set them up once, - // any subsequent `parse()` runs will fail in very obscure ways when - // these functions are invoked in the user action code block(s) as - // their closure will still refer to the `parse()` instance which set - // them up. Hence we MUST set them up at the start of every `parse()` run! - if (this.yyError) { - this.yyError = function yyError(str /*, ...args */) { - - - - var error_rule_depth = (this.options.parserErrorsAreRecoverable ? locateNearestErrorRecoveryRule(state) : -1); - var expected = this.collect_expected_token_set(state); - var hash = this.constructParseErrorInfo(str, null, expected, (error_rule_depth >= 0)); - - - // Add any extra args to the hash under the name `extra_error_attributes`: - var args = Array.prototype.slice.call(arguments, 1); - if (args.length) { - hash.extra_error_attributes = args; - } - - var r = this.parseError(str, hash, this.JisonParserError); - return r; - }; - } - - - - - - - lexer.setInput(input, sharedState_yy); - - var yyloc = lexer.yylloc || {}; - lstack[sp] = yyloc; - vstack[sp] = null; - sstack[sp] = 0; - stack[sp] = 0; - ++sp; - - - - - - var ranges = lexer.options && lexer.options.ranges; - - // Does the shared state override the default `parseError` that already comes with this instance? - if (typeof sharedState_yy.parseError === 'function') { - this.parseError = function parseErrorAlt(str, hash, ExceptionClass) { - if (!ExceptionClass) { - ExceptionClass = this.JisonParserError; - } - return sharedState_yy.parseError(str, hash, ExceptionClass); - }; - } else { - this.parseError = this.originalParseError; - } - - // Does the shared state override the default `quoteName` that already comes with this instance? - if (typeof sharedState_yy.quoteName === 'function') { - this.quoteName = sharedState_yy.quoteName; - } else { - this.quoteName = this.originalQuoteName; - } - - // set up the cleanup function; make it an API so that external code can re-use this one in case of - // calamities or when the `%options no-try-catch` option has been specified for the grammar, in which - // case this parse() API method doesn't come with a `finally { ... }` block any more! - // - // NOTE: as this API uses parse() as a closure, it MUST be set again on every parse() invocation, - // or else your `sharedState`, etc. references will be *wrong*! - this.cleanupAfterParse = function parser_cleanupAfterParse(resultValue, invoke_post_methods, do_not_nuke_errorinfos) { - var rv; - - if (invoke_post_methods) { - if (sharedState_yy.post_parse) { - rv = sharedState_yy.post_parse.call(this, sharedState_yy, resultValue); - if (typeof rv !== 'undefined') resultValue = rv; - } - if (this.post_parse) { - rv = this.post_parse.call(this, sharedState_yy, resultValue); - if (typeof rv !== 'undefined') resultValue = rv; - } - } - - if (this.__reentrant_call_depth > 1) return resultValue; // do not (yet) kill the sharedState when this is a reentrant run. - - // clean up the lingering lexer structures as well: - if (lexer.cleanupAfterLex) { - lexer.cleanupAfterLex(do_not_nuke_errorinfos); - } - - // prevent lingering circular references from causing memory leaks: - if (sharedState_yy) { - sharedState_yy.parseError = undefined; - sharedState_yy.quoteName = undefined; - sharedState_yy.lexer = undefined; - sharedState_yy.parser = undefined; - if (lexer.yy === sharedState_yy) { - lexer.yy = undefined; - } - } - sharedState_yy = undefined; - this.parseError = this.originalParseError; - this.quoteName = this.originalQuoteName; - - // nuke the vstack[] array at least as that one will still reference obsoleted user values. - // To be safe, we nuke the other internal stack columns as well... - stack.length = 0; // fastest way to nuke an array without overly bothering the GC - sstack.length = 0; - lstack.length = 0; - vstack.length = 0; - sp = 0; - - // nuke the error hash info instances created during this run. - // Userland code must COPY any data/references - // in the error hash instance(s) it is more permanently interested in. - if (!do_not_nuke_errorinfos) { - for (var i = this.__error_infos.length - 1; i >= 0; i--) { - var el = this.__error_infos[i]; - if (el && typeof el.destroy === 'function') { - el.destroy(); - } - } - this.__error_infos.length = 0; - } - - return resultValue; - }; - - // NOTE: as this API uses parse() as a closure, it MUST be set again on every parse() invocation, - // or else your `lexer`, `sharedState`, etc. references will be *wrong*! - this.constructParseErrorInfo = function parser_constructParseErrorInfo(msg, ex, expected, recoverable) { - var pei = { - errStr: msg, - exception: ex, - text: lexer.match, - value: lexer.yytext, - token: this.describeSymbol(symbol) || symbol, - token_id: symbol, - line: lexer.yylineno, - loc: lexer.yylloc || {}, - expected: expected, - recoverable: recoverable, - state: state, - action: action, - new_state: newState, - symbol_stack: stack, - state_stack: sstack, - value_stack: vstack, - location_stack: lstack, - stack_pointer: sp, - yy: sharedState_yy, - lexer: lexer, - parser: this, - - // and make sure the error info doesn't stay due to potential - // ref cycle via userland code manipulations. - // These would otherwise all be memory leak opportunities! - // - // Note that only array and object references are nuked as those - // constitute the set of elements which can produce a cyclic ref. - // The rest of the members is kept intact as they are harmless. - destroy: function destructParseErrorInfo() { - // remove cyclic references added to error info: - // info.yy = null; - // info.lexer = null; - // info.value = null; - // info.value_stack = null; - // ... - var rec = !!this.recoverable; - for (var key in this) { - if (this.hasOwnProperty(key) && typeof key === 'object') { - this[key] = undefined; - } - } - this.recoverable = rec; - } - }; - // track this instance so we can `destroy()` it once we deem it superfluous and ready for garbage collection! - this.__error_infos.push(pei); - return pei; - }; - - - function lex() { - var token = lexer.lex(); - // if token isn't its numeric value, convert - if (typeof token !== 'number') { - token = self.symbols_[token] || token; - } - return token || EOF; - } - - - var symbol = 0; - var preErrorSymbol = 0; - var lastEofErrorStateDepth = 0; - var state, action, r, t; - var yyval = { - $: true, - _$: undefined, - yy: sharedState_yy - }; - var p, len, this_production; - var lstack_begin, lstack_end; - var newState; - var retval = false; - - - // Return the rule stack depth where the nearest error rule can be found. - // Return -1 when no error recovery rule was found. - function locateNearestErrorRecoveryRule(state) { - var stack_probe = sp - 1; - var depth = 0; - - // try to recover from error - for (;;) { - // check for error recovery rule in this state - - var t = table[state][TERROR] || NO_ACTION; - if (t[0]) { - // We need to make sure we're not cycling forever: - // once we hit EOF, even when we `yyerrok()` an error, we must - // prevent the core from running forever, - // e.g. when parent rules are still expecting certain input to - // follow after this, for example when you handle an error inside a set - // of braces which are matched by a parent rule in your grammar. - // - // Hence we require that every error handling/recovery attempt - // *after we've hit EOF* has a diminishing state stack: this means - // we will ultimately have unwound the state stack entirely and thus - // terminate the parse in a controlled fashion even when we have - // very complex error/recovery code interplay in the core + user - // action code blocks: - - if (symbol === EOF) { - if (!lastEofErrorStateDepth) { - lastEofErrorStateDepth = sp - 1 - depth; - } else if (lastEofErrorStateDepth <= sp - 1 - depth) { - - --stack_probe; // popStack(1): [symbol, action] - state = sstack[stack_probe]; - ++depth; - continue; - } - } - return depth; - } - if (state === 0 /* $accept rule */ || stack_probe < 1) { - - return -1; // No suitable error recovery rule available. - } - --stack_probe; // popStack(1): [symbol, action] - state = sstack[stack_probe]; - ++depth; - } - } - - - try { - this.__reentrant_call_depth++; - - if (this.pre_parse) { - this.pre_parse.call(this, sharedState_yy); - } - if (sharedState_yy.pre_parse) { - sharedState_yy.pre_parse.call(this, sharedState_yy); - } - - newState = sstack[sp - 1]; - for (;;) { - // retrieve state number from top of stack - state = newState; // sstack[sp - 1]; - - // use default actions if available - if (this.defaultActions[state]) { - action = 2; - newState = this.defaultActions[state]; - } else { - // The single `==` condition below covers both these `===` comparisons in a single - // operation: - // - // if (symbol === null || typeof symbol === 'undefined') ... - if (!symbol) { - symbol = lex(); - } - // read action for current state and first input - t = (table[state] && table[state][symbol]) || NO_ACTION; - newState = t[1]; - action = t[0]; - - - - - // handle parse error - if (!action) { - // first see if there's any chance at hitting an error recovery rule: - var error_rule_depth = locateNearestErrorRecoveryRule(state); - var errStr = null; - var errSymbolDescr = (this.describeSymbol(symbol) || symbol); - var expected = this.collect_expected_token_set(state); - - if (!recovering) { - // Report error - if (typeof lexer.yylineno === 'number') { - errStr = 'Parse error on line ' + (lexer.yylineno + 1) + ': '; - } else { - errStr = 'Parse error: '; - } - if (lexer.showPosition) { - errStr += '\n' + lexer.showPosition(79 - 10, 10) + '\n'; - } - if (expected.length) { - errStr += 'Expecting ' + expected.join(', ') + ', got unexpected ' + errSymbolDescr; - } else { - errStr += 'Unexpected ' + errSymbolDescr; - } - p = this.constructParseErrorInfo(errStr, null, expected, (error_rule_depth >= 0)); - r = this.parseError(p.errStr, p, this.JisonParserError); - - - if (!p.recoverable) { - retval = r; - break; - } else { - // TODO: allow parseError callback to edit symbol and or state at the start of the error recovery process... - } - } - - - - // just recovered from another error - if (recovering === ERROR_RECOVERY_TOKEN_DISCARD_COUNT && error_rule_depth >= 0) { - // only barf a fatal hairball when we're out of look-ahead symbols and none hit a match; - // this DOES discard look-ahead while recovering from an error when said look-ahead doesn't - // suit the error recovery rules... The error HAS been reported already so we're fine with - // throwing away a few items if that is what it takes to match the nearest recovery rule! - if (symbol === EOF || preErrorSymbol === EOF) { - p = this.__error_infos[this.__error_infos.length - 1]; - if (!p) { - p = this.constructParseErrorInfo('Parsing halted while starting to recover from another error.', null, expected, false); - } else { - p.errStr = 'Parsing halted while starting to recover from another error. Previous error which resulted in this fatal result: ' + p.errStr; - p.recoverable = false; - } - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - - // discard current lookahead and grab another - - - - yyloc = lexer.yylloc || {}; - - symbol = lex(); - - - } - - // try to recover from error - if (error_rule_depth < 0) { - p = this.constructParseErrorInfo((errStr || 'Parsing halted. No suitable error recovery rule available.'), null, expected, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - sp -= error_rule_depth; - - preErrorSymbol = (symbol === TERROR ? 0 : symbol); // save the lookahead token - symbol = TERROR; // insert generic error symbol as new lookahead - // allow N (default: 3) real symbols to be shifted before reporting a new error - recovering = ERROR_RECOVERY_TOKEN_DISCARD_COUNT; - - newState = sstack[sp - 1]; - - - - continue; - } - - - } - - - - - - - - - - switch (action) { - // catch misc. parse failures: - default: - // this shouldn't happen, unless resolve defaults are off - if (action instanceof Array) { - p = this.constructParseErrorInfo(('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol), null, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - // Another case of better safe than sorry: in case state transitions come out of another error recovery process - // or a buggy LUT (LookUp Table): - p = this.constructParseErrorInfo('Parsing halted. No viable error recovery approach available due to internal system failure.', null, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - - // shift: - case 1: - //this.shiftCount++; - stack[sp] = symbol; - vstack[sp] = lexer.yytext; - lstack[sp] = lexer.yylloc || {}; - sstack[sp] = newState; // push state - ++sp; - symbol = 0; - if (!preErrorSymbol) { // normal execution / no error - // Pick up the lexer details for the current symbol as that one is not 'look-ahead' any more: - - - - yyloc = lexer.yylloc || {}; - - if (recovering > 0) { - recovering--; - - } - } else { - // error just occurred, resume old lookahead f/ before error, *unless* that drops us straight back into error mode: - symbol = preErrorSymbol; - preErrorSymbol = 0; - - // read action for current state and first input - t = (table[newState] && table[newState][symbol]) || NO_ACTION; - if (!t[0] || symbol === TERROR) { - // forget about that symbol and move forward: this wasn't a 'forgot to insert' error type where - // (simple) stuff might have been missing before the token which caused the error we're - // recovering from now... - // - // Also check if the LookAhead symbol isn't the ERROR token we set as part of the error - // recovery, for then this we would we idling (cycling) on the error forever. - // Yes, this does not take into account the possibility that the *lexer* may have - // produced a *new* TERROR token all by itself, but that would be a very peculiar grammar! - - symbol = 0; - } - } - - continue; - - // reduce: - case 2: - //this.reductionCount++; - this_production = this.productions_[newState - 1]; // `this.productions_[]` is zero-based indexed while states start from 1 upwards... - len = this_production[1]; - lstack_end = sp; - lstack_begin = lstack_end - (len || 1); - lstack_end--; - - - - // Make sure subsequent `$$ = $1` default action doesn't fail - // for rules where len==0 as then there's no $1 (you're reducing an epsilon rule then!) - // - // Also do this to prevent nasty action block codes to *read* `$0` or `$$` - // and *not* get `undefined` as a result for their efforts! - vstack[sp] = undefined; - - // perform semantic action - yyval.$ = vstack[sp - len]; // default to $$ = $1; result must produce `undefined` when len == 0, as then there's no $1 - - // default location, uses first token for firsts, last for lasts - yyval._$ = { - first_line: lstack[lstack_begin].first_line, - last_line: lstack[lstack_end].last_line, - first_column: lstack[lstack_begin].first_column, - last_column: lstack[lstack_end].last_column - }; - if (ranges) { - yyval._$.range = [lstack[lstack_begin].range[0], lstack[lstack_end].range[1]]; - } - - r = this.performAction.call(yyval, yyloc, newState, sp - 1, vstack, lstack); - - if (typeof r !== 'undefined') { - retval = r; - break; - } - - // pop off stack - sp -= len; - - // don't overwrite the `symbol` variable: use a local var to speed things up: - var ntsymbol = this_production[0]; // push nonterminal (reduce) - stack[sp] = ntsymbol; - vstack[sp] = yyval.$; - lstack[sp] = yyval._$; - // goto new state = table[STATE][NONTERMINAL] - newState = table[sstack[sp - 1]][ntsymbol]; - sstack[sp] = newState; - ++sp; - - continue; - - // accept: - case 3: - retval = true; - // Return the `$accept` rule's `$$` result, if available. - // - // Also note that JISON always adds this top-most `$accept` rule (with implicit, - // default, action): - // - // $accept: $end - // %{ $$ = $1; @$ = @1; %} - // - // which, combined with the parse kernel's `$accept` state behaviour coded below, - // will produce the `$$` value output of the rule as the parse result, - // IFF that result is *not* `undefined`. (See also the parser kernel code.) - // - // In code: - // - // %{ - // @$ = @1; // if location tracking support is included - // if (typeof $1 !== 'undefined') - // return $1; - // else - // return true; // the default parse result if the rule actions don't produce anything - // %} - if (typeof yyval.$ !== 'undefined') { - retval = yyval.$; - } - break; - } - - // break out of loop: we accept or fail with error - break; - } - } catch (ex) { - // report exceptions through the parseError callback too, but keep the exception intact - // if it is a known parser or lexer error which has been thrown by parseError() already: - if (ex instanceof this.JisonParserError) { - throw ex; - } - else if (lexer && typeof lexer.JisonLexerError === 'function' && ex instanceof lexer.JisonLexerError) { - throw ex; - } - else { - p = this.constructParseErrorInfo('Parsing aborted due to exception.', ex, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - } - } finally { - retval = this.cleanupAfterParse(retval, true, true); - this.__reentrant_call_depth--; - } - - return retval; -}, -yyError: 1 -}; -parser.originalParseError = parser.parseError; -parser.originalQuoteName = parser.quoteName; - -var fs = require('fs'); -var transform = require('./ebnf-transform').transform; -var ebnf = false; -var XRegExp = require('xregexp'); // for helping out the `%options xregexp` in the lexer - - -// transform ebnf to bnf if necessary -function extend(json, grammar) { - json.bnf = ebnf ? transform(grammar.grammar) : grammar.grammar; - if (grammar.actionInclude) { - json.actionInclude = grammar.actionInclude; - } - return json; -} - -// convert string value to number or boolean value, when possible -// (and when this is more or less obviously the intent) -// otherwise produce the string itself as value. -function parseValue(v) { - if (v === 'false') { - return false; - } - if (v === 'true') { - return true; - } - // http://stackoverflow.com/questions/175739/is-there-a-built-in-way-in-javascript-to-check-if-a-string-is-a-valid-number - // Note that the `v` check ensures that we do not convert `undefined`, `null` and `''` (empty string!) - if (v && !isNaN(v)) { - var rv = +v; - if (isFinite(rv)) { - return rv; - } - } - return v; -} -/* lexer generated by jison-lex 0.3.4-166 */ -/* - * Returns a Lexer object of the following structure: - * - * Lexer: { - * yy: {} The so-called "shared state" or rather the *source* of it; - * the real "shared state" `yy` passed around to - * the rule actions, etc. is a derivative/copy of this one, - * not a direct reference! - * } - * - * Lexer.prototype: { - * yy: {}, - * EOF: 1, - * ERROR: 2, - * - * JisonLexerError: function(msg, hash), - * - * performAction: function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * where `...` denotes the (optional) additional arguments the user passed to - * `lexer.lex(...)` and specified by way of `%parse-param ...` in the **parser** grammar file - * - * The function parameters and `this` have the following value/meaning: - * - `this` : reference to the `lexer` instance. - * - * - `yy` : a reference to the `yy` "shared state" object which was passed to the lexer - * by way of the `lexer.setInput(str, yy)` API before. - * - * - `yy_` : lexer instance reference used internally. - * - * - `$avoiding_name_collisions` : index of the matched lexer rule (regex), used internally. - * - * - `YY_START`: the current lexer "start condition" state. - * - * - `...` : the extra arguments you specified in the `%parse-param` statement in your - * **parser** grammar definition file and which are passed to the lexer via - * its `lexer.lex(...)` API. - * - * parseError: function(str, hash, ExceptionClass), - * - * constructLexErrorInfo: function(error_message, is_recoverable), - * Helper function. - * Produces a new errorInfo 'hash object' which can be passed into `parseError()`. - * See it's use in this lexer kernel in many places; example usage: - * - * var infoObj = lexer.constructParseErrorInfo('fail!', true); - * var retVal = lexer.parseError(infoObj.errStr, infoObj, lexer.JisonLexerError); - * - * options: { ... lexer %options ... }, - * - * lex: function([args...]), - * Produce one token of lexed input, which was passed in earlier via the `lexer.setInput()` API. - * You MAY use the additional `args...` parameters as per `%parse-param` spec of the **parser** grammar: - * these extra `args...` are passed verbatim to the lexer rules' action code. - * - * cleanupAfterLex: function(do_not_nuke_errorinfos), - * Helper function. - * This helper API is invoked when the parse process has completed. This helper may - * be invoked by user code to ensure the internal lexer gets properly garbage collected. - * - * setInput: function(input, [yy]), - * input: function(), - * unput: function(str), - * more: function(), - * reject: function(), - * less: function(n), - * pastInput: function(n), - * upcomingInput: function(n), - * showPosition: function(), - * test_match: function(regex_match_array, rule_index), - * next: function(...), - * lex: function(...), - * begin: function(condition), - * pushState: function(condition), - * popState: function(), - * topState: function(), - * _currentRules: function(), - * stateStackSize: function(), - * - * options: { ... lexer %options ... }, - * - * performAction: function(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * rules: [...], - * conditions: {associative list: name ==> set}, - * } - * - * - * token location info (`yylloc`): { - * first_line: n, - * last_line: n, - * first_column: n, - * last_column: n, - * range: [start_number, end_number] - * (where the numbers are indexes into the input string, zero-based) - * } - * - * --- - * - * The `parseError` function receives a 'hash' object with these members for lexer errors: - * - * { - * text: (matched text) - * token: (the produced terminal token, if any) - * token_id: (the produced terminal token numeric ID, if any) - * line: (yylineno) - * loc: (yylloc) - * recoverable: (boolean: TRUE when the parser MAY have an error recovery rule - * available for this particular error) - * yy: (object: the current parser internal "shared state" `yy` - * as is also available in the rule actions; this can be used, - * for instance, for advanced error analysis and reporting) - * lexer: (reference to the current lexer instance used by the parser) - * } - * - * while `this` will reference the current lexer instance. - * - * When `parseError` is invoked by the lexer, the default implementation will - * attempt to invoke `yy.parser.parseError()`; when this callback is not provided - * it will try to invoke `yy.parseError()` instead. When that callback is also not - * provided, a `JisonLexerError` exception will be thrown containing the error - * message and `hash`, as constructed by the `constructLexErrorInfo()` API. - * - * Note that the lexer's `JisonLexerError` error class is passed via the - * `ExceptionClass` argument, which is invoked to construct the exception - * instance to be thrown, so technically `parseError` will throw the object - * produced by the `new ExceptionClass(str, hash)` JavaScript expression. - * - * --- - * - * You can specify lexer options by setting / modifying the `.options` object of your Lexer instance. - * These options are available: - * - * (Options are permanent.) - * - * yy: { - * parseError: function(str, hash, ExceptionClass) - * optional: overrides the default `parseError` function. - * } - * - * lexer.options: { - * pre_lex: function() - * optional: is invoked before the lexer is invoked to produce another token. - * `this` refers to the Lexer object. - * post_lex: function(token) { return token; } - * optional: is invoked when the lexer has produced a token `token`; - * this function can override the returned token value by returning another. - * When it does not return any (truthy) value, the lexer will return - * the original `token`. - * `this` refers to the Lexer object. - * - * WARNING: the next set of options are not meant to be changed. They echo the abilities of - * the lexer as per when it was compiled! - * - * ranges: boolean - * optional: `true` ==> token location info will include a .range[] member. - * flex: boolean - * optional: `true` ==> flex-like lexing behaviour where the rules are tested - * exhaustively to find the longest match. - * backtrack_lexer: boolean - * optional: `true` ==> lexer regexes are tested in order and for invoked; - * the lexer terminates the scan when a token is returned by the action code. - * xregexp: boolean - * optional: `true` ==> lexer rule regexes are "extended regex format" requiring the - * `XRegExp` library. When this %option has not been specified at compile time, all lexer - * rule regexes have been written as standard JavaScript RegExp expressions. - * } - */ - - -var lexer = (function () { -// See also: -// http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508 -// but we keep the prototype.constructor and prototype.name assignment lines too for compatibility -// with userland code which might access the derived class in a 'classic' way. -function JisonLexerError(msg, hash) { - Object.defineProperty(this, 'name', { - enumerable: false, - writable: false, - value: 'JisonLexerError' - }); - - if (msg == null) msg = '???'; - - Object.defineProperty(this, 'message', { - enumerable: false, - writable: true, - value: msg - }); - - this.hash = hash; - - var stacktrace; - if (hash && hash.exception instanceof Error) { - var ex2 = hash.exception; - this.message = ex2.message || msg; - stacktrace = ex2.stack; - } - if (!stacktrace) { - if (Error.hasOwnProperty('captureStackTrace')) { // V8 - Error.captureStackTrace(this, this.constructor); - } else { - stacktrace = (new Error(msg)).stack; - } - } - if (stacktrace) { - Object.defineProperty(this, 'stack', { - enumerable: false, - writable: false, - value: stacktrace - }); - } -} - -if (typeof Object.setPrototypeOf === 'function') { - Object.setPrototypeOf(JisonLexerError.prototype, Error.prototype); -} else { - JisonLexerError.prototype = Object.create(Error.prototype); -} -JisonLexerError.prototype.constructor = JisonLexerError; -JisonLexerError.prototype.name = 'JisonLexerError'; - - - - -var lexer = { - - // Code Generator Information Report - // --------------------------------- - // - // Options: - // - // backtracking: .................... false - // location.ranges: ................. true - // location line+column tracking: ... true - // - // - // Forwarded Parser Analysis flags: - // - // uses yyleng: ..................... false - // uses yylineno: ................... false - // uses yytext: ..................... false - // uses yylloc: ..................... false - // uses lexer values: ............... true / true - // location tracking: ............... true - // location assignment: ............. false - // - // - // Lexer Analysis flags: - // - // uses yyleng: ..................... undefined - // uses yylineno: ................... undefined - // uses yytext: ..................... undefined - // uses yylloc: ..................... undefined - // uses ParseError API: ............. undefined - // uses location tracking & editing: undefined - // uses more() API: ................. undefined - // uses unput() API: ................ undefined - // uses reject() API: ............... undefined - // uses less() API: ................. undefined - // uses display APIs pastInput(), upcomingInput(), showPosition(): - // ............................. undefined - // uses describeYYLLOC() API: ....... undefined - // - // --------- END OF REPORT ----------- - - - EOF: 1, - ERROR: 2, - - // JisonLexerError: JisonLexerError, /// <-- injected by the code generator - - // options: {}, /// <-- injected by the code generator - - // yy: ..., /// <-- injected by setInput() - - __currentRuleSet__: null, /// <-- internal rule set cache for the current lexer state - - __error_infos: [], /// INTERNAL USE ONLY: the set of lexErrorInfo objects created since the last cleanup - - __decompressed: false, /// INTERNAL USE ONLY: mark whether the lexer instance has been 'unfolded' completely and is now ready for use - - done: false, /// INTERNAL USE ONLY - _backtrack: false, /// INTERNAL USE ONLY - _input: '', /// INTERNAL USE ONLY - _more: false, /// INTERNAL USE ONLY - _signaled_error_token: false, /// INTERNAL USE ONLY - - conditionStack: [], /// INTERNAL USE ONLY; managed via `pushState()`, `popState()`, `topState()` and `stateStackSize()` - - match: '', /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks input which has been matched so far for the lexer token under construction. `match` is identical to `yytext` except that this one still contains the matched input string after `lexer.performAction()` has been invoked, where userland code MAY have changed/replaced the `yytext` value entirely! - matched: '', /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks entire input which has been matched so far - matches: false, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks RE match result for last (successful) match attempt - yytext: '', /// ADVANCED USE ONLY: tracks input which has been matched so far for the lexer token under construction; this value is transferred to the parser as the 'token value' when the parser consumes the lexer token produced through a call to the `lex()` API. - offset: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks the 'cursor position' in the input string, i.e. the number of characters matched so far - yyleng: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: length of matched input for the token under construction (`yytext`) - yylineno: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: 'line number' at which the token under construction is located - yylloc: null, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks location info (lines + columns) for the token under construction - - /** - INTERNAL USE: construct a suitable error info hash object instance for `parseError`. - - @public - @this {RegExpLexer} - */ - constructLexErrorInfo: function lexer_constructLexErrorInfo(msg, recoverable) { - /** @constructor */ - var pei = { - errStr: msg, - recoverable: !!recoverable, - text: this.match, // This one MAY be empty; userland code should use the `upcomingInput` API to obtain more text which follows the 'lexer cursor position'... - token: null, - line: this.yylineno, - loc: this.yylloc, - yy: this.yy, - lexer: this, - - /** - and make sure the error info doesn't stay due to potential - ref cycle via userland code manipulations. - These would otherwise all be memory leak opportunities! - - Note that only array and object references are nuked as those - constitute the set of elements which can produce a cyclic ref. - The rest of the members is kept intact as they are harmless. - - @public - @this {LexErrorInfo} - */ - destroy: function destructLexErrorInfo() { - // remove cyclic references added to error info: - // info.yy = null; - // info.lexer = null; - // ... - var rec = !!this.recoverable; - for (var key in this) { - if (this.hasOwnProperty(key) && typeof key === 'object') { - this[key] = undefined; - } - } - this.recoverable = rec; - } - }; - // track this instance so we can `destroy()` it once we deem it superfluous and ready for garbage collection! - this.__error_infos.push(pei); - return pei; - }, - - /** - handler which is invoked when a lexer error occurs. - - @public - @this {RegExpLexer} - */ - parseError: function lexer_parseError(str, hash, ExceptionClass) { - if (!ExceptionClass) { - ExceptionClass = this.JisonLexerError; - } - if (this.yy.parser && typeof this.yy.parser.parseError === 'function') { - return this.yy.parser.parseError(str, hash, ExceptionClass) || this.ERROR; - } else if (typeof this.yy.parseError === 'function') { - return this.yy.parseError(str, hash, ExceptionClass) || this.ERROR; - } else { - throw new ExceptionClass(str, hash); - } - }, - - /** - method which implements `yyerror(str, ...args)` functionality for use inside lexer actions. - - @public - @this {RegExpLexer} - */ - yyerror: function yyError(str /*, ...args */) { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': ' + str, this.options.lexerErrorsAreRecoverable); - - // Add any extra args to the hash under the name `extra_error_attributes`: - var args = Array.prototype.slice.call(arguments, 1); - if (args.length) { - hash.extra_error_attributes = args; - } - - return (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - }, - - /** - final cleanup function for when we have completed lexing the input; - make it an API so that external code can use this one once userland - code has decided it's time to destroy any lingering lexer error - hash object instances and the like: this function helps to clean - up these constructs, which *may* carry cyclic references which would - otherwise prevent the instances from being properly and timely - garbage-collected, i.e. this function helps prevent memory leaks! - - @public - @this {RegExpLexer} - */ - cleanupAfterLex: function lexer_cleanupAfterLex(do_not_nuke_errorinfos) { - var rv; - - // prevent lingering circular references from causing memory leaks: - this.setInput('', {}); - - // nuke the error hash info instances created during this run. - // Userland code must COPY any data/references - // in the error hash instance(s) it is more permanently interested in. - if (!do_not_nuke_errorinfos) { - for (var i = this.__error_infos.length - 1; i >= 0; i--) { - var el = this.__error_infos[i]; - if (el && typeof el.destroy === 'function') { - el.destroy(); - } - } - this.__error_infos.length = 0; - } - - return this; - }, - - /** - clear the lexer token context; intended for internal use only - - @public - @this {RegExpLexer} - */ - clear: function lexer_clear() { - this.yytext = ''; - this.yyleng = 0; - this.match = ''; - this.matches = false; - this._more = false; - this._backtrack = false; - - var col = this.yylloc ? this.yylloc.last_column : 0; - this.yylloc = { - first_line: this.yylineno + 1, - first_column: col, - last_line: this.yylineno + 1, - last_column: col, - - range: (this.options.ranges ? [this.offset, this.offset] : undefined) - }; - }, - - /** - resets the lexer, sets new input - - @public - @this {RegExpLexer} - */ - setInput: function lexer_setInput(input, yy) { - this.yy = yy || this.yy || {}; - - // also check if we've fully initialized the lexer instance, - // including expansion work to be done to go from a loaded - // lexer to a usable lexer: - if (!this.__decompressed) { - // step 1: decompress the regex list: - var rules = this.rules; - for (var i = 0, len = rules.length; i < len; i++) { - var rule_re = rules[i]; - - // compression: is the RE an xref to another RE slot in the rules[] table? - if (typeof rule_re === 'number') { - rules[i] = rules[rule_re]; - } - } - - // step 2: unfold the conditions[] set to make these ready for use: - var conditions = this.conditions; - for (var k in conditions) { - var spec = conditions[k]; - - var rule_ids = spec.rules; - - var len = rule_ids.length; - var rule_regexes = new Array(len + 1); // slot 0 is unused; we use a 1-based index approach here to keep the hottest code in `lexer_next()` fast and simple! - var rule_new_ids = new Array(len + 1); - - for (var i = 0; i < len; i++) { - var idx = rule_ids[i]; - var rule_re = rules[idx]; - rule_regexes[i + 1] = rule_re; - rule_new_ids[i + 1] = idx; - } - - spec.rules = rule_new_ids; - spec.__rule_regexes = rule_regexes; - spec.__rule_count = len; - } - - this.__decompressed = true; - } - - this._input = input || ''; - this.clear(); - this._signaled_error_token = false; - this.done = false; - this.yylineno = 0; - this.matched = ''; - this.conditionStack = ['INITIAL']; - this.__currentRuleSet__ = null; - this.yylloc = { - first_line: 1, - first_column: 0, - last_line: 1, - last_column: 0, - - range: (this.options.ranges ? [0, 0] : undefined) - }; - this.offset = 0; - return this; - }, - - /** - push a new input into the lexer and activate it: - the old input position is stored and will be resumed - once this new input has been consumed. - - Use this API to help implement C-preprocessor-like - `#include` statements. - - Available options: - - - `emit_EOF_at_end` : {int} the `EOF`-like token to emit - when the new input is consumed: use - this to mark the end of the new input - in the parser grammar. zero/falsey - token value means no end marker token - will be emitted before the lexer - resumes reading from the previous input. - - @public - @this {RegExpLexer} - */ - pushInput: function lexer_pushInput(input, label, options) { - options = options || {}; - - this._input = input || ''; - this.clear(); - // this._signaled_error_token = false; - this.done = false; - this.yylineno = 0; - this.matched = ''; - // this.conditionStack = ['INITIAL']; - // this.__currentRuleSet__ = null; - this.yylloc = { - first_line: 1, - first_column: 0, - last_line: 1, - last_column: 0, - - range: (this.options.ranges ? [0, 0] : undefined) - }; - this.offset = 0; - return this; - }, - - /** - consumes and returns one char from the input - - @public - @this {RegExpLexer} - */ - input: function lexer_input() { - if (!this._input) { - //this.done = true; -- don't set `done` as we want the lex()/next() API to be able to produce one custom EOF token match after this anyhow. (lexer can match special <> tokens and perform user action code for a <> match, but only does so *once*) - return null; - } - var ch = this._input[0]; - this.yytext += ch; - this.yyleng++; - this.offset++; - this.match += ch; - this.matched += ch; - // Count the linenumber up when we hit the LF (or a stand-alone CR). - // On CRLF, the linenumber is incremented when you fetch the CR or the CRLF combo - // and we advance immediately past the LF as well, returning both together as if - // it was all a single 'character' only. - var slice_len = 1; - var lines = false; - if (ch === '\n') { - lines = true; - } else if (ch === '\r') { - lines = true; - var ch2 = this._input[1]; - if (ch2 === '\n') { - slice_len++; - ch += ch2; - this.yytext += ch2; - this.yyleng++; - this.offset++; - this.match += ch2; - this.matched += ch2; - if (this.options.ranges) { - this.yylloc.range[1]++; - } - } - } - if (lines) { - this.yylineno++; - this.yylloc.last_line++; - this.yylloc.last_column = 0; - } else { - this.yylloc.last_column++; - } - if (this.options.ranges) { - this.yylloc.range[1]++; - } - - this._input = this._input.slice(slice_len); - return ch; - }, - - /** - unshifts one char (or an entire string) into the input - - @public - @this {RegExpLexer} - */ - unput: function lexer_unput(ch) { - var len = ch.length; - var lines = ch.split(/(?:\r\n?|\n)/g); - - this._input = ch + this._input; - this.yytext = this.yytext.substr(0, this.yytext.length - len); - this.yyleng = this.yytext.length; - this.offset -= len; - this.match = this.match.substr(0, this.match.length - len); - this.matched = this.matched.substr(0, this.matched.length - len); - - if (lines.length > 1) { - this.yylineno -= lines.length - 1; - - this.yylloc.last_line = this.yylineno + 1; - var pre = this.match; - var pre_lines = pre.split(/(?:\r\n?|\n)/g); - if (pre_lines.length === 1) { - pre = this.matched; - pre_lines = pre.split(/(?:\r\n?|\n)/g); - } - this.yylloc.last_column = pre_lines[pre_lines.length - 1].length; - } else { - this.yylloc.last_column -= len; - } - - if (this.options.ranges) { - this.yylloc.range[1] = this.yylloc.range[0] + this.yyleng; - } - this.done = false; - return this; - }, - - /** - cache matched text and append it on next action - - @public - @this {RegExpLexer} - */ - more: function lexer_more() { - this._more = true; - return this; - }, - - /** - signal the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead. - - @public - @this {RegExpLexer} - */ - reject: function lexer_reject() { - if (this.options.backtrack_lexer) { - this._backtrack = true; - } else { - // when the `parseError()` call returns, we MUST ensure that the error is registered. - // We accomplish this by signaling an 'error' token to be produced for the current - // `.lex()` run. - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).' + pos_str, false); - this._signaled_error_token = (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - } - return this; - }, - - /** - retain first n characters of the match - - @public - @this {RegExpLexer} - */ - less: function lexer_less(n) { - return this.unput(this.match.slice(n)); - }, - - /** - return (part of the) already matched input, i.e. for error messages. - - Limit the returned string length to `maxSize` (default: 20). - - Limit the returned string to the `maxLines` number of lines of input (default: 1). - - Negative limit values equal *unlimited*. - - @public - @this {RegExpLexer} - */ - pastInput: function lexer_pastInput(maxSize, maxLines) { - var past = this.matched.substring(0, this.matched.length - this.match.length); - if (maxSize < 0) - maxSize = past.length; - else if (!maxSize) - maxSize = 20; - if (maxLines < 0) - maxLines = past.length; // can't ever have more input lines than this! - else if (!maxLines) - maxLines = 1; - // `substr` anticipation: treat \r\n as a single character and take a little - // more than necessary so that we can still properly check against maxSize - // after we've transformed and limited the newLines in here: - past = past.substr(-maxSize * 2 - 2); - // now that we have a significantly reduced string to process, transform the newlines - // and chop them, then limit them: - var a = past.replace(/\r\n|\r/g, '\n').split('\n'); - a = a.slice(-maxLines); - past = a.join('\n'); - // When, after limiting to maxLines, we still have too much to return, - // do add an ellipsis prefix... - if (past.length > maxSize) { - past = '...' + past.substr(-maxSize); - } - return past; - }, - - /** - return (part of the) upcoming input, i.e. for error messages. - - Limit the returned string length to `maxSize` (default: 20). - - Limit the returned string to the `maxLines` number of lines of input (default: 1). - - Negative limit values equal *unlimited*. - - @public - @this {RegExpLexer} - */ - upcomingInput: function lexer_upcomingInput(maxSize, maxLines) { - var next = this.match; - if (maxSize < 0) - maxSize = next.length + this._input.length; - else if (!maxSize) - maxSize = 20; - if (maxLines < 0) - maxLines = maxSize; // can't ever have more input lines than this! - else if (!maxLines) - maxLines = 1; - // `substring` anticipation: treat \r\n as a single character and take a little - // more than necessary so that we can still properly check against maxSize - // after we've transformed and limited the newLines in here: - if (next.length < maxSize * 2 + 2) { - next += this._input.substring(0, maxSize * 2 + 2); // substring is faster on Chrome/V8 - } - // now that we have a significantly reduced string to process, transform the newlines - // and chop them, then limit them: - var a = next.replace(/\r\n|\r/g, '\n').split('\n'); - a = a.slice(0, maxLines); - next = a.join('\n'); - // When, after limiting to maxLines, we still have too much to return, - // do add an ellipsis postfix... - if (next.length > maxSize) { - next = next.substring(0, maxSize) + '...'; - } - return next; - }, - - /** - return a string which displays the character position where the lexing error occurred, i.e. for error messages - - @public - @this {RegExpLexer} - */ - showPosition: function lexer_showPosition(maxPrefix, maxPostfix) { - var pre = this.pastInput(maxPrefix).replace(/\s/g, ' '); - var c = new Array(pre.length + 1).join('-'); - return pre + this.upcomingInput(maxPostfix).replace(/\s/g, ' ') + '\n' + c + '^'; - }, - - /** - helper function, used to produce a human readable description as a string, given - the input `yylloc` location object. - - Set `display_range_too` to TRUE to include the string character index position(s) - in the description if the `yylloc.range` is available. - - @public - @this {RegExpLexer} - */ - describeYYLLOC: function lexer_describe_yylloc(yylloc, display_range_too) { - var l1 = yylloc.first_line; - var l2 = yylloc.last_line; - var o1 = yylloc.first_column; - var o2 = yylloc.last_column; - var dl = l2 - l1; - var d_o = o2 - o1; - var rv; - if (dl === 0) { - rv = 'line ' + l1 + ', '; - if (d_o === 1) { - rv += 'column ' + o1; - } else { - rv += 'columns ' + o1 + ' .. ' + o2; - } - } else { - rv = 'lines ' + l1 + '(column ' + o1 + ') .. ' + l2 + '(column ' + o2 + ')'; - } - if (yylloc.range && display_range_too) { - var r1 = yylloc.range[0]; - var r2 = yylloc.range[1] - 1; - if (r2 === r1) { - rv += ' {String Offset: ' + r1 + '}'; - } else { - rv += ' {String Offset range: ' + r1 + ' .. ' + r2 + '}'; - } - } - return rv; - // return JSON.stringify(yylloc); - }, - - /** - test the lexed token: return FALSE when not a match, otherwise return token. - - `match` is supposed to be an array coming out of a regex match, i.e. `match[0]` - contains the actually matched text string. - - Also move the input cursor forward and update the match collectors: - - - `yytext` - - `yyleng` - - `match` - - `matches` - - `yylloc` - - `offset` - - @public - @this {RegExpLexer} - */ - test_match: function lexer_test_match(match, indexed_rule) { - var token, - lines, - backup, - match_str, - match_str_len; - - if (this.options.backtrack_lexer) { - // save context - backup = { - yylineno: this.yylineno, - yylloc: { - first_line: this.yylloc.first_line, - last_line: this.yylloc.last_line, - first_column: this.yylloc.first_column, - last_column: this.yylloc.last_column, - - range: (this.options.ranges ? this.yylloc.range.slice(0) : undefined) - }, - yytext: this.yytext, - match: this.match, - matches: this.matches, - matched: this.matched, - yyleng: this.yyleng, - offset: this.offset, - _more: this._more, - _input: this._input, - //_signaled_error_token: this._signaled_error_token, - yy: this.yy, - conditionStack: this.conditionStack.slice(0), - done: this.done - }; - } - - match_str = match[0]; - match_str_len = match_str.length; - // if (match_str.indexOf('\n') !== -1 || match_str.indexOf('\r') !== -1) { - lines = match_str.split(/(?:\r\n?|\n)/g); - if (lines.length > 1) { - this.yylineno += lines.length - 1; - - this.yylloc.last_line = this.yylineno + 1, - this.yylloc.last_column = lines[lines.length - 1].length; - } else { - this.yylloc.last_column += match_str_len; - } - // } - this.yytext += match_str; - this.match += match_str; - this.matches = match; - this.yyleng = this.yytext.length; - if (this.options.ranges) { - this.yylloc.range[1] += match_str_len; - } - // previous lex rules MAY have invoked the `more()` API rather than producing a token: - // those rules will already have moved this `offset` forward matching their match lengths, - // hence we must only add our own match length now: - this.offset += match_str_len; - this._more = false; - this._backtrack = false; - this._input = this._input.slice(match_str_len); - this.matched += match_str; - - // calling this method: - // - // function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START) {...} - token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1] /* = YY_START */); - // otherwise, when the action codes are all simple return token statements: - //token = this.simpleCaseActionClusters[indexed_rule]; - - if (this.done && this._input) { - this.done = false; - } - if (token) { - return token; - } else if (this._backtrack) { - // recover context - for (var k in backup) { - this[k] = backup[k]; - } - this.__currentRuleSet__ = null; - return false; // rule action called reject() implying the next rule should be tested instead. - } else if (this._signaled_error_token) { - // produce one 'error' token as `.parseError()` in `reject()` did not guarantee a failure signal by throwing an exception! - token = this._signaled_error_token; - this._signaled_error_token = false; - return token; - } - return false; - }, - - /** - return next match in input - - @public - @this {RegExpLexer} - */ - next: function lexer_next() { - if (this.done) { - this.clear(); - return this.EOF; - } - if (!this._input) { - this.done = true; - } - - var token, - match, - tempMatch, - index; - if (!this._more) { - this.clear(); - } - var spec = this.__currentRuleSet__; - if (!spec) { - // Update the ruleset cache as we apparently encountered a state change or just started lexing. - // The cache is set up for fast lookup -- we assume a lexer will switch states much less often than it will - // invoke the `lex()` token-producing API and related APIs, hence caching the set for direct access helps - // speed up those activities a tiny bit. - spec = this.__currentRuleSet__ = this._currentRules(); - // Check whether a *sane* condition has been pushed before: this makes the lexer robust against - // user-programmer bugs such as https://github.com/zaach/jison-lex/issues/19 - if (!spec || !spec.rules) { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Internal lexer engine error' + lineno_msg + ': The lex grammar programmer pushed a non-existing condition name "' + this.topState() + '"; this is a fatal error and should be reported to the application programmer team!' + pos_str, false); - // produce one 'error' token until this situation has been resolved, most probably by parse termination! - return (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - } - } - - var rule_ids = spec.rules; - //var dispatch = spec.__dispatch_lut; - var regexes = spec.__rule_regexes; - var len = spec.__rule_count; - - // Note: the arrays are 1-based, while `len` itself is a valid index, - // hence the non-standard less-or-equal check in the next loop condition! - for (var i = 1; i <= len; i++) { - tempMatch = this._input.match(regexes[i]); - if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { - match = tempMatch; - index = i; - if (this.options.backtrack_lexer) { - token = this.test_match(tempMatch, rule_ids[i]); - if (token !== false) { - return token; - } else if (this._backtrack) { - match = undefined; - continue; // rule action called reject() implying a rule MISmatch. - } else { - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - } else if (!this.options.flex) { - break; - } - } - } - if (match) { - token = this.test_match(match, rule_ids[index]); - if (token !== false) { - return token; - } - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - if (!this._input) { - this.done = true; - this.clear(); - return this.EOF; - } else { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': Unrecognized text.' + pos_str, this.options.lexerErrorsAreRecoverable); - token = (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - if (token === this.ERROR) { - // we can try to recover from a lexer error that `parseError()` did not 'recover' for us - // by moving forward at least one character at a time: - if (!this.match.length) { - this.input(); - } - } - return token; - } - }, - - /** - return next match that has a token - - @public - @this {RegExpLexer} - */ - lex: function lexer_lex() { - var r; - // allow the PRE/POST handlers set/modify the return token for maximum flexibility of the generated lexer: - if (typeof this.options.pre_lex === 'function') { - r = this.options.pre_lex.call(this); - } - while (!r) { - r = this.next(); - } - if (typeof this.options.post_lex === 'function') { - // (also account for a userdef function which does not return any value: keep the token as is) - r = this.options.post_lex.call(this, r) || r; - } - return r; - }, - - /** - backwards compatible alias for `pushState()`; - the latter is symmetrical with `popState()` and we advise to use - those APIs in any modern lexer code, rather than `begin()`. - - @public - @this {RegExpLexer} - */ - begin: function lexer_begin(condition) { - return this.pushState(condition); - }, - - /** - activates a new lexer condition state (pushes the new lexer condition state onto the condition stack) - - @public - @this {RegExpLexer} - */ - pushState: function lexer_pushState(condition) { - this.conditionStack.push(condition); - this.__currentRuleSet__ = null; - return this; - }, - - /** - pop the previously active lexer condition state off the condition stack - - @public - @this {RegExpLexer} - */ - popState: function lexer_popState() { - var n = this.conditionStack.length - 1; - if (n > 0) { - this.__currentRuleSet__ = null; - return this.conditionStack.pop(); - } else { - return this.conditionStack[0]; - } - }, - - /** - return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available - - @public - @this {RegExpLexer} - */ - topState: function lexer_topState(n) { - n = this.conditionStack.length - 1 - Math.abs(n || 0); - if (n >= 0) { - return this.conditionStack[n]; - } else { - return 'INITIAL'; - } - }, - - /** - (internal) determine the lexer rule set which is active for the currently active lexer condition state - - @public - @this {RegExpLexer} - */ - _currentRules: function lexer__currentRules() { - if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) { - return this.conditions[this.conditionStack[this.conditionStack.length - 1]]; - } else { - return this.conditions['INITIAL']; - } - }, - - /** - return the number of states currently on the stack - - @public - @this {RegExpLexer} - */ - stateStackSize: function lexer_stateStackSize() { - return this.conditionStack.length; - }, - options: { - xregexp: true, - ranges: true, - trackPosition: true, - easy_keyword_rules: true -}, - JisonLexerError: JisonLexerError, - performAction: function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START) { - -var YYSTATE = YY_START; -switch($avoiding_name_collisions) { -case 0 : -/*! Conditions:: token */ -/*! Rule:: {BR} */ - this.popState(); -break; -case 1 : -/*! Conditions:: token */ -/*! Rule:: %% */ - this.popState(); -break; -case 2 : -/*! Conditions:: token */ -/*! Rule:: ; */ - this.popState(); -break; -case 3 : -/*! Conditions:: bnf ebnf */ -/*! Rule:: %% */ - this.pushState('code'); return 14; -break; -case 17 : -/*! Conditions:: options */ -/*! Rule:: "{DOUBLEQUOTED_STRING_CONTENT}" */ - yy_.yytext = this.matches[1]; return 28; // value is always a string type -break; -case 18 : -/*! Conditions:: options */ -/*! Rule:: '{QUOTED_STRING_CONTENT}' */ - yy_.yytext = this.matches[1]; return 28; // value is always a string type -break; -case 19 : -/*! Conditions:: INITIAL ebnf bnf token path options */ -/*! Rule:: \/\/[^\r\n]* */ - /* skip single-line comment */ -break; -case 20 : -/*! Conditions:: INITIAL ebnf bnf token path options */ -/*! Rule:: \/\*(.|\n|\r)*?\*\/ */ - /* skip multi-line comment */ -break; -case 22 : -/*! Conditions:: options */ -/*! Rule:: {BR}{WS}+(?=\S) */ - /* skip leading whitespace on the next line of input, when followed by more options */ -break; -case 23 : -/*! Conditions:: options */ -/*! Rule:: {BR} */ - this.popState(); return 26; -break; -case 24 : -/*! Conditions:: options */ -/*! Rule:: {WS}+ */ - /* skip whitespace */ -break; -case 25 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: {WS}+ */ - /* skip whitespace */ -break; -case 26 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: {BR}+ */ - /* skip newlines */ -break; -case 27 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: \[{ID}\] */ - yy_.yytext = this.matches[1]; return 38; -break; -case 31 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: "{DOUBLEQUOTED_STRING_CONTENT}" */ - yy_.yytext = this.matches[1]; return 24; -break; -case 32 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: '{QUOTED_STRING_CONTENT}' */ - yy_.yytext = this.matches[1]; return 24; -break; -case 37 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: %% */ - this.pushState(ebnf ? 'ebnf' : 'bnf'); return 14; -break; -case 38 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: %ebnf\b */ - if (!yy.options) { yy.options = {}; } ebnf = yy.options.ebnf = true; -break; -case 39 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: %debug\b */ - if (!yy.options) { yy.options = {}; } yy.options.debug = true; return 19; -break; -case 46 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: %token\b */ - this.pushState('token'); return 18; -break; -case 48 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: %options\b */ - this.pushState('options'); return 25; -break; -case 49 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: %lex{LEX_CONTENT}\/lex\b */ - - // remove the %lex../lex wrapper and return the pure lex section: - yy_.yytext = this.matches[1]; - return 17; - -break; -case 52 : -/*! Conditions:: INITIAL ebnf bnf code */ -/*! Rule:: %include\b */ - this.pushState('path'); return 42; -break; -case 53 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: %{NAME}([^\r\n]*) */ - - /* ignore unrecognized decl */ - console.warn('EBNF: ignoring unsupported parser option: ', yy_.yytext, ' while lexing in ', this.topState(), ' state'); - // this.pushState('options'); - yy_.yytext = [ - this.matches[1], // {NAME} - this.matches[2].trim() // optional value/parameters - ]; - return 20; - -break; -case 54 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: <{ID}> */ - yy_.yytext = this.matches[1]; return 35; -break; -case 55 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: \{\{[\w\W]*?\}\} */ - yy_.yytext = yy_.yytext.substr(2, yy_.yyleng - 4); return 15; -break; -case 56 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: %\{(?:.|\r|\n)*?%\} */ - yy_.yytext = yy_.yytext.substr(2, yy_.yyleng - 4); return 15; -break; -case 57 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: \{ */ - yy.depth = 0; this.pushState('action'); return 12; -break; -case 58 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: ->.* */ - yy_.yytext = yy_.yytext.substr(2, yy_.yyleng - 2).trim(); return 40; -break; -case 59 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: →.* */ - yy_.yytext = yy_.yytext.substr(1, yy_.yyleng - 1).trim(); return 40; -break; -case 60 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: {HEX_NUMBER} */ - yy_.yytext = parseInt(yy_.yytext, 16); return 36; -break; -case 61 : -/*! Conditions:: bnf ebnf token INITIAL */ -/*! Rule:: {DECIMAL_NUMBER}(?![xX0-9a-fA-F]) */ - yy_.yytext = parseInt(yy_.yytext, 10); return 36; -break; -case 64 : -/*! Conditions:: action */ -/*! Rule:: \/[^ /]*?['"{}'][^ ]*?\/ */ - return 41; // regexp with braces or quotes (and no spaces) -break; -case 69 : -/*! Conditions:: action */ -/*! Rule:: \{ */ - yy.depth++; return 12; -break; -case 70 : -/*! Conditions:: action */ -/*! Rule:: \} */ - if (yy.depth === 0) { this.popState(); } else { yy.depth--; } return 13; -break; -case 72 : -/*! Conditions:: code */ -/*! Rule:: [^\r\n]+ */ - return 44; // the bit of CODE just before EOF... -break; -case 73 : -/*! Conditions:: path */ -/*! Rule:: {BR} */ - this.popState(); this.unput(yy_.yytext); -break; -case 74 : -/*! Conditions:: path */ -/*! Rule:: "{DOUBLEQUOTED_STRING_CONTENT}" */ - yy_.yytext = this.matches[1]; this.popState(); return 43; -break; -case 75 : -/*! Conditions:: path */ -/*! Rule:: '{QUOTED_STRING_CONTENT}' */ - yy_.yytext = this.matches[1]; this.popState(); return 43; -break; -case 76 : -/*! Conditions:: path */ -/*! Rule:: {WS}+ */ - // skip whitespace in the line -break; -case 77 : -/*! Conditions:: path */ -/*! Rule:: [^\s\r\n]+ */ - this.popState(); return 43; -break; -case 78 : -/*! Conditions:: * */ -/*! Rule:: . */ - - /* b0rk on bad characters */ - var l0 = Math.max(0, yy_.yylloc.last_column - yy_.yylloc.first_column); - var l2 = 39; - var l1 = Math.min(79 - 4 - l0 - l2, yy_.yylloc.first_column, 0); - var pos_str = this.showPosition(l1, l2); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n\n Offending input:\n' + indent(pos_str, 4); - } - yy_.yyerror('unsupported parser input: ' + dquote(yy_.yytext) + ' @ ' + this.describeYYLLOC(yy_.yylloc) + ' while lexing in ' + dquote(this.topState()) + ' state.' + pos_str); - -break; -default: - return this.simpleCaseActionClusters[$avoiding_name_collisions]; -} -}, - simpleCaseActionClusters: { - - /*! Conditions:: bnf ebnf */ - /*! Rule:: %empty\b */ - 4 : 37, - /*! Conditions:: bnf ebnf */ - /*! Rule:: %epsilon\b */ - 5 : 37, - /*! Conditions:: bnf ebnf */ - /*! Rule:: \u0190 */ - 6 : 37, - /*! Conditions:: bnf ebnf */ - /*! Rule:: \u025B */ - 7 : 37, - /*! Conditions:: bnf ebnf */ - /*! Rule:: \u03B5 */ - 8 : 37, - /*! Conditions:: bnf ebnf */ - /*! Rule:: \u03F5 */ - 9 : 37, - /*! Conditions:: ebnf */ - /*! Rule:: \( */ - 10 : 7, - /*! Conditions:: ebnf */ - /*! Rule:: \) */ - 11 : 8, - /*! Conditions:: ebnf */ - /*! Rule:: \* */ - 12 : 9, - /*! Conditions:: ebnf */ - /*! Rule:: \? */ - 13 : 10, - /*! Conditions:: ebnf */ - /*! Rule:: \+ */ - 14 : 11, - /*! Conditions:: options */ - /*! Rule:: {NAME} */ - 15 : 27, - /*! Conditions:: options */ - /*! Rule:: = */ - 16 : 3, - /*! Conditions:: options */ - /*! Rule:: [^\s\r\n]+ */ - 21 : 29, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: {ID} */ - 28 : 23, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: \$end\b */ - 29 : 23, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: \$eof\b */ - 30 : 23, - /*! Conditions:: token */ - /*! Rule:: [^\s\r\n]+ */ - 33 : 'TOKEN_WORD', - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: : */ - 34 : 4, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: ; */ - 35 : 5, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: \| */ - 36 : 6, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: %parser-type\b */ - 40 : 31, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: %prec\b */ - 41 : 39, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: %start\b */ - 42 : 16, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: %left\b */ - 43 : 32, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: %right\b */ - 44 : 33, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: %nonassoc\b */ - 45 : 34, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: %parse-param\b */ - 47 : 30, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: %code\b */ - 50 : 22, - /*! Conditions:: bnf ebnf token INITIAL */ - /*! Rule:: %import\b */ - 51 : 21, - /*! Conditions:: action */ - /*! Rule:: \/\*(.|\n|\r)*?\*\/ */ - 62 : 41, - /*! Conditions:: action */ - /*! Rule:: \/\/[^\r\n]* */ - 63 : 41, - /*! Conditions:: action */ - /*! Rule:: "{DOUBLEQUOTED_STRING_CONTENT}" */ - 65 : 41, - /*! Conditions:: action */ - /*! Rule:: '{QUOTED_STRING_CONTENT}' */ - 66 : 41, - /*! Conditions:: action */ - /*! Rule:: [/"'][^{}/"']+ */ - 67 : 41, - /*! Conditions:: action */ - /*! Rule:: [^{}/"']+ */ - 68 : 41, - /*! Conditions:: code */ - /*! Rule:: [^\r\n]*(\r|\n)+ */ - 71 : 44, - /*! Conditions:: * */ - /*! Rule:: $ */ - 79 : 1 -}, - rules: [ - /^(?:(\r\n|\n|\r))/, -/^(?:%%)/, -/^(?:;)/, -/^(?:%%)/, -/^(?:%empty\b)/, -/^(?:%epsilon\b)/, -/^(?:\u0190)/, -/^(?:\u025B)/, -/^(?:\u03B5)/, -/^(?:\u03F5)/, -/^(?:\()/, -/^(?:\))/, -/^(?:\*)/, -/^(?:\?)/, -/^(?:\+)/, -new XRegExp("^(?:([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}\\-_]*(?:[\\p{Alphabetic}\\p{Number}_]))?))", ""), -/^(?:=)/, -/^(?:"((?:\\"|\\[^"]|[^"\\])*)")/, -/^(?:'((?:\\'|\\[^']|[^'\\])*)')/, -/^(?:\/\/[^\r\n]*)/, -/^(?:\/\*(.|\n|\r)*?\*\/)/, -/^(?:\S+)/, -/^(?:(\r\n|\n|\r)([^\S\n\r])+(?=\S))/, -/^(?:(\r\n|\n|\r))/, -/^(?:([^\S\n\r])+)/, -/^(?:([^\S\n\r])+)/, -/^(?:(\r\n|\n|\r)+)/, -new XRegExp("^(?:\\[([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}_])*)\\])", ""), -new XRegExp("^(?:([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}_])*))", ""), -/^(?:\$end\b)/, -/^(?:\$eof\b)/, -/^(?:"((?:\\"|\\[^"]|[^"\\])*)")/, -/^(?:'((?:\\'|\\[^']|[^'\\])*)')/, -/^(?:\S+)/, -/^(?::)/, -/^(?:;)/, -/^(?:\|)/, -/^(?:%%)/, -/^(?:%ebnf\b)/, -/^(?:%debug\b)/, -/^(?:%parser-type\b)/, -/^(?:%prec\b)/, -/^(?:%start\b)/, -/^(?:%left\b)/, -/^(?:%right\b)/, -/^(?:%nonassoc\b)/, -/^(?:%token\b)/, -/^(?:%parse-param\b)/, -/^(?:%options\b)/, -/^(?:%lex((?:[^\S\n\r])*(?:(?:\r\n|\n|\r)[\S\s]*?)?(?:\r\n|\n|\r)(?:[^\S\n\r])*)\/lex\b)/, -/^(?:%code\b)/, -/^(?:%import\b)/, -/^(?:%include\b)/, -new XRegExp("^(?:%([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}\\-_]*(?:[\\p{Alphabetic}\\p{Number}_]))?)([^\\n\\r]*))", ""), -new XRegExp("^(?:<([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}_])*)>)", ""), -/^(?:\{\{[\w\W]*?\}\})/, -/^(?:%\{(?:.|\r|\n)*?%\})/, -/^(?:\{)/, -/^(?:->.*)/, -/^(?:→.*)/, -/^(?:(0[Xx][\dA-Fa-f]+))/, -/^(?:([1-9]\d*)(?![\dA-FXa-fx]))/, -/^(?:\/\*(.|\n|\r)*?\*\/)/, -/^(?:\/\/[^\r\n]*)/, -/^(?:\/[^ \/]*?["'{}][^ ]*?\/)/, -/^(?:"((?:\\"|\\[^"]|[^"\\])*)")/, -/^(?:'((?:\\'|\\[^']|[^'\\])*)')/, -/^(?:[\/"'][^{}\/"']+)/, -/^(?:[^{}\/"']+)/, -/^(?:\{)/, -/^(?:\})/, -/^(?:[^\r\n]*(\r|\n)+)/, -/^(?:[^\r\n]+)/, -/^(?:(\r\n|\n|\r))/, -/^(?:"((?:\\"|\\[^"]|[^"\\])*)")/, -/^(?:'((?:\\'|\\[^']|[^'\\])*)')/, -/^(?:([^\S\n\r])+)/, -/^(?:\S+)/, -/^(?:.)/, -/^(?:$)/ - ], - conditions: { - "bnf": { - rules: [ - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 19, - 20, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 78, - 79 - ], - inclusive: true - }, - "ebnf": { - rules: [ - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 19, - 20, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 78, - 79 - ], - inclusive: true - }, - "token": { - rules: [ - 0, - 1, - 2, - 19, - 20, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 78, - 79 - ], - inclusive: true - }, - "action": { - rules: [ - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 78, - 79 - ], - inclusive: false - }, - "code": { - rules: [ - 52, - 71, - 72, - 78, - 79 - ], - inclusive: false - }, - "path": { - rules: [ - 19, - 20, - 73, - 74, - 75, - 76, - 77, - 78, - 79 - ], - inclusive: false - }, - "options": { - rules: [ - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 78, - 79 - ], - inclusive: false - }, - "INITIAL": { - rules: [ - 19, - 20, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 78, - 79 - ], - inclusive: true - } -} -}; - - -function indent(s, i) { - var a = s.split('\n'); - var pf = (new Array(i + 1)).join(' '); - return pf + a.join('\n' + pf); -} - -// properly quote and escape the given input string -function dquote(s) { - var sq = (s.indexOf('\'') >= 0); - var dq = (s.indexOf('"') >= 0); - if (sq && dq) { - s = s.replace(/"/g, '\\"'); - dq = false; - } - if (dq) { - s = '\'' + s + '\''; - } - else { - s = '"' + s + '"'; - } - return s; -}; - -return lexer; -})(); -parser.lexer = lexer; - -function Parser() { - this.yy = {}; -} -Parser.prototype = parser; -parser.Parser = Parser; - -return new Parser(); -})(); - - - - -if (typeof require !== 'undefined' && typeof exports !== 'undefined') { - exports.parser = parser; - exports.Parser = parser.Parser; - exports.parse = function () { - return parser.parse.apply(parser, arguments); - }; - -} - -},{"./ebnf-transform":4,"fs":15,"xregexp":22}],8:[function(require,module,exports){ -// Basic Lexer implemented using JavaScript regular expressions -// Zachary Carter -// MIT Licensed - -'use strict'; - -var XRegExp = require('xregexp'); -var json5 = require('json5'); -var lexParser = require('./lex-parser'); -var setmgmt = require('./regexp-set-management'); -var code_exec = require('./safe-code-exec-and-diag'); -var version = require('./package.json').version; -var assert = require('assert'); - - - -const XREGEXP_UNICODE_ESCAPE_RE = setmgmt.XREGEXP_UNICODE_ESCAPE_RE; // Matches the XRegExp Unicode escape braced part, e.g. `{Number}` -const CHR_RE = setmgmt.CHR_RE; -const SET_PART_RE = setmgmt.SET_PART_RE; -const NOTHING_SPECIAL_RE = setmgmt.NOTHING_SPECIAL_RE; -const UNICODE_BASE_PLANE_MAX_CP = setmgmt.UNICODE_BASE_PLANE_MAX_CP; - -// The expanded regex sets which are equivalent to the given `\\{c}` escapes: -// -// `/\s/`: -const WHITESPACE_SETSTR = setmgmt.WHITESPACE_SETSTR; -// `/\d/`: -const DIGIT_SETSTR = setmgmt.DIGIT_SETSTR; -// `/\w/`: -const WORDCHAR_SETSTR = setmgmt.WORDCHAR_SETSTR; - - - -// see also ./lib/cli.js -/** -@public -@nocollapse -*/ -const defaultJisonLexOptions = { - moduleType: 'commonjs', - debug: false, - enableDebugLogs: false, - json: false, - main: false, // CLI: not:(--main option) - dumpSourceCodeOnFailure: true, - throwErrorOnCompileFailure: true, - - moduleName: undefined, - defaultModuleName: 'lexer', - file: undefined, - outfile: undefined, - inputPath: undefined, - inputFilename: undefined, - warn_cb: undefined, // function(msg) | true (= use Jison.Print) | false (= throw Exception) - - parseParams: undefined, - xregexp: false, - lexerErrorsAreRecoverable: false, - flex: false, - backtrack_lexer: false, - ranges: false, // track position range, i.e. start+end indexes in the input string - trackPosition: true, // track line+column position in the input string - caseInsensitive: false, - showSource: false, - pre_lex: undefined, - post_lex: undefined, -}; - - -// Convert dashed option keys to Camel Case, e.g. `camelCase('camels-have-one-hump')` => `'camelsHaveOneHump'` -/** @public */ -function camelCase(s) { - // Convert first character to lowercase - return s.replace(/^\w/, function (match) { - return match.toLowerCase(); - }) - .replace(/-\w/g, function (match) { - return match.charAt(1).toUpperCase(); - }); -} - -// Merge sets of options. -// -// Convert alternative jison option names to their base option. -// -// The *last* option set which overrides the default wins, where 'override' is -// defined as specifying a not-undefined value which is not equal to the -// default value. -// -// Return a fresh set of options. -/** @public */ -function mkStdOptions(/*...args*/) { - var h = Object.prototype.hasOwnProperty; - - // clone defaults, so we do not modify those constants. - var opts = {}; - var o = defaultJisonLexOptions; - - for (var p in o) { - if (h.call(o, p) && typeof o[p] !== 'undefined') { - opts[p] = o[p]; - } - } - - for (var i = 0, len = arguments.length; i < len; i++) { - o = arguments[i]; - - // clone input (while camel-casing the options), so we do not modify those either. - var o2 = {}; - - for (var p in o) { - if (h.call(o, p) && typeof o[p] !== 'undefined') { - o2[camelCase(p)] = o[p]; - } - } - - // now clean them options up: - if (typeof o2.main !== 'undefined') { - o2.noMain = !o2.main; - } - - delete o2.main; - - // special check for `moduleName` to ensure we detect the 'default' moduleName entering from the CLI - // NOT overriding the moduleName set in the grammar definition file via an `%options` entry: - if (o2.moduleName === o2.defaultModuleName) { - delete o2.moduleName; - } - - // now see if we have an overriding option here: - for (var p in o2) { - if (h.call(o2, p)) { - if (o2[p] !== undefined && o2[p] !== defaultJisonLexOptions[p]) { - opts[p] = o2[p]; - } - } - } - } - - return opts; -} - - -// Autodetect if the input lexer spec is in JSON or JISON -// format when the `options.json` flag is `true`. -// -// Produce the JSON lexer spec result when these are JSON formatted already as that -// would save us the trouble of doing this again, anywhere else in the JISON -// compiler/generator. -// -// Otherwise return the *parsed* lexer spec as it has -// been processed through LEXParser. -function autodetectAndConvertToJSONformat(lexerSpec, options) { - var chk_l = null; - var ex1, err; - - if (typeof lexerSpec === 'string') { - if (options.json) { - try { - chk_l = json5.parse(lexerSpec); - - // When JSON5-based parsing of the lexer spec succeeds, this implies the lexer spec is specified in `JSON mode` - // *OR* there's a JSON/JSON5 format error in the input: - } catch (e) { - ex1 = e; - } - } - if (!chk_l) { - // // WARNING: the lexer may receive options specified in the **grammar spec file**, - // // hence we should mix the options to ensure the lexParser always - // // receives the full set! - // // - // // make sure all options are 'standardized' before we go and mix them together: - // options = mkStdOptions(grammar.options, options); - try { - chk_l = lexParser.parse(lexerSpec, options); - } catch (e) { - if (options.json) { - err = new Error('Could not parse lexer spec in JSON AUTODETECT mode\nError: ' + ex1.message + ' (' + e.message + ')'); - err.secondary_exception = e; - err.stack = ex1.stack; - } else { - err = new Error('Could not parse lexer spec\nError: ' + e.message); - err.stack = e.stack; - } - throw err; - } - } - } else { - chk_l = lexerSpec; - } - - // Save time! Don't reparse the entire lexer spec *again* inside the code generators when that's not necessary: - - return chk_l; -} - - - - - -// HELPER FUNCTION: print the function in source code form, properly indented. -/** @public */ -function printFunctionSourceCode(f) { - return String(f).replace(/^ /gm, ''); -} -/** @public */ -function printFunctionSourceCodeContainer(f, depth) { - var s = String(f); - for (var d = (depth || 2); d > 0; d--) { - s = s.replace(/^ /gm, ''); - } - s = s.replace(/^\s*function\b[^\{]+\{/, '').replace(/\}\s*$/, ''); - return s; -} - - - -// expand macros and convert matchers to RegExp's -function prepareRules(dict, actions, caseHelper, tokens, startConditions, opts) { - var m, i, k, rule, action, conditions, - active_conditions, - rules = dict.rules, - newRules = [], - macros = {}, - regular_rule_count = 0, - simple_rule_count = 0; - - // Assure all options are camelCased: - assert(typeof opts.options['case-insensitive'] === 'undefined'); - - if (!tokens) { - tokens = {}; - } - - // Depending on the location within the regex we need different expansions of the macros: - // one expansion for when a macro is *inside* a `[...]` and another expansion when a macro - // is anywhere else in a regex: - if (dict.macros) { - macros = prepareMacros(dict.macros, opts); - } - - function tokenNumberReplacement(str, token) { - return 'return ' + (tokens[token] || '\'' + token.replace(/'/g, '\\\'') + '\''); - } - - // Make sure a comment does not contain any embedded '*/' end-of-comment marker - // as that would break the generated code - function postprocessComment(str) { - if (Array.isArray(str)) { - str = str.join(' '); - } - str = str.replace(/\*\//g, '*\\/'); // destroy any inner `*/` comment terminator sequence. - return str; - } - - actions.push('switch($avoiding_name_collisions) {'); - - for (i = 0; i < rules.length; i++) { - rule = rules[i]; - m = rule[0]; - - active_conditions = []; - if (Object.prototype.toString.apply(m) !== '[object Array]') { - // implicit add to all inclusive start conditions - for (k in startConditions) { - if (startConditions[k].inclusive) { - active_conditions.push(k); - startConditions[k].rules.push(i); - } - } - } else if (m[0] === '*') { - // Add to ALL start conditions - active_conditions.push('*'); - for (k in startConditions) { - startConditions[k].rules.push(i); - } - rule.shift(); - m = rule[0]; - } else { - // Add to explicit start conditions - conditions = rule.shift(); - m = rule[0]; - for (k = 0; k < conditions.length; k++) { - if (!startConditions.hasOwnProperty(conditions[k])) { - startConditions[conditions[k]] = { - rules: [], - inclusive: false - }; - console.warn('Lexer Warning : "' + conditions[k] + '" start condition should be defined as %s or %x; assuming %x now.'); - } - active_conditions.push(conditions[k]); - startConditions[conditions[k]].rules.push(i); - } - } - - if (typeof m === 'string') { - m = expandMacros(m, macros, opts); - m = new XRegExp('^(?:' + m + ')', opts.options.caseInsensitive ? 'i' : ''); - } - newRules.push(m); - if (typeof rule[1] === 'function') { - rule[1] = String(rule[1]).replace(/^\s*function \(\)\s?\{/, '').replace(/\}\s*$/, ''); - } - action = rule[1]; - action = action.replace(/return '((?:\\'|[^']+)+)'/g, tokenNumberReplacement); - action = action.replace(/return "((?:\\"|[^"]+)+)"/g, tokenNumberReplacement); - - var code = ['\n/*! Conditions::']; - code.push(postprocessComment(active_conditions)); - code.push('*/', '\n/*! Rule:: '); - code.push(postprocessComment(rules[i][0])); - code.push('*/', '\n'); - - // When the action is *only* a simple `return TOKEN` statement, then add it to the caseHelpers; - // otherwise add the additional `break;` at the end. - // - // Note: we do NOT analyze the action block any more to see if the *last* line is a simple - // `return NNN;` statement as there are too many shoddy idioms, e.g. - // - // ``` - // %{ if (cond) - // return TOKEN; - // %} - // ``` - // - // which would then cause havoc when our action code analysis (using regexes or otherwise) was 'too simple' - // to catch these culprits; hence we resort and stick with the most fundamental approach here: - // always append `break;` even when it would be obvious to a human that such would be 'unreachable code'. - var match_nr = /^return[\s\r\n]+((?:'(?:\\'|[^']+)+')|(?:"(?:\\"|[^"]+)+")|\d+)[\s\r\n]*;?$/.exec(action.trim()); - if (match_nr) { - simple_rule_count++; - caseHelper.push([].concat(code, i, ':', match_nr[1]).join(' ').replace(/[\n]/g, '\n ')); - } else { - regular_rule_count++; - actions.push([].concat('case', i, ':', code, action, '\nbreak;').join(' ')); - } - } - actions.push('default:'); - actions.push(' return this.simpleCaseActionClusters[$avoiding_name_collisions];'); - actions.push('}'); - - return { - rules: newRules, - macros: macros, - - regular_rule_count: regular_rule_count, - simple_rule_count: simple_rule_count, - }; -} - - - - - - - -// expand all macros (with maybe one exception) in the given regex: the macros may exist inside `[...]` regex sets or -// elsewhere, which requires two different treatments to expand these macros. -function reduceRegex(s, name, opts, expandAllMacrosInSet_cb, expandAllMacrosElsewhere_cb) { - var orig = s; - var regex_simple_size = 0; - var regex_previous_alts_simple_size = 0; - - function errinfo() { - if (name) { - return 'macro [[' + name + ']]'; - } else { - return 'regex [[' + orig + ']]'; - } - } - - // propagate deferred exceptions = error reports. - if (s instanceof Error) { - return s; - } - - var c1, c2; - var rv = []; - var derr; - var se; - - while (s.length) { - c1 = s.match(CHR_RE); - if (!c1) { - // cope with illegal escape sequences too! - return new Error(errinfo() + ': illegal escape sequence at start of regex part: ' + s); - } else { - c1 = c1[0]; - } - s = s.substr(c1.length); - - switch (c1) { - case '[': - // this is starting a set within the regex: scan until end of set! - var set_content = []; - var l = new Array(UNICODE_BASE_PLANE_MAX_CP + 1); - - while (s.length) { - var inner = s.match(SET_PART_RE); - if (!inner) { - inner = s.match(CHR_RE); - if (!inner) { - // cope with illegal escape sequences too! - return new Error(errinfo() + ': illegal escape sequence at start of regex part: ' + s); - } else { - inner = inner[0]; - } - if (inner === ']') break; - } else { - inner = inner[0]; - } - set_content.push(inner); - s = s.substr(inner.length); - } - - // ensure that we hit the terminating ']': - c2 = s.match(CHR_RE); - if (!c2) { - // cope with illegal escape sequences too! - return new Error(errinfo() + ': regex set expression is broken: "' + s + '"'); - } else { - c2 = c2[0]; - } - if (c2 !== ']') { - return new Error(errinfo() + ': regex set expression is broken: apparently unterminated'); - } - s = s.substr(c2.length); - - se = set_content.join(''); - - // expand any macros in here: - if (expandAllMacrosInSet_cb) { - se = expandAllMacrosInSet_cb(se); - assert(se); - if (se instanceof Error) { - return new Error(errinfo() + ': ' + se.message); - } - } - - derr = setmgmt.set2bitarray(l, se, opts); - if (derr instanceof Error) { - return new Error(errinfo() + ': ' + derr.message); - } - - // find out which set expression is optimal in size: - var s1 = setmgmt.produceOptimizedRegex4Set(l); - - // check if the source regex set potentially has any expansions (guestimate!) - // - // The indexOf('{') picks both XRegExp Unicode escapes and JISON lexer macros, which is perfect for us here. - var has_expansions = (se.indexOf('{') >= 0); - - se = '[' + se + ']'; - - if (!has_expansions && se.length < s1.length) { - s1 = se; - } - rv.push(s1); - break; - - // XRegExp Unicode escape, e.g. `\\p{Number}`: - case '\\p': - c2 = s.match(XREGEXP_UNICODE_ESCAPE_RE); - if (c2) { - c2 = c2[0]; - s = s.substr(c2.length); - - // nothing to expand. - rv.push(c1 + c2); - } else { - // nothing to stretch this match, hence nothing to expand. - rv.push(c1); - } - break; - - // Either a range expression or the start of a macro reference: `.{1,3}` or `{NAME}`. - // Treat it as a macro reference and see if it will expand to anything: - case '{': - c2 = s.match(NOTHING_SPECIAL_RE); - if (c2) { - c2 = c2[0]; - s = s.substr(c2.length); - - var c3 = s[0]; - s = s.substr(c3.length); - if (c3 === '}') { - // possibly a macro name in there... Expand if possible: - c2 = c1 + c2 + c3; - if (expandAllMacrosElsewhere_cb) { - c2 = expandAllMacrosElsewhere_cb(c2); - assert(c2); - if (c2 instanceof Error) { - return new Error(errinfo() + ': ' + c2.message); - } - } - } else { - // not a well-terminated macro reference or something completely different: - // we do not even attempt to expand this as there's guaranteed nothing to expand - // in this bit. - c2 = c1 + c2 + c3; - } - rv.push(c2); - } else { - // nothing to stretch this match, hence nothing to expand. - rv.push(c1); - } - break; - - // Recognize some other regex elements, but there's no need to understand them all. - // - // We are merely interested in any chunks now which do *not* include yet another regex set `[...]` - // nor any `{MACRO}` reference: - default: - // non-set character or word: see how much of this there is for us and then see if there - // are any macros still lurking inside there: - c2 = s.match(NOTHING_SPECIAL_RE); - if (c2) { - c2 = c2[0]; - s = s.substr(c2.length); - - // nothing to expand. - rv.push(c1 + c2); - } else { - // nothing to stretch this match, hence nothing to expand. - rv.push(c1); - } - break; - } - } - - s = rv.join(''); - - // When this result is suitable for use in a set, than we should be able to compile - // it in a regex; that way we can easily validate whether macro X is fit to be used - // inside a regex set: - try { - var re; - re = new XRegExp(s); - re.test(s[0]); - } catch (ex) { - // make sure we produce a regex expression which will fail badly when it is used - // in actual code: - return new Error(errinfo() + ': expands to an invalid regex: /' + s + '/'); - } - - assert(s); - return s; -} - - -// expand macros within macros and cache the result -function prepareMacros(dict_macros, opts) { - var macros = {}; - - // expand a `{NAME}` macro which exists inside a `[...]` set: - function expandMacroInSet(i) { - var k, a, m; - if (!macros[i]) { - m = dict_macros[i]; - - if (m.indexOf('{') >= 0) { - // set up our own record so we can detect definition loops: - macros[i] = { - in_set: false, - elsewhere: null, - raw: dict_macros[i] - }; - - for (k in dict_macros) { - if (dict_macros.hasOwnProperty(k) && i !== k) { - // it doesn't matter if the lexer recognized that the inner macro(s) - // were sitting inside a `[...]` set or not: the fact that they are used - // here in macro `i` which itself sits in a set, makes them *all* live in - // a set so all of them get the same treatment: set expansion style. - // - // Note: make sure we don't try to expand any XRegExp `\p{...}` or `\P{...}` - // macros here: - if (XRegExp._getUnicodeProperty(k)) { - // Work-around so that you can use `\p{ascii}` for a XRegExp slug, a.k.a. - // Unicode 'General Category' Property cf. http://unicode.org/reports/tr18/#Categories, - // while using `\p{ASCII}` as a *macro expansion* of the `ASCII` - // macro: - if (k.toUpperCase() !== k) { - m = new Error('Cannot use name "' + k + '" as a macro name as it clashes with the same XRegExp "\\p{..}" Unicode \'General Category\' Property name. Use all-uppercase macro names, e.g. name your macro "' + k.toUpperCase() + '" to work around this issue or give your offending macro a different name.'); - break; - } - } - - a = m.split('{' + k + '}'); - if (a.length > 1) { - var x = expandMacroInSet(k); - assert(x); - if (x instanceof Error) { - m = x; - break; - } - m = a.join(x); - } - } - } - } - - var mba = setmgmt.reduceRegexToSetBitArray(m, i, opts); - - var s1; - - // propagate deferred exceptions = error reports. - if (mba instanceof Error) { - s1 = mba; - } else { - s1 = setmgmt.bitarray2set(mba, false); - - m = s1; - } - - macros[i] = { - in_set: s1, - elsewhere: null, - raw: dict_macros[i] - }; - } else { - m = macros[i].in_set; - - if (m instanceof Error) { - // this turns out to be an macro with 'issues' and it is used, so the 'issues' do matter: bombs away! - return new Error(m.message); - } - - // detect definition loop: - if (m === false) { - return new Error('Macro name "' + i + '" has an illegal, looping, definition, i.e. it\'s definition references itself, either directly or indirectly, via other macros.'); - } - } - - return m; - } - - function expandMacroElsewhere(i) { - var k, a, m; - - if (macros[i].elsewhere == null) { - m = dict_macros[i]; - - // set up our own record so we can detect definition loops: - macros[i].elsewhere = false; - - // the macro MAY contain other macros which MAY be inside a `[...]` set in this - // macro or elsewhere, hence we must parse the regex: - m = reduceRegex(m, i, opts, expandAllMacrosInSet, expandAllMacrosElsewhere); - // propagate deferred exceptions = error reports. - if (m instanceof Error) { - return m; - } - - macros[i].elsewhere = m; - } else { - m = macros[i].elsewhere; - - if (m instanceof Error) { - // this turns out to be an macro with 'issues' and it is used, so the 'issues' do matter: bombs away! - return m; - } - - // detect definition loop: - if (m === false) { - return new Error('Macro name "' + i + '" has an illegal, looping, definition, i.e. it\'s definition references itself, either directly or indirectly, via other macros.'); - } - } - - return m; - } - - function expandAllMacrosInSet(s) { - var i, x; - - // process *all* the macros inside [...] set: - if (s.indexOf('{') >= 0) { - for (i in macros) { - if (macros.hasOwnProperty(i)) { - var a = s.split('{' + i + '}'); - if (a.length > 1) { - x = expandMacroInSet(i); - assert(x); - if (x instanceof Error) { - return new Error('failure to expand the macro [' + i + '] in set [' + s + ']: ' + x.message); - } - s = a.join(x); - } - - // stop the brute-force expansion attempt when we done 'em all: - if (s.indexOf('{') === -1) { - break; - } - } - } - } - - return s; - } - - function expandAllMacrosElsewhere(s) { - var i, x; - - // When we process the remaining macro occurrences in the regex - // every macro used in a lexer rule will become its own capture group. - // - // Meanwhile the cached expansion will expand any submacros into - // *NON*-capturing groups so that the backreference indexes remain as you'ld - // expect and using macros doesn't require you to know exactly what your - // used macro will expand into, i.e. which and how many submacros it has. - // - // This is a BREAKING CHANGE from vanilla jison 0.4.15! - if (s.indexOf('{') >= 0) { - for (i in macros) { - if (macros.hasOwnProperty(i)) { - // These are all submacro expansions, hence non-capturing grouping is applied: - var a = s.split('{' + i + '}'); - if (a.length > 1) { - x = expandMacroElsewhere(i); - assert(x); - if (x instanceof Error) { - return new Error('failure to expand the macro [' + i + '] in regex /' + s + '/: ' + x.message); - } - s = a.join('(?:' + x + ')'); - } - - // stop the brute-force expansion attempt when we done 'em all: - if (s.indexOf('{') === -1) { - break; - } - } - } - } - - return s; - } - - - var m, i; - - if (opts.debug) console.log('\n############## RAW macros: ', dict_macros); - - // first we create the part of the dictionary which is targeting the use of macros - // *inside* `[...]` sets; once we have completed that half of the expansions work, - // we then go and expand the macros for when they are used elsewhere in a regex: - // iff we encounter submacros then which are used *inside* a set, we can use that - // first half dictionary to speed things up a bit as we can use those expansions - // straight away! - for (i in dict_macros) { - if (dict_macros.hasOwnProperty(i)) { - expandMacroInSet(i); - } - } - - for (i in dict_macros) { - if (dict_macros.hasOwnProperty(i)) { - expandMacroElsewhere(i); - } - } - - if (opts.debug) console.log('\n############### expanded macros: ', macros); - - return macros; -} - - - -// expand macros in a regex; expands them recursively -function expandMacros(src, macros, opts) { - var expansion_count = 0; - - // By the time we call this function `expandMacros` we MUST have expanded and cached all macros already! - // Hence things should be easy in there: - - function expandAllMacrosInSet(s) { - var i, m, x; - - // process *all* the macros inside [...] set: - if (s.indexOf('{') >= 0) { - for (i in macros) { - if (macros.hasOwnProperty(i)) { - m = macros[i]; - - var a = s.split('{' + i + '}'); - if (a.length > 1) { - x = m.in_set; - - assert(x); - if (x instanceof Error) { - // this turns out to be an macro with 'issues' and it is used, so the 'issues' do matter: bombs away! - throw x; - } - - // detect definition loop: - if (x === false) { - return new Error('Macro name "' + i + '" has an illegal, looping, definition, i.e. it\'s definition references itself, either directly or indirectly, via other macros.'); - } - - s = a.join(x); - expansion_count++; - } - - // stop the brute-force expansion attempt when we done 'em all: - if (s.indexOf('{') === -1) { - break; - } - } - } - } - - return s; - } - - function expandAllMacrosElsewhere(s) { - var i, m, x; - - // When we process the main macro occurrences in the regex - // every macro used in a lexer rule will become its own capture group. - // - // Meanwhile the cached expansion will expand any submacros into - // *NON*-capturing groups so that the backreference indexes remain as you'ld - // expect and using macros doesn't require you to know exactly what your - // used macro will expand into, i.e. which and how many submacros it has. - // - // This is a BREAKING CHANGE from vanilla jison 0.4.15! - if (s.indexOf('{') >= 0) { - for (i in macros) { - if (macros.hasOwnProperty(i)) { - m = macros[i]; - - var a = s.split('{' + i + '}'); - if (a.length > 1) { - // These are all main macro expansions, hence CAPTURING grouping is applied: - x = m.elsewhere; - assert(x); - - // detect definition loop: - if (x === false) { - return new Error('Macro name "' + i + '" has an illegal, looping, definition, i.e. it\'s definition references itself, either directly or indirectly, via other macros.'); - } - - s = a.join('(' + x + ')'); - expansion_count++; - } - - // stop the brute-force expansion attempt when we done 'em all: - if (s.indexOf('{') === -1) { - break; - } - } - } - } - - return s; - } - - - // When we process the macro occurrences in the regex - // every macro used in a lexer rule will become its own capture group. - // - // Meanwhile the cached expansion will have expanded any submacros into - // *NON*-capturing groups so that the backreference indexes remain as you'ld - // expect and using macros doesn't require you to know exactly what your - // used macro will expand into, i.e. which and how many submacros it has. - // - // This is a BREAKING CHANGE from vanilla jison 0.4.15! - var s2 = reduceRegex(src, null, opts, expandAllMacrosInSet, expandAllMacrosElsewhere); - // propagate deferred exceptions = error reports. - if (s2 instanceof Error) { - throw s2; - } - - // only when we did expand some actual macros do we take the re-interpreted/optimized/regenerated regex from reduceRegex() - // in order to keep our test cases simple and rules recognizable. This assumes the user can code good regexes on his own, - // as long as no macros are involved... - // - // Also pick the reduced regex when there (potentially) are XRegExp extensions in the original, e.g. `\\p{Number}`, - // unless the `xregexp` output option has been enabled. - if (expansion_count > 0 || (src.indexOf('\\p{') >= 0 && !opts.options.xregexp)) { - src = s2; - } else { - // Check if the reduced regex is smaller in size; when it is, we still go with the new one! - if (s2.length < src.length) { - src = s2; - } - } - - return src; -} - -function prepareStartConditions(conditions) { - var sc, - hash = {}; - for (sc in conditions) { - if (conditions.hasOwnProperty(sc)) { - hash[sc] = {rules:[], inclusive: !conditions[sc]}; - } - } - return hash; -} - -function buildActions(dict, tokens, opts) { - var actions = [dict.actionInclude || '', 'var YYSTATE = YY_START;']; - var tok; - var toks = {}; - var caseHelper = []; - - // tokens: map/array of token numbers to token names - for (tok in tokens) { - var idx = parseInt(tok); - if (idx && idx > 0) { - toks[tokens[tok]] = idx; - } - } - - if (opts.options.flex) { - dict.rules.push(['.', 'console.log(yytext); /* `flex` lexing mode: the last resort rule! */']); - } - - var gen = prepareRules(dict, actions, caseHelper, tokens && toks, opts.conditions, opts); - - var fun = actions.join('\n'); - 'yytext yyleng yylineno yylloc yyerror'.split(' ').forEach(function (yy) { - fun = fun.replace(new RegExp('\\b(' + yy + ')\\b', 'g'), 'yy_.$1'); - }); - - return { - caseHelperInclude: '{\n' + caseHelper.join(',') + '\n}', - - actions: expandParseArguments('function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START, parseParams) {\n', opts) + fun + '\n}', - - rules: gen.rules, - macros: gen.macros, // propagate these for debugging/diagnostic purposes - - regular_rule_count: gen.regular_rule_count, - simple_rule_count: gen.simple_rule_count, - }; -} - -// -// NOTE: this is *almost* a copy of the JisonParserError producing code in -// jison/lib/jison.js @ line 2304:lrGeneratorMixin.generateErrorClass -// -function generateErrorClass() { - /** - * See also: - * http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508 - * but we keep the prototype.constructor and prototype.name assignment lines too for compatibility - * with userland code which might access the derived class in a 'classic' way. - * - * @public - * @constructor - * @nocollapse - */ - function JisonLexerError(msg, hash) { - Object.defineProperty(this, 'name', { - enumerable: false, - writable: false, - value: 'JisonLexerError' - }); - - if (msg == null) msg = '???'; - - Object.defineProperty(this, 'message', { - enumerable: false, - writable: true, - value: msg - }); - - this.hash = hash; - - var stacktrace; - if (hash && hash.exception instanceof Error) { - var ex2 = hash.exception; - this.message = ex2.message || msg; - stacktrace = ex2.stack; - } - if (!stacktrace) { - if (Error.hasOwnProperty('captureStackTrace')) { // V8 - Error.captureStackTrace(this, this.constructor); - } else { - stacktrace = (new Error(msg)).stack; - } - } - if (stacktrace) { - Object.defineProperty(this, 'stack', { - enumerable: false, - writable: false, - value: stacktrace - }); - } - } - - // wrap this init code in a function so we can String(function)-dump it into the generated - // output: that way we only have to write this code *once*! - function __extra_code__() { - if (typeof Object.setPrototypeOf === 'function') { - Object.setPrototypeOf(JisonLexerError.prototype, Error.prototype); - } else { - JisonLexerError.prototype = Object.create(Error.prototype); - } - JisonLexerError.prototype.constructor = JisonLexerError; - JisonLexerError.prototype.name = 'JisonLexerError'; - } - __extra_code__(); - - var prelude = [ - '// See also:', - '// http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508', - '// but we keep the prototype.constructor and prototype.name assignment lines too for compatibility', - '// with userland code which might access the derived class in a \'classic\' way.', - printFunctionSourceCode(JisonLexerError), - printFunctionSourceCodeContainer(__extra_code__), - '', - ]; - - return prelude.join('\n'); -} - - -var jisonLexerErrorDefinition = generateErrorClass(); - - -function generateFakeXRegExpClassSrcCode() { - function fake() { - var __hacky_counter__ = 0; - - /** - * @constructor - * @nocollapse - */ - function XRegExp(re, f) { - this.re = re; - this.flags = f; - this._getUnicodeProperty = function (k) {}; - var fake = /./; // WARNING: this exact 'fake' is also depended upon by the xregexp unit test! - __hacky_counter__++; - fake.__hacky_backy__ = __hacky_counter__; - return fake; - } - } - - return printFunctionSourceCodeContainer(fake); -} - - - -/** @constructor */ -function RegExpLexer(dict, input, tokens, build_options) { - var opts; - var dump = false; - - function test_me(tweak_cb, description, src_exception, ex_callback) { - opts = processGrammar(dict, tokens, build_options); - opts.__in_rules_failure_analysis_mode__ = false; - assert(opts.options); - if (tweak_cb) { - tweak_cb(); - } - var source = generateModuleBody(opts); - try { - // The generated code will always have the `lexer` variable declared at local scope - // as `eval()` will use the local scope. - // - // The compiled code will look something like this: - // - // ``` - // var lexer; - // bla bla... - // ``` - // - // or - // - // ``` - // var lexer = { bla... }; - // ``` - var testcode = [ - '// provide a local version for test purposes:', - jisonLexerErrorDefinition, - '', - generateFakeXRegExpClassSrcCode(), - '', - source, - '', - 'return lexer;'].join('\n'); - var lexer = code_exec(testcode, function generated_code_exec_wrapper(sourcecode) { - //console.log("===============================LEXER TEST CODE\n", sourcecode, "\n=====================END====================\n"); - var lexer_f = new Function('', sourcecode); - return lexer_f(); - }, opts.options, "lexer"); - - if (!lexer) { - throw new Error('no lexer defined *at all*?!'); - } - if (typeof lexer.options !== 'object' || lexer.options == null) { - throw new Error('your lexer class MUST have an .options member object or it won\'t fly!'); - } - if (typeof lexer.setInput !== 'function') { - throw new Error('your lexer class MUST have a .setInput function member or it won\'t fly!'); - } - if (lexer.EOF !== 1 && lexer.ERROR !== 2) { - throw new Error('your lexer class MUST have these constants defined: lexer.EOF = 1 and lexer.ERROR = 2 or it won\'t fly!'); - } - - // When we do NOT crash, we found/killed the problem area just before this call! - if (src_exception && description) { - src_exception.message += '\n (' + description + ')'; - } - - // patch the pre and post handlers in there, now that we have some live code to work with: - if (opts.options) { - var pre = opts.options.pre_lex; - var post = opts.options.post_lex; - // since JSON cannot encode functions, we'll have to do it manually now: - if (typeof pre === 'function') { - lexer.options.pre_lex = pre; - } - if (typeof post === 'function') { - lexer.options.post_lex = post; - } - } - - if (opts.options.showSource) { - if (typeof opts.options.showSource === 'function') { - opts.options.showSource(lexer, source, opts); - } else { - console.log("\nGenerated lexer sourcecode:\n----------------------------------------\n", source, "\n----------------------------------------\n"); - } - } - return lexer; - } catch (ex) { - // if (src_exception) { - // src_exception.message += '\n (' + description + ': ' + ex.message + ')'; - // } - - if (ex_callback) { - ex_callback(ex); - } else if (dump) { - console.log('source code:\n', source); - } - return false; - } - } - - /** @constructor */ - var lexer = test_me(null, null, null, function (ex) { - // When we get an exception here, it means some part of the user-specified lexer is botched. - // - // Now we go and try to narrow down the problem area/category: - if (!test_me(function () { - opts.conditions = []; - opts.showSource = false; - }, (dict.rules.length > 0 ? - 'One or more of your lexer state names are possibly botched?' : - 'Your custom lexer is somehow botched.'), ex, null)) { - if (!test_me(function () { - // opts.conditions = []; - opts.rules = []; - opts.showSource = false; - opts.__in_rules_failure_analysis_mode__ = true; - }, 'One or more of your lexer rules are possibly botched?', ex, null)) { - // kill each rule action block, one at a time and test again after each 'edit': - var rv = false; - for (var i = 0, len = dict.rules.length; i < len; i++) { - dict.rules[i][1] = '{ /* nada */ }'; - rv = test_me(function () { - // opts.conditions = []; - // opts.rules = []; - // opts.__in_rules_failure_analysis_mode__ = true; - }, 'Your lexer rule "' + dict.rules[i][0] + '" action code block is botched?', ex, null); - if (rv) { - break; - } - } - if (!rv) { - test_me(function () { - opts.conditions = []; - opts.rules = []; - opts.performAction = 'null'; - // opts.options = {}; - // opts.caseHelperInclude = '{}'; - opts.showSource = false; - opts.__in_rules_failure_analysis_mode__ = true; - - dump = false; - }, 'One or more of your lexer rule action code block(s) are possibly botched?', ex, null); - } - } - } - throw ex; - }); - - lexer.setInput(input); - - /** @public */ - lexer.generate = function () { - return generateFromOpts(opts); - }; - /** @public */ - lexer.generateModule = function () { - return generateModule(opts); - }; - /** @public */ - lexer.generateCommonJSModule = function () { - return generateCommonJSModule(opts); - }; - /** @public */ - lexer.generateAMDModule = function () { - return generateAMDModule(opts); - }; - - // internal APIs to aid testing: - /** @public */ - lexer.getExpandedMacros = function () { - return opts.macros; - }; - - return lexer; -} - -// code stripping performance test for very simple grammar: -// -// - removing backtracking parser code branches: 730K -> 750K rounds -// - removing all location info tracking: yylineno, yylloc, etc.: 750K -> 900K rounds -// - no `yyleng`: 900K -> 905K rounds -// - no `this.done` as we cannot have a NULL `_input` anymore: 905K -> 930K rounds -// - `simpleCaseActionClusters` as array instead of hash object: 930K -> 940K rounds -// - lexers which have only return stmts, i.e. only a -// `simpleCaseActionClusters` lookup table to produce -// lexer tokens: *inline* the `performAction` call: 940K -> 950K rounds -// - given all the above, you can *inline* what's left of -// `lexer_next()`: 950K -> 955K rounds (? this stuff becomes hard to measure; inaccuracy abounds!) -// -// Total gain when we forget about very minor (and tough to nail) *inlining* `lexer_next()` gains: -// -// 730 -> 950 ~ 30% performance gain. -// - -// As a function can be reproduced in source-code form by any JavaScript engine, we're going to wrap this chunk -// of code in a function so that we can easily get it including it comments, etc.: -/** -@public -@nocollapse -*/ -function getRegExpLexerPrototype() { - return { - EOF: 1, - ERROR: 2, - - // JisonLexerError: JisonLexerError, /// <-- injected by the code generator - - // options: {}, /// <-- injected by the code generator - - // yy: ..., /// <-- injected by setInput() - - __currentRuleSet__: null, /// <-- internal rule set cache for the current lexer state - - __error_infos: [], /// INTERNAL USE ONLY: the set of lexErrorInfo objects created since the last cleanup - - __decompressed: false, /// INTERNAL USE ONLY: mark whether the lexer instance has been 'unfolded' completely and is now ready for use - - done: false, /// INTERNAL USE ONLY - _backtrack: false, /// INTERNAL USE ONLY - _input: '', /// INTERNAL USE ONLY - _more: false, /// INTERNAL USE ONLY - _signaled_error_token: false, /// INTERNAL USE ONLY - - conditionStack: [], /// INTERNAL USE ONLY; managed via `pushState()`, `popState()`, `topState()` and `stateStackSize()` - - match: '', /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks input which has been matched so far for the lexer token under construction. `match` is identical to `yytext` except that this one still contains the matched input string after `lexer.performAction()` has been invoked, where userland code MAY have changed/replaced the `yytext` value entirely! - matched: '', /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks entire input which has been matched so far - matches: false, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks RE match result for last (successful) match attempt - yytext: '', /// ADVANCED USE ONLY: tracks input which has been matched so far for the lexer token under construction; this value is transferred to the parser as the 'token value' when the parser consumes the lexer token produced through a call to the `lex()` API. - offset: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks the 'cursor position' in the input string, i.e. the number of characters matched so far - yyleng: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: length of matched input for the token under construction (`yytext`) - yylineno: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: 'line number' at which the token under construction is located - yylloc: null, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks location info (lines + columns) for the token under construction - - /** - INTERNAL USE: construct a suitable error info hash object instance for `parseError`. - - @public - @this {RegExpLexer} - */ - constructLexErrorInfo: function lexer_constructLexErrorInfo(msg, recoverable) { - /** @constructor */ - var pei = { - errStr: msg, - recoverable: !!recoverable, - text: this.match, // This one MAY be empty; userland code should use the `upcomingInput` API to obtain more text which follows the 'lexer cursor position'... - token: null, - line: this.yylineno, - loc: this.yylloc, - yy: this.yy, - lexer: this, - - /** - and make sure the error info doesn't stay due to potential - ref cycle via userland code manipulations. - These would otherwise all be memory leak opportunities! - - Note that only array and object references are nuked as those - constitute the set of elements which can produce a cyclic ref. - The rest of the members is kept intact as they are harmless. - - @public - @this {LexErrorInfo} - */ - destroy: function destructLexErrorInfo() { - // remove cyclic references added to error info: - // info.yy = null; - // info.lexer = null; - // ... - var rec = !!this.recoverable; - for (var key in this) { - if (this.hasOwnProperty(key) && typeof key === 'object') { - this[key] = undefined; - } - } - this.recoverable = rec; - } - }; - // track this instance so we can `destroy()` it once we deem it superfluous and ready for garbage collection! - this.__error_infos.push(pei); - return pei; - }, - - /** - handler which is invoked when a lexer error occurs. - - @public - @this {RegExpLexer} - */ - parseError: function lexer_parseError(str, hash, ExceptionClass) { - if (!ExceptionClass) { - ExceptionClass = this.JisonLexerError; - } - if (this.yy.parser && typeof this.yy.parser.parseError === 'function') { - return this.yy.parser.parseError(str, hash, ExceptionClass) || this.ERROR; - } else if (typeof this.yy.parseError === 'function') { - return this.yy.parseError(str, hash, ExceptionClass) || this.ERROR; - } else { - throw new ExceptionClass(str, hash); - } - }, - - /** - method which implements `yyerror(str, ...args)` functionality for use inside lexer actions. - - @public - @this {RegExpLexer} - */ - yyerror: function yyError(str /*, ...args */) { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': ' + str, this.options.lexerErrorsAreRecoverable); - - // Add any extra args to the hash under the name `extra_error_attributes`: - var args = Array.prototype.slice.call(arguments, 1); - if (args.length) { - hash.extra_error_attributes = args; - } - - return (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - }, - - /** - final cleanup function for when we have completed lexing the input; - make it an API so that external code can use this one once userland - code has decided it's time to destroy any lingering lexer error - hash object instances and the like: this function helps to clean - up these constructs, which *may* carry cyclic references which would - otherwise prevent the instances from being properly and timely - garbage-collected, i.e. this function helps prevent memory leaks! - - @public - @this {RegExpLexer} - */ - cleanupAfterLex: function lexer_cleanupAfterLex(do_not_nuke_errorinfos) { - var rv; - - // prevent lingering circular references from causing memory leaks: - this.setInput('', {}); - - // nuke the error hash info instances created during this run. - // Userland code must COPY any data/references - // in the error hash instance(s) it is more permanently interested in. - if (!do_not_nuke_errorinfos) { - for (var i = this.__error_infos.length - 1; i >= 0; i--) { - var el = this.__error_infos[i]; - if (el && typeof el.destroy === 'function') { - el.destroy(); - } - } - this.__error_infos.length = 0; - } - - return this; - }, - - /** - clear the lexer token context; intended for internal use only - - @public - @this {RegExpLexer} - */ - clear: function lexer_clear() { - this.yytext = ''; - this.yyleng = 0; - this.match = ''; - this.matches = false; - this._more = false; - this._backtrack = false; - - var col = this.yylloc ? this.yylloc.last_column : 0; - this.yylloc = { - first_line: this.yylineno + 1, - first_column: col, - last_line: this.yylineno + 1, - last_column: col, - - range: (this.options.ranges ? [this.offset, this.offset] : undefined) - }; - }, - - /** - resets the lexer, sets new input - - @public - @this {RegExpLexer} - */ - setInput: function lexer_setInput(input, yy) { - this.yy = yy || this.yy || {}; - - // also check if we've fully initialized the lexer instance, - // including expansion work to be done to go from a loaded - // lexer to a usable lexer: - if (!this.__decompressed) { - // step 1: decompress the regex list: - var rules = this.rules; - for (var i = 0, len = rules.length; i < len; i++) { - var rule_re = rules[i]; - - // compression: is the RE an xref to another RE slot in the rules[] table? - if (typeof rule_re === 'number') { - rules[i] = rules[rule_re]; - } - } - - // step 2: unfold the conditions[] set to make these ready for use: - var conditions = this.conditions; - for (var k in conditions) { - var spec = conditions[k]; - - var rule_ids = spec.rules; - - var len = rule_ids.length; - var rule_regexes = new Array(len + 1); // slot 0 is unused; we use a 1-based index approach here to keep the hottest code in `lexer_next()` fast and simple! - var rule_new_ids = new Array(len + 1); - - for (var i = 0; i < len; i++) { - var idx = rule_ids[i]; - var rule_re = rules[idx]; - rule_regexes[i + 1] = rule_re; - rule_new_ids[i + 1] = idx; - } - - spec.rules = rule_new_ids; - spec.__rule_regexes = rule_regexes; - spec.__rule_count = len; - } - - this.__decompressed = true; - } - - this._input = input || ''; - this.clear(); - this._signaled_error_token = false; - this.done = false; - this.yylineno = 0; - this.matched = ''; - this.conditionStack = ['INITIAL']; - this.__currentRuleSet__ = null; - this.yylloc = { - first_line: 1, - first_column: 0, - last_line: 1, - last_column: 0, - - range: (this.options.ranges ? [0, 0] : undefined) - }; - this.offset = 0; - return this; - }, - - /** - push a new input into the lexer and activate it: - the old input position is stored and will be resumed - once this new input has been consumed. - - Use this API to help implement C-preprocessor-like - `#include` statements. - - Available options: - - - `emit_EOF_at_end` : {int} the `EOF`-like token to emit - when the new input is consumed: use - this to mark the end of the new input - in the parser grammar. zero/falsey - token value means no end marker token - will be emitted before the lexer - resumes reading from the previous input. - - @public - @this {RegExpLexer} - */ - pushInput: function lexer_pushInput(input, label, options) { - options = options || {}; - - this._input = input || ''; - this.clear(); - // this._signaled_error_token = false; - this.done = false; - this.yylineno = 0; - this.matched = ''; - // this.conditionStack = ['INITIAL']; - // this.__currentRuleSet__ = null; - this.yylloc = { - first_line: 1, - first_column: 0, - last_line: 1, - last_column: 0, - - range: (this.options.ranges ? [0, 0] : undefined) - }; - this.offset = 0; - return this; - }, - - /** - consumes and returns one char from the input - - @public - @this {RegExpLexer} - */ - input: function lexer_input() { - if (!this._input) { - //this.done = true; -- don't set `done` as we want the lex()/next() API to be able to produce one custom EOF token match after this anyhow. (lexer can match special <> tokens and perform user action code for a <> match, but only does so *once*) - return null; - } - var ch = this._input[0]; - this.yytext += ch; - this.yyleng++; - this.offset++; - this.match += ch; - this.matched += ch; - // Count the linenumber up when we hit the LF (or a stand-alone CR). - // On CRLF, the linenumber is incremented when you fetch the CR or the CRLF combo - // and we advance immediately past the LF as well, returning both together as if - // it was all a single 'character' only. - var slice_len = 1; - var lines = false; - if (ch === '\n') { - lines = true; - } else if (ch === '\r') { - lines = true; - var ch2 = this._input[1]; - if (ch2 === '\n') { - slice_len++; - ch += ch2; - this.yytext += ch2; - this.yyleng++; - this.offset++; - this.match += ch2; - this.matched += ch2; - if (this.options.ranges) { - this.yylloc.range[1]++; - } - } - } - if (lines) { - this.yylineno++; - this.yylloc.last_line++; - this.yylloc.last_column = 0; - } else { - this.yylloc.last_column++; - } - if (this.options.ranges) { - this.yylloc.range[1]++; - } - - this._input = this._input.slice(slice_len); - return ch; - }, - - /** - unshifts one char (or an entire string) into the input - - @public - @this {RegExpLexer} - */ - unput: function lexer_unput(ch) { - var len = ch.length; - var lines = ch.split(/(?:\r\n?|\n)/g); - - this._input = ch + this._input; - this.yytext = this.yytext.substr(0, this.yytext.length - len); - this.yyleng = this.yytext.length; - this.offset -= len; - this.match = this.match.substr(0, this.match.length - len); - this.matched = this.matched.substr(0, this.matched.length - len); - - if (lines.length > 1) { - this.yylineno -= lines.length - 1; - - this.yylloc.last_line = this.yylineno + 1; - var pre = this.match; - var pre_lines = pre.split(/(?:\r\n?|\n)/g); - if (pre_lines.length === 1) { - pre = this.matched; - pre_lines = pre.split(/(?:\r\n?|\n)/g); - } - this.yylloc.last_column = pre_lines[pre_lines.length - 1].length; - } else { - this.yylloc.last_column -= len; - } - - if (this.options.ranges) { - this.yylloc.range[1] = this.yylloc.range[0] + this.yyleng; - } - this.done = false; - return this; - }, - - /** - cache matched text and append it on next action - - @public - @this {RegExpLexer} - */ - more: function lexer_more() { - this._more = true; - return this; - }, - - /** - signal the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead. - - @public - @this {RegExpLexer} - */ - reject: function lexer_reject() { - if (this.options.backtrack_lexer) { - this._backtrack = true; - } else { - // when the `parseError()` call returns, we MUST ensure that the error is registered. - // We accomplish this by signaling an 'error' token to be produced for the current - // `.lex()` run. - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).' + pos_str, false); - this._signaled_error_token = (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - } - return this; - }, - - /** - retain first n characters of the match - - @public - @this {RegExpLexer} - */ - less: function lexer_less(n) { - return this.unput(this.match.slice(n)); - }, - - /** - return (part of the) already matched input, i.e. for error messages. - - Limit the returned string length to `maxSize` (default: 20). - - Limit the returned string to the `maxLines` number of lines of input (default: 1). - - Negative limit values equal *unlimited*. - - @public - @this {RegExpLexer} - */ - pastInput: function lexer_pastInput(maxSize, maxLines) { - var past = this.matched.substring(0, this.matched.length - this.match.length); - if (maxSize < 0) - maxSize = past.length; - else if (!maxSize) - maxSize = 20; - if (maxLines < 0) - maxLines = past.length; // can't ever have more input lines than this! - else if (!maxLines) - maxLines = 1; - // `substr` anticipation: treat \r\n as a single character and take a little - // more than necessary so that we can still properly check against maxSize - // after we've transformed and limited the newLines in here: - past = past.substr(-maxSize * 2 - 2); - // now that we have a significantly reduced string to process, transform the newlines - // and chop them, then limit them: - var a = past.replace(/\r\n|\r/g, '\n').split('\n'); - a = a.slice(-maxLines); - past = a.join('\n'); - // When, after limiting to maxLines, we still have too much to return, - // do add an ellipsis prefix... - if (past.length > maxSize) { - past = '...' + past.substr(-maxSize); - } - return past; - }, - - /** - return (part of the) upcoming input, i.e. for error messages. - - Limit the returned string length to `maxSize` (default: 20). - - Limit the returned string to the `maxLines` number of lines of input (default: 1). - - Negative limit values equal *unlimited*. - - @public - @this {RegExpLexer} - */ - upcomingInput: function lexer_upcomingInput(maxSize, maxLines) { - var next = this.match; - if (maxSize < 0) - maxSize = next.length + this._input.length; - else if (!maxSize) - maxSize = 20; - if (maxLines < 0) - maxLines = maxSize; // can't ever have more input lines than this! - else if (!maxLines) - maxLines = 1; - // `substring` anticipation: treat \r\n as a single character and take a little - // more than necessary so that we can still properly check against maxSize - // after we've transformed and limited the newLines in here: - if (next.length < maxSize * 2 + 2) { - next += this._input.substring(0, maxSize * 2 + 2); // substring is faster on Chrome/V8 - } - // now that we have a significantly reduced string to process, transform the newlines - // and chop them, then limit them: - var a = next.replace(/\r\n|\r/g, '\n').split('\n'); - a = a.slice(0, maxLines); - next = a.join('\n'); - // When, after limiting to maxLines, we still have too much to return, - // do add an ellipsis postfix... - if (next.length > maxSize) { - next = next.substring(0, maxSize) + '...'; - } - return next; - }, - - /** - return a string which displays the character position where the lexing error occurred, i.e. for error messages - - @public - @this {RegExpLexer} - */ - showPosition: function lexer_showPosition(maxPrefix, maxPostfix) { - var pre = this.pastInput(maxPrefix).replace(/\s/g, ' '); - var c = new Array(pre.length + 1).join('-'); - return pre + this.upcomingInput(maxPostfix).replace(/\s/g, ' ') + '\n' + c + '^'; - }, - - /** - helper function, used to produce a human readable description as a string, given - the input `yylloc` location object. - - Set `display_range_too` to TRUE to include the string character index position(s) - in the description if the `yylloc.range` is available. - - @public - @this {RegExpLexer} - */ - describeYYLLOC: function lexer_describe_yylloc(yylloc, display_range_too) { - var l1 = yylloc.first_line; - var l2 = yylloc.last_line; - var o1 = yylloc.first_column; - var o2 = yylloc.last_column; - var dl = l2 - l1; - var d_o = o2 - o1; - var rv; - if (dl === 0) { - rv = 'line ' + l1 + ', '; - if (d_o === 1) { - rv += 'column ' + o1; - } else { - rv += 'columns ' + o1 + ' .. ' + o2; - } - } else { - rv = 'lines ' + l1 + '(column ' + o1 + ') .. ' + l2 + '(column ' + o2 + ')'; - } - if (yylloc.range && display_range_too) { - var r1 = yylloc.range[0]; - var r2 = yylloc.range[1] - 1; - if (r2 === r1) { - rv += ' {String Offset: ' + r1 + '}'; - } else { - rv += ' {String Offset range: ' + r1 + ' .. ' + r2 + '}'; - } - } - return rv; - // return JSON.stringify(yylloc); - }, - - /** - test the lexed token: return FALSE when not a match, otherwise return token. - - `match` is supposed to be an array coming out of a regex match, i.e. `match[0]` - contains the actually matched text string. - - Also move the input cursor forward and update the match collectors: - - - `yytext` - - `yyleng` - - `match` - - `matches` - - `yylloc` - - `offset` - - @public - @this {RegExpLexer} - */ - test_match: function lexer_test_match(match, indexed_rule, parseParams) { - var token, - lines, - backup, - match_str, - match_str_len; - - if (this.options.backtrack_lexer) { - // save context - backup = { - yylineno: this.yylineno, - yylloc: { - first_line: this.yylloc.first_line, - last_line: this.yylloc.last_line, - first_column: this.yylloc.first_column, - last_column: this.yylloc.last_column, - - range: (this.options.ranges ? this.yylloc.range.slice(0) : undefined) - }, - yytext: this.yytext, - match: this.match, - matches: this.matches, - matched: this.matched, - yyleng: this.yyleng, - offset: this.offset, - _more: this._more, - _input: this._input, - //_signaled_error_token: this._signaled_error_token, - yy: this.yy, - conditionStack: this.conditionStack.slice(0), - done: this.done - }; - } - - match_str = match[0]; - match_str_len = match_str.length; - // if (match_str.indexOf('\n') !== -1 || match_str.indexOf('\r') !== -1) { - lines = match_str.split(/(?:\r\n?|\n)/g); - if (lines.length > 1) { - this.yylineno += lines.length - 1; - - this.yylloc.last_line = this.yylineno + 1, - this.yylloc.last_column = lines[lines.length - 1].length; - } else { - this.yylloc.last_column += match_str_len; - } - // } - this.yytext += match_str; - this.match += match_str; - this.matches = match; - this.yyleng = this.yytext.length; - if (this.options.ranges) { - this.yylloc.range[1] += match_str_len; - } - // previous lex rules MAY have invoked the `more()` API rather than producing a token: - // those rules will already have moved this `offset` forward matching their match lengths, - // hence we must only add our own match length now: - this.offset += match_str_len; - this._more = false; - this._backtrack = false; - this._input = this._input.slice(match_str_len); - this.matched += match_str; - - // calling this method: - // - // function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START) {...} - token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1] /* = YY_START */, parseParams); - // otherwise, when the action codes are all simple return token statements: - //token = this.simpleCaseActionClusters[indexed_rule]; - - if (this.done && this._input) { - this.done = false; - } - if (token) { - return token; - } else if (this._backtrack) { - // recover context - for (var k in backup) { - this[k] = backup[k]; - } - this.__currentRuleSet__ = null; - return false; // rule action called reject() implying the next rule should be tested instead. - } else if (this._signaled_error_token) { - // produce one 'error' token as `.parseError()` in `reject()` did not guarantee a failure signal by throwing an exception! - token = this._signaled_error_token; - this._signaled_error_token = false; - return token; - } - return false; - }, - - /** - return next match in input - - @public - @this {RegExpLexer} - */ - next: function lexer_next(parseParams) { - if (this.done) { - this.clear(); - return this.EOF; - } - if (!this._input) { - this.done = true; - } - - var token, - match, - tempMatch, - index; - if (!this._more) { - this.clear(); - } - var spec = this.__currentRuleSet__; - if (!spec) { - // Update the ruleset cache as we apparently encountered a state change or just started lexing. - // The cache is set up for fast lookup -- we assume a lexer will switch states much less often than it will - // invoke the `lex()` token-producing API and related APIs, hence caching the set for direct access helps - // speed up those activities a tiny bit. - spec = this.__currentRuleSet__ = this._currentRules(); - // Check whether a *sane* condition has been pushed before: this makes the lexer robust against - // user-programmer bugs such as https://github.com/zaach/jison-lex/issues/19 - if (!spec || !spec.rules) { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Internal lexer engine error' + lineno_msg + ': The lex grammar programmer pushed a non-existing condition name "' + this.topState() + '"; this is a fatal error and should be reported to the application programmer team!' + pos_str, false); - // produce one 'error' token until this situation has been resolved, most probably by parse termination! - return (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - } - } - - var rule_ids = spec.rules; - //var dispatch = spec.__dispatch_lut; - var regexes = spec.__rule_regexes; - var len = spec.__rule_count; - - // Note: the arrays are 1-based, while `len` itself is a valid index, - // hence the non-standard less-or-equal check in the next loop condition! - for (var i = 1; i <= len; i++) { - tempMatch = this._input.match(regexes[i]); - if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { - match = tempMatch; - index = i; - if (this.options.backtrack_lexer) { - token = this.test_match(tempMatch, rule_ids[i], parseParams); - if (token !== false) { - return token; - } else if (this._backtrack) { - match = undefined; - continue; // rule action called reject() implying a rule MISmatch. - } else { - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - } else if (!this.options.flex) { - break; - } - } - } - if (match) { - token = this.test_match(match, rule_ids[index], parseParams); - if (token !== false) { - return token; - } - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - if (!this._input) { - this.done = true; - this.clear(); - return this.EOF; - } else { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': Unrecognized text.' + pos_str, this.options.lexerErrorsAreRecoverable); - token = (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - if (token === this.ERROR) { - // we can try to recover from a lexer error that `parseError()` did not 'recover' for us - // by moving forward at least one character at a time: - if (!this.match.length) { - this.input(); - } - } - return token; - } - }, - - /** - return next match that has a token - - @public - @this {RegExpLexer} - */ - lex: function lexer_lex(parseParams) { - var r; - // allow the PRE/POST handlers set/modify the return token for maximum flexibility of the generated lexer: - if (typeof this.options.pre_lex === 'function') { - r = this.options.pre_lex.call(this, parseParams); - } - while (!r) { - r = this.next(parseParams); - } - if (typeof this.options.post_lex === 'function') { - // (also account for a userdef function which does not return any value: keep the token as is) - r = this.options.post_lex.call(this, r, parseParams) || r; - } - return r; - }, - - /** - backwards compatible alias for `pushState()`; - the latter is symmetrical with `popState()` and we advise to use - those APIs in any modern lexer code, rather than `begin()`. - - @public - @this {RegExpLexer} - */ - begin: function lexer_begin(condition) { - return this.pushState(condition); - }, - - /** - activates a new lexer condition state (pushes the new lexer condition state onto the condition stack) - - @public - @this {RegExpLexer} - */ - pushState: function lexer_pushState(condition) { - this.conditionStack.push(condition); - this.__currentRuleSet__ = null; - return this; - }, - - /** - pop the previously active lexer condition state off the condition stack - - @public - @this {RegExpLexer} - */ - popState: function lexer_popState() { - var n = this.conditionStack.length - 1; - if (n > 0) { - this.__currentRuleSet__ = null; - return this.conditionStack.pop(); - } else { - return this.conditionStack[0]; - } - }, - - /** - return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available - - @public - @this {RegExpLexer} - */ - topState: function lexer_topState(n) { - n = this.conditionStack.length - 1 - Math.abs(n || 0); - if (n >= 0) { - return this.conditionStack[n]; - } else { - return 'INITIAL'; - } - }, - - /** - (internal) determine the lexer rule set which is active for the currently active lexer condition state - - @public - @this {RegExpLexer} - */ - _currentRules: function lexer__currentRules() { - if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) { - return this.conditions[this.conditionStack[this.conditionStack.length - 1]]; - } else { - return this.conditions['INITIAL']; - } - }, - - /** - return the number of states currently on the stack - - @public - @this {RegExpLexer} - */ - stateStackSize: function lexer_stateStackSize() { - return this.conditionStack.length; - } - }; -} - -RegExpLexer.prototype = getRegExpLexerPrototype(); - - - - -// Fill in the optional, extra parse parameters (`%parse-param ...`) -// in the generated *lexer*. -// -// See for important context: -// -// https://github.com/zaach/jison/pull/332 -function expandParseArguments(parseFn, options) { - var arglist = (options && options.parseParams); - - if (!arglist) { - parseFn = parseFn.replace(/, parseParams\b/g, ''); - parseFn = parseFn.replace(/\bparseParams\b/g, ''); - } else { - parseFn = parseFn.replace(/, parseParams\b/g, ', ' + arglist.join(', ')); - parseFn = parseFn.replace(/\bparseParams\b/g, arglist.join(', ')); - } - return parseFn; -} - - - -// The lexer code stripper, driven by optimization analysis settings and -// lexer options, which cannot be changed at run-time. -function stripUnusedLexerCode(src, options) { - return src; -} - - - - - -// generate lexer source from a grammar -/** @public */ -function generate(dict, tokens, build_options) { - var opt = processGrammar(dict, tokens, build_options); - - return generateFromOpts(opt); -} - -// process the grammar and build final data structures and functions -/** @public */ -function processGrammar(dict, tokens, build_options) { - build_options = build_options || {}; - var opts = { - // include the knowledge passed through `build_options` about which lexer - // features will actually be *used* by the environment (which in 99.9% - // of cases is a jison *parser*): - // - // (this stuff comes straight from the jison Optimization Analysis.) - // - parseActionsAreAllDefault: build_options.parseActionsAreAllDefault, - parseActionsUseYYLENG: build_options.parseActionsUseYYLENG, - parseActionsUseYYLINENO: build_options.parseActionsUseYYLINENO, - parseActionsUseYYTEXT: build_options.parseActionsUseYYTEXT, - parseActionsUseYYLOC: build_options.parseActionsUseYYLOC, - parseActionsUseParseError: build_options.parseActionsUseParseError, - parseActionsUseYYERROR: build_options.parseActionsUseYYERROR, - parseActionsUseYYERROK: build_options.parseActionsUseYYERROK, - parseActionsUseYYCLEARIN: build_options.parseActionsUseYYCLEARIN, - parseActionsUseValueTracking: build_options.parseActionsUseValueTracking, - parseActionsUseValueAssignment: build_options.parseActionsUseValueAssignment, - parseActionsUseLocationTracking: build_options.parseActionsUseLocationTracking, - parseActionsUseLocationAssignment: build_options.parseActionsUseLocationAssignment, - parseActionsUseYYSTACK: build_options.parseActionsUseYYSTACK, - parseActionsUseYYSSTACK: build_options.parseActionsUseYYSSTACK, - parseActionsUseYYSTACKPOINTER: build_options.parseActionsUseYYSTACKPOINTER, - parserHasErrorRecovery: build_options.parserHasErrorRecovery, - }; - - dict = autodetectAndConvertToJSONformat(dict, build_options) || {}; - - // Feed the possibly reprocessed 'dictionary' above back to the caller - // (for use by our error diagnostic assistance code) - opts.lex_rule_dictionary = dict; - - // Always provide the lexer with an options object, even if it's empty! - // Make sure to camelCase all options: - opts.options = mkStdOptions(build_options, dict.options); - - opts.parseParams = opts.options.parseParams; - - opts.moduleType = opts.options.moduleType; - opts.moduleName = opts.options.moduleName; - - opts.conditions = prepareStartConditions(dict.startConditions); - opts.conditions.INITIAL = { - rules: [], - inclusive: true - }; - - var code = buildActions(dict, tokens, opts); - opts.performAction = code.actions; - opts.caseHelperInclude = code.caseHelperInclude; - opts.rules = code.rules; - opts.macros = code.macros; - - opts.conditionStack = ['INITIAL']; - - opts.actionInclude = (dict.actionInclude || ''); - opts.moduleInclude = (opts.moduleInclude || '') + (dict.moduleInclude || '').trim(); - - return opts; -} - -// Assemble the final source from the processed grammar -/** @public */ -function generateFromOpts(opt) { - var code = ''; - - switch (opt.moduleType) { - case 'js': - code = generateModule(opt); - break; - case 'amd': - code = generateAMDModule(opt); - break; - case 'es': - code = generateESModule(opt); - break; - case 'commonjs': - default: - code = generateCommonJSModule(opt); - break; - } - - return code; -} - -function generateRegexesInitTableCode(opt) { - var a = opt.rules; - var print_xregexp = opt.options && opt.options.xregexp; - a = a.map(function generateXRegExpInitCode(re) { - if (re instanceof XRegExp) { - // When we don't need the special XRegExp sauce at run-time, we do with the original - // JavaScript RegExp instance a.k.a. 'native regex': - if (re.xregexp.isNative || !print_xregexp) { - return re; - } - // And make sure to escape the regex to make it suitable for placement inside a *string* - // as it is passed as a string argument to the XRegExp constructor here. - return 'new XRegExp("' + re.xregexp.source.replace(/[\\"]/g, '\\$&') + '", "' + re.xregexp.flags + '")'; - } else { - return re; - } - }); - return a.join(',\n'); -} - -function generateModuleBody(opt) { - // make the JSON output look more like JavaScript: - function cleanupJSON(str) { - str = str.replace(/ "rules": \[/g, ' rules: ['); - str = str.replace(/ "inclusive": /g, ' inclusive: '); - return str; - } - - function produceOptions(opts) { - var obj = {}; - var do_not_pass = { - debug: !opts.debug, // do not include this item when it is FALSE as there's no debug tracing built into the generated grammar anyway! - enableDebugLogs: 1, - json: 1, - _: 1, - noMain: 1, - dumpSourceCodeOnFailure: 1, - throwErrorOnCompileFailure: 1, - reportStats: 1, - file: 1, - outfile: 1, - inputPath: 1, - inputFilename: 1, - defaultModuleName: 1, - moduleName: 1, - moduleType: 1, - lexerErrorsAreRecoverable: 0, - flex: 0, - backtrack_lexer: 0, - caseInsensitive: 0, - showSource: 1, - parseActionsAreAllDefault: 1, - parseActionsUseYYLENG: 1, - parseActionsUseYYLINENO: 1, - parseActionsUseYYTEXT: 1, - parseActionsUseYYLOC: 1, - parseActionsUseParseError: 1, - parseActionsUseYYERROR: 1, - parseActionsUseYYERROK: 1, - parseActionsUseYYCLEARIN: 1, - parseActionsUseValueTracking: 1, - parseActionsUseValueAssignment: 1, - parseActionsUseLocationTracking: 1, - parseActionsUseLocationAssignment: 1, - parseActionsUseYYSTACK: 1, - parseActionsUseYYSSTACK: 1, - parseActionsUseYYSTACKPOINTER: 1, - parserHasErrorRecovery: 1, - }; - for (var k in opts) { - if (!do_not_pass[k] && opts[k] != null && opts[k] !== false) { - // make sure numeric values are encoded as numeric, the rest as boolean/string. - if (typeof opts[k] === 'string') { - var f = parseFloat(opts[k]); - if (f == opts[k]) { - obj[k] = f; - continue; - } - } - obj[k] = opts[k]; - } - } - - // And now some options which should receive some special processing: - var pre = obj.pre_lex; - var post = obj.post_lex; - // since JSON cannot encode functions, we'll have to do it manually at run-time, i.e. later on: - if (pre) { - obj.pre_lex = true; - } - if (post) { - obj.post_lex = true; - } - - var js = JSON.stringify(obj, null, 2); - - js = js.replace(new XRegExp(' "([\\p{Alphabetic}_][\\p{Alphabetic}_\\p{Number}]*)": ', 'g'), ' $1: '); - js = js.replace(/^( +)pre_lex: true(,)?$/gm, function (m, ls, tc) { - return ls + 'pre_lex: ' + String(pre) + (tc || ''); - }); - js = js.replace(/^( +)post_lex: true(,)?$/gm, function (m, ls, tc) { - return ls + 'post_lex: ' + String(post) + (tc || ''); - }); - return js; - } - - - var out; - if (opt.rules.length > 0 || opt.__in_rules_failure_analysis_mode__) { - var descr; - - // we don't mind that the `test_me()` code above will have this `lexer` variable re-defined: - // JavaScript is fine with that. - var code = [` -var lexer = { -`, '' /* slot #1: placeholder for analysis report further below */]; - - // get the RegExpLexer.prototype in source code form: - var protosrc = printFunctionSourceCodeContainer(getRegExpLexerPrototype, 1); - // and strip off the surrounding bits we don't want: - protosrc = protosrc - .replace(/^[\s\r\n]*return[\s\r\n]*\{/, '') - .replace(/\s*\};[\s\r\n]*$/, ''); - protosrc = expandParseArguments(protosrc, opt.options); - protosrc = stripUnusedLexerCode(protosrc, opt); - code.push(protosrc + ',\n'); - - assert(opt.options); - // Assure all options are camelCased: - assert(typeof opt.options['case-insensitive'] === 'undefined'); - - code.push(' options: ' + produceOptions(opt.options)); - - var performActionCode = String(opt.performAction); - var simpleCaseActionClustersCode = String(opt.caseHelperInclude); - var rulesCode = generateRegexesInitTableCode(opt); - var conditionsCode = cleanupJSON(JSON.stringify(opt.conditions, null, 2)); - code.push(`, - JisonLexerError: JisonLexerError, - performAction: ${performActionCode}, - simpleCaseActionClusters: ${simpleCaseActionClustersCode}, - rules: [ - ${rulesCode} - ], - conditions: ${conditionsCode} -}; -`); - - // inject analysis report now: - code[1] = ` - // Code Generator Information Report - // --------------------------------- - // - // Options: - // - // backtracking: .................... ${opt.options.backtrack_lexer} - // location.ranges: ................. ${opt.options.ranges} - // location line+column tracking: ... ${opt.options.trackPosition} - // - // - // Forwarded Parser Analysis flags: - // - // uses yyleng: ..................... ${opt.parseActionsUseYYLENG} - // uses yylineno: ................... ${opt.parseActionsUseYYLINENO} - // uses yytext: ..................... ${opt.parseActionsUseYYTEXT} - // uses yylloc: ..................... ${opt.parseActionsUseYYLOC} - // uses lexer values: ............... ${opt.parseActionsUseValueTracking} / ${opt.parseActionsUseValueAssignment} - // location tracking: ............... ${opt.parseActionsUseLocationTracking} - // location assignment: ............. ${opt.parseActionsUseLocationAssignment} - // - // - // Lexer Analysis flags: - // - // uses yyleng: ..................... ${opt.lexerActionsUseYYLENG} - // uses yylineno: ................... ${opt.lexerActionsUseYYLINENO} - // uses yytext: ..................... ${opt.lexerActionsUseYYTEXT} - // uses yylloc: ..................... ${opt.lexerActionsUseYYLOC} - // uses ParseError API: ............. ${opt.lexerActionsUseParseError} - // uses location tracking & editing: ${opt.lexerActionsUseLocationTracking} - // uses more() API: ................. ${opt.lexerActionsUseMore} - // uses unput() API: ................ ${opt.lexerActionsUseUnput} - // uses reject() API: ............... ${opt.lexerActionsUseReject} - // uses less() API: ................. ${opt.lexerActionsUseLess} - // uses display APIs pastInput(), upcomingInput(), showPosition(): - // ............................. ${opt.lexerActionsUseDisplayAPIs} - // uses describeYYLLOC() API: ....... ${opt.lexerActionsUseDescribeYYLOC} - // - // --------- END OF REPORT ----------- - -`; - - out = code.join(''); - } else { - // We're clearly looking at a custom lexer here as there's no lexer rules at all. - // - // We are re-purposing the `%{...%}` `actionInclude` code block here as it serves no purpose otherwise. - // - // Meanwhile we make sure we have the `lexer` variable declared in *local scope* no matter - // what crazy stuff (or lack thereof) the userland code is pulling in the `actionInclude` chunk. - out = 'var lexer;\n'; - - if (opt.actionInclude) { - out += opt.actionInclude + (!opt.actionInclude.match(/;[\s\r\n]*$/) ? ';' : '') + '\n'; - } - } - - // The output of this function is guaranteed to read something like this: - // - // ``` - // var lexer; - // - // bla bla bla bla ... lotsa bla bla; - // ``` - // - // and that should work nicely as an `eval()`-able piece of source code. - return out; -} - -function generateGenericHeaderComment() { - var out = '/* lexer generated by jison-lex ' + version + ' */\n' - + '/*\n' - + ' * Returns a Lexer object of the following structure:\n' - + ' *\n' - + ' * Lexer: {\n' - + ' * yy: {} The so-called "shared state" or rather the *source* of it;\n' - + ' * the real "shared state" `yy` passed around to\n' - + ' * the rule actions, etc. is a derivative/copy of this one,\n' - + ' * not a direct reference!\n' - + ' * }\n' - + ' *\n' - + ' * Lexer.prototype: {\n' - + ' * yy: {},\n' - + ' * EOF: 1,\n' - + ' * ERROR: 2,\n' - + ' *\n' - + ' * JisonLexerError: function(msg, hash),\n' - + ' *\n' - + ' * performAction: function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START, ...),\n' - + ' * where `...` denotes the (optional) additional arguments the user passed to\n' - + ' * `lexer.lex(...)` and specified by way of `%parse-param ...` in the **parser** grammar file\n' - + ' *\n' - + ' * The function parameters and `this` have the following value/meaning:\n' - + ' * - `this` : reference to the `lexer` instance.\n' - + ' *\n' - + ' * - `yy` : a reference to the `yy` "shared state" object which was passed to the lexer\n' - + ' * by way of the `lexer.setInput(str, yy)` API before.\n' - + ' *\n' - + ' * - `yy_` : lexer instance reference used internally.\n' - + ' *\n' - + ' * - `$avoiding_name_collisions` : index of the matched lexer rule (regex), used internally.\n' - + ' *\n' - + ' * - `YY_START`: the current lexer "start condition" state.\n' - + ' *\n' - + ' * - `...` : the extra arguments you specified in the `%parse-param` statement in your\n' - + ' * **parser** grammar definition file and which are passed to the lexer via\n' - + ' * its `lexer.lex(...)` API.\n' - + ' *\n' - + ' * parseError: function(str, hash, ExceptionClass),\n' - + ' *\n' - + ' * constructLexErrorInfo: function(error_message, is_recoverable),\n' - + ' * Helper function.\n' - + ' * Produces a new errorInfo \'hash object\' which can be passed into `parseError()`.\n' - + ' * See it\'s use in this lexer kernel in many places; example usage:\n' - + ' *\n' - + ' * var infoObj = lexer.constructParseErrorInfo(\'fail!\', true);\n' - + ' * var retVal = lexer.parseError(infoObj.errStr, infoObj, lexer.JisonLexerError);\n' - + ' *\n' - + ' * options: { ... lexer %options ... },\n' - + ' *\n' - + ' * lex: function([args...]),\n' - + ' * Produce one token of lexed input, which was passed in earlier via the `lexer.setInput()` API.\n' - + ' * You MAY use the additional `args...` parameters as per `%parse-param` spec of the **parser** grammar:\n' - + ' * these extra `args...` are passed verbatim to the lexer rules\' action code.\n' - + ' *\n' - + ' * cleanupAfterLex: function(do_not_nuke_errorinfos),\n' - + ' * Helper function.\n' - + ' * This helper API is invoked when the parse process has completed. This helper may\n' - + ' * be invoked by user code to ensure the internal lexer gets properly garbage collected.\n' - + ' *\n' - + ' * setInput: function(input, [yy]),\n' - + ' * input: function(),\n' - + ' * unput: function(str),\n' - + ' * more: function(),\n' - + ' * reject: function(),\n' - + ' * less: function(n),\n' - + ' * pastInput: function(n),\n' - + ' * upcomingInput: function(n),\n' - + ' * showPosition: function(),\n' - + ' * test_match: function(regex_match_array, rule_index),\n' - + ' * next: function(...),\n' - + ' * lex: function(...),\n' - + ' * begin: function(condition),\n' - + ' * pushState: function(condition),\n' - + ' * popState: function(),\n' - + ' * topState: function(),\n' - + ' * _currentRules: function(),\n' - + ' * stateStackSize: function(),\n' - + ' *\n' - + ' * options: { ... lexer %options ... },\n' - + ' *\n' - + ' * performAction: function(yy, yy_, $avoiding_name_collisions, YY_START, ...),\n' - + ' * rules: [...],\n' - + ' * conditions: {associative list: name ==> set},\n' - + ' * }\n' - + ' *\n' - + ' *\n' - + ' * token location info (`yylloc`): {\n' - + ' * first_line: n,\n' - + ' * last_line: n,\n' - + ' * first_column: n,\n' - + ' * last_column: n,\n' - + ' * range: [start_number, end_number]\n' - + ' * (where the numbers are indexes into the input string, zero-based)\n' - + ' * }\n' - + ' *\n' - + ' * ---\n' - + ' *\n' - + ' * The `parseError` function receives a \'hash\' object with these members for lexer errors:\n' - + ' *\n' - + ' * {\n' - + ' * text: (matched text)\n' - + ' * token: (the produced terminal token, if any)\n' - + ' * token_id: (the produced terminal token numeric ID, if any)\n' - + ' * line: (yylineno)\n' - + ' * loc: (yylloc)\n' - + ' * recoverable: (boolean: TRUE when the parser MAY have an error recovery rule\n' - + ' * available for this particular error)\n' - + ' * yy: (object: the current parser internal "shared state" `yy`\n' - + ' * as is also available in the rule actions; this can be used,\n' - + ' * for instance, for advanced error analysis and reporting)\n' - + ' * lexer: (reference to the current lexer instance used by the parser)\n' - + ' * }\n' - + ' *\n' - + ' * while `this` will reference the current lexer instance.\n' - + ' *\n' - + ' * When `parseError` is invoked by the lexer, the default implementation will\n' - + ' * attempt to invoke `yy.parser.parseError()`; when this callback is not provided\n' - + ' * it will try to invoke `yy.parseError()` instead. When that callback is also not\n' - + ' * provided, a `JisonLexerError` exception will be thrown containing the error\n' - + ' * message and `hash`, as constructed by the `constructLexErrorInfo()` API.\n' - + ' *\n' - + ' * Note that the lexer\'s `JisonLexerError` error class is passed via the\n' - + ' * `ExceptionClass` argument, which is invoked to construct the exception\n' - + ' * instance to be thrown, so technically `parseError` will throw the object\n' - + ' * produced by the `new ExceptionClass(str, hash)` JavaScript expression.\n' - + ' *\n' - + ' * ---\n' - + ' *\n' - + ' * You can specify lexer options by setting / modifying the `.options` object of your Lexer instance.\n' - + ' * These options are available:\n' - + ' *\n' - + ' * (Options are permanent.)\n' - + ' * \n' - + ' * yy: {\n' - + ' * parseError: function(str, hash, ExceptionClass)\n' - + ' * optional: overrides the default `parseError` function.\n' - + ' * }\n' - + ' *\n' - + ' * lexer.options: {\n' - + ' * pre_lex: function()\n' - + ' * optional: is invoked before the lexer is invoked to produce another token.\n' - + ' * `this` refers to the Lexer object.\n' - + ' * post_lex: function(token) { return token; }\n' - + ' * optional: is invoked when the lexer has produced a token `token`;\n' - + ' * this function can override the returned token value by returning another.\n' - + ' * When it does not return any (truthy) value, the lexer will return\n' - + ' * the original `token`.\n' - + ' * `this` refers to the Lexer object.\n' - + ' *\n' - + ' * WARNING: the next set of options are not meant to be changed. They echo the abilities of\n' - + ' * the lexer as per when it was compiled!\n' - + ' *\n' - + ' * ranges: boolean\n' - + ' * optional: `true` ==> token location info will include a .range[] member.\n' - + ' * flex: boolean\n' - + ' * optional: `true` ==> flex-like lexing behaviour where the rules are tested\n' - + ' * exhaustively to find the longest match.\n' - + ' * backtrack_lexer: boolean\n' - + ' * optional: `true` ==> lexer regexes are tested in order and for invoked;\n' - + ' * the lexer terminates the scan when a token is returned by the action code.\n' - + ' * xregexp: boolean\n' - + ' * optional: `true` ==> lexer rule regexes are "extended regex format" requiring the\n' - + ' * `XRegExp` library. When this %option has not been specified at compile time, all lexer\n' - + ' * rule regexes have been written as standard JavaScript RegExp expressions.\n' - + ' * }\n' - + ' */\n'; - - return out; -} - -function prepareOptions(opt) { - opt = opt || {}; - - // check for illegal identifier - if (!opt.moduleName || !opt.moduleName.match(/^[a-zA-Z_$][a-zA-Z0-9_$\.]*$/)) { - if (opt.moduleName) { - var msg = 'WARNING: The specified moduleName "' + opt.moduleName + '" is illegal (only characters [a-zA-Z0-9_$] and "." dot are accepted); using the default moduleName "lexer" instead.'; - if (typeof opt.warn_cb === 'function') { - opt.warn_cb(msg); - } else { - // do not treat as warning; barf hairball instead so that this oddity gets noticed right away! - throw new Error(msg); - } - } - opt.moduleName = 'lexer'; - } - return opt; -} - -function generateModule(opt) { - opt = prepareOptions(opt); - - var out = [ - generateGenericHeaderComment(), - '', - 'var ' + opt.moduleName + ' = (function () {', - jisonLexerErrorDefinition, - '', - generateModuleBody(opt), - '', - (opt.moduleInclude ? opt.moduleInclude + ';' : ''), - '', - 'return lexer;', - '})();' - ]; - - return out.join('\n'); -} - -function generateAMDModule(opt) { - opt = prepareOptions(opt); - - var out = [ - generateGenericHeaderComment(), - '', - 'define([], function () {', - jisonLexerErrorDefinition, - '', - generateModuleBody(opt), - '', - (opt.moduleInclude ? opt.moduleInclude + ';' : ''), - '', - 'return lexer;', - '});' - ]; - - return out.join('\n'); -} - -function generateESModule(opt) { - opt = prepareOptions(opt); - - var out = [ - generateGenericHeaderComment(), - '', - 'var lexer = (function () {', - jisonLexerErrorDefinition, - '', - generateModuleBody(opt), - '', - (opt.moduleInclude ? opt.moduleInclude + ';' : ''), - '', - 'return lexer;', - '})();', - '', - 'export {lexer};' - ]; - - return out.join('\n'); -} - -function generateCommonJSModule(opt) { - opt = prepareOptions(opt); - - var out = [ - generateGenericHeaderComment(), - '', - 'var ' + opt.moduleName + ' = (function () {', - jisonLexerErrorDefinition, - '', - generateModuleBody(opt), - '', - (opt.moduleInclude ? opt.moduleInclude + ';' : ''), - '', - 'return lexer;', - '})();', - '', - 'if (typeof require !== \'undefined\' && typeof exports !== \'undefined\') {', - ' exports.lexer = ' + opt.moduleName + ';', - ' exports.lex = function () {', - ' return ' + opt.moduleName + '.lex.apply(lexer, arguments);', - ' };', - '}' - ]; - - return out.join('\n'); -} - -RegExpLexer.generate = generate; - -RegExpLexer.defaultJisonLexOptions = defaultJisonLexOptions; -RegExpLexer.mkStdOptions = mkStdOptions; -RegExpLexer.camelCase = camelCase; -RegExpLexer.printFunctionSourceCode = printFunctionSourceCode; -RegExpLexer.printFunctionSourceCodeContainer = printFunctionSourceCodeContainer; -RegExpLexer.autodetectAndConvertToJSONformat = autodetectAndConvertToJSONformat; - -module.exports = RegExpLexer; - - -},{"./lex-parser":5,"./package.json":6,"./regexp-set-management":9,"./safe-code-exec-and-diag":10,"assert":14,"json5":16,"xregexp":22}],9:[function(require,module,exports){ -// -// Helper library for set definitions -// -// MIT Licensed -// -// -// This code is intended to help parse regex set expressions and mix them -// together, i.e. to answer questions like this: -// -// what is the resulting regex set expression when we mix the regex set -// `[a-z]` with the regex set `[^\s]` where with 'mix' we mean that any -// input which matches either input regex should match the resulting -// regex set. (a.k.a. Full Outer Join, see also http://www.diffen.com/difference/Inner_Join_vs_Outer_Join) -// - -'use strict'; - -var XRegExp = require('xregexp'); -var assert = require('assert'); - - - - -const XREGEXP_UNICODE_ESCAPE_RE = /^\{[A-Za-z0-9 \-\._]+\}/; // Matches the XRegExp Unicode escape braced part, e.g. `{Number}` -const CHR_RE = /^(?:[^\\]|\\[^cxu0-9]|\\[0-9]{1,3}|\\c[A-Z]|\\x[0-9a-fA-F]{2}|\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]+\})/; -const SET_PART_RE = /^(?:[^\\\]]|\\[^cxu0-9]|\\[0-9]{1,3}|\\c[A-Z]|\\x[0-9a-fA-F]{2}|\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]+\})+/; -const NOTHING_SPECIAL_RE = /^(?:[^\\\[\]\(\)\|^\{\}]|\\[^cxu0-9]|\\[0-9]{1,3}|\\c[A-Z]|\\x[0-9a-fA-F]{2}|\\u[0-9a-fA-F]{4}|\\u\{[0-9a-fA-F]+\})+/; -const SET_IS_SINGLE_PCODE_RE = /^\\[dDwWsS]$|^\\p\{[A-Za-z0-9 \-\._]+\}$/; - -const UNICODE_BASE_PLANE_MAX_CP = 65535; - -// The expanded regex sets which are equivalent to the given `\\{c}` escapes: -// -// `/\s/`: -const WHITESPACE_SETSTR = ' \f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff'; -// `/\d/`: -const DIGIT_SETSTR = '0-9'; -// `/\w/`: -const WORDCHAR_SETSTR = 'A-Za-z0-9_'; - - - - - -// Helper for `bitarray2set()`: convert character code to a representation string suitable for use in a regex -function i2c(i) { - var c, x; - - switch (i) { - case 10: - return '\\n'; - - case 13: - return '\\r'; - - case 9: - return '\\t'; - - case 8: - return '\\b'; - - case 12: - return '\\f'; - - case 11: - return '\\v'; - - case 45: // ASCII/Unicode for '-' dash - return '\\-'; - - case 91: // '[' - return '\\['; - - case 92: // '\\' - return '\\\\'; - - case 93: // ']' - return '\\]'; - - case 94: // ']' - return '\\^'; - } - if (i < 32 - || i > 0xFFF0 /* Unicode Specials, also in UTF16 */ - || (i >= 0xD800 && i <= 0xDFFF) /* Unicode Supplementary Planes; we're TOAST in JavaScript as we're NOT UTF-16 but UCS-2! */ - || String.fromCharCode(i).match(/[\u2028\u2029]/) /* Code compilation via `new Function()` does not like to see these, or rather: treats them as just another form of CRLF, which breaks your generated regex code! */ - ) { - // Detail about a detail: - // U+2028 and U+2029 are part of the `\s` regex escape code (`\s` and `[\s]` match either of these) and when placed in a JavaScript - // source file verbatim (without escaping it as a `\uNNNN` item) then JavaScript will interpret it as such and consequently report - // a b0rked generated parser, as the generated code would include this regex right here. - // Hence we MUST escape these buggers everywhere we go... - x = i.toString(16); - if (x.length >= 1 && i <= 0xFFFF) { - c = '0000' + x; - return '\\u' + c.substr(c.length - 4); - } else { - return '\\u{' + x + '}'; - } - } - return String.fromCharCode(i); -} - - -// Helper collection for `bitarray2set()`: we have expanded all these cached `\\p{NAME}` regex sets when creating -// this bitarray and now we should look at these expansions again to see if `bitarray2set()` can produce a -// `\\p{NAME}` shorthand to represent [part of] the bitarray: -var Pcodes_bitarray_cache = {}; -var Pcodes_bitarray_cache_test_order = []; - -// Helper collection for `bitarray2set()` for minifying special cases of result sets which can be represented by -// a single regex 'escape', e.g. `\d` for digits 0-9. -var EscCode_bitarray_output_refs; - -// now initialize the EscCodes_... table above: -init_EscCode_lookup_table(); - -function init_EscCode_lookup_table() { - var s, bitarr, set2esc = {}, esc2bitarr = {}; - - // patch global lookup tables for the time being, while we calculate their *real* content in this function: - EscCode_bitarray_output_refs = { - esc2bitarr: {}, - set2esc: {} - }; - Pcodes_bitarray_cache_test_order = []; - - // `/\S': - bitarr = []; - set2bitarray(bitarr, '^' + WHITESPACE_SETSTR); - s = bitarray2set(bitarr); - esc2bitarr['S'] = bitarr; - set2esc[s] = 'S'; - // set2esc['^' + s] = 's'; - Pcodes_bitarray_cache['\\S'] = bitarr; - - // `/\s': - bitarr = []; - set2bitarray(bitarr, WHITESPACE_SETSTR); - s = bitarray2set(bitarr); - esc2bitarr['s'] = bitarr; - set2esc[s] = 's'; - // set2esc['^' + s] = 'S'; - Pcodes_bitarray_cache['\\s'] = bitarr; - - // `/\D': - bitarr = []; - set2bitarray(bitarr, '^' + DIGIT_SETSTR); - s = bitarray2set(bitarr); - esc2bitarr['D'] = bitarr; - set2esc[s] = 'D'; - // set2esc['^' + s] = 'd'; - Pcodes_bitarray_cache['\\D'] = bitarr; - - // `/\d': - bitarr = []; - set2bitarray(bitarr, DIGIT_SETSTR); - s = bitarray2set(bitarr); - esc2bitarr['d'] = bitarr; - set2esc[s] = 'd'; - // set2esc['^' + s] = 'D'; - Pcodes_bitarray_cache['\\d'] = bitarr; - - // `/\W': - bitarr = []; - set2bitarray(bitarr, '^' + WORDCHAR_SETSTR); - s = bitarray2set(bitarr); - esc2bitarr['W'] = bitarr; - set2esc[s] = 'W'; - // set2esc['^' + s] = 'w'; - Pcodes_bitarray_cache['\\W'] = bitarr; - - // `/\w': - bitarr = []; - set2bitarray(bitarr, WORDCHAR_SETSTR); - s = bitarray2set(bitarr); - esc2bitarr['w'] = bitarr; - set2esc[s] = 'w'; - // set2esc['^' + s] = 'W'; - Pcodes_bitarray_cache['\\w'] = bitarr; - - EscCode_bitarray_output_refs = { - esc2bitarr: esc2bitarr, - set2esc: set2esc - }; - - updatePcodesBitarrayCacheTestOrder(); -} - -function updatePcodesBitarrayCacheTestOrder(opts) { - var t = new Array(UNICODE_BASE_PLANE_MAX_CP + 1); - var l = {}; - var user_has_xregexp = opts && opts.options && opts.options.xregexp; - var i, j, k, ba; - - // mark every character with which regex pcodes they are part of: - for (k in Pcodes_bitarray_cache) { - ba = Pcodes_bitarray_cache[k]; - - if (!user_has_xregexp && k.indexOf('\\p{') >= 0) { - continue; - } - - var cnt = 0; - for (i = 0; i <= UNICODE_BASE_PLANE_MAX_CP; i++) { - if (ba[i]) { - cnt++; - if (!t[i]) { - t[i] = [k]; - } else { - t[i].push(k); - } - } - } - l[k] = cnt; - } - - // now dig out the unique ones: only need one per pcode. - // - // We ASSUME every \\p{NAME} 'pcode' has at least ONE character - // in it that is ONLY matched by that particular pcode. - // If this assumption fails, nothing is lost, but our 'regex set - // optimized representation' will be sub-optimal as than this pcode - // won't be tested during optimization. - // - // Now that would be a pity, so the assumption better holds... - // Turns out the assumption doesn't hold already for /\S/ + /\D/ - // as the second one (\D) is a pure subset of \S. So we have to - // look for markers which match multiple escapes/pcodes for those - // ones where a unique item isn't available... - var lut = []; - var done = {}; - var keys = Object.keys(Pcodes_bitarray_cache); - - for (i = 0; i <= UNICODE_BASE_PLANE_MAX_CP; i++) { - k = t[i][0]; - if (t[i].length === 1 && !done[k]) { - assert(l[k] > 0); - lut.push([i, k]); - done[k] = true; - } - } - - for (j = 0; keys[j]; j++) { - k = keys[j]; - - if (!user_has_xregexp && k.indexOf('\\p{') >= 0) { - continue; - } - - if (!done[k]) { - assert(l[k] > 0); - // find a minimum span character to mark this one: - var w = Infinity; - var rv; - ba = Pcodes_bitarray_cache[k]; - for (i = 0; i <= UNICODE_BASE_PLANE_MAX_CP; i++) { - if (ba[i]) { - var tl = t[i].length; - if (tl > 1 && tl < w) { - assert(l[k] > 0); - rv = [i, k]; - w = tl; - } - } - } - if (rv) { - done[k] = true; - lut.push(rv); - } - } - } - - // order from large set to small set so that small sets don't gobble - // characters also represented by overlapping larger set pcodes. - // - // Again we assume something: that finding the large regex pcode sets - // before the smaller, more specialized ones, will produce a more - // optimal minification of the regex set expression. - // - // This is a guestimate/heuristic only! - lut.sort(function (a, b) { - var k1 = a[1]; - var k2 = b[1]; - var ld = l[k2] - l[k1]; - if (ld) { - return ld; - } - // and for same-size sets, order from high to low unique identifier. - return b[0] - a[0]; - }); - - Pcodes_bitarray_cache_test_order = lut; -} - - - - - - -// 'Join' a regex set `[...]` into a Unicode range spanning logic array, flagging every character in the given set. -function set2bitarray(bitarr, s, opts) { - var orig = s; - var set_is_inverted = false; - var bitarr_orig; - - function mark(d1, d2) { - if (d2 == null) d2 = d1; - for (var i = d1; i <= d2; i++) { - bitarr[i] = true; - } - } - - function add2bitarray(dst, src) { - for (var i = 0; i <= UNICODE_BASE_PLANE_MAX_CP; i++) { - if (src[i]) { - dst[i] = true; - } - } - } - - function eval_escaped_code(s) { - var c; - // decode escaped code? If none, just take the character as-is - if (s.indexOf('\\') === 0) { - var l = s.substr(0, 2); - switch (l) { - case '\\c': - c = s.charCodeAt(2) - 'A'.charCodeAt(0) + 1; - return String.fromCharCode(c); - - case '\\x': - s = s.substr(2); - c = parseInt(s, 16); - return String.fromCharCode(c); - - case '\\u': - s = s.substr(2); - if (s[0] === '{') { - s = s.substr(1, s.length - 2); - } - c = parseInt(s, 16); - if (c >= 0x10000) { - return new Error('We do NOT support Extended Plane Unicode Codepoints (i.e. CodePoints beyond U:FFFF) in regex set expressions, e.g. \\u{' + s + '}'); - } - return String.fromCharCode(c); - - case '\\0': - case '\\1': - case '\\2': - case '\\3': - case '\\4': - case '\\5': - case '\\6': - case '\\7': - s = s.substr(1); - c = parseInt(s, 8); - return String.fromCharCode(c); - - case '\\r': - return '\r'; - - case '\\n': - return '\n'; - - case '\\v': - return '\v'; - - case '\\f': - return '\f'; - - case '\\t': - return '\t'; - - case '\\b': - return '\b'; - - default: - // just the character itself: - return s.substr(1); - } - } else { - return s; - } - } - - if (s && s.length) { - var c1, c2; - - // inverted set? - if (s[0] === '^') { - set_is_inverted = true; - s = s.substr(1); - bitarr_orig = bitarr; - bitarr = new Array(UNICODE_BASE_PLANE_MAX_CP + 1); - } - - // BITARR collects flags for characters set. Inversion means the complement set of character is st instead. - // This results in an OR operations when sets are joined/chained. - - while (s.length) { - c1 = s.match(CHR_RE); - if (!c1) { - // hit an illegal escape sequence? cope anyway! - c1 = s[0]; - } else { - c1 = c1[0]; - // Quick hack for XRegExp escapes inside a regex `[...]` set definition: we *could* try to keep those - // intact but it's easier to unfold them here; this is not nice for when the grammar specifies explicit - // XRegExp support, but alas, we'll get there when we get there... ;-) - switch (c1) { - case '\\p': - s = s.substr(c1.length); - c2 = s.match(XREGEXP_UNICODE_ESCAPE_RE); - if (c2) { - c2 = c2[0]; - s = s.substr(c2.length); - // do we have this one cached already? - var pex = c1 + c2; - var ba4p = Pcodes_bitarray_cache[pex]; - if (!ba4p) { - // expand escape: - var xr = new XRegExp('[' + pex + ']'); // TODO: case-insensitive grammar??? - // rewrite to a standard `[...]` regex set: XRegExp will do this for us via `XRegExp.toString()`: - var xs = '' + xr; - // remove the wrapping `/.../` to get at the (possibly *combined* series of) `[...]` sets inside: - xs = xs.substr(1, xs.length - 2); - - ba4p = reduceRegexToSetBitArray(xs, pex, opts); - - Pcodes_bitarray_cache[pex] = ba4p; - updatePcodesBitarrayCacheTestOrder(opts); - } - // merge bitarrays: - add2bitarray(bitarr, ba4p); - continue; - } - break; - - case '\\S': - case '\\s': - case '\\W': - case '\\w': - case '\\d': - case '\\D': - // these can't participate in a range, but need to be treated special: - s = s.substr(c1.length); - // check for \S, \s, \D, \d, \W, \w and expand them: - var ba4e = EscCode_bitarray_output_refs.esc2bitarr[c1[1]]; - assert(ba4e); - add2bitarray(bitarr, ba4e); - continue; - - case '\\b': - // matches a backspace: https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions#special-backspace - c1 = '\u0008'; - break; - } - } - var v1 = eval_escaped_code(c1); - // propagate deferred exceptions = error reports. - if (v1 instanceof Error) { - return v1; - } - v1 = v1.charCodeAt(0); - s = s.substr(c1.length); - - if (s[0] === '-' && s.length >= 2) { - // we can expect a range like 'a-z': - s = s.substr(1); - c2 = s.match(CHR_RE); - if (!c2) { - // hit an illegal escape sequence? cope anyway! - c2 = s[0]; - } else { - c2 = c2[0]; - } - var v2 = eval_escaped_code(c2); - // propagate deferred exceptions = error reports. - if (v2 instanceof Error) { - return v1; - } - v2 = v2.charCodeAt(0); - s = s.substr(c2.length); - - // legal ranges go UP, not /DOWN! - if (v1 <= v2) { - mark(v1, v2); - } else { - console.warn('INVALID CHARACTER RANGE found in regex: ', { re: orig, start: c1, start_n: v1, end: c2, end_n: v2 }); - mark(v1); - mark('-'.charCodeAt(0)); - mark(v2); - } - continue; - } - mark(v1); - } - - // When we have marked all slots, '^' NEGATES the set, hence we flip all slots. - // - // Since a regex like `[^]` should match everything(?really?), we don't need to check if the MARK - // phase actually marked anything at all: the `^` negation will correctly flip=mark the entire - // range then. - if (set_is_inverted) { - for (var i = 0; i <= UNICODE_BASE_PLANE_MAX_CP; i++) { - if (!bitarr[i]) { - bitarr_orig[i] = true; - } - } - } - } - return false; -} - - -// convert a simple bitarray back into a regex set `[...]` content: -function bitarray2set(l, output_inverted_variant, output_minimized) { - // construct the inverse(?) set from the mark-set: - // - // Before we do that, we inject a sentinel so that our inner loops - // below can be simple and fast: - l[UNICODE_BASE_PLANE_MAX_CP + 1] = 1; - // now reconstruct the regex set: - var rv = []; - var i, j, cnt, lut, tn, tspec, match, pcode, ba4pcode, l2; - var bitarr_is_cloned = false; - var l_orig = l; - - if (output_inverted_variant) { - // generate the inverted set, hence all unmarked slots are part of the output range: - cnt = 0; - for (i = 0; i <= UNICODE_BASE_PLANE_MAX_CP; i++) { - if (!l[i]) { - cnt++; - } - } - if (cnt === UNICODE_BASE_PLANE_MAX_CP + 1) { - // When there's nothing in the output we output a special 'match-nothing' regex: `[^\S\s]`. - // BUT... since we output the INVERTED set, we output the match-all set instead: - return '\\S\\s'; - } - else if (cnt === 0) { - // When we find the entire Unicode range is in the output match set, we replace this with - // a shorthand regex: `[\S\s]` - // BUT... since we output the INVERTED set, we output the match-nothing set instead: - return '^\\S\\s'; - } - - // Now see if we can replace several bits by an escape / pcode: - if (output_minimized) { - lut = Pcodes_bitarray_cache_test_order; - for (tn = 0; lut[tn]; tn++) { - tspec = lut[tn]; - // check if the uniquely identifying char is in the inverted set: - if (!l[tspec[0]]) { - // check if the pcode is covered by the inverted set: - pcode = tspec[1]; - ba4pcode = Pcodes_bitarray_cache[pcode]; - match = 0; - for (j = 0; j <= UNICODE_BASE_PLANE_MAX_CP; j++) { - if (ba4pcode[j]) { - if (!l[j]) { - // match in current inverted bitset, i.e. there's at - // least one 'new' bit covered by this pcode/escape: - match++; - } else if (l_orig[j]) { - // mismatch! - match = false; - break; - } - } - } - - // We're only interested in matches which actually cover some - // yet uncovered bits: `match !== 0 && match !== false`. - // - // Apply the heuristic that the pcode/escape is only going to be used - // when it covers *more* characters than its own identifier's length: - if (match && match > pcode.length) { - rv.push(pcode); - - // and nuke the bits in the array which match the given pcode: - // make sure these edits are visible outside this function as - // `l` is an INPUT parameter (~ not modified)! - if (!bitarr_is_cloned) { - l2 = new Array(UNICODE_BASE_PLANE_MAX_CP + 1); - for (j = 0; j <= UNICODE_BASE_PLANE_MAX_CP; j++) { - l2[j] = l[j] || ba4pcode[j]; // `!(!l[j] && !ba4pcode[j])` - } - // recreate sentinel - l2[UNICODE_BASE_PLANE_MAX_CP + 1] = 1; - l = l2; - bitarr_is_cloned = true; - } else { - for (j = 0; j <= UNICODE_BASE_PLANE_MAX_CP; j++) { - l[j] = l[j] || ba4pcode[j]; - } - } - } - } - } - } - - i = 0; - while (i <= UNICODE_BASE_PLANE_MAX_CP) { - // find first character not in original set: - while (l[i]) { - i++; - } - if (i >= UNICODE_BASE_PLANE_MAX_CP + 1) { - break; - } - // find next character not in original set: - for (j = i + 1; !l[j]; j++) {} /* empty loop */ - // generate subset: - rv.push(i2c(i)); - if (j - 1 > i) { - rv.push((j - 2 > i ? '-' : '') + i2c(j - 1)); - } - i = j; - } - } else { - // generate the non-inverted set, hence all logic checks are inverted here... - cnt = 0; - for (i = 0; i <= UNICODE_BASE_PLANE_MAX_CP; i++) { - if (l[i]) { - cnt++; - } - } - if (cnt === UNICODE_BASE_PLANE_MAX_CP + 1) { - // When we find the entire Unicode range is in the output match set, we replace this with - // a shorthand regex: `[\S\s]` - return '\\S\\s'; - } - else if (cnt === 0) { - // When there's nothing in the output we output a special 'match-nothing' regex: `[^\S\s]`. - return '^\\S\\s'; - } - - // Now see if we can replace several bits by an escape / pcode: - if (output_minimized) { - lut = Pcodes_bitarray_cache_test_order; - for (tn = 0; lut[tn]; tn++) { - tspec = lut[tn]; - // check if the uniquely identifying char is in the set: - if (l[tspec[0]]) { - // check if the pcode is covered by the set: - pcode = tspec[1]; - ba4pcode = Pcodes_bitarray_cache[pcode]; - match = 0; - for (j = 0; j <= UNICODE_BASE_PLANE_MAX_CP; j++) { - if (ba4pcode[j]) { - if (l[j]) { - // match in current bitset, i.e. there's at - // least one 'new' bit covered by this pcode/escape: - match++; - } else if (!l_orig[j]) { - // mismatch! - match = false; - break; - } - } - } - - // We're only interested in matches which actually cover some - // yet uncovered bits: `match !== 0 && match !== false`. - // - // Apply the heuristic that the pcode/escape is only going to be used - // when it covers *more* characters than its own identifier's length: - if (match && match > pcode.length) { - rv.push(pcode); - - // and nuke the bits in the array which match the given pcode: - // make sure these edits are visible outside this function as - // `l` is an INPUT parameter (~ not modified)! - if (!bitarr_is_cloned) { - l2 = new Array(UNICODE_BASE_PLANE_MAX_CP + 1); - for (j = 0; j <= UNICODE_BASE_PLANE_MAX_CP; j++) { - l2[j] = l[j] && !ba4pcode[j]; - } - // recreate sentinel - l2[UNICODE_BASE_PLANE_MAX_CP + 1] = 1; - l = l2; - bitarr_is_cloned = true; - } else { - for (j = 0; j <= UNICODE_BASE_PLANE_MAX_CP; j++) { - l[j] = l[j] && !ba4pcode[j]; - } - } - } - } - } - } - - i = 0; - while (i <= UNICODE_BASE_PLANE_MAX_CP) { - // find first character not in original set: - while (!l[i]) { - i++; - } - if (i >= UNICODE_BASE_PLANE_MAX_CP + 1) { - break; - } - // find next character not in original set: - for (j = i + 1; l[j]; j++) {} /* empty loop */ - if (j > UNICODE_BASE_PLANE_MAX_CP + 1) { - j = UNICODE_BASE_PLANE_MAX_CP + 1; - } - // generate subset: - rv.push(i2c(i)); - if (j - 1 > i) { - rv.push((j - 2 > i ? '-' : '') + i2c(j - 1)); - } - i = j; - } - } - - assert(rv.length); - var s = rv.join(''); - assert(s); - - // Check if the set is better represented by one of the regex escapes: - var esc4s = EscCode_bitarray_output_refs.set2esc[s]; - if (esc4s) { - // When we hit a special case like this, it is always the shortest notation, hence wins on the spot! - return '\\' + esc4s; - } - return s; -} - - - - - -// Pretty brutal conversion of 'regex' `s` back to raw regex set content: strip outer [...] when they're there; -// ditto for inner combos of sets, i.e. `]|[` as in `[0-9]|[a-z]`. -function reduceRegexToSetBitArray(s, name, opts) { - var orig = s; - - // propagate deferred exceptions = error reports. - if (s instanceof Error) { - return s; - } - - var l = new Array(UNICODE_BASE_PLANE_MAX_CP + 1); - var internal_state = 0; - var derr; - - while (s.length) { - var c1 = s.match(CHR_RE); - if (!c1) { - // cope with illegal escape sequences too! - return new Error('illegal escape sequence at start of regex part: "' + s + '" of regex "' + orig + '"'); - } else { - c1 = c1[0]; - } - s = s.substr(c1.length); - - switch (c1) { - case '[': - // this is starting a set within the regex: scan until end of set! - var set_content = []; - while (s.length) { - var inner = s.match(SET_PART_RE); - if (!inner) { - inner = s.match(CHR_RE); - if (!inner) { - // cope with illegal escape sequences too! - return new Error('illegal escape sequence at start of regex part: ' + s + '" of regex "' + orig + '"'); - } else { - inner = inner[0]; - } - if (inner === ']') break; - } else { - inner = inner[0]; - } - set_content.push(inner); - s = s.substr(inner.length); - } - - // ensure that we hit the terminating ']': - var c2 = s.match(CHR_RE); - if (!c2) { - // cope with illegal escape sequences too! - return new Error('regex set expression is broken in regex: "' + orig + '" --> "' + s + '"'); - } else { - c2 = c2[0]; - } - if (c2 !== ']') { - return new Error('regex set expression is broken in regex: ' + orig); - } - s = s.substr(c2.length); - - var se = set_content.join(''); - if (!internal_state) { - derr = set2bitarray(l, se, opts); - // propagate deferred exceptions = error reports. - if (derr instanceof Error) { - return derr; - } - - // a set is to use like a single character in a longer literal phrase, hence input `[abc]word[def]` would thus produce output `[abc]`: - internal_state = 1; - } - break; - - // Strip unescaped pipes to catch constructs like `\\r|\\n` and turn them into - // something ready for use inside a regex set, e.g. `\\r\\n`. - // - // > Of course, we realize that converting more complex piped constructs this way - // > will produce something you might not expect, e.g. `A|WORD2` which - // > would end up as the set `[AW]` which is something else than the input - // > entirely. - // > - // > However, we can only depend on the user (grammar writer) to realize this and - // > prevent this from happening by not creating such oddities in the input grammar. - case '|': - // a|b --> [ab] - internal_state = 0; - break; - - case '(': - // (a) --> a - // - // TODO - right now we treat this as 'too complex': - - // Strip off some possible outer wrappers which we know how to remove. - // We don't worry about 'damaging' the regex as any too-complex regex will be caught - // in the validation check at the end; our 'strippers' here would not damage useful - // regexes anyway and them damaging the unacceptable ones is fine. - s = s.replace(/^\((?:\?:)?(.*?)\)$/, '$1'); // (?:...) -> ... and (...) -> ... - s = s.replace(/^\^?(.*?)\$?$/, '$1'); // ^...$ --> ... (catch these both inside and outside the outer grouping, hence do the ungrouping twice: one before, once after this) - s = s.replace(/^\((?:\?:)?(.*?)\)$/, '$1'); // (?:...) -> ... and (...) -> ... - - return new Error('[macro [' + name + '] is unsuitable for use inside regex set expressions: "[' + orig + ']"]'); - - case '.': - case '*': - case '+': - case '?': - // wildcard - // - // TODO - right now we treat this as 'too complex': - return new Error('[macro [' + name + '] is unsuitable for use inside regex set expressions: "[' + orig + ']"]'); - - case '{': // range, e.g. `x{1,3}`, or macro? - // TODO - right now we treat this as 'too complex': - return new Error('[macro [' + name + '] is unsuitable for use inside regex set expressions: "[' + orig + ']"]'); - - default: - // literal character or word: take the first character only and ignore the rest, so that - // the constructed set for `word|noun` would be `[wb]`: - if (!internal_state) { - derr = set2bitarray(l, c1, opts); - // propagate deferred exceptions = error reports. - if (derr instanceof Error) { - return derr; - } - - internal_state = 2; - } - break; - } - } - - s = bitarray2set(l); - - // When this result is suitable for use in a set, than we should be able to compile - // it in a regex; that way we can easily validate whether macro X is fit to be used - // inside a regex set: - try { - var re; - assert(s); - assert(!(s instanceof Error)); - re = new XRegExp('[' + s + ']'); - re.test(s[0]); - - // One thing is apparently *not* caught by the RegExp compile action above: `[a[b]c]` - // so we check for lingering UNESCAPED brackets in here as those cannot be: - if (/[^\\][\[\]]/.exec(s)) { - throw new Error('unescaped brackets in set data'); - } - } catch (ex) { - // make sure we produce a set range expression which will fail badly when it is used - // in actual code: - s = new Error('[macro [' + name + '] is unsuitable for use inside regex set expressions: "[' + s + ']"]: ' + ex.message); - } - - assert(s); - // propagate deferred exceptions = error reports. - if (s instanceof Error) { - return s; - } - return l; -} - - - - -// Convert bitarray representing, for example, `'0-9'` to regex string `[0-9]` -// -- or in this example it can be further optimized to only `\d`! -function produceOptimizedRegex4Set(bitarr) { - // First try to produce a minimum regex from the bitarray directly: - var s1 = bitarray2set(bitarr, false, true); - - // and when the regex set turns out to match a single pcode/escape, then - // use that one as-is: - if (s1.match(SET_IS_SINGLE_PCODE_RE)) { - // When we hit a special case like this, it is always the shortest notation, hence wins on the spot! - return s1; - } else { - s1 = '[' + s1 + ']'; - } - - // Now try to produce a minimum regex from the *inverted* bitarray via negation: - // Because we look at a negated bitset, there's no use looking for matches with - // special cases here. - var s2 = bitarray2set(bitarr, true, true); - - if (s2[0] === '^') { - s2 = s2.substr(1); - if (s2.match(SET_IS_SINGLE_PCODE_RE)) { - // When we hit a special case like this, it is always the shortest notation, hence wins on the spot! - return s2; - } - } else { - s2 = '^' + s2; - } - s2 = '[' + s2 + ']'; - - // Then, as some pcode/escapes still happen to deliver a LARGER regex string in the end, - // we also check against the plain, unadulterated regex set expressions: - // - // First try to produce a minimum regex from the bitarray directly: - var s3 = bitarray2set(bitarr, false, false); - - // and when the regex set turns out to match a single pcode/escape, then - // use that one as-is: - if (s3.match(SET_IS_SINGLE_PCODE_RE)) { - // When we hit a special case like this, it is always the shortest notation, hence wins on the spot! - return s3; - } else { - s3 = '[' + s3 + ']'; - } - - // Now try to produce a minimum regex from the *inverted* bitarray via negation: - // Because we look at a negated bitset, there's no use looking for matches with - // special cases here. - var s4 = bitarray2set(bitarr, true, false); - - if (s4[0] === '^') { - s4 = s4.substr(1); - if (s4.match(SET_IS_SINGLE_PCODE_RE)) { - // When we hit a special case like this, it is always the shortest notation, hence wins on the spot! - return s4; - } - } else { - s4 = '^' + s4; - } - s4 = '[' + s4 + ']'; - - if (s2.length < s1.length) { - s1 = s2; - } - if (s3.length < s1.length) { - s1 = s3; - } - if (s4.length < s1.length) { - s1 = s4; - } - - return s1; -} - - - - - - -module.exports = { - XREGEXP_UNICODE_ESCAPE_RE: XREGEXP_UNICODE_ESCAPE_RE, - CHR_RE: CHR_RE, - SET_PART_RE: SET_PART_RE, - NOTHING_SPECIAL_RE: NOTHING_SPECIAL_RE, - SET_IS_SINGLE_PCODE_RE: SET_IS_SINGLE_PCODE_RE, - - UNICODE_BASE_PLANE_MAX_CP: UNICODE_BASE_PLANE_MAX_CP, - - WHITESPACE_SETSTR: WHITESPACE_SETSTR, - DIGIT_SETSTR: DIGIT_SETSTR, - WORDCHAR_SETSTR: WORDCHAR_SETSTR, - - set2bitarray: set2bitarray, - bitarray2set: bitarray2set, - produceOptimizedRegex4Set: produceOptimizedRegex4Set, - reduceRegexToSetBitArray: reduceRegexToSetBitArray, -}; - - -},{"assert":14,"xregexp":22}],10:[function(require,module,exports){ -(function (process){ -// -// Helper library for safe code execution/compilation, including dumping offending code to file for further error analysis -// (the idea was originally coded in https://github.com/GerHobbelt/jison/commit/85e367d03b977780516d2b643afbe6f65ee758f2 ) -// -// MIT Licensed -// -// -// This code is intended to help test and diagnose arbitrary chunks of code, answering questions like this: -// -// the given code fails, but where exactly and why? It's precise failure conditions are 'hidden' due to -// the stuff running inside an `eval()` or `Function(...)` call, so we want the code dumped to file so that -// we can test the code in a different environment so that we can see what precisely is causing the failure. -// - -'use strict'; - -var fs = require('fs'); -var path = require('path'); - -var assert = require('assert'); - - - - -// Helper function: pad number with leading zeroes -function pad(n, p) { - p = p || 2; - var rv = '0000' + n; - return rv.slice(-p); -} - - -// attempt to dump in one of several locations: first winner is *it*! -function dumpSourceToFile(sourcecode, errname, err_id, options, ex) { - var dumpfile; - - try { - var dumpPaths = [(options.outfile ? path.dirname(options.outfile) : null), options.inputPath, process.cwd()]; - var dumpName = (options.inputFilename || options.moduleName || options.defaultModuleName || errname).replace(/[^a-z0-9_]/ig, "_"); - - var ts = new Date(); - var tm = ts.getUTCFullYear() + - '_' + pad(ts.getUTCMonth() + 1) + - '_' + pad(ts.getUTCDate()) + - 'T' + pad(ts.getUTCHours()) + - '' + pad(ts.getUTCMinutes()) + - '' + pad(ts.getUTCSeconds()) + - '.' + pad(ts.getUTCMilliseconds(), 3) + - 'Z'; - - dumpName += '.fatal_' + err_id + '_dump_' + tm + '.js'; - - for (var i = 0, l = dumpPaths.length; i < l; i++) { - if (!dumpPaths[i]) { - continue; - } - - try { - dumpfile = path.normalize(dumpPaths[i] + '/' + dumpName); - fs.writeFileSync(dumpfile, sourcecode, 'utf8'); - console.error("****** offending generated " + errname + " source code dumped into file: ", dumpfile); - break; // abort loop once a dump action was successful! - } catch (ex3) { - //console.error("generated " + errname + " source code fatal DUMPING error ATTEMPT: ", i, " = ", ex3.message, " -- while attempting to dump into file: ", dumpfile, "\n", ex3.stack); - if (i === l - 1) { - throw ex3; - } - } - } - } catch (ex2) { - console.error("generated " + errname + " source code fatal DUMPING error: ", ex2.message, " -- while attempting to dump into file: ", dumpfile, "\n", ex2.stack); - } - - // augment the exception info, when available: - if (ex) { - ex.offending_source_code = sourcecode; - ex.offending_source_title = errname; - ex.offending_source_dumpfile = dumpfile; - } -} - - - - -// -// `code_execution_rig` is a function which gets executed, while it is fed the `sourcecode` as a parameter. -// When the `code_execution_rig` crashes, its failure is caught and (using the `options`) the sourcecode -// is dumped to file for later diagnosis. -// -// Two options drive the internal behaviour: -// -// - options.dumpSourceCodeOnFailure -- default: FALSE -// - options.throwErrorOnCompileFailure -- default: FALSE -// -// Dumpfile naming and path are determined through these options: -// -// - options.outfile -// - options.inputPath -// - options.inputFilename -// - options.moduleName -// - options.defaultModuleName -// -function exec_and_diagnose_this_stuff(sourcecode, code_execution_rig, options, title) { - options = options || {}; - var errname = "" + title; - var err_id = errname.replace(/[^a-z0-9_]/ig, "_"); - if (err_id.length === 0) { - err_id = "exec_crash"; - } - const debug = false; - - if (debug) console.warn('generated ' + errname + ' code under EXEC TEST:\n', sourcecode); - - var p; - try { - // p = eval(sourcecode); - if (typeof code_execution_rig !== 'function') { - throw new Error("safe-code-exec-and-diag: code_execution_rig MUST be a JavaScript function"); - } - p = code_execution_rig.call(this, sourcecode, options, errname, debug); - } catch (ex) { - console.error("generated " + errname + " source code fatal error: ", ex.message); - - if (options.dumpSourceCodeOnFailure) { - dumpSourceToFile(sourcecode, errname, err_id, options, ex); - } - - if (options.throwErrorOnCompileFailure) { - throw ex; - } - } - return p; -} - - - - - - - - -module.exports = exec_and_diagnose_this_stuff; - - -}).call(this,require('_process')) -},{"_process":18,"assert":14,"fs":15,"path":17}],11:[function(require,module,exports){ -// Set class to wrap arrays - -var typal = require('./typal').typal; -var assert = require('assert'); - -var setMixin = { - constructor: function Set_constructor(set, raw) { - this._items = []; - if (set && set.constructor === Array) { - this._items = raw ? set: set.slice(0); - } - else if (arguments.length) { - this._items = [].slice.call(arguments, 0); - } - }, - concat: function concat(setB) { - this._items.push.apply(this._items, setB._items || setB); - return this; - }, - eq: function eq(set) { - return this._items.length === set._items.length && this.subset(set) && this.superset(set); - }, - indexOf: function indexOf(item) { - if (item && item.eq) { - for (var k = 0; k < this._items.length; k++) { - if (item.eq(this._items[k])) { - return k; - } - } - return -1; - } - return this._items.indexOf(item); - }, - intersection: function intersection(set) { - return this.filter(function intersection_filter(elm) { - return set.contains(elm); - }); - }, - complement: function complement(set) { - var that = this; - return set.filter(function sub_complement(elm) { - return !that.contains(elm); - }); - }, - subset: function subset(set) { - var cont = true; - for (var i = 0; i < this._items.length && cont; i++) { - cont = cont && set.contains(this._items[i]); - } - return cont; - }, - superset: function superset(set) { - return set.subset(this); - }, - joinSet: function joinSet(set) { - return this.concat(this.complement(set)); - }, - contains: function contains(item) { - return this.indexOf(item) !== -1; - }, - item: function item(v) { - return this._items[v]; - }, - i: function i(v) { - return this._items[v]; - }, - assign: function assign(index, value) { - this._items[index] = value; - return this; - }, - first: function first() { - return this._items[0]; - }, - last: function last() { - return this._items[this._items.length - 1]; - }, - size: function size() { - return this._items.length; - }, - isEmpty: function isEmpty() { - return this._items.length === 0; - }, - copy: function copy() { - return new Set(this._items); - }, - toString: function toString() { - return this._items.toString(); - } -}; - -'push shift unshift forEach some every join sort'.split(' ').forEach(function (e, i) { - setMixin[e] = function () { - return Array.prototype[e].apply(this._items, arguments); - }; - setMixin[e].name = e; -}); -'filter slice map'.split(' ').forEach(function (e, i) { - setMixin[e] = function () { - return new Set(Array.prototype[e].apply(this._items, arguments), true); - }; - setMixin[e].name = e; -}); - -var Set = typal.construct(setMixin); - -if (typeof exports !== 'undefined') { - exports.Set = Set; -} - - -},{"./typal":13,"assert":14}],12:[function(require,module,exports){ -/* parser generated by jison 0.4.18-184 */ - -/* - * Returns a Parser object of the following structure: - * - * Parser: { - * yy: {} The so-called "shared state" or rather the *source* of it; - * the real "shared state" `yy` passed around to - * the rule actions, etc. is a derivative/copy of this one, - * not a direct reference! - * } - * - * Parser.prototype: { - * yy: {}, - * EOF: 1, - * TERROR: 2, - * - * trace: function(errorMessage, ...), - * - * JisonParserError: function(msg, hash), - * - * quoteName: function(name), - * Helper function which can be overridden by user code later on: put suitable - * quotes around literal IDs in a description string. - * - * originalQuoteName: function(name), - * The basic quoteName handler provided by JISON. - * `cleanupAfterParse()` will clean up and reset `quoteName()` to reference this function - * at the end of the `parse()`. - * - * describeSymbol: function(symbol), - * Return a more-or-less human-readable description of the given symbol, when - * available, or the symbol itself, serving as its own 'description' for lack - * of something better to serve up. - * - * Return NULL when the symbol is unknown to the parser. - * - * symbols_: {associative list: name ==> number}, - * terminals_: {associative list: number ==> name}, - * nonterminals: {associative list: rule-name ==> {associative list: number ==> rule-alt}}, - * terminal_descriptions_: (if there are any) {associative list: number ==> description}, - * productions_: [...], - * - * performAction: function parser__performAction(yytext, yyleng, yylineno, yyloc, yystate, yysp, yyvstack, yylstack, yystack, yysstack, ...), - * where `...` denotes the (optional) additional arguments the user passed to - * `parser.parse(str, ...)` and specified by way of `%parse-param ...` in the grammar file - * - * The function parameters and `this` have the following value/meaning: - * - `this` : reference to the `yyval` internal object, which has members (`$` and `_$`) - * to store/reference the rule value `$$` and location info `@$`. - * - * One important thing to note about `this` a.k.a. `yyval`: every *reduce* action gets - * to see the same object via the `this` reference, i.e. if you wish to carry custom - * data from one reduce action through to the next within a single parse run, then you - * may get nasty and use `yyval` a.k.a. `this` for storing you own semi-permanent data. - * - * - `yytext` : reference to the lexer value which belongs to the last lexer token used - * to match this rule. This is *not* the look-ahead token, but the last token - * that's actually part of this rule. - * - * Formulated another way, `yytext` is the value of the token immediately preceeding - * the current look-ahead token. - * Caveats apply for rules which don't require look-ahead, such as epsilon rules. - * - * - `yyleng` : ditto as `yytext`, only now for the lexer.yyleng value. - * - * - `yylineno`: ditto as `yytext`, only now for the lexer.yylineno value. - * - * - `yyloc` : ditto as `yytext`, only now for the lexer.yylloc lexer token location info. - * - * - `yystate` : the current parser state number, used internally for dispatching and - * executing the action code chunk matching the rule currently being reduced. - * - * - `yysp` : the current state stack position (a.k.a. 'stack pointer') - * - * This one comes in handy when you are going to do advanced things to the parser - * stacks, all of which are accessible from your action code (see the next entries below). - * - * Also note that you can access this and other stack index values using the new double-hash - * syntax, i.e. `##$ === ##0 === yysp`, while `##1` is the stack index for all things - * related to the first rule term, just like you have `$1`, `@1` and `#1`. - * This is made available to write very advanced grammar action rules, e.g. when you want - * to investigate the parse state stack in your action code, which would, for example, - * be relevant when you wish to implement error diagnostics and reporting schemes similar - * to the work described here: - * - * + Pottier, F., 2016. Reachability and error diagnosis in LR(1) automata. - * In Journées Francophones des Languages Applicatifs. - * - * + Jeffery, C.L., 2003. Generating LR syntax error messages from examples. - * ACM Transactions on Programming Languages and Systems (TOPLAS), 25(5), pp.631–640. - * - * - `yyvstack`: reference to the parser value stack. Also accessed via the `$1` etc. - * constructs. - * - * - `yylstack`: reference to the parser token location stack. Also accessed via - * the `@1` etc. constructs. - * - * - `yystack` : reference to the parser token id stack. Also accessed via the - * `#1` etc. constructs. - * - * Note: this is a bit of a **white lie** as we can statically decode any `#n` reference to - * its numeric token id value, hence that code wouldn't need the `yystack` but *you* might - * want access for your own purposes, such as error analysis as mentioned above! - * - * Note that this stack stores the current stack of *tokens*, that is the sequence of - * already parsed=reduced *nonterminals* (tokens representing rules) and *terminals* - * (lexer tokens *shifted* onto the stack until the rule they belong to is found and - * *reduced*. - * - * - `yysstack`: reference to the parser state stack. This one carries the internal parser - * *states* such as the one in `yystate`, which are used to represent - * the parser state machine in the *parse table*. *Very* *internal* stuff, - * what can I say? If you access this one, you're clearly doing wicked things - * - * - `...` : the extra arguments you specified in the `%parse-param` statement in your - * grammar definition file. - * - * table: [...], - * State transition table - * ---------------------- - * - * index levels are: - * - `state` --> hash table - * - `symbol` --> action (number or array) - * - * If the `action` is an array, these are the elements' meaning: - * - index [0]: 1 = shift, 2 = reduce, 3 = accept - * - index [1]: GOTO `state` - * - * If the `action` is a number, it is the GOTO `state` - * - * defaultActions: {...}, - * - * parseError: function(str, hash, ExceptionClass), - * yyError: function(str, ...), - * yyRecovering: function(), - * yyErrOk: function(), - * yyClearIn: function(), - * - * constructParseErrorInfo: function(error_message, exception_object, expected_token_set, is_recoverable), - * Helper function **which will be set up during the first invocation of the `parse()` method**. - * Produces a new errorInfo 'hash object' which can be passed into `parseError()`. - * See it's use in this parser kernel in many places; example usage: - * - * var infoObj = parser.constructParseErrorInfo('fail!', null, - * parser.collect_expected_token_set(state), true); - * var retVal = parser.parseError(infoObj.errStr, infoObj, parser.JisonParserError); - * - * originalParseError: function(str, hash, ExceptionClass), - * The basic `parseError` handler provided by JISON. - * `cleanupAfterParse()` will clean up and reset `parseError()` to reference this function - * at the end of the `parse()`. - * - * options: { ... parser %options ... }, - * - * parse: function(input[, args...]), - * Parse the given `input` and return the parsed value (or `true` when none was provided by - * the root action, in which case the parser is acting as a *matcher*). - * You MAY use the additional `args...` parameters as per `%parse-param` spec of this grammar: - * these extra `args...` are passed verbatim to the grammar rules' action code. - * - * cleanupAfterParse: function(resultValue, invoke_post_methods, do_not_nuke_errorinfos), - * Helper function **which will be set up during the first invocation of the `parse()` method**. - * This helper API is invoked at the end of the `parse()` call, unless an exception was thrown - * and `%options no-try-catch` has been defined for this grammar: in that case this helper MAY - * be invoked by calling user code to ensure the `post_parse` callbacks are invoked and - * the internal parser gets properly garbage collected under these particular circumstances. - * - * lexer: { - * yy: {...}, A reference to the so-called "shared state" `yy` once - * received via a call to the `.setInput(input, yy)` lexer API. - * EOF: 1, - * ERROR: 2, - * JisonLexerError: function(msg, hash), - * parseError: function(str, hash, ExceptionClass), - * setInput: function(input, [yy]), - * input: function(), - * unput: function(str), - * more: function(), - * reject: function(), - * less: function(n), - * pastInput: function(n), - * upcomingInput: function(n), - * showPosition: function(), - * test_match: function(regex_match_array, rule_index, ...), - * next: function(...), - * lex: function(...), - * begin: function(condition), - * pushState: function(condition), - * popState: function(), - * topState: function(), - * _currentRules: function(), - * stateStackSize: function(), - * cleanupAfterLex: function() - * - * options: { ... lexer %options ... }, - * - * performAction: function(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * rules: [...], - * conditions: {associative list: name ==> set}, - * } - * } - * - * - * token location info (@$, _$, etc.): { - * first_line: n, - * last_line: n, - * first_column: n, - * last_column: n, - * range: [start_number, end_number] - * (where the numbers are indexes into the input string, zero-based) - * } - * - * --- - * - * The `parseError` function receives a 'hash' object with these members for lexer and - * parser errors: - * - * { - * text: (matched text) - * token: (the produced terminal token, if any) - * token_id: (the produced terminal token numeric ID, if any) - * line: (yylineno) - * loc: (yylloc) - * } - * - * parser (grammar) errors will also provide these additional members: - * - * { - * expected: (array describing the set of expected tokens; - * may be UNDEFINED when we cannot easily produce such a set) - * state: (integer (or array when the table includes grammar collisions); - * represents the current internal state of the parser kernel. - * can, for example, be used to pass to the `collect_expected_token_set()` - * API to obtain the expected token set) - * action: (integer; represents the current internal action which will be executed) - * new_state: (integer; represents the next/planned internal state, once the current - * action has executed) - * recoverable: (boolean: TRUE when the parser MAY have an error recovery rule - * available for this particular error) - * state_stack: (array: the current parser LALR/LR internal state stack; this can be used, - * for instance, for advanced error analysis and reporting) - * value_stack: (array: the current parser LALR/LR internal `$$` value stack; this can be used, - * for instance, for advanced error analysis and reporting) - * location_stack: (array: the current parser LALR/LR internal location stack; this can be used, - * for instance, for advanced error analysis and reporting) - * yy: (object: the current parser internal "shared state" `yy` - * as is also available in the rule actions; this can be used, - * for instance, for advanced error analysis and reporting) - * lexer: (reference to the current lexer instance used by the parser) - * parser: (reference to the current parser instance) - * } - * - * while `this` will reference the current parser instance. - * - * When `parseError` is invoked by the lexer, `this` will still reference the related *parser* - * instance, while these additional `hash` fields will also be provided: - * - * { - * lexer: (reference to the current lexer instance which reported the error) - * } - * - * When `parseError` is invoked by the parser due to a **JavaScript exception** being fired - * from either the parser or lexer, `this` will still reference the related *parser* - * instance, while these additional `hash` fields will also be provided: - * - * { - * exception: (reference to the exception thrown) - * } - * - * Please do note that in the latter situation, the `expected` field will be omitted as - * this type of failure is assumed not to be due to *parse errors* but rather due to user - * action code in either parser or lexer failing unexpectedly. - * - * --- - * - * You can specify parser options by setting / modifying the `.yy` object of your Parser instance. - * These options are available: - * - * ### options which are global for all parser instances - * - * Parser.pre_parse: function(yy [, optional parse() args]) - * optional: you can specify a pre_parse() function in the chunk following - * the grammar, i.e. after the last `%%`. - * Parser.post_parse: function(yy, retval [, optional parse() args]) { return retval; } - * optional: you can specify a post_parse() function in the chunk following - * the grammar, i.e. after the last `%%`. When it does not return any value, - * the parser will return the original `retval`. - * - * ### options which can be set up per parser instance - * - * yy: { - * pre_parse: function(yy [, optional parse() args]) - * optional: is invoked before the parse cycle starts (and before the first - * invocation of `lex()`) but immediately after the invocation of - * `parser.pre_parse()`). - * post_parse: function(yy, retval [, optional parse() args]) { return retval; } - * optional: is invoked when the parse terminates due to success ('accept') - * or failure (even when exceptions are thrown). - * `retval` contains the return value to be produced by `Parser.parse()`; - * this function can override the return value by returning another. - * When it does not return any value, the parser will return the original - * `retval`. - * This function is invoked immediately before `Parser.post_parse()`. - * - * parseError: function(str, hash, ExceptionClass) - * optional: overrides the default `parseError` function. - * quoteName: function(name), - * optional: overrides the default `quoteName` function. - * } - * - * parser.lexer.options: { - * pre_lex: function() - * optional: is invoked before the lexer is invoked to produce another token. - * `this` refers to the Lexer object. - * post_lex: function(token) { return token; } - * optional: is invoked when the lexer has produced a token `token`; - * this function can override the returned token value by returning another. - * When it does not return any (truthy) value, the lexer will return - * the original `token`. - * `this` refers to the Lexer object. - * - * ranges: boolean - * optional: `true` ==> token location info will include a .range[] member. - * flex: boolean - * optional: `true` ==> flex-like lexing behaviour where the rules are tested - * exhaustively to find the longest match. - * backtrack_lexer: boolean - * optional: `true` ==> lexer regexes are tested in order and for invoked; - * the lexer terminates the scan when a token is returned by the action code. - * xregexp: boolean - * optional: `true` ==> lexer rule regexes are "extended regex format" requiring the - * `XRegExp` library. When this %option has not been specified at compile time, all lexer - * rule regexes have been written as standard JavaScript RegExp expressions. - * } - */ -var ebnf = (function () { - -// See also: -// http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508 -// but we keep the prototype.constructor and prototype.name assignment lines too for compatibility -// with userland code which might access the derived class in a 'classic' way. -function JisonParserError(msg, hash) { - Object.defineProperty(this, 'name', { - enumerable: false, - writable: false, - value: 'JisonParserError' - }); - - if (msg == null) msg = '???'; - - Object.defineProperty(this, 'message', { - enumerable: false, - writable: true, - value: msg - }); - - this.hash = hash; - - var stacktrace; - if (hash && hash.exception instanceof Error) { - var ex2 = hash.exception; - this.message = ex2.message || msg; - stacktrace = ex2.stack; - } - if (!stacktrace) { - if (Error.hasOwnProperty('captureStackTrace')) { // V8 - Error.captureStackTrace(this, this.constructor); - } else { - stacktrace = (new Error(msg)).stack; - } - } - if (stacktrace) { - Object.defineProperty(this, 'stack', { - enumerable: false, - writable: false, - value: stacktrace - }); - } -} - -if (typeof Object.setPrototypeOf === 'function') { - Object.setPrototypeOf(JisonParserError.prototype, Error.prototype); -} else { - JisonParserError.prototype = Object.create(Error.prototype); -} -JisonParserError.prototype.constructor = JisonParserError; -JisonParserError.prototype.name = 'JisonParserError'; - - - - -// helper: reconstruct the productions[] table -function bp(s) { - var rv = []; - var p = s.pop; - var r = s.rule; - for (var i = 0, l = p.length; i < l; i++) { - rv.push([ - p[i], - r[i] - ]); - } - return rv; -} - - - -// helper: reconstruct the defaultActions[] table -function bda(s) { - var rv = {}; - var d = s.idx; - var g = s.goto; - for (var i = 0, l = d.length; i < l; i++) { - var j = d[i]; - rv[j] = g[i]; - } - return rv; -} - - - -// helper: reconstruct the 'goto' table -function bt(s) { - var rv = []; - var d = s.len; - var y = s.symbol; - var t = s.type; - var a = s.state; - var m = s.mode; - var g = s.goto; - for (var i = 0, l = d.length; i < l; i++) { - var n = d[i]; - var q = {}; - for (var j = 0; j < n; j++) { - var z = y.shift(); - switch (t.shift()) { - case 2: - q[z] = [ - m.shift(), - g.shift() - ]; - break; - - case 0: - q[z] = a.shift(); - break; - - default: - // type === 1: accept - q[z] = [ - 3 - ]; - } - } - rv.push(q); - } - return rv; -} - - - -// helper: runlength encoding with increment step: code, length: step (default step = 0) -// `this` references an array -function s(c, l, a) { - a = a || 0; - for (var i = 0; i < l; i++) { - this.push(c); - c += a; - } -} - -// helper: duplicate sequence from *relative* offset and length. -// `this` references an array -function c(i, l) { - i = this.length - i; - for (l += i; i < l; i++) { - this.push(this[i]); - } -} - -// helper: unpack an array using helpers and data, all passed in an array argument 'a'. -function u(a) { - var rv = []; - for (var i = 0, l = a.length; i < l; i++) { - var e = a[i]; - // Is this entry a helper function? - if (typeof e === 'function') { - i++; - e.apply(rv, a[i]); - } else { - rv.push(e); - } - } - return rv; -} - - -var parser = { - // Code Generator Information Report - // --------------------------------- - // - // Options: - // - // no default action: ............... false - // no try..catch: ................... false - // no default resolve on conflict: false - // on-demand look-ahead: ............ false - // error recovery token skip maximum: 3 - // yyerror in parse actions is: ..... NOT recoverable, - // yyerror in lexer actions and other non-fatal lexer are: - // .................................. NOT recoverable, - // debug grammar/output: ............ false - // has partial LR conflict upgrade: true - // rudimentary token-stack support: false - // parser table compression mode: ... 2 - // export debug tables: ............. false - // export *all* tables: ............. false - // module type: ..................... commonjs - // parser engine type: .............. lalr - // output main() in the module: ..... true - // number of expected conflicts: .... 0 - // - // - // Parser Analysis flags: - // - // all actions are default: ......... false - // uses yyleng: ..................... false - // uses yylineno: ................... false - // uses yytext: ..................... false - // uses yylloc: ..................... false - // uses ParseError API: ............. false - // uses YYERROR: .................... false - // uses YYRECOVERING: ............... false - // uses YYERROK: .................... false - // uses YYCLEARIN: .................. false - // tracks rule values: .............. true - // assigns rule values: ............. true - // uses location tracking: .......... false - // assigns location: ................ false - // uses yystack: .................... false - // uses yysstack: ................... false - // uses yysp: ....................... true - // has error recovery: .............. false - // - // --------- END OF REPORT ----------- - -trace: function no_op_trace() { }, -JisonParserError: JisonParserError, -yy: {}, -options: { - type: "lalr", - hasPartialLrUpgradeOnConflict: true, - errorRecoveryTokenDiscardCount: 3 -}, -symbols_: { - "$accept": 0, - "$end": 1, - "(": 4, - ")": 5, - "*": 6, - "+": 8, - "?": 7, - "ALIAS": 10, - "EOF": 1, - "EPSILON": 9, - "SYMBOL": 11, - "error": 2, - "expression": 17, - "handle": 14, - "handle_list": 13, - "production": 12, - "rule": 15, - "suffix": 18, - "suffixed_expression": 16, - "|": 3 -}, -terminals_: { - 1: "EOF", - 2: "error", - 3: "|", - 4: "(", - 5: ")", - 6: "*", - 7: "?", - 8: "+", - 9: "EPSILON", - 10: "ALIAS", - 11: "SYMBOL" -}, -TERROR: 2, -EOF: 1, - -// internals: defined here so the object *structure* doesn't get modified by parse() et al, -// thus helping JIT compilers like Chrome V8. -originalQuoteName: null, -originalParseError: null, -cleanupAfterParse: null, -constructParseErrorInfo: null, - -__reentrant_call_depth: 0, // INTERNAL USE ONLY -__error_infos: [], // INTERNAL USE ONLY: the set of parseErrorInfo objects created since the last cleanup - -// APIs which will be set up depending on user action code analysis: -//yyRecovering: 0, -//yyErrOk: 0, -//yyClearIn: 0, - -// Helper APIs -// ----------- - -// Helper function which can be overridden by user code later on: put suitable quotes around -// literal IDs in a description string. -quoteName: function parser_quoteName(id_str) { - return '"' + id_str + '"'; -}, - -// Return a more-or-less human-readable description of the given symbol, when available, -// or the symbol itself, serving as its own 'description' for lack of something better to serve up. -// -// Return NULL when the symbol is unknown to the parser. -describeSymbol: function parser_describeSymbol(symbol) { - if (symbol !== this.EOF && this.terminal_descriptions_ && this.terminal_descriptions_[symbol]) { - return this.terminal_descriptions_[symbol]; - } - else if (symbol === this.EOF) { - return 'end of input'; - } - else if (this.terminals_[symbol]) { - return this.quoteName(this.terminals_[symbol]); - } - // Otherwise... this might refer to a RULE token i.e. a non-terminal: see if we can dig that one up. - // - // An example of this may be where a rule's action code contains a call like this: - // - // parser.describeSymbol(#$) - // - // to obtain a human-readable description or name of the current grammar rule. This comes handy in - // error handling action code blocks, for example. - var s = this.symbols_; - for (var key in s) { - if (s[key] === symbol) { - return key; - } - } - return null; -}, - -// Produce a (more or less) human-readable list of expected tokens at the point of failure. -// -// The produced list may contain token or token set descriptions instead of the tokens -// themselves to help turning this output into something that easier to read by humans -// unless `do_not_describe` parameter is set, in which case a list of the raw, *numeric*, -// expected terminals and nonterminals is produced. -// -// The returned list (array) will not contain any duplicate entries. -collect_expected_token_set: function parser_collect_expected_token_set(state, do_not_describe) { - var TERROR = this.TERROR; - var tokenset = []; - var check = {}; - // Has this (error?) state been outfitted with a custom expectations description text for human consumption? - // If so, use that one instead of the less palatable token set. - if (!do_not_describe && this.state_descriptions_ && this.state_descriptions_[state]) { - return [ - this.state_descriptions_[state] - ]; - } - for (var p in this.table[state]) { - p = +p; - if (p !== TERROR) { - var d = do_not_describe ? p : this.describeSymbol(p); - if (d && !check[d]) { - tokenset.push(d); - check[d] = true; // Mark this token description as already mentioned to prevent outputting duplicate entries. - } - } - } - return tokenset; -}, -productions_: bp({ - pop: u([ - 12, - 13, - 13, - s, - [14, 3], - 15, - 15, - 16, - 16, - 17, - 17, - s, - [18, 4] -]), - rule: u([ - 2, - 1, - 3, - 0, - s, - [1, 3], - 2, - 3, - c, - [9, 7] -]) -}), -performAction: function parser__PerformAction(yystate /* action[1] */, yysp, yyvstack) { -/* this == yyval */ -var yy = this.yy; - -switch (yystate) { -case 1: - /*! Production:: production : handle EOF */ - return yyvstack[yysp - 1]; - break; - -case 2: - /*! Production:: handle_list : handle */ -case 7: - /*! Production:: rule : suffixed_expression */ - this.$ = [yyvstack[yysp]]; - break; - -case 3: - /*! Production:: handle_list : handle_list "|" handle */ - yyvstack[yysp - 2].push(yyvstack[yysp]); - break; - -case 4: - /*! Production:: handle : ε */ -case 5: - /*! Production:: handle : EPSILON */ - this.$ = []; - break; - -case 6: - /*! Production:: handle : rule */ - this.$ = yyvstack[yysp]; - break; - -case 8: - /*! Production:: rule : rule suffixed_expression */ - yyvstack[yysp - 1].push(yyvstack[yysp]); - break; - -case 9: - /*! Production:: suffixed_expression : expression suffix ALIAS */ - this.$ = ['xalias', yyvstack[yysp - 1], yyvstack[yysp - 2], yyvstack[yysp]]; - break; - -case 10: - /*! Production:: suffixed_expression : expression suffix */ - if (yyvstack[yysp]) { - this.$ = [yyvstack[yysp], yyvstack[yysp - 1]]; - } else { - this.$ = yyvstack[yysp - 1]; - } - break; - -case 11: - /*! Production:: expression : SYMBOL */ - this.$ = ['symbol', yyvstack[yysp]]; - break; - -case 12: - /*! Production:: expression : "(" handle_list ")" */ - this.$ = ['()', yyvstack[yysp - 1]]; - break; - -} -}, -table: bt({ - len: u([ - 9, - 1, - 1, - 0, - 7, - 0, - 10, - 0, - 10, - 0, - 0, - 6, - s, - [0, 3], - 2, - s, - [0, 3], - 9, - 0 -]), - symbol: u([ - 1, - 4, - 9, - 11, - 12, - s, - [14, 4, 1], - s, - [1, 3], - 3, - 4, - 5, - 11, - c, - [9, 3], - s, - [3, 6, 1], - 10, - 11, - 18, - c, - [9, 3], - 9, - 11, - s, - [13, 5, 1], - c, - [20, 4], - 10, - 11, - 3, - 5, - c, - [18, 5], - c, - [17, 4] -]), - type: u([ - s, - [2, 4], - s, - [0, 5], - 1, - s, - [2, 6], - 0, - 0, - s, - [2, 9], - c, - [10, 6], - s, - [0, 5], - s, - [2, 13], - s, - [0, 4] -]), - state: u([ - 1, - 2, - 4, - 5, - 6, - 10, - 6, - 11, - 15, - 16, - c, - [8, 3], - 20, - c, - [4, 3] -]), - mode: u([ - 2, - s, - [1, 4], - 2, - 2, - 1, - 2, - c, - [5, 3], - c, - [7, 3], - c, - [12, 4], - c, - [13, 4], - c, - [14, 6], - c, - [8, 4], - c, - [5, 4] -]), - goto: u([ - 4, - 8, - 3, - 7, - 9, - 6, - 6, - 8, - 6, - 7, - s, - [13, 4], - 12, - 13, - 14, - 13, - 13, - 4, - 8, - 4, - 3, - 7, - s, - [10, 4], - 17, - 10, - 19, - 18, - c, - [13, 5] -]) -}), -defaultActions: bda({ - idx: u([ - s, - [3, 4, 2], - 10, - 12, - 13, - 14, - 16, - 17, - 18, - 20 -]), - goto: u([ - 5, - 7, - 11, - 1, - 8, - 14, - 15, - 16, - 2, - 9, - 12, - 3 -]) -}), -parseError: function parseError(str, hash, ExceptionClass) { - if (hash.recoverable && typeof this.trace === 'function') { - this.trace(str); - hash.destroy(); // destroy... well, *almost*! - } else { - if (!ExceptionClass) { - ExceptionClass = this.JisonParserError; - } - throw new ExceptionClass(str, hash); - } -}, -parse: function parse(input) { - var self = this, - stack = new Array(128), // token stack: stores token which leads to state at the same index (column storage) - sstack = new Array(128), // state stack: stores states (column storage) - - vstack = new Array(128), // semantic value stack - - table = this.table, - sp = 0; // 'stack pointer': index into the stacks - - var TERROR = this.TERROR, - EOF = this.EOF, - ERROR_RECOVERY_TOKEN_DISCARD_COUNT = (this.options.errorRecoveryTokenDiscardCount | 0) || 3; - var NO_ACTION = [0, table.length /* ensures that anyone using this new state will fail dramatically! */]; - - //this.reductionCount = this.shiftCount = 0; - - var lexer; - if (this.__lexer__) { - lexer = this.__lexer__; - } else { - lexer = this.__lexer__ = Object.create(this.lexer); - } - - var sharedState_yy = { - parseError: null, - quoteName: null, - lexer: null, - parser: null, - pre_parse: null, - post_parse: null - }; - // copy state - for (var k in this.yy) { - if (Object.prototype.hasOwnProperty.call(this.yy, k)) { - sharedState_yy[k] = this.yy[k]; - } - } - - sharedState_yy.lexer = lexer; - sharedState_yy.parser = this; - - - - - - - lexer.setInput(input, sharedState_yy); - - - - vstack[sp] = null; - sstack[sp] = 0; - stack[sp] = 0; - ++sp; - - - // Does the shared state override the default `parseError` that already comes with this instance? - if (typeof sharedState_yy.parseError === 'function') { - this.parseError = function parseErrorAlt(str, hash, ExceptionClass) { - if (!ExceptionClass) { - ExceptionClass = this.JisonParserError; - } - return sharedState_yy.parseError(str, hash, ExceptionClass); - }; - } else { - this.parseError = this.originalParseError; - } - - // Does the shared state override the default `quoteName` that already comes with this instance? - if (typeof sharedState_yy.quoteName === 'function') { - this.quoteName = sharedState_yy.quoteName; - } else { - this.quoteName = this.originalQuoteName; - } - - // set up the cleanup function; make it an API so that external code can re-use this one in case of - // calamities or when the `%options no-try-catch` option has been specified for the grammar, in which - // case this parse() API method doesn't come with a `finally { ... }` block any more! - // - // NOTE: as this API uses parse() as a closure, it MUST be set again on every parse() invocation, - // or else your `sharedState`, etc. references will be *wrong*! - this.cleanupAfterParse = function parser_cleanupAfterParse(resultValue, invoke_post_methods, do_not_nuke_errorinfos) { - var rv; - - if (invoke_post_methods) { - if (sharedState_yy.post_parse) { - rv = sharedState_yy.post_parse.call(this, sharedState_yy, resultValue); - if (typeof rv !== 'undefined') resultValue = rv; - } - if (this.post_parse) { - rv = this.post_parse.call(this, sharedState_yy, resultValue); - if (typeof rv !== 'undefined') resultValue = rv; - } - } - - if (this.__reentrant_call_depth > 1) return resultValue; // do not (yet) kill the sharedState when this is a reentrant run. - - // clean up the lingering lexer structures as well: - if (lexer.cleanupAfterLex) { - lexer.cleanupAfterLex(do_not_nuke_errorinfos); - } - - // prevent lingering circular references from causing memory leaks: - if (sharedState_yy) { - sharedState_yy.parseError = undefined; - sharedState_yy.quoteName = undefined; - sharedState_yy.lexer = undefined; - sharedState_yy.parser = undefined; - if (lexer.yy === sharedState_yy) { - lexer.yy = undefined; - } - } - sharedState_yy = undefined; - this.parseError = this.originalParseError; - this.quoteName = this.originalQuoteName; - - // nuke the vstack[] array at least as that one will still reference obsoleted user values. - // To be safe, we nuke the other internal stack columns as well... - stack.length = 0; // fastest way to nuke an array without overly bothering the GC - sstack.length = 0; - - vstack.length = 0; - sp = 0; - - // nuke the error hash info instances created during this run. - // Userland code must COPY any data/references - // in the error hash instance(s) it is more permanently interested in. - if (!do_not_nuke_errorinfos) { - for (var i = this.__error_infos.length - 1; i >= 0; i--) { - var el = this.__error_infos[i]; - if (el && typeof el.destroy === 'function') { - el.destroy(); - } - } - this.__error_infos.length = 0; - } - - return resultValue; - }; - - // NOTE: as this API uses parse() as a closure, it MUST be set again on every parse() invocation, - // or else your `lexer`, `sharedState`, etc. references will be *wrong*! - this.constructParseErrorInfo = function parser_constructParseErrorInfo(msg, ex, expected, recoverable) { - var pei = { - errStr: msg, - exception: ex, - text: lexer.match, - value: lexer.yytext, - token: this.describeSymbol(symbol) || symbol, - token_id: symbol, - line: lexer.yylineno, - - expected: expected, - recoverable: recoverable, - state: state, - action: action, - new_state: newState, - symbol_stack: stack, - state_stack: sstack, - value_stack: vstack, - - stack_pointer: sp, - yy: sharedState_yy, - lexer: lexer, - parser: this, - - // and make sure the error info doesn't stay due to potential - // ref cycle via userland code manipulations. - // These would otherwise all be memory leak opportunities! - // - // Note that only array and object references are nuked as those - // constitute the set of elements which can produce a cyclic ref. - // The rest of the members is kept intact as they are harmless. - destroy: function destructParseErrorInfo() { - // remove cyclic references added to error info: - // info.yy = null; - // info.lexer = null; - // info.value = null; - // info.value_stack = null; - // ... - var rec = !!this.recoverable; - for (var key in this) { - if (this.hasOwnProperty(key) && typeof key === 'object') { - this[key] = undefined; - } - } - this.recoverable = rec; - } - }; - // track this instance so we can `destroy()` it once we deem it superfluous and ready for garbage collection! - this.__error_infos.push(pei); - return pei; - }; - - - function lex() { - var token = lexer.lex(); - // if token isn't its numeric value, convert - if (typeof token !== 'number') { - token = self.symbols_[token] || token; - } - return token || EOF; - } - - - var symbol = 0; - - var state, action, r, t; - var yyval = { - $: true, - _$: undefined, - yy: sharedState_yy - }; - var p, len, this_production; - - var newState; - var retval = false; - - - try { - this.__reentrant_call_depth++; - - if (this.pre_parse) { - this.pre_parse.call(this, sharedState_yy); - } - if (sharedState_yy.pre_parse) { - sharedState_yy.pre_parse.call(this, sharedState_yy); - } - - newState = sstack[sp - 1]; - for (;;) { - // retrieve state number from top of stack - state = newState; // sstack[sp - 1]; - - // use default actions if available - if (this.defaultActions[state]) { - action = 2; - newState = this.defaultActions[state]; - } else { - // The single `==` condition below covers both these `===` comparisons in a single - // operation: - // - // if (symbol === null || typeof symbol === 'undefined') ... - if (!symbol) { - symbol = lex(); - } - // read action for current state and first input - t = (table[state] && table[state][symbol]) || NO_ACTION; - newState = t[1]; - action = t[0]; - - - - - // handle parse error - if (!action) { - var errStr; - var errSymbolDescr = (this.describeSymbol(symbol) || symbol); - var expected = this.collect_expected_token_set(state); - - // Report error - if (typeof lexer.yylineno === 'number') { - errStr = 'Parse error on line ' + (lexer.yylineno + 1) + ': '; - } else { - errStr = 'Parse error: '; - } - if (lexer.showPosition) { - errStr += '\n' + lexer.showPosition(79 - 10, 10) + '\n'; - } - if (expected.length) { - errStr += 'Expecting ' + expected.join(', ') + ', got unexpected ' + errSymbolDescr; - } else { - errStr += 'Unexpected ' + errSymbolDescr; - } - // we cannot recover from the error! - p = this.constructParseErrorInfo(errStr, null, expected, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - - - } - - - - - - - - - - switch (action) { - // catch misc. parse failures: - default: - // this shouldn't happen, unless resolve defaults are off - if (action instanceof Array) { - p = this.constructParseErrorInfo(('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol), null, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - } - // Another case of better safe than sorry: in case state transitions come out of another error recovery process - // or a buggy LUT (LookUp Table): - p = this.constructParseErrorInfo('Parsing halted. No viable error recovery approach available due to internal system failure.', null, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - break; - - // shift: - case 1: - //this.shiftCount++; - stack[sp] = symbol; - vstack[sp] = lexer.yytext; - - sstack[sp] = newState; // push state - ++sp; - symbol = 0; - - // Pick up the lexer details for the current symbol as that one is not 'look-ahead' any more: - - - - - - - - - - - - - - - - continue; - - // reduce: - case 2: - //this.reductionCount++; - this_production = this.productions_[newState - 1]; // `this.productions_[]` is zero-based indexed while states start from 1 upwards... - len = this_production[1]; - - - - - - - // Make sure subsequent `$$ = $1` default action doesn't fail - // for rules where len==0 as then there's no $1 (you're reducing an epsilon rule then!) - // - // Also do this to prevent nasty action block codes to *read* `$0` or `$$` - // and *not* get `undefined` as a result for their efforts! - vstack[sp] = undefined; - - // perform semantic action - yyval.$ = vstack[sp - len]; // default to $$ = $1; result must produce `undefined` when len == 0, as then there's no $1 - - - - - - - - - - - r = this.performAction.call(yyval, newState, sp - 1, vstack); - - if (typeof r !== 'undefined') { - retval = r; - break; - } - - // pop off stack - sp -= len; - - // don't overwrite the `symbol` variable: use a local var to speed things up: - var ntsymbol = this_production[0]; // push nonterminal (reduce) - stack[sp] = ntsymbol; - vstack[sp] = yyval.$; - - // goto new state = table[STATE][NONTERMINAL] - newState = table[sstack[sp - 1]][ntsymbol]; - sstack[sp] = newState; - ++sp; - - continue; - - // accept: - case 3: - retval = true; - // Return the `$accept` rule's `$$` result, if available. - // - // Also note that JISON always adds this top-most `$accept` rule (with implicit, - // default, action): - // - // $accept: $end - // %{ $$ = $1; @$ = @1; %} - // - // which, combined with the parse kernel's `$accept` state behaviour coded below, - // will produce the `$$` value output of the rule as the parse result, - // IFF that result is *not* `undefined`. (See also the parser kernel code.) - // - // In code: - // - // %{ - // @$ = @1; // if location tracking support is included - // if (typeof $1 !== 'undefined') - // return $1; - // else - // return true; // the default parse result if the rule actions don't produce anything - // %} - if (typeof yyval.$ !== 'undefined') { - retval = yyval.$; - } - break; - } - - // break out of loop: we accept or fail with error - break; - } - } catch (ex) { - // report exceptions through the parseError callback too, but keep the exception intact - // if it is a known parser or lexer error which has been thrown by parseError() already: - if (ex instanceof this.JisonParserError) { - throw ex; - } - else if (lexer && typeof lexer.JisonLexerError === 'function' && ex instanceof lexer.JisonLexerError) { - throw ex; - } - else { - p = this.constructParseErrorInfo('Parsing aborted due to exception.', ex, null, false); - retval = this.parseError(p.errStr, p, this.JisonParserError); - } - } finally { - retval = this.cleanupAfterParse(retval, true, true); - this.__reentrant_call_depth--; - } - - return retval; -} -}; -parser.originalParseError = parser.parseError; -parser.originalQuoteName = parser.quoteName; - -var XRegExp = require('xregexp'); // for helping out the `%options xregexp` in the lexer; -/* lexer generated by jison-lex 0.3.4-166 */ -/* - * Returns a Lexer object of the following structure: - * - * Lexer: { - * yy: {} The so-called "shared state" or rather the *source* of it; - * the real "shared state" `yy` passed around to - * the rule actions, etc. is a derivative/copy of this one, - * not a direct reference! - * } - * - * Lexer.prototype: { - * yy: {}, - * EOF: 1, - * ERROR: 2, - * - * JisonLexerError: function(msg, hash), - * - * performAction: function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * where `...` denotes the (optional) additional arguments the user passed to - * `lexer.lex(...)` and specified by way of `%parse-param ...` in the **parser** grammar file - * - * The function parameters and `this` have the following value/meaning: - * - `this` : reference to the `lexer` instance. - * - * - `yy` : a reference to the `yy` "shared state" object which was passed to the lexer - * by way of the `lexer.setInput(str, yy)` API before. - * - * - `yy_` : lexer instance reference used internally. - * - * - `$avoiding_name_collisions` : index of the matched lexer rule (regex), used internally. - * - * - `YY_START`: the current lexer "start condition" state. - * - * - `...` : the extra arguments you specified in the `%parse-param` statement in your - * **parser** grammar definition file and which are passed to the lexer via - * its `lexer.lex(...)` API. - * - * parseError: function(str, hash, ExceptionClass), - * - * constructLexErrorInfo: function(error_message, is_recoverable), - * Helper function. - * Produces a new errorInfo 'hash object' which can be passed into `parseError()`. - * See it's use in this lexer kernel in many places; example usage: - * - * var infoObj = lexer.constructParseErrorInfo('fail!', true); - * var retVal = lexer.parseError(infoObj.errStr, infoObj, lexer.JisonLexerError); - * - * options: { ... lexer %options ... }, - * - * lex: function([args...]), - * Produce one token of lexed input, which was passed in earlier via the `lexer.setInput()` API. - * You MAY use the additional `args...` parameters as per `%parse-param` spec of the **parser** grammar: - * these extra `args...` are passed verbatim to the lexer rules' action code. - * - * cleanupAfterLex: function(do_not_nuke_errorinfos), - * Helper function. - * This helper API is invoked when the parse process has completed. This helper may - * be invoked by user code to ensure the internal lexer gets properly garbage collected. - * - * setInput: function(input, [yy]), - * input: function(), - * unput: function(str), - * more: function(), - * reject: function(), - * less: function(n), - * pastInput: function(n), - * upcomingInput: function(n), - * showPosition: function(), - * test_match: function(regex_match_array, rule_index), - * next: function(...), - * lex: function(...), - * begin: function(condition), - * pushState: function(condition), - * popState: function(), - * topState: function(), - * _currentRules: function(), - * stateStackSize: function(), - * - * options: { ... lexer %options ... }, - * - * performAction: function(yy, yy_, $avoiding_name_collisions, YY_START, ...), - * rules: [...], - * conditions: {associative list: name ==> set}, - * } - * - * - * token location info (`yylloc`): { - * first_line: n, - * last_line: n, - * first_column: n, - * last_column: n, - * range: [start_number, end_number] - * (where the numbers are indexes into the input string, zero-based) - * } - * - * --- - * - * The `parseError` function receives a 'hash' object with these members for lexer errors: - * - * { - * text: (matched text) - * token: (the produced terminal token, if any) - * token_id: (the produced terminal token numeric ID, if any) - * line: (yylineno) - * loc: (yylloc) - * recoverable: (boolean: TRUE when the parser MAY have an error recovery rule - * available for this particular error) - * yy: (object: the current parser internal "shared state" `yy` - * as is also available in the rule actions; this can be used, - * for instance, for advanced error analysis and reporting) - * lexer: (reference to the current lexer instance used by the parser) - * } - * - * while `this` will reference the current lexer instance. - * - * When `parseError` is invoked by the lexer, the default implementation will - * attempt to invoke `yy.parser.parseError()`; when this callback is not provided - * it will try to invoke `yy.parseError()` instead. When that callback is also not - * provided, a `JisonLexerError` exception will be thrown containing the error - * message and `hash`, as constructed by the `constructLexErrorInfo()` API. - * - * Note that the lexer's `JisonLexerError` error class is passed via the - * `ExceptionClass` argument, which is invoked to construct the exception - * instance to be thrown, so technically `parseError` will throw the object - * produced by the `new ExceptionClass(str, hash)` JavaScript expression. - * - * --- - * - * You can specify lexer options by setting / modifying the `.options` object of your Lexer instance. - * These options are available: - * - * (Options are permanent.) - * - * yy: { - * parseError: function(str, hash, ExceptionClass) - * optional: overrides the default `parseError` function. - * } - * - * lexer.options: { - * pre_lex: function() - * optional: is invoked before the lexer is invoked to produce another token. - * `this` refers to the Lexer object. - * post_lex: function(token) { return token; } - * optional: is invoked when the lexer has produced a token `token`; - * this function can override the returned token value by returning another. - * When it does not return any (truthy) value, the lexer will return - * the original `token`. - * `this` refers to the Lexer object. - * - * WARNING: the next set of options are not meant to be changed. They echo the abilities of - * the lexer as per when it was compiled! - * - * ranges: boolean - * optional: `true` ==> token location info will include a .range[] member. - * flex: boolean - * optional: `true` ==> flex-like lexing behaviour where the rules are tested - * exhaustively to find the longest match. - * backtrack_lexer: boolean - * optional: `true` ==> lexer regexes are tested in order and for invoked; - * the lexer terminates the scan when a token is returned by the action code. - * xregexp: boolean - * optional: `true` ==> lexer rule regexes are "extended regex format" requiring the - * `XRegExp` library. When this %option has not been specified at compile time, all lexer - * rule regexes have been written as standard JavaScript RegExp expressions. - * } - */ - - -var lexer = (function () { -// See also: -// http://stackoverflow.com/questions/1382107/whats-a-good-way-to-extend-error-in-javascript/#35881508 -// but we keep the prototype.constructor and prototype.name assignment lines too for compatibility -// with userland code which might access the derived class in a 'classic' way. -function JisonLexerError(msg, hash) { - Object.defineProperty(this, 'name', { - enumerable: false, - writable: false, - value: 'JisonLexerError' - }); - - if (msg == null) msg = '???'; - - Object.defineProperty(this, 'message', { - enumerable: false, - writable: true, - value: msg - }); - - this.hash = hash; - - var stacktrace; - if (hash && hash.exception instanceof Error) { - var ex2 = hash.exception; - this.message = ex2.message || msg; - stacktrace = ex2.stack; - } - if (!stacktrace) { - if (Error.hasOwnProperty('captureStackTrace')) { // V8 - Error.captureStackTrace(this, this.constructor); - } else { - stacktrace = (new Error(msg)).stack; - } - } - if (stacktrace) { - Object.defineProperty(this, 'stack', { - enumerable: false, - writable: false, - value: stacktrace - }); - } -} - -if (typeof Object.setPrototypeOf === 'function') { - Object.setPrototypeOf(JisonLexerError.prototype, Error.prototype); -} else { - JisonLexerError.prototype = Object.create(Error.prototype); -} -JisonLexerError.prototype.constructor = JisonLexerError; -JisonLexerError.prototype.name = 'JisonLexerError'; - - - - -var lexer = { - - // Code Generator Information Report - // --------------------------------- - // - // Options: - // - // backtracking: .................... false - // location.ranges: ................. true - // location line+column tracking: ... true - // - // - // Forwarded Parser Analysis flags: - // - // uses yyleng: ..................... false - // uses yylineno: ................... false - // uses yytext: ..................... false - // uses yylloc: ..................... false - // uses lexer values: ............... true / true - // location tracking: ............... false - // location assignment: ............. false - // - // - // Lexer Analysis flags: - // - // uses yyleng: ..................... undefined - // uses yylineno: ................... undefined - // uses yytext: ..................... undefined - // uses yylloc: ..................... undefined - // uses ParseError API: ............. undefined - // uses location tracking & editing: undefined - // uses more() API: ................. undefined - // uses unput() API: ................ undefined - // uses reject() API: ............... undefined - // uses less() API: ................. undefined - // uses display APIs pastInput(), upcomingInput(), showPosition(): - // ............................. undefined - // uses describeYYLLOC() API: ....... undefined - // - // --------- END OF REPORT ----------- - - - EOF: 1, - ERROR: 2, - - // JisonLexerError: JisonLexerError, /// <-- injected by the code generator - - // options: {}, /// <-- injected by the code generator - - // yy: ..., /// <-- injected by setInput() - - __currentRuleSet__: null, /// <-- internal rule set cache for the current lexer state - - __error_infos: [], /// INTERNAL USE ONLY: the set of lexErrorInfo objects created since the last cleanup - - __decompressed: false, /// INTERNAL USE ONLY: mark whether the lexer instance has been 'unfolded' completely and is now ready for use - - done: false, /// INTERNAL USE ONLY - _backtrack: false, /// INTERNAL USE ONLY - _input: '', /// INTERNAL USE ONLY - _more: false, /// INTERNAL USE ONLY - _signaled_error_token: false, /// INTERNAL USE ONLY - - conditionStack: [], /// INTERNAL USE ONLY; managed via `pushState()`, `popState()`, `topState()` and `stateStackSize()` - - match: '', /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks input which has been matched so far for the lexer token under construction. `match` is identical to `yytext` except that this one still contains the matched input string after `lexer.performAction()` has been invoked, where userland code MAY have changed/replaced the `yytext` value entirely! - matched: '', /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks entire input which has been matched so far - matches: false, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks RE match result for last (successful) match attempt - yytext: '', /// ADVANCED USE ONLY: tracks input which has been matched so far for the lexer token under construction; this value is transferred to the parser as the 'token value' when the parser consumes the lexer token produced through a call to the `lex()` API. - offset: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks the 'cursor position' in the input string, i.e. the number of characters matched so far - yyleng: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: length of matched input for the token under construction (`yytext`) - yylineno: 0, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: 'line number' at which the token under construction is located - yylloc: null, /// READ-ONLY EXTERNAL ACCESS - ADVANCED USE ONLY: tracks location info (lines + columns) for the token under construction - - /** - INTERNAL USE: construct a suitable error info hash object instance for `parseError`. - - @public - @this {RegExpLexer} - */ - constructLexErrorInfo: function lexer_constructLexErrorInfo(msg, recoverable) { - /** @constructor */ - var pei = { - errStr: msg, - recoverable: !!recoverable, - text: this.match, // This one MAY be empty; userland code should use the `upcomingInput` API to obtain more text which follows the 'lexer cursor position'... - token: null, - line: this.yylineno, - loc: this.yylloc, - yy: this.yy, - lexer: this, - - /** - and make sure the error info doesn't stay due to potential - ref cycle via userland code manipulations. - These would otherwise all be memory leak opportunities! - - Note that only array and object references are nuked as those - constitute the set of elements which can produce a cyclic ref. - The rest of the members is kept intact as they are harmless. - - @public - @this {LexErrorInfo} - */ - destroy: function destructLexErrorInfo() { - // remove cyclic references added to error info: - // info.yy = null; - // info.lexer = null; - // ... - var rec = !!this.recoverable; - for (var key in this) { - if (this.hasOwnProperty(key) && typeof key === 'object') { - this[key] = undefined; - } - } - this.recoverable = rec; - } - }; - // track this instance so we can `destroy()` it once we deem it superfluous and ready for garbage collection! - this.__error_infos.push(pei); - return pei; - }, - - /** - handler which is invoked when a lexer error occurs. - - @public - @this {RegExpLexer} - */ - parseError: function lexer_parseError(str, hash, ExceptionClass) { - if (!ExceptionClass) { - ExceptionClass = this.JisonLexerError; - } - if (this.yy.parser && typeof this.yy.parser.parseError === 'function') { - return this.yy.parser.parseError(str, hash, ExceptionClass) || this.ERROR; - } else if (typeof this.yy.parseError === 'function') { - return this.yy.parseError(str, hash, ExceptionClass) || this.ERROR; - } else { - throw new ExceptionClass(str, hash); - } - }, - - /** - method which implements `yyerror(str, ...args)` functionality for use inside lexer actions. - - @public - @this {RegExpLexer} - */ - yyerror: function yyError(str /*, ...args */) { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': ' + str, this.options.lexerErrorsAreRecoverable); - - // Add any extra args to the hash under the name `extra_error_attributes`: - var args = Array.prototype.slice.call(arguments, 1); - if (args.length) { - hash.extra_error_attributes = args; - } - - return (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - }, - - /** - final cleanup function for when we have completed lexing the input; - make it an API so that external code can use this one once userland - code has decided it's time to destroy any lingering lexer error - hash object instances and the like: this function helps to clean - up these constructs, which *may* carry cyclic references which would - otherwise prevent the instances from being properly and timely - garbage-collected, i.e. this function helps prevent memory leaks! - - @public - @this {RegExpLexer} - */ - cleanupAfterLex: function lexer_cleanupAfterLex(do_not_nuke_errorinfos) { - var rv; - - // prevent lingering circular references from causing memory leaks: - this.setInput('', {}); - - // nuke the error hash info instances created during this run. - // Userland code must COPY any data/references - // in the error hash instance(s) it is more permanently interested in. - if (!do_not_nuke_errorinfos) { - for (var i = this.__error_infos.length - 1; i >= 0; i--) { - var el = this.__error_infos[i]; - if (el && typeof el.destroy === 'function') { - el.destroy(); - } - } - this.__error_infos.length = 0; - } - - return this; - }, - - /** - clear the lexer token context; intended for internal use only - - @public - @this {RegExpLexer} - */ - clear: function lexer_clear() { - this.yytext = ''; - this.yyleng = 0; - this.match = ''; - this.matches = false; - this._more = false; - this._backtrack = false; - - var col = this.yylloc ? this.yylloc.last_column : 0; - this.yylloc = { - first_line: this.yylineno + 1, - first_column: col, - last_line: this.yylineno + 1, - last_column: col, - - range: (this.options.ranges ? [this.offset, this.offset] : undefined) - }; - }, - - /** - resets the lexer, sets new input - - @public - @this {RegExpLexer} - */ - setInput: function lexer_setInput(input, yy) { - this.yy = yy || this.yy || {}; - - // also check if we've fully initialized the lexer instance, - // including expansion work to be done to go from a loaded - // lexer to a usable lexer: - if (!this.__decompressed) { - // step 1: decompress the regex list: - var rules = this.rules; - for (var i = 0, len = rules.length; i < len; i++) { - var rule_re = rules[i]; - - // compression: is the RE an xref to another RE slot in the rules[] table? - if (typeof rule_re === 'number') { - rules[i] = rules[rule_re]; - } - } - - // step 2: unfold the conditions[] set to make these ready for use: - var conditions = this.conditions; - for (var k in conditions) { - var spec = conditions[k]; - - var rule_ids = spec.rules; - - var len = rule_ids.length; - var rule_regexes = new Array(len + 1); // slot 0 is unused; we use a 1-based index approach here to keep the hottest code in `lexer_next()` fast and simple! - var rule_new_ids = new Array(len + 1); - - for (var i = 0; i < len; i++) { - var idx = rule_ids[i]; - var rule_re = rules[idx]; - rule_regexes[i + 1] = rule_re; - rule_new_ids[i + 1] = idx; - } - - spec.rules = rule_new_ids; - spec.__rule_regexes = rule_regexes; - spec.__rule_count = len; - } - - this.__decompressed = true; - } - - this._input = input || ''; - this.clear(); - this._signaled_error_token = false; - this.done = false; - this.yylineno = 0; - this.matched = ''; - this.conditionStack = ['INITIAL']; - this.__currentRuleSet__ = null; - this.yylloc = { - first_line: 1, - first_column: 0, - last_line: 1, - last_column: 0, - - range: (this.options.ranges ? [0, 0] : undefined) - }; - this.offset = 0; - return this; - }, - - /** - push a new input into the lexer and activate it: - the old input position is stored and will be resumed - once this new input has been consumed. - - Use this API to help implement C-preprocessor-like - `#include` statements. - - Available options: - - - `emit_EOF_at_end` : {int} the `EOF`-like token to emit - when the new input is consumed: use - this to mark the end of the new input - in the parser grammar. zero/falsey - token value means no end marker token - will be emitted before the lexer - resumes reading from the previous input. - - @public - @this {RegExpLexer} - */ - pushInput: function lexer_pushInput(input, label, options) { - options = options || {}; - - this._input = input || ''; - this.clear(); - // this._signaled_error_token = false; - this.done = false; - this.yylineno = 0; - this.matched = ''; - // this.conditionStack = ['INITIAL']; - // this.__currentRuleSet__ = null; - this.yylloc = { - first_line: 1, - first_column: 0, - last_line: 1, - last_column: 0, - - range: (this.options.ranges ? [0, 0] : undefined) - }; - this.offset = 0; - return this; - }, - - /** - consumes and returns one char from the input - - @public - @this {RegExpLexer} - */ - input: function lexer_input() { - if (!this._input) { - //this.done = true; -- don't set `done` as we want the lex()/next() API to be able to produce one custom EOF token match after this anyhow. (lexer can match special <> tokens and perform user action code for a <> match, but only does so *once*) - return null; - } - var ch = this._input[0]; - this.yytext += ch; - this.yyleng++; - this.offset++; - this.match += ch; - this.matched += ch; - // Count the linenumber up when we hit the LF (or a stand-alone CR). - // On CRLF, the linenumber is incremented when you fetch the CR or the CRLF combo - // and we advance immediately past the LF as well, returning both together as if - // it was all a single 'character' only. - var slice_len = 1; - var lines = false; - if (ch === '\n') { - lines = true; - } else if (ch === '\r') { - lines = true; - var ch2 = this._input[1]; - if (ch2 === '\n') { - slice_len++; - ch += ch2; - this.yytext += ch2; - this.yyleng++; - this.offset++; - this.match += ch2; - this.matched += ch2; - if (this.options.ranges) { - this.yylloc.range[1]++; - } - } - } - if (lines) { - this.yylineno++; - this.yylloc.last_line++; - this.yylloc.last_column = 0; - } else { - this.yylloc.last_column++; - } - if (this.options.ranges) { - this.yylloc.range[1]++; - } - - this._input = this._input.slice(slice_len); - return ch; - }, - - /** - unshifts one char (or an entire string) into the input - - @public - @this {RegExpLexer} - */ - unput: function lexer_unput(ch) { - var len = ch.length; - var lines = ch.split(/(?:\r\n?|\n)/g); - - this._input = ch + this._input; - this.yytext = this.yytext.substr(0, this.yytext.length - len); - this.yyleng = this.yytext.length; - this.offset -= len; - this.match = this.match.substr(0, this.match.length - len); - this.matched = this.matched.substr(0, this.matched.length - len); - - if (lines.length > 1) { - this.yylineno -= lines.length - 1; - - this.yylloc.last_line = this.yylineno + 1; - var pre = this.match; - var pre_lines = pre.split(/(?:\r\n?|\n)/g); - if (pre_lines.length === 1) { - pre = this.matched; - pre_lines = pre.split(/(?:\r\n?|\n)/g); - } - this.yylloc.last_column = pre_lines[pre_lines.length - 1].length; - } else { - this.yylloc.last_column -= len; - } - - if (this.options.ranges) { - this.yylloc.range[1] = this.yylloc.range[0] + this.yyleng; - } - this.done = false; - return this; - }, - - /** - cache matched text and append it on next action - - @public - @this {RegExpLexer} - */ - more: function lexer_more() { - this._more = true; - return this; - }, - - /** - signal the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead. - - @public - @this {RegExpLexer} - */ - reject: function lexer_reject() { - if (this.options.backtrack_lexer) { - this._backtrack = true; - } else { - // when the `parseError()` call returns, we MUST ensure that the error is registered. - // We accomplish this by signaling an 'error' token to be produced for the current - // `.lex()` run. - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).' + pos_str, false); - this._signaled_error_token = (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - } - return this; - }, - - /** - retain first n characters of the match - - @public - @this {RegExpLexer} - */ - less: function lexer_less(n) { - return this.unput(this.match.slice(n)); - }, - - /** - return (part of the) already matched input, i.e. for error messages. - - Limit the returned string length to `maxSize` (default: 20). - - Limit the returned string to the `maxLines` number of lines of input (default: 1). - - Negative limit values equal *unlimited*. - - @public - @this {RegExpLexer} - */ - pastInput: function lexer_pastInput(maxSize, maxLines) { - var past = this.matched.substring(0, this.matched.length - this.match.length); - if (maxSize < 0) - maxSize = past.length; - else if (!maxSize) - maxSize = 20; - if (maxLines < 0) - maxLines = past.length; // can't ever have more input lines than this! - else if (!maxLines) - maxLines = 1; - // `substr` anticipation: treat \r\n as a single character and take a little - // more than necessary so that we can still properly check against maxSize - // after we've transformed and limited the newLines in here: - past = past.substr(-maxSize * 2 - 2); - // now that we have a significantly reduced string to process, transform the newlines - // and chop them, then limit them: - var a = past.replace(/\r\n|\r/g, '\n').split('\n'); - a = a.slice(-maxLines); - past = a.join('\n'); - // When, after limiting to maxLines, we still have too much to return, - // do add an ellipsis prefix... - if (past.length > maxSize) { - past = '...' + past.substr(-maxSize); - } - return past; - }, - - /** - return (part of the) upcoming input, i.e. for error messages. - - Limit the returned string length to `maxSize` (default: 20). - - Limit the returned string to the `maxLines` number of lines of input (default: 1). - - Negative limit values equal *unlimited*. - - @public - @this {RegExpLexer} - */ - upcomingInput: function lexer_upcomingInput(maxSize, maxLines) { - var next = this.match; - if (maxSize < 0) - maxSize = next.length + this._input.length; - else if (!maxSize) - maxSize = 20; - if (maxLines < 0) - maxLines = maxSize; // can't ever have more input lines than this! - else if (!maxLines) - maxLines = 1; - // `substring` anticipation: treat \r\n as a single character and take a little - // more than necessary so that we can still properly check against maxSize - // after we've transformed and limited the newLines in here: - if (next.length < maxSize * 2 + 2) { - next += this._input.substring(0, maxSize * 2 + 2); // substring is faster on Chrome/V8 - } - // now that we have a significantly reduced string to process, transform the newlines - // and chop them, then limit them: - var a = next.replace(/\r\n|\r/g, '\n').split('\n'); - a = a.slice(0, maxLines); - next = a.join('\n'); - // When, after limiting to maxLines, we still have too much to return, - // do add an ellipsis postfix... - if (next.length > maxSize) { - next = next.substring(0, maxSize) + '...'; - } - return next; - }, - - /** - return a string which displays the character position where the lexing error occurred, i.e. for error messages - - @public - @this {RegExpLexer} - */ - showPosition: function lexer_showPosition(maxPrefix, maxPostfix) { - var pre = this.pastInput(maxPrefix).replace(/\s/g, ' '); - var c = new Array(pre.length + 1).join('-'); - return pre + this.upcomingInput(maxPostfix).replace(/\s/g, ' ') + '\n' + c + '^'; - }, - - /** - helper function, used to produce a human readable description as a string, given - the input `yylloc` location object. - - Set `display_range_too` to TRUE to include the string character index position(s) - in the description if the `yylloc.range` is available. - - @public - @this {RegExpLexer} - */ - describeYYLLOC: function lexer_describe_yylloc(yylloc, display_range_too) { - var l1 = yylloc.first_line; - var l2 = yylloc.last_line; - var o1 = yylloc.first_column; - var o2 = yylloc.last_column; - var dl = l2 - l1; - var d_o = o2 - o1; - var rv; - if (dl === 0) { - rv = 'line ' + l1 + ', '; - if (d_o === 1) { - rv += 'column ' + o1; - } else { - rv += 'columns ' + o1 + ' .. ' + o2; - } - } else { - rv = 'lines ' + l1 + '(column ' + o1 + ') .. ' + l2 + '(column ' + o2 + ')'; - } - if (yylloc.range && display_range_too) { - var r1 = yylloc.range[0]; - var r2 = yylloc.range[1] - 1; - if (r2 === r1) { - rv += ' {String Offset: ' + r1 + '}'; - } else { - rv += ' {String Offset range: ' + r1 + ' .. ' + r2 + '}'; - } - } - return rv; - // return JSON.stringify(yylloc); - }, - - /** - test the lexed token: return FALSE when not a match, otherwise return token. - - `match` is supposed to be an array coming out of a regex match, i.e. `match[0]` - contains the actually matched text string. - - Also move the input cursor forward and update the match collectors: - - - `yytext` - - `yyleng` - - `match` - - `matches` - - `yylloc` - - `offset` - - @public - @this {RegExpLexer} - */ - test_match: function lexer_test_match(match, indexed_rule) { - var token, - lines, - backup, - match_str, - match_str_len; - - if (this.options.backtrack_lexer) { - // save context - backup = { - yylineno: this.yylineno, - yylloc: { - first_line: this.yylloc.first_line, - last_line: this.yylloc.last_line, - first_column: this.yylloc.first_column, - last_column: this.yylloc.last_column, - - range: (this.options.ranges ? this.yylloc.range.slice(0) : undefined) - }, - yytext: this.yytext, - match: this.match, - matches: this.matches, - matched: this.matched, - yyleng: this.yyleng, - offset: this.offset, - _more: this._more, - _input: this._input, - //_signaled_error_token: this._signaled_error_token, - yy: this.yy, - conditionStack: this.conditionStack.slice(0), - done: this.done - }; - } - - match_str = match[0]; - match_str_len = match_str.length; - // if (match_str.indexOf('\n') !== -1 || match_str.indexOf('\r') !== -1) { - lines = match_str.split(/(?:\r\n?|\n)/g); - if (lines.length > 1) { - this.yylineno += lines.length - 1; - - this.yylloc.last_line = this.yylineno + 1, - this.yylloc.last_column = lines[lines.length - 1].length; - } else { - this.yylloc.last_column += match_str_len; - } - // } - this.yytext += match_str; - this.match += match_str; - this.matches = match; - this.yyleng = this.yytext.length; - if (this.options.ranges) { - this.yylloc.range[1] += match_str_len; - } - // previous lex rules MAY have invoked the `more()` API rather than producing a token: - // those rules will already have moved this `offset` forward matching their match lengths, - // hence we must only add our own match length now: - this.offset += match_str_len; - this._more = false; - this._backtrack = false; - this._input = this._input.slice(match_str_len); - this.matched += match_str; - - // calling this method: - // - // function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START) {...} - token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1] /* = YY_START */); - // otherwise, when the action codes are all simple return token statements: - //token = this.simpleCaseActionClusters[indexed_rule]; - - if (this.done && this._input) { - this.done = false; - } - if (token) { - return token; - } else if (this._backtrack) { - // recover context - for (var k in backup) { - this[k] = backup[k]; - } - this.__currentRuleSet__ = null; - return false; // rule action called reject() implying the next rule should be tested instead. - } else if (this._signaled_error_token) { - // produce one 'error' token as `.parseError()` in `reject()` did not guarantee a failure signal by throwing an exception! - token = this._signaled_error_token; - this._signaled_error_token = false; - return token; - } - return false; - }, - - /** - return next match in input - - @public - @this {RegExpLexer} - */ - next: function lexer_next() { - if (this.done) { - this.clear(); - return this.EOF; - } - if (!this._input) { - this.done = true; - } - - var token, - match, - tempMatch, - index; - if (!this._more) { - this.clear(); - } - var spec = this.__currentRuleSet__; - if (!spec) { - // Update the ruleset cache as we apparently encountered a state change or just started lexing. - // The cache is set up for fast lookup -- we assume a lexer will switch states much less often than it will - // invoke the `lex()` token-producing API and related APIs, hence caching the set for direct access helps - // speed up those activities a tiny bit. - spec = this.__currentRuleSet__ = this._currentRules(); - // Check whether a *sane* condition has been pushed before: this makes the lexer robust against - // user-programmer bugs such as https://github.com/zaach/jison-lex/issues/19 - if (!spec || !spec.rules) { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Internal lexer engine error' + lineno_msg + ': The lex grammar programmer pushed a non-existing condition name "' + this.topState() + '"; this is a fatal error and should be reported to the application programmer team!' + pos_str, false); - // produce one 'error' token until this situation has been resolved, most probably by parse termination! - return (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - } - } - - var rule_ids = spec.rules; - //var dispatch = spec.__dispatch_lut; - var regexes = spec.__rule_regexes; - var len = spec.__rule_count; - - // Note: the arrays are 1-based, while `len` itself is a valid index, - // hence the non-standard less-or-equal check in the next loop condition! - for (var i = 1; i <= len; i++) { - tempMatch = this._input.match(regexes[i]); - if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { - match = tempMatch; - index = i; - if (this.options.backtrack_lexer) { - token = this.test_match(tempMatch, rule_ids[i]); - if (token !== false) { - return token; - } else if (this._backtrack) { - match = undefined; - continue; // rule action called reject() implying a rule MISmatch. - } else { - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - } else if (!this.options.flex) { - break; - } - } - } - if (match) { - token = this.test_match(match, rule_ids[index]); - if (token !== false) { - return token; - } - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - if (!this._input) { - this.done = true; - this.clear(); - return this.EOF; - } else { - var lineno_msg = ''; - if (this.options.trackPosition) { - lineno_msg = ' on line ' + (this.yylineno + 1); - } - var pos_str = this.showPosition(); - if (pos_str && pos_str[0] !== '\n') { - pos_str = '\n' + pos_str; - } - var p = this.constructLexErrorInfo('Lexical error' + lineno_msg + ': Unrecognized text.' + pos_str, this.options.lexerErrorsAreRecoverable); - token = (this.parseError(p.errStr, p, this.JisonLexerError) || this.ERROR); - if (token === this.ERROR) { - // we can try to recover from a lexer error that `parseError()` did not 'recover' for us - // by moving forward at least one character at a time: - if (!this.match.length) { - this.input(); - } - } - return token; - } - }, - - /** - return next match that has a token - - @public - @this {RegExpLexer} - */ - lex: function lexer_lex() { - var r; - // allow the PRE/POST handlers set/modify the return token for maximum flexibility of the generated lexer: - if (typeof this.options.pre_lex === 'function') { - r = this.options.pre_lex.call(this); - } - while (!r) { - r = this.next(); - } - if (typeof this.options.post_lex === 'function') { - // (also account for a userdef function which does not return any value: keep the token as is) - r = this.options.post_lex.call(this, r) || r; - } - return r; - }, - - /** - backwards compatible alias for `pushState()`; - the latter is symmetrical with `popState()` and we advise to use - those APIs in any modern lexer code, rather than `begin()`. - - @public - @this {RegExpLexer} - */ - begin: function lexer_begin(condition) { - return this.pushState(condition); - }, - - /** - activates a new lexer condition state (pushes the new lexer condition state onto the condition stack) - - @public - @this {RegExpLexer} - */ - pushState: function lexer_pushState(condition) { - this.conditionStack.push(condition); - this.__currentRuleSet__ = null; - return this; - }, - - /** - pop the previously active lexer condition state off the condition stack - - @public - @this {RegExpLexer} - */ - popState: function lexer_popState() { - var n = this.conditionStack.length - 1; - if (n > 0) { - this.__currentRuleSet__ = null; - return this.conditionStack.pop(); - } else { - return this.conditionStack[0]; - } - }, - - /** - return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available - - @public - @this {RegExpLexer} - */ - topState: function lexer_topState(n) { - n = this.conditionStack.length - 1 - Math.abs(n || 0); - if (n >= 0) { - return this.conditionStack[n]; - } else { - return 'INITIAL'; - } - }, - - /** - (internal) determine the lexer rule set which is active for the currently active lexer condition state - - @public - @this {RegExpLexer} - */ - _currentRules: function lexer__currentRules() { - if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) { - return this.conditions[this.conditionStack[this.conditionStack.length - 1]]; - } else { - return this.conditions['INITIAL']; - } - }, - - /** - return the number of states currently on the stack - - @public - @this {RegExpLexer} - */ - stateStackSize: function lexer_stateStackSize() { - return this.conditionStack.length; - }, - options: { - xregexp: true, - ranges: true, - trackPosition: true, - easy_keyword_rules: true -}, - JisonLexerError: JisonLexerError, - performAction: function lexer__performAction(yy, yy_, $avoiding_name_collisions, YY_START) { - -var YYSTATE = YY_START; -switch($avoiding_name_collisions) { -case 0 : -/*! Conditions:: INITIAL */ -/*! Rule:: \s+ */ - /* skip whitespace */ -break; -case 4 : -/*! Conditions:: INITIAL */ -/*! Rule:: \[{ID}\] */ - yy_.yytext = this.matches[1]; return 10; -break; -default: - return this.simpleCaseActionClusters[$avoiding_name_collisions]; -} -}, - simpleCaseActionClusters: { - - /*! Conditions:: INITIAL */ - /*! Rule:: {ID} */ - 1 : 11, - /*! Conditions:: INITIAL */ - /*! Rule:: \$end\b */ - 2 : 11, - /*! Conditions:: INITIAL */ - /*! Rule:: \$eof\b */ - 3 : 11, - /*! Conditions:: INITIAL */ - /*! Rule:: %empty\b */ - 5 : 9, - /*! Conditions:: INITIAL */ - /*! Rule:: %epsilon\b */ - 6 : 9, - /*! Conditions:: INITIAL */ - /*! Rule:: \u0190 */ - 7 : 9, - /*! Conditions:: INITIAL */ - /*! Rule:: \u025B */ - 8 : 9, - /*! Conditions:: INITIAL */ - /*! Rule:: \u03B5 */ - 9 : 9, - /*! Conditions:: INITIAL */ - /*! Rule:: \u03F5 */ - 10 : 9, - /*! Conditions:: INITIAL */ - /*! Rule:: '{QUOTED_STRING_CONTENT}' */ - 11 : 11, - /*! Conditions:: INITIAL */ - /*! Rule:: "{DOUBLEQUOTED_STRING_CONTENT}" */ - 12 : 11, - /*! Conditions:: INITIAL */ - /*! Rule:: \. */ - 13 : 11, - /*! Conditions:: INITIAL */ - /*! Rule:: \( */ - 14 : 4, - /*! Conditions:: INITIAL */ - /*! Rule:: \) */ - 15 : 5, - /*! Conditions:: INITIAL */ - /*! Rule:: \* */ - 16 : 6, - /*! Conditions:: INITIAL */ - /*! Rule:: \? */ - 17 : 7, - /*! Conditions:: INITIAL */ - /*! Rule:: \| */ - 18 : 3, - /*! Conditions:: INITIAL */ - /*! Rule:: \+ */ - 19 : 8, - /*! Conditions:: INITIAL */ - /*! Rule:: $ */ - 20 : 1 -}, - rules: [ - /^(?:\s+)/, -new XRegExp("^(?:([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}_])*))", ""), -/^(?:\$end\b)/, -/^(?:\$eof\b)/, -new XRegExp("^(?:\\[([\\p{Alphabetic}_](?:[\\p{Alphabetic}\\p{Number}_])*)\\])", ""), -/^(?:%empty\b)/, -/^(?:%epsilon\b)/, -/^(?:\u0190)/, -/^(?:\u025B)/, -/^(?:\u03B5)/, -/^(?:\u03F5)/, -/^(?:'((?:\\'|\\[^']|[^'\\])*)')/, -/^(?:"((?:\\"|\\[^"]|[^"\\])*)")/, -/^(?:\.)/, -/^(?:\()/, -/^(?:\))/, -/^(?:\*)/, -/^(?:\?)/, -/^(?:\|)/, -/^(?:\+)/, -/^(?:$)/ - ], - conditions: { - "INITIAL": { - rules: [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20 - ], - inclusive: true - } -} -}; - - - - -return lexer; -})(); -parser.lexer = lexer; - -function Parser() { - this.yy = {}; -} -Parser.prototype = parser; -parser.Parser = Parser; - -return new Parser(); -})(); - - - - -if (typeof require !== 'undefined' && typeof exports !== 'undefined') { - exports.parser = ebnf; - exports.Parser = ebnf.Parser; - exports.parse = function () { - return ebnf.parse.apply(ebnf, arguments); - }; - -} - -},{"xregexp":22}],13:[function(require,module,exports){ -/* - * Introduces a typal object to make classical/prototypal patterns easier - * Plus some AOP sugar - * - * By Zachary Carter - * MIT Licensed - * */ - -var typal = (function () { -'use strict'; - -var create = Object.create || function (o) { - function F(){} - F.prototype = o; - return new F(); -}; -var position = /^(before|after)/; - -// basic method layering -// always returns original method's return value -function layerMethod(pos, key, prop, fun) { - if (pos === 'after') { - return function () { - var ret = prop.apply(this, arguments); - var args = [].slice.call(arguments); - args.splice(0, 0, ret); - fun.apply(this, args); - return ret; - }; - } else if (pos === 'before') { - return function () { - fun.apply(this, arguments); - var ret = prop.apply(this, arguments); - return ret; - }; - } - return fun; -} - -// mixes each argument's own properties into calling object, -// overwriting them or layering them. i.e. an object method 'meth' is -// layered by mixin methods 'beforemeth' or 'aftermeth' -function typal_mix() { - var self = this; - var i, o, k; - for (i = 0; i < arguments.length; i++) { - o = arguments[i]; - if (!o) continue; - if (Object.prototype.hasOwnProperty.call(o, 'constructor')) { - this.constructor = o.constructor; - } - if (Object.prototype.hasOwnProperty.call(o, 'toString')) { - this.toString = o.toString; - } - for (k in o) { - if (Object.prototype.hasOwnProperty.call(o, k)) { - var match = k.match(position); - var key = k.replace(position, ''); - if (match && typeof this[key] === 'function') { - this[key] = layerMethod(match[0], key, this[key], o[k]); - } else { - this[k] = o[k]; - } - } - } - } - return this; -} - -// Same as typal_mix but also camelCases every object member and 'standardizes' the key set of every input -// argument through a caLLback function. -// -// This is useful for processing options with dashes in their key, e.g. `token-stack` --> tokenStack. -function typal_camel_mix(cb) { - var self = this; - var i, o, k; - - // Convert dashed option keys to Camel Case, e.g. `camelCase('camels-have-one-hump')` => `'camelsHaveOneHump'` - function camelCase(s) { - return s.replace(/-\w/g, function (match) { - return match.charAt(1).toUpperCase(); - }); - } - - // Convert first character to lowercase - function lcase0(s) { - return s.replace(/^\w/, function (match) { - return match.toLowerCase(); - }); - } - - for (i = 1; i < arguments.length; i++) { - o = arguments[i]; - if (!o) continue; - if (Object.prototype.hasOwnProperty.call(o, 'constructor')) { - this.constructor = o.constructor; - } - if (Object.prototype.hasOwnProperty.call(o, 'toString')) { - this.toString = o.toString; - } - if (cb) { - o = cb(o); - } - for (k in o) { - if (Object.prototype.hasOwnProperty.call(o, k)) { - var nk = camelCase(k); - var match = k.match(position); - var key = k.replace(position, ''); - // This anticipates before/after members to be camelcased already, e.g. - // 'afterParse()' for layering 'parse()': - var alt_key = lcase0(key); - if (match && typeof this[key] === 'function') { - this[key] = layerMethod(match[0], key, this[key], o[k]); - } - else if (match && typeof this[alt_key] === 'function') { - this[alt_key] = layerMethod(match[0], alt_key, this[alt_key], o[k]); - } else { - this[nk] = o[k]; - } - } - } - } - return this; -} - -return { - // extend object with own properties of each argument - mix: typal_mix, - - camelMix: typal_camel_mix, - - // sugar for object begetting and mixing - // - Object.create(typal).mix(etc, etc); - // + typal.beget(etc, etc); - beget: function typal_beget() { - return arguments.length ? typal_mix.apply(create(this), arguments) : create(this); - }, - - // Creates a new Class function based on an object with a constructor method - construct: function typal_construct() { - var o = typal_mix.apply(create(this), arguments); - var constructor = o.constructor; - var Klass = o.constructor = function () { return constructor.apply(this, arguments); }; - Klass.prototype = o; - Klass.mix = typal_mix; // allow for easy singleton property extension - return Klass; - }, - - // no op - constructor: function typal_constructor() { return this; } -}; - -})(); - -if (typeof exports !== 'undefined') - exports.typal = typal; - -},{}],14:[function(require,module,exports){ -(function (global){ -'use strict'; - -// compare and isBuffer taken from https://github.com/feross/buffer/blob/680e9e5e488f22aac27599a57dc844a6315928dd/index.js -// original notice: - -/*! - * The buffer module from node.js, for the browser. - * - * @author Feross Aboukhadijeh - * @license MIT - */ -function compare(a, b) { - if (a === b) { - return 0; - } - - var x = a.length; - var y = b.length; - - for (var i = 0, len = Math.min(x, y); i < len; ++i) { - if (a[i] !== b[i]) { - x = a[i]; - y = b[i]; - break; - } - } - - if (x < y) { - return -1; - } - if (y < x) { - return 1; - } - return 0; -} -function isBuffer(b) { - if (global.Buffer && typeof global.Buffer.isBuffer === 'function') { - return global.Buffer.isBuffer(b); - } - return !!(b != null && b._isBuffer); -} - -// based on node assert, original notice: - -// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 -// -// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! -// -// Originally from narwhal.js (http://narwhaljs.org) -// Copyright (c) 2009 Thomas Robinson <280north.com> -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the 'Software'), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -var util = require('util/'); -var hasOwn = Object.prototype.hasOwnProperty; -var pSlice = Array.prototype.slice; -var functionsHaveNames = (function () { - return function foo() {}.name === 'foo'; -}()); -function pToString (obj) { - return Object.prototype.toString.call(obj); -} -function isView(arrbuf) { - if (isBuffer(arrbuf)) { - return false; - } - if (typeof global.ArrayBuffer !== 'function') { - return false; - } - if (typeof ArrayBuffer.isView === 'function') { - return ArrayBuffer.isView(arrbuf); - } - if (!arrbuf) { - return false; - } - if (arrbuf instanceof DataView) { - return true; - } - if (arrbuf.buffer && arrbuf.buffer instanceof ArrayBuffer) { - return true; - } - return false; -} -// 1. The assert module provides functions that throw -// AssertionError's when particular conditions are not met. The -// assert module must conform to the following interface. - -var assert = module.exports = ok; - -// 2. The AssertionError is defined in assert. -// new assert.AssertionError({ message: message, -// actual: actual, -// expected: expected }) - -var regex = /\s*function\s+([^\(\s]*)\s*/; -// based on https://github.com/ljharb/function.prototype.name/blob/adeeeec8bfcc6068b187d7d9fb3d5bb1d3a30899/implementation.js -function getName(func) { - if (!util.isFunction(func)) { - return; - } - if (functionsHaveNames) { - return func.name; - } - var str = func.toString(); - var match = str.match(regex); - return match && match[1]; -} -assert.AssertionError = function AssertionError(options) { - this.name = 'AssertionError'; - this.actual = options.actual; - this.expected = options.expected; - this.operator = options.operator; - if (options.message) { - this.message = options.message; - this.generatedMessage = false; - } else { - this.message = getMessage(this); - this.generatedMessage = true; - } - var stackStartFunction = options.stackStartFunction || fail; - if (Error.captureStackTrace) { - Error.captureStackTrace(this, stackStartFunction); - } else { - // non v8 browsers so we can have a stacktrace - var err = new Error(); - if (err.stack) { - var out = err.stack; - - // try to strip useless frames - var fn_name = getName(stackStartFunction); - var idx = out.indexOf('\n' + fn_name); - if (idx >= 0) { - // once we have located the function frame - // we need to strip out everything before it (and its line) - var next_line = out.indexOf('\n', idx + 1); - out = out.substring(next_line + 1); - } - - this.stack = out; - } - } -}; - -// assert.AssertionError instanceof Error -util.inherits(assert.AssertionError, Error); - -function truncate(s, n) { - if (typeof s === 'string') { - return s.length < n ? s : s.slice(0, n); - } else { - return s; - } -} -function inspect(something) { - if (functionsHaveNames || !util.isFunction(something)) { - return util.inspect(something); - } - var rawname = getName(something); - var name = rawname ? ': ' + rawname : ''; - return '[Function' + name + ']'; -} -function getMessage(self) { - return truncate(inspect(self.actual), 128) + ' ' + - self.operator + ' ' + - truncate(inspect(self.expected), 128); -} - -// At present only the three keys mentioned above are used and -// understood by the spec. Implementations or sub modules can pass -// other keys to the AssertionError's constructor - they will be -// ignored. - -// 3. All of the following functions must throw an AssertionError -// when a corresponding condition is not met, with a message that -// may be undefined if not provided. All assertion methods provide -// both the actual and expected values to the assertion error for -// display purposes. - -function fail(actual, expected, message, operator, stackStartFunction) { - throw new assert.AssertionError({ - message: message, - actual: actual, - expected: expected, - operator: operator, - stackStartFunction: stackStartFunction - }); -} - -// EXTENSION! allows for well behaved errors defined elsewhere. -assert.fail = fail; - -// 4. Pure assertion tests whether a value is truthy, as determined -// by !!guard. -// assert.ok(guard, message_opt); -// This statement is equivalent to assert.equal(true, !!guard, -// message_opt);. To test strictly for the value true, use -// assert.strictEqual(true, guard, message_opt);. - -function ok(value, message) { - if (!value) fail(value, true, message, '==', assert.ok); -} -assert.ok = ok; - -// 5. The equality assertion tests shallow, coercive equality with -// ==. -// assert.equal(actual, expected, message_opt); - -assert.equal = function equal(actual, expected, message) { - if (actual != expected) fail(actual, expected, message, '==', assert.equal); -}; - -// 6. The non-equality assertion tests for whether two objects are not equal -// with != assert.notEqual(actual, expected, message_opt); - -assert.notEqual = function notEqual(actual, expected, message) { - if (actual == expected) { - fail(actual, expected, message, '!=', assert.notEqual); - } -}; - -// 7. The equivalence assertion tests a deep equality relation. -// assert.deepEqual(actual, expected, message_opt); - -assert.deepEqual = function deepEqual(actual, expected, message) { - if (!_deepEqual(actual, expected, false)) { - fail(actual, expected, message, 'deepEqual', assert.deepEqual); - } -}; - -assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) { - if (!_deepEqual(actual, expected, true)) { - fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual); - } -}; - -function _deepEqual(actual, expected, strict, memos) { - // 7.1. All identical values are equivalent, as determined by ===. - if (actual === expected) { - return true; - } else if (isBuffer(actual) && isBuffer(expected)) { - return compare(actual, expected) === 0; - - // 7.2. If the expected value is a Date object, the actual value is - // equivalent if it is also a Date object that refers to the same time. - } else if (util.isDate(actual) && util.isDate(expected)) { - return actual.getTime() === expected.getTime(); - - // 7.3 If the expected value is a RegExp object, the actual value is - // equivalent if it is also a RegExp object with the same source and - // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). - } else if (util.isRegExp(actual) && util.isRegExp(expected)) { - return actual.source === expected.source && - actual.global === expected.global && - actual.multiline === expected.multiline && - actual.lastIndex === expected.lastIndex && - actual.ignoreCase === expected.ignoreCase; - - // 7.4. Other pairs that do not both pass typeof value == 'object', - // equivalence is determined by ==. - } else if ((actual === null || typeof actual !== 'object') && - (expected === null || typeof expected !== 'object')) { - return strict ? actual === expected : actual == expected; - - // If both values are instances of typed arrays, wrap their underlying - // ArrayBuffers in a Buffer each to increase performance - // This optimization requires the arrays to have the same type as checked by - // Object.prototype.toString (aka pToString). Never perform binary - // comparisons for Float*Arrays, though, since e.g. +0 === -0 but their - // bit patterns are not identical. - } else if (isView(actual) && isView(expected) && - pToString(actual) === pToString(expected) && - !(actual instanceof Float32Array || - actual instanceof Float64Array)) { - return compare(new Uint8Array(actual.buffer), - new Uint8Array(expected.buffer)) === 0; - - // 7.5 For all other Object pairs, including Array objects, equivalence is - // determined by having the same number of owned properties (as verified - // with Object.prototype.hasOwnProperty.call), the same set of keys - // (although not necessarily the same order), equivalent values for every - // corresponding key, and an identical 'prototype' property. Note: this - // accounts for both named and indexed properties on Arrays. - } else if (isBuffer(actual) !== isBuffer(expected)) { - return false; - } else { - memos = memos || {actual: [], expected: []}; - - var actualIndex = memos.actual.indexOf(actual); - if (actualIndex !== -1) { - if (actualIndex === memos.expected.indexOf(expected)) { - return true; - } - } - - memos.actual.push(actual); - memos.expected.push(expected); - - return objEquiv(actual, expected, strict, memos); - } -} - -function isArguments(object) { - return Object.prototype.toString.call(object) == '[object Arguments]'; -} - -function objEquiv(a, b, strict, actualVisitedObjects) { - if (a === null || a === undefined || b === null || b === undefined) - return false; - // if one is a primitive, the other must be same - if (util.isPrimitive(a) || util.isPrimitive(b)) - return a === b; - if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) - return false; - var aIsArgs = isArguments(a); - var bIsArgs = isArguments(b); - if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) - return false; - if (aIsArgs) { - a = pSlice.call(a); - b = pSlice.call(b); - return _deepEqual(a, b, strict); - } - var ka = objectKeys(a); - var kb = objectKeys(b); - var key, i; - // having the same number of owned properties (keys incorporates - // hasOwnProperty) - if (ka.length !== kb.length) - return false; - //the same set of keys (although not necessarily the same order), - ka.sort(); - kb.sort(); - //~~~cheap key test - for (i = ka.length - 1; i >= 0; i--) { - if (ka[i] !== kb[i]) - return false; - } - //equivalent values for every corresponding key, and - //~~~possibly expensive deep test - for (i = ka.length - 1; i >= 0; i--) { - key = ka[i]; - if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects)) - return false; - } - return true; -} - -// 8. The non-equivalence assertion tests for any deep inequality. -// assert.notDeepEqual(actual, expected, message_opt); - -assert.notDeepEqual = function notDeepEqual(actual, expected, message) { - if (_deepEqual(actual, expected, false)) { - fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); - } -}; - -assert.notDeepStrictEqual = notDeepStrictEqual; -function notDeepStrictEqual(actual, expected, message) { - if (_deepEqual(actual, expected, true)) { - fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual); - } -} - - -// 9. The strict equality assertion tests strict equality, as determined by ===. -// assert.strictEqual(actual, expected, message_opt); - -assert.strictEqual = function strictEqual(actual, expected, message) { - if (actual !== expected) { - fail(actual, expected, message, '===', assert.strictEqual); - } -}; - -// 10. The strict non-equality assertion tests for strict inequality, as -// determined by !==. assert.notStrictEqual(actual, expected, message_opt); - -assert.notStrictEqual = function notStrictEqual(actual, expected, message) { - if (actual === expected) { - fail(actual, expected, message, '!==', assert.notStrictEqual); - } -}; - -function expectedException(actual, expected) { - if (!actual || !expected) { - return false; - } - - if (Object.prototype.toString.call(expected) == '[object RegExp]') { - return expected.test(actual); - } - - try { - if (actual instanceof expected) { - return true; - } - } catch (e) { - // Ignore. The instanceof check doesn't work for arrow functions. - } - - if (Error.isPrototypeOf(expected)) { - return false; - } - - return expected.call({}, actual) === true; -} - -function _tryBlock(block) { - var error; - try { - block(); - } catch (e) { - error = e; - } - return error; -} - -function _throws(shouldThrow, block, expected, message) { - var actual; - - if (typeof block !== 'function') { - throw new TypeError('"block" argument must be a function'); - } - - if (typeof expected === 'string') { - message = expected; - expected = null; - } - - actual = _tryBlock(block); - - message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + - (message ? ' ' + message : '.'); - - if (shouldThrow && !actual) { - fail(actual, expected, 'Missing expected exception' + message); - } - - var userProvidedMessage = typeof message === 'string'; - var isUnwantedException = !shouldThrow && util.isError(actual); - var isUnexpectedException = !shouldThrow && actual && !expected; - - if ((isUnwantedException && - userProvidedMessage && - expectedException(actual, expected)) || - isUnexpectedException) { - fail(actual, expected, 'Got unwanted exception' + message); - } - - if ((shouldThrow && actual && expected && - !expectedException(actual, expected)) || (!shouldThrow && actual)) { - throw actual; - } -} - -// 11. Expected to throw an error: -// assert.throws(block, Error_opt, message_opt); - -assert.throws = function(block, /*optional*/error, /*optional*/message) { - _throws(true, block, error, message); -}; - -// EXTENSION! This is annoying to write outside this module. -assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { - _throws(false, block, error, message); -}; - -assert.ifError = function(err) { if (err) throw err; }; - -var objectKeys = Object.keys || function (obj) { - var keys = []; - for (var key in obj) { - if (hasOwn.call(obj, key)) keys.push(key); - } - return keys; -}; - -}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"util/":21}],15:[function(require,module,exports){ - -},{}],16:[function(require,module,exports){ -// json5.js -// JSON for Humans. See README.md for details. -// -// This file is based directly off of Douglas Crockford's json_parse.js: -// https://github.com/douglascrockford/JSON-js/blob/master/json_parse.js - -var JSON5 = (typeof exports === 'object' ? exports : {}); - -JSON5.parse = (function () { - "use strict"; - -// This is a function that can parse a JSON5 text, producing a JavaScript -// data structure. It is a simple, recursive descent parser. It does not use -// eval or regular expressions, so it can be used as a model for implementing -// a JSON5 parser in other languages. - -// We are defining the function inside of another function to avoid creating -// global variables. - - var at, // The index of the current character - lineNumber, // The current line number - columnNumber, // The current column number - ch, // The current character - escapee = { - "'": "'", - '"': '"', - '\\': '\\', - '/': '/', - '\n': '', // Replace escaped newlines in strings w/ empty string - 0: '\0', - b: '\b', - f: '\f', - n: '\n', - r: '\r', - t: '\t', - v: '\v' - }, - escapee_4_multiline = { - '`': '`', - '\\': '\\' - }, - ws = [ - ' ', - '\t', - '\r', - '\n', - '\v', - '\f', - '\xA0', - '\uFEFF' - ], - text, - - renderChar = function (chr) { - return chr === '' ? 'EOF' : "'" + chr + "'"; - }, - - error = function (m) { - -// Call error when something is wrong. - - var error = new SyntaxError(); - // beginning of message suffix to agree with that provided by Gecko - see https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse - error.message = m + " at line " + lineNumber + " column " + columnNumber + " of the JSON5 data. Still to read: " + JSON.stringify(text.substring(at - 1, at + 19)); - error.at = at; - // These two property names have been chosen to agree with the ones in Gecko, the only popular - // environment which seems to supply this info on JSON.parse - error.lineNumber = lineNumber; - error.columnNumber = columnNumber; - throw error; - }, - - next = function (c) { - -// If a c parameter is provided, verify that it matches the current character. - - if (c && c !== ch) { - error("Expected " + renderChar(c) + " instead of " + renderChar(ch)); - } - -// Get the next character. When there are no more characters, -// return the empty string. - - ch = text.charAt(at); - at++; - columnNumber++; - if (ch === '\n' || ch === '\r' && peek() !== '\n') { - lineNumber++; - columnNumber = 0; - } - return ch; - }, - - peek = function () { - -// Get the next character without consuming it or -// assigning it to the ch varaible. - - return text.charAt(at); - }, - - identifier = function () { - -// Parse an identifier. Normally, reserved words are disallowed here, but we -// only use this for unquoted object keys, where reserved words are allowed, -// so we don't check for those here. References: -// - http://es5.github.com/#x7.6 -// - https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Core_Language_Features#Variables -// - http://docstore.mik.ua/orelly/webprog/jscript/ch02_07.htm -// TODO Identifiers can have Unicode "letters" in them; add support for those. - - var key = ch; - - // Identifiers must start with a letter, _ or $. - if ((ch !== '_' && ch !== '$') && - (ch < 'a' || ch > 'z') && - (ch < 'A' || ch > 'Z')) { - error("Bad identifier as unquoted key"); - } - - // Subsequent characters can contain digits. - while (next() && ( - ch === '_' || ch === '$' || - (ch >= 'a' && ch <= 'z') || - (ch >= 'A' && ch <= 'Z') || - (ch >= '0' && ch <= '9'))) { - key += ch; - } - - return key; - }, - - number = function () { - -// Parse a number value. - - var number, - sign = '', - string = '', - base = 10; - - if (ch === '-' || ch === '+') { - sign = ch; - next(ch); - } - - // support for Infinity (could tweak to allow other words): - if (ch === 'I') { - number = word(); - if (typeof number !== 'number' || isNaN(number)) { - error('Unexpected word for number'); - } - return (sign === '-') ? -number : number; - } - - // support for NaN - if (ch === 'N' ) { - number = word(); - if (!isNaN(number)) { - error('expected word to be NaN'); - } - // ignore sign as -NaN also is NaN - return number; - } - - if (ch === '0') { - string += ch; - next(); - if (ch === 'x' || ch === 'X') { - string += ch; - next(); - base = 16; - } else if (ch >= '0' && ch <= '9') { - error('Octal literal'); - } - } - - switch (base) { - case 10: - while (ch >= '0' && ch <= '9' ) { - string += ch; - next(); - } - if (ch === '.') { - string += '.'; - while (next() && ch >= '0' && ch <= '9') { - string += ch; - } - } - if (ch === 'e' || ch === 'E') { - string += ch; - next(); - if (ch === '-' || ch === '+') { - string += ch; - next(); - } - while (ch >= '0' && ch <= '9') { - string += ch; - next(); - } - } - break; - case 16: - while (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'f') { - string += ch; - next(); - } - break; - } - - if(sign === '-') { - number = -string; - } else { - number = +string; - } - - if (!isFinite(number)) { - error("Bad number"); - } else { - return number; - } - }, - - string = function () { - -// Parse a string value. - - var hex, - i, - string = '', - delim, // double quote or single quote - uffff, - xff, - is_basic_str, - escapes; - -// When parsing for string values, we must look for ', ", ` and \ characters. - - is_basic_str = (ch === '"' || ch === "'"); - if (is_basic_str || ch === '`') { - escapes = (is_basic_str ? escapee : escapee_4_multiline); - delim = ch; - while (next()) { - if (ch === delim) { - next(); - return string; - } else if (ch === '\\') { - next(); - if (ch === 'u') { - uffff = 0; - for (i = 0; i < 4; i += 1) { - hex = parseInt(next(), 16); - if (!isFinite(hex)) { - break; - } - uffff = uffff * 16 + hex; - } - string += String.fromCharCode(uffff); - // TODO: add \u{fffff} support for *Unicode Code Points* as per ES2017 - } else if (ch === 'x') { - xff = 0; - for (i = 0; i < 2; i += 1) { - hex = parseInt(next(), 16); - if (!isFinite(hex)) { - break; - } - xff = xff * 16 + hex; - } - string += String.fromCharCode(xff); - } else if (ch === '\r') { - if (peek() === '\n') { - next(); - } - // CR and CRLF get transformed to LF when the string being parsed is a `string template` type. - // We also keep the `\\` at the end of line as it's an unrecognized escape in that mode. - if (!is_basic_str) { - string += '\\\n'; - } - } else if (ch >= '1' && ch <= '7') { - // since octal literals are not supported, - // octal escapes in strings are not either. - // - // While https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Template_literals - // says otherwise, we still DO NOT support octal escapes in JSON5 '`'-delimited multiline - // strings, as these ARE NOT identical to JavaScript 'template strings' as we DO NOT - // intend to support the `${...}` template variable expansion feature either! - break; - } else if (typeof escapes[ch] === 'string') { - string += escapes[ch]; - } else if (is_basic_str) { - // javascript treats the '\' char as an escape for any character in a classic string - string += ch; - } else { - // in '`'-delimited strings, we only accept \u, \x, \` and \\ escapes; - // everyhing else is copied verbatim - string += '\\' + ch; - } - } else if (is_basic_str && ch === '\n') { - // unescaped newlines are invalid; see: - // https://github.com/json5/json5/issues/24 - // - // TODO: this feels special-cased; are there other - // invalid unescaped chars? - break; - } else if (ch === '\r') { - if (peek() === '\n') { - next(); - } - // CR and CRLF get transformed to LF when the string being parsed is a `string template` type. - if (!is_basic_str) { - string += '\n'; - } else { - // unescaped newlines are invalid; see: - // https://github.com/json5/json5/issues/24 - // - // TODO: this feels special-cased; are there other - // invalid unescaped chars? - break; - } - } else { - string += ch; - } - } - } - error("Bad string"); - }, - - heredoc = function () { - -// Parse a heredoc (string) value. - - var i, - string, - delim, - input, - m, - l, - offset, - chkstr_e, - chkstr_s; - -// When parsing for heredoc values, we must extract the EOT marker before anything else. -// Once we've done that, we skip the first newline and start scanning/consuming heredoc -// content until we hit the EOT marker on a line by itself, sans whitespace. -// -// By convention we do not accept 'formatting whitespace/indentation' before the EOT -// marker on the same line. - - input = text.substring(at); - // we accept 2 or more(!) `<` characters to mark the start of a heredoc chunk: - m = /(<+)([^\s\r\n<>,"'\/\[\]\{\}]+)(\r?\n|\r\n?)/.exec(input); - if (!m) { - error("Expected heredoc starting EOT marker to immediately follow the initial << or <<<"); - } - //offset = m[1].length; - delim = m[2]; - - // strip off start marker including CRLF: - l = m[0].length; - input = input.substring(l); - - at += l; - columnNumber = 0; - - // scan for first occurrence of the lone EOT marker (which can really be ANYTHING - // as long as the ANYTHING isn't whitespace!): - for (i = 0, l = delim.length; ; i = offset + 1) { - offset = input.indexOf(delim, i); - if (offset < 0) { - error("Expected heredoc terminating EOT marker \"" + delim + "\" on a line by itself (sans whitespace)"); - } - // now check if located EOT delimiter is on a line by its lonesome: - if (offset < 1) continue; - chkstr_e = input.substring(offset + l); - if (chkstr_e.length && chkstr_e[0] !== '\r' && chkstr_e[0] !== '\n') { - // we don't tolerate any whitespace trailing the EOT marker: - // it must be the only item on the line! - // - // Note: however, we DO TOLERATE a comma field separator following - // on the same line à la BASH, while we also accept any trailing - // whitespace as per the JSON5 spec for all other field formats - // in an object/array: - if (!chkstr_e.match(/^\s*,\s*(?:\r?\n|\r\n?)/)) { - continue; - } else { - // trailing comma found: bump skip length accordingly - } - } - chkstr_s = input.substr(offset - 2, 2); - if (chkstr_s[1] !== '\r' && chkstr_s[1] !== '\n') { - // we don't tolerate any whitespace leading the EOT marker: - // it must be at the start of a new line! - continue; - } else if (chkstr_s[1] === '\n' && chkstr_s[0] === '\r') { - // found leading CRLF: ignore it entirely - i = offset - 2; - } else { - // found leading CR or LF: ignore it - i = offset - 1; - } - - // found a legal EOT marker! `i` is now index of first non-content character. - // - // Hence we now may extract the heredoc'ed content as a string, - // ignoring the preceding CR/LF/CRLF: - string = input.substring(0, i); - - // and we jump over the EOT marker: - offset += l; - // we DO NOT jump over the trailing optional comma and/or trailing CR/LF/CRLF - // as those are expected to be parsed by the outer call: when parsing a heredoc - // string in an array or object specifically, the outer call EXPECTS to see - // that trailing comma in global `ch` or it will barf a hairball on the next - // element! - - at += offset; - columnNumber = l; - - // count the number of lines in the extracted string: - var lines = (string.match(/\r?\n|\r\n?/g) || []); - lineNumber += lines.length + 2; // add the skipped newlines at start and end of heredoc: 1 at start, 1 at the end, not counting the trailing EOL as that one isn't skipped yet! - - // also make sure `ch` is primed a la `next()` API: - ch = text.charAt(at); - next(); - break; - } - - return string; - }, - - inlineComment = function () { - -// Skip an inline comment, assuming this is one. The current character should -// be the second / character in the // pair that begins this inline comment. -// To finish the inline comment, we look for a newline or the end of the text. - - if (ch !== '/') { - error("Not an inline comment"); - } - - do { - next(); - if (ch === '\n' || ch === '\r') { - next(); - return; - } - } while (ch); - }, - - blockComment = function () { - -// Skip a block comment, assuming this is one. The current character should be -// the * character in the /* pair that begins this block comment. -// To finish the block comment, we look for an ending */ pair of characters, -// but we also watch for the end of text before the comment is terminated. - - if (ch !== '*') { - error("Not a block comment"); - } - - do { - next(); - while (ch === '*') { - next('*'); - if (ch === '/') { - next('/'); - return; - } - } - } while (ch); - - error("Unterminated block comment"); - }, - - comment = function () { - -// Skip a comment, whether inline or block-level, assuming this is one. -// Comments always begin with a / character. - - if (ch !== '/') { - error("Not a comment"); - } - - next('/'); - - if (ch === '/') { - inlineComment(); - } else if (ch === '*') { - blockComment(); - } else { - error("Unrecognized comment"); - } - }, - - white = function () { - -// Skip whitespace and comments. -// Note that we're detecting comments by only a single / character. -// This works since regular expressions are not valid JSON(5), but this will -// break if there are other valid values that begin with a / character! - - while (ch) { - if (ch === '/') { - comment(); - } else if (ws.indexOf(ch) >= 0) { - next(); - } else { - return; - } - } - }, - - word = function () { - -// true, false, or null. - - switch (ch) { - case 't': - next('t'); - next('r'); - next('u'); - next('e'); - return true; - case 'f': - next('f'); - next('a'); - next('l'); - next('s'); - next('e'); - return false; - case 'n': - next('n'); - next('u'); - next('l'); - next('l'); - return null; - case 'I': - next('I'); - next('n'); - next('f'); - next('i'); - next('n'); - next('i'); - next('t'); - next('y'); - return Infinity; - case 'N': - next( 'N' ); - next( 'a' ); - next( 'N' ); - return NaN; - } - error("Unexpected " + renderChar(ch)); - }, - - value, // Place holder for the value function. - - array = function () { - -// Parse an array value. - - var array = []; - - if (ch === '[') { - next('['); - white(); - while (ch) { - if (ch === ']') { - next(']'); - return array; // Potentially empty array - } - // ES5 allows omitting elements in arrays, e.g. [,] and - // [,null]. We don't allow this in JSON5. - if (ch === ',') { - error("Missing array element"); - } else { - array.push(value()); - } - white(); - // If there's no comma after this value, this needs to - // be the end of the array. - if (ch !== ',') { - next(']'); - return array; - } - next(','); - white(); - } - } - error("Bad array"); - }, - - object = function () { - -// Parse an object value. - - var key, - object = {}; - - if (ch === '{') { - next('{'); - white(); - while (ch) { - if (ch === '}') { - next('}'); - return object; // Potentially empty object - } - - // Keys can be unquoted. If they are, they need to be - // valid JS identifiers. - if (ch === '"' || ch === "'") { - key = string(); - } else { - key = identifier(); - } - - white(); - next(':'); - object[key] = value(); - white(); - // If there's no comma after this pair, this needs to be - // the end of the object. - if (ch !== ',') { - next('}'); - return object; - } - next(','); - white(); - } - } - error("Bad object"); - }; - - value = function () { - -// Parse a JSON value. It could be an object, an array, a string, a number, -// or a word. - - white(); - switch (ch) { - case '{': - return object(); - case '[': - return array(); - case '"': - case "'": - case "`": - return string(); - case "<": - return heredoc(); - case '-': - case '+': - case '.': - return number(); - default: - return ch >= '0' && ch <= '9' ? number() : word(); - } - }; - -// Return the json_parse function. It will have access to all of the above -// functions and variables. - - return function json_parse(source, reviver) { - var result; - - text = String(source); - at = 0; - lineNumber = 1; - columnNumber = 1; - ch = ' '; - result = value(); - white(); - if (ch) { - error("Syntax error"); - } - -// If there is a reviver function, we recursively walk the new structure, -// passing each name/value pair to the reviver function for possible -// transformation, starting with a temporary root object that holds the result -// in an empty key. If there is not a reviver function, we simply return the -// result. - - return typeof reviver === 'function' ? (function walk(holder, key) { - var k, v, value = holder[key]; - if (value && typeof value === 'object') { - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = walk(value, k); - if (v !== undefined) { - value[k] = v; - } else { - delete value[k]; - } - } - } - } - return reviver.call(holder, key, value); - }({'': result}, '')) : result; - }; -}()); - -// JSON5 stringify will not quote keys where appropriate -JSON5.stringify = function (obj, replacer, space) { - if (replacer && (typeof(replacer) !== "function" && !isArray(replacer))) { - throw new Error('Replacer must be a function or an array'); - } - var getReplacedValueOrUndefined = function(holder, key, isTopLevel) { - var value = holder[key]; - - // Replace the value with its toJSON value first, if possible - if (value && value.toJSON && typeof value.toJSON === "function") { - value = value.toJSON(); - } - - // If the user-supplied replacer if a function, call it. If it's an array, check objects' string keys for - // presence in the array (removing the key/value pair from the resulting JSON if the key is missing). - if (typeof(replacer) === "function") { - return replacer.call(holder, key, value); - } else if(replacer) { - if (isTopLevel || isArray(holder) || replacer.indexOf(key) >= 0) { - return value; - } else { - return undefined; - } - } else { - return value; - } - }; - - function isWordChar(c) { - return (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || - c === '_' || c === '$'; - } - - function isWordStart(c) { - return (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - c === '_' || c === '$'; - } - - function isWord(key) { - if (typeof key !== 'string') { - return false; - } - if (!isWordStart(key[0])) { - return false; - } - var i = 1, length = key.length; - while (i < length) { - if (!isWordChar(key[i])) { - return false; - } - i++; - } - return true; - } - - // export for use in tests - JSON5.isWord = isWord; - - // polyfills - function isArray(obj) { - if (Array.isArray) { - return Array.isArray(obj); - } else { - return Object.prototype.toString.call(obj) === '[object Array]'; - } - } - - function isDate(obj) { - return Object.prototype.toString.call(obj) === '[object Date]'; - } - - var objStack = []; - function checkForCircular(obj) { - for (var i = 0; i < objStack.length; i++) { - if (objStack[i] === obj) { - throw new TypeError("Converting circular structure to JSON"); - } - } - } - - function makeIndent(str, num, noNewLine) { - if (!str) { - return ""; - } - // indentation no more than 10 chars - if (str.length > 10) { - str = str.substring(0, 10); - } - - var indent = noNewLine ? "" : "\n"; - for (var i = 0; i < num; i++) { - indent += str; - } - - return indent; - } - - var indentStr; - if (space) { - if (typeof space === "string") { - indentStr = space; - } else if (typeof space === "number" && space >= 0) { - indentStr = makeIndent(" ", space, true); - } else { - // ignore space parameter - } - } - - // Copied from Crokford's implementation of JSON - // See https://github.com/douglascrockford/JSON-js/blob/e39db4b7e6249f04a195e7dd0840e610cc9e941e/json2.js#L195 - // Begin - var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - meta = { // table of character substitutions - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '"' : '\\"', - '\\': '\\\\' - }; - - function escapeString(string) { - -// If the string contains no control characters, no quote characters, and no -// backslash characters, then we can safely slap some quotes around it. -// Otherwise we must also replace the offending characters with safe escape -// sequences. - escapable.lastIndex = 0; - return escapable.test(string) ? '"' + string.replace(escapable, function (a) { - var c = meta[a]; - return typeof c === 'string' ? - c : - '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }) + '"' : '"' + string + '"'; - } - // End - - function internalStringify(holder, key, isTopLevel) { - var buffer, res; - - // Replace the value, if necessary - var obj_part = getReplacedValueOrUndefined(holder, key, isTopLevel); - - if (obj_part && !isDate(obj_part)) { - // unbox objects - // don't unbox dates, since will turn it into number - obj_part = obj_part.valueOf(); - } - switch(typeof obj_part) { - case "boolean": - return obj_part.toString(); - - case "number": - if (isNaN(obj_part) || !isFinite(obj_part)) { - return "null"; - } - return obj_part.toString(); - - case "string": - return escapeString(obj_part.toString()); - - case "object": - if (obj_part === null) { - return "null"; - } else if (isArray(obj_part)) { - checkForCircular(obj_part); - buffer = "["; - objStack.push(obj_part); - - for (var i = 0; i < obj_part.length; i++) { - res = internalStringify(obj_part, i, false); - buffer += makeIndent(indentStr, objStack.length); - if (res === null || typeof res === "undefined") { - buffer += "null"; - } else { - buffer += res; - } - if (i < obj_part.length-1) { - buffer += ","; - } else if (indentStr) { - buffer += "\n"; - } - } - objStack.pop(); - if (obj_part.length) { - buffer += makeIndent(indentStr, objStack.length, true) - } - buffer += "]"; - } else { - checkForCircular(obj_part); - buffer = "{"; - var nonEmpty = false; - objStack.push(obj_part); - for (var prop in obj_part) { - if (obj_part.hasOwnProperty(prop)) { - var value = internalStringify(obj_part, prop, false); - isTopLevel = false; - if (typeof value !== "undefined" && value !== null) { - buffer += makeIndent(indentStr, objStack.length); - nonEmpty = true; - key = isWord(prop) ? prop : escapeString(prop); - buffer += key + ":" + (indentStr ? ' ' : '') + value + ","; - } - } - } - objStack.pop(); - if (nonEmpty) { - buffer = buffer.substring(0, buffer.length-1) + makeIndent(indentStr, objStack.length) + "}"; - } else { - buffer = '{}'; - } - } - return buffer; - default: - // functions and undefined should be ignored - return undefined; - } - } - - // special case...when undefined is used inside of - // a compound object/array, return null. - // but when top-level, return undefined - var topLevelHolder = {"":obj}; - if (obj === undefined) { - return getReplacedValueOrUndefined(topLevelHolder, '', true); - } - return internalStringify(topLevelHolder, '', true); -}; - -},{}],17:[function(require,module,exports){ -(function (process){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// resolves . and .. elements in a path array with directory names there -// must be no slashes, empty elements, or device names (c:\) in the array -// (so also no leading and trailing slashes - it does not distinguish -// relative and absolute paths) -function normalizeArray(parts, allowAboveRoot) { - // if the path tries to go above the root, `up` ends up > 0 - var up = 0; - for (var i = parts.length - 1; i >= 0; i--) { - var last = parts[i]; - if (last === '.') { - parts.splice(i, 1); - } else if (last === '..') { - parts.splice(i, 1); - up++; - } else if (up) { - parts.splice(i, 1); - up--; - } - } - - // if the path is allowed to go above the root, restore leading ..s - if (allowAboveRoot) { - for (; up--; up) { - parts.unshift('..'); - } - } - - return parts; -} - -// Split a filename into [root, dir, basename, ext], unix version -// 'root' is just a slash, or nothing. -var splitPathRe = - /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; -var splitPath = function(filename) { - return splitPathRe.exec(filename).slice(1); -}; - -// path.resolve([from ...], to) -// posix version -exports.resolve = function() { - var resolvedPath = '', - resolvedAbsolute = false; - - for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { - var path = (i >= 0) ? arguments[i] : process.cwd(); - - // Skip empty and invalid entries - if (typeof path !== 'string') { - throw new TypeError('Arguments to path.resolve must be strings'); - } else if (!path) { - continue; - } - - resolvedPath = path + '/' + resolvedPath; - resolvedAbsolute = path.charAt(0) === '/'; - } - - // At this point the path should be resolved to a full absolute path, but - // handle relative paths to be safe (might happen when process.cwd() fails) - - // Normalize the path - resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) { - return !!p; - }), !resolvedAbsolute).join('/'); - - return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; -}; - -// path.normalize(path) -// posix version -exports.normalize = function(path) { - var isAbsolute = exports.isAbsolute(path), - trailingSlash = substr(path, -1) === '/'; - - // Normalize the path - path = normalizeArray(filter(path.split('/'), function(p) { - return !!p; - }), !isAbsolute).join('/'); - - if (!path && !isAbsolute) { - path = '.'; - } - if (path && trailingSlash) { - path += '/'; - } - - return (isAbsolute ? '/' : '') + path; -}; - -// posix version -exports.isAbsolute = function(path) { - return path.charAt(0) === '/'; -}; - -// posix version -exports.join = function() { - var paths = Array.prototype.slice.call(arguments, 0); - return exports.normalize(filter(paths, function(p, index) { - if (typeof p !== 'string') { - throw new TypeError('Arguments to path.join must be strings'); - } - return p; - }).join('/')); -}; - - -// path.relative(from, to) -// posix version -exports.relative = function(from, to) { - from = exports.resolve(from).substr(1); - to = exports.resolve(to).substr(1); - - function trim(arr) { - var start = 0; - for (; start < arr.length; start++) { - if (arr[start] !== '') break; - } - - var end = arr.length - 1; - for (; end >= 0; end--) { - if (arr[end] !== '') break; - } - - if (start > end) return []; - return arr.slice(start, end - start + 1); - } - - var fromParts = trim(from.split('/')); - var toParts = trim(to.split('/')); - - var length = Math.min(fromParts.length, toParts.length); - var samePartsLength = length; - for (var i = 0; i < length; i++) { - if (fromParts[i] !== toParts[i]) { - samePartsLength = i; - break; - } - } - - var outputParts = []; - for (var i = samePartsLength; i < fromParts.length; i++) { - outputParts.push('..'); - } - - outputParts = outputParts.concat(toParts.slice(samePartsLength)); - - return outputParts.join('/'); -}; - -exports.sep = '/'; -exports.delimiter = ':'; - -exports.dirname = function(path) { - var result = splitPath(path), - root = result[0], - dir = result[1]; - - if (!root && !dir) { - // No dirname whatsoever - return '.'; - } - - if (dir) { - // It has a dirname, strip trailing slash - dir = dir.substr(0, dir.length - 1); - } - - return root + dir; -}; - - -exports.basename = function(path, ext) { - var f = splitPath(path)[2]; - // TODO: make this comparison case-insensitive on windows? - if (ext && f.substr(-1 * ext.length) === ext) { - f = f.substr(0, f.length - ext.length); - } - return f; -}; - - -exports.extname = function(path) { - return splitPath(path)[3]; -}; - -function filter (xs, f) { - if (xs.filter) return xs.filter(f); - var res = []; - for (var i = 0; i < xs.length; i++) { - if (f(xs[i], i, xs)) res.push(xs[i]); - } - return res; -} - -// String.prototype.substr - negative index don't work in IE8 -var substr = 'ab'.substr(-1) === 'b' - ? function (str, start, len) { return str.substr(start, len) } - : function (str, start, len) { - if (start < 0) start = str.length + start; - return str.substr(start, len); - } -; - -}).call(this,require('_process')) -},{"_process":18}],18:[function(require,module,exports){ -// shim for using process in browser -var process = module.exports = {}; - -// cached from whatever global is present so that test runners that stub it -// don't break things. But we need to wrap it in a try catch in case it is -// wrapped in strict mode code which doesn't define any globals. It's inside a -// function because try/catches deoptimize in certain engines. - -var cachedSetTimeout; -var cachedClearTimeout; - -function defaultSetTimout() { - throw new Error('setTimeout has not been defined'); -} -function defaultClearTimeout () { - throw new Error('clearTimeout has not been defined'); -} -(function () { - try { - if (typeof setTimeout === 'function') { - cachedSetTimeout = setTimeout; - } else { - cachedSetTimeout = defaultSetTimout; - } - } catch (e) { - cachedSetTimeout = defaultSetTimout; - } - try { - if (typeof clearTimeout === 'function') { - cachedClearTimeout = clearTimeout; - } else { - cachedClearTimeout = defaultClearTimeout; - } - } catch (e) { - cachedClearTimeout = defaultClearTimeout; - } -} ()) -function runTimeout(fun) { - if (cachedSetTimeout === setTimeout) { - //normal enviroments in sane situations - return setTimeout(fun, 0); - } - // if setTimeout wasn't available but was latter defined - if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { - cachedSetTimeout = setTimeout; - return setTimeout(fun, 0); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedSetTimeout(fun, 0); - } catch(e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedSetTimeout.call(null, fun, 0); - } catch(e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error - return cachedSetTimeout.call(this, fun, 0); - } - } - - -} -function runClearTimeout(marker) { - if (cachedClearTimeout === clearTimeout) { - //normal enviroments in sane situations - return clearTimeout(marker); - } - // if clearTimeout wasn't available but was latter defined - if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { - cachedClearTimeout = clearTimeout; - return clearTimeout(marker); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedClearTimeout(marker); - } catch (e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedClearTimeout.call(null, marker); - } catch (e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. - // Some versions of I.E. have different rules for clearTimeout vs setTimeout - return cachedClearTimeout.call(this, marker); - } - } - - - -} -var queue = []; -var draining = false; -var currentQueue; -var queueIndex = -1; - -function cleanUpNextTick() { - if (!draining || !currentQueue) { - return; - } - draining = false; - if (currentQueue.length) { - queue = currentQueue.concat(queue); - } else { - queueIndex = -1; - } - if (queue.length) { - drainQueue(); - } -} - -function drainQueue() { - if (draining) { - return; - } - var timeout = runTimeout(cleanUpNextTick); - draining = true; - - var len = queue.length; - while(len) { - currentQueue = queue; - queue = []; - while (++queueIndex < len) { - if (currentQueue) { - currentQueue[queueIndex].run(); - } - } - queueIndex = -1; - len = queue.length; - } - currentQueue = null; - draining = false; - runClearTimeout(timeout); -} - -process.nextTick = function (fun) { - var args = new Array(arguments.length - 1); - if (arguments.length > 1) { - for (var i = 1; i < arguments.length; i++) { - args[i - 1] = arguments[i]; - } - } - queue.push(new Item(fun, args)); - if (queue.length === 1 && !draining) { - runTimeout(drainQueue); - } -}; - -// v8 likes predictible objects -function Item(fun, array) { - this.fun = fun; - this.array = array; -} -Item.prototype.run = function () { - this.fun.apply(null, this.array); -}; -process.title = 'browser'; -process.browser = true; -process.env = {}; -process.argv = []; -process.version = ''; // empty string to avoid regexp issues -process.versions = {}; - -function noop() {} - -process.on = noop; -process.addListener = noop; -process.once = noop; -process.off = noop; -process.removeListener = noop; -process.removeAllListeners = noop; -process.emit = noop; -process.prependListener = noop; -process.prependOnceListener = noop; - -process.listeners = function (name) { return [] } - -process.binding = function (name) { - throw new Error('process.binding is not supported'); -}; - -process.cwd = function () { return '/' }; -process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); -}; -process.umask = function() { return 0; }; - -},{}],19:[function(require,module,exports){ -if (typeof Object.create === 'function') { - // implementation from standard node.js 'util' module - module.exports = function inherits(ctor, superCtor) { - ctor.super_ = superCtor - ctor.prototype = Object.create(superCtor.prototype, { - constructor: { - value: ctor, - enumerable: false, - writable: true, - configurable: true - } - }); - }; -} else { - // old school shim for old browsers - module.exports = function inherits(ctor, superCtor) { - ctor.super_ = superCtor - var TempCtor = function () {} - TempCtor.prototype = superCtor.prototype - ctor.prototype = new TempCtor() - ctor.prototype.constructor = ctor - } -} - -},{}],20:[function(require,module,exports){ -module.exports = function isBuffer(arg) { - return arg && typeof arg === 'object' - && typeof arg.copy === 'function' - && typeof arg.fill === 'function' - && typeof arg.readUInt8 === 'function'; -} -},{}],21:[function(require,module,exports){ -(function (process,global){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var formatRegExp = /%[sdj%]/g; -exports.format = function(f) { - if (!isString(f)) { - var objects = []; - for (var i = 0; i < arguments.length; i++) { - objects.push(inspect(arguments[i])); - } - return objects.join(' '); - } - - var i = 1; - var args = arguments; - var len = args.length; - var str = String(f).replace(formatRegExp, function(x) { - if (x === '%%') return '%'; - if (i >= len) return x; - switch (x) { - case '%s': return String(args[i++]); - case '%d': return Number(args[i++]); - case '%j': - try { - return JSON.stringify(args[i++]); - } catch (_) { - return '[Circular]'; - } - default: - return x; - } - }); - for (var x = args[i]; i < len; x = args[++i]) { - if (isNull(x) || !isObject(x)) { - str += ' ' + x; - } else { - str += ' ' + inspect(x); - } - } - return str; -}; - - -// Mark that a method should not be used. -// Returns a modified function which warns once by default. -// If --no-deprecation is set, then it is a no-op. -exports.deprecate = function(fn, msg) { - // Allow for deprecating things in the process of starting up. - if (isUndefined(global.process)) { - return function() { - return exports.deprecate(fn, msg).apply(this, arguments); - }; - } - - if (process.noDeprecation === true) { - return fn; - } - - var warned = false; - function deprecated() { - if (!warned) { - if (process.throwDeprecation) { - throw new Error(msg); - } else if (process.traceDeprecation) { - console.trace(msg); - } else { - console.error(msg); - } - warned = true; - } - return fn.apply(this, arguments); - } - - return deprecated; -}; - - -var debugs = {}; -var debugEnviron; -exports.debuglog = function(set) { - if (isUndefined(debugEnviron)) - debugEnviron = process.env.NODE_DEBUG || ''; - set = set.toUpperCase(); - if (!debugs[set]) { - if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { - var pid = process.pid; - debugs[set] = function() { - var msg = exports.format.apply(exports, arguments); - console.error('%s %d: %s', set, pid, msg); - }; - } else { - debugs[set] = function() {}; - } - } - return debugs[set]; -}; - - -/** - * Echos the value of a value. Trys to print the value out - * in the best way possible given the different types. - * - * @param {Object} obj The object to print out. - * @param {Object} opts Optional options object that alters the output. - */ -/* legacy: obj, showHidden, depth, colors*/ -function inspect(obj, opts) { - // default options - var ctx = { - seen: [], - stylize: stylizeNoColor - }; - // legacy... - if (arguments.length >= 3) ctx.depth = arguments[2]; - if (arguments.length >= 4) ctx.colors = arguments[3]; - if (isBoolean(opts)) { - // legacy... - ctx.showHidden = opts; - } else if (opts) { - // got an "options" object - exports._extend(ctx, opts); - } - // set default options - if (isUndefined(ctx.showHidden)) ctx.showHidden = false; - if (isUndefined(ctx.depth)) ctx.depth = 2; - if (isUndefined(ctx.colors)) ctx.colors = false; - if (isUndefined(ctx.customInspect)) ctx.customInspect = true; - if (ctx.colors) ctx.stylize = stylizeWithColor; - return formatValue(ctx, obj, ctx.depth); -} -exports.inspect = inspect; - - -// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics -inspect.colors = { - 'bold' : [1, 22], - 'italic' : [3, 23], - 'underline' : [4, 24], - 'inverse' : [7, 27], - 'white' : [37, 39], - 'grey' : [90, 39], - 'black' : [30, 39], - 'blue' : [34, 39], - 'cyan' : [36, 39], - 'green' : [32, 39], - 'magenta' : [35, 39], - 'red' : [31, 39], - 'yellow' : [33, 39] -}; - -// Don't use 'blue' not visible on cmd.exe -inspect.styles = { - 'special': 'cyan', - 'number': 'yellow', - 'boolean': 'yellow', - 'undefined': 'grey', - 'null': 'bold', - 'string': 'green', - 'date': 'magenta', - // "name": intentionally not styling - 'regexp': 'red' -}; - - -function stylizeWithColor(str, styleType) { - var style = inspect.styles[styleType]; - - if (style) { - return '\u001b[' + inspect.colors[style][0] + 'm' + str + - '\u001b[' + inspect.colors[style][1] + 'm'; - } else { - return str; - } -} - - -function stylizeNoColor(str, styleType) { - return str; -} - - -function arrayToHash(array) { - var hash = {}; - - array.forEach(function(val, idx) { - hash[val] = true; - }); - - return hash; -} - - -function formatValue(ctx, value, recurseTimes) { - // Provide a hook for user-specified inspect functions. - // Check that value is an object with an inspect function on it - if (ctx.customInspect && - value && - isFunction(value.inspect) && - // Filter out the util module, it's inspect function is special - value.inspect !== exports.inspect && - // Also filter out any prototype objects using the circular check. - !(value.constructor && value.constructor.prototype === value)) { - var ret = value.inspect(recurseTimes, ctx); - if (!isString(ret)) { - ret = formatValue(ctx, ret, recurseTimes); - } - return ret; - } - - // Primitive types cannot have properties - var primitive = formatPrimitive(ctx, value); - if (primitive) { - return primitive; - } - - // Look up the keys of the object. - var keys = Object.keys(value); - var visibleKeys = arrayToHash(keys); - - if (ctx.showHidden) { - keys = Object.getOwnPropertyNames(value); - } - - // IE doesn't make error fields non-enumerable - // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx - if (isError(value) - && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { - return formatError(value); - } - - // Some type of object without properties can be shortcutted. - if (keys.length === 0) { - if (isFunction(value)) { - var name = value.name ? ': ' + value.name : ''; - return ctx.stylize('[Function' + name + ']', 'special'); - } - if (isRegExp(value)) { - return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); - } - if (isDate(value)) { - return ctx.stylize(Date.prototype.toString.call(value), 'date'); - } - if (isError(value)) { - return formatError(value); - } - } - - var base = '', array = false, braces = ['{', '}']; - - // Make Array say that they are Array - if (isArray(value)) { - array = true; - braces = ['[', ']']; - } - - // Make functions say that they are functions - if (isFunction(value)) { - var n = value.name ? ': ' + value.name : ''; - base = ' [Function' + n + ']'; - } - - // Make RegExps say that they are RegExps - if (isRegExp(value)) { - base = ' ' + RegExp.prototype.toString.call(value); - } - - // Make dates with properties first say the date - if (isDate(value)) { - base = ' ' + Date.prototype.toUTCString.call(value); - } - - // Make error with message first say the error - if (isError(value)) { - base = ' ' + formatError(value); - } - - if (keys.length === 0 && (!array || value.length == 0)) { - return braces[0] + base + braces[1]; - } - - if (recurseTimes < 0) { - if (isRegExp(value)) { - return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); - } else { - return ctx.stylize('[Object]', 'special'); - } - } - - ctx.seen.push(value); - - var output; - if (array) { - output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); - } else { - output = keys.map(function(key) { - return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); - }); - } - - ctx.seen.pop(); - - return reduceToSingleString(output, base, braces); -} - - -function formatPrimitive(ctx, value) { - if (isUndefined(value)) - return ctx.stylize('undefined', 'undefined'); - if (isString(value)) { - var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') - .replace(/'/g, "\\'") - .replace(/\\"/g, '"') + '\''; - return ctx.stylize(simple, 'string'); - } - if (isNumber(value)) - return ctx.stylize('' + value, 'number'); - if (isBoolean(value)) - return ctx.stylize('' + value, 'boolean'); - // For some reason typeof null is "object", so special case here. - if (isNull(value)) - return ctx.stylize('null', 'null'); -} - - -function formatError(value) { - return '[' + Error.prototype.toString.call(value) + ']'; -} - - -function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { - var output = []; - for (var i = 0, l = value.length; i < l; ++i) { - if (hasOwnProperty(value, String(i))) { - output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - String(i), true)); - } else { - output.push(''); - } - } - keys.forEach(function(key) { - if (!key.match(/^\d+$/)) { - output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - key, true)); - } - }); - return output; -} - - -function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { - var name, str, desc; - desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; - if (desc.get) { - if (desc.set) { - str = ctx.stylize('[Getter/Setter]', 'special'); - } else { - str = ctx.stylize('[Getter]', 'special'); - } - } else { - if (desc.set) { - str = ctx.stylize('[Setter]', 'special'); - } - } - if (!hasOwnProperty(visibleKeys, key)) { - name = '[' + key + ']'; - } - if (!str) { - if (ctx.seen.indexOf(desc.value) < 0) { - if (isNull(recurseTimes)) { - str = formatValue(ctx, desc.value, null); - } else { - str = formatValue(ctx, desc.value, recurseTimes - 1); - } - if (str.indexOf('\n') > -1) { - if (array) { - str = str.split('\n').map(function(line) { - return ' ' + line; - }).join('\n').substr(2); - } else { - str = '\n' + str.split('\n').map(function(line) { - return ' ' + line; - }).join('\n'); - } - } - } else { - str = ctx.stylize('[Circular]', 'special'); - } - } - if (isUndefined(name)) { - if (array && key.match(/^\d+$/)) { - return str; - } - name = JSON.stringify('' + key); - if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { - name = name.substr(1, name.length - 2); - name = ctx.stylize(name, 'name'); - } else { - name = name.replace(/'/g, "\\'") - .replace(/\\"/g, '"') - .replace(/(^"|"$)/g, "'"); - name = ctx.stylize(name, 'string'); - } - } - - return name + ': ' + str; -} - - -function reduceToSingleString(output, base, braces) { - var numLinesEst = 0; - var length = output.reduce(function(prev, cur) { - numLinesEst++; - if (cur.indexOf('\n') >= 0) numLinesEst++; - return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; - }, 0); - - if (length > 60) { - return braces[0] + - (base === '' ? '' : base + '\n ') + - ' ' + - output.join(',\n ') + - ' ' + - braces[1]; - } - - return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; -} - - -// NOTE: These type checking functions intentionally don't use `instanceof` -// because it is fragile and can be easily faked with `Object.create()`. -function isArray(ar) { - return Array.isArray(ar); -} -exports.isArray = isArray; - -function isBoolean(arg) { - return typeof arg === 'boolean'; -} -exports.isBoolean = isBoolean; - -function isNull(arg) { - return arg === null; -} -exports.isNull = isNull; - -function isNullOrUndefined(arg) { - return arg == null; -} -exports.isNullOrUndefined = isNullOrUndefined; - -function isNumber(arg) { - return typeof arg === 'number'; -} -exports.isNumber = isNumber; - -function isString(arg) { - return typeof arg === 'string'; -} -exports.isString = isString; - -function isSymbol(arg) { - return typeof arg === 'symbol'; -} -exports.isSymbol = isSymbol; - -function isUndefined(arg) { - return arg === void 0; -} -exports.isUndefined = isUndefined; - -function isRegExp(re) { - return isObject(re) && objectToString(re) === '[object RegExp]'; -} -exports.isRegExp = isRegExp; - -function isObject(arg) { - return typeof arg === 'object' && arg !== null; -} -exports.isObject = isObject; - -function isDate(d) { - return isObject(d) && objectToString(d) === '[object Date]'; -} -exports.isDate = isDate; - -function isError(e) { - return isObject(e) && - (objectToString(e) === '[object Error]' || e instanceof Error); -} -exports.isError = isError; - -function isFunction(arg) { - return typeof arg === 'function'; -} -exports.isFunction = isFunction; - -function isPrimitive(arg) { - return arg === null || - typeof arg === 'boolean' || - typeof arg === 'number' || - typeof arg === 'string' || - typeof arg === 'symbol' || // ES6 symbol - typeof arg === 'undefined'; -} -exports.isPrimitive = isPrimitive; - -exports.isBuffer = require('./support/isBuffer'); - -function objectToString(o) { - return Object.prototype.toString.call(o); -} - - -function pad(n) { - return n < 10 ? '0' + n.toString(10) : n.toString(10); -} - - -var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', - 'Oct', 'Nov', 'Dec']; - -// 26 Feb 16:19:34 -function timestamp() { - var d = new Date(); - var time = [pad(d.getHours()), - pad(d.getMinutes()), - pad(d.getSeconds())].join(':'); - return [d.getDate(), months[d.getMonth()], time].join(' '); -} - - -// log is just a thin wrapper to console.log that prepends a timestamp -exports.log = function() { - console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); -}; - - -/** - * Inherit the prototype methods from one constructor into another. - * - * The Function.prototype.inherits from lang.js rewritten as a standalone - * function (not on Function.prototype). NOTE: If this file is to be loaded - * during bootstrapping this function needs to be rewritten using some native - * functions as prototype setup using normal JavaScript does not work as - * expected during bootstrapping (see mirror.js in r114903). - * - * @param {function} ctor Constructor function which needs to inherit the - * prototype. - * @param {function} superCtor Constructor function to inherit prototype from. - */ -exports.inherits = require('inherits'); - -exports._extend = function(origin, add) { - // Don't do anything if add isn't an object - if (!add || !isObject(add)) return origin; - - var keys = Object.keys(add); - var i = keys.length; - while (i--) { - origin[keys[i]] = add[keys[i]]; - } - return origin; -}; - -function hasOwnProperty(obj, prop) { - return Object.prototype.hasOwnProperty.call(obj, prop); -} - -}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./support/isBuffer":20,"_process":18,"inherits":19}],22:[function(require,module,exports){ -/*! - * XRegExp-All - * - * Steven Levithan (c) 2012-2016 MIT License - */ - -// Module systems magic dance -// Don't use strict mode for this function, so it can assign to global -(function(root, definition) { - // RequireJS - if (typeof define === 'function' && define.amd) { - define(definition); - // CommonJS - } else if (typeof exports === 'object') { - var self = definition(); - // Use Node.js's `module.exports`. This supports both `require('xregexp')` and - // `require('xregexp').XRegExp` - (typeof module === 'object' ? (module.exports = self) : exports).XRegExp = self; - //