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