const JisonLex = require("jison-lex"),
path = require("path").win32,
fs = require("fs"),
escapeRegexpString = require("escape-string-regexp");
const grammar = fs.readFileSync(require.resolve("./comspec.l")).toString(),
lexer = new JisonLex(grammar);
// ;;;;;;;;;;;;;;;;;;;;;;;
// ;; Utility functions ;;
// ;;;;;;;;;;;;;;;;;;;;;;;
//
function stringify_tokens(tokens) {
return tokens.map(t => t.text).join("");
}
function has (obj, key) {
return obj.hasOwnProperty(key);
}
function hasAll (obj, keys) {
return keys.every(k => obj.hasOwnProperty(k));
}
function hasAny (obj, keys) {
return keys.some(k => obj.hasOwnProperty(k));
}
// ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
// ;; ;;
// ;; TYPE DEFINITIONS FOR DOCUMENTATION ;;
// ;; ;;
// ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//
// Token
// =====
/**
* @typedef {Object} Token
* @property {string} name - Name of the token (LITERAL, SET, ESCAPE, ...).
* @property {string} match - Match object for the matched token.
* @property {string} line - Line number upon which the token was found.
* @property {string} text - Similar to 'match'.
* @property {number} len - Length (in chars) of the matched token.
* @property {Object} loc - Location of match (first/last line, first/last col).
*/
//
// Identified Command
// ==================
/**
* @typedef {Object} IdentifiedCommand
* @property {string} command - The name of the command ("" if unknown).
* @property {number} offset - The offset of where the command identifier
* ends in the tokens array.
*/
/**
* Command dispatch table maps commands we can handle to functions
* which do the command handling. Generally, when we're talking about
* "handling a command", we mean altering the parsed token stream so
* that the tokens are correctly altered based upon the command
* handling them. For example, we may have been passed something
* like:
*
* cmd "set foo=bar"
*
* The token stream for this command will look something like:
*
* LIT(c), LIT(m), LIT(d), STR("), STRLIT(s), STRLIT(e), ...
*
* If we remove the leading "cmd " from the string, and strip the
* surrounding double quotes from the command and re-tokenise it,
* we'll get a new tokenised sequence which we can handle.
*
*/
const cmd_dispatch = {
cmd: FILTER_handle_cmd
};
/**
* Provides specific command clean-up for the 'cmd.exe' command.
*
* @param {IdentifiedCommand} ident - The identified command object.
* @param {Tokens|Array} tokens - The array of tokens from parsing the cmd.
*
* @returns {Tokens|Array}
*/
function FILTER_handle_cmd (ident, tokens) {
tokens = Array.prototype.slice.call(tokens);
if (ident.offset >= tokens.length) {
return {
tokens: tokens,
switches: {},
finished: true
};
}
let cmd = tokens
.slice(ident.offset)
.map(t => t.text)
.join("")
.replace(/^\s+/, "");
// A note about argument parsing
// =============================
//
// The CMD.EXE help page says that the syntax of the 'cmd.exe'
// command is:
//
// CMD [charset] [options] [/C command]
// CMD [charset] [options] [/K command]
//
// For example:
//
// CMD /V:on "set foo=bar& calc.exe"
//
//
// The important part is the location of the first dquote, which
// tells us where the COMMAND part of the line begins. We capture
// the location of this char (if exists), and use its string
// offset as the point at which we stop looking for command
// switches.
//
const first_dquote_offset = cmd.split("").findIndex(chr => chr === '"'),
switch_re = /\/([A-Z])([:][^\s]+)?(?:$|\s)/ig;
let match = undefined,
last_match_offset = undefined,
switches = {
delayed_expansion: false
};
const switch_lookup = {
"c": "run_then_terminate",
"C": "run_then_terminate",
"v": "delayed_expansion",
"V": "delayed_expansion",
"e": "cmd_extensions",
"E": "cmd_extensions",
"f": "path_autocomplete",
"F": "path_autocomplete"
};
while ((match = switch_re.exec(cmd))) {
let wholematch = match[0],
_switch = match[1],
_value = "",
match_end_offset = match[0].length + match.index;
last_match_offset = match_end_offset;
if (match[2] !== undefined) {
_value = match[2].replace(/^:/, "");
}
if (/^[efv]$/i.test(_switch)) {
_switch = switch_lookup[_switch];
switch (_value.toLowerCase()) {
case "off":
_value = false;
break;
default:
_value = true;
}
}
else if (has(switch_lookup, _switch)) {
_switch = switch_lookup[_switch];
}
switches[_switch] = _value;
if (match_end_offset && (match_end_offset > first_dquote_offset)) {
break;
}
}
// Now we've finished parsing the command arguments, we can strip
// the args leaving only the next part of the command string.
if (last_match_offset !== undefined) {
cmd = cmd.substr(last_match_offset);
}
// If the remaining command part starts and ends with a double
// quote, we strip them.
cmd = cmd.replace(/^\"|\"$/g, "");
return {
tokens: tokenise(cmd),
switches: switches,
finished: false
};
}
/**
* Parses a given command string in to individual commands, before
* applying expansion and de-obfuscation filters to each command.
*
* @param {string} cmdstr - The original command string to be
* de-obfuscated.
*
*/
function parse_cmdstr (cmdstr, options) {
const DEFAULTS = {
delayed_expansion: false,
expand_inline: false,
vars: {}
};
options = options || {};
options = Object.assign({}, DEFAULTS, options);
let collector = { vars: {}, switches: {}, output: [] };
cmdstr = expand_environment_variables(cmdstr, options.vars);
(function parse_cmdstr_rec (cmdstr, switches) {
switches = switches || {};
split_command(cmdstr).forEach(cmd => {
let result = interpret_command(cmd);
collector.vars = Object.assign(collector.vars, result.vars);
if (result.ident.finished) {
collector.output.push(stringify_tokens(result.ident.tokens));
}
else if (result.ident.command === "cmd") {
//
// NOTES ON DELAYED EXPANSION
// ==========================
//
// We have support for delayed expansion. Tests on
// Win7 and Win10 hosts show that delayed expansion is
// only enabled for the current CMD context, for
// example, given the following command:
//
// cmd /V "set foo=bar& echo !foo!"
//
// The output will be "echo bar" because delayed
// expansion is set. However, it does not cascade in
// to lower-down CMD instances, for example:
//
// cmd /V "cmd \"set foo=bar& echo !foo!\""
//
// This will produce "echo !foo!" because we created a
// sub-cmd context, and the default was applied.
//
let new_cmd = stringify_tokens(result.ident.tokens);
if (new_cmd.toLowerCase() !== "cmd") { // infinite loop protection.
parse_cmdstr_rec(
stringify_tokens(result.ident.tokens),
result.ident.switches
);
}
}
else {
let delayed_exp = Object.assign({}, options, switches).delayed_expansion;
// We do not want to expand percentage vars as
// that time has passed.
cmd = expand_environment_variables(
result.clean,
collector.vars,
{
expand_percent_vars: options.expand_inline,
delayed_expansion: delayed_exp
}
);
collector.output.push(cmd);
}
});
}(cmdstr));
return collector.output;
}
/**
* Given an array of Token objects, attempts to identify the command
* being run. If a command is found, an IdentifiedCommand object is
* returned which will contain both the command name and the offset
* from where abouts in the tokens array the command string ends. If
* the command cannot be found, returns an empty name ("") and -1 for
* the offset.
*
* For best results, this command should be called AFTER all filtering
* has taken place, thus ensuring the command is in the least
* obfuscated state BEFORE attempting command identification.
*
* @param {Token|Array} tokens - The command string to analyse.
* @returns {IdentifiedCommand}
*/
function try_identify_command (tokens) {
tokens = Array.prototype.slice.call(tokens);
let identified_command = {
command : "",
switches: {},
offset : -1
};
/*
* Double-Quoted commands
* ======================
*
* For example, matches something similar to:
*
* "C:\Windows\System32\cmd.exe"
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*/
if (tokens[0].name === "STRING_DQUOTE_BEGIN") {
let dquote_end_index = tokens.findIndex(
(t, i) => i > 0 && t.name === "STRING_DQUOTE_END"
);
if (!dquote_end_index) {
// We can't do much if the CMD doesn't have an ending
// DQUOTE. Bad command.
return identified_command;
}
let cmd = tokens
.splice(0, dquote_end_index)
.map(tok => tok.text)
.join("")
.replace(/^\"|\"$/g, "");
identified_command.command = path.basename(cmd).replace(/\.exe$/i, "");
identified_command.offset = dquote_end_index + 1;
}
else if (tokens[0].name === "SET") {
identified_command.command = "set";
identified_command.offset = 1;
}
else {
let end_index = tokens.findIndex(t => (t.text === " " || t.name === "SEMICOLON"));
end_index = (end_index < 0) ? tokens.length : end_index;
let cmd = tokens
.splice(0, end_index)
.map(tok => tok.text)
.join("");
if (/[\\/]/.test(cmd) || /^[a-z]:/i.test(cmd)) {
// If the path contains path separators, or some drive
// identifier such as 'C:', then clean-up the path and
// return the command.
identified_command.command = path.basename(cmd).replace(/\.exe$/i, "");
identified_command.offset = end_index + 1;
}
else if (cmd) {
identified_command.command = cmd.replace(/\.exe$/i, "");
identified_command.offset = end_index;
}
}
return identified_command;
}
/**
* Given an array of Token objects, attempts to remove all non-quoted
* contiguous whitespace LITERALS, leaving a single space between each
* word boundary.
*
* @param {Token|Array} tokens - An array of tokens.
* @returns {Token|Array}
*/
function FILTER_strip_excessive_whitespace (tokens) {
for (let i = 0; i < tokens.length; i++) {
let token = tokens[i],
lookahead = tokens[i + 1];
if (token.name === "LITERAL" && token.text === " ") {
if (i === 0) {
tokens.splice(0,1);
i = -1;
}
else if (i === (tokens.length - 1) && token.text === " ") {
tokens.splice(i, 1);
i = -1;
}
else if (lookahead && lookahead.name === "LITERAL" && lookahead.text === " ") {
tokens.splice(i, 1);
i = -1;
}
}
}
return tokens;
}
/**
* Given a command string, attempts to slurp all LITERAL,
* non-whitespace tokens surrounding a string inside that string. For
* example:
*
* c"alc".exe --[slurped]--> "calc.exe"
*
* @param {Token|Array} tokens - An array of tokens.
* @returns {Token|Array}
*/
function FILTER_slurp_literals_into_strings (tokens) {
for (let i = 0; i < tokens.length; i++) {
let token = tokens[i],
lookahead = tokens[i + 1],
lookbehind = tokens[i - 1];
if (token.name === "STRING_DQUOTE_BEGIN") {
if (lookbehind && lookbehind.name === "LITERAL" && lookbehind.text !== " ") {
tokens[i - 1] = token;
tokens[i] = lookbehind;
tokens[i].name = "STRING_DQUOTE_CHAR";
i = 0;
}
}
else if (token.name === "STRING_DQUOTE_END") {
if (lookahead && lookahead.name === "LITERAL" && lookahead.text !== " ") {
tokens[i + 1] = token;
tokens[i] = lookahead;
tokens[i].name = "STRING_DQUOTE_CHAR";
}
}
}
// We need to clean-up the tokens. Consider the following input:
//
// h"t"t"p"
//
// The way the algorithm works, we'll end up with our tokens being
// ordered:
//
// "htt""p"
//
for (i = 0; i < tokens.length; i++) {
let token = tokens[i],
lookahead_1 = tokens[i + 1],
lookahead_2 = tokens[i + 2];
if (token.name === "STRING_DQUOTE_END") {
if (lookahead_1 && lookahead_1.name === "STRING_DQUOTE_BEGIN") {
tokens.splice(i, 2);
}
}
}
return tokens;
}
/**
* Given an array of Tokens, attempts to remove all empty strings ("")
* from the list, returning a new list of tokens with empty string
* tokens removed.
*
* @param {Token|Array} tokens - An array of tokens.
* @returns {Token|Array}
*/
function FILTER_strip_empty_strings (tokens) {
let out_tokens = [],
skip_token = false;
for (let i = 0; i < tokens.length; i++) {
let token = tokens[i],
lookahead = tokens[i + 1];
if (skip_token) {
out_tokens.pop();
skip_token = false;
continue;
}
out_tokens.push(token);
switch (token.name) {
case "STRING_DQUOTE_BEGIN":
if (lookahead && lookahead.name === "STRING_DQUOTE_END") {
skip_token = true;
}
break;
}
}
return out_tokens;
}
/**
* Given an array of Tokens, attempts to remove all unnecessary commas
* from the tokenised sequence.
*
* @param {Token|Array} tokens - An array of tokens.
* @returns {Token|Array}
*/
function FILTER_strip_commas (tokens) {
return tokens.filter(token => token.name !== "COMMA");
}
/**
* Given an array of Tokens, attempts to fix-up all tokens which were
* previously escaped tokens.
*
* @param {Token|Array} tokens - An array of tokens.
* @returns {Token|Array}
*/
function FILTER_apply_escapes (tokens) {
let filtered = tokens
.filter(tok => tok.name !== "ESCAPE")
.map((tok, i, tokens) => {
if (tok.name === "ESCAPED_LITERAL") tokens[i].name = "LITERAL";
return tokens[i];
});
return filtered;
}
/**
* Given a command string, attempts to partially interpret the
* command, returning an object which can be used to present the
* command in an easy-to-understand way.
*
* @param {string} cmdstr - The command to run/parse.
* @returns {Object}
*/
function interpret_command (cmdstr) {
let clean_cmdstr = cmdstr.replace(/^\s+|\s+$/, "");
// Parse the command string in to an array of Token objects.
let tokens = tokenise(clean_cmdstr),
ident = try_identify_command(tokens);
if (cmd_dispatch.hasOwnProperty(ident.command)) {
let handled = cmd_dispatch[ident.command](ident, tokens);
ident.tokens = handled.tokens;
ident.switches = handled.switches;
ident.finished = handled.finished;
}
let flags = {
in_set_cmd : false,
capturing_env_var_name : false,
capturing_env_var_value : false
};
let env_vars = {},
env_var_name = "",
env_var_value = "";
// The `outbuf` var holds a cleaned-up version of the command with
// all obfuscation removed.
let outbuf = [];
// When TRUE, the parser skips the next token. Used in cases
// where we want to ignore "".
let skip = false;
for (let i = 0; i < tokens.length; i++) {
if (skip) {
outbuf.pop();
skip = false;
continue;
}
let token = tokens[i],
lookahead = tokens[i + 1];
outbuf.push(token.text);
switch (token.name) {
case "LITERAL":
if (flags.in_set_cmd) {
if (flags.capturing_env_var_name) {
env_var_name += token.text;
}
else if (flags.capturing_env_var_value) {
env_var_value += token.text;
}
}
break;
case "ESCAPED":
break;
case "SET":
flags.capturing_env_var_name = true;
flags.in_set_cmd = true;
break;
case "SET_ASSIGNMENT":
flags.capturing_env_var_name = false;
flags.capturing_env_var_value = true;
break;
case "SET_DQUOTE_CHAR":
if (flags.capturing_env_var_name) {
env_var_name += token.text;
}
else if (flags.capturing_env_var_value) {
env_var_value += token.text;
}
break;
case "SET_DQUOTE_BEGIN":
case "SET_DQUOTE_END":
// TODO: may need to add another flag here...
break;
case "STRING_DQUOTE_BEGIN":
if (lookahead.name === "STRING_DQUOTE_END") {
skip = true;
}
break;
default:
//console.log("UNKNOWN TOK>", token.name, token.text);
}
}
if (env_var_name.length && env_var_value.length) {
if (/^%[^%]+[^%]%$/.test(env_var_name)) {
// Special handling for the case where someone sets:
//
// SET %foo%=bar
//
// In this case, '%foo%' is treated as 'foo'. This is
// different from something like:
//
// SET %%foo%%=bar
//
// which Windows treats as '%%foo%%' which is !== '%foo%'.
//
env_var_name = env_var_name.replace(/^%|%$/g, "");
}
env_vars[env_var_name] = {
first: env_var_value,
curr: env_var_value
};
}
return {
ident: ident,
clean: outbuf.join(""),
vars: env_vars
};
}
/**
* Given a command string, attempts to split the string, returning an
* array of individual command strings.
*
* @param {string} command - a CMD.EXE command.
* @returns {Tokens|Array} Each command is an element in the array.
*/
function split_command (command_str) {
let tokens = tokenise(command_str, { filter: false }),
index = 0,
commands = [""];
tokens.forEach(tok => {
if (/^(?:CALL|COND_CALL|SEMICOLON)$/.test(tok.name)) {
index++;
commands[index] = "";
}
else {
commands[index] += tok.text;
}
});
return commands
.map(cmd => cmd.replace(/^\s*|\s*$/g, "")) // Remove leading and trailing whitespace
.filter(cmd => ! /^\s*$/.test(cmd));
}
/**
* Given a command string, attempts to split the string in to an array
* of Token objects.
*
* @param {string} cmdstr - The command string to split in to tokens.
* @param {string} [options] - Set .filter T|F to enable/disable filtering.
* @returns {Token|Array} Token objects, one-per-token.
*/
function tokenise (cmdstr, options) {
options = options || {};
options = Object.assign({}, { escapes_as_literals: false, filter: true }, options);
lexer.setInput(cmdstr);
let tokens = [];
while (true) {
let token = lexer.lex();
if (token === "EOF") break;
if (options.escapes_as_literals) {
if (token.name === "ESCAPE") {
token.name = "LITERAL";
}
else if (token.name === "ESCAPED_LITERAL") {
if (token.text === "=") {
token.name = "SET_ASSIGNMENT";
}
else {
token.name = "LITERAL";
}
}
}
tokens.push(token);
}
if (options.filter) {
tokens = FILTER_apply_escapes(tokens);
tokens = FILTER_strip_empty_strings(tokens);
tokens = FILTER_slurp_literals_into_strings(tokens);
tokens = FILTER_strip_excessive_whitespace(tokens);
//tokens = FILTER_strip_commas(tokens);
}
let cleancmd = stringify_tokens(tokens);
if (cmdstr !== cleancmd) {
return tokenise(cleancmd, { escapes_as_literals: true });
}
return tokens;
}
/**
* Attempts to perform a find/replace with variable expansion against
* a given DOS command with values read from an optional variable
* key/value object. BATCH implements some syntactic-sugar to support
* finding and replacing characters within an environment variable:
*
* @example
* // Replace all 'a' chars with 'b' in var 'foo':
* "%foo:a=b%"
*
* @param {string} cmdstr - DOS command we wish to deobfuscate.
* @param {Object} [vars] - An object mapping var names to values.
*
* @returns {string} An expanded form of `cmdstr` with all variable
* find/replace operations performed.
*/
function substr_replace (cmdstr, vars) {
let find_replace_re = /%([^:]*):([^\s]+)=([^\s]+)?%/ig,
got_match;
while ((got_match = find_replace_re.exec(cmdstr))) {
let wholematch = got_match[0],
findstr = got_match[2],
replstr = got_match[3] || "",
varname = got_match[1].toLowerCase(),
varvalue = vars[varname];
if (vars.hasOwnProperty(varname) === false) {
continue;
}
let replaced_varvalue = varvalue.first.split(findstr).join(replstr);
cmdstr = cmdstr.split(wholematch).join(replaced_varvalue);
}
return cmdstr;
}
/**
* Given a command string an an object mapping varname => varvalue,
* attempts to apply the range of text manipulations supported by the
* BATCH language. The following features are supported:
*
* - expansion :: %foo% expands to the valueOf(%foo%).
* - substrings :: %foo:~5%, %foo:0,3%, %foo:~-3%
* -
*
*/
function expand_environment_variables (cmdstr, vars, options) {
options = options || {};
const defaults = {
expand_percent_vars: true,
delayed_expansion: false
};
options = Object.assign({}, defaults, options);
const default_vars = {
appdata: {
first: `C:\\Users\\whoami\\AppData\\Roaming`,
curr: `C:\\Users\\whoami\\AppData\\Roaming`
},
comspec: {
first: `C:\\Windows\\System32\\cmd.exe`,
curr: `C:\\Windows\\System32\\cmd.exe`
}
};
if (vars) {
Object.keys(vars).forEach(varname => {
const varvalue = vars[varname];
if (typeof varvalue === "string") {
vars[varname] = {
first: varvalue,
curr: varvalue
};
}
});
}
vars = Object.assign(default_vars, vars);
// Expand Variables
// ================
//
// Take all instances of '%foo%' and replace with the value found
// within the 'vars' dict.
//
let cmd = cmdstr;
if (options.expand_percent_vars) {
Object.keys(vars).forEach(varname => {
cmd = cmd.replace(
new RegExp(escapeRegexpString(`%${varname}%`), "gi"), vars[varname].first
);
});
}
// Delayed Expansion
// =================
//
// Instead of '%foo%', delayed expansion works with '!foo!', and
// reads from '.curr' instead of '.first'.
//
if (options.delayed_expansion) {
Object.keys(vars).forEach(varname => {
cmd = cmd.replace(
new RegExp(escapeRegexpString(`!${varname}!`), "gi"), // find
vars[varname].curr // replace
);
});
}
// Apply Find/Replace
// ==================
//
// Searches the variable for all instances of STR, replacing with
// REP, for example:
//
// %foo:STR=REP%
// %foo:cat=dog%
//
cmd = substr_replace(cmd, vars);
// Substring handling
// ==================
//
// There are a few different ways we can apply substrings. Assume
// %foo% = "abcdef".
//
// - %foo:~3% => def
// - %foo:~0,3% => abc
// - %foo:~-3% => def
// - %foo:~1,3% => bcd
//
let substr_re = /%([^:]*):\s*~\s*([+-]?\d+)(?:,([+-]?\d+))?%/ig,
replacements = [],
substr_match;
while ((substr_match = substr_re.exec(cmd))) {
let var_name = substr_match[1].toLowerCase(),
var_value = vars[var_name],
substr_start = substr_match[2],
substr_end = substr_match[3];
if (substr_start !== undefined) {
substr_start = parseInt(substr_start, 10);
}
if (substr_end !== undefined) {
substr_end = parseInt(substr_end, 10);
}
if (var_value === undefined) {
continue;
}
else {
var_value = var_value.first;
}
let replace = {
find: substr_match[0]
};
let rev = s => s.split("").reverse().join("");
if ((substr_start !== undefined) && (substr_end === undefined)) {
if (substr_start == 0) {
// Special case -- when the substr pattern is
// something like:
//
// %FOO:~0%
//
// Windows expands this to the full variable value.
replace.replace = var_value;
replacements.push(replace);
continue;
}
else if (substr_start < 0) {
// Negative substr values start from the last char and
// substr forwards.
let rev_var_value = rev(var_value);
var_value = rev(rev_var_value.substr(0, (substr_start * -1)));
replace.replace = var_value;
replacements.push(replace);
continue;
}
replace.replace = var_value.substring(substr_start, substr_end);
}
else if ((substr_start !== undefined) && (substr_end !== undefined)) {
if (substr_start < 0 && substr_end < 0) {
substr_start = (substr_start * -1);
substr_end = (substr_end * -1);
let tmpstart = Math.min(substr_start, substr_end),
tmpend = Math.max(substr_start, substr_end);
replace.replace = rev(rev(var_value).split("").slice(tmpstart, tmpend).join(""));
}
else if (substr_start < 0 && substr_end > 0) {
/*
* Handles cases such as: %foo:~-10,3%.
*/
let substr_offset = (substr_end + substr_start) * -1;
replace.replace = rev((rev(var_value).substr(substr_offset, substr_end)));
}
else if (substr_end < 0 && substr_start === 0) {
replace.replace = rev(rev(var_value).substr(substr_end * -1));
}
else if (substr_start === 0) {
replace.replace = var_value.substring(0, substr_end);
}
else if (substr_start > 0) {
replace.replace = var_value.substring(substr_start, substr_end + substr_start);
}
}
replacements.push(replace);
}
replacements.forEach(r => {
cmd = cmd.replace(new RegExp(escapeRegexpString(r.find), "gi"), r.replace);
});
return cmd;
}
module.exports = {
filter: {
widen_strings: FILTER_slurp_literals_into_strings,
strip_escapes: FILTER_apply_escapes,
strip_whitespace: FILTER_strip_excessive_whitespace,
strip_empty_strings: FILTER_strip_empty_strings,
strip_commas: FILTER_strip_commas,
// Command handlers
handle_CMD: FILTER_handle_cmd,
},
try_identify_command: try_identify_command,
tokenise: tokenise,
split_command: split_command,
parse: parse_cmdstr,
expand_variables: expand_environment_variables
};
/*! chevrotain - v1.0.1 */
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define("chevrotain", [], factory);
else if(typeof exports === 'object')
exports["chevrotain"] = factory();
else
root["chevrotain"] = factory();
})(typeof self !== 'undefined' ? self : this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 20);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/*
Utils using lodash style API. (not necessarily 100% compliant) for functional and other utils.
These utils should replace usage of lodash in the production code base. not because they are any better...
but for the purpose of being a dependency free library.
The hotspots in the code are already written in imperative style for performance reasons.
so writing several dozen utils which may be slower than the original lodash, does not matter as much
considering they will not be invoked in hotspots...
*/
function isEmpty(arr) {
return arr && arr.length === 0;
}
exports.isEmpty = isEmpty;
function keys(obj) {
if (obj === undefined || obj === null) {
return [];
}
return Object.keys(obj);
}
exports.keys = keys;
function values(obj) {
var vals = [];
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
vals.push(obj[keys[i]]);
}
return vals;
}
exports.values = values;
function mapValues(obj, callback) {
var result = [];
var objKeys = keys(obj);
for (var idx = 0; idx < objKeys.length; idx++) {
var currKey = objKeys[idx];
result.push(callback.call(null, obj[currKey], currKey));
}
return result;
}
exports.mapValues = mapValues;
function map(arr, callback) {
var result = [];
for (var idx = 0; idx < arr.length; idx++) {
result.push(callback.call(null, arr[idx], idx));
}
return result;
}
exports.map = map;
function flatten(arr) {
var result = [];
for (var idx = 0; idx < arr.length; idx++) {
var currItem = arr[idx];
if (Array.isArray(currItem)) {
result = result.concat(flatten(currItem));
}
else {
result.push(currItem);
}
}
return result;
}
exports.flatten = flatten;
function first(arr) {
return isEmpty(arr) ? undefined : arr[0];
}
exports.first = first;
function last(arr) {
var len = arr && arr.length;
return len ? arr[len - 1] : undefined;
}
exports.last = last;
function forEach(collection, iteratorCallback) {
if (Array.isArray(collection)) {
for (var i = 0; i < collection.length; i++) {
iteratorCallback.call(null, collection[i], i);
}
}
else if (isObject(collection)) {
var colKeys = keys(collection);
for (var i = 0; i < colKeys.length; i++) {
var key = colKeys[i];
var value = collection[key];
iteratorCallback.call(null, value, key);
}
}
else {
/* istanbul ignore next */
throw Error("non exhaustive match");
}
}
exports.forEach = forEach;
function isString(item) {
return typeof item === "string";
}
exports.isString = isString;
function isUndefined(item) {
return item === undefined;
}
exports.isUndefined = isUndefined;
function isFunction(item) {
return item instanceof Function;
}
exports.isFunction = isFunction;
function drop(arr, howMuch) {
if (howMuch === void 0) { howMuch = 1; }
return arr.slice(howMuch, arr.length);
}
exports.drop = drop;
function dropRight(arr, howMuch) {
if (howMuch === void 0) { howMuch = 1; }
return arr.slice(0, arr.length - howMuch);
}
exports.dropRight = dropRight;
function filter(arr, predicate) {
var result = [];
if (Array.isArray(arr)) {
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
if (predicate.call(null, item)) {
result.push(item);
}
}
}
return result;
}
exports.filter = filter;
function reject(arr, predicate) {
return filter(arr, function (item) { return !predicate(item); });
}
exports.reject = reject;
function pick(obj, predicate) {
var keys = Object.keys(obj);
var result = {};
for (var i = 0; i < keys.length; i++) {
var currKey = keys[i];
var currItem = obj[currKey];
if (predicate(currItem)) {
result[currKey] = currItem;
}
}
return result;
}
exports.pick = pick;
function has(obj, prop) {
if (isObject(obj)) {
return obj.hasOwnProperty(prop);
}
return false;
}
exports.has = has;
function contains(arr, item) {
return find(arr, function (currItem) { return currItem === item; }) !== undefined ? true : false;
}
exports.contains = contains;
/**
* shallow clone
*/
function cloneArr(arr) {
var newArr = [];
for (var i = 0; i < arr.length; i++) {
newArr.push(arr[i]);
}
return newArr;
}
exports.cloneArr = cloneArr;
/**
* shallow clone
*/
function cloneObj(obj) {
var clonedObj = {};
for (var key in obj) {
/* istanbul ignore else */
if (Object.prototype.hasOwnProperty.call(obj, key)) {
clonedObj[key] = obj[key];
}
}
return clonedObj;
}
exports.cloneObj = cloneObj;
function find(arr, predicate) {
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
if (predicate.call(null, item)) {
return item;
}
}
return undefined;
}
exports.find = find;
function findAll(arr, predicate) {
var found = [];
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
if (predicate.call(null, item)) {
found.push(item);
}
}
return found;
}
exports.findAll = findAll;
function reduce(arrOrObj, iterator, initial) {
var vals = Array.isArray(arrOrObj)
? arrOrObj
: values(arrOrObj);
var accumulator = initial;
for (var i = 0; i < vals.length; i++) {
accumulator = iterator.call(null, accumulator, vals[i], i);
}
return accumulator;
}
exports.reduce = reduce;
function compact(arr) {
return reject(arr, function (item) { return item === null || item === undefined; });
}
exports.compact = compact;
function uniq(arr, identity) {
if (identity === void 0) { identity = function (item) { return item; }; }
var identities = [];
return reduce(arr, function (result, currItem) {
var currIdentity = identity(currItem);
if (contains(identities, currIdentity)) {
return result;
}
else {
identities.push(currIdentity);
return result.concat(currItem);
}
}, []);
}
exports.uniq = uniq;
function partial(func) {
var restArgs = [];
for (var _i = 1; _i < arguments.length; _i++) {
restArgs[_i - 1] = arguments[_i];
}
var firstArg = [null];
var allArgs = firstArg.concat(restArgs);
return Function.bind.apply(func, allArgs);
}
exports.partial = partial;
function isArray(obj) {
return Array.isArray(obj);
}
exports.isArray = isArray;
function isRegExp(obj) {
return obj instanceof RegExp;
}
exports.isRegExp = isRegExp;
function isObject(obj) {
return obj instanceof Object;
}
exports.isObject = isObject;
function every(arr, predicate) {
for (var i = 0; i < arr.length; i++) {
if (!predicate(arr[i], i)) {
return false;
}
}
return true;
}
exports.every = every;
function difference(arr, values) {
return reject(arr, function (item) { return contains(values, item); });
}
exports.difference = difference;
function some(arr, predicate) {
for (var i = 0; i < arr.length; i++) {
if (predicate(arr[i])) {
return true;
}
}
return false;
}
exports.some = some;
function indexOf(arr, value) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] === value) {
return i;
}
}
return -1;
}
exports.indexOf = indexOf;
function sortBy(arr, orderFunc) {
var result = cloneArr(arr);
result.sort(function (a, b) { return orderFunc(a) - orderFunc(b); });
return result;
}
exports.sortBy = sortBy;
function zipObject(keys, values) {
if (keys.length !== values.length) {
throw Error("can't zipObject with different number of keys and values!");
}
var result = {};
for (var i = 0; i < keys.length; i++) {
result[keys[i]] = values[i];
}
return result;
}
exports.zipObject = zipObject;
/**
* mutates! (and returns) target
*/
function assign(target) {
var sources = [];
for (var _i = 1; _i < arguments.length; _i++) {
sources[_i - 1] = arguments[_i];
}
for (var i = 0; i < sources.length; i++) {
var curSource = sources[i];
var currSourceKeys = keys(curSource);
for (var j = 0; j < currSourceKeys.length; j++) {
var currKey = currSourceKeys[j];
target[currKey] = curSource[currKey];
}
}
return target;
}
exports.assign = assign;
/**
* mutates! (and returns) target
*/
function assignNoOverwrite(target) {
var sources = [];
for (var _i = 1; _i < arguments.length; _i++) {
sources[_i - 1] = arguments[_i];
}
for (var i = 0; i < sources.length; i++) {
var curSource = sources[i];
if (isUndefined(curSource)) {
continue;
}
var currSourceKeys = keys(curSource);
for (var j = 0; j < currSourceKeys.length; j++) {
var currKey = currSourceKeys[j];
if (!has(target, currKey)) {
target[currKey] = curSource[currKey];
}
}
}
return target;
}
exports.assignNoOverwrite = assignNoOverwrite;
function defaults() {
var sources = [];
for (var _i = 0; _i < arguments.length; _i++) {
sources[_i] = arguments[_i];
}
return assignNoOverwrite.apply(null, [{}].concat(sources));
}
exports.defaults = defaults;
function groupBy(arr, groupKeyFunc) {
var result = {};
forEach(arr, function (item) {
var currGroupKey = groupKeyFunc(item);
var currGroupArr = result[currGroupKey];
if (currGroupArr) {
currGroupArr.push(item);
}
else {
result[currGroupKey] = [item];
}
});
return result;
}
exports.groupBy = groupBy;
/**
* Merge obj2 into obj1.
* Will overwrite existing properties with the same name
*/
function merge(obj1, obj2) {
var result = cloneObj(obj1);
var keys2 = keys(obj2);
for (var i = 0; i < keys2.length; i++) {
var key = keys2[i];
var value = obj2[key];
result[key] = value;
}
return result;
}
exports.merge = merge;
function NOOP() { }
exports.NOOP = NOOP;
function IDENTITY(item) {
return item;
}
exports.IDENTITY = IDENTITY;
//# sourceMappingURL=utils.js.map
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var utils_1 = __webpack_require__(0);
var tokens_public_1 = __webpack_require__(2);
var gast;
(function (gast) {
var AbstractProduction = /** @class */ (function () {
function AbstractProduction(definition) {
this.definition = definition;
}
AbstractProduction.prototype.accept = function (visitor) {
visitor.visit(this);
utils_1.forEach(this.definition, function (prod) {
prod.accept(visitor);
});
};
return AbstractProduction;
}());
gast.AbstractProduction = AbstractProduction;
var NonTerminal = /** @class */ (function (_super) {
__extends(NonTerminal, _super);
function NonTerminal(nonTerminalName, referencedRule, occurrenceInParent, implicitOccurrenceIndex) {
if (referencedRule === void 0) { referencedRule = undefined; }
if (occurrenceInParent === void 0) { occurrenceInParent = 1; }
if (implicitOccurrenceIndex === void 0) { implicitOccurrenceIndex = false; }
var _this = _super.call(this, []) || this;
_this.nonTerminalName = nonTerminalName;
_this.referencedRule = referencedRule;
_this.occurrenceInParent = occurrenceInParent;
_this.implicitOccurrenceIndex = implicitOccurrenceIndex;
return _this;
}
Object.defineProperty(NonTerminal.prototype, "definition", {
get: function () {
if (this.referencedRule !== undefined) {
return this.referencedRule.definition;
}
return [];
},
set: function (definition) {
// immutable
},
enumerable: true,
configurable: true
});
NonTerminal.prototype.accept = function (visitor) {
visitor.visit(this);
// don't visit children of a reference, we will get cyclic infinite loops if we do so
};
return NonTerminal;
}(AbstractProduction));
gast.NonTerminal = NonTerminal;
var Rule = /** @class */ (function (_super) {
__extends(Rule, _super);
function Rule(name, definition, orgText) {
if (orgText === void 0) { orgText = ""; }
var _this = _super.call(this, definition) || this;
_this.name = name;
_this.orgText = orgText;
return _this;
}
return Rule;
}(AbstractProduction));
gast.Rule = Rule;
var Flat = /** @class */ (function (_super) {
__extends(Flat, _super);
// A named Flat production is used to indicate a Nested Rule in an alternation
function Flat(definition, name) {
var _this = _super.call(this, definition) || this;
_this.name = name;
return _this;
}
return Flat;
}(AbstractProduction));
gast.Flat = Flat;
var Option = /** @class */ (function (_super) {
__extends(Option, _super);
function Option(definition, occurrenceInParent, name, implicitOccurrenceIndex) {
if (occurrenceInParent === void 0) { occurrenceInParent = 1; }
if (implicitOccurrenceIndex === void 0) { implicitOccurrenceIndex = false; }
var _this = _super.call(this, definition) || this;
_this.occurrenceInParent = occurrenceInParent;
_this.name = name;
_this.implicitOccurrenceIndex = implicitOccurrenceIndex;
return _this;
}
return Option;
}(AbstractProduction));
gast.Option = Option;
var RepetitionMandatory = /** @class */ (function (_super) {
__extends(RepetitionMandatory, _super);
function RepetitionMandatory(definition, occurrenceInParent, name, implicitOccurrenceIndex) {
if (occurrenceInParent === void 0) { occurrenceInParent = 1; }
if (implicitOccurrenceIndex === void 0) { implicitOccurrenceIndex = false; }
var _this = _super.call(this, definition) || this;
_this.occurrenceInParent = occurrenceInParent;
_this.name = name;
_this.implicitOccurrenceIndex = implicitOccurrenceIndex;
return _this;
}
return RepetitionMandatory;
}(AbstractProduction));
gast.RepetitionMandatory = RepetitionMandatory;
var RepetitionMandatoryWithSeparator = /** @class */ (function (_super) {
__extends(RepetitionMandatoryWithSeparator, _super);
function RepetitionMandatoryWithSeparator(definition, separator, occurrenceInParent, name, implicitOccurrenceIndex) {
if (occurrenceInParent === void 0) { occurrenceInParent = 1; }
if (implicitOccurrenceIndex === void 0) { implicitOccurrenceIndex = false; }
var _this = _super.call(this, definition) || this;
_this.separator = separator;
_this.occurrenceInParent = occurrenceInParent;
_this.name = name;
_this.implicitOccurrenceIndex = implicitOccurrenceIndex;
return _this;
}
return RepetitionMandatoryWithSeparator;
}(AbstractProduction));
gast.RepetitionMandatoryWithSeparator = RepetitionMandatoryWithSeparator;
var Repetition = /** @class */ (function (_super) {
__extends(Repetition, _super);
function Repetition(definition, occurrenceInParent, name, implicitOccurrenceIndex) {
if (occurrenceInParent === void 0) { occurrenceInParent = 1; }
if (implicitOccurrenceIndex === void 0) { implicitOccurrenceIndex = false; }
var _this = _super.call(this, definition) || this;
_this.occurrenceInParent = occurrenceInParent;
_this.name = name;
_this.implicitOccurrenceIndex = implicitOccurrenceIndex;
return _this;
}
return Repetition;
}(AbstractProduction));
gast.Repetition = Repetition;
var RepetitionWithSeparator = /** @class */ (function (_super) {
__extends(RepetitionWithSeparator, _super);
function RepetitionWithSeparator(definition, separator, occurrenceInParent, name, implicitOccurrenceIndex) {
if (occurrenceInParent === void 0) { occurrenceInParent = 1; }
if (implicitOccurrenceIndex === void 0) { implicitOccurrenceIndex = false; }
var _this = _super.call(this, definition) || this;
_this.separator = separator;
_this.occurrenceInParent = occurrenceInParent;
_this.name = name;
_this.implicitOccurrenceIndex = implicitOccurrenceIndex;
return _this;
}
return RepetitionWithSeparator;
}(AbstractProduction));
gast.RepetitionWithSeparator = RepetitionWithSeparator;
var Alternation = /** @class */ (function (_super) {
__extends(Alternation, _super);
function Alternation(definition, occurrenceInParent, name, implicitOccurrenceIndex) {
if (occurrenceInParent === void 0) { occurrenceInParent = 1; }
if (implicitOccurrenceIndex === void 0) { implicitOccurrenceIndex = false; }
var _this = _super.call(this, definition) || this;
_this.occurrenceInParent = occurrenceInParent;
_this.name = name;
_this.implicitOccurrenceIndex = implicitOccurrenceIndex;
return _this;
}
return Alternation;
}(AbstractProduction));
gast.Alternation = Alternation;
var Terminal = /** @class */ (function () {
function Terminal(terminalType, occurrenceInParent, implicitOccurrenceIndex) {
if (occurrenceInParent === void 0) { occurrenceInParent = 1; }
if (implicitOccurrenceIndex === void 0) { implicitOccurrenceIndex = false; }
this.terminalType = terminalType;
this.occurrenceInParent = occurrenceInParent;
this.implicitOccurrenceIndex = implicitOccurrenceIndex;
}
Terminal.prototype.accept = function (visitor) {
visitor.visit(this);
};
return Terminal;
}());
gast.Terminal = Terminal;
var GAstVisitor = /** @class */ (function () {
function GAstVisitor() {
}
GAstVisitor.prototype.visit = function (node) {
if (node instanceof NonTerminal) {
return this.visitNonTerminal(node);
}
else if (node instanceof Flat) {
return this.visitFlat(node);
}
else if (node instanceof Option) {
return this.visitOption(node);
}
else if (node instanceof RepetitionMandatory) {
return this.visitRepetitionMandatory(node);
}
else if (node instanceof RepetitionMandatoryWithSeparator) {
return this.visitRepetitionMandatoryWithSeparator(node);
}
else if (node instanceof RepetitionWithSeparator) {
return this.visitRepetitionWithSeparator(node);
}
else if (node instanceof Repetition) {
return this.visitRepetition(node);
}
else if (node instanceof Alternation) {
return this.visitAlternation(node);
}
else if (node instanceof Terminal) {
return this.visitTerminal(node);
}
else if (node instanceof Rule) {
return this.visitRule(node);
}
else {
/* istanbul ignore next */
throw Error("non exhaustive match");
}
};
GAstVisitor.prototype.visitNonTerminal = function (node) { };
GAstVisitor.prototype.visitFlat = function (node) { };
GAstVisitor.prototype.visitOption = function (node) { };
GAstVisitor.prototype.visitRepetition = function (node) { };
GAstVisitor.prototype.visitRepetitionMandatory = function (node) { };
GAstVisitor.prototype.visitRepetitionMandatoryWithSeparator = function (node) { };
GAstVisitor.prototype.visitRepetitionWithSeparator = function (node) { };
GAstVisitor.prototype.visitAlternation = function (node) { };
GAstVisitor.prototype.visitTerminal = function (node) { };
GAstVisitor.prototype.visitRule = function (node) { };
return GAstVisitor;
}());
gast.GAstVisitor = GAstVisitor;
function serializeGrammar(topRules) {
return utils_1.map(topRules, serializeProduction);
}
gast.serializeGrammar = serializeGrammar;
function serializeProduction(node) {
function convertDefinition(definition) {
return utils_1.map(definition, serializeProduction);
}
if (node instanceof NonTerminal) {
return {
type: "NonTerminal",
name: node.nonTerminalName,
occurrenceInParent: node.occurrenceInParent
};
}
else if (node instanceof Flat) {
return {
type: "Flat",
definition: convertDefinition(node.definition)
};
}
else if (node instanceof Option) {
return {
type: "Option",
definition: convertDefinition(node.definition)
};
}
else if (node instanceof RepetitionMandatory) {
return {
type: "RepetitionMandatory",
definition: convertDefinition(node.definition)
};
}
else if (node instanceof RepetitionMandatoryWithSeparator) {
return {
type: "RepetitionMandatoryWithSeparator",
separator: serializeProduction(new Terminal(node.separator)),
definition: convertDefinition(node.definition)
};
}
else if (node instanceof RepetitionWithSeparator) {
return {
type: "RepetitionWithSeparator",
separator: serializeProduction(new Terminal(node.separator)),
definition: convertDefinition(node.definition)
};
}
else if (node instanceof Repetition) {
return {
type: "Repetition",
definition: convertDefinition(node.definition)
};
}
else if (node instanceof Alternation) {
return {
type: "Alternation",
definition: convertDefinition(node.definition)
};
}
else if (node instanceof Terminal) {
var serializedTerminal = {
type: "Terminal",
name: tokens_public_1.tokenName(node.terminalType),
label: tokens_public_1.tokenLabel(node.terminalType),
occurrenceInParent: node.occurrenceInParent
};
var pattern = node.terminalType.PATTERN;
if (node.terminalType.PATTERN) {
serializedTerminal.pattern = utils_1.isRegExp(pattern)
? pattern.source
: pattern;
}
return serializedTerminal;
}
else if (node instanceof Rule) {
// IGNORE ABOVE ELSE
return {
type: "Rule",
name: node.name,
definition: convertDefinition(node.definition)
};
}
else {
/* istanbul ignore next */
throw Error("non exhaustive match");
}
}
gast.serializeProduction = serializeProduction;
})(gast = exports.gast || (exports.gast = {}));
//# sourceMappingURL=gast_public.js.map
/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var utils_1 = __webpack_require__(0);
var lang_extensions_1 = __webpack_require__(3);
var lexer_public_1 = __webpack_require__(8);
var tokens_1 = __webpack_require__(4);
/**
* This can be used to improve the quality/readability of error messages or syntax diagrams.
*
* @param {TokenType} clazz - A constructor for a Token subclass
* @returns {string} - The Human readable label for a Token if it exists.
*/
function tokenLabel(clazz) {
if (hasTokenLabel(clazz)) {
return clazz.LABEL;
}
else {
return tokenName(clazz);
}
}
exports.tokenLabel = tokenLabel;
function hasTokenLabel(obj) {
return utils_1.isString(obj.LABEL) && obj.LABEL !== "";
}
exports.hasTokenLabel = hasTokenLabel;
function tokenName(obj) {
// The tokenName property is needed under some old versions of node.js (0.10/0.12)
// where the Function.prototype.name property is not defined as a 'configurable' property
// enable producing readable error messages.
/* istanbul ignore if -> will only run in old versions of node.js */
if (utils_1.isObject(obj) &&
obj.hasOwnProperty("tokenName") &&
utils_1.isString(obj.tokenName)) {
return obj.tokenName;
}
else {
return lang_extensions_1.functionName(obj);
}
}
exports.tokenName = tokenName;
var PARENT = "parent";
var CATEGORIES = "categories";
var LABEL = "label";
var GROUP = "group";
var PUSH_MODE = "push_mode";
var POP_MODE = "pop_mode";
var LONGER_ALT = "longer_alt";
var LINE_BREAKS = "line_breaks";
/**
* @param {ITokenConfig} config - The configuration for
* @returns {TokenType} - A constructor for the new Token subclass
*/
function createToken(config) {
return createTokenInternal(config);
}
exports.createToken = createToken;
function createTokenInternal(config) {
var tokenName = config.name;
var pattern = config.pattern;
var tokenType = {};
// can be overwritten according to:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/
// name?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FFunction%2Fname
/* istanbul ignore if -> will only run in old versions of node.js */
if (!lang_extensions_1.defineNameProp(tokenType, tokenName)) {
// hack to save the tokenName in situations where the constructor's name property cannot be reconfigured
tokenType.tokenName = tokenName;
}
if (!utils_1.isUndefined(pattern)) {
tokenType.PATTERN = pattern;
}
if (utils_1.has(config, PARENT)) {
throw "The parent property is no longer supported.\n" +
"See: [TODO-add link] for details.";
}
if (utils_1.has(config, CATEGORIES)) {
tokenType.CATEGORIES = config[CATEGORIES];
}
tokens_1.augmentTokenTypes([tokenType]);
if (utils_1.has(config, LABEL)) {
tokenType.LABEL = config[LABEL];
}
if (utils_1.has(config, GROUP)) {
tokenType.GROUP = config[GROUP];
}
if (utils_1.has(config, POP_MODE)) {
tokenType.POP_MODE = config[POP_MODE];
}
if (utils_1.has(config, PUSH_MODE)) {
tokenType.PUSH_MODE = config[PUSH_MODE];
}
if (utils_1.has(config, LONGER_ALT)) {
tokenType.LONGER_ALT = config[LONGER_ALT];
}
if (utils_1.has(config, LINE_BREAKS)) {
tokenType.LINE_BREAKS = config[LINE_BREAKS];
}
return tokenType;
}
exports.EOF = createToken({ name: "EOF", pattern: lexer_public_1.Lexer.NA });
tokens_1.augmentTokenTypes([exports.EOF]);
/**
* Utility to create Chevrotain Token "instances"
* Note that Chevrotain tokens are not real instances, and thus the instanceOf cannot be used.
*
* @param tokType
* @param image
* @param startOffset
* @param endOffset
* @param startLine
* @param endLine
* @param startColumn
* @param endColumn
* @returns {{image: string,
* startOffset: number,
* endOffset: number,
* startLine: number,
* endLine: number,
* startColumn: number,
* endColumn: number,
* tokenType}}
*/
function createTokenInstance(tokType, image, startOffset, endOffset, startLine, endLine, startColumn, endColumn) {
return {
image: image,
startOffset: startOffset,
endOffset: endOffset,
startLine: startLine,
endLine: endLine,
startColumn: startColumn,
endColumn: endColumn,
tokenTypeIdx: tokType.tokenTypeIdx,
tokenType: tokType
};
}
exports.createTokenInstance = createTokenInstance;
/**
* A Utility method to check if a token is of the type of the argument Token class.
* This utility is needed because Chevrotain tokens support "categories" which means
* A TokenType may have multiple categories, so a TokenType for the "true" literal in JavaScript
* May be both a Keyword Token and a Literal Token.
*
* @param token {IToken}
* @param tokType {TokenType}
* @returns {boolean}
*/
function tokenMatcher(token, tokType) {
return tokens_1.tokenStructuredMatcher(token, tokType);
}
exports.tokenMatcher = tokenMatcher;
//# sourceMappingURL=tokens_public.js.map
/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var utils = __webpack_require__(0);
var utils_1 = __webpack_require__(0);
function classNameFromInstance(instance) {
return functionName(instance.constructor);
}
exports.classNameFromInstance = classNameFromInstance;
var FUNC_NAME_REGEXP = /^\s*function\s*(\S*)\s*\(/;
var NAME = "name";
/* istanbul ignore next too many hacks for IE/old versions of node.js here*/
function functionName(func) {
// Engines that support Function.prototype.name OR the nth (n>1) time after
// the name has been computed in the following else block.
var existingNameProp = func.name;
if (existingNameProp) {
return existingNameProp;
}
// hack for IE and engines that do not support Object.defineProperty on function.name (Node.js 0.10 && 0.12)
var computedName = func.toString().match(FUNC_NAME_REGEXP)[1];
return computedName;
}
exports.functionName = functionName;
/**
* @returns {boolean} - has the property been successfully defined
*/
function defineNameProp(obj, nameValue) {
var namePropDescriptor = Object.getOwnPropertyDescriptor(obj, NAME);
/* istanbul ignore else -> will only run in old versions of node.js */
if (utils_1.isUndefined(namePropDescriptor) || namePropDescriptor.configurable) {
Object.defineProperty(obj, NAME, {
enumerable: false,
configurable: true,
writable: false,
value: nameValue
});
return true;
}
/* istanbul ignore next -> will only run in old versions of node.js */
return false;
}
exports.defineNameProp = defineNameProp;
/**
* simple Hashtable between a string and some generic value
* this should be removed once typescript supports ES6 style Hashtable
*/
var HashTable = /** @class */ (function () {
function HashTable() {
this._state = {};
}
HashTable.prototype.keys = function () {
return utils.keys(this._state);
};
HashTable.prototype.values = function () {
return utils.values(this._state);
};
HashTable.prototype.put = function (key, value) {
this._state[key] = value;
};
HashTable.prototype.putAll = function (other) {
this._state = utils.assign(this._state, other._state);
};
HashTable.prototype.get = function (key) {
// To avoid edge case with a key called "hasOwnProperty" we need to perform the commented out check below
// -> if (Object.prototype.hasOwnProperty.call(this._state, key)) { ... } <-
// however this costs nearly 25% of the parser's runtime.
// if someone decides to name their Parser class "hasOwnProperty" they deserve what they will get :)
return this._state[key];
};
HashTable.prototype.containsKey = function (key) {
return utils.has(this._state, key);
};
HashTable.prototype.clear = function () {
this._state = {};
};
return HashTable;
}());
exports.HashTable = HashTable;
//# sourceMappingURL=lang_extensions.js.map
/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var utils_1 = __webpack_require__(0);
var lang_extensions_1 = __webpack_require__(3);
var tokens_public_1 = __webpack_require__(2);
function tokenStructuredMatcher(tokInstance, tokConstructor) {
var instanceType = tokInstance.tokenTypeIdx;
if (instanceType === tokConstructor.tokenTypeIdx) {
return true;
}
else {
return (tokConstructor.isParent === true &&
tokConstructor.categoryMatchesMap[instanceType] === true);
}
}
exports.tokenStructuredMatcher = tokenStructuredMatcher;
// Optimized tokenMatcher in case our grammar does not use token categories
// Being so tiny it is much more likely to be in-lined and this avoid the function call overhead
function tokenStructuredMatcherNoCategories(token, tokType) {
return token.tokenTypeIdx === tokType.tokenTypeIdx;
}
exports.tokenStructuredMatcherNoCategories = tokenStructuredMatcherNoCategories;
exports.tokenShortNameIdx = 1;
exports.tokenIdxToClass = new lang_extensions_1.HashTable();
function augmentTokenTypes(tokenTypes) {
// collect the parent Token Types as well.
var tokenTypesAndParents = expandCategories(tokenTypes);
// add required tokenType and categoryMatches properties
assignTokenDefaultProps(tokenTypesAndParents);
// fill up the categoryMatches
assignCategoriesMapProp(tokenTypesAndParents);
assignCategoriesTokensProp(tokenTypesAndParents);
utils_1.forEach(tokenTypesAndParents, function (tokType) {
tokType.isParent = tokType.categoryMatches.length > 0;
});
}
exports.augmentTokenTypes = augmentTokenTypes;
function expandCategories(tokenTypes) {
var result = utils_1.cloneArr(tokenTypes);
var categories = tokenTypes;
var searching = true;
while (searching) {
categories = utils_1.compact(utils_1.flatten(utils_1.map(categories, function (currTokType) { return currTokType.CATEGORIES; })));
var newCategories = utils_1.difference(categories, result);
result = result.concat(newCategories);
if (utils_1.isEmpty(newCategories)) {
searching = false;
}
else {
categories = newCategories;
}
}
return result;
}
exports.expandCategories = expandCategories;
function assignTokenDefaultProps(tokenTypes) {
utils_1.forEach(tokenTypes, function (currTokType) {
if (!hasShortKeyProperty(currTokType)) {
exports.tokenIdxToClass.put(exports.tokenShortNameIdx, currTokType);
currTokType.tokenTypeIdx = exports.tokenShortNameIdx++;
}
// CATEGORIES? : TokenType | TokenType[]
if (hasCategoriesProperty(currTokType) &&
!utils_1.isArray(currTokType.CATEGORIES)) {
currTokType.CATEGORIES = [currTokType.CATEGORIES];
}
if (!hasCategoriesProperty(currTokType)) {
currTokType.CATEGORIES = [];
}
if (!hasExtendingTokensTypesProperty(currTokType)) {
currTokType.categoryMatches = [];
}
if (!hasExtendingTokensTypesMapProperty(currTokType)) {
currTokType.categoryMatchesMap = {};
}
if (!hasTokenNameProperty(currTokType)) {
// saved for fast access during CST building.
currTokType.tokenName = tokens_public_1.tokenName(currTokType);
}
});
}
exports.assignTokenDefaultProps = assignTokenDefaultProps;
function assignCategoriesTokensProp(tokenTypes) {
utils_1.forEach(tokenTypes, function (currTokType) {
// avoid duplications
currTokType.categoryMatches = [];
utils_1.forEach(currTokType.categoryMatchesMap, function (val, key) {
currTokType.categoryMatches.push(exports.tokenIdxToClass.get(key).tokenTypeIdx);
});
});
}
exports.assignCategoriesTokensProp = assignCategoriesTokensProp;
function assignCategoriesMapProp(tokenTypes) {
utils_1.forEach(tokenTypes, function (currTokType) {
singleAssignCategoriesToksMap([], currTokType);
});
}
exports.assignCategoriesMapProp = assignCategoriesMapProp;
function singleAssignCategoriesToksMap(path, nextNode) {
utils_1.forEach(path, function (pathNode) {
nextNode.categoryMatchesMap[pathNode.tokenTypeIdx] = true;
});
utils_1.forEach(nextNode.CATEGORIES, function (nextCategory) {
var newPath = path.concat(nextNode);
if (!utils_1.contains(newPath, nextCategory)) {
singleAssignCategoriesToksMap(newPath, nextCategory);
}
});
}
function hasShortKeyProperty(tokType) {
return utils_1.has(tokType, "tokenTypeIdx");
}
exports.hasShortKeyProperty = hasShortKeyProperty;
function hasCategoriesProperty(tokType) {
return utils_1.has(tokType, "CATEGORIES");
}
exports.hasCategoriesProperty = hasCategoriesProperty;
function hasExtendingTokensTypesProperty(tokType) {
return utils_1.has(tokType, "categoryMatches");
}
exports.hasExtendingTokensTypesProperty = hasExtendingTokensTypesProperty;
function hasExtendingTokensTypesMapProperty(tokType) {
return utils_1.has(tokType, "categoryMatchesMap");
}
exports.hasExtendingTokensTypesMapProperty = hasExtendingTokensTypesMapProperty;
function hasTokenNameProperty(tokType) {
return utils_1.has(tokType, "tokenName");
}
exports.hasTokenNameProperty = hasTokenNameProperty;
function isTokenType(tokType) {
return utils_1.has(tokType, "tokenTypeIdx");
}
exports.isTokenType = isTokenType;
//# sourceMappingURL=tokens.js.map
/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
/* tslint:disable:no-use-before-declare */
var rest_1 = __webpack_require__(10);
var gast_public_1 = __webpack_require__(1);
var utils_1 = __webpack_require__(0);
var tokens_public_1 = __webpack_require__(2);
var first_1 = __webpack_require__(15);
/* tslint:enable:no-use-before-declare */
var AbstractNextPossibleTokensWalker = /** @class */ (function (_super) {
__extends(AbstractNextPossibleTokensWalker, _super);
function AbstractNextPossibleTokensWalker(topProd, path) {
var _this = _super.call(this) || this;
_this.topProd = topProd;
_this.path = path;
_this.possibleTokTypes = [];
_this.nextProductionName = "";
_this.nextProductionOccurrence = 0;
_this.found = false;
_this.isAtEndOfPath = false;
return _this;
}
AbstractNextPossibleTokensWalker.prototype.startWalking = function () {
this.found = false;
if (this.path.ruleStack[0] !== this.topProd.name) {
throw Error("The path does not start with the walker's top Rule!");
}
// immutable for the win
this.ruleStack = utils_1.cloneArr(this.path.ruleStack).reverse(); // intelij bug requires assertion
this.occurrenceStack = utils_1.cloneArr(this.path.occurrenceStack).reverse(); // intelij bug requires assertion
// already verified that the first production is valid, we now seek the 2nd production
this.ruleStack.pop();
this.occurrenceStack.pop();
this.updateExpectedNext();
this.walk(this.topProd);
return this.possibleTokTypes;
};
AbstractNextPossibleTokensWalker.prototype.walk = function (prod, prevRest) {
if (prevRest === void 0) { prevRest = []; }
// stop scanning once we found the path
if (!this.found) {
_super.prototype.walk.call(this, prod, prevRest);
}
};
AbstractNextPossibleTokensWalker.prototype.walkProdRef = function (refProd, currRest, prevRest) {
// found the next production, need to keep walking in it
if (refProd.referencedRule.name === this.nextProductionName &&
refProd.occurrenceInParent === this.nextProductionOccurrence) {
var fullRest = currRest.concat(prevRest);
this.updateExpectedNext();
this.walk(refProd.referencedRule, fullRest);
}
};
AbstractNextPossibleTokensWalker.prototype.updateExpectedNext = function () {
// need to consume the Terminal
if (utils_1.isEmpty(this.ruleStack)) {
// must reset nextProductionXXX to avoid walking down another Top Level production while what we are
// really seeking is the last Terminal...
this.nextProductionName = "";
this.nextProductionOccurrence = 0;
this.isAtEndOfPath = true;
}
else {
this.nextProductionName = this.ruleStack.pop();
this.nextProductionOccurrence = this.occurrenceStack.pop();
}
};
return AbstractNextPossibleTokensWalker;
}(rest_1.RestWalker));
exports.AbstractNextPossibleTokensWalker = AbstractNextPossibleTokensWalker;
var NextAfterTokenWalker = /** @class */ (function (_super) {
__extends(NextAfterTokenWalker, _super);
function NextAfterTokenWalker(topProd, path) {
var _this = _super.call(this, topProd, path) || this;
_this.path = path;
_this.nextTerminalName = "";
_this.nextTerminalOccurrence = 0;
_this.nextTerminalName = tokens_public_1.tokenName(_this.path.lastTok);
_this.nextTerminalOccurrence = _this.path.lastTokOccurrence;
return _this;
}
NextAfterTokenWalker.prototype.walkTerminal = function (terminal, currRest, prevRest) {
if (this.isAtEndOfPath &&
tokens_public_1.tokenName(terminal.terminalType) === this.nextTerminalName &&
terminal.occurrenceInParent === this.nextTerminalOccurrence &&
!this.found) {
var fullRest = currRest.concat(prevRest);
var restProd = new gast_public_1.gast.Flat(fullRest);
this.possibleTokTypes = first_1.first(restProd);
this.found = true;
}
};
return NextAfterTokenWalker;
}(AbstractNextPossibleTokensWalker));
exports.NextAfterTokenWalker = NextAfterTokenWalker;
/**
* This walker only "walks" a single "TOP" level in the Grammar Ast, this means
* it never "follows" production refs
*/
var AbstractNextTerminalAfterProductionWalker = /** @class */ (function (_super) {
__extends(AbstractNextTerminalAfterProductionWalker, _super);
function AbstractNextTerminalAfterProductionWalker(topRule, occurrence) {
var _this = _super.call(this) || this;
_this.topRule = topRule;
_this.occurrence = occurrence;
_this.result = {
token: undefined,
occurrence: undefined,
isEndOfRule: undefined
};
return _this;
}
AbstractNextTerminalAfterProductionWalker.prototype.startWalking = function () {
this.walk(this.topRule);
return this.result;
};
return AbstractNextTerminalAfterProductionWalker;
}(rest_1.RestWalker));
exports.AbstractNextTerminalAfterProductionWalker = AbstractNextTerminalAfterProductionWalker;
var NextTerminalAfterManyWalker = /** @class */ (function (_super) {
__extends(NextTerminalAfterManyWalker, _super);
function NextTerminalAfterManyWalker() {
return _super !== null && _super.apply(this, arguments) || this;
}
NextTerminalAfterManyWalker.prototype.walkMany = function (manyProd, currRest, prevRest) {
if (manyProd.occurrenceInParent === this.occurrence) {
var firstAfterMany = utils_1.first(currRest.concat(prevRest));
this.result.isEndOfRule = firstAfterMany === undefined;
if (firstAfterMany instanceof gast_public_1.gast.Terminal) {
this.result.token = firstAfterMany.terminalType;
this.result.occurrence = firstAfterMany.occurrenceInParent;
}
}
else {
_super.prototype.walkMany.call(this, manyProd, currRest, prevRest);
}
};
return NextTerminalAfterManyWalker;
}(AbstractNextTerminalAfterProductionWalker));
exports.NextTerminalAfterManyWalker = NextTerminalAfterManyWalker;
var NextTerminalAfterManySepWalker = /** @class */ (function (_super) {
__extends(NextTerminalAfterManySepWalker, _super);
function NextTerminalAfterManySepWalker() {
return _super !== null && _super.apply(this, arguments) || this;
}
NextTerminalAfterManySepWalker.prototype.walkManySep = function (manySepProd, currRest, prevRest) {
if (manySepProd.occurrenceInParent === this.occurrence) {
var firstAfterManySep = utils_1.first(currRest.concat(prevRest));
this.result.isEndOfRule = firstAfterManySep === undefined;
if (firstAfterManySep instanceof gast_public_1.gast.Terminal) {
this.result.token = firstAfterManySep.terminalType;
this.result.occurrence = firstAfterManySep.occurrenceInParent;
}
}
else {
_super.prototype.walkManySep.call(this, manySepProd, currRest, prevRest);
}
};
return NextTerminalAfterManySepWalker;
}(AbstractNextTerminalAfterProductionWalker));
exports.NextTerminalAfterManySepWalker = NextTerminalAfterManySepWalker;
var NextTerminalAfterAtLeastOneWalker = /** @class */ (function (_super) {
__extends(NextTerminalAfterAtLeastOneWalker, _super);
function NextTerminalAfterAtLeastOneWalker() {
return _super !== null && _super.apply(this, arguments) || this;
}
NextTerminalAfterAtLeastOneWalker.prototype.walkAtLeastOne = function (atLeastOneProd, currRest, prevRest) {
if (atLeastOneProd.occurrenceInParent === this.occurrence) {
var firstAfterAtLeastOne = utils_1.first(currRest.concat(prevRest));
this.result.isEndOfRule = firstAfterAtLeastOne === undefined;
if (firstAfterAtLeastOne instanceof gast_public_1.gast.Terminal) {
this.result.token = firstAfterAtLeastOne.terminalType;
this.result.occurrence = firstAfterAtLeastOne.occurrenceInParent;
}
}
else {
_super.prototype.walkAtLeastOne.call(this, atLeastOneProd, currRest, prevRest);
}
};
return NextTerminalAfterAtLeastOneWalker;
}(AbstractNextTerminalAfterProductionWalker));
exports.NextTerminalAfterAtLeastOneWalker = NextTerminalAfterAtLeastOneWalker;
// TODO: reduce code duplication in the AfterWalkers
var NextTerminalAfterAtLeastOneSepWalker = /** @class */ (function (_super) {
__extends(NextTerminalAfterAtLeastOneSepWalker, _super);
function NextTerminalAfterAtLeastOneSepWalker() {
return _super !== null && _super.apply(this, arguments) || this;
}
NextTerminalAfterAtLeastOneSepWalker.prototype.walkAtLeastOneSep = function (atleastOneSepProd, currRest, prevRest) {
if (atleastOneSepProd.occurrenceInParent === this.occurrence) {
var firstAfterfirstAfterAtLeastOneSep = utils_1.first(currRest.concat(prevRest));
this.result.isEndOfRule =
firstAfterfirstAfterAtLeastOneSep === undefined;
if (firstAfterfirstAfterAtLeastOneSep instanceof gast_public_1.gast.Terminal) {
this.result.token =
firstAfterfirstAfterAtLeastOneSep.terminalType;
this.result.occurrence =
firstAfterfirstAfterAtLeastOneSep.occurrenceInParent;
}
}
else {
_super.prototype.walkAtLeastOneSep.call(this, atleastOneSepProd, currRest, prevRest);
}
};
return NextTerminalAfterAtLeastOneSepWalker;
}(AbstractNextTerminalAfterProductionWalker));
exports.NextTerminalAfterAtLeastOneSepWalker = NextTerminalAfterAtLeastOneSepWalker;
function possiblePathsFrom(targetDef, maxLength, currPath) {
if (currPath === void 0) { currPath = []; }
// avoid side effects
currPath = utils_1.cloneArr(currPath);
var result = [];
var i = 0;
function remainingPathWith(nextDef) {
return nextDef.concat(utils_1.drop(targetDef, i + 1));
}
function getAlternativesForProd(definition) {
var alternatives = possiblePathsFrom(remainingPathWith(definition), maxLength, currPath);
return result.concat(alternatives);
}
/**
* Mandatory productions will halt the loop as the paths computed from their recursive calls will already contain the
* following (rest) of the targetDef.
*
* For optional productions (Option/Repetition/...) the loop will continue to represent the paths that do not include the
* the optional production.
*/
while (currPath.length < maxLength && i < targetDef.length) {
var prod = targetDef[i];
if (prod instanceof gast_public_1.gast.Flat) {
return getAlternativesForProd(prod.definition);
}
else if (prod instanceof gast_public_1.gast.NonTerminal) {
return getAlternativesForProd(prod.definition);
}
else if (prod instanceof gast_public_1.gast.Option) {
result = getAlternativesForProd(prod.definition);
}
else if (prod instanceof gast_public_1.gast.RepetitionMandatory) {
return getAlternativesForProd(prod.definition);
}
else if (prod instanceof gast_public_1.gast.RepetitionMandatoryWithSeparator) {
var newDef = [
new gast_public_1.gast.Flat(prod.definition),
new gast_public_1.gast.Repetition([new gast_public_1.gast.Terminal(prod.separator)].concat(prod.definition))
];
return getAlternativesForProd(newDef);
}
else if (prod instanceof gast_public_1.gast.RepetitionWithSeparator) {
var newDef = prod.definition.concat([
new gast_public_1.gast.Repetition([new gast_public_1.gast.Terminal(prod.separator)].concat(prod.definition))
]);
result = getAlternativesForProd(newDef);
}
else if (prod instanceof gast_public_1.gast.Repetition) {
result = getAlternativesForProd(prod.definition);
}
else if (prod instanceof gast_public_1.gast.Alternation) {
utils_1.forEach(prod.definition, function (currAlt) {
result = getAlternativesForProd(currAlt.definition);
});
return result;
}
else if (prod instanceof gast_public_1.gast.Terminal) {
currPath.push(prod.terminalType);
}
else {
/* istanbul ignore next */
throw Error("non exhaustive match");
}
i++;
}
result.push({
partialPath: currPath,
suffixDef: utils_1.drop(targetDef, i)
});
return result;
}
exports.possiblePathsFrom = possiblePathsFrom;
function nextPossibleTokensAfter(initialDef, tokenVector, tokMatcher, maxLookAhead) {
var EXIT_NON_TERMINAL = "EXIT_NONE_TERMINAL";
// to avoid creating a new Array each time.
var EXIT_NON_TERMINAL_ARR = [EXIT_NON_TERMINAL];
var EXIT_ALTERNATIVE = "EXIT_ALTERNATIVE";
var foundCompletePath = false;
var tokenVectorLength = tokenVector.length;
var minimalAlternativesIndex = tokenVectorLength - maxLookAhead - 1;
var result = [];
var possiblePaths = [];
possiblePaths.push({
idx: -1,
def: initialDef,
ruleStack: [],
occurrenceStack: []
});
while (!utils_1.isEmpty(possiblePaths)) {
var currPath = possiblePaths.pop();
// skip alternatives if no more results can be found (assuming deterministic grammar with fixed lookahead)
if (currPath === EXIT_ALTERNATIVE) {
if (foundCompletePath &&
utils_1.last(possiblePaths).idx <= minimalAlternativesIndex) {
// remove irrelevant alternative
possiblePaths.pop();
}
continue;
}
var currDef = currPath.def;
var currIdx = currPath.idx;
var currRuleStack = currPath.ruleStack;
var currOccurrenceStack = currPath.occurrenceStack;
// For Example: an empty path could exist in a valid grammar in the case of an EMPTY_ALT
if (utils_1.isEmpty(currDef)) {
continue;
}
var prod = currDef[0];
if (prod === EXIT_NON_TERMINAL) {
var nextPath = {
idx: currIdx,
def: utils_1.drop(currDef),
ruleStack: utils_1.dropRight(currRuleStack),
occurrenceStack: utils_1.dropRight(currOccurrenceStack)
};
possiblePaths.push(nextPath);
}
else if (prod instanceof gast_public_1.gast.Terminal) {
if (currIdx < tokenVectorLength - 1) {
var nextIdx = currIdx + 1;
var actualToken = tokenVector[nextIdx];
if (tokMatcher(actualToken, prod.terminalType)) {
var nextPath = {
idx: nextIdx,
def: utils_1.drop(currDef),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(nextPath);
}
// end of the line
}
else if (currIdx === tokenVectorLength - 1) {
// IGNORE ABOVE ELSE
result.push({
nextTokenType: prod.terminalType,
nextTokenOccurrence: prod.occurrenceInParent,
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
});
foundCompletePath = true;
}
else {
/* istanbul ignore next */
throw Error("non exhaustive match");
}
}
else if (prod instanceof gast_public_1.gast.NonTerminal) {
var newRuleStack = utils_1.cloneArr(currRuleStack);
newRuleStack.push(prod.nonTerminalName);
var newOccurrenceStack = utils_1.cloneArr(currOccurrenceStack);
newOccurrenceStack.push(prod.occurrenceInParent);
var nextPath = {
idx: currIdx,
def: prod.definition.concat(EXIT_NON_TERMINAL_ARR, utils_1.drop(currDef)),
ruleStack: newRuleStack,
occurrenceStack: newOccurrenceStack
};
possiblePaths.push(nextPath);
}
else if (prod instanceof gast_public_1.gast.Option) {
// the order of alternatives is meaningful, FILO (Last path will be traversed first).
var nextPathWithout = {
idx: currIdx,
def: utils_1.drop(currDef),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(nextPathWithout);
// required marker to avoid backtracking paths whose higher priority alternatives already matched
possiblePaths.push(EXIT_ALTERNATIVE);
var nextPathWith = {
idx: currIdx,
def: prod.definition.concat(utils_1.drop(currDef)),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(nextPathWith);
}
else if (prod instanceof gast_public_1.gast.RepetitionMandatory) {
// TODO:(THE NEW operators here take a while...) (convert once?)
var secondIteration = new gast_public_1.gast.Repetition(prod.definition, prod.occurrenceInParent);
var nextDef = prod.definition.concat([secondIteration], utils_1.drop(currDef));
var nextPath = {
idx: currIdx,
def: nextDef,
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(nextPath);
}
else if (prod instanceof gast_public_1.gast.RepetitionMandatoryWithSeparator) {
// TODO:(THE NEW operators here take a while...) (convert once?)
var separatorGast = new gast_public_1.gast.Terminal(prod.separator);
var secondIteration = new gast_public_1.gast.Repetition([separatorGast].concat(prod.definition), prod.occurrenceInParent);
var nextDef = prod.definition.concat([secondIteration], utils_1.drop(currDef));
var nextPath = {
idx: currIdx,
def: nextDef,
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(nextPath);
}
else if (prod instanceof gast_public_1.gast.RepetitionWithSeparator) {
// the order of alternatives is meaningful, FILO (Last path will be traversed first).
var nextPathWithout = {
idx: currIdx,
def: utils_1.drop(currDef),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(nextPathWithout);
// required marker to avoid backtracking paths whose higher priority alternatives already matched
possiblePaths.push(EXIT_ALTERNATIVE);
var separatorGast = new gast_public_1.gast.Terminal(prod.separator);
var nthRepetition = new gast_public_1.gast.Repetition([separatorGast].concat(prod.definition), prod.occurrenceInParent);
var nextDef = prod.definition.concat([nthRepetition], utils_1.drop(currDef));
var nextPathWith = {
idx: currIdx,
def: nextDef,
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(nextPathWith);
}
else if (prod instanceof gast_public_1.gast.Repetition) {
// the order of alternatives is meaningful, FILO (Last path will be traversed first).
var nextPathWithout = {
idx: currIdx,
def: utils_1.drop(currDef),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(nextPathWithout);
// required marker to avoid backtracking paths whose higher priority alternatives already matched
possiblePaths.push(EXIT_ALTERNATIVE);
// TODO: an empty repetition will cause infinite loops here, will the parser detect this in selfAnalysis?
var nthRepetition = new gast_public_1.gast.Repetition(prod.definition, prod.occurrenceInParent);
var nextDef = prod.definition.concat([nthRepetition], utils_1.drop(currDef));
var nextPathWith = {
idx: currIdx,
def: nextDef,
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(nextPathWith);
}
else if (prod instanceof gast_public_1.gast.Alternation) {
// the order of alternatives is meaningful, FILO (Last path will be traversed first).
for (var i = prod.definition.length - 1; i >= 0; i--) {
var currAlt = prod.definition[i];
var currAltPath = {
idx: currIdx,
def: currAlt.definition.concat(utils_1.drop(currDef)),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
};
possiblePaths.push(currAltPath);
possiblePaths.push(EXIT_ALTERNATIVE);
}
}
else if (prod instanceof gast_public_1.gast.Flat) {
possiblePaths.push({
idx: currIdx,
def: prod.definition.concat(utils_1.drop(currDef)),
ruleStack: currRuleStack,
occurrenceStack: currOccurrenceStack
});
}
else if (prod instanceof gast_public_1.gast.Rule) {
// last because we should only encounter at most a single one of these per invocation.
possiblePaths.push(expandTopLevelRule(prod, currIdx, currRuleStack, currOccurrenceStack));
}
else {
/* istanbul ignore next */
throw Error("non exhaustive match");
}
}
return result;
}
exports.nextPossibleTokensAfter = nextPossibleTokensAfter;
function expandTopLevelRule(topRule, currIdx, currRuleStack, currOccurrenceStack) {
var newRuleStack = utils_1.cloneArr(currRuleStack);
newRuleStack.push(topRule.name);
var newCurrOccurrenceStack = utils_1.cloneArr(currOccurrenceStack);
// top rule is always assumed to have been called with occurrence index 1
newCurrOccurrenceStack.push(1);
return {
idx: currIdx,
def: topRule.definition,
ruleStack: newRuleStack,
occurrenceStack: newCurrOccurrenceStack
};
}
//# sourceMappingURL=interpreter.js.map
/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var cache = __webpack_require__(7);
var cache_1 = __webpack_require__(7);
var exceptions_public_1 = __webpack_require__(12);
var lang_extensions_1 = __webpack_require__(3);
var resolver_1 = __webpack_require__(21);
var checks_1 = __webpack_require__(13);
var utils_1 = __webpack_require__(0);
var follow_1 = __webpack_require__(23);
var tokens_public_1 = __webpack_require__(2);
var lookahead_1 = __webpack_require__(14);
var gast_builder_1 = __webpack_require__(24);
var interpreter_1 = __webpack_require__(5);
var constants_1 = __webpack_require__(18);
var gast_public_1 = __webpack_require__(1);
var gast_1 = __webpack_require__(9);
var tokens_1 = __webpack_require__(4);
var cst_1 = __webpack_require__(16);
var keys_1 = __webpack_require__(17);
var cst_visitor_1 = __webpack_require__(26);
var errors_public_1 = __webpack_require__(19);
var serializeGrammar = gast_public_1.gast.serializeGrammar;
var ParserDefinitionErrorType;
(function (ParserDefinitionErrorType) {
ParserDefinitionErrorType[ParserDefinitionErrorType["INVALID_RULE_NAME"] = 0] = "INVALID_RULE_NAME";
ParserDefinitionErrorType[ParserDefinitionErrorType["DUPLICATE_RULE_NAME"] = 1] = "DUPLICATE_RULE_NAME";
ParserDefinitionErrorType[ParserDefinitionErrorType["INVALID_RULE_OVERRIDE"] = 2] = "INVALID_RULE_OVERRIDE";
ParserDefinitionErrorType[ParserDefinitionErrorType["DUPLICATE_PRODUCTIONS"] = 3] = "DUPLICATE_PRODUCTIONS";
ParserDefinitionErrorType[ParserDefinitionErrorType["UNRESOLVED_SUBRULE_REF"] = 4] = "UNRESOLVED_SUBRULE_REF";
ParserDefinitionErrorType[ParserDefinitionErrorType["LEFT_RECURSION"] = 5] = "LEFT_RECURSION";
ParserDefinitionErrorType[ParserDefinitionErrorType["NONE_LAST_EMPTY_ALT"] = 6] = "NONE_LAST_EMPTY_ALT";
ParserDefinitionErrorType[ParserDefinitionErrorType["AMBIGUOUS_ALTS"] = 7] = "AMBIGUOUS_ALTS";
ParserDefinitionErrorType[ParserDefinitionErrorType["CONFLICT_TOKENS_RULES_NAMESPACE"] = 8] = "CONFLICT_TOKENS_RULES_NAMESPACE";
ParserDefinitionErrorType[ParserDefinitionErrorType["INVALID_TOKEN_NAME"] = 9] = "INVALID_TOKEN_NAME";
ParserDefinitionErrorType[ParserDefinitionErrorType["INVALID_NESTED_RULE_NAME"] = 10] = "INVALID_NESTED_RULE_NAME";
ParserDefinitionErrorType[ParserDefinitionErrorType["DUPLICATE_NESTED_NAME"] = 11] = "DUPLICATE_NESTED_NAME";
ParserDefinitionErrorType[ParserDefinitionErrorType["NO_NON_EMPTY_LOOKAHEAD"] = 12] = "NO_NON_EMPTY_LOOKAHEAD";
ParserDefinitionErrorType[ParserDefinitionErrorType["AMBIGUOUS_PREFIX_ALTS"] = 13] = "AMBIGUOUS_PREFIX_ALTS";
ParserDefinitionErrorType[ParserDefinitionErrorType["TOO_MANY_ALTS"] = 14] = "TOO_MANY_ALTS";
})(ParserDefinitionErrorType = exports.ParserDefinitionErrorType || (exports.ParserDefinitionErrorType = {}));
var IN_RULE_RECOVERY_EXCEPTION = "InRuleRecoveryException";
exports.END_OF_FILE = tokens_public_1.createTokenInstance(tokens_public_1.EOF, "", NaN, NaN, NaN, NaN, NaN, NaN);
Object.freeze(exports.END_OF_FILE);
var DEFAULT_PARSER_CONFIG = Object.freeze({
recoveryEnabled: false,
maxLookahead: 4,
ignoredIssues: {},
dynamicTokensEnabled: false,
// TODO: Document this breaking change, can it be mitigated?
// TODO: change to true
outputCst: false,
errorMessageProvider: errors_public_1.defaultErrorProvider
});
var DEFAULT_RULE_CONFIG = Object.freeze({
recoveryValueFunc: function () { return undefined; },
resyncEnabled: true
});
/**
* Convenience used to express an empty alternative in an OR (alternation).
* can be used to more clearly describe the intent in a case of empty alternation.
*
* For example:
*
* 1. without using EMPTY_ALT:
*
* this.OR([
* {ALT: () => {
* this.CONSUME1(OneTok)
* return "1"
* }},
* {ALT: () => {
* this.CONSUME1(TwoTok)
* return "2"
* }},
* {ALT: () => { // implicitly empty because there are no invoked grammar rules (OR/MANY/CONSUME...) inside this alternative.
* return "666"
* }},
* ])
*
*
* 2. using EMPTY_ALT:
*
* this.OR([
* {ALT: () => {
* this.CONSUME1(OneTok)
* return "1"
* }},
* {ALT: () => {
* this.CONSUME1(TwoTok)
* return "2"
* }},
* {ALT: EMPTY_ALT("666")}, // explicitly empty, clearer intent
* ])
*
*/
function EMPTY_ALT(value) {
if (value === void 0) { value = undefined; }
return function () {
return value;
};
}
exports.EMPTY_ALT = EMPTY_ALT;
var EOF_FOLLOW_KEY = {};
/**
* A Recognizer capable of self analysis to determine it's grammar structure
* This is used for more advanced features requiring such information.
* For example: Error Recovery, Automatic lookahead calculation.
*/
var Parser = /** @class */ (function () {
function Parser(input, tokensDictionary, config) {
if (config === void 0) { config = DEFAULT_PARSER_CONFIG; }
this._errors = [];
this.isBackTrackingStack = [];
this.RULE_STACK = [];
this.RULE_OCCURRENCE_STACK = [];
this.CST_STACK = [];
this.tokensMap = undefined;
this.definedRulesNames = [];
this.shortRuleNameToFull = new lang_extensions_1.HashTable();
this.fullRuleNameToShort = new lang_extensions_1.HashTable();
// The shortName Index must be coded "after" the first 8bits to enable building unique lookahead keys
this.ruleShortNameIdx = 256;
this.LAST_EXPLICIT_RULE_STACK = [];
this.selfAnalysisDone = false;
this.currIdx = -1;
/**
* Only used internally for storing productions as they are built for the first time.
* The final productions should be accessed from the static cache.
*/
this._productions = new lang_extensions_1.HashTable();
this.input = input;
// configuration
this.recoveryEnabled = utils_1.has(config, "recoveryEnabled")
? config.recoveryEnabled
: DEFAULT_PARSER_CONFIG.recoveryEnabled;
// performance optimization, NOOP will be inlined which
// effectively means that this optional feature does not exist
// when not used.
if (!this.recoveryEnabled) {
this.attemptInRepetitionRecovery = utils_1.NOOP;
}
this.dynamicTokensEnabled = utils_1.has(config, "dynamicTokensEnabled")
? config.dynamicTokensEnabled
: DEFAULT_PARSER_CONFIG.dynamicTokensEnabled;
this.maxLookahead = utils_1.has(config, "maxLookahead")
? config.maxLookahead
: DEFAULT_PARSER_CONFIG.maxLookahead;
this.ignoredIssues = utils_1.has(config, "ignoredIssues")
? config.ignoredIssues
: DEFAULT_PARSER_CONFIG.ignoredIssues;
this.outputCst = utils_1.has(config, "outputCst")
? config.outputCst
: DEFAULT_PARSER_CONFIG.outputCst;
this.errorMessageProvider = utils_1.defaults(config.errorMessageProvider, DEFAULT_PARSER_CONFIG.errorMessageProvider);
if (!this.outputCst) {
this.cstInvocationStateUpdate = utils_1.NOOP;
this.cstFinallyStateUpdate = utils_1.NOOP;
this.cstPostTerminal = utils_1.NOOP;
this.cstPostNonTerminal = utils_1.NOOP;
this.getLastExplicitRuleShortName = this.getLastExplicitRuleShortNameNoCst;
this.getPreviousExplicitRuleShortName = this.getPreviousExplicitRuleShortNameNoCst;
this.getPreviousExplicitRuleOccurenceIndex = this.getPreviousExplicitRuleOccurenceIndexNoCst;
this.manyInternal = this.manyInternalNoCst;
this.orInternal = this.orInternalNoCst;
this.optionInternal = this.optionInternalNoCst;
this.atLeastOneInternal = this.atLeastOneInternalNoCst;
this.manySepFirstInternal = this.manySepFirstInternalNoCst;
this.atLeastOneSepFirstInternal = this.atLeastOneSepFirstInternalNoCst;
}
this.className = lang_extensions_1.classNameFromInstance(this);
this.firstAfterRepMap = cache.getFirstAfterRepForClass(this.className);
this.classLAFuncs = cache.getLookaheadFuncsForClass(this.className);
this.cstDictDefForRule = cache.getCstDictDefPerRuleForClass(this.className);
if (!cache.CLASS_TO_DEFINITION_ERRORS.containsKey(this.className)) {
this.definitionErrors = [];
cache.CLASS_TO_DEFINITION_ERRORS.put(this.className, this.definitionErrors);
}
else {
this.definitionErrors = cache.CLASS_TO_DEFINITION_ERRORS.get(this.className);
}
if (utils_1.isArray(tokensDictionary)) {
this.tokensMap = utils_1.reduce(tokensDictionary, function (acc, tokenClazz) {
acc[tokens_public_1.tokenName(tokenClazz)] = tokenClazz;
return acc;
}, {});
}
else if (utils_1.has(tokensDictionary, "modes") &&
utils_1.every(utils_1.flatten(utils_1.values(tokensDictionary.modes)), tokens_1.isTokenType)) {
var allTokenTypes = utils_1.flatten(utils_1.values(tokensDictionary.modes));
var uniqueTokens = utils_1.uniq(allTokenTypes);
this.tokensMap = utils_1.reduce(uniqueTokens, function (acc, tokenClazz) {
acc[tokens_public_1.tokenName(tokenClazz)] = tokenClazz;
return acc;
}, {});
}
else