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