|
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) { |
|
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 () { |
|
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 }; |
|
94 |
|
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 }; |
|
109 |
|
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 }; |
|
119 |
|
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 }; |
|
128 |
|
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 }; |
|
143 |
|
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 }; |
|
160 |
|
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 }; |
|
192 |
|
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 }; |
|
209 |
|
210 var type = function () { |
|
211 return single_type() || union_type(); |
|
212 }; |
|
213 |
|
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 }; |
|
246 |
|
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 }; |
|
259 |
|
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 }; |
|
283 |
|
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 }; |
|
300 |
|
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 }; |
|
316 |
|
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 }; |
|
345 |
|
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 }; |
|
354 |
|
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 }; |
|
369 |
|
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 }; |
|
397 |
|
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 }; |
|
445 |
|
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 }; |
|
456 |
|
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 }; |
|
520 |
|
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 }; |
|
532 |
|
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 }; |
|
596 |
|
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 }; |
|
631 |
|
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 }; |
|
639 |
|
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 }; |
|
677 |
|
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 }; |
|
720 |
|
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 }; |
|
754 |
|
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 }; |
|
772 |
|
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 }; |
|
796 |
|
797 var definition = function () { |
|
798 return callback() || |
|
799 interface_() || |
|
800 partial() || |
|
801 dictionary() || |
|
802 exception() || |
|
803 enum_() || |
|
804 typedef() || |
|
805 implements_() |
|
806 ; |
|
807 }; |
|
808 |
|
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 }; |
|
828 |
|
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 }()); |