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;