/*
|
|
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;
|