1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | var TextParser = function( text, begin, end ) {
|
9 | if( typeof text === 'undefined' ) text = '';
|
10 | if( typeof begin === 'undefined' ) begin = 0;
|
11 | if( typeof end === 'undefined' ) end = text.length;
|
12 |
|
13 | this._text = text;
|
14 | this._cursor = begin;
|
15 | this._end = end;
|
16 | };
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | TextParser.prototype.eos = function() {
|
24 | return this._cursor >= this._end;
|
25 | };
|
26 |
|
27 |
|
28 |
|
29 |
|
30 | TextParser.prototype.peek = function() {
|
31 | if (this.eos()) return null;
|
32 | return this._text.charAt( this._cursor );
|
33 | };
|
34 |
|
35 |
|
36 |
|
37 |
|
38 | TextParser.prototype.next = function() {
|
39 | if (this.eos()) return null;
|
40 | return this._text.charAt( this._cursor++ );
|
41 | };
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 | TextParser.prototype.eat = function() {
|
49 | if (!this.eos()) {
|
50 | var i, arg;
|
51 | for (i = 0 ; i < arguments.length ; i++) {
|
52 | arg = arguments[i];
|
53 | if (this._text.substr( this._cursor, arg.length ) == arg) {
|
54 | this._cursor += arg.length;
|
55 | return arg;
|
56 | }
|
57 | }
|
58 | }
|
59 | return null;
|
60 | };
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 | TextParser.prototype.eatRegex = function(regex, group) {
|
70 | if (!regex.global) throw Error("[text-parser:eatRegex] Regular expression MUST be global! "
|
71 | + "Please add option `g` at the end of /" + regex.source + "/.");
|
72 | if( typeof group !== 'number' ) group = 0;
|
73 | if (this.eos()) return null;
|
74 | regex.lastIndex = this._cursor;
|
75 | var matcher = regex.exec( this._text );
|
76 | if (!matcher) return null;
|
77 |
|
78 | if (matcher.index != this._cursor) return null;
|
79 | this._cursor += matcher[0].length;
|
80 | return matcher[group];
|
81 | };
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 | TextParser.prototype.eatUntilChar = function( chars ) {
|
89 | if (this.eos()) return null;
|
90 | var begin = this._cursor;
|
91 | while (this._cursor < this._end) {
|
92 | if (chars.indexOf( this._text.charAt( this._cursor ) ) != -1 ) {
|
93 | break;
|
94 | }
|
95 | this._cursor++;
|
96 | }
|
97 | return this._text.substr( begin, this._cursor - begin );
|
98 | };
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 | TextParser.prototype.eatUntilText = function( text ) {
|
106 | if (this.eos()) return null;
|
107 | var begin = this._cursor;
|
108 | var len = text.length;
|
109 | while (this._cursor < this._end) {
|
110 | if (this._text.substr( this._cursor, len ) == text) break;
|
111 | this._cursor++;
|
112 | }
|
113 | return this._text.substr( begin, this._cursor - begin );
|
114 | };
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 | TextParser.prototype.eatUntilRegex = function( regex ) {
|
122 | if (this.eos()) return null;
|
123 | if (!regex.global) throw Error("[text-parser:eatRegex] Regular expression MUST be global! "
|
124 | + "Please add option `g` at the end of /" + regex.source + "/.");
|
125 | var begin = this._cursor;
|
126 | var matcher;
|
127 | while (this._cursor < this._end) {
|
128 | regex.lastIndex = this._cursor;
|
129 | matcher = regex.exec( this._text );
|
130 | if (matcher) break;
|
131 | this._cursor++;
|
132 | }
|
133 | return this._text.substr( begin, this._cursor - begin );
|
134 | };
|
135 |
|
136 |
|
137 |
|
138 | module.exports = function( args ) {
|
139 | if( typeof args !== 'object' || args === null ) {
|
140 | throw Error("[text-parser] Bad argument! Expecting {text, grammar, state, begin, end}.");
|
141 | }
|
142 |
|
143 | var grammar = args.grammar;
|
144 | if( typeof grammar === 'undefined' ) {
|
145 | throw Error("[text-parser] Missing mandatory argument `grammar`!");
|
146 | }
|
147 | var step = '';
|
148 |
|
149 | for( step in grammar ) break;
|
150 |
|
151 | if( typeof args.state === 'undefined' ) args.state = {};
|
152 | var stream = new TextParser( args.text, args.begin, args.end );
|
153 | var nextStep;
|
154 | var stepFunc = args.grammar[step];
|
155 | while (!stream.eos()) {
|
156 | nextStep = stepFunc( stream, args.state );
|
157 | if (nextStep === null) break;
|
158 | if (typeof nextStep === 'string') {
|
159 | stepFunc = args.grammar[nextStep];
|
160 | if (typeof stepFunc !== 'function') {
|
161 | throw Error("[text-parser] Invalid next step in step `"
|
162 | + step + "`: `" + nextStep + "`!");
|
163 | }
|
164 | step = stepFunc;
|
165 | }
|
166 | }
|
167 |
|
168 | return args.state;
|
169 | };
|