summaryrefslogtreecommitdiffstats
path: root/dom/imptests/WebIDLParser.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/imptests/WebIDLParser.js')
-rw-r--r--dom/imptests/WebIDLParser.js924
1 files changed, 924 insertions, 0 deletions
diff --git a/dom/imptests/WebIDLParser.js b/dom/imptests/WebIDLParser.js
new file mode 100644
index 0000000000..103a7f48bd
--- /dev/null
+++ b/dom/imptests/WebIDLParser.js
@@ -0,0 +1,924 @@
+
+
+(function () {
+ var tokenise = function (str) {
+ var tokens = []
+ , re = {
+ "float": /^-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][-+]?[0-9]+)?|[0-9]+[Ee][-+]?[0-9]+)/
+ , "integer": /^-?(0([Xx][0-9A-Fa-f]+|[0-7]*)|[1-9][0-9]*)/
+ , "identifier": /^[A-Z_a-z][0-9A-Z_a-z]*/
+ , "string": /^"[^"]*"/
+ , "whitespace": /^(?:[\t\n\r ]+|[\t\n\r ]*((\/\/.*|\/\*(.|\n|\r)*?\*\/)[\t\n\r ]*))+/
+ , "other": /^[^\t\n\r 0-9A-Z_a-z]/
+ }
+ , types = []
+ ;
+ for (var k in re) types.push(k);
+ while (str.length > 0) {
+ var matched = false;
+ for (var i = 0, n = types.length; i < n; i++) {
+ var type = types[i];
+ str = str.replace(re[type], function (tok) {
+ tokens.push({ type: type, value: tok });
+ matched = true;
+ return "";
+ });
+ if (matched) break;
+ }
+ if (matched) continue;
+ throw new Error("Token stream not progressing");
+ }
+ return tokens;
+ };
+
+ var parse = function (tokens, opt) {
+ var line = 1;
+ tokens = tokens.slice();
+
+ var FLOAT = "float"
+ , INT = "integer"
+ , ID = "identifier"
+ , STR = "string"
+ , OTHER = "other"
+ ;
+
+ var WebIDLParseError = function (str, line, input, tokens) {
+ this.message = str;
+ this.line = line;
+ this.input = input;
+ this.tokens = tokens;
+ };
+ WebIDLParseError.prototype.toString = function () {
+ return this.message + ", line " + this.line + " (tokens: '" + this.input + "')\n" +
+ JSON.stringify(this.tokens, null, 4);
+ };
+
+ var error = function (str) {
+ var tok = "", numTokens = 0, maxTokens = 5;
+ while (numTokens < maxTokens && tokens.length > numTokens) {
+ tok += tokens[numTokens].value;
+ numTokens++;
+ }
+ throw new WebIDLParseError(str, line, tok, tokens.slice(0, 5));
+ };
+
+ var last_token = null;
+
+ var consume = function (type, value) {
+ if (!tokens.length || tokens[0].type !== type) return;
+ if (typeof value === "undefined" || tokens[0].value === value) {
+ last_token = tokens.shift();
+ if (type === ID) last_token.value = last_token.value.replace(/^_/, "");
+ return last_token;
+ }
+ };
+
+ var ws = function () {
+ if (!tokens.length) return;
+ if (tokens[0].type === "whitespace") {
+ var t = tokens.shift();
+ t.value.replace(/\n/g, function (m) { line++; return m; });
+ return t;
+ }
+ };
+
+ var all_ws = function (store, pea) { // pea == post extended attribute, tpea = same for types
+ var t = { type: "whitespace", value: "" };
+ while (true) {
+ var w = ws();
+ if (!w) break;
+ t.value += w.value;
+ }
+ if (t.value.length > 0) {
+ if (store) {
+ var w = t.value
+ , re = {
+ "ws": /^([\t\n\r ]+)/
+ , "line-comment": /^\/\/(.*)\n?/m
+ , "multiline-comment": /^\/\*((?:.|\n|\r)*?)\*\//
+ }
+ , wsTypes = []
+ ;
+ for (var k in re) wsTypes.push(k);
+ while (w.length) {
+ var matched = false;
+ for (var i = 0, n = wsTypes.length; i < n; i++) {
+ var type = wsTypes[i];
+ w = w.replace(re[type], function (tok, m1) {
+ store.push({ type: type + (pea ? ("-" + pea) : ""), value: m1 });
+ matched = true;
+ return "";
+ });
+ if (matched) break;
+ }
+ if (matched) continue;
+ throw new Error("Surprising white space construct."); // this shouldn't happen
+ }
+ }
+ return t;
+ }
+ };
+
+ var integer_type = function () {
+ var ret = "";
+ all_ws();
+ if (consume(ID, "unsigned")) ret = "unsigned ";
+ all_ws();
+ if (consume(ID, "short")) return ret + "short";
+ if (consume(ID, "long")) {
+ ret += "long";
+ all_ws();
+ if (consume(ID, "long")) return ret + " long";
+ return ret;
+ }
+ if (ret) error("Failed to parse integer type");
+ };
+
+ var float_type = function () {
+ var ret = "";
+ all_ws();
+ if (consume(ID, "unrestricted")) ret = "unrestricted ";
+ all_ws();
+ if (consume(ID, "float")) return ret + "float";
+ if (consume(ID, "double")) return ret + "double";
+ if (ret) error("Failed to parse float type");
+ };
+
+ var primitive_type = function () {
+ var num_type = integer_type() || float_type();
+ if (num_type) return num_type;
+ all_ws();
+ if (consume(ID, "boolean")) return "boolean";
+ if (consume(ID, "byte")) return "byte";
+ if (consume(ID, "octet")) return "octet";
+ };
+
+ var const_value = function () {
+ if (consume(ID, "true")) return { type: "boolean", value: true };
+ if (consume(ID, "false")) return { type: "boolean", value: false };
+ if (consume(ID, "null")) return { type: "null" };
+ if (consume(ID, "Infinity")) return { type: "Infinity", negative: false };
+ if (consume(ID, "NaN")) return { type: "NaN" };
+ var ret = consume(FLOAT) || consume(INT);
+ if (ret) return { type: "number", value: 1 * ret.value };
+ var tok = consume(OTHER, "-");
+ if (tok) {
+ if (consume(ID, "Infinity")) return { type: "Infinity", negative: true };
+ else tokens.unshift(tok);
+ }
+ };
+
+ var type_suffix = function (obj) {
+ while (true) {
+ all_ws();
+ if (consume(OTHER, "?")) {
+ if (obj.nullable) error("Can't nullable more than once");
+ obj.nullable = true;
+ }
+ else if (consume(OTHER, "[")) {
+ all_ws();
+ consume(OTHER, "]") || error("Unterminated array type");
+ if (!obj.array) {
+ obj.array = 1;
+ obj.nullableArray = [obj.nullable];
+ }
+ else {
+ obj.array++;
+ obj.nullableArray.push(obj.nullable);
+ }
+ obj.nullable = false;
+ }
+ else return;
+ }
+ };
+
+ var single_type = function () {
+ var prim = primitive_type()
+ , ret = { sequence: false, generic: null, nullable: false, array: false, union: false }
+ , name
+ , value
+ ;
+ if (prim) {
+ ret.idlType = prim;
+ }
+ else if (name = consume(ID)) {
+ value = name.value;
+ all_ws();
+ // Generic types
+ if (consume(OTHER, "<")) {
+ // backwards compat
+ if (value === "sequence") {
+ ret.sequence = true;
+ }
+ ret.generic = value;
+ ret.idlType = type() || error("Error parsing generic type " + value);
+ all_ws();
+ if (!consume(OTHER, ">")) error("Unterminated generic type " + value);
+ all_ws();
+ if (consume(OTHER, "?")) ret.nullable = true;
+ return ret;
+ }
+ else {
+ ret.idlType = value;
+ }
+ }
+ else {
+ return;
+ }
+ type_suffix(ret);
+ if (ret.nullable && !ret.array && ret.idlType === "any") error("Type any cannot be made nullable");
+ return ret;
+ };
+
+ var union_type = function () {
+ all_ws();
+ if (!consume(OTHER, "(")) return;
+ var ret = { sequence: false, generic: null, nullable: false, array: false, union: true, idlType: [] };
+ var fst = type() || error("Union type with no content");
+ ret.idlType.push(fst);
+ while (true) {
+ all_ws();
+ if (!consume(ID, "or")) break;
+ var typ = type() || error("No type after 'or' in union type");
+ ret.idlType.push(typ);
+ }
+ if (!consume(OTHER, ")")) error("Unterminated union type");
+ type_suffix(ret);
+ return ret;
+ };
+
+ var type = function () {
+ return single_type() || union_type();
+ };
+
+ var argument = function (store) {
+ var ret = { optional: false, variadic: false };
+ ret.extAttrs = extended_attrs(store);
+ all_ws(store, "pea");
+ var opt_token = consume(ID, "optional");
+ if (opt_token) {
+ ret.optional = true;
+ all_ws();
+ }
+ ret.idlType = type();
+ if (!ret.idlType) {
+ if (opt_token) tokens.unshift(opt_token);
+ return;
+ }
+ var type_token = last_token;
+ if (!ret.optional) {
+ all_ws();
+ if (tokens.length >= 3 &&
+ tokens[0].type === "other" && tokens[0].value === "." &&
+ tokens[1].type === "other" && tokens[1].value === "." &&
+ tokens[2].type === "other" && tokens[2].value === "."
+ ) {
+ tokens.shift();
+ tokens.shift();
+ tokens.shift();
+ ret.variadic = true;
+ }
+ }
+ all_ws();
+ var name = consume(ID);
+ if (!name) {
+ if (opt_token) tokens.unshift(opt_token);
+ tokens.unshift(type_token);
+ return;
+ }
+ ret.name = name.value;
+ if (ret.optional) {
+ all_ws();
+ ret["default"] = default_();
+ }
+ return ret;
+ };
+
+ var argument_list = function (store) {
+ var ret = []
+ , arg = argument(store ? ret : null)
+ ;
+ if (!arg) return;
+ ret.push(arg);
+ while (true) {
+ all_ws(store ? ret : null);
+ if (!consume(OTHER, ",")) return ret;
+ var nxt = argument(store ? ret : null) || error("Trailing comma in arguments list");
+ ret.push(nxt);
+ }
+ };
+
+ var type_pair = function () {
+ all_ws();
+ var k = type();
+ if (!k) return;
+ all_ws()
+ if (!consume(OTHER, ",")) return;
+ all_ws();
+ var v = type();
+ if (!v) return;
+ return [k, v];
+ };
+
+ var simple_extended_attr = function (store) {
+ all_ws();
+ var name = consume(ID);
+ if (!name) return;
+ var ret = {
+ name: name.value
+ , "arguments": null
+ };
+ all_ws();
+ var eq = consume(OTHER, "=");
+ if (eq) {
+ all_ws();
+ ret.rhs = consume(ID);
+ if (!ret.rhs) return error("No right hand side to extended attribute assignment");
+ }
+ all_ws();
+ if (consume(OTHER, "(")) {
+ var args, pair;
+ // [Constructor(DOMString str)]
+ if (args = argument_list(store)) {
+ ret["arguments"] = args;
+ }
+ // [MapClass(DOMString, DOMString)]
+ else if (pair = type_pair()) {
+ ret.typePair = pair;
+ }
+ // [Constructor()]
+ else {
+ ret["arguments"] = [];
+ }
+ all_ws();
+ consume(OTHER, ")") || error("Unexpected token in extended attribute argument list or type pair");
+ }
+ return ret;
+ };
+
+ // Note: we parse something simpler than the official syntax. It's all that ever
+ // seems to be used
+ var extended_attrs = function (store) {
+ var eas = [];
+ all_ws(store);
+ if (!consume(OTHER, "[")) return eas;
+ eas[0] = simple_extended_attr(store) || error("Extended attribute with not content");
+ all_ws();
+ while (consume(OTHER, ",")) {
+ eas.push(simple_extended_attr(store) || error("Trailing comma in extended attribute"));
+ all_ws();
+ }
+ consume(OTHER, "]") || error("No end of extended attribute");
+ return eas;
+ };
+
+ var default_ = function () {
+ all_ws();
+ if (consume(OTHER, "=")) {
+ all_ws();
+ var def = const_value();
+ if (def) {
+ return def;
+ }
+ else {
+ var str = consume(STR) || error("No value for default");
+ str.value = str.value.replace(/^"/, "").replace(/"$/, "");
+ return str;
+ }
+ }
+ };
+
+ var const_ = function (store) {
+ all_ws(store, "pea");
+ if (!consume(ID, "const")) return;
+ var ret = { type: "const", nullable: false };
+ all_ws();
+ var typ = primitive_type();
+ if (!typ) {
+ typ = consume(ID) || error("No type for const");
+ typ = typ.value;
+ }
+ ret.idlType = typ;
+ all_ws();
+ if (consume(OTHER, "?")) {
+ ret.nullable = true;
+ all_ws();
+ }
+ var name = consume(ID) || error("No name for const");
+ ret.name = name.value;
+ all_ws();
+ consume(OTHER, "=") || error("No value assignment for const");
+ all_ws();
+ var cnt = const_value();
+ if (cnt) ret.value = cnt;
+ else error("No value for const");
+ all_ws();
+ consume(OTHER, ";") || error("Unterminated const");
+ return ret;
+ };
+
+ var inheritance = function () {
+ all_ws();
+ if (consume(OTHER, ":")) {
+ all_ws();
+ var inh = consume(ID) || error ("No type in inheritance");
+ return inh.value;
+ }
+ };
+
+ var operation_rest = function (ret, store) {
+ all_ws();
+ if (!ret) ret = {};
+ var name = consume(ID);
+ ret.name = name ? name.value : null;
+ all_ws();
+ consume(OTHER, "(") || error("Invalid operation");
+ ret["arguments"] = argument_list(store) || [];
+ all_ws();
+ consume(OTHER, ")") || error("Unterminated operation");
+ all_ws();
+ consume(OTHER, ";") || error("Unterminated operation");
+ return ret;
+ };
+
+ var callback = function (store) {
+ all_ws(store, "pea");
+ var ret;
+ if (!consume(ID, "callback")) return;
+ all_ws();
+ var tok = consume(ID, "interface");
+ if (tok) {
+ tokens.unshift(tok);
+ ret = interface_();
+ ret.type = "callback interface";
+ return ret;
+ }
+ var name = consume(ID) || error("No name for callback");
+ ret = { type: "callback", name: name.value };
+ all_ws();
+ consume(OTHER, "=") || error("No assignment in callback");
+ all_ws();
+ ret.idlType = return_type();
+ all_ws();
+ consume(OTHER, "(") || error("No arguments in callback");
+ ret["arguments"] = argument_list(store) || [];
+ all_ws();
+ consume(OTHER, ")") || error("Unterminated callback");
+ all_ws();
+ consume(OTHER, ";") || error("Unterminated callback");
+ return ret;
+ };
+
+ var attribute = function (store) {
+ all_ws(store, "pea");
+ var grabbed = []
+ , ret = {
+ type: "attribute"
+ , "static": false
+ , stringifier: false
+ , inherit: false
+ , readonly: false
+ };
+ if (consume(ID, "static")) {
+ ret["static"] = true;
+ grabbed.push(last_token);
+ }
+ else if (consume(ID, "stringifier")) {
+ ret.stringifier = true;
+ grabbed.push(last_token);
+ }
+ var w = all_ws();
+ if (w) grabbed.push(w);
+ if (consume(ID, "inherit")) {
+ if (ret["static"] || ret.stringifier) error("Cannot have a static or stringifier inherit");
+ ret.inherit = true;
+ grabbed.push(last_token);
+ var w = all_ws();
+ if (w) grabbed.push(w);
+ }
+ if (consume(ID, "readonly")) {
+ ret.readonly = true;
+ grabbed.push(last_token);
+ var w = all_ws();
+ if (w) grabbed.push(w);
+ }
+ if (!consume(ID, "attribute")) {
+ tokens = grabbed.concat(tokens);
+ return;
+ }
+ all_ws();
+ ret.idlType = type() || error("No type in attribute");
+ if (ret.idlType.sequence) error("Attributes cannot accept sequence types");
+ all_ws();
+ var name = consume(ID) || error("No name in attribute");
+ ret.name = name.value;
+ all_ws();
+ consume(OTHER, ";") || error("Unterminated attribute");
+ return ret;
+ };
+
+ var return_type = function () {
+ var typ = type();
+ if (!typ) {
+ if (consume(ID, "void")) {
+ return "void";
+ }
+ else error("No return type");
+ }
+ return typ;
+ };
+
+ var operation = function (store) {
+ all_ws(store, "pea");
+ var ret = {
+ type: "operation"
+ , getter: false
+ , setter: false
+ , creator: false
+ , deleter: false
+ , legacycaller: false
+ , "static": false
+ , stringifier: false
+ };
+ while (true) {
+ all_ws();
+ if (consume(ID, "getter")) ret.getter = true;
+ else if (consume(ID, "setter")) ret.setter = true;
+ else if (consume(ID, "creator")) ret.creator = true;
+ else if (consume(ID, "deleter")) ret.deleter = true;
+ else if (consume(ID, "legacycaller")) ret.legacycaller = true;
+ else break;
+ }
+ if (ret.getter || ret.setter || ret.creator || ret.deleter || ret.legacycaller) {
+ all_ws();
+ ret.idlType = return_type();
+ operation_rest(ret, store);
+ return ret;
+ }
+ if (consume(ID, "static")) {
+ ret["static"] = true;
+ ret.idlType = return_type();
+ operation_rest(ret, store);
+ return ret;
+ }
+ else if (consume(ID, "stringifier")) {
+ ret.stringifier = true;
+ all_ws();
+ if (consume(OTHER, ";")) return ret;
+ ret.idlType = return_type();
+ operation_rest(ret, store);
+ return ret;
+ }
+ ret.idlType = return_type();
+ all_ws();
+ if (consume(ID, "iterator")) {
+ all_ws();
+ ret.type = "iterator";
+ if (consume(ID, "object")) {
+ ret.iteratorObject = "object";
+ }
+ else if (consume(OTHER, "=")) {
+ all_ws();
+ var name = consume(ID) || error("No right hand side in iterator");
+ ret.iteratorObject = name.value;
+ }
+ all_ws();
+ consume(OTHER, ";") || error("Unterminated iterator");
+ return ret;
+ }
+ else {
+ operation_rest(ret, store);
+ return ret;
+ }
+ };
+
+ var identifiers = function (arr) {
+ while (true) {
+ all_ws();
+ if (consume(OTHER, ",")) {
+ all_ws();
+ var name = consume(ID) || error("Trailing comma in identifiers list");
+ arr.push(name.value);
+ }
+ else break;
+ }
+ };
+
+ var serialiser = function (store) {
+ all_ws(store, "pea");
+ if (!consume(ID, "serializer")) return;
+ var ret = { type: "serializer" };
+ all_ws();
+ if (consume(OTHER, "=")) {
+ all_ws();
+ if (consume(OTHER, "{")) {
+ ret.patternMap = true;
+ all_ws();
+ var id = consume(ID);
+ if (id && id.value === "getter") {
+ ret.names = ["getter"];
+ }
+ else if (id && id.value === "inherit") {
+ ret.names = ["inherit"];
+ identifiers(ret.names);
+ }
+ else if (id) {
+ ret.names = [id.value];
+ identifiers(ret.names);
+ }
+ else {
+ ret.names = [];
+ }
+ all_ws();
+ consume(OTHER, "}") || error("Unterminated serializer pattern map");
+ }
+ else if (consume(OTHER, "[")) {
+ ret.patternList = true;
+ all_ws();
+ var id = consume(ID);
+ if (id && id.value === "getter") {
+ ret.names = ["getter"];
+ }
+ else if (id) {
+ ret.names = [id.value];
+ identifiers(ret.names);
+ }
+ else {
+ ret.names = [];
+ }
+ all_ws();
+ consume(OTHER, "]") || error("Unterminated serializer pattern list");
+ }
+ else {
+ var name = consume(ID) || error("Invalid serializer");
+ ret.name = name.value;
+ }
+ all_ws();
+ consume(OTHER, ";") || error("Unterminated serializer");
+ return ret;
+ }
+ else if (consume(OTHER, ";")) {
+ // noop, just parsing
+ }
+ else {
+ ret.idlType = return_type();
+ all_ws();
+ ret.operation = operation_rest(null, store);
+ }
+ return ret;
+ };
+
+ var interface_ = function (isPartial, store) {
+ all_ws(isPartial ? null : store, "pea");
+ if (!consume(ID, "interface")) return;
+ all_ws();
+ var name = consume(ID) || error("No name for interface");
+ var mems = []
+ , ret = {
+ type: "interface"
+ , name: name.value
+ , partial: false
+ , members: mems
+ };
+ if (!isPartial) ret.inheritance = inheritance() || null;
+ all_ws();
+ consume(OTHER, "{") || error("Bodyless interface");
+ while (true) {
+ all_ws(store ? mems : null);
+ if (consume(OTHER, "}")) {
+ all_ws();
+ consume(OTHER, ";") || error("Missing semicolon after interface");
+ return ret;
+ }
+ var ea = extended_attrs(store ? mems : null);
+ all_ws();
+ var cnt = const_(store ? mems : null);
+ if (cnt) {
+ cnt.extAttrs = ea;
+ ret.members.push(cnt);
+ continue;
+ }
+ var mem = serialiser(store ? mems : null) ||
+ attribute(store ? mems : null) ||
+ operation(store ? mems : null) ||
+ error("Unknown member");
+ mem.extAttrs = ea;
+ ret.members.push(mem);
+ }
+ };
+
+ var partial = function (store) {
+ all_ws(store, "pea");
+ if (!consume(ID, "partial")) return;
+ var thing = dictionary(true, store) ||
+ interface_(true, store) ||
+ error("Partial doesn't apply to anything");
+ thing.partial = true;
+ return thing;
+ };
+
+ var dictionary = function (isPartial, store) {
+ all_ws(isPartial ? null : store, "pea");
+ if (!consume(ID, "dictionary")) return;
+ all_ws();
+ var name = consume(ID) || error("No name for dictionary");
+ var mems = []
+ , ret = {
+ type: "dictionary"
+ , name: name.value
+ , partial: false
+ , members: mems
+ };
+ if (!isPartial) ret.inheritance = inheritance() || null;
+ all_ws();
+ consume(OTHER, "{") || error("Bodyless dictionary");
+ while (true) {
+ all_ws(store ? mems : null);
+ if (consume(OTHER, "}")) {
+ all_ws();
+ consume(OTHER, ";") || error("Missing semicolon after dictionary");
+ return ret;
+ }
+ var ea = extended_attrs(store ? mems : null);
+ all_ws(store ? mems : null, "pea");
+ var typ = type() || error("No type for dictionary member");
+ all_ws();
+ var name = consume(ID) || error("No name for dictionary member");
+ ret.members.push({
+ type: "field"
+ , name: name.value
+ , idlType: typ
+ , extAttrs: ea
+ , "default": default_()
+ });
+ all_ws();
+ consume(OTHER, ";") || error("Unterminated dictionary member");
+ }
+ };
+
+ var exception = function (store) {
+ all_ws(store, "pea");
+ if (!consume(ID, "exception")) return;
+ all_ws();
+ var name = consume(ID) || error("No name for exception");
+ var mems = []
+ , ret = {
+ type: "exception"
+ , name: name.value
+ , members: mems
+ };
+ ret.inheritance = inheritance() || null;
+ all_ws();
+ consume(OTHER, "{") || error("Bodyless exception");
+ while (true) {
+ all_ws(store ? mems : null);
+ if (consume(OTHER, "}")) {
+ all_ws();
+ consume(OTHER, ";") || error("Missing semicolon after exception");
+ return ret;
+ }
+ var ea = extended_attrs(store ? mems : null);
+ all_ws(store ? mems : null, "pea");
+ var cnt = const_();
+ if (cnt) {
+ cnt.extAttrs = ea;
+ ret.members.push(cnt);
+ }
+ else {
+ var typ = type();
+ all_ws();
+ var name = consume(ID);
+ all_ws();
+ if (!typ || !name || !consume(OTHER, ";")) error("Unknown member in exception body");
+ ret.members.push({
+ type: "field"
+ , name: name.value
+ , idlType: typ
+ , extAttrs: ea
+ });
+ }
+ }
+ };
+
+ var enum_ = function (store) {
+ all_ws(store, "pea");
+ if (!consume(ID, "enum")) return;
+ all_ws();
+ var name = consume(ID) || error("No name for enum");
+ var vals = []
+ , ret = {
+ type: "enum"
+ , name: name.value
+ , values: vals
+ };
+ all_ws();
+ consume(OTHER, "{") || error("No curly for enum");
+ var saw_comma = false;
+ while (true) {
+ all_ws(store ? vals : null);
+ if (consume(OTHER, "}")) {
+ all_ws();
+ if (saw_comma) error("Trailing comma in enum");
+ consume(OTHER, ";") || error("No semicolon after enum");
+ return ret;
+ }
+ var val = consume(STR) || error("Unexpected value in enum");
+ ret.values.push(val.value.replace(/"/g, ""));
+ all_ws(store ? vals : null);
+ if (consume(OTHER, ",")) {
+ if (store) vals.push({ type: "," });
+ all_ws(store ? vals : null);
+ saw_comma = true;
+ }
+ else {
+ saw_comma = false;
+ }
+ }
+ };
+
+ var typedef = function (store) {
+ all_ws(store, "pea");
+ if (!consume(ID, "typedef")) return;
+ var ret = {
+ type: "typedef"
+ };
+ all_ws();
+ ret.typeExtAttrs = extended_attrs();
+ all_ws(store, "tpea");
+ ret.idlType = type() || error("No type in typedef");
+ all_ws();
+ var name = consume(ID) || error("No name in typedef");
+ ret.name = name.value;
+ all_ws();
+ consume(OTHER, ";") || error("Unterminated typedef");
+ return ret;
+ };
+
+ var implements_ = function (store) {
+ all_ws(store, "pea");
+ var target = consume(ID);
+ if (!target) return;
+ var w = all_ws();
+ if (consume(ID, "implements")) {
+ var ret = {
+ type: "implements"
+ , target: target.value
+ };
+ all_ws();
+ var imp = consume(ID) || error("Incomplete implements statement");
+ ret["implements"] = imp.value;
+ all_ws();
+ consume(OTHER, ";") || error("No terminating ; for implements statement");
+ return ret;
+ }
+ else {
+ // rollback
+ tokens.unshift(w);
+ tokens.unshift(target);
+ }
+ };
+
+ var definition = function (store) {
+ return callback(store) ||
+ interface_(false, store) ||
+ partial(store) ||
+ dictionary(false, store) ||
+ exception(store) ||
+ enum_(store) ||
+ typedef(store) ||
+ implements_(store)
+ ;
+ };
+
+ var definitions = function (store) {
+ if (!tokens.length) return [];
+ var defs = [];
+ while (true) {
+ var ea = extended_attrs(store ? defs : null)
+ , def = definition(store ? defs : null);
+ if (!def) {
+ if (ea.length) error("Stray extended attributes");
+ break;
+ }
+ def.extAttrs = ea;
+ defs.push(def);
+ }
+ return defs;
+ };
+ var res = definitions(opt.ws);
+ if (tokens.length) error("Unrecognised tokens");
+ return res;
+ };
+
+ var inNode = typeof module !== "undefined" && module.exports
+ , obj = {
+ parse: function (str, opt) {
+ if (!opt) opt = {};
+ var tokens = tokenise(str);
+ return parse(tokens, opt);
+ }
+ };
+
+ if (inNode) module.exports = obj;
+ else self.WebIDL2 = obj;
+}());