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