UNPKG

2.62 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6function ignoreFunction() {}
7
8function createReturningFunction(value) {
9 return function() {
10 return value;
11 };
12}
13
14function Parser(states) {
15 this.states = this.compileStates(states);
16}
17
18Parser.prototype.compileStates = function(states) {
19 var result = {};
20 Object.keys(states).forEach(function(name) {
21 result[name] = this.compileState(states[name], states);
22 }, this);
23 return result;
24};
25
26Parser.prototype.compileState = function(state, states) {
27 var regExps = [];
28 function iterator(str, value) {
29 regExps.push({
30 groups: Parser.getGroupCount(str),
31 regExp: str,
32 value: value
33 });
34 }
35 function processState(statePart) {
36 if(Array.isArray(statePart)) {
37 statePart.forEach(processState);
38 } else if(typeof statePart === "object") {
39 Object.keys(statePart).forEach(function(key) {
40 iterator(key, statePart[key]);
41 });
42 } else if(typeof statePart === "string") {
43 processState(states[statePart]);
44 } else {
45 throw new Error("Unexpected 'state' format");
46 }
47 }
48 processState(state);
49 var total = regExps.map(function(r) {
50 return "(" + r.regExp + ")";
51 }).join("|");
52 var actions = [];
53 var pos = 1;
54 regExps.forEach(function(r) {
55 var fn;
56 if(typeof r.value === "function") {
57 fn = r.value;
58 } else if(typeof r.value === "string") {
59 fn = createReturningFunction(r.value);
60 } else {
61 fn = ignoreFunction;
62 }
63 actions.push({
64 name: r.regExp,
65 fn: fn,
66 pos: pos,
67 pos2: pos + r.groups + 1
68 });
69 pos += r.groups + 1;
70 });
71 return {
72 regExp: new RegExp(total, "g"),
73 actions: actions
74 };
75};
76
77Parser.getGroupCount = function(regExpStr) {
78 return new RegExp("(" + regExpStr + ")|^$").exec("").length - 2;
79};
80
81Parser.prototype.parse = function(initialState, string, context) {
82 context = context || {};
83 var currentState = initialState;
84 var currentIndex = 0;
85 for(;;) {
86 var state = this.states[currentState];
87 var regExp = state.regExp;
88 regExp.lastIndex = currentIndex;
89 var match = regExp.exec(string);
90 if(!match) return context;
91 var actions = state.actions;
92 currentIndex = state.regExp.lastIndex;
93 for(var i = 0; i < actions.length; i++) {
94 var action = actions[i];
95 if(match[action.pos]) {
96 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]));
97 if(ret) {
98 if(!(ret in this.states))
99 throw new Error("State '" + ret + "' doesn't exist");
100 currentState = ret;
101 }
102 break;
103 }
104 }
105 }
106};
107
108module.exports = Parser;