1 /*
  2 * Copyright (c) 2014 Nonki Takahashi. All rights reserved.
  3 *
  4 * History:
  5 *  0.2 2014-01-17 Created by EBNFParser. Runner has been separated.
  6 */
  7 
  8 /**
  9  * @fileOverview EBNFParser - EBNF Parser Object
 10  * @version 0.2
 11  * @author EBNFParser written by Nonki Takahashi
 12  */
 13 
 14 /**
 15  * EBNF Parser Generator Object
 16  * @this {EBNFParser}
 17  * @param {String} syntax syntax object for generate parser
 18  * @param {Object} gapFree array of gap free id
 19  * @param {String} name name of the parser
 20  * @param {String} ver version of the parser
 21  * @param {Boolean} ignoreCase true if ignore case (optional)
 22  * @property {String} buf syntax buffer
 23  * @property {Integer} ptr syntax buffer pointer
 24  * @property {Object} gapFree array of gap free id
 25  * @property {String} name name of the parser
 26  * @property {String} ver version of the parser
 27  * @property {Boolean} ignoreCase true if ignore case
 28  * @since 0.2
 29  */
 30 EBNFParser = function(syntax, gapFree, name, ver, ignoreCase) {
 31 	this.gapFree = gapFree;
 32 	this.name = name;
 33 	this.ver = ver;
 34 	if (ignoreCase)
 35 		this.ignoreCase = true;
 36 	else
 37 		this.ignoreCase = false;
 38 	this.inGapFree = false;
 39 	// inherit the methods of class Lex
 40 	Lex.call(this, syntax);
 41 };
 42 EBNFParser.prototype = new Lex();
 43 
 44 /**
 45  * syntax = rule, {rule}.
 46  * @returns {String} code generated if matched, null if not matched
 47  * @since 0.2
 48  */
 49 EBNFParser.prototype.syntax = function() {
 50 	var match = [];
 51 	var save = this.ptr;
 52 	var n = -1;
 53 	this.sp();
 54 	match[++n] = this.rule();
 55 	if (match[n]) {
 56 		this.sp();
 57 		while (match[n]) {
 58 			this.sp();
 59 			match[++n] = this.rule();
 60 		}
 61 		match[++n] = true;
 62 	}
 63 	if (!match[n]) {
 64 		this.ptr = save;
 65 		return null;
 66 	}
 67 	return this.run(n, match, "syntax");
 68 };
 69 
 70 /**
 71  * rule = idl, '=', alternative, '.'.
 72  * @returns {String} code generated if matched, null if not matched
 73  * @since 0.2
 74  */
 75 EBNFParser.prototype.rule = function() {
 76 	var match = [];
 77 	var save = this.ptr;
 78 	var n = -1;
 79 	this.sp();
 80 	match[++n] = this.idl();
 81 	if (match[n]) {
 82 		this.sp();
 83 		match[++n] = this.text('=');
 84 	}
 85 	if (match[n]) {
 86 		this.sp();
 87 		match[++n] = this.alternative();
 88 	}
 89 	if (match[n]) {
 90 		this.sp();
 91 		match[++n] = this.text('.');
 92 	}
 93 	if (!match[n]) {
 94 		this.ptr = save;
 95 		return null;
 96 	}
 97 	return this.run(n, match, "rule");
 98 };
 99 
