4 var tokenise = function (str
) {
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]/
16 for (var k
in re
) types
.push(k
);
17 while (str
.length
> 0) {
19 for (var i
= 0, n
= types
.length
; i
< n
; i
++) {
21 str
= str
.replace(re
[type
], function (tok
) {
22 tokens
.push({ type
: type
, value
: tok
});
28 if (matched
) continue;
29 throw new Error("Token stream not progressing");
34 var parse = function (tokens
, opt
) {
36 tokens
= tokens
.slice();
45 var WebIDLParseError = function (str
, line
, input
, tokens
) {
51 WebIDLParseError
.prototype.toString = function () {
52 return this.message
+ ", line " + this.line
+ " (tokens: '" + this.input
+ "')\n" +
53 JSON
.stringify(this.tokens
, null, 4);
56 var error = function (str
) {
57 var tok
= "", numTokens
= 0, maxTokens
= 5;
58 while (numTokens
< maxTokens
&& tokens
.length
> numTokens
) {
59 tok
+= tokens
[numTokens
].value
;
62 throw new WebIDLParseError(str
, line
, tok
, tokens
.slice(0, 5));
65 var last_token
= null;
67 var consume = function (type
, value
) {
68 if (!tokens
.length
|| tokens
[0].type
!== type
) return;
69 if (typeof value
=== "undefined" || tokens
[0].value
=== value
) {
70 last_token
= tokens
.shift();
71 if (type
=== ID
) last_token
.value
= last_token
.value
.replace(/^_/, "");
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
; });
85 var all_ws = function (store
, pea
) { // pea == post extended attribute, tpea = same for types
86 var t
= { type
: "whitespace", value
: "" };
92 if (t
.value
.length
> 0) {
97 , "line-comment": /^\/\/(.*)\n?/m
98 , "multiline-comment": /^\/\*((?:.|\n|\r)*?)\*\//
102 for (var k
in re
) wsTypes
.push(k
);
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
});
114 if (matched
) continue;
115 throw new Error("Surprising white space construct."); // this shouldn't happen
122 var integer_type = function () {
125 if (consume(ID
, "unsigned")) ret
= "unsigned ";
127 if (consume(ID
, "short")) return ret
+ "short";
128 if (consume(ID
, "long")) {
131 if (consume(ID
, "long")) return ret
+ " long";
134 if (ret
) error("Failed to parse integer type");
137 var float_type = function () {
140 if (consume(ID
, "unrestricted")) ret
= "unrestricted ";
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");
147 var primitive_type = function () {
148 var num_type
= integer_type() || float_type();
149 if (num_type
) return num_type
;
151 if (consume(ID
, "boolean")) return "boolean";
152 if (consume(ID
, "byte")) return "byte";
153 if (consume(ID
, "octet")) return "octet";
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
, "-");
166 if (consume(ID
, "Infinity")) return { type
: "Infinity", negative
: true };
167 else tokens
.unshift(tok
);
171 var type_suffix = function (obj
) {
174 if (consume(OTHER
, "?")) {
175 if (obj
.nullable
) error("Can't nullable more than once");
178 else if (consume(OTHER
, "[")) {
180 consume(OTHER
, "]") || error("Unterminated array type");
183 obj
.nullableArray
= [obj
.nullable
];
187 obj
.nullableArray
.push(obj
.nullable
);
189 obj
.nullable
= false;
195 var single_type = function () {
196 var prim
= primitive_type()
197 , ret
= { sequence
: false, generic
: null, nullable
: false, array
: false, union
: false }
204 else if (name
= consume(ID
)) {
208 if (consume(OTHER
, "<")) {
210 if (value
=== "sequence") {
214 ret
.idlType
= type() || error("Error parsing generic type " + value
);
216 if (!consume(OTHER
, ">")) error("Unterminated generic type " + value
);
218 if (consume(OTHER
, "?")) ret
.nullable
= true;
229 if (ret
.nullable
&& !ret
.array
&& ret
.idlType
=== "any") error("Type any cannot be made nullable");
233 var union_type = function () {
235 if (!consume(OTHER
, "(")) return;
236 var ret
= { sequence
: false, generic
: null, nullable
: false, array
: false, union
: true, idlType
: [] };
237 var fst
= type() || error("Union type with no content");
238 ret
.idlType
.push(fst
);
241 if (!consume(ID
, "or")) break;
242 var typ
= type() || error("No type after 'or' in union type");
243 ret
.idlType
.push(typ
);
245 if (!consume(OTHER
, ")")) error("Unterminated union type");
250 var type = function () {
251 return single_type() || union_type();
254 var argument = function (store
) {
255 var ret
= { optional
: false, variadic
: false };
256 ret
.extAttrs
= extended_attrs(store
);
257 all_ws(store
, "pea");
258 var opt_token
= consume(ID
, "optional");
263 ret
.idlType
= type();
265 if (opt_token
) tokens
.unshift(opt_token
);
268 var type_token
= last_token
;
271 if (tokens
.length
>= 3 &&
272 tokens
[0].type
=== "other" && tokens
[0].value
=== "." &&
273 tokens
[1].type
=== "other" && tokens
[1].value
=== "." &&
274 tokens
[2].type
=== "other" && tokens
[2].value
=== "."
283 var name
= consume(ID
);
285 if (opt_token
) tokens
.unshift(opt_token
);
286 tokens
.unshift(type_token
);
289 ret
.name
= name
.value
;
292 ret
["default"] = default_();
297 var argument_list = function (store
) {
299 , arg
= argument(store
? ret
: null)
304 all_ws(store
? ret
: null);
305 if (!consume(OTHER
, ",")) return ret
;
306 var nxt
= argument(store
? ret
: null) || error("Trailing comma in arguments list");
311 var type_pair = function () {
316 if (!consume(OTHER
, ",")) return;
323 var simple_extended_attr = function (store
) {
325 var name
= consume(ID
);
332 var eq
= consume(OTHER
, "=");
335 ret
.rhs
= consume(ID
);
336 if (!ret
.rhs
) return error("No right hand side to extended attribute assignment");
339 if (consume(OTHER
, "(")) {
341 // [Constructor(DOMString str)]
342 if (args
= argument_list(store
)) {
343 ret
["arguments"] = args
;
345 // [MapClass(DOMString, DOMString)]
346 else if (pair
= type_pair()) {
351 ret
["arguments"] = [];
354 consume(OTHER
, ")") || error("Unexpected token in extended attribute argument list or type pair");
359 // Note: we parse something simpler than the official syntax. It's all that ever
361 var extended_attrs = function (store
) {
364 if (!consume(OTHER
, "[")) return eas
;
365 eas
[0] = simple_extended_attr(store
) || error("Extended attribute with not content");
367 while (consume(OTHER
, ",")) {
368 eas
.push(simple_extended_attr(store
) || error("Trailing comma in extended attribute"));
371 consume(OTHER
, "]") || error("No end of extended attribute");
375 var default_ = function () {
377 if (consume(OTHER
, "=")) {
379 var def
= const_value();
384 var str
= consume(STR
) || error("No value for default");
385 str
.value
= str
.value
.replace(/^"/, "").replace(/"$/, "");
391 var const_ = function (store
) {
392 all_ws(store
, "pea");
393 if (!consume(ID
, "const")) return;
394 var ret
= { type
: "const", nullable
: false };
396 var typ
= primitive_type();
398 typ
= consume(ID
) || error("No type for const");
403 if (consume(OTHER
, "?")) {
407 var name
= consume(ID
) || error("No name for const");
408 ret
.name
= name
.value
;
410 consume(OTHER
, "=") || error("No value assignment for const");
412 var cnt
= const_value();
413 if (cnt
) ret
.value
= cnt
;
414 else error("No value for const");
416 consume(OTHER
, ";") || error("Unterminated const");
420 var inheritance = function () {
422 if (consume(OTHER
, ":")) {
424 var inh
= consume(ID
) || error ("No type in inheritance");
429 var operation_rest = function (ret
, store
) {
432 var name
= consume(ID
);
433 ret
.name
= name
? name
.value
: null;
435 consume(OTHER
, "(") || error("Invalid operation");
436 ret
["arguments"] = argument_list(store
) || [];
438 consume(OTHER
, ")") || error("Unterminated operation");
440 consume(OTHER
, ";") || error("Unterminated operation");
444 var callback = function (store
) {
445 all_ws(store
, "pea");
447 if (!consume(ID
, "callback")) return;
449 var tok
= consume(ID
, "interface");
453 ret
.type
= "callback interface";
456 var name
= consume(ID
) || error("No name for callback");
457 ret
= { type
: "callback", name
: name
.value
};
459 consume(OTHER
, "=") || error("No assignment in callback");
461 ret
.idlType
= return_type();
463 consume(OTHER
, "(") || error("No arguments in callback");
464 ret
["arguments"] = argument_list(store
) || [];
466 consume(OTHER
, ")") || error("Unterminated callback");
468 consume(OTHER
, ";") || error("Unterminated callback");
472 var attribute = function (store
) {
473 all_ws(store
, "pea");
482 if (consume(ID
, "static")) {
483 ret
["static"] = true;
484 grabbed
.push(last_token
);
486 else if (consume(ID
, "stringifier")) {
487 ret
.stringifier
= true;
488 grabbed
.push(last_token
);
491 if (w
) grabbed
.push(w
);
492 if (consume(ID
, "inherit")) {
493 if (ret
["static"] || ret
.stringifier
) error("Cannot have a static or stringifier inherit");
495 grabbed
.push(last_token
);
497 if (w
) grabbed
.push(w
);
499 if (consume(ID
, "readonly")) {
501 grabbed
.push(last_token
);
503 if (w
) grabbed
.push(w
);
505 if (!consume(ID
, "attribute")) {
506 tokens
= grabbed
.concat(tokens
);
510 ret
.idlType
= type() || error("No type in attribute");
511 if (ret
.idlType
.sequence
) error("Attributes cannot accept sequence types");
513 var name
= consume(ID
) || error("No name in attribute");
514 ret
.name
= name
.value
;
516 consume(OTHER
, ";") || error("Unterminated attribute");
520 var return_type = function () {
523 if (consume(ID
, "void")) {
526 else error("No return type");
531 var operation = function (store
) {
532 all_ws(store
, "pea");
539 , legacycaller
: false
545 if (consume(ID
, "getter")) ret
.getter
= true;
546 else if (consume(ID
, "setter")) ret
.setter
= true;
547 else if (consume(ID
, "creator")) ret
.creator
= true;
548 else if (consume(ID
, "deleter")) ret
.deleter
= true;
549 else if (consume(ID
, "legacycaller")) ret
.legacycaller
= true;
552 if (ret
.getter
|| ret
.setter
|| ret
.creator
|| ret
.deleter
|| ret
.legacycaller
) {
554 ret
.idlType
= return_type();
555 operation_rest(ret
, store
);
558 if (consume(ID
, "static")) {
559 ret
["static"] = true;
560 ret
.idlType
= return_type();
561 operation_rest(ret
, store
);
564 else if (consume(ID
, "stringifier")) {
565 ret
.stringifier
= true;
567 if (consume(OTHER
, ";")) return ret
;
568 ret
.idlType
= return_type();
569 operation_rest(ret
, store
);
572 ret
.idlType
= return_type();
574 if (consume(ID
, "iterator")) {
576 ret
.type
= "iterator";
577 if (consume(ID
, "object")) {
578 ret
.iteratorObject
= "object";
580 else if (consume(OTHER
, "=")) {
582 var name
= consume(ID
) || error("No right hand side in iterator");
583 ret
.iteratorObject
= name
.value
;
586 consume(OTHER
, ";") || error("Unterminated iterator");
590 operation_rest(ret
, store
);
595 var identifiers = function (arr
) {
598 if (consume(OTHER
, ",")) {
600 var name
= consume(ID
) || error("Trailing comma in identifiers list");
601 arr
.push(name
.value
);
607 var serialiser = function (store
) {
608 all_ws(store
, "pea");
609 if (!consume(ID
, "serializer")) return;
610 var ret
= { type
: "serializer" };
612 if (consume(OTHER
, "=")) {
614 if (consume(OTHER
, "{")) {
615 ret
.patternMap
= true;
617 var id
= consume(ID
);
618 if (id
&& id
.value
=== "getter") {
619 ret
.names
= ["getter"];
621 else if (id
&& id
.value
=== "inherit") {
622 ret
.names
= ["inherit"];
623 identifiers(ret
.names
);
626 ret
.names
= [id
.value
];
627 identifiers(ret
.names
);
633 consume(OTHER
, "}") || error("Unterminated serializer pattern map");
635 else if (consume(OTHER
, "[")) {
636 ret
.patternList
= true;
638 var id
= consume(ID
);
639 if (id
&& id
.value
=== "getter") {
640 ret
.names
= ["getter"];
643 ret
.names
= [id
.value
];
644 identifiers(ret
.names
);
650 consume(OTHER
, "]") || error("Unterminated serializer pattern list");
653 var name
= consume(ID
) || error("Invalid serializer");
654 ret
.name
= name
.value
;
657 consume(OTHER
, ";") || error("Unterminated serializer");
660 else if (consume(OTHER
, ";")) {
661 // noop, just parsing
664 ret
.idlType
= return_type();
666 ret
.operation
= operation_rest(null, store
);
671 var interface_ = function (isPartial
, store
) {
672 all_ws(isPartial
? null : store
, "pea");
673 if (!consume(ID
, "interface")) return;
675 var name
= consume(ID
) || error("No name for interface");
683 if (!isPartial
) ret
.inheritance
= inheritance() || null;
685 consume(OTHER
, "{") || error("Bodyless interface");
687 all_ws(store
? mems
: null);
688 if (consume(OTHER
, "}")) {
690 consume(OTHER
, ";") || error("Missing semicolon after interface");
693 var ea
= extended_attrs(store
? mems
: null);
695 var cnt
= const_(store
? mems
: null);
698 ret
.members
.push(cnt
);
701 var mem
= serialiser(store
? mems
: null) ||
702 attribute(store
? mems
: null) ||
703 operation(store
? mems
: null) ||
704 error("Unknown member");
706 ret
.members
.push(mem
);
710 var partial = function (store
) {
711 all_ws(store
, "pea");
712 if (!consume(ID
, "partial")) return;
713 var thing
= dictionary(true, store
) ||
714 interface_(true, store
) ||
715 error("Partial doesn't apply to anything");
716 thing
.partial
= true;
720 var dictionary = function (isPartial
, store
) {
721 all_ws(isPartial
? null : store
, "pea");
722 if (!consume(ID
, "dictionary")) return;
724 var name
= consume(ID
) || error("No name for dictionary");
732 if (!isPartial
) ret
.inheritance
= inheritance() || null;
734 consume(OTHER
, "{") || error("Bodyless dictionary");
736 all_ws(store
? mems
: null);
737 if (consume(OTHER
, "}")) {
739 consume(OTHER
, ";") || error("Missing semicolon after dictionary");
742 var ea
= extended_attrs(store
? mems
: null);
743 all_ws(store
? mems
: null, "pea");
744 var typ
= type() || error("No type for dictionary member");
746 var name
= consume(ID
) || error("No name for dictionary member");
752 , "default": default_()
755 consume(OTHER
, ";") || error("Unterminated dictionary member");
759 var exception = function (store
) {
760 all_ws(store
, "pea");
761 if (!consume(ID
, "exception")) return;
763 var name
= consume(ID
) || error("No name for exception");
770 ret
.inheritance
= inheritance() || null;
772 consume(OTHER
, "{") || error("Bodyless exception");
774 all_ws(store
? mems
: null);
775 if (consume(OTHER
, "}")) {
777 consume(OTHER
, ";") || error("Missing semicolon after exception");
780 var ea
= extended_attrs(store
? mems
: null);
781 all_ws(store
? mems
: null, "pea");
785 ret
.members
.push(cnt
);
790 var name
= consume(ID
);
792 if (!typ
|| !name
|| !consume(OTHER
, ";")) error("Unknown member in exception body");
803 var enum_ = function (store
) {
804 all_ws(store
, "pea");
805 if (!consume(ID
, "enum")) return;
807 var name
= consume(ID
) || error("No name for enum");
815 consume(OTHER
, "{") || error("No curly for enum");
816 var saw_comma
= false;
818 all_ws(store
? vals
: null);
819 if (consume(OTHER
, "}")) {
821 if (saw_comma
) error("Trailing comma in enum");
822 consume(OTHER
, ";") || error("No semicolon after enum");
825 var val
= consume(STR
) || error("Unexpected value in enum");
826 ret
.values
.push(val
.value
.replace(/"/g, ""));
827 all_ws(store ? vals : null);
828 if (consume(OTHER, ",")) {
829 if (store) vals.push({ type: "," });
830 all_ws(store ? vals : null);
839 var typedef = function (store) {
840 all_ws(store, "pea
");
841 if (!consume(ID, "typedef
")) return;
846 ret.typeExtAttrs = extended_attrs();
847 all_ws(store, "tpea
");
848 ret.idlType = type() || error("No type
in typedef
");
850 var name = consume(ID) || error("No name
in typedef
");
851 ret.name = name.value;
853 consume(OTHER, ";") || error("Unterminated typedef
");
857 var implements_ = function (store) {
858 all_ws(store, "pea
");
859 var target = consume(ID);
862 if (consume(ID, "implements")) {
865 , target: target.value
868 var imp = consume(ID) || error("Incomplete
implements statement
");
869 ret["implements"] = imp.value;
871 consume(OTHER, ";") || error("No terminating
; for implements statement
");
877 tokens.unshift(target);
881 var definition = function (store) {
882 return callback(store) ||
883 interface_(false, store) ||
885 dictionary(false, store) ||
893 var definitions = function (store) {
894 if (!tokens.length) return [];
897 var ea = extended_attrs(store ? defs : null)
898 , def = definition(store ? defs : null);
900 if (ea.length) error("Stray extended attributes
");
908 var res = definitions(opt.ws);
909 if (tokens.length) error("Unrecognised tokens
");
913 var inNode = typeof module !== "undefined" && module.exports
915 parse: function (str, opt) {
917 var tokens = tokenise(str);
918 return parse(tokens, opt);
922 if (inNode) module.exports = obj;
923 else self.WebIDL2 = obj;