# Coverage

95%
572
548
24

## webidl2.js

95%
572
548
24

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