100 /**
101  * alternative = sequence, {'|', sequence}.
102  * @returns {String} code generated if matched, null if not matched
103  * @since 0.2
104  */
105 EBNFParser.prototype.alternative = function() {
106 	var match = [];
107 	var save = this.ptr;
108 	var n = -1;
109 	this.sp();
110 	match[++n] = this.sequence();
111 	if (match[n]) {
112 		this.sp();
113 		while (match[n]) {
114 			this.sp();
115 			match[++n] = this.text('|');
116 			if (match[n]) {
117 				this.sp();
118 				match[++n] = this.sequence();
119 			}
120 		}
121 		match[++n] = true;
122 	}
123 	if (!match[n]) {
124 		this.ptr = save;
125 		return null;
126 	}
127 	return this.run(n, match, "alternative");
128 };
129 
130 /**
131  * sequence = term, {',', term}.
132  * @returns {String} code generated if matched, null if not matched
133  * @since 0.2
134  */
135 EBNFParser.prototype.sequence = function() {
136 	var match = [];
137 	var save = this.ptr;
138 	var n = -1;
139 	this.sp();
140 	match[++n] = this.term();
141 	if (match[n]) {
142 		this.sp();
143 		while (match[n]) {
144 			this.sp();
145 			match[++n] = this.text(',');
146 			if (match[n]) {
147 				this.sp();
148 				match[++n] = this.term();
149 			}
150 		}
151 		match[++n] = true;
152 	}
153 	if (!match[n]) {
154 		this.ptr = save;
155 		return null;
156 	}
157 	return this.run(n, match, "sequence");
158 };
159 
160 /**
161  * term = primary.
162  * @returns {String} code generated if matched, null if not matched
163  * @since 0.2
164  */
165 EBNFParser.prototype.term = function() {
166 	var match = [];
167 	var save = this.ptr;
168 	var n = -1;
169 	this.sp();
170 	match[++n] = this.primary();
171 	if (!match[n]) {
172 		this.ptr = save;
173 		return null;
174 	}
175 	return this.run(n, match, "term");
176 };
177 
178 /**
179  * primary = terminal | id | repeat | option | group.
180  * @returns {String} code generated if matched, null if not matched
181  * @since 0.2
182  */
183 EBNFParser.prototype.primary = function() {
184 	var match = [];
185 	var save = this.ptr;
186 	var n = -1;
187 	this.sp();
188 	match[++n] = this.terminal();
189 	if (!match[n]) {
190 		n--;
191 		this.sp();
192 		match[++n] = this.id();
193 	}
194 	if (!match[n]) {
195 		n--;
196 		this.sp();
197 		match[++n] = this.repeat();
198 	}
199 	if (!match[n]) {
200 		n--;
201 		this.sp();
202 		match[++n] = this.option();
203 	}
204 	if (!match[n]) {
205 		n--;
206 		this.sp();
207 		match[++n] = this.group();
208 	}
209 	if (!match[n]) {
210 		this.ptr = save;
211 		return null;
212 	}
213 	return this.run(n, match, "primary");
214 };
215 
216 /**
217  * terminal = sq, character, {character}, sq.
218  * @returns {String} code generated if matched, null if not matched
219  * @since 0.2
220  */
221 EBNFParser.prototype.terminal = function() {
222 	var match = [];
223 	var save = this.ptr;
224 	var n = -1;
225 	this.sp();
226 	match[++n] = this.sq();
227 	if (match[n]) {
228 		match[++n] = this.character();
229 	}
230 	if (match[n]) {
231 		while (match[n]) {
232 			match[++n] = this.character();
233 		}
234 		match[++n] = true;
235 	}
236 	if (match[n]) {
237 		match[++n] = this.sq();
238 	}
239 	if (!match[n]) {
240 		this.ptr = save;
241 		return null;
242 	}
243 	return this.run(n, match, "terminal");
244 };
245 
246 /**
247  * idl = letter, {letter | digit}.
248  * @returns {String} code generated if matched, null if not matched
249  * @since 0.2
250  */
251 EBNFParser.prototype.idl = function() {
252 	var match = [];
253 	var save = this.ptr;
254 	var n = -1;
255 	this.sp();
256 	match[++n] = this.letter();
257 	if (match[n]) {
258 		while (match[n]) {
259 			match[++n] = this.letter();
260 			if (!match[n]) {
261 				n--;
262 				match[++n] = this.digit();
263 			}
264 		}
265 		match[++n] = true;
266 	}
267 	if (!match[n]) {
268 		this.ptr = save;
269 		return null;
270 	}
271 	return this.run(n, match, "idl");
272 };
273 
274 /**
275  * id = letter, {letter | digit}.
276  * @returns {String} code generated if matched, null if not matched
277  * @since 0.2
278  */
279 EBNFParser.prototype.id = function() {
280 	var match = [];
281 	var save = this.ptr;
282 	var n = -1;
283 	this.sp();
284 	match[++n] = this.letter();
285 	if (match[n]) {
286 		while (match[n]) {
287 			match[++n] = this.letter();
288 			if (!match[n]) {
289 				n--;
290 				match[++n] = this.digit();
291 			}
292 		}
293 		match[++n] = true;
294 	}
295 	if (!match[n]) {
296 		this.ptr = save;
297 		return null;
298 	}
299 	return this.run(n, match, "id");
300 };
301 
302 /**
303  * option = '[', alternative, ']'.
304  * @returns {String} code generated if matched, null if not matched
305  * @since 0.2
306  */
307 EBNFParser.prototype.option = function() {
308 	var match = [];
309 	var save = this.ptr;
310 	var n = -1;
311 	this.sp();
312 	match[++n] = this.text('[');
313 	if (match[n]) {
314 		this.sp();
315 		match[++n] = this.alternative();
316 	}
317 	if (match[n]) {
318 		this.sp();
319 		match[++n] = this.text(']');
320 	}
321 	if (!match[n]) {
322 		this.ptr = save;
323 		return null;
324 	}
325 	return this.run(n, match, "option");
326 };
327 
328 /**
329  * repeat = '{', alternative, '}'.
330  * @returns {String} code generated if matched, null if not matched
331  * @since 0.2
332  */
333 EBNFParser.prototype.repeat = function() {
334 	var match = [];
335 	var save = this.ptr;
336 	var n = -1;
337 	this.sp();
338 	match[++n] = this.text('{');
339 	if (match[n]) {
340 		this.sp();
341 		match[++n] = this.alternative();
342 	}
343 	if (match[n]) {
344 		this.sp();
345 		match[++n] = this.text('}');
346 	}
347 	if (!match[n]) {
348 		this.ptr = save;
349 		return null;
350 	}
351 	return this.run(n, match, "repeat");
352 };
353 
354 /**
355  * group = '(', alternative, ')'.
356  * @returns {String} code generated if matched, null if not matched
357  * @since 0.2
358  */
359 EBNFParser.prototype.group = function() {
360 	var match = [];
361 	var save = this.ptr;
362 	var n = -1;
363 	this.sp();
364 	match[++n] = this.text('(');
365 	if (match[n]) {
366 		this.sp();
367 		match[++n] = this.alternative();
368 	}
369 	if (match[n]) {
370 		this.sp();
371 		match[++n] = this.text(')');
372 	}
373 	if (!match[n]) {
374 		this.ptr = save;
375 		return null;
376 	}
377 	return this.run(n, match, "group");
378 };
379 
380 /**
381  * letter = lower | upper.
382  * @returns {String} code generated if matched, null if not matched
383  * @since 0.2
384  */
385 EBNFParser.prototype.letter = function() {
386 	var match = [];
387 	var save = this.ptr;
388 	var n = -1;
389 	this.sp();
390 	match[++n] = this.lower();
391 	if (!match[n]) {
392 		n--;
393 		this.sp();
394 		match[++n] = this.upper();
395 	}
396 	if (!match[n]) {
397 		this.ptr = save;
398 		return null;
399 	}
400 	return this.run(n, match, "letter");
401 };
402 
403 /**
404  * character = symbol | letter | digit.
405  * @returns {String} code generated if matched, null if not matched
406  * @since 0.2
407  */
408 EBNFParser.prototype.character = function() {
409 	var match = [];
410 	var save = this.ptr;
411 	var n = -1;
412 	this.sp();
413 	match[++n] = this.symbol();
414 	if (!match[n]) {
415 		n--;
416 		this.sp();
417 		match[++n] = this.letter();
418 	}
419 	if (!match[n]) {
420 		n--;
421 		this.sp();
422 		match[++n] = this.digit();
423 	}
424 	if (!match[n]) {
425 		this.ptr = save;
426 		return null;
427 	}
428 	return this.run(n, match, "character");
429 };
430 
431 /**
432  * symbol = '=' | ',' | '.' | '|' |
433  '[' | ']' | '{' | '}' | '(' | ')'.
434  * @returns {String} code generated if matched, null if not matched
435  * @since 0.2
436  */
437 EBNFParser.prototype.symbol = function() {
438 	var match = [];
439 	var save = this.ptr;
440 	var n = -1;
441 	this.sp();
442 	match[++n] = this.text('=');
443 	if (!match[n]) {
444 		n--;
445 		this.sp();
446 		match[++n] = this.text(',');
447 	}
448 	if (!match[n]) {
449 		n--;
450 		this.sp();
451 		match[++n] = this.text('.');
452 	}
453 	if (!match[n]) {
454 		n--;
455 		this.sp();
456 		match[++n] = this.text('|');
457 	}
458 	if (!match[n]) {
459 		n--;
460 		this.sp();
461 		match[++n] = this.text('[');
462 	}
463 	if (!match[n]) {
464 		n--;
465 		this.sp();
466 		match[++n] = this.text(']');
467 	}
468 	if (!match[n]) {
469 		n--;
470 		this.sp();
471 		match[++n] = this.text('{');
472 	}
473 	if (!match[n]) {
474 		n--;
475 		this.sp();
476 		match[++n] = this.text('}');
477 	}
478 	if (!match[n]) {
479 		n--;
480 		this.sp();
481 		match[++n] = this.text('(');
482 	}
483 	if (!match[n]) {
484 		n--;
485 		this.sp();
486 		match[++n] = this.text(')');
487 	}
488 	if (!match[n]) {
489 		this.ptr = save;
490 		return null;
491 	}
492 	return this.run(n, match, "symbol");
493 };
494