UNPKG

snakeparser

Version:

Casual parser generator inspired by PEG.js

373 lines (337 loc) 8.73 kB
var expressions = require("./expressions"); expressions.nop.prototype.optimize = function(disuseProduce) { return { expression: this, advance: 0, produce: 0, success: 2, constant: [], match: "", }; }; expressions.fl.prototype.optimize = function(disuseProduce) { return { expression: this, advance: 0, produce: 0, success: 0, }; }; expressions.str.prototype.optimize = function(disuseProduce) { if (this.string.length === 0) { return new expressions.nop().optimize(disuseProduce); } return { expression: this, advance: 2, produce: 0, success: 1, match: this.string, }; }; expressions.cc.prototype.optimize = function(disuseProduce) { if (this.characterClass.length === 0) { if (!this.invert) { // 必ず失敗 return new expressions.fl().optimize(disuseProduce); } else { // . と同じ return new expressions.ac().optimize(disuseProduce); } } else if (this.characterClass.length === 1 && !this.invert && this.characterClass[0].type === "single") { return new expressions.str(String.fromCharCode(this.characterClass[0].char)) .optimize(disuseProduce); } return { expression: this, advance: 2, produce: 0, success: 1, }; }; expressions.ac.prototype.optimize = function(disuseProduce) { return { expression: this, advance: 2, produce: 0, success: 1, }; }; expressions.oc.prototype.optimize = function(disuseProduce) { var advance = null; var produce = null; var success = 0; var children = []; for (var i = 0; i < this.children.length; ++i) { var res = this.children[i].optimize(disuseProduce); if (res.success === 0) continue; if (res.expression instanceof expressions.oc) { // 子供がocなら展開する [].push.apply(children, res.expression.children); } else { children.push(res.expression); } if (advance !== res.advance) advance = advance === null ? res.advance : 1; if (produce !== res.produce) produce = produce === null ? res.produce : 1; success = Math.max(success, res.success); if (res.success === 2) break; } if (children.length === 0) { return new expressions.fl().optimize(disuseProduce); } else if (children.length === 1) { return { expression: children[0], advance: advance, produce: produce, success: success, }; } else { this.children = children; return { expression: this, advance: advance, produce: produce, success: success, }; } }; expressions.seq.prototype.optimize = function(disuseProduce) { var advance = 0; var produce = 0; var success = 2; var children = []; var constant = []; var match = ""; for (var i = 0; i < this.children.length; ++i) { var res = this.children[i].optimize(disuseProduce); if (res.advance === 0 && res.produce === 0 && res.success === 2) continue; if (res.expression instanceof expressions.seq) { // 子供がseqなら展開する [].push.apply(children, res.expression.children); } else { res.expression.neverAdvance = res.advance === 0; res.expression.neverProduce = res.produce === 0; res.expression.alwaysSuccess = res.success === 2; children.push(res.expression); } advance = Math.max(advance, res.advance); produce = Math.max(produce, res.produce); success = Math.min(success, res.success); if (constant && res.constant) [].push.apply(constant, res.constant); else constant = undefined; if (typeof match === "string" && typeof res.match === "string") match += res.match; else match = undefined; } if (success === 0) { // 必ず失敗 return new expressions.fl().optimize(disuseProduce); } this.children = children; return { expression: this, advance: advance, produce: produce, success: success, constant: constant, match: match, }; }; expressions.rep.prototype.optimize = function(disuseProduce) { if (this.max === 0) { return new expressions.nop().optimize(); } var res = this.child.optimize(disuseProduce); this.child = res.expression; if (this.max === Infinity && res.success === 2) throw new Error("Infinite loop detected."); if (res.advance === 2) this.possibleInfiniteLoop = false; if (this.min === 0) { res.advance = Math.min(res.advance, 1); res.success = Math.max(res.success, 1); } if (res.constant) { // 定数化 var newConstant = []; for (var i = 0; i < this.max; ++i) [].push.apply(newConstant, res.constant); res.constant = newConstant; } if (typeof res.match === "string") { if (this.min === this.max) { var newMatch = ""; for (var i = 0; i < this.max; ++i) newMatch += res.match; res.match = newMatch; } } res.expression = this; return res; }; expressions.obj.prototype.optimize = function(disuseProduce) { var res = this.child.optimize(disuseProduce); if (disuseProduce) return res; if (res.constant) { // 定数化 var value = {}; for (var i = 0; i < res.constant.length; i += 2) value[res.constant[i + 1]] = res.constant[i]; res.expression = new expressions.ltr(value); res.produce = 2; res.constant = [value]; } else { this.child = res.expression; res.produce = 2; res.expression = this; res.constant = undefined; } return res; }; expressions.arr.prototype.optimize = function(disuseProduce) { var res = this.child.optimize(disuseProduce); if (disuseProduce) return res; if (res.constant) { // 定数化 var value = {}; res.expression = new expressions.ltr(res.constant); res.produce = 2; res.constant = [res.constant]; } else { this.child = res.expression; res.produce = 2; res.expression = this; } return res; }; expressions.pr.prototype.optimize = function(disuseProduce) { var res = this.child.optimize(disuseProduce); if (disuseProduce) return res; this.child = res.expression; if (res.constant) { // 定数化 res.expression = this; res.advance = 0; res.produce = 2; res.success = 2; res.constant = [res.constant[0], this.key]; } else { res.produce = 2; res.expression = this; res.constant = undefined; } return res; }; expressions.tkn.prototype.optimize = function(disuseProduce) { var res = this.child.optimize(disuseProduce); if (disuseProduce) return res; this.child = res.expression; res.produce = 2; res.expression = this; if (res.constant && typeof res.match === "string") res.constant.push(res.match); else res.constant = undefined; if (typeof res.match === "string" && res.match.length <= 20) this.product = res.match; return res; }; expressions.ltr.prototype.optimize = function(disuseProduce) { if (disuseProduce) { return new expressions.nop().optimize(disuseProduce); } return { expression: this, advance: 0, produce: 2, success: 2, constant: [this.value], match: "", }; }; expressions.cv.prototype.optimize = function(disuseProduce) { if (disuseProduce) { return new expressions.nop().optimize(disuseProduce); } return { expression: this, advance: 0, produce: 2, success: 2, match: "", }; }; expressions.pla.prototype.optimize = function(disuseProduce) { var res = this.child.optimize(true); if (res.success === 0) { res.expression = new expressions.fl(); } else if (res.success === 2) { res.expression = new expressions.nop(); } else { this.child = res.expression; res.expression = this; } res.advance = 0; res.produce = 0; res.constant = undefined; res.match = ""; return res; }; expressions.nla.prototype.optimize = function(disuseProduce) { var res = this.child.optimize(true); if (res.success === 0) { res.expression = new expressions.fl(); } else if (res.success === 2) { res.expression = new expressions.nop(); } else { this.child = res.expression; res.expression = this; } res.advance = 0; res.produce = 0; res.success = 2 - res.success; res.constant = undefined; res.match = ""; return res; }; expressions.mod.prototype.optimize = function(disuseProduce) { var res = this.child.optimize(false); this.child = res.expression; res.produce = 2; res.expression = this; res.constant = undefined; return res; }; expressions.grd.prototype.optimize = function(disuseProduce) { var res = this.child.optimize(false); this.child = res.expression; res.produce = 2; res.success = 1; res.expression = this; res.constant = undefined; return res; }; expressions.wst.prototype.optimize = function(disuseProduce) { var res = this.child.optimize(true); this.child = res.expression; res.produce = 0; res.expression = this; res.constant = undefined; return res; }; expressions.rul.prototype.optimize = function(disuseProduce) { return { expression: this, advance: 1, produce: 1, success: 1, }; }; var ruleOptimize = function(rule) { rule.body = rule.body.optimize(false).expression; }; module.exports = ruleOptimize;