| /* |
| MIT License http://www.opensource.org/licenses/mit-license.php |
| Author Tobias Koppers @sokra |
| */ |
| |
| function ignoreFunction() {} |
| |
| function createReturningFunction(value) { |
| return function() { |
| return value; |
| }; |
| } |
| |
| function Parser(states) { |
| this.states = this.compileStates(states); |
| } |
| |
| Parser.prototype.compileStates = function(states) { |
| var result = {}; |
| Object.keys(states).forEach(function(name) { |
| result[name] = this.compileState(states[name], states); |
| }, this); |
| return result; |
| }; |
| |
| Parser.prototype.compileState = function(state, states) { |
| var regExps = []; |
| function iterator(str, value) { |
| regExps.push({ |
| groups: Parser.getGroupCount(str), |
| regExp: str, |
| value: value |
| }); |
| } |
| function processState(statePart) { |
| if(Array.isArray(statePart)) { |
| statePart.forEach(processState); |
| } else if(typeof statePart === "object") { |
| Object.keys(statePart).forEach(function(key) { |
| iterator(key, statePart[key]); |
| }); |
| } else if(typeof statePart === "string") { |
| processState(states[statePart]); |
| } else { |
| throw new Error("Unexpected 'state' format"); |
| } |
| } |
| processState(state); |
| var total = regExps.map(function(r) { |
| return "(" + r.regExp + ")"; |
| }).join("|"); |
| var actions = []; |
| var pos = 1; |
| regExps.forEach(function(r) { |
| var fn; |
| if(typeof r.value === "function") { |
| fn = r.value; |
| } else if(typeof r.value === "string") { |
| fn = createReturningFunction(r.value); |
| } else { |
| fn = ignoreFunction; |
| } |
| actions.push({ |
| name: r.regExp, |
| fn: fn, |
| pos: pos, |
| pos2: pos + r.groups + 1 |
| }); |
| pos += r.groups + 1; |
| }); |
| return { |
| regExp: new RegExp(total, "g"), |
| actions: actions |
| }; |
| }; |
| |
| Parser.getGroupCount = function(regExpStr) { |
| return new RegExp("(" + regExpStr + ")|^$").exec("").length - 2; |
| }; |
| |
| Parser.prototype.parse = function(initialState, string, context) { |
| context = context || {}; |
| var currentState = initialState; |
| var currentIndex = 0; |
| for(;;) { |
| var state = this.states[currentState]; |
| var regExp = state.regExp; |
| regExp.lastIndex = currentIndex; |
| var match = regExp.exec(string); |
| if(!match) return context; |
| var actions = state.actions; |
| currentIndex = state.regExp.lastIndex; |
| for(var i = 0; i < actions.length; i++) { |
| var action = actions[i]; |
| if(match[action.pos]) { |
| var ret = action.fn.apply(context, Array.prototype.slice.call(match, action.pos, action.pos2).concat([state.regExp.lastIndex - match[0].length, match[0].length])); |
| if(ret) { |
| if(!(ret in this.states)) |
| throw new Error("State '" + ret + "' doesn't exist"); |
| currentState = ret; |
| } |
| break; |
| } |
| } |
| } |
| }; |
| |
| module.exports = Parser; |