dom/imptests/WebIDLParser.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/imptests/WebIDLParser.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,841 @@
     1.4 +
     1.5 +
     1.6 +(function () {
     1.7 +    var tokenise = function (str) {
     1.8 +        var tokens = []
     1.9 +        ,   re = {
    1.10 +                "float":        /^-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][-+]?[0-9]+)?|[0-9]+[Ee][-+]?[0-9]+)/
    1.11 +            ,   "integer":      /^-?(0([Xx][0-9A-Fa-f]+|[0-7]*)|[1-9][0-9]*)/
    1.12 +            ,   "identifier":   /^[A-Z_a-z][0-9A-Z_a-z]*/
    1.13 +            ,   "string":       /^"[^"]*"/
    1.14 +            ,   "whitespace":   /^(?:[\t\n\r ]+|[\t\n\r ]*((\/\/.*|\/\*(.|\n|\r)*?\*\/)[\t\n\r ]*))+/
    1.15 +            ,   "other":        /^[^\t\n\r 0-9A-Z_a-z]/
    1.16 +            }
    1.17 +        ,   types = []
    1.18 +        ;
    1.19 +        for (var k in re) types.push(k);
    1.20 +        while (str.length > 0) {
    1.21 +            var matched = false;
    1.22 +            for (var i = 0, n = types.length; i < n; i++) {
    1.23 +                var type = types[i];
    1.24 +                str = str.replace(re[type], function (tok) {
    1.25 +                    tokens.push({ type: type, value: tok });
    1.26 +                    matched = true;
    1.27 +                    return "";
    1.28 +                });
    1.29 +                if (matched) break;
    1.30 +            }
    1.31 +            if (matched) continue;
    1.32 +            throw new Error("Token stream not progressing");
    1.33 +        }
    1.34 +        return tokens;
    1.35 +    };
    1.36 +    
    1.37 +    var parse = function (tokens) {
    1.38 +        var line = 1;
    1.39 +        tokens = tokens.slice();
    1.40 +        
    1.41 +        var FLOAT = "float"
    1.42 +        ,   INT = "integer"
    1.43 +        ,   ID = "identifier"
    1.44 +        ,   STR = "string"
    1.45 +        ,   OTHER = "other"
    1.46 +        ;
    1.47 +        
    1.48 +        var WebIDLParseError = function (str, line, input, tokens) {
    1.49 +            this.message = str;
    1.50 +            this.line = line;
    1.51 +            this.input = input;
    1.52 +            this.tokens = tokens;
    1.53 +        };
    1.54 +        WebIDLParseError.prototype.toString = function () {
    1.55 +            return this.message + ", line " + this.line + " (tokens: '" + this.input + "')\n" +
    1.56 +                   JSON.stringify(this.tokens, null, 4);
    1.57 +        };
    1.58 +        
    1.59 +        var error = function (str) {
    1.60 +            var tok = "", numTokens = 0, maxTokens = 5;
    1.61 +            while (numTokens < maxTokens && tokens.length > numTokens) {
    1.62 +                tok += tokens[numTokens].value;
    1.63 +                numTokens++;
    1.64 +            }
    1.65 +            throw new WebIDLParseError(str, line, tok, tokens.slice(0, 5));
    1.66 +        };
    1.67 +        
    1.68 +        var last_token = null;
    1.69 +        
    1.70 +        var consume = function (type, value) {
    1.71 +            if (!tokens.length || tokens[0].type !== type) return;
    1.72 +            if (typeof value === "undefined" || tokens[0].value === value) {
    1.73 +                 last_token = tokens.shift();
    1.74 +                 if (type === ID) last_token.value = last_token.value.replace(/^_/, "");
    1.75 +                 return last_token;
    1.76 +             }
    1.77 +        };
    1.78 +        
    1.79 +        var ws = function () {
    1.80 +            if (!tokens.length) return;
    1.81 +            if (tokens[0].type === "whitespace") {
    1.82 +                var t = tokens.shift();
    1.83 +                t.value.replace(/\n/g, function (m) { line++; return m; });
    1.84 +                return t;
    1.85 +            }
    1.86 +        };
    1.87 +        
    1.88 +        var all_ws = function () {
    1.89 +            var t = { type: "whitespace", value: "" };
    1.90 +            while (true) {
    1.91 +                var w = ws();
    1.92 +                if (!w) break;
    1.93 +                t.value += w.value;
    1.94 +            }
    1.95 +            if (t.value.length > 0) return t;
    1.96 +        };
    1.97 +        
    1.98 +        var integer_type = function () {
    1.99 +            var ret = "";
   1.100 +            all_ws();
   1.101 +            if (consume(ID, "unsigned")) ret = "unsigned ";
   1.102 +            all_ws();
   1.103 +            if (consume(ID, "short")) return ret + "short";
   1.104 +            if (consume(ID, "long")) {
   1.105 +                ret += "long";
   1.106 +                all_ws();
   1.107 +                if (consume(ID, "long")) return ret + " long";
   1.108 +                return ret;
   1.109 +            }
   1.110 +            if (ret) error("Failed to parse integer type");
   1.111 +        };
   1.112 +        
   1.113 +        var float_type = function () {
   1.114 +            var ret = "";
   1.115 +            all_ws();
   1.116 +            if (consume(ID, "unrestricted")) ret = "unrestricted ";
   1.117 +            all_ws();
   1.118 +            if (consume(ID, "float")) return ret + "float";
   1.119 +            if (consume(ID, "double")) return ret + "double";
   1.120 +            if (ret) error("Failed to parse float type");
   1.121 +        };
   1.122 +        
   1.123 +        var primitive_type = function () {
   1.124 +            var num_type = integer_type() || float_type();
   1.125 +            if (num_type) return num_type;
   1.126 +            all_ws();
   1.127 +            if (consume(ID, "boolean")) return "boolean";
   1.128 +            if (consume(ID, "byte")) return "byte";
   1.129 +            if (consume(ID, "octet")) return "octet";
   1.130 +        };
   1.131 +        
   1.132 +        var const_value = function () {
   1.133 +            if (consume(ID, "true")) return { type: "boolean", value: true };
   1.134 +            if (consume(ID, "false")) return { type: "boolean", value: false };
   1.135 +            if (consume(ID, "null")) return { type: "null" };
   1.136 +            if (consume(ID, "Infinity")) return { type: "Infinity", negative: false };
   1.137 +            if (consume(ID, "NaN")) return { type: "NaN" };
   1.138 +            var ret = consume(FLOAT) || consume(INT);
   1.139 +            if (ret) return { type: "number", value: 1 * ret.value };
   1.140 +            var tok = consume(OTHER, "-");
   1.141 +            if (tok) {
   1.142 +                if (consume(ID, "Infinity")) return { type: "Infinity", negative: true };
   1.143 +                else tokens.unshift(tok);
   1.144 +            }
   1.145 +        };
   1.146 +        
   1.147 +        var type_suffix = function (obj) {
   1.148 +            while (true) {
   1.149 +                all_ws();
   1.150 +                if (consume(OTHER, "?")) {
   1.151 +                    if (obj.nullable) error("Can't nullable more than once");
   1.152 +                    obj.nullable = true;
   1.153 +                }
   1.154 +                else if (consume(OTHER, "[")) {
   1.155 +                    all_ws();
   1.156 +                    consume(OTHER, "]") || error("Unterminated array type");
   1.157 +                    if (!obj.array) obj.array = 1;
   1.158 +                    else obj.array++;
   1.159 +                }
   1.160 +                else return;
   1.161 +            }
   1.162 +        };
   1.163 +        
   1.164 +        var single_type = function () {
   1.165 +            var prim = primitive_type()
   1.166 +            ,   ret = { sequence: false, nullable: false, array: false, union: false }
   1.167 +            ;
   1.168 +            if (prim) {
   1.169 +                ret.idlType = prim;
   1.170 +            }
   1.171 +            else if (consume(ID, "sequence")) {
   1.172 +                all_ws();
   1.173 +                if (!consume(OTHER, "<")) {
   1.174 +                    ret.idlType = "sequence";
   1.175 +                }
   1.176 +                else {
   1.177 +                    ret.sequence = true;
   1.178 +                    ret.idlType = type() || error("Error parsing sequence type");
   1.179 +                    all_ws();
   1.180 +                    if (!consume(OTHER, ">")) error("Unterminated sequence");
   1.181 +                    all_ws();
   1.182 +                    if (consume(OTHER, "?")) ret.nullable = true;
   1.183 +                    return ret;
   1.184 +                }
   1.185 +            }
   1.186 +            else {
   1.187 +                var name = consume(ID);
   1.188 +                if (!name) return;
   1.189 +                ret.idlType = name.value;
   1.190 +            }
   1.191 +            type_suffix(ret);
   1.192 +            if (ret.nullable && ret.idlType === "any") error("Type any cannot be made nullable");
   1.193 +            return ret;
   1.194 +        };
   1.195 +        
   1.196 +        var union_type = function () {
   1.197 +            all_ws();
   1.198 +            if (!consume(OTHER, "(")) return;
   1.199 +            var ret = { sequence: false, nullable: false, array: false, union: true, idlType: [] };
   1.200 +            var fst = type() || error("Union type with no content");
   1.201 +            ret.idlType.push(fst);
   1.202 +            while (true) {
   1.203 +                all_ws();
   1.204 +                if (!consume(ID, "or")) break;
   1.205 +                var typ = type() || error("No type after 'or' in union type");
   1.206 +                ret.idlType.push(typ);
   1.207 +            }
   1.208 +            if (!consume(OTHER, ")")) error("Unterminated union type");
   1.209 +            type_suffix(ret);
   1.210 +            return ret;
   1.211 +        };
   1.212 +        
   1.213 +        var type = function () {
   1.214 +            return single_type() || union_type();
   1.215 +        };
   1.216 +        
   1.217 +        var argument = function () {
   1.218 +            var ret = { optional: false, variadic: false };
   1.219 +            ret.extAttrs = extended_attrs();
   1.220 +            all_ws();
   1.221 +            if (consume(ID, "optional")) {
   1.222 +                ret.optional = true;
   1.223 +                all_ws();
   1.224 +            }
   1.225 +            ret.idlType = type();
   1.226 +            if (!ret.idlType) return;
   1.227 +            if (!ret.optional) {
   1.228 +                all_ws();
   1.229 +                if (tokens.length >= 3 &&
   1.230 +                    tokens[0].type === "other" && tokens[0].value === "." &&
   1.231 +                    tokens[1].type === "other" && tokens[1].value === "." &&
   1.232 +                    tokens[2].type === "other" && tokens[2].value === "."
   1.233 +                    ) {
   1.234 +                    tokens.shift();
   1.235 +                    tokens.shift();
   1.236 +                    tokens.shift();
   1.237 +                    ret.variadic = true;
   1.238 +                }
   1.239 +            }
   1.240 +            all_ws();
   1.241 +            var name = consume(ID) || error("No name in argument");
   1.242 +            ret.name = name.value;
   1.243 +            if (ret.optional) {
   1.244 +                all_ws();
   1.245 +                ret["default"] = default_();
   1.246 +            }
   1.247 +            return ret;
   1.248 +        };
   1.249 +        
   1.250 +        var argument_list = function () {
   1.251 +            var arg = argument(), ret = [];
   1.252 +            if (!arg) return ret;
   1.253 +            ret.push(arg);
   1.254 +            while (true) {
   1.255 +                all_ws();
   1.256 +                if (!consume(OTHER, ",")) return ret;
   1.257 +                all_ws();
   1.258 +                var nxt = argument() || error("Trailing comma in arguments list");
   1.259 +                ret.push(nxt);
   1.260 +            }
   1.261 +        };
   1.262 +        
   1.263 +        var simple_extended_attr = function () {
   1.264 +            all_ws();
   1.265 +            var name = consume(ID);
   1.266 +            if (!name) return;
   1.267 +            var ret = {
   1.268 +                name: name.value
   1.269 +            ,   "arguments": null
   1.270 +            };
   1.271 +            all_ws();
   1.272 +            var eq = consume(OTHER, "=");
   1.273 +            if (eq) {
   1.274 +                all_ws();
   1.275 +                ret.rhs = consume(ID);
   1.276 +                if (!ret.rhs) return error("No right hand side to extended attribute assignment");
   1.277 +            }
   1.278 +            all_ws();
   1.279 +            if (consume(OTHER, "(")) {
   1.280 +                ret["arguments"] = argument_list();
   1.281 +                all_ws();
   1.282 +                consume(OTHER, ")") || error("Unclosed argument in extended attribute");
   1.283 +            }
   1.284 +            return ret;
   1.285 +        };
   1.286 +        
   1.287 +        // Note: we parse something simpler than the official syntax. It's all that ever
   1.288 +        // seems to be used
   1.289 +        var extended_attrs = function () {
   1.290 +            var eas = [];
   1.291 +            all_ws();
   1.292 +            if (!consume(OTHER, "[")) return eas;
   1.293 +            eas[0] = simple_extended_attr() || error("Extended attribute with not content");
   1.294 +            all_ws();
   1.295 +            while (consume(OTHER, ",")) {
   1.296 +                all_ws();
   1.297 +                eas.push(simple_extended_attr() || error("Trailing comma in extended attribute"));
   1.298 +                all_ws();
   1.299 +            }
   1.300 +            consume(OTHER, "]") || error("No end of extended attribute");
   1.301 +            return eas;
   1.302 +        };
   1.303 +        
   1.304 +        var default_ = function () {
   1.305 +            all_ws();
   1.306 +            if (consume(OTHER, "=")) {
   1.307 +                all_ws();
   1.308 +                var def = const_value();
   1.309 +                if (def) {
   1.310 +                    return def;
   1.311 +                }
   1.312 +                else {
   1.313 +                    var str = consume(STR) || error("No value for default");
   1.314 +                    str.value = str.value.replace(/^"/, "").replace(/"$/, "");
   1.315 +                    return str;
   1.316 +                }
   1.317 +            }
   1.318 +        };
   1.319 +        
   1.320 +        var const_ = function () {
   1.321 +            all_ws();
   1.322 +            if (!consume(ID, "const")) return;
   1.323 +            var ret = { type: "const", nullable: false };
   1.324 +            all_ws();
   1.325 +            var typ = primitive_type();
   1.326 +            if (!typ) {
   1.327 +                typ = consume(ID) || error("No type for const");
   1.328 +                typ = typ.value;
   1.329 +            }
   1.330 +            ret.idlType = typ;
   1.331 +            all_ws();
   1.332 +            if (consume(OTHER, "?")) {
   1.333 +                ret.nullable = true;
   1.334 +                all_ws();
   1.335 +            }
   1.336 +            var name = consume(ID) || error("No name for const");
   1.337 +            ret.name = name.value;
   1.338 +            all_ws();
   1.339 +            consume(OTHER, "=") || error("No value assignment for const");
   1.340 +            all_ws();
   1.341 +            var cnt = const_value();
   1.342 +            if (cnt) ret.value = cnt;
   1.343 +            else error("No value for const");
   1.344 +            all_ws();
   1.345 +            consume(OTHER, ";") || error("Unterminated const");
   1.346 +            return ret;
   1.347 +        };
   1.348 +        
   1.349 +        var inheritance = function () {
   1.350 +            all_ws();
   1.351 +            if (consume(OTHER, ":")) {
   1.352 +                all_ws();
   1.353 +                var inh = consume(ID) || error ("No type in inheritance");
   1.354 +                return inh.value;
   1.355 +            }
   1.356 +        };
   1.357 +        
   1.358 +        var operation_rest = function (ret) {
   1.359 +            all_ws();
   1.360 +            if (!ret) ret = {};
   1.361 +            var name = consume(ID);
   1.362 +            ret.name = name ? name.value : null;
   1.363 +            all_ws();
   1.364 +            consume(OTHER, "(") || error("Invalid operation");
   1.365 +            ret["arguments"] = argument_list();
   1.366 +            all_ws();
   1.367 +            consume(OTHER, ")") || error("Unterminated operation");
   1.368 +            all_ws();
   1.369 +            consume(OTHER, ";") || error("Unterminated operation");
   1.370 +            return ret;
   1.371 +        };
   1.372 +        
   1.373 +        var callback = function () {
   1.374 +            all_ws();
   1.375 +            var ret;
   1.376 +            if (!consume(ID, "callback")) return;
   1.377 +            all_ws();
   1.378 +            var tok = consume(ID, "interface");
   1.379 +            if (tok) {
   1.380 +                tokens.unshift(tok);
   1.381 +                ret = interface_();
   1.382 +                ret.type = "callback interface";
   1.383 +                return ret;
   1.384 +            }
   1.385 +            var name = consume(ID) || error("No name for callback");
   1.386 +            ret = { type: "callback", name: name.value };
   1.387 +            all_ws();
   1.388 +            consume(OTHER, "=") || error("No assignment in callback");
   1.389 +            all_ws();
   1.390 +            ret.idlType = return_type();
   1.391 +            all_ws();
   1.392 +            consume(OTHER, "(") || error("No arguments in callback");
   1.393 +            ret["arguments"] = argument_list();
   1.394 +            all_ws();
   1.395 +            consume(OTHER, ")") || error("Unterminated callback");
   1.396 +            all_ws();
   1.397 +            consume(OTHER, ";") || error("Unterminated callback");
   1.398 +            return ret;
   1.399 +        };
   1.400 +
   1.401 +        var attribute = function () {
   1.402 +            all_ws();
   1.403 +            var grabbed = []
   1.404 +            ,   ret = {
   1.405 +                type:           "attribute"
   1.406 +            ,   "static":       false
   1.407 +            ,   stringifier:    false
   1.408 +            ,   inherit:        false
   1.409 +            ,   readonly:       false
   1.410 +            };
   1.411 +            if (consume(ID, "static")) {
   1.412 +                ret["static"] = true;
   1.413 +                grabbed.push(last_token);
   1.414 +            }
   1.415 +            else if (consume(ID, "stringifier")) {
   1.416 +                ret.stringifier = true;
   1.417 +                grabbed.push(last_token);
   1.418 +            }
   1.419 +            var w = all_ws();
   1.420 +            if (w) grabbed.push(w);
   1.421 +            if (consume(ID, "inherit")) {
   1.422 +                if (ret["static"] || ret.stringifier) error("Cannot have a static or stringifier inherit");
   1.423 +                ret.inherit = true;
   1.424 +                grabbed.push(last_token);
   1.425 +                var w = all_ws();
   1.426 +                if (w) grabbed.push(w);
   1.427 +            }
   1.428 +            if (consume(ID, "readonly")) {
   1.429 +                ret.readonly = true;
   1.430 +                grabbed.push(last_token);
   1.431 +                var w = all_ws();
   1.432 +                if (w) grabbed.push(w);
   1.433 +            }
   1.434 +            if (!consume(ID, "attribute")) {
   1.435 +                tokens = grabbed.concat(tokens);
   1.436 +                return;
   1.437 +            }
   1.438 +            all_ws();
   1.439 +            ret.idlType = type() || error("No type in attribute");
   1.440 +            if (ret.idlType.sequence) error("Attributes cannot accept sequence types");
   1.441 +            all_ws();
   1.442 +            var name = consume(ID) || error("No name in attribute");
   1.443 +            ret.name = name.value;
   1.444 +            all_ws();
   1.445 +            consume(OTHER, ";") || error("Unterminated attribute");
   1.446 +            return ret;
   1.447 +        };
   1.448 +        
   1.449 +        var return_type = function () {
   1.450 +            var typ = type();
   1.451 +            if (!typ) {
   1.452 +                if (consume(ID, "void")) {
   1.453 +                    return "void";
   1.454 +                }
   1.455 +                else error("No return type");
   1.456 +            }
   1.457 +            return typ;
   1.458 +        };
   1.459 +        
   1.460 +        var operation = function () {
   1.461 +            all_ws();
   1.462 +            var ret = {
   1.463 +                type:           "operation"
   1.464 +            ,   getter:         false
   1.465 +            ,   setter:         false
   1.466 +            ,   creator:        false
   1.467 +            ,   deleter:        false
   1.468 +            ,   legacycaller:   false
   1.469 +            ,   "static":       false
   1.470 +            ,   stringifier:    false
   1.471 +            };
   1.472 +            while (true) {
   1.473 +                all_ws();
   1.474 +                if (consume(ID, "getter")) ret.getter = true;
   1.475 +                else if (consume(ID, "setter")) ret.setter = true;
   1.476 +                else if (consume(ID, "creator")) ret.creator = true;
   1.477 +                else if (consume(ID, "deleter")) ret.deleter = true;
   1.478 +                else if (consume(ID, "legacycaller")) ret.legacycaller = true;
   1.479 +                else break;
   1.480 +            }
   1.481 +            if (ret.getter || ret.setter || ret.creator || ret.deleter || ret.legacycaller) {
   1.482 +                all_ws();
   1.483 +                ret.idlType = return_type();
   1.484 +                operation_rest(ret);
   1.485 +                return ret;
   1.486 +            }
   1.487 +            if (consume(ID, "static")) {
   1.488 +                ret["static"] = true;
   1.489 +                ret.idlType = return_type();
   1.490 +                operation_rest(ret);
   1.491 +                return ret;
   1.492 +            }
   1.493 +            else if (consume(ID, "stringifier")) {
   1.494 +                ret.stringifier = true;
   1.495 +                all_ws();
   1.496 +                if (consume(OTHER, ";")) return ret;
   1.497 +                ret.idlType = return_type();
   1.498 +                operation_rest(ret);
   1.499 +                return ret;
   1.500 +            }
   1.501 +            ret.idlType = return_type();
   1.502 +            all_ws();
   1.503 +            if (consume(ID, "iterator")) {
   1.504 +                all_ws();
   1.505 +                ret.type = "iterator";
   1.506 +                if (consume(ID, "object")) {
   1.507 +                    ret.iteratorObject = "object";
   1.508 +                }
   1.509 +                else if (consume(OTHER, "=")) {
   1.510 +                    all_ws();
   1.511 +                    var name = consume(ID) || error("No right hand side in iterator");
   1.512 +                    ret.iteratorObject = name.value;
   1.513 +                }
   1.514 +                all_ws();
   1.515 +                consume(OTHER, ";") || error("Unterminated iterator");
   1.516 +                return ret;
   1.517 +            }
   1.518 +            else {
   1.519 +                operation_rest(ret);
   1.520 +                return ret;
   1.521 +            }
   1.522 +        };
   1.523 +        
   1.524 +        var identifiers = function (arr) {
   1.525 +            while (true) {
   1.526 +                all_ws();
   1.527 +                if (consume(OTHER, ",")) {
   1.528 +                    all_ws();
   1.529 +                    var name = consume(ID) || error("Trailing comma in identifiers list");
   1.530 +                    arr.push(name.value);
   1.531 +                }
   1.532 +                else break;
   1.533 +            }
   1.534 +        };
   1.535 +        
   1.536 +        var serialiser = function () {
   1.537 +            all_ws();
   1.538 +            if (!consume(ID, "serializer")) return;
   1.539 +            var ret = { type: "serializer" };
   1.540 +            all_ws();
   1.541 +            if (consume(OTHER, "=")) {
   1.542 +                all_ws();
   1.543 +                if (consume(OTHER, "{")) {
   1.544 +                    ret.patternMap = true;
   1.545 +                    all_ws();
   1.546 +                    var id = consume(ID);
   1.547 +                    if (id && id.value === "getter") {
   1.548 +                        ret.names = ["getter"];
   1.549 +                    }
   1.550 +                    else if (id && id.value === "inherit") {
   1.551 +                        ret.names = ["inherit"];
   1.552 +                        identifiers(ret.names);
   1.553 +                    }
   1.554 +                    else if (id) {
   1.555 +                        ret.names = [id.value];
   1.556 +                        identifiers(ret.names);
   1.557 +                    }
   1.558 +                    else {
   1.559 +                        ret.names = [];
   1.560 +                    }
   1.561 +                    all_ws();
   1.562 +                    consume(OTHER, "}") || error("Unterminated serializer pattern map");
   1.563 +                }
   1.564 +                else if (consume(OTHER, "[")) {
   1.565 +                    ret.patternList = true;
   1.566 +                    all_ws();
   1.567 +                    var id = consume(ID);
   1.568 +                    if (id && id.value === "getter") {
   1.569 +                        ret.names = ["getter"];
   1.570 +                    }
   1.571 +                    else if (id) {
   1.572 +                        ret.names = [id.value];
   1.573 +                        identifiers(ret.names);
   1.574 +                    }
   1.575 +                    else {
   1.576 +                        ret.names = [];
   1.577 +                    }
   1.578 +                    all_ws();
   1.579 +                    consume(OTHER, "]") || error("Unterminated serializer pattern list");
   1.580 +                }
   1.581 +                else {
   1.582 +                    var name = consume(ID) || error("Invalid serializer");
   1.583 +                    ret.name = name.value;
   1.584 +                }
   1.585 +                all_ws();
   1.586 +                consume(OTHER, ";") || error("Unterminated serializer");
   1.587 +                return ret;
   1.588 +            }
   1.589 +            else if (consume(OTHER, ";")) {
   1.590 +                // noop, just parsing
   1.591 +            }
   1.592 +            else {
   1.593 +                ret.idlType = return_type();
   1.594 +                all_ws();
   1.595 +                ret.operation = operation_rest();
   1.596 +            }
   1.597 +            return ret;
   1.598 +        };
   1.599 +        
   1.600 +        var interface_ = function (isPartial) {
   1.601 +            all_ws();
   1.602 +            if (!consume(ID, "interface")) return;
   1.603 +            all_ws();
   1.604 +            var name = consume(ID) || error("No name for interface");
   1.605 +            var ret = {
   1.606 +                type:   "interface"
   1.607 +            ,   name:   name.value
   1.608 +            ,   partial:    false
   1.609 +            ,   members:    []
   1.610 +            };
   1.611 +            if (!isPartial) ret.inheritance = inheritance() || null;
   1.612 +            all_ws();
   1.613 +            consume(OTHER, "{") || error("Bodyless interface");
   1.614 +            while (true) {
   1.615 +                all_ws();
   1.616 +                if (consume(OTHER, "}")) {
   1.617 +                    all_ws();
   1.618 +                    consume(OTHER, ";") || error("Missing semicolon after interface");
   1.619 +                    return ret;
   1.620 +                }
   1.621 +                var ea = extended_attrs();
   1.622 +                all_ws();
   1.623 +                var cnt = const_();
   1.624 +                if (cnt) {
   1.625 +                    cnt.extAttrs = ea;
   1.626 +                    ret.members.push(cnt);
   1.627 +                    continue;
   1.628 +                }
   1.629 +                var mem = serialiser() || attribute() || operation() || error("Unknown member");
   1.630 +                mem.extAttrs = ea;
   1.631 +                ret.members.push(mem);
   1.632 +            }
   1.633 +        };
   1.634 +        
   1.635 +        var partial = function () {
   1.636 +            all_ws();
   1.637 +            if (!consume(ID, "partial")) return;
   1.638 +            var thing = dictionary(true) || interface_(true) || error("Partial doesn't apply to anything");
   1.639 +            thing.partial = true;
   1.640 +            return thing;
   1.641 +        };
   1.642 +        
   1.643 +        var dictionary = function (isPartial) {
   1.644 +            all_ws();
   1.645 +            if (!consume(ID, "dictionary")) return;
   1.646 +            all_ws();
   1.647 +            var name = consume(ID) || error("No name for dictionary");
   1.648 +            var ret = {
   1.649 +                type:   "dictionary"
   1.650 +            ,   name:   name.value
   1.651 +            ,   partial:    false
   1.652 +            ,   members:    []
   1.653 +            };
   1.654 +            if (!isPartial) ret.inheritance = inheritance() || null;
   1.655 +            all_ws();
   1.656 +            consume(OTHER, "{") || error("Bodyless dictionary");
   1.657 +            while (true) {
   1.658 +                all_ws();
   1.659 +                if (consume(OTHER, "}")) {
   1.660 +                    all_ws();
   1.661 +                    consume(OTHER, ";") || error("Missing semicolon after dictionary");
   1.662 +                    return ret;
   1.663 +                }
   1.664 +                var ea = extended_attrs();
   1.665 +                all_ws();
   1.666 +                var typ = type() || error("No type for dictionary member");
   1.667 +                all_ws();
   1.668 +                var name = consume(ID) || error("No name for dictionary member");
   1.669 +                ret.members.push({
   1.670 +                    type:       "field"
   1.671 +                ,   name:       name.value
   1.672 +                ,   idlType:    typ
   1.673 +                ,   extAttrs:   ea
   1.674 +                ,   "default":  default_()
   1.675 +                });
   1.676 +                all_ws();
   1.677 +                consume(OTHER, ";") || error("Unterminated dictionary member");
   1.678 +            }
   1.679 +        };
   1.680 +        
   1.681 +        var exception = function () {
   1.682 +            all_ws();
   1.683 +            if (!consume(ID, "exception")) return;
   1.684 +            all_ws();
   1.685 +            var name = consume(ID) || error("No name for exception");
   1.686 +            var ret = {
   1.687 +                type:   "exception"
   1.688 +            ,   name:   name.value
   1.689 +            ,   members:    []
   1.690 +            };
   1.691 +            ret.inheritance = inheritance() || null;
   1.692 +            all_ws();
   1.693 +            consume(OTHER, "{") || error("Bodyless exception");
   1.694 +            while (true) {
   1.695 +                all_ws();
   1.696 +                if (consume(OTHER, "}")) {
   1.697 +                    all_ws();
   1.698 +                    consume(OTHER, ";") || error("Missing semicolon after exception");
   1.699 +                    return ret;
   1.700 +                }
   1.701 +                var ea = extended_attrs();
   1.702 +                all_ws();
   1.703 +                var cnt = const_();
   1.704 +                if (cnt) {
   1.705 +                    cnt.extAttrs = ea;
   1.706 +                    ret.members.push(cnt);
   1.707 +                }
   1.708 +                else {
   1.709 +                    var typ = type();
   1.710 +                    all_ws();
   1.711 +                    var name = consume(ID);
   1.712 +                    all_ws();
   1.713 +                    if (!typ || !name || !consume(OTHER, ";")) error("Unknown member in exception body");
   1.714 +                    ret.members.push({
   1.715 +                        type:       "field"
   1.716 +                    ,   name:       name.value
   1.717 +                    ,   idlType:    typ
   1.718 +                    ,   extAttrs:   ea
   1.719 +                    });
   1.720 +                }
   1.721 +            }
   1.722 +        };
   1.723 +        
   1.724 +        var enum_ = function () {
   1.725 +            all_ws();
   1.726 +            if (!consume(ID, "enum")) return;
   1.727 +            all_ws();
   1.728 +            var name = consume(ID) || error("No name for enum");
   1.729 +            var ret = {
   1.730 +                type:   "enum"
   1.731 +            ,   name:   name.value
   1.732 +            ,   values: []
   1.733 +            };
   1.734 +            all_ws();
   1.735 +            consume(OTHER, "{") || error("No curly for enum");
   1.736 +            var saw_comma = false;
   1.737 +            while (true) {
   1.738 +                all_ws();
   1.739 +                if (consume(OTHER, "}")) {
   1.740 +                    all_ws();
   1.741 +                    if (saw_comma) error("Trailing comma in enum");
   1.742 +                    consume(OTHER, ";") || error("No semicolon after enum");
   1.743 +                    return ret;
   1.744 +                }
   1.745 +                var val = consume(STR) || error("Unexpected value in enum");
   1.746 +                ret.values.push(val.value.replace(/"/g, ""));
   1.747 +                all_ws();
   1.748 +                if (consume(OTHER, ",")) {
   1.749 +                    all_ws();
   1.750 +                    saw_comma = true;
   1.751 +                }
   1.752 +                else {
   1.753 +                    saw_comma = false;
   1.754 +                }
   1.755 +            }
   1.756 +        };
   1.757 +        
   1.758 +        var typedef = function () {
   1.759 +            all_ws();
   1.760 +            if (!consume(ID, "typedef")) return;
   1.761 +            var ret = {
   1.762 +                type:   "typedef"
   1.763 +            };
   1.764 +            all_ws();
   1.765 +            ret.typeExtAttrs = extended_attrs();
   1.766 +            all_ws();
   1.767 +            ret.idlType = type() || error("No type in typedef");
   1.768 +            all_ws();
   1.769 +            var name = consume(ID) || error("No name in typedef");
   1.770 +            ret.name = name.value;
   1.771 +            all_ws();
   1.772 +            consume(OTHER, ";") || error("Unterminated typedef");
   1.773 +            return ret;
   1.774 +        };
   1.775 +        
   1.776 +        var implements_ = function () {
   1.777 +            all_ws();
   1.778 +            var target = consume(ID);
   1.779 +            if (!target) return;
   1.780 +            var w = all_ws();
   1.781 +            if (consume(ID, "implements")) {
   1.782 +                var ret = {
   1.783 +                    type:   "implements"
   1.784 +                ,   target: target.value
   1.785 +                };
   1.786 +                all_ws();
   1.787 +                var imp = consume(ID) || error("Incomplete implements statement");
   1.788 +                ret["implements"] = imp.value;
   1.789 +                all_ws();
   1.790 +                consume(OTHER, ";") || error("No terminating ; for implements statement");
   1.791 +                return ret;
   1.792 +            }
   1.793 +            else {
   1.794 +                // rollback
   1.795 +                tokens.unshift(w);
   1.796 +                tokens.unshift(target);
   1.797 +            }
   1.798 +        };
   1.799 +        
   1.800 +        var definition = function () {
   1.801 +            return  callback()      ||
   1.802 +                    interface_()    ||
   1.803 +                    partial()       ||
   1.804 +                    dictionary()    ||
   1.805 +                    exception()     ||
   1.806 +                    enum_()         ||
   1.807 +                    typedef()       ||
   1.808 +                    implements_()
   1.809 +                    ;
   1.810 +        };
   1.811 +        
   1.812 +        var definitions = function () {
   1.813 +            if (!tokens.length) return [];
   1.814 +            var defs = [];
   1.815 +            while (true) {
   1.816 +                var ea = extended_attrs()
   1.817 +                ,   def = definition();
   1.818 +                if (!def) {
   1.819 +                    if (ea.length) error("Stray extended attributes");
   1.820 +                    break;
   1.821 +                }
   1.822 +                def.extAttrs = ea;
   1.823 +                defs.push(def);
   1.824 +            }
   1.825 +            return defs;
   1.826 +        };
   1.827 +        var res = definitions();
   1.828 +        if (tokens.length) error("Unrecognised tokens");
   1.829 +        return res;
   1.830 +    };
   1.831 +
   1.832 +    var obj = {
   1.833 +        parse:  function (str) {
   1.834 +            var tokens = tokenise(str);
   1.835 +            return parse(tokens);
   1.836 +        }
   1.837 +    };
   1.838 +    if (typeof module !== "undefined" && module.exports) {
   1.839 +        module.exports = obj;
   1.840 +    }
   1.841 +    else {
   1.842 +        window.WebIDL2 = obj;
   1.843 +    }
   1.844 +}());

mercurial