UNPKG

snakeparser

Version:

Casual parser generator inspired by PEG.js

748 lines (682 loc) 26.2 kB
var expressions = require("./expressions"); var ruleOptimize = require("./optimize"); var jsLiteralify = require("./jsLiteralify"); var indentStr = "\t"; var makeIndent = function(level) { return new Array(level + 1).join(indentStr); }; var addIndent = function(str, level) { if (str === "") return str; var indent = makeIndent(level); return indent + str.replace(/\n(?!$)/g, "\n" + indent); }; var newId = function(ids, name) { if (name in ids) return name + ++ids[name]; else return name + (ids[name] = 0); }; var makeVarState = function(vs, indentLevel) { vs = vs.filter(function(v) { if (v instanceof Object) return v[0]; else return v; }); vs = vs.map(function(v) { if (v instanceof Object) return v[0] + (v[1] ? " = " + v[1] : ""); else return v; }); if (vs.length) return makeIndent(indentLevel) + "var " + vs.join(", ") + ";\n"; else return ""; }; var stringEscape = function(str) { return str .replace(/\\/g, "\\\\") .replace(/"/g, "\\\"") .replace(/\x08/g, "\\b") .replace(/\t/g, "\\t") .replace(/\n/g, "\\n") .replace(/\f/g, "\\f") .replace(/\r/g, "\\r") .replace(/[\x00-\x07\x0B\x0E\x0F\x10-\x1F\x80-\xFF]/g, function(c) { return "\\x" + ("0" + c.charCodeAt().toString(16)).slice(-2); }) .replace(/[\u0100-\uFFFF]/g, function(c) { return "\\u" + ("000" + c.charCodeAt().toString(16)).slice(-4); }); }; var charCodeToRegexpClassChar = function(cc) { switch (cc) { case 92: // backslash case 47: // slash case 93: // closing bracket case 94: // caret case 45: // dash return "\\" + String.fromCharCode(cc); case 0: // null return "\\0"; case 9: // horizontal tab return "\\t"; case 10: // line feed return "\\n"; case 11: // vertical tab return "\\v"; case 12: // form feed return "\\f"; case 13: // carriage return return "\\r"; } if (0x00 <= cc && cc <= 0x08 || cc === 0x0e || cc === 0x0f || 0x10 <= cc && cc <= 0x1f || 0x80 <= cc && cc <= 0xFF) return "\\x" + ("0" + cc.toString(16)).slice(-2); if (0x100 <= cc && cc <= 0xffff) return "\\u" + ("000" + cc.toString(16)).slice(-4); return String.fromCharCode(cc); }; var makeErrorLogging = function(match, indentLevel) { var matchStr = '"' + stringEscape(match) + '"'; var indent = makeIndent(indentLevel); return indent + "$matchingFail(" + matchStr + ");\n"; }; expressions.nop.prototype.gen = function(ids, pos, objsLen, indentLevel) { return ""; }; expressions.fl.prototype.gen = function(ids, pos, objsLen, indentLevel) { return makeIndent(indentLevel) + "$pos = -1;\n"; }; expressions.oc.prototype.gen = function(ids, pos, objsLen, indentLevel) { if (this.children.length === 1) return this.children[0].gen(ids, pos, objsLen, indentLevel); var indent = makeIndent(indentLevel); var posV, objsLenV; pos = pos || (posV = newId(ids, "pos")); objsLen = objsLen || (objsLenV = newId(ids, "objsLen")); var states = []; states.push(makeVarState([[posV, "$pos"], [objsLenV, "$objsLen"]], indentLevel)); var ids1 = {}; ids1.__proto__ = ids; states.push(this.children[0].gen(ids1, pos, objsLen, indentLevel)); var nest = 0; for (var i = 1; i < this.children.length; ++i) { states.push(makeIndent(indentLevel + nest++) + "if ($pos === -1) {\n"); states.push(makeIndent(indentLevel + nest) + "$pos = " + pos + ";\n"); states.push(makeIndent(indentLevel + nest) + "$objsLen = " + objsLen + ";\n"); var ids1 = {}; ids1.__proto__ = ids; states.push(this.children[i].gen(ids1, pos, objsLen, indentLevel + nest)); } while (nest) states.push(makeIndent(indentLevel + --nest) + "}\n"); return states.join(""); }; expressions.seq.prototype.gen = function(ids, pos, objsLen, indentLevel) { if (this.children.length === 1) return this.children[0].gen(ids, pos, objsLen, indentLevel); var indent = makeIndent(indentLevel); var states = []; var ids1 = {}; ids1.__proto__ = ids; var checkSuccess = false; var nest = 0; for (var i = 0; i < this.children.length; ++i) { var ids1 = {}; ids1.__proto__ = ids; if (checkSuccess) states.push(makeIndent(indentLevel + nest++) + "if ($pos !== -1) {\n"); states.push(this.children[i].gen(ids1, pos, objsLen, indentLevel + nest)); if (!this.children[i].neverAdvance) pos = null; if (!this.children[i].neverProduce) objsLen = null; checkSuccess = !this.children[i].alwaysSuccess; } while (nest) states.push(makeIndent(indentLevel + --nest) + "}\n"); return states.join(""); }; expressions.rep.prototype.gen = function(ids, pos, objsLen, indentLevel) { var indent = makeIndent(indentLevel); if (this.min === 0 && this.max === 1) { var posV, objsLenV; pos = pos || (posV = newId(ids, "pos")); objsLen = objsLen || (objsLenV = newId(ids, "objsLen")); var states = []; states.push(makeVarState([[posV, "$pos"], [objsLenV, "$objsLen"]], indentLevel)); states.push(this.child.gen(ids, pos, objsLen, indentLevel)); states.push(indent + "if ($pos === -1) {\n"); states.push(indent + indentStr + "$pos = " + pos + ";\n"); states.push(indent + indentStr + "$objsLen = " + objsLen + ";\n"); states.push(indent + "}\n"); return states.join(""); } else { pos = newId(ids, "pos"); objsLen = newId(ids, "objsLen"); var i = newId(ids, "i"); var states = []; states.push(makeVarState([[pos, "$pos"], [objsLen, "$objsLen"]], indentLevel)); if (this.max != Infinity) { states.push(indent + "for (var " + i + " = 0; " + i + " < " + this.max + "; " + i + "++) {\n"); } else if (0 < this.min) { states.push(indent + "for (var " + i + " = 0; ; " + i + "++) {\n"); } else { states.push(indent + "while (true) {\n"); } states.push(this.child.gen(ids, pos, objsLen, indentLevel + 1)); states.push(indent + indentStr + "if ($pos !== -1) {\n"); if (this.possibleInfiniteLoop) { states.push(indent + indentStr + indentStr + "if ($pos === " + pos + ") {\n"); states.push(indent + indentStr + indentStr + indentStr + "throw new Error(\"Infinite loop detected.\");\n"); states.push(indent + indentStr + indentStr + "}\n"); } states.push(indent + indentStr + indentStr + pos + " = $pos;\n"); states.push(indent + indentStr + indentStr + objsLen + " = $objsLen;\n"); states.push(indent + indentStr + "} else {\n"); states.push(indent + indentStr + indentStr + "break;\n"); states.push(indent + indentStr + "}\n"); states.push(indent + "}\n"); states.push(indent + "$pos = " + pos + ";\n"); states.push(indent + "$objsLen = " + objsLen + ";\n"); if (0 < this.min) { states.push(indent + "if (" + i + " < " + this.min + ") {\n"); states.push(indent + indentStr + "$pos = -1;\n"); states.push(indent + "}\n"); } return states.join(""); } }; expressions.str.prototype.gen = function(ids, pos, objsLen, indentLevel) { if (this.string.length === 0) return ""; var indent = makeIndent(indentLevel); var states = []; if (this.string.length !== 1) states.push(indent + "if ($input.substr($pos, " + this.string.length + ") === " + jsLiteralify(this.string) + ") {\n"); else states.push(indent + "if ($input.charCodeAt($pos) === " + this.string.charCodeAt() + ") {\n"); states.push(indent + indentStr + "$pos += " + this.string.length + ";\n"); states.push(indent + "} else {\n"); states.push(makeErrorLogging(jsLiteralify(this.string), indentLevel + 1)); states.push(indent + "}\n"); return states.join(""); }; expressions.cc.prototype.gen = function(ids, pos, objsLen, indentLevel) { var indent = makeIndent(indentLevel); var states = []; if (this.characterClass.length < 4) { // 適当 var c = "c"; states.push(indent + "var " + c + " = $input.charCodeAt($pos);\n"); states.push(indent + "if (" + this.makeCondition(c) + ") {\n"); } else { states.push(indent + "if (/" + this.makeRegexp() + "/.test($input.charAt($pos))) {\n"); } states.push(indent + indentStr + "$pos += 1;\n"); states.push(indent + "} else {\n"); states.push(makeErrorLogging(this.makeRegexp(), indentLevel + 1)); states.push(indent + "}\n"); return states.join(""); }; expressions.cc.prototype.makeCondition = function(c) { var conds = []; if (!this.invert) { for (var i in this.characterClass) { var cc = this.characterClass[i]; if (cc.type === "range") conds.push(cc.start + " <= " + c + " && " + c + " <= " + cc.end); else conds.push(c + " === " + cc.char); } return conds.length === 0 ? "false" : conds.join(" || "); } else { for (var i in this.characterClass) { var cc = this.characterClass[i]; if (cc.type === "range") conds.push("(" + c + " < " + cc.start + " || " + cc.end + " < " + c + ")"); else conds.push(c + " !== " + cc.char); } return conds.length === 0 ? true : "!isNaN(" + c + ") && " + conds.join(" && "); } }; expressions.cc.prototype.makeRegexp = function() { return (this.invert ? "[^" : "[") + this.characterClass.map( function(x) { if (x.type == "range") return charCodeToRegexpClassChar(x.start) + "-" + charCodeToRegexpClassChar(x.end); else return charCodeToRegexpClassChar(x.char); }).join("") + "]"; }; expressions.ac.prototype.gen = function(ids, pos, objsLen, indentLevel) { var indent = makeIndent(indentLevel); var states = []; states.push(indent + "if ($pos < $inputLength) {\n"); states.push(indent + indentStr + "$pos += 1;\n"); states.push(indent + "} else {\n"); states.push(makeErrorLogging(".", indentLevel + 1)); states.push(indent + "}\n"); return states.join(""); }; expressions.obj.prototype.gen = function(ids, pos, objsLen, indentLevel) { var indent = makeIndent(indentLevel); var objsLenV; objsLen = objsLen || (objsLenV = newId(ids, "objsLen")); var obj = newId(ids, "obj"); var states = []; states.push(makeVarState([[objsLenV, "$objsLen"]], indentLevel)); states.push(this.child.gen(ids, pos, objsLen, indentLevel)); states.push(indent + "if ($pos !== -1) {\n"); states.push(indent + indentStr + "var " + obj + " = {};\n"); states.push(indent + indentStr + "for (var i = " + objsLen + "; i < $objsLen; i += 2)\n"); states.push(indent + indentStr + indentStr + obj + "[$objs[i + 1]] = $objs[i];\n"); states.push(indent + indentStr + "$objsLen = " + objsLen + " + 1;\n"); states.push(indent + indentStr + "$objs[" + objsLen + "] = " + obj + ";\n"); states.push(indent + "}\n"); return states.join(""); }; expressions.pr.prototype.gen = function(ids, pos, objsLen, indentLevel) { var indent = makeIndent(indentLevel); if (this.child instanceof expressions.ltr) { var states = []; states.push(indent + "$objs[$objsLen++] = " + jsLiteralify(this.child.value) + ";\n"); states.push(indent + "$objs[$objsLen++] = " + jsLiteralify(this.key) + ";\n"); return states.join(""); } else { var objsLenV; objsLen = objsLen || (objsLenV = newId(ids, "objsLen")); var states = []; states.push(makeVarState([[objsLenV, "$objsLen"]], indentLevel)); states.push(this.child.gen(ids, pos, objsLen, indentLevel)); states.push(indent + "if ($pos !== -1) {\n"); states.push(indent + indentStr + "if ($objsLen === " + objsLen + ")\n"); states.push(indent + indentStr + indentStr + "$objs[" + objsLen + "] = undefined;\n"); states.push(indent + indentStr + "$objs[" + objsLen + " + 1] = " + jsLiteralify(this.key) + ";\n"); states.push(indent + indentStr + "$objsLen = " + objsLen + " + 2;\n"); states.push(indent + "}\n"); return states.join(""); } }; expressions.arr.prototype.gen = function(ids, pos, objsLen, indentLevel) { var indent = makeIndent(indentLevel); var objsLenV; objsLen = objsLen || (objsLenV = newId(ids, "objsLen")); var states = []; states.push(makeVarState([[objsLenV, "$objsLen"]], indentLevel)); states.push(this.child.gen(ids, pos, objsLen, indentLevel)); states.push(indent + "if ($pos !== -1) {\n"); states.push(indent + indentStr + "$objs[" + objsLen + "] = $objs.slice(" + objsLen + ", $objsLen);\n"); states.push(indent + indentStr + "$objsLen = " + objsLen + " + 1;\n"); states.push(indent + "}\n"); return states.join(""); }; expressions.tkn.prototype.gen = function(ids, pos, objsLen, indentLevel) { var indent = makeIndent(indentLevel); var posV; pos = pos || (posV = newId(ids, "pos")); var states = []; states.push(makeVarState([[posV, "$pos"]], indentLevel)); states.push(this.child.gen(ids, pos, objsLen, indentLevel)); states.push(indent + "if ($pos !== -1) {\n"); if (this.product !== undefined) states.push(indent + indentStr + "$objs[$objsLen++] = " + jsLiteralify(this.product) + ";\n"); else states.push(indent + indentStr + "$objs[$objsLen++] = $input.substring(" + pos + ", $pos);\n"); states.push(indent + "}\n"); return states.join(""); }; expressions.ltr.prototype.gen = function(ids, pos, objsLen, indentLevel) { var indent = makeIndent(indentLevel); var states = []; states.push(indent + "$objs[$objsLen++] = " + jsLiteralify(this.value) + ";\n"); return states.join(""); }; expressions.cv.prototype.gen = function(ids, pos, objsLen, indentLevel) { var indent = makeIndent(indentLevel); var states = []; switch (this.variable) { case "input": states.push(indent + "$objs[$objsLen++] = $input;\n"); break; case "pos": states.push(indent + "$objs[$objsLen++] = $pos;\n"); break; case "row": states.push(indent + "$objs[$objsLen++] = ($input.slice(0, $pos).match(/\\r\\n|\\r|\\n/g) || []).length;\n"); break; case "column": states.push(indent + '$objs[$objsLen++] = $pos - Math.max($input.lastIndexOf("\\r", $pos - 1), $input.lastIndexOf("\\n", $pos - 1));\n'); break; } return states.join(""); }; expressions.pla.prototype.gen = function(ids, pos, objsLen, indentLevel) { var indent = makeIndent(indentLevel); var posV, objsLenV; pos = pos || (posV = newId(ids, "pos")); objsLen = objsLen || (objsLenV = newId(ids, "objsLen")); var states = []; states.push(makeVarState([[posV, "$pos"], [objsLenV, "$objsLen"]], indentLevel)); states.push(this.child.gen(ids, pos, objsLen, indentLevel)); states.push(indent + "if ($pos !== -1) {\n"); states.push(addIndent("$objsLen = " + objsLen + ";\n" + "$pos = " + pos + ";\n", indentLevel + 1)); states.push(indent + "}\n"); return states.join(""); }; expressions.nla.prototype.gen = function(ids, pos, objsLen, indentLevel) { var indent = makeIndent(indentLevel); var posV, objsLenV; pos = pos || (posV = newId(ids, "pos")); objsLen = objsLen || (objsLenV = newId(ids, "objsLen")); var states = []; states.push(makeVarState([[posV, "$pos"], [objsLenV, "$objsLen"]], indentLevel)); states.push(this.child.gen(ids, pos, objsLen, indentLevel)); states.push(indent + "if ($pos === -1) {\n"); states.push(addIndent("$objsLen = " + objsLen + ";\n" + "$pos = " + pos + ";\n", indentLevel + 1)); states.push(indent + "} else {\n"); states.push(indent + indentStr + "$pos = -1;\n"); states.push(indent + "}\n"); return states.join(""); }; // エラーロギング ! expressions.mod.prototype.gen = function(ids, pos, objsLen, indentLevel) { var indent = makeIndent(indentLevel); var objsLenV; objsLen = objsLen || (objsLenV = newId(ids, "objsLen")); var states = []; states.push(makeVarState([[objsLenV, "$objsLen"]], indentLevel)); states.push(this.child.gen(ids, pos, objsLen, indentLevel)); states.push(indent + "if ($pos !== -1) {\n"); states.push(indent + indentStr + "$objs[" + objsLen + "] = " + resolveIdentifier(this) + "($objs[" + objsLen + "]);\n"); states.push(indent + indentStr + "$objsLen = " + objsLen + " + 1;\n"); states.push(indent + "}\n"); return states.join(""); }; expressions.grd.prototype.gen = function(ids, pos, objsLen, indentLevel) { var indent = makeIndent(indentLevel); var posV, objsLenV; pos = pos || (posV = newId(ids, "pos")); objsLen = objsLen || (objsLenV = newId(ids, "objsLen")); var states = []; states.push(makeVarState([[objsLenV, "$objsLen"], [posV, "$pos"]], indentLevel)); states.push(this.child.gen(ids, pos, objsLen, indentLevel)); states.push(indent + "if ($pos !== -1 && !" + resolveIdentifier(this) + "($objs[" + objsLen + "])) {\n"); states.push(indent + indentStr + "$pos = " + pos + ";\n"); states.push(makeErrorLogging("a guarded expression", indentLevel + 1)); states.push(indent + "}\n"); return states.join(""); }; function resolveIdentifier(exp) { if (exp.identifier) { return exp.identifier; } else { return resolveIdentifier(exp.identifierPlaceholder); } } expressions.wst.prototype.gen = function(ids, pos, objsLen, indentLevel) { var indent = makeIndent(indentLevel); var objsLenV; objsLen = objsLen || (objsLenV = newId(ids, "objsLen")); var states = []; states.push(makeVarState([[objsLenV, "$objsLen"]], indentLevel)); states.push(this.child.gen(ids, pos, objsLen, indentLevel)); states.push(indent + "$objsLen = " + objsLen + ";\n"); return states.join(""); }; expressions.rul.prototype.gen = function(ids, pos, objsLen, indentLevel) { if (this.arguments && this.rule) { // 引数付きルールの呼び出し return this.body.gen(ids, pos, objsLen, indentLevel); } var indent = makeIndent(indentLevel); var states = []; states.push(indent + "rule$" + this.ruleIdent + "();\n"); return states.join(""); }; var genRule = function(rule, memoRules, useUndet, indentLevel) { var indent = makeIndent(indentLevel); var ids = {}; var key = "key"; var keyValue = "$pos * " + memoRules.length + " + " + memoRules.indexOf(rule.ident); if (memoRules.indexOf(rule.ident) === -1) key = null; var pos = newId(ids, "pos"); var setMatchTable = ""; var unsetMatchTable = ""; if (rule.name) { setMatchTable = "if (!$matchTable[" + pos + "]) {\n" + indentStr + "$matchTable[" + pos + "] = " + jsLiteralify(rule.name) + ";\n" + "}\n"; unsetMatchTable = "if ($matchTable[" + pos + "] === " + jsLiteralify(rule.name) + ") {\n" + indentStr + "$matchTable[" + pos + "] = null;\n" + "}\n"; } if (rule.leftRecurs) { // 左再帰対応 var objs = newId(ids, "objs"); var states = []; states.push("function rule$" + rule.ident + "() {\n"); states.push(makeVarState([[key, keyValue]], indentLevel + 1)); states.push(indent + indentStr + "if ($readMemo(" + key + ")){\n"); states.push(indent + indentStr + indentStr + "return;\n"); states.push(indent + indentStr + "}\n"); states.push(addIndent(setMatchTable, indentLevel + 1)); states.push(indent + indentStr + "$objs.length = $objsLen;\n"); states.push(makeVarState([[pos, "$pos"], [objs, "$objs"], ["rpos", "-1"]], indentLevel + 1)); states.push(indent + indentStr + "$undet[" + pos + "] = ($undet[" + pos + "] || 0) + 1;\n"); states.push(indent + indentStr + "$memo[" + key + "] = $failureObj;\n"); states.push(indent + indentStr + "while (true) {\n"); states.push(indent + indentStr + indentStr + "$pos = " + pos + ";\n"); states.push(indent + indentStr + indentStr + "$objs = [];\n"); states.push(indent + indentStr + indentStr + "$objsLen = 0;\n"); states.push(rule.body.gen(ids, pos, "0", indentLevel + 2)); states.push(indent + indentStr + indentStr + "if ($pos === -1 || $pos <= rpos) {\n"); states.push(indent + indentStr + indentStr + indentStr + "break;\n"); states.push(indent + indentStr + indentStr + "}\n"); states.push(indent + indentStr + indentStr + "rpos = $pos;\n"); states.push(indent + indentStr + indentStr + "$objs.length = $objsLen;\n"); states.push(indent + indentStr + indentStr + "$writeMemo(" + key + ", $pos !== -1 && $objs);\n"); states.push(indent + indentStr + "}\n"); states.push(indent + indentStr + "$objs = " + objs + ";\n"); states.push(indent + indentStr + "$objsLen = $objs.length;\n"); states.push(indent + indentStr + "$readMemo(" + key + ");\n"); states.push(indent + indentStr + "if (--$undet[" + pos + "]) {\n"); states.push(indent + indentStr + indentStr + "delete $memo[" + key + "];\n"); states.push(indent + indentStr + "}\n"); states.push(addIndent(unsetMatchTable, indentLevel + 1)); states.push(indent + "}"); return states.join(""); } else { // 左再帰非対応 var objsLen = newId(ids, "objsLen"); var states = []; states.push("function rule$" + rule.ident + "() {\n"); states.push(makeVarState([[key, keyValue], [pos, "$pos"], [objsLen, "$objsLen"]], indentLevel + 1)); if (key) { states.push(indent + indentStr + "if ($readMemo(" + key + ")) {\n"); states.push(indent + indentStr + indentStr + "return;\n"); states.push(indent + indentStr + "}\n"); } states.push(addIndent(setMatchTable, indentLevel + 1)); states.push(rule.body.gen(ids, pos, objsLen, indentLevel + 1)); states.push(addIndent(unsetMatchTable, indentLevel + 1)); if (key) { if (useUndet) { states.push(indent + indentStr + "if (!$undet[" + pos + "]) {\n"); states.push(indent + indentStr + indentStr + "$writeMemo(" + key + ", $pos !== -1 && $objs.slice(" + objsLen + ", $objsLen));\n"); states.push(indent + indentStr + "}\n"); } else { states.push(indent + indentStr + "$writeMemo(" + key + ", $pos !== -1 && $objs.slice(" + objsLen + ", $objsLen));\n"); } } states.push(indent + "}"); return states.join(""); } }; var genjs = function(rules, initializer, options) { options = options || {}; for (var s in rules) { if (rules[s].parameters) { // 引数付きルール var shadowedRules = {}; shadowedRules.__proto__ = rules; for (var j in rules[s].parameters) shadowedRules[rules[s].parameters[j]] = "argument"; rules[s].body.prepare(shadowedRules); } else { rules[s].body.prepare(rules); } } // 再帰している引数付きルールを見つける for (var s in rules) { if (rules[s].parameters && rules[s].body.isRecursive(s, [])) rules[s].recursive = true; } // 引数付きでないルールの展開 for (var s in rules) { if (!rules[s].parameters) rules[s].body.expand({}); } // 再帰している引数付きルールの特殊化 var reduceds = []; for (var s in rules) if (rules[s].recursive) [].push.apply(reduceds, rules[s].reduceds); for (var i in reduceds) { rules[reduceds[i].ruleIdent] = { ident: reduceds[i].ruleIdent, body: reduceds[i].body, referenceCount: 1, //? }; } var useUndet = false; for (var s in rules) { if (!rules[s].parameters) { // 引数なしルール var b = rules[s].body.canLeftRecurs(rules[s].ident, []) === 1; rules[s].leftRecurs = b; useUndet = useUndet || b; } } var memoRules = []; for (var s in rules) { if (!rules[s].parameters) { // 引数なしルール if (rules[s].referenceCount) memoRules.push(s); } } var states = []; states.push("(function() {\n"); states.push(indentStr + "\"use strict\";\n"); states.push(addIndent(sign, 1)); states.push("\n"); states.push(indentStr + 'function $parse($input, options) {\n'); states.push(addIndent('options = options || {};\n\ var $inputLength = $input.length;\n\ var $pos = 0;\n\ var $objs = [];\n\ var $objsLen = 0;\n\ var $memo = [];\n\ var $matchTable = new Array($inputLength);\n\ var $failMatchs = [];\n\ var $failPos = 0;\n\ var $failureObj = {};\n\ ', 2)); if (useUndet) states.push(indentStr + indentStr + "var $undet = new Array($inputLength);\n"); // initializer states.push(initializer); states.push("\n\n"); // modifiers and guards var functions = {}; var functionId = 0; for (var r in rules) { rules[r].body.traverse(function(expr) { if (expr instanceof expressions.mod || expr instanceof expressions.grd) { if (!expr.identifier) { if (!functions[expr.code]) { functions[expr.code] = expr.identifier = "func$" + functionId++; states.push(makeIndent(2) + "function " + expr.identifier + "($) {" + expr.code + "}" + "\n\n"); } else { expr.identifier = functions[expr.code]; } } } }); } // rules for (var key in rules) { if (!rules[key].parameters) { ruleOptimize(rules[key]); states.push(makeIndent(2) + genRule(rules[key], memoRules, useUndet, 2) + "\n\n"); } } states.push(addIndent('function $matchingFail(match) {\n\ if ($failPos <= $pos) {\n\ match = $matchTable[$pos] ? $matchTable[$pos] : match;\n\ if ($failPos === $pos) {\n\ $failMatchs.push(match);\n\ } else {\n\ $failMatchs.length = 0;\n\ $failMatchs[0] = match;\n\ $failPos = $pos;\n\ }\n\ }\n\ $pos = -1;\n\ }', 2) + "\n\n"); states.push(addIndent($joinWithOr.toString(), 2) + "\n\n"); states.push(addIndent('function $readMemo(key) {\n\ var res = $memo[key];\n\ if (res !== undefined) {\n\ if (res !== $failureObj) {\n\ $pos = res.pos;\n\ for (var i = 0, il = res.objs.length; i < il; ++i) {\n\ $objs[$objsLen++] = res.objs[i];\n\ }\n\ } else {\n\ $pos = -1;\n\ }\n\ return true;\n\ }\n\ return false;\n\ }', 2) + "\n\n"); states.push(addIndent('function $writeMemo(key, objs) {\n\ $memo[key] = objs ? {\n\ pos: $pos,\n\ objs: objs\n\ } : $failureObj;\n\ }', 2) + "\n\n"); states.push(addIndent(' rule$start();\n\ if ($pos !== -1) {\n\ if ($pos === $inputLength) {\n\ $objs.length = $objsLen;\n\ return $objs[0];\n\ }\n\ $matchingFail("end of input");\n\ }\n\ if ($failMatchs.length === 0) {\n\ $failMatchs.push("something");\n\ }\n\ $failMatchs = $failMatchs.filter(function (x, i, self) {\n\ return self.indexOf(x) === i;\n\ });\n\ var $line = ($input.slice(0, $failPos).match(/\\r\\n|\\r|\\n/g) || []).length;\n\ var $column = $failPos - Math.max($input.lastIndexOf("\\r", $failPos - 1), $input.lastIndexOf("\\n", $failPos - 1));\n\ var $errorMessage = "Line " + ($line + 1) + ", column " + $column + ": Expected " + $joinWithOr($failMatchs) + " but " + (JSON.stringify($input[$failPos]) || "end of input") + " found.";\n\ throw new Error($errorMessage);\n\ }\n', 1)); states.push(indentStr + "return $parse;\n"); states.push("})()"); if (options.exportVariable) { states.unshift(options.exportVariable + " = "); states.push(";\n"); } return states.join(""); }; function $joinWithOr(strs) { if (strs.length === 0) { return ""; } if (strs.length === 1) { return strs[0]; } return strs.slice(0, strs.length - 1).join(", ") + " or " + strs[strs.length - 1]; } var version = require("./version"); var sign = "/*\n * Generated by snake parser " + version + "\n */"; module.exports = genjs;