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