2013-09-30 15:35:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
(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;
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var parse = function (tokens, opt) {
|
2013-09-30 15:35:32 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var all_ws = function (store, pea) { // pea == post extended attribute, tpea = same for types
|
2013-09-30 15:35:32 +00:00
|
|
|
var t = { type: "whitespace", value: "" };
|
|
|
|
while (true) {
|
|
|
|
var w = ws();
|
|
|
|
if (!w) break;
|
|
|
|
t.value += w.value;
|
|
|
|
}
|
2014-11-30 23:36:45 +00:00
|
|
|
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;
|
|
|
|
}
|
2013-09-30 15:35:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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");
|
2014-11-30 23:36:45 +00:00
|
|
|
if (!obj.array) {
|
|
|
|
obj.array = 1;
|
|
|
|
obj.nullableArray = [obj.nullable];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
obj.array++;
|
|
|
|
obj.nullableArray.push(obj.nullable);
|
|
|
|
}
|
|
|
|
obj.nullable = false;
|
2013-09-30 15:35:32 +00:00
|
|
|
}
|
|
|
|
else return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
var single_type = function () {
|
|
|
|
var prim = primitive_type()
|
2014-11-30 23:36:45 +00:00
|
|
|
, ret = { sequence: false, generic: null, nullable: false, array: false, union: false }
|
|
|
|
, name
|
|
|
|
, value
|
2013-09-30 15:35:32 +00:00
|
|
|
;
|
|
|
|
if (prim) {
|
|
|
|
ret.idlType = prim;
|
|
|
|
}
|
2014-11-30 23:36:45 +00:00
|
|
|
else if (name = consume(ID)) {
|
|
|
|
value = name.value;
|
2013-09-30 15:35:32 +00:00
|
|
|
all_ws();
|
2014-11-30 23:36:45 +00:00
|
|
|
// 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);
|
2013-09-30 15:35:32 +00:00
|
|
|
all_ws();
|
2014-11-30 23:36:45 +00:00
|
|
|
if (!consume(OTHER, ">")) error("Unterminated generic type " + value);
|
2013-09-30 15:35:32 +00:00
|
|
|
all_ws();
|
|
|
|
if (consume(OTHER, "?")) ret.nullable = true;
|
|
|
|
return ret;
|
|
|
|
}
|
2014-11-30 23:36:45 +00:00
|
|
|
else {
|
|
|
|
ret.idlType = value;
|
|
|
|
}
|
2013-09-30 15:35:32 +00:00
|
|
|
}
|
|
|
|
else {
|
2014-11-30 23:36:45 +00:00
|
|
|
return;
|
2013-09-30 15:35:32 +00:00
|
|
|
}
|
|
|
|
type_suffix(ret);
|
2014-11-30 23:36:45 +00:00
|
|
|
if (ret.nullable && !ret.array && ret.idlType === "any") error("Type any cannot be made nullable");
|
2013-09-30 15:35:32 +00:00
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
|
|
|
var union_type = function () {
|
|
|
|
all_ws();
|
|
|
|
if (!consume(OTHER, "(")) return;
|
2014-11-30 23:36:45 +00:00
|
|
|
var ret = { sequence: false, generic: null, nullable: false, array: false, union: true, idlType: [] };
|
2013-09-30 15:35:32 +00:00
|
|
|
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();
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var argument = function (store) {
|
2013-09-30 15:35:32 +00:00
|
|
|
var ret = { optional: false, variadic: false };
|
2014-11-30 23:36:45 +00:00
|
|
|
ret.extAttrs = extended_attrs(store);
|
|
|
|
all_ws(store, "pea");
|
|
|
|
var opt_token = consume(ID, "optional");
|
|
|
|
if (opt_token) {
|
2013-09-30 15:35:32 +00:00
|
|
|
ret.optional = true;
|
|
|
|
all_ws();
|
|
|
|
}
|
|
|
|
ret.idlType = type();
|
2014-11-30 23:36:45 +00:00
|
|
|
if (!ret.idlType) {
|
|
|
|
if (opt_token) tokens.unshift(opt_token);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var type_token = last_token;
|
2013-09-30 15:35:32 +00:00
|
|
|
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();
|
2014-11-30 23:36:45 +00:00
|
|
|
var name = consume(ID);
|
|
|
|
if (!name) {
|
|
|
|
if (opt_token) tokens.unshift(opt_token);
|
|
|
|
tokens.unshift(type_token);
|
|
|
|
return;
|
|
|
|
}
|
2013-09-30 15:35:32 +00:00
|
|
|
ret.name = name.value;
|
|
|
|
if (ret.optional) {
|
|
|
|
all_ws();
|
|
|
|
ret["default"] = default_();
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var argument_list = function (store) {
|
|
|
|
var ret = []
|
|
|
|
, arg = argument(store ? ret : null)
|
|
|
|
;
|
|
|
|
if (!arg) return;
|
2013-09-30 15:35:32 +00:00
|
|
|
ret.push(arg);
|
|
|
|
while (true) {
|
2014-11-30 23:36:45 +00:00
|
|
|
all_ws(store ? ret : null);
|
2013-09-30 15:35:32 +00:00
|
|
|
if (!consume(OTHER, ",")) return ret;
|
2014-11-30 23:36:45 +00:00
|
|
|
var nxt = argument(store ? ret : null) || error("Trailing comma in arguments list");
|
2013-09-30 15:35:32 +00:00
|
|
|
ret.push(nxt);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
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) {
|
2013-09-30 15:35:32 +00:00
|
|
|
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, "(")) {
|
2014-11-30 23:36:45 +00:00
|
|
|
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"] = [];
|
|
|
|
}
|
2013-09-30 15:35:32 +00:00
|
|
|
all_ws();
|
2014-11-30 23:36:45 +00:00
|
|
|
consume(OTHER, ")") || error("Unexpected token in extended attribute argument list or type pair");
|
2013-09-30 15:35:32 +00:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Note: we parse something simpler than the official syntax. It's all that ever
|
|
|
|
// seems to be used
|
2014-11-30 23:36:45 +00:00
|
|
|
var extended_attrs = function (store) {
|
2013-09-30 15:35:32 +00:00
|
|
|
var eas = [];
|
2014-11-30 23:36:45 +00:00
|
|
|
all_ws(store);
|
2013-09-30 15:35:32 +00:00
|
|
|
if (!consume(OTHER, "[")) return eas;
|
2014-11-30 23:36:45 +00:00
|
|
|
eas[0] = simple_extended_attr(store) || error("Extended attribute with not content");
|
2013-09-30 15:35:32 +00:00
|
|
|
all_ws();
|
|
|
|
while (consume(OTHER, ",")) {
|
2014-11-30 23:36:45 +00:00
|
|
|
eas.push(simple_extended_attr(store) || error("Trailing comma in extended attribute"));
|
2013-09-30 15:35:32 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var const_ = function (store) {
|
|
|
|
all_ws(store, "pea");
|
2013-09-30 15:35:32 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var operation_rest = function (ret, store) {
|
2013-09-30 15:35:32 +00:00
|
|
|
all_ws();
|
|
|
|
if (!ret) ret = {};
|
|
|
|
var name = consume(ID);
|
|
|
|
ret.name = name ? name.value : null;
|
|
|
|
all_ws();
|
|
|
|
consume(OTHER, "(") || error("Invalid operation");
|
2014-11-30 23:36:45 +00:00
|
|
|
ret["arguments"] = argument_list(store) || [];
|
2013-09-30 15:35:32 +00:00
|
|
|
all_ws();
|
|
|
|
consume(OTHER, ")") || error("Unterminated operation");
|
|
|
|
all_ws();
|
|
|
|
consume(OTHER, ";") || error("Unterminated operation");
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var callback = function (store) {
|
|
|
|
all_ws(store, "pea");
|
2013-09-30 15:35:32 +00:00
|
|
|
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");
|
2014-11-30 23:36:45 +00:00
|
|
|
ret["arguments"] = argument_list(store) || [];
|
2013-09-30 15:35:32 +00:00
|
|
|
all_ws();
|
|
|
|
consume(OTHER, ")") || error("Unterminated callback");
|
|
|
|
all_ws();
|
|
|
|
consume(OTHER, ";") || error("Unterminated callback");
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var attribute = function (store) {
|
|
|
|
all_ws(store, "pea");
|
2013-09-30 15:35:32 +00:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var operation = function (store) {
|
|
|
|
all_ws(store, "pea");
|
2013-09-30 15:35:32 +00:00
|
|
|
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();
|
2014-11-30 23:36:45 +00:00
|
|
|
operation_rest(ret, store);
|
2013-09-30 15:35:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
if (consume(ID, "static")) {
|
|
|
|
ret["static"] = true;
|
|
|
|
ret.idlType = return_type();
|
2014-11-30 23:36:45 +00:00
|
|
|
operation_rest(ret, store);
|
2013-09-30 15:35:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
else if (consume(ID, "stringifier")) {
|
|
|
|
ret.stringifier = true;
|
|
|
|
all_ws();
|
|
|
|
if (consume(OTHER, ";")) return ret;
|
|
|
|
ret.idlType = return_type();
|
2014-11-30 23:36:45 +00:00
|
|
|
operation_rest(ret, store);
|
2013-09-30 15:35:32 +00:00
|
|
|
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 {
|
2014-11-30 23:36:45 +00:00
|
|
|
operation_rest(ret, store);
|
2013-09-30 15:35:32 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var serialiser = function (store) {
|
|
|
|
all_ws(store, "pea");
|
2013-09-30 15:35:32 +00:00
|
|
|
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);
|
2012-10-14 07:48:14 +00:00
|
|
|
}
|
|
|
|
else {
|
2013-09-30 15:35:32 +00:00
|
|
|
ret.names = [];
|
2013-07-18 13:43:19 +00:00
|
|
|
}
|
2013-09-30 15:35:32 +00:00
|
|
|
all_ws();
|
|
|
|
consume(OTHER, "}") || error("Unterminated serializer pattern map");
|
2013-07-18 13:43:19 +00:00
|
|
|
}
|
2013-09-30 15:35:32 +00:00
|
|
|
else if (consume(OTHER, "[")) {
|
|
|
|
ret.patternList = true;
|
|
|
|
all_ws();
|
|
|
|
var id = consume(ID);
|
|
|
|
if (id && id.value === "getter") {
|
|
|
|
ret.names = ["getter"];
|
2013-07-18 13:43:19 +00:00
|
|
|
}
|
2013-09-30 15:35:32 +00:00
|
|
|
else if (id) {
|
|
|
|
ret.names = [id.value];
|
|
|
|
identifiers(ret.names);
|
2013-07-18 13:43:19 +00:00
|
|
|
}
|
2013-09-30 15:35:32 +00:00
|
|
|
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();
|
2014-11-30 23:36:45 +00:00
|
|
|
ret.operation = operation_rest(null, store);
|
2013-09-30 15:35:32 +00:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var interface_ = function (isPartial, store) {
|
|
|
|
all_ws(isPartial ? null : store, "pea");
|
2013-09-30 15:35:32 +00:00
|
|
|
if (!consume(ID, "interface")) return;
|
|
|
|
all_ws();
|
|
|
|
var name = consume(ID) || error("No name for interface");
|
2014-11-30 23:36:45 +00:00
|
|
|
var mems = []
|
|
|
|
, ret = {
|
2013-09-30 15:35:32 +00:00
|
|
|
type: "interface"
|
|
|
|
, name: name.value
|
|
|
|
, partial: false
|
2014-11-30 23:36:45 +00:00
|
|
|
, members: mems
|
2013-09-30 15:35:32 +00:00
|
|
|
};
|
|
|
|
if (!isPartial) ret.inheritance = inheritance() || null;
|
|
|
|
all_ws();
|
|
|
|
consume(OTHER, "{") || error("Bodyless interface");
|
|
|
|
while (true) {
|
2014-11-30 23:36:45 +00:00
|
|
|
all_ws(store ? mems : null);
|
2013-09-30 15:35:32 +00:00
|
|
|
if (consume(OTHER, "}")) {
|
|
|
|
all_ws();
|
|
|
|
consume(OTHER, ";") || error("Missing semicolon after interface");
|
|
|
|
return ret;
|
|
|
|
}
|
2014-11-30 23:36:45 +00:00
|
|
|
var ea = extended_attrs(store ? mems : null);
|
2013-09-30 15:35:32 +00:00
|
|
|
all_ws();
|
2014-11-30 23:36:45 +00:00
|
|
|
var cnt = const_(store ? mems : null);
|
2013-09-30 15:35:32 +00:00
|
|
|
if (cnt) {
|
|
|
|
cnt.extAttrs = ea;
|
|
|
|
ret.members.push(cnt);
|
|
|
|
continue;
|
|
|
|
}
|
2014-11-30 23:36:45 +00:00
|
|
|
var mem = serialiser(store ? mems : null) ||
|
|
|
|
attribute(store ? mems : null) ||
|
|
|
|
operation(store ? mems : null) ||
|
|
|
|
error("Unknown member");
|
2013-09-30 15:35:32 +00:00
|
|
|
mem.extAttrs = ea;
|
|
|
|
ret.members.push(mem);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var partial = function (store) {
|
|
|
|
all_ws(store, "pea");
|
2013-09-30 15:35:32 +00:00
|
|
|
if (!consume(ID, "partial")) return;
|
2014-11-30 23:36:45 +00:00
|
|
|
var thing = dictionary(true, store) ||
|
|
|
|
interface_(true, store) ||
|
|
|
|
error("Partial doesn't apply to anything");
|
2013-09-30 15:35:32 +00:00
|
|
|
thing.partial = true;
|
|
|
|
return thing;
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var dictionary = function (isPartial, store) {
|
|
|
|
all_ws(isPartial ? null : store, "pea");
|
2013-09-30 15:35:32 +00:00
|
|
|
if (!consume(ID, "dictionary")) return;
|
|
|
|
all_ws();
|
|
|
|
var name = consume(ID) || error("No name for dictionary");
|
2014-11-30 23:36:45 +00:00
|
|
|
var mems = []
|
|
|
|
, ret = {
|
2013-09-30 15:35:32 +00:00
|
|
|
type: "dictionary"
|
|
|
|
, name: name.value
|
|
|
|
, partial: false
|
2014-11-30 23:36:45 +00:00
|
|
|
, members: mems
|
2013-09-30 15:35:32 +00:00
|
|
|
};
|
|
|
|
if (!isPartial) ret.inheritance = inheritance() || null;
|
|
|
|
all_ws();
|
|
|
|
consume(OTHER, "{") || error("Bodyless dictionary");
|
|
|
|
while (true) {
|
2014-11-30 23:36:45 +00:00
|
|
|
all_ws(store ? mems : null);
|
2013-09-30 15:35:32 +00:00
|
|
|
if (consume(OTHER, "}")) {
|
|
|
|
all_ws();
|
|
|
|
consume(OTHER, ";") || error("Missing semicolon after dictionary");
|
|
|
|
return ret;
|
|
|
|
}
|
2014-11-30 23:36:45 +00:00
|
|
|
var ea = extended_attrs(store ? mems : null);
|
|
|
|
all_ws(store ? mems : null, "pea");
|
2013-09-30 15:35:32 +00:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var exception = function (store) {
|
|
|
|
all_ws(store, "pea");
|
2013-09-30 15:35:32 +00:00
|
|
|
if (!consume(ID, "exception")) return;
|
|
|
|
all_ws();
|
|
|
|
var name = consume(ID) || error("No name for exception");
|
2014-11-30 23:36:45 +00:00
|
|
|
var mems = []
|
|
|
|
, ret = {
|
2013-09-30 15:35:32 +00:00
|
|
|
type: "exception"
|
|
|
|
, name: name.value
|
2014-11-30 23:36:45 +00:00
|
|
|
, members: mems
|
2013-09-30 15:35:32 +00:00
|
|
|
};
|
|
|
|
ret.inheritance = inheritance() || null;
|
|
|
|
all_ws();
|
|
|
|
consume(OTHER, "{") || error("Bodyless exception");
|
|
|
|
while (true) {
|
2014-11-30 23:36:45 +00:00
|
|
|
all_ws(store ? mems : null);
|
2013-09-30 15:35:32 +00:00
|
|
|
if (consume(OTHER, "}")) {
|
|
|
|
all_ws();
|
|
|
|
consume(OTHER, ";") || error("Missing semicolon after exception");
|
|
|
|
return ret;
|
|
|
|
}
|
2014-11-30 23:36:45 +00:00
|
|
|
var ea = extended_attrs(store ? mems : null);
|
|
|
|
all_ws(store ? mems : null, "pea");
|
2013-09-30 15:35:32 +00:00
|
|
|
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
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var enum_ = function (store) {
|
|
|
|
all_ws(store, "pea");
|
2013-09-30 15:35:32 +00:00
|
|
|
if (!consume(ID, "enum")) return;
|
|
|
|
all_ws();
|
|
|
|
var name = consume(ID) || error("No name for enum");
|
2014-11-30 23:36:45 +00:00
|
|
|
var vals = []
|
|
|
|
, ret = {
|
2013-09-30 15:35:32 +00:00
|
|
|
type: "enum"
|
|
|
|
, name: name.value
|
2014-11-30 23:36:45 +00:00
|
|
|
, values: vals
|
2013-09-30 15:35:32 +00:00
|
|
|
};
|
|
|
|
all_ws();
|
|
|
|
consume(OTHER, "{") || error("No curly for enum");
|
|
|
|
var saw_comma = false;
|
|
|
|
while (true) {
|
2014-11-30 23:36:45 +00:00
|
|
|
all_ws(store ? vals : null);
|
2013-09-30 15:35:32 +00:00
|
|
|
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, ""));
|
2014-11-30 23:36:45 +00:00
|
|
|
all_ws(store ? vals : null);
|
2013-09-30 15:35:32 +00:00
|
|
|
if (consume(OTHER, ",")) {
|
2014-11-30 23:36:45 +00:00
|
|
|
if (store) vals.push({ type: "," });
|
|
|
|
all_ws(store ? vals : null);
|
2013-09-30 15:35:32 +00:00
|
|
|
saw_comma = true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
saw_comma = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var typedef = function (store) {
|
|
|
|
all_ws(store, "pea");
|
2013-09-30 15:35:32 +00:00
|
|
|
if (!consume(ID, "typedef")) return;
|
|
|
|
var ret = {
|
|
|
|
type: "typedef"
|
|
|
|
};
|
|
|
|
all_ws();
|
|
|
|
ret.typeExtAttrs = extended_attrs();
|
2014-11-30 23:36:45 +00:00
|
|
|
all_ws(store, "tpea");
|
2013-09-30 15:35:32 +00:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var implements_ = function (store) {
|
|
|
|
all_ws(store, "pea");
|
2013-09-30 15:35:32 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var definition = function (store) {
|
|
|
|
return callback(store) ||
|
|
|
|
interface_(false, store) ||
|
|
|
|
partial(store) ||
|
|
|
|
dictionary(false, store) ||
|
|
|
|
exception(store) ||
|
|
|
|
enum_(store) ||
|
|
|
|
typedef(store) ||
|
|
|
|
implements_(store)
|
2013-09-30 15:35:32 +00:00
|
|
|
;
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var definitions = function (store) {
|
2013-09-30 15:35:32 +00:00
|
|
|
if (!tokens.length) return [];
|
|
|
|
var defs = [];
|
|
|
|
while (true) {
|
2014-11-30 23:36:45 +00:00
|
|
|
var ea = extended_attrs(store ? defs : null)
|
|
|
|
, def = definition(store ? defs : null);
|
2013-09-30 15:35:32 +00:00
|
|
|
if (!def) {
|
|
|
|
if (ea.length) error("Stray extended attributes");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
def.extAttrs = ea;
|
|
|
|
defs.push(def);
|
|
|
|
}
|
|
|
|
return defs;
|
|
|
|
};
|
2014-11-30 23:36:45 +00:00
|
|
|
var res = definitions(opt.ws);
|
2013-09-30 15:35:32 +00:00
|
|
|
if (tokens.length) error("Unrecognised tokens");
|
|
|
|
return res;
|
|
|
|
};
|
|
|
|
|
2014-11-30 23:36:45 +00:00
|
|
|
var inNode = typeof module !== "undefined" && module.exports
|
|
|
|
, obj = {
|
|
|
|
parse: function (str, opt) {
|
|
|
|
if (!opt) opt = {};
|
|
|
|
var tokens = tokenise(str);
|
|
|
|
return parse(tokens, opt);
|
|
|
|
}
|
2013-09-30 15:35:32 +00:00
|
|
|
};
|
2014-11-30 23:36:45 +00:00
|
|
|
|
|
|
|
if (inNode) module.exports = obj;
|
2015-06-20 07:16:51 +00:00
|
|
|
else self.WebIDL2 = obj;
|
2013-09-30 15:35:32 +00:00
|
|
|
}());
|