WebIDLParser.js (33933B)
1 (function () { 2 var tokenise = function (str) { 3 var tokens = [] 4 , re = { 5 "float": /^-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][-+]?[0-9]+)?|[0-9]+[Ee][-+]?[0-9]+)/ 6 , "integer": /^-?(0([Xx][0-9A-Fa-f]+|[0-7]*)|[1-9][0-9]*)/ 7 , "identifier": /^[A-Z_a-z][0-9A-Z_a-z]*/ 8 , "string": /^"[^"]*"/ 9 , "whitespace": /^(?:[\t\n\r ]+|[\t\n\r ]*((\/\/.*|\/\*(.|\n|\r)*?\*\/)[\t\n\r ]*))+/ 10 , "other": /^[^\t\n\r 0-9A-Z_a-z]/ 11 } 12 , types = [] 13 ; 14 for (var k in re) types.push(k); 15 while (str.length > 0) { 16 var matched = false; 17 for (var i = 0, n = types.length; i < n; i++) { 18 var type = types[i]; 19 str = str.replace(re[type], function (tok) { 20 tokens.push({ type: type, value: tok }); 21 matched = true; 22 return ""; 23 }); 24 if (matched) break; 25 } 26 if (matched) continue; 27 throw new Error("Token stream not progressing"); 28 } 29 return tokens; 30 }; 31 32 var parse = function (tokens, opt) { 33 var line = 1; 34 tokens = tokens.slice(); 35 36 var FLOAT = "float" 37 , INT = "integer" 38 , ID = "identifier" 39 , STR = "string" 40 , OTHER = "other" 41 ; 42 43 var WebIDLParseError = function (str, line, input, tokens) { 44 this.message = str; 45 this.line = line; 46 this.input = input; 47 this.tokens = tokens; 48 }; 49 WebIDLParseError.prototype.toString = function () { 50 return this.message + ", line " + this.line + " (tokens: '" + this.input + "')\n" + 51 JSON.stringify(this.tokens, null, 4); 52 }; 53 54 var error = function (str) { 55 var tok = "", numTokens = 0, maxTokens = 5; 56 while (numTokens < maxTokens && tokens.length > numTokens) { 57 tok += tokens[numTokens].value; 58 numTokens++; 59 } 60 throw new WebIDLParseError(str, line, tok, tokens.slice(0, 5)); 61 }; 62 63 var last_token = null; 64 65 var consume = function (type, value) { 66 if (!tokens.length || tokens[0].type !== type) return; 67 if (typeof value === "undefined" || tokens[0].value === value) { 68 last_token = tokens.shift(); 69 if (type === ID) last_token.value = last_token.value.replace(/^_/, ""); 70 return last_token; 71 } 72 }; 73 74 var ws = function () { 75 if (!tokens.length) return; 76 if (tokens[0].type === "whitespace") { 77 var t = tokens.shift(); 78 t.value.replace(/\n/g, function (m) { line++; return m; }); 79 return t; 80 } 81 }; 82 83 var all_ws = function (store, pea) { // pea == post extended attribute, tpea = same for types 84 var t = { type: "whitespace", value: "" }; 85 while (true) { 86 var w = ws(); 87 if (!w) break; 88 t.value += w.value; 89 } 90 if (t.value.length > 0) { 91 if (store) { 92 var w = t.value 93 , re = { 94 "ws": /^([\t\n\r ]+)/ 95 , "line-comment": /^\/\/(.*)\n?/m 96 , "multiline-comment": /^\/\*((?:.|\n|\r)*?)\*\// 97 } 98 , wsTypes = [] 99 ; 100 for (var k in re) wsTypes.push(k); 101 while (w.length) { 102 var matched = false; 103 for (var i = 0, n = wsTypes.length; i < n; i++) { 104 var type = wsTypes[i]; 105 w = w.replace(re[type], function (tok, m1) { 106 store.push({ type: type + (pea ? ("-" + pea) : ""), value: m1 }); 107 matched = true; 108 return ""; 109 }); 110 if (matched) break; 111 } 112 if (matched) continue; 113 throw new Error("Surprising white space construct."); // this shouldn't happen 114 } 115 } 116 return t; 117 } 118 }; 119 120 var integer_type = function () { 121 var ret = ""; 122 all_ws(); 123 if (consume(ID, "unsigned")) ret = "unsigned "; 124 all_ws(); 125 if (consume(ID, "short")) return ret + "short"; 126 if (consume(ID, "long")) { 127 ret += "long"; 128 all_ws(); 129 if (consume(ID, "long")) return ret + " long"; 130 return ret; 131 } 132 if (ret) error("Failed to parse integer type"); 133 }; 134 135 var float_type = function () { 136 var ret = ""; 137 all_ws(); 138 if (consume(ID, "unrestricted")) ret = "unrestricted "; 139 all_ws(); 140 if (consume(ID, "float")) return ret + "float"; 141 if (consume(ID, "double")) return ret + "double"; 142 if (ret) error("Failed to parse float type"); 143 }; 144 145 var primitive_type = function () { 146 var num_type = integer_type() || float_type(); 147 if (num_type) return num_type; 148 all_ws(); 149 if (consume(ID, "boolean")) return "boolean"; 150 if (consume(ID, "byte")) return "byte"; 151 if (consume(ID, "octet")) return "octet"; 152 }; 153 154 var const_value = function () { 155 if (consume(ID, "true")) return { type: "boolean", value: true }; 156 if (consume(ID, "false")) return { type: "boolean", value: false }; 157 if (consume(ID, "null")) return { type: "null" }; 158 if (consume(ID, "Infinity")) return { type: "Infinity", negative: false }; 159 if (consume(ID, "NaN")) return { type: "NaN" }; 160 var ret = consume(FLOAT) || consume(INT); 161 if (ret) return { type: "number", value: 1 * ret.value }; 162 var tok = consume(OTHER, "-"); 163 if (tok) { 164 if (consume(ID, "Infinity")) return { type: "Infinity", negative: true }; 165 else tokens.unshift(tok); 166 } 167 }; 168 169 var type_suffix = function (obj) { 170 while (true) { 171 all_ws(); 172 if (consume(OTHER, "?")) { 173 if (obj.nullable) error("Can't nullable more than once"); 174 obj.nullable = true; 175 } 176 else if (consume(OTHER, "[")) { 177 all_ws(); 178 consume(OTHER, "]") || error("Unterminated array type"); 179 if (!obj.array) { 180 obj.array = 1; 181 obj.nullableArray = [obj.nullable]; 182 } 183 else { 184 obj.array++; 185 obj.nullableArray.push(obj.nullable); 186 } 187 obj.nullable = false; 188 } 189 else return; 190 } 191 }; 192 193 var single_type = function () { 194 var prim = primitive_type() 195 , ret = { sequence: false, generic: null, nullable: false, array: false, union: false } 196 , name 197 , value 198 ; 199 if (prim) { 200 ret.idlType = prim; 201 } 202 else if (name = consume(ID)) { 203 value = name.value; 204 all_ws(); 205 // Generic types 206 if (consume(OTHER, "<")) { 207 // backwards compat 208 if (value === "sequence") { 209 ret.sequence = true; 210 } 211 ret.generic = value; 212 ret.idlType = type() || error("Error parsing generic type " + value); 213 all_ws(); 214 if (!consume(OTHER, ">")) error("Unterminated generic type " + value); 215 all_ws(); 216 if (consume(OTHER, "?")) ret.nullable = true; 217 return ret; 218 } 219 else { 220 ret.idlType = value; 221 } 222 } 223 else { 224 return; 225 } 226 type_suffix(ret); 227 if (ret.nullable && !ret.array && ret.idlType === "any") error("Type any cannot be made nullable"); 228 return ret; 229 }; 230 231 var union_type = function () { 232 all_ws(); 233 if (!consume(OTHER, "(")) return; 234 var ret = { sequence: false, generic: null, nullable: false, array: false, union: true, idlType: [] }; 235 var fst = type() || error("Union type with no content"); 236 ret.idlType.push(fst); 237 while (true) { 238 all_ws(); 239 if (!consume(ID, "or")) break; 240 var typ = type() || error("No type after 'or' in union type"); 241 ret.idlType.push(typ); 242 } 243 if (!consume(OTHER, ")")) error("Unterminated union type"); 244 type_suffix(ret); 245 return ret; 246 }; 247 248 var type = function () { 249 return single_type() || union_type(); 250 }; 251 252 var argument = function (store) { 253 var ret = { optional: false, variadic: false }; 254 ret.extAttrs = extended_attrs(store); 255 all_ws(store, "pea"); 256 var opt_token = consume(ID, "optional"); 257 if (opt_token) { 258 ret.optional = true; 259 all_ws(); 260 } 261 ret.idlType = type(); 262 if (!ret.idlType) { 263 if (opt_token) tokens.unshift(opt_token); 264 return; 265 } 266 var type_token = last_token; 267 if (!ret.optional) { 268 all_ws(); 269 if (tokens.length >= 3 && 270 tokens[0].type === "other" && tokens[0].value === "." && 271 tokens[1].type === "other" && tokens[1].value === "." && 272 tokens[2].type === "other" && tokens[2].value === "." 273 ) { 274 tokens.shift(); 275 tokens.shift(); 276 tokens.shift(); 277 ret.variadic = true; 278 } 279 } 280 all_ws(); 281 var name = consume(ID); 282 if (!name) { 283 if (opt_token) tokens.unshift(opt_token); 284 tokens.unshift(type_token); 285 return; 286 } 287 ret.name = name.value; 288 if (ret.optional) { 289 all_ws(); 290 ret["default"] = default_(); 291 } 292 return ret; 293 }; 294 295 var argument_list = function (store) { 296 var ret = [] 297 , arg = argument(store ? ret : null) 298 ; 299 if (!arg) return; 300 ret.push(arg); 301 while (true) { 302 all_ws(store ? ret : null); 303 if (!consume(OTHER, ",")) return ret; 304 var nxt = argument(store ? ret : null) || error("Trailing comma in arguments list"); 305 ret.push(nxt); 306 } 307 }; 308 309 var type_pair = function () { 310 all_ws(); 311 var k = type(); 312 if (!k) return; 313 all_ws() 314 if (!consume(OTHER, ",")) return; 315 all_ws(); 316 var v = type(); 317 if (!v) return; 318 return [k, v]; 319 }; 320 321 var simple_extended_attr = function (store) { 322 all_ws(); 323 var name = consume(ID); 324 if (!name) return; 325 var ret = { 326 name: name.value 327 , "arguments": null 328 }; 329 all_ws(); 330 var eq = consume(OTHER, "="); 331 if (eq) { 332 all_ws(); 333 ret.rhs = consume(ID); 334 if (!ret.rhs) return error("No right hand side to extended attribute assignment"); 335 } 336 all_ws(); 337 if (consume(OTHER, "(")) { 338 var args, pair; 339 // [Constructor(DOMString str)] 340 if (args = argument_list(store)) { 341 ret["arguments"] = args; 342 } 343 // [MapClass(DOMString, DOMString)] 344 else if (pair = type_pair()) { 345 ret.typePair = pair; 346 } 347 // [Constructor()] 348 else { 349 ret["arguments"] = []; 350 } 351 all_ws(); 352 consume(OTHER, ")") || error("Unexpected token in extended attribute argument list or type pair"); 353 } 354 return ret; 355 }; 356 357 // Note: we parse something simpler than the official syntax. It's all that ever 358 // seems to be used 359 var extended_attrs = function (store) { 360 var eas = []; 361 all_ws(store); 362 if (!consume(OTHER, "[")) return eas; 363 eas[0] = simple_extended_attr(store) || error("Extended attribute with not content"); 364 all_ws(); 365 while (consume(OTHER, ",")) { 366 eas.push(simple_extended_attr(store) || error("Trailing comma in extended attribute")); 367 all_ws(); 368 } 369 consume(OTHER, "]") || error("No end of extended attribute"); 370 return eas; 371 }; 372 373 var default_ = function () { 374 all_ws(); 375 if (consume(OTHER, "=")) { 376 all_ws(); 377 var def = const_value(); 378 if (def) { 379 return def; 380 } 381 else { 382 var str = consume(STR) || error("No value for default"); 383 str.value = str.value.replace(/^"/, "").replace(/"$/, ""); 384 return str; 385 } 386 } 387 }; 388 389 var const_ = function (store) { 390 all_ws(store, "pea"); 391 if (!consume(ID, "const")) return; 392 var ret = { type: "const", nullable: false }; 393 all_ws(); 394 var typ = primitive_type(); 395 if (!typ) { 396 typ = consume(ID) || error("No type for const"); 397 typ = typ.value; 398 } 399 ret.idlType = typ; 400 all_ws(); 401 if (consume(OTHER, "?")) { 402 ret.nullable = true; 403 all_ws(); 404 } 405 var name = consume(ID) || error("No name for const"); 406 ret.name = name.value; 407 all_ws(); 408 consume(OTHER, "=") || error("No value assignment for const"); 409 all_ws(); 410 var cnt = const_value(); 411 if (cnt) ret.value = cnt; 412 else error("No value for const"); 413 all_ws(); 414 consume(OTHER, ";") || error("Unterminated const"); 415 return ret; 416 }; 417 418 var inheritance = function () { 419 all_ws(); 420 if (consume(OTHER, ":")) { 421 all_ws(); 422 var inh = consume(ID) || error ("No type in inheritance"); 423 return inh.value; 424 } 425 }; 426 427 var operation_rest = function (ret, store) { 428 all_ws(); 429 if (!ret) ret = {}; 430 var name = consume(ID); 431 ret.name = name ? name.value : null; 432 all_ws(); 433 consume(OTHER, "(") || error("Invalid operation"); 434 ret["arguments"] = argument_list(store) || []; 435 all_ws(); 436 consume(OTHER, ")") || error("Unterminated operation"); 437 all_ws(); 438 consume(OTHER, ";") || error("Unterminated operation"); 439 return ret; 440 }; 441 442 var callback = function (store) { 443 all_ws(store, "pea"); 444 var ret; 445 if (!consume(ID, "callback")) return; 446 all_ws(); 447 var tok = consume(ID, "interface"); 448 if (tok) { 449 tokens.unshift(tok); 450 ret = interface_(); 451 ret.type = "callback interface"; 452 return ret; 453 } 454 var name = consume(ID) || error("No name for callback"); 455 ret = { type: "callback", name: name.value }; 456 all_ws(); 457 consume(OTHER, "=") || error("No assignment in callback"); 458 all_ws(); 459 ret.idlType = return_type(); 460 all_ws(); 461 consume(OTHER, "(") || error("No arguments in callback"); 462 ret["arguments"] = argument_list(store) || []; 463 all_ws(); 464 consume(OTHER, ")") || error("Unterminated callback"); 465 all_ws(); 466 consume(OTHER, ";") || error("Unterminated callback"); 467 return ret; 468 }; 469 470 var attribute = function (store) { 471 all_ws(store, "pea"); 472 var grabbed = [] 473 , ret = { 474 type: "attribute" 475 , "static": false 476 , stringifier: false 477 , inherit: false 478 , readonly: false 479 }; 480 if (consume(ID, "static")) { 481 ret["static"] = true; 482 grabbed.push(last_token); 483 } 484 else if (consume(ID, "stringifier")) { 485 ret.stringifier = true; 486 grabbed.push(last_token); 487 } 488 var w = all_ws(); 489 if (w) grabbed.push(w); 490 if (consume(ID, "inherit")) { 491 if (ret["static"] || ret.stringifier) error("Cannot have a static or stringifier inherit"); 492 ret.inherit = true; 493 grabbed.push(last_token); 494 var w = all_ws(); 495 if (w) grabbed.push(w); 496 } 497 if (consume(ID, "readonly")) { 498 ret.readonly = true; 499 grabbed.push(last_token); 500 var w = all_ws(); 501 if (w) grabbed.push(w); 502 } 503 if (!consume(ID, "attribute")) { 504 tokens = grabbed.concat(tokens); 505 return; 506 } 507 all_ws(); 508 ret.idlType = type() || error("No type in attribute"); 509 if (ret.idlType.sequence) error("Attributes cannot accept sequence types"); 510 all_ws(); 511 var name = consume(ID) || error("No name in attribute"); 512 ret.name = name.value; 513 all_ws(); 514 consume(OTHER, ";") || error("Unterminated attribute"); 515 return ret; 516 }; 517 518 var return_type = function () { 519 var typ = type(); 520 if (!typ) { 521 if (consume(ID, "void")) { 522 return "void"; 523 } 524 else error("No return type"); 525 } 526 return typ; 527 }; 528 529 var operation = function (store) { 530 all_ws(store, "pea"); 531 var ret = { 532 type: "operation" 533 , getter: false 534 , setter: false 535 , creator: false 536 , deleter: false 537 , legacycaller: false 538 , "static": false 539 , stringifier: false 540 }; 541 while (true) { 542 all_ws(); 543 if (consume(ID, "getter")) ret.getter = true; 544 else if (consume(ID, "setter")) ret.setter = true; 545 else if (consume(ID, "creator")) ret.creator = true; 546 else if (consume(ID, "deleter")) ret.deleter = true; 547 else if (consume(ID, "legacycaller")) ret.legacycaller = true; 548 else break; 549 } 550 if (ret.getter || ret.setter || ret.creator || ret.deleter || ret.legacycaller) { 551 all_ws(); 552 ret.idlType = return_type(); 553 operation_rest(ret, store); 554 return ret; 555 } 556 if (consume(ID, "static")) { 557 ret["static"] = true; 558 ret.idlType = return_type(); 559 operation_rest(ret, store); 560 return ret; 561 } 562 else if (consume(ID, "stringifier")) { 563 ret.stringifier = true; 564 all_ws(); 565 if (consume(OTHER, ";")) return ret; 566 ret.idlType = return_type(); 567 operation_rest(ret, store); 568 return ret; 569 } 570 ret.idlType = return_type(); 571 all_ws(); 572 if (consume(ID, "iterator")) { 573 all_ws(); 574 ret.type = "iterator"; 575 if (consume(ID, "object")) { 576 ret.iteratorObject = "object"; 577 } 578 else if (consume(OTHER, "=")) { 579 all_ws(); 580 var name = consume(ID) || error("No right hand side in iterator"); 581 ret.iteratorObject = name.value; 582 } 583 all_ws(); 584 consume(OTHER, ";") || error("Unterminated iterator"); 585 return ret; 586 } 587 else { 588 operation_rest(ret, store); 589 return ret; 590 } 591 }; 592 593 var identifiers = function (arr) { 594 while (true) { 595 all_ws(); 596 if (consume(OTHER, ",")) { 597 all_ws(); 598 var name = consume(ID) || error("Trailing comma in identifiers list"); 599 arr.push(name.value); 600 } 601 else break; 602 } 603 }; 604 605 var serialiser = function (store) { 606 all_ws(store, "pea"); 607 if (!consume(ID, "serializer")) return; 608 var ret = { type: "serializer" }; 609 all_ws(); 610 if (consume(OTHER, "=")) { 611 all_ws(); 612 if (consume(OTHER, "{")) { 613 ret.patternMap = true; 614 all_ws(); 615 var id = consume(ID); 616 if (id && id.value === "getter") { 617 ret.names = ["getter"]; 618 } 619 else if (id && id.value === "inherit") { 620 ret.names = ["inherit"]; 621 identifiers(ret.names); 622 } 623 else if (id) { 624 ret.names = [id.value]; 625 identifiers(ret.names); 626 } 627 else { 628 ret.names = []; 629 } 630 all_ws(); 631 consume(OTHER, "}") || error("Unterminated serializer pattern map"); 632 } 633 else if (consume(OTHER, "[")) { 634 ret.patternList = true; 635 all_ws(); 636 var id = consume(ID); 637 if (id && id.value === "getter") { 638 ret.names = ["getter"]; 639 } 640 else if (id) { 641 ret.names = [id.value]; 642 identifiers(ret.names); 643 } 644 else { 645 ret.names = []; 646 } 647 all_ws(); 648 consume(OTHER, "]") || error("Unterminated serializer pattern list"); 649 } 650 else { 651 var name = consume(ID) || error("Invalid serializer"); 652 ret.name = name.value; 653 } 654 all_ws(); 655 consume(OTHER, ";") || error("Unterminated serializer"); 656 return ret; 657 } 658 else if (consume(OTHER, ";")) { 659 // noop, just parsing 660 } 661 else { 662 ret.idlType = return_type(); 663 all_ws(); 664 ret.operation = operation_rest(null, store); 665 } 666 return ret; 667 }; 668 669 var interface_ = function (isPartial, store) { 670 all_ws(isPartial ? null : store, "pea"); 671 if (!consume(ID, "interface")) return; 672 all_ws(); 673 var name = consume(ID) || error("No name for interface"); 674 var mems = [] 675 , ret = { 676 type: "interface" 677 , name: name.value 678 , partial: false 679 , members: mems 680 }; 681 if (!isPartial) ret.inheritance = inheritance() || null; 682 all_ws(); 683 consume(OTHER, "{") || error("Bodyless interface"); 684 while (true) { 685 all_ws(store ? mems : null); 686 if (consume(OTHER, "}")) { 687 all_ws(); 688 consume(OTHER, ";") || error("Missing semicolon after interface"); 689 return ret; 690 } 691 var ea = extended_attrs(store ? mems : null); 692 all_ws(); 693 var cnt = const_(store ? mems : null); 694 if (cnt) { 695 cnt.extAttrs = ea; 696 ret.members.push(cnt); 697 continue; 698 } 699 var mem = serialiser(store ? mems : null) || 700 attribute(store ? mems : null) || 701 operation(store ? mems : null) || 702 error("Unknown member"); 703 mem.extAttrs = ea; 704 ret.members.push(mem); 705 } 706 }; 707 708 var partial = function (store) { 709 all_ws(store, "pea"); 710 if (!consume(ID, "partial")) return; 711 var thing = dictionary(true, store) || 712 interface_(true, store) || 713 error("Partial doesn't apply to anything"); 714 thing.partial = true; 715 return thing; 716 }; 717 718 var dictionary = function (isPartial, store) { 719 all_ws(isPartial ? null : store, "pea"); 720 if (!consume(ID, "dictionary")) return; 721 all_ws(); 722 var name = consume(ID) || error("No name for dictionary"); 723 var mems = [] 724 , ret = { 725 type: "dictionary" 726 , name: name.value 727 , partial: false 728 , members: mems 729 }; 730 if (!isPartial) ret.inheritance = inheritance() || null; 731 all_ws(); 732 consume(OTHER, "{") || error("Bodyless dictionary"); 733 while (true) { 734 all_ws(store ? mems : null); 735 if (consume(OTHER, "}")) { 736 all_ws(); 737 consume(OTHER, ";") || error("Missing semicolon after dictionary"); 738 return ret; 739 } 740 var ea = extended_attrs(store ? mems : null); 741 all_ws(store ? mems : null, "pea"); 742 var typ = type() || error("No type for dictionary member"); 743 all_ws(); 744 var name = consume(ID) || error("No name for dictionary member"); 745 ret.members.push({ 746 type: "field" 747 , name: name.value 748 , idlType: typ 749 , extAttrs: ea 750 , "default": default_() 751 }); 752 all_ws(); 753 consume(OTHER, ";") || error("Unterminated dictionary member"); 754 } 755 }; 756 757 var exception = function (store) { 758 all_ws(store, "pea"); 759 if (!consume(ID, "exception")) return; 760 all_ws(); 761 var name = consume(ID) || error("No name for exception"); 762 var mems = [] 763 , ret = { 764 type: "exception" 765 , name: name.value 766 , members: mems 767 }; 768 ret.inheritance = inheritance() || null; 769 all_ws(); 770 consume(OTHER, "{") || error("Bodyless exception"); 771 while (true) { 772 all_ws(store ? mems : null); 773 if (consume(OTHER, "}")) { 774 all_ws(); 775 consume(OTHER, ";") || error("Missing semicolon after exception"); 776 return ret; 777 } 778 var ea = extended_attrs(store ? mems : null); 779 all_ws(store ? mems : null, "pea"); 780 var cnt = const_(); 781 if (cnt) { 782 cnt.extAttrs = ea; 783 ret.members.push(cnt); 784 } 785 else { 786 var typ = type(); 787 all_ws(); 788 var name = consume(ID); 789 all_ws(); 790 if (!typ || !name || !consume(OTHER, ";")) error("Unknown member in exception body"); 791 ret.members.push({ 792 type: "field" 793 , name: name.value 794 , idlType: typ 795 , extAttrs: ea 796 }); 797 } 798 } 799 }; 800 801 var enum_ = function (store) { 802 all_ws(store, "pea"); 803 if (!consume(ID, "enum")) return; 804 all_ws(); 805 var name = consume(ID) || error("No name for enum"); 806 var vals = [] 807 , ret = { 808 type: "enum" 809 , name: name.value 810 , values: vals 811 }; 812 all_ws(); 813 consume(OTHER, "{") || error("No curly for enum"); 814 var saw_comma = false; 815 while (true) { 816 all_ws(store ? vals : null); 817 if (consume(OTHER, "}")) { 818 all_ws(); 819 if (saw_comma) error("Trailing comma in enum"); 820 consume(OTHER, ";") || error("No semicolon after enum"); 821 return ret; 822 } 823 var val = consume(STR) || error("Unexpected value in enum"); 824 ret.values.push(val.value.replace(/"/g, "")); 825 all_ws(store ? vals : null); 826 if (consume(OTHER, ",")) { 827 if (store) vals.push({ type: "," }); 828 all_ws(store ? vals : null); 829 saw_comma = true; 830 } 831 else { 832 saw_comma = false; 833 } 834 } 835 }; 836 837 var typedef = function (store) { 838 all_ws(store, "pea"); 839 if (!consume(ID, "typedef")) return; 840 var ret = { 841 type: "typedef" 842 }; 843 all_ws(); 844 ret.typeExtAttrs = extended_attrs(); 845 all_ws(store, "tpea"); 846 ret.idlType = type() || error("No type in typedef"); 847 all_ws(); 848 var name = consume(ID) || error("No name in typedef"); 849 ret.name = name.value; 850 all_ws(); 851 consume(OTHER, ";") || error("Unterminated typedef"); 852 return ret; 853 }; 854 855 var implements_ = function (store) { 856 all_ws(store, "pea"); 857 var target = consume(ID); 858 if (!target) return; 859 var w = all_ws(); 860 if (consume(ID, "implements")) { 861 var ret = { 862 type: "implements" 863 , target: target.value 864 }; 865 all_ws(); 866 var imp = consume(ID) || error("Incomplete implements statement"); 867 ret["implements"] = imp.value; 868 all_ws(); 869 consume(OTHER, ";") || error("No terminating ; for implements statement"); 870 return ret; 871 } 872 else { 873 // rollback 874 tokens.unshift(w); 875 tokens.unshift(target); 876 } 877 }; 878 879 var definition = function (store) { 880 return callback(store) || 881 interface_(false, store) || 882 partial(store) || 883 dictionary(false, store) || 884 exception(store) || 885 enum_(store) || 886 typedef(store) || 887 implements_(store) 888 ; 889 }; 890 891 var definitions = function (store) { 892 if (!tokens.length) return []; 893 var defs = []; 894 while (true) { 895 var ea = extended_attrs(store ? defs : null) 896 , def = definition(store ? defs : null); 897 if (!def) { 898 if (ea.length) error("Stray extended attributes"); 899 break; 900 } 901 def.extAttrs = ea; 902 defs.push(def); 903 } 904 return defs; 905 }; 906 var res = definitions(opt.ws); 907 if (tokens.length) error("Unrecognised tokens"); 908 return res; 909 }; 910 911 var inNode = typeof module !== "undefined" && module.exports 912 , obj = { 913 parse: function (str, opt) { 914 if (!opt) opt = {}; 915 var tokens = tokenise(str); 916 return parse(tokens, opt); 917 } 918 }; 919 920 if (inNode) module.exports = obj; 921 else self.WebIDL2 = obj; 922 }());