1 /*
  2 * Copyright (c) 2014 Nonki Takahashi. All rights reserved.
  3 *
  4 * History:
  5 *  0.3 2014-01-20 Moved window.onload to HTML and conbined method run. Added some symbols.
  6 *  0.2 2014-01-19 Remained only onload and run method and changed.
  7 *  0.1 2014-01-17 Created.
  8 */
  9 
 10 /**
 11  * @fileOverview EBNFParser - EBNF Parser Generator Object
 12  * @version 0.3
 13  * @author EBNFParser, Nonki Takahashi
 14  */
 15 
 16 /**
 17  * EBNF Parser Generator Object
 18  * @class Represents EBNF Parser Generator Object
 19  * @this {EBNFParser}
 20  * @param {String} syntax syntax object for generate parser
 21  * @param {Object} gapFree array of gap free id
 22  * @param {String} name name of the parser
 23  * @param {String} ver version of the parser
 24  * @param {Boolean} ignoreCase true if ignore case (optional)
 25  * @property {String} buf syntax buffer
 26  * @property {Integer} ptr syntax buffer pointer
 27  * @property {Object} gapFree array of gap free id
 28  * @property {String} name name of the parser
 29  * @property {String} ver version of the parser
 30  * @property {Boolean} ignoreCase true if ignore case
 31  * @since 0.2
 32  */
 33 EBNFParser = function(syntax, gapFree, name, ver, ignoreCase) {
 34 	this.gapFree = gapFree;
 35 	this.name = name;
 36 	this.ver = ver;
 37 	if (ignoreCase)
 38 		this.ignoreCase = true;
 39 	else
 40 		this.ignoreCase = false;
 41 	// inherit the methods of class Lex
 42 	Lex.call(this, syntax);
 43 };
 44 EBNFParser.prototype = new Lex();
 45 
 46 /**
 47  * syntax = rule, {rule}.
 48  * @return {String} code generated if matched, null if not matched
 49  * @since 0.2
 50  */
 51 EBNFParser.prototype.syntax = function() {
 52 	var match = [];
 53 	var save = this.ptr;
 54 	var n = -1;
 55 	this.sp();
 56 	match[++n] = this.rule();
 57 	if (match[n]) {
 58 		this.sp();
 59 		while (match[n]) {
 60 			this.sp();
 61 			match[++n] = this.rule();
 62 		}
 63 		match[++n] = true;
 64 	}
 65 	if (!match[n]) {
 66 		this.ptr = save;
 67 		return null;
 68 	}
 69 	return this.run(n, match, "syntax");
 70 };
 71 
 72 /**
 73  * rule = idl, '=', alternative, '.'.
 74  * @return {String} code generated if matched, null if not matched
 75  * @since 0.2
 76  */
 77 EBNFParser.prototype.rule = function() {
 78 	var match = [];
 79 	var save = this.ptr;
 80 	var n = -1;
 81 	this.sp();
 82 	match[++n] = this.idl();
 83 	if (match[n]) {
 84 		this.sp();
 85 		match[++n] = this.text('=');
 86 	}
 87 	if (match[n]) {
 88 		this.sp();
 89 		match[++n] = this.alternative();
 90 	}
 91 	if (match[n]) {
 92 		this.sp();
 93 		match[++n] = this.text('.');
 94 	}
 95 	if (!match[n]) {
 96 		this.ptr = save;
 97 		return null;
 98 	}
 99 	return this.run(n, match, "rule");
100 };
101 
102 /**
103  * alternative = sequence, {'|', sequence}.
104  * @return {String} code generated if matched, null if not matched
105  * @since 0.2
106  */
107 EBNFParser.prototype.alternative = function() {
108 	var match = [];
109 	var save = this.ptr;
110 	var n = -1;
111 	this.sp();
112 	match[++n] = this.sequence();
113 	if (match[n]) {
114 		this.sp();
115 		while (match[n]) {
116 			this.sp();
117 			match[++n] = this.text('|');
118 			if (match[n]) {
119 				this.sp();
120 				match[++n] = this.sequence();
121 			}
122 		}
123 		match[++n] = true;
124 	}
125 	if (!match[n]) {
126 		this.ptr = save;
127 		return null;
128 	}
129 	return this.run(n, match, "alternative");
130 };
131 
132 /**
133  * sequence = term, {',', term}.
134  * @return {String} code generated if matched, null if not matched
135  * @since 0.2
136  */
137 EBNFParser.prototype.sequence = function() {
138 	var match = [];
139 	var save = this.ptr;
140 	var n = -1;
141 	this.sp();
142 	match[++n] = this.term();
143 	if (match[n]) {
144 		this.sp();
145 		while (match[n]) {
146 			this.sp();
147 			match[++n] = this.text(',');
148 			if (match[n]) {
149 				this.sp();
150 				match[++n] = this.term();
151 			}
152 		}
153 		match[++n] = true;
154 	}
155 	if (!match[n]) {
156 		this.ptr = save;
157 		return null;
158 	}
159 	return this.run(n, match, "sequence");
160 };
161 
162 /**
163  * term = primary.
164  * @return {String} code generated if matched, null if not matched
165  * @since 0.2
166  */
167 EBNFParser.prototype.term = function() {
168 	var match = [];
169 	var save = this.ptr;
170 	var n = -1;
171 	this.sp();
172 	match[++n] = this.primary();
173 	if (!match[n]) {
174 		this.ptr = save;
175 		return null;
176 	}
177 	return this.run(n, match, "term");
178 };
179 
180 /**
181  * primary = terminal | id | repeat | option | group.
182  * @return {String} code generated if matched, null if not matched
183  * @since 0.2
184  */
185 EBNFParser.prototype.primary = function() {
186 	var match = [];
187 	var save = this.ptr;
188 	var n = -1;
189 	this.sp();
190 	match[++n] = this.terminal();
191 	if (!match[n]) {
192 		n--;
193 		this.sp();
194 		match[++n] = this.id();
195 	}
196 	if (!match[n]) {
197 		n--;
198 		this.sp();
199 		match[++n] = this.repeat();
200 	}
201 	if (!match[n]) {
202 		n--;
203 		this.sp();
204 		match[++n] = this.option();
205 	}
206 	if (!match[n]) {
207 		n--;
208 		this.sp();
209 		match[++n] = this.group();
210 	}
211 	if (!match[n]) {
212 		this.ptr = save;
213 		return null;
214 	}
215 	return this.run(n, match, "primary");
216 };
217 
218 /**
219  * terminal = sq, character, {character}, sq.
220  * @return {String} code generated if matched, null if not matched
221  * @since 0.2
222  */
223 EBNFParser.prototype.terminal = function() {
224 	var match = [];
225 	var save = this.ptr;
226 	var n = -1;
227 	this.sp();
228 	match[++n] = this.sq();
229 	if (match[n]) {
230 		match[++n] = this.character();
231 	}
232 	if (match[n]) {
233 		while (match[n]) {
234 			match[++n] = this.character();
235 		}
236 		match[++n] = true;
237 	}
238 	if (match[n]) {
239 		match[++n] = this.sq();
240 	}
241 	if (!match[n]) {
242 		this.ptr = save;
243 		return null;
244 	}
245 	return this.run(n, match, "terminal");
246 };
247 
248 /**
249  * idl = letter, {letter | digit}.
250  * @return {String} code generated if matched, null if not matched
251  * @since 0.2
252  */
253 EBNFParser.prototype.idl = function() {
254 	var match = [];
255 	var save = this.ptr;
256 	var n = -1;
257 	this.sp();
258 	match[++n] = this.letter();
259 	if (match[n]) {
260 		while (match[n]) {
261 			match[++n] = this.letter();
262 			if (!match[n]) {
263 				n--;
264 				match[++n] = this.digit();
265 			}
266 		}
267 		match[++n] = true;
268 	}
269 	if (!match[n]) {
270 		this.ptr = save;
271 		return null;
272 	}
273 	return this.run(n, match, "idl");
274 };
275 
276 /**
277  * id = letter, {letter | digit}.
278  * @return {String} code generated if matched, null if not matched
279  * @since 0.2
280  */
281 EBNFParser.prototype.id = function() {
282 	var match = [];
283 	var save = this.ptr;
284 	var n = -1;
285 	this.sp();
286 	match[++n] = this.letter();
287 	if (match[n]) {
288 		while (match[n]) {
289 			match[++n] = this.letter();
290 			if (!match[n]) {
291 				n--;
292 				match[++n] = this.digit();
293 			}
294 		}
295 		match[++n] = true;
296 	}
297 	if (!match[n]) {
298 		this.ptr = save;
299 		return null;
300 	}
301 	return this.run(n, match, "id");
302 };
303 
304 /**
305  * option = '[', alternative, ']'.
306  * @return {String} code generated if matched, null if not matched
307  * @since 0.2
308  */
309 EBNFParser.prototype.option = function() {
310 	var match = [];
311 	var save = this.ptr;
312 	var n = -1;
313 	this.sp();
314 	match[++n] = this.text('[');
315 	if (match[n]) {
316 		this.sp();
317 		match[++n] = this.alternative();
318 	}
319 	if (match[n]) {
320 		this.sp();
321 		match[++n] = this.text(']');
322 	}
323 	if (!match[n]) {
324 		this.ptr = save;
325 		return null;
326 	}
327 	return this.run(n, match, "option");
328 };
329 
330 /**
331  * repeat = '{', alternative, '}'.
332  * @return {String} code generated if matched, null if not matched
333  * @since 0.2
334  */
335 EBNFParser.prototype.repeat = function() {
336 	var match = [];
337 	var save = this.ptr;
338 	var n = -1;
339 	this.sp();
340 	match[++n] = this.text('{');
341 	if (match[n]) {
342 		this.sp();
343 		match[++n] = this.alternative();
344 	}
345 	if (match[n]) {
346 		this.sp();
347 		match[++n] = this.text('}');
348 	}
349 	if (!match[n]) {
350 		this.ptr = save;
351 		return null;
352 	}
353 	return this.run(n, match, "repeat");
354 };
355 
356 /**
357  * group = '(', alternative, ')'.
358  * @return {String} code generated if matched, null if not matched
359  * @since 0.2
360  */
361 EBNFParser.prototype.group = function() {
362 	var match = [];
363 	var save = this.ptr;
364 	var n = -1;
365 	this.sp();
366 	match[++n] = this.text('(');
367 	if (match[n]) {
368 		this.sp();
369 		match[++n] = this.alternative();
370 	}
371 	if (match[n]) {
372 		this.sp();
373 		match[++n] = this.text(')');
374 	}
375 	if (!match[n]) {
376 		this.ptr = save;
377 		return null;
378 	}
379 	return this.run(n, match, "group");
380 };
381 
382 /**
383  * letter = lower | upper.
384  * @return {String} code generated if matched, null if not matched
385  * @since 0.2
386  */
387 EBNFParser.prototype.letter = function() {
388 	var match = [];
389 	var save = this.ptr;
390 	var n = -1;
391 	this.sp();
392 	match[++n] = this.lower();
393 	if (!match[n]) {
394 		n--;
395 		this.sp();
396 		match[++n] = this.upper();
397 	}
398 	if (!match[n]) {
399 		this.ptr = save;
400 		return null;
401 	}
402 	return this.run(n, match, "letter");
403 };
404 
405 /**
406  * character = symbol | letter | digit.
407  * @return {String} code generated if matched, null if not matched
408  * @since 0.2
409  */
410 EBNFParser.prototype.character = function() {
411 	var match = [];
412 	var save = this.ptr;
413 	var n = -1;
414 	this.sp();
415 	match[++n] = this.symbol();
416 	if (!match[n]) {
417 		n--;
418 		this.sp();
419 		match[++n] = this.letter();
420 	}
421 	if (!match[n]) {
422 		n--;
423 		this.sp();
424 		match[++n] = this.digit();
425 	}
426 	if (!match[n]) {
427 		this.ptr = save;
428 		return null;
429 	}
430 	return this.run(n, match, "character");
431 };
432 
433 /**
434  * symbol = '=' | ',' | '.' | '|' | '+' | '-' | '*' | '/' |
435  *          '[' | ']' | '{' | '}' | '(' | ')'.
436  * @return {String} code generated if matched, null if not matched
437  * @since 0.3
438  */
439 EBNFParser.prototype.symbol = function() {
440 	var match = [];
441 	var save = this.ptr;
442 	var n = -1;
443 	this.sp();
444 	match[++n] = this.text('=');
445 	if (!match[n]) {
446 		n--;
447 		this.sp();
448 		match[++n] = this.text(',');
449 	}
450 	if (!match[n]) {
451 		n--;
452 		this.sp();
453 		match[++n] = this.text('.');
454 	}
455 	if (!match[n]) {
456 		n--;
457 		this.sp();
458 		match[++n] = this.text('|');
459 	}
460 	if (!match[n]) {
461 		n--;
462 		this.sp();
463 		match[++n] = this.text('+');
464 	}
465 	if (!match[n]) {
466 		n--;
467 		this.sp();
468 		match[++n] = this.text('-');
469 	}
470 	if (!match[n]) {
471 		n--;
472 		this.sp();
473 		match[++n] = this.text('*');
474 	}
475 	if (!match[n]) {
476 		n--;
477 		this.sp();
478 		match[++n] = this.text('/');
479 	}
480 	if (!match[n]) {
481 		n--;
482 		this.sp();
483 		match[++n] = this.text('[');
484 	}
485 	if (!match[n]) {
486 		n--;
487 		this.sp();
488 		match[++n] = this.text(']');
489 	}
490 	if (!match[n]) {
491 		n--;
492 		this.sp();
493 		match[++n] = this.text('{');
494 	}
495 	if (!match[n]) {
496 		n--;
497 		this.sp();
498 		match[++n] = this.text('}');
499 	}
500 	if (!match[n]) {
501 		n--;
502 		this.sp();
503 		match[++n] = this.text('(');
504 	}
505 	if (!match[n]) {
506 		n--;
507 		this.sp();
508 		match[++n] = this.text(')');
509 	}
510 	if (!match[n]) {
511 		this.ptr = save;
512 		return null;
513 	}
514 	return this.run(n, match, "symbol");
515 };
516 
517 /**
518  * Parser Runner (Generator) for EBNF Syntax
519  * @param {String} n number of match
520  * @param {Object} match array of parsed result
521  * @param {String} parser caller method name
522  * @return {String} generated code
523  * @since 0.2
524  */
525 EBNFParser.prototype.run = function(n, match, parser) {
526 	var code = "";
527 	if (parser == "syntax") {
528 		code += "/**\n";
529 		code += " * @fileOverview " + this.name + "Parser - " + this.name + " Parser Object\n";
530 		code += " * @version " + this.ver + "\n";
531 		code += " * @author EBNFParser written by Nonki Takahashi\n";
532 		code += " */\n";
533 		code += "\n";
534 		code += "/**\n";
535 		code += " * " + this.name + " Parser Object\n";
536 		code += " * @class Represents " + this.name + " Parser Object\n";
537 		code += " * @this {" + this.name + "Parser}\n";
538 		code += " * @param {String} src source to parser\n";
539 		code += " * @param {Object} gapFree array of gap free id\n";
540 		code += " * @param {String} name name of the parser\n";
541 		code += " * @param {String} ver version of the parser\n";
542 		code += " * @param {Boolean} ignoreCase true if ignore case (optional)\n";
543 		code += " * @property {String} buf source buffer\n";
544 		code += " * @property {Integer} ptr source buffer pointer\n";
545 		code += " * @property {Object} gapFree array of gap free id\n";
546 		code += " * @property {String} name name of the parser\n";
547 		code += " * @property {String} ver version of the parser\n";
548 		code += " * @property {Boolean} ignoreCase true if ignore case\n";
549 		code += " * @since " + this.ver + "\n";
550 		code += " */\n";
551 		code += this.name + "Parser = function(src, gapFree, name, ver, ignoreCase) {\n";
552 		code += "this.gapFree = gapFree;\n";
553 		code += "this.name = name;\n";
554 		code += "this.ver = ver;\n";
555 		code += "if (ignoreCase)\n";
556 		code += "this.ignoreCase = true;\n";
557 		code += "else\n";
558 		code += "this.ignoreCase = false;\n";
559 		code += "// inherit the methods of class Lex\n";
560 		code += "Lex.call(this, src);\n";
561 		code += "};\n";
562 		code += this.name + "Parser.prototype = new Lex();\n";
563 		code += "\n";
564 		for (var i = 0; i <= n; i++) {
565 			if (match[i] !== true && match[i] !== null)
566 				code += match[i];
567 		}
568 	} else if (parser == "rule") {
569 		code += "/**\n";
570 		var ptr = this.buf.lastIndexOf(match[0], this.ptr);
571 		code += " * " + this.buf.slice(ptr, this.ptr) + "\n";
572 		code += " * @return {String} code generated if matched, null if not matched\n";
573 		code += " * @since " + this.ver + "\n";
574 		code += " */\n";
575 		code += this.name + "Parser.prototype." + match[0] + " = function() {\n";
576 		code += "var match = [];\n";
577 		code += "var save = this.ptr;\n";
578 		code += "var n = -1;\n";
579 		var inGapFree = false;
580 		for (var i in this.gapFree) {
581 			if (this.gapFree[i] == match[0]) {
582 				code += "this.sp();\n";
583 				inGapFree = true;
584 				break;
585 			}
586 		}
587 		if (inGapFree)
588 			code += match[2].replace(/this\.sp\(\);\n/g, "");
589 		else
590 			code += match[2];
591 		code += "if (!match[n]) {\n";
592 		code += "this.ptr = save;\n";
593 		code += "return null;\n";
594 		code += "}\n";
595 		code += "return this.run(n, match, \"" + match[0] + "\");\n";
596 		code += "};\n\n";
597 	} else if (parser == "alternative") {
598 		var i = 0;
599 		code += match[i];
600 		for ( i = 2; i <= n; i += 2) {
601 			if (match[i] !== true && match[i] !== null) {
602 				code += "if (!match[n]) {\n";
603 				code += "n--;\n";
604 				code += match[i];
605 				code += "}\n";
606 			}
607 		}
608 	} else if (parser == "sequence") {
609 		var i = 0;
610 		code += "this.sp();\n";
611 		code += match[i];
612 		for ( i = 2; i <= n; i += 2) {
613 			if (match[i] !== true && match[i] !== null) {
614 				code += "if (match[n]) {\n";
615 				code += "this.sp();\n";
616 				code += match[i];
617 				code += "}\n";
618 			}
619 		}
620 	} else if (parser == "term" || parser == "primary") {
621 		code += match[0];
622 	} else if (parser == "terminal") {
623 		code += "match[++n] = this.text(";
624 		for (var i = 0; i <= n; i++) {
625 			if (match[i] !== true && match[i] !== null)
626 				code += match[i];
627 		}
628 		code += ");\n";
629 	} else if (parser == "idl") {
630 		for (var i = 0; i <= n; i++) {
631 			if (match[i] !== true && match[i] !== null)
632 				code += match[i];
633 		}
634 	} else if (parser == "id") {
635 		code += "match[++n] = this.";
636 		for (var i = 0; i <= n; i++) {
637 			if (match[i] !== true && match[i] !== null)
638 				code += match[i];
639 		}
640 		code += "();\n";
641 	} else if (parser == "option") {
642 		code += match[1];
643 		code += "match[++n] = true;\n";
644 	} else if (parser == "repeat") {
645 		code += "while(match[n]) {\n";
646 		code += match[1];
647 		code += "}\n";
648 		code += "match[++n] = true;\n";
649 	} else if (parser == "group") {
650 		code += match[1];
651 	} else if (parser == "letter" || parser == "character" || parser == "symbol") {
652 		code += match[0];
653 	}
654 	return code;
655 };
656