forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			924 lines
		
	
	
	
		
			33 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			924 lines
		
	
	
	
		
			33 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
 | |
| 
 | |
| (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;
 | |
| }());
 | 
