dom/imptests/WebIDLParser.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial