dom/imptests/WebIDLParser.js

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1
michael@0 2
michael@0 3 (function () {
michael@0 4 var tokenise = function (str) {
michael@0 5 var tokens = []
michael@0 6 , re = {
michael@0 7 "float": /^-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][-+]?[0-9]+)?|[0-9]+[Ee][-+]?[0-9]+)/
michael@0 8 , "integer": /^-?(0([Xx][0-9A-Fa-f]+|[0-7]*)|[1-9][0-9]*)/
michael@0 9 , "identifier": /^[A-Z_a-z][0-9A-Z_a-z]*/
michael@0 10 , "string": /^"[^"]*"/
michael@0 11 , "whitespace": /^(?:[\t\n\r ]+|[\t\n\r ]*((\/\/.*|\/\*(.|\n|\r)*?\*\/)[\t\n\r ]*))+/
michael@0 12 , "other": /^[^\t\n\r 0-9A-Z_a-z]/
michael@0 13 }
michael@0 14 , types = []
michael@0 15 ;
michael@0 16 for (var k in re) types.push(k);
michael@0 17 while (str.length > 0) {
michael@0 18 var matched = false;
michael@0 19 for (var i = 0, n = types.length; i < n; i++) {
michael@0 20 var type = types[i];
michael@0 21 str = str.replace(re[type], function (tok) {
michael@0 22 tokens.push({ type: type, value: tok });
michael@0 23 matched = true;
michael@0 24 return "";
michael@0 25 });
michael@0 26 if (matched) break;
michael@0 27 }
michael@0 28 if (matched) continue;
michael@0 29 throw new Error("Token stream not progressing");
michael@0 30 }
michael@0 31 return tokens;
michael@0 32 };
michael@0 33
michael@0 34 var parse = function (tokens) {
michael@0 35 var line = 1;
michael@0 36 tokens = tokens.slice();
michael@0 37
michael@0 38 var FLOAT = "float"
michael@0 39 , INT = "integer"
michael@0 40 , ID = "identifier"
michael@0 41 , STR = "string"
michael@0 42 , OTHER = "other"
michael@0 43 ;
michael@0 44
michael@0 45 var WebIDLParseError = function (str, line, input, tokens) {
michael@0 46 this.message = str;
michael@0 47 this.line = line;
michael@0 48 this.input = input;
michael@0 49 this.tokens = tokens;
michael@0 50 };
michael@0 51 WebIDLParseError.prototype.toString = function () {
michael@0 52 return this.message + ", line " + this.line + " (tokens: '" + this.input + "')\n" +
michael@0 53 JSON.stringify(this.tokens, null, 4);
michael@0 54 };
michael@0 55
michael@0 56 var error = function (str) {
michael@0 57 var tok = "", numTokens = 0, maxTokens = 5;
michael@0 58 while (numTokens < maxTokens && tokens.length > numTokens) {
michael@0 59 tok += tokens[numTokens].value;
michael@0 60 numTokens++;
michael@0 61 }
michael@0 62 throw new WebIDLParseError(str, line, tok, tokens.slice(0, 5));
michael@0 63 };
michael@0 64
michael@0 65 var last_token = null;
michael@0 66
michael@0 67 var consume = function (type, value) {
michael@0 68 if (!tokens.length || tokens[0].type !== type) return;
michael@0 69 if (typeof value === "undefined" || tokens[0].value === value) {
michael@0 70 last_token = tokens.shift();
michael@0 71 if (type === ID) last_token.value = last_token.value.replace(/^_/, "");
michael@0 72 return last_token;
michael@0 73 }
michael@0 74 };
michael@0 75
michael@0 76 var ws = function () {
michael@0 77 if (!tokens.length) return;
michael@0 78 if (tokens[0].type === "whitespace") {
michael@0 79 var t = tokens.shift();
michael@0 80 t.value.replace(/\n/g, function (m) { line++; return m; });
michael@0 81 return t;
michael@0 82 }
michael@0 83 };
michael@0 84
michael@0 85 var all_ws = function () {
michael@0 86 var t = { type: "whitespace", value: "" };
michael@0 87 while (true) {
michael@0 88 var w = ws();
michael@0 89 if (!w) break;
michael@0 90 t.value += w.value;
michael@0 91 }
michael@0 92 if (t.value.length > 0) return t;
michael@0 93 };
michael@0 94
michael@0 95 var integer_type = function () {
michael@0 96 var ret = "";
michael@0 97 all_ws();
michael@0 98 if (consume(ID, "unsigned")) ret = "unsigned ";
michael@0 99 all_ws();
michael@0 100 if (consume(ID, "short")) return ret + "short";
michael@0 101 if (consume(ID, "long")) {
michael@0 102 ret += "long";
michael@0 103 all_ws();
michael@0 104 if (consume(ID, "long")) return ret + " long";
michael@0 105 return ret;
michael@0 106 }
michael@0 107 if (ret) error("Failed to parse integer type");
michael@0 108 };
michael@0 109
michael@0 110 var float_type = function () {
michael@0 111 var ret = "";
michael@0 112 all_ws();
michael@0 113 if (consume(ID, "unrestricted")) ret = "unrestricted ";
michael@0 114 all_ws();
michael@0 115 if (consume(ID, "float")) return ret + "float";
michael@0 116 if (consume(ID, "double")) return ret + "double";
michael@0 117 if (ret) error("Failed to parse float type");
michael@0 118 };
michael@0 119
michael@0 120 var primitive_type = function () {
michael@0 121 var num_type = integer_type() || float_type();
michael@0 122 if (num_type) return num_type;
michael@0 123 all_ws();
michael@0 124 if (consume(ID, "boolean")) return "boolean";
michael@0 125 if (consume(ID, "byte")) return "byte";
michael@0 126 if (consume(ID, "octet")) return "octet";
michael@0 127 };
michael@0 128
michael@0 129 var const_value = function () {
michael@0 130 if (consume(ID, "true")) return { type: "boolean", value: true };
michael@0 131 if (consume(ID, "false")) return { type: "boolean", value: false };
michael@0 132 if (consume(ID, "null")) return { type: "null" };
michael@0 133 if (consume(ID, "Infinity")) return { type: "Infinity", negative: false };
michael@0 134 if (consume(ID, "NaN")) return { type: "NaN" };
michael@0 135 var ret = consume(FLOAT) || consume(INT);
michael@0 136 if (ret) return { type: "number", value: 1 * ret.value };
michael@0 137 var tok = consume(OTHER, "-");
michael@0 138 if (tok) {
michael@0 139 if (consume(ID, "Infinity")) return { type: "Infinity", negative: true };
michael@0 140 else tokens.unshift(tok);
michael@0 141 }
michael@0 142 };
michael@0 143
michael@0 144 var type_suffix = function (obj) {
michael@0 145 while (true) {
michael@0 146 all_ws();
michael@0 147 if (consume(OTHER, "?")) {
michael@0 148 if (obj.nullable) error("Can't nullable more than once");
michael@0 149 obj.nullable = true;
michael@0 150 }
michael@0 151 else if (consume(OTHER, "[")) {
michael@0 152 all_ws();
michael@0 153 consume(OTHER, "]") || error("Unterminated array type");
michael@0 154 if (!obj.array) obj.array = 1;
michael@0 155 else obj.array++;
michael@0 156 }
michael@0 157 else return;
michael@0 158 }
michael@0 159 };
michael@0 160
michael@0 161 var single_type = function () {
michael@0 162 var prim = primitive_type()
michael@0 163 , ret = { sequence: false, nullable: false, array: false, union: false }
michael@0 164 ;
michael@0 165 if (prim) {
michael@0 166 ret.idlType = prim;
michael@0 167 }
michael@0 168 else if (consume(ID, "sequence")) {
michael@0 169 all_ws();
michael@0 170 if (!consume(OTHER, "<")) {
michael@0 171 ret.idlType = "sequence";
michael@0 172 }
michael@0 173 else {
michael@0 174 ret.sequence = true;
michael@0 175 ret.idlType = type() || error("Error parsing sequence type");
michael@0 176 all_ws();
michael@0 177 if (!consume(OTHER, ">")) error("Unterminated sequence");
michael@0 178 all_ws();
michael@0 179 if (consume(OTHER, "?")) ret.nullable = true;
michael@0 180 return ret;
michael@0 181 }
michael@0 182 }
michael@0 183 else {
michael@0 184 var name = consume(ID);
michael@0 185 if (!name) return;
michael@0 186 ret.idlType = name.value;
michael@0 187 }
michael@0 188 type_suffix(ret);
michael@0 189 if (ret.nullable && ret.idlType === "any") error("Type any cannot be made nullable");
michael@0 190 return ret;
michael@0 191 };
michael@0 192
michael@0 193 var union_type = function () {
michael@0 194 all_ws();
michael@0 195 if (!consume(OTHER, "(")) return;
michael@0 196 var ret = { sequence: false, nullable: false, array: false, union: true, idlType: [] };
michael@0 197 var fst = type() || error("Union type with no content");
michael@0 198 ret.idlType.push(fst);
michael@0 199 while (true) {
michael@0 200 all_ws();
michael@0 201 if (!consume(ID, "or")) break;
michael@0 202 var typ = type() || error("No type after 'or' in union type");
michael@0 203 ret.idlType.push(typ);
michael@0 204 }
michael@0 205 if (!consume(OTHER, ")")) error("Unterminated union type");
michael@0 206 type_suffix(ret);
michael@0 207 return ret;
michael@0 208 };
michael@0 209
michael@0 210 var type = function () {
michael@0 211 return single_type() || union_type();
michael@0 212 };
michael@0 213
michael@0 214 var argument = function () {
michael@0 215 var ret = { optional: false, variadic: false };
michael@0 216 ret.extAttrs = extended_attrs();
michael@0 217 all_ws();
michael@0 218 if (consume(ID, "optional")) {
michael@0 219 ret.optional = true;
michael@0 220 all_ws();
michael@0 221 }
michael@0 222 ret.idlType = type();
michael@0 223 if (!ret.idlType) return;
michael@0 224 if (!ret.optional) {
michael@0 225 all_ws();
michael@0 226 if (tokens.length >= 3 &&
michael@0 227 tokens[0].type === "other" && tokens[0].value === "." &&
michael@0 228 tokens[1].type === "other" && tokens[1].value === "." &&
michael@0 229 tokens[2].type === "other" && tokens[2].value === "."
michael@0 230 ) {
michael@0 231 tokens.shift();
michael@0 232 tokens.shift();
michael@0 233 tokens.shift();
michael@0 234 ret.variadic = true;
michael@0 235 }
michael@0 236 }
michael@0 237 all_ws();
michael@0 238 var name = consume(ID) || error("No name in argument");
michael@0 239 ret.name = name.value;
michael@0 240 if (ret.optional) {
michael@0 241 all_ws();
michael@0 242 ret["default"] = default_();
michael@0 243 }
michael@0 244 return ret;
michael@0 245 };
michael@0 246
michael@0 247 var argument_list = function () {
michael@0 248 var arg = argument(), ret = [];
michael@0 249 if (!arg) return ret;
michael@0 250 ret.push(arg);
michael@0 251 while (true) {
michael@0 252 all_ws();
michael@0 253 if (!consume(OTHER, ",")) return ret;
michael@0 254 all_ws();
michael@0 255 var nxt = argument() || error("Trailing comma in arguments list");
michael@0 256 ret.push(nxt);
michael@0 257 }
michael@0 258 };
michael@0 259
michael@0 260 var simple_extended_attr = function () {
michael@0 261 all_ws();
michael@0 262 var name = consume(ID);
michael@0 263 if (!name) return;
michael@0 264 var ret = {
michael@0 265 name: name.value
michael@0 266 , "arguments": null
michael@0 267 };
michael@0 268 all_ws();
michael@0 269 var eq = consume(OTHER, "=");
michael@0 270 if (eq) {
michael@0 271 all_ws();
michael@0 272 ret.rhs = consume(ID);
michael@0 273 if (!ret.rhs) return error("No right hand side to extended attribute assignment");
michael@0 274 }
michael@0 275 all_ws();
michael@0 276 if (consume(OTHER, "(")) {
michael@0 277 ret["arguments"] = argument_list();
michael@0 278 all_ws();
michael@0 279 consume(OTHER, ")") || error("Unclosed argument in extended attribute");
michael@0 280 }
michael@0 281 return ret;
michael@0 282 };
michael@0 283
michael@0 284 // Note: we parse something simpler than the official syntax. It's all that ever
michael@0 285 // seems to be used
michael@0 286 var extended_attrs = function () {
michael@0 287 var eas = [];
michael@0 288 all_ws();
michael@0 289 if (!consume(OTHER, "[")) return eas;
michael@0 290 eas[0] = simple_extended_attr() || error("Extended attribute with not content");
michael@0 291 all_ws();
michael@0 292 while (consume(OTHER, ",")) {
michael@0 293 all_ws();
michael@0 294 eas.push(simple_extended_attr() || error("Trailing comma in extended attribute"));
michael@0 295 all_ws();
michael@0 296 }
michael@0 297 consume(OTHER, "]") || error("No end of extended attribute");
michael@0 298 return eas;
michael@0 299 };
michael@0 300
michael@0 301 var default_ = function () {
michael@0 302 all_ws();
michael@0 303 if (consume(OTHER, "=")) {
michael@0 304 all_ws();
michael@0 305 var def = const_value();
michael@0 306 if (def) {
michael@0 307 return def;
michael@0 308 }
michael@0 309 else {
michael@0 310 var str = consume(STR) || error("No value for default");
michael@0 311 str.value = str.value.replace(/^"/, "").replace(/"$/, "");
michael@0 312 return str;
michael@0 313 }
michael@0 314 }
michael@0 315 };
michael@0 316
michael@0 317 var const_ = function () {
michael@0 318 all_ws();
michael@0 319 if (!consume(ID, "const")) return;
michael@0 320 var ret = { type: "const", nullable: false };
michael@0 321 all_ws();
michael@0 322 var typ = primitive_type();
michael@0 323 if (!typ) {
michael@0 324 typ = consume(ID) || error("No type for const");
michael@0 325 typ = typ.value;
michael@0 326 }
michael@0 327 ret.idlType = typ;
michael@0 328 all_ws();
michael@0 329 if (consume(OTHER, "?")) {
michael@0 330 ret.nullable = true;
michael@0 331 all_ws();
michael@0 332 }
michael@0 333 var name = consume(ID) || error("No name for const");
michael@0 334 ret.name = name.value;
michael@0 335 all_ws();
michael@0 336 consume(OTHER, "=") || error("No value assignment for const");
michael@0 337 all_ws();
michael@0 338 var cnt = const_value();
michael@0 339 if (cnt) ret.value = cnt;
michael@0 340 else error("No value for const");
michael@0 341 all_ws();
michael@0 342 consume(OTHER, ";") || error("Unterminated const");
michael@0 343 return ret;
michael@0 344 };
michael@0 345
michael@0 346 var inheritance = function () {
michael@0 347 all_ws();
michael@0 348 if (consume(OTHER, ":")) {
michael@0 349 all_ws();
michael@0 350 var inh = consume(ID) || error ("No type in inheritance");
michael@0 351 return inh.value;
michael@0 352 }
michael@0 353 };
michael@0 354
michael@0 355 var operation_rest = function (ret) {
michael@0 356 all_ws();
michael@0 357 if (!ret) ret = {};
michael@0 358 var name = consume(ID);
michael@0 359 ret.name = name ? name.value : null;
michael@0 360 all_ws();
michael@0 361 consume(OTHER, "(") || error("Invalid operation");
michael@0 362 ret["arguments"] = argument_list();
michael@0 363 all_ws();
michael@0 364 consume(OTHER, ")") || error("Unterminated operation");
michael@0 365 all_ws();
michael@0 366 consume(OTHER, ";") || error("Unterminated operation");
michael@0 367 return ret;
michael@0 368 };
michael@0 369
michael@0 370 var callback = function () {
michael@0 371 all_ws();
michael@0 372 var ret;
michael@0 373 if (!consume(ID, "callback")) return;
michael@0 374 all_ws();
michael@0 375 var tok = consume(ID, "interface");
michael@0 376 if (tok) {
michael@0 377 tokens.unshift(tok);
michael@0 378 ret = interface_();
michael@0 379 ret.type = "callback interface";
michael@0 380 return ret;
michael@0 381 }
michael@0 382 var name = consume(ID) || error("No name for callback");
michael@0 383 ret = { type: "callback", name: name.value };
michael@0 384 all_ws();
michael@0 385 consume(OTHER, "=") || error("No assignment in callback");
michael@0 386 all_ws();
michael@0 387 ret.idlType = return_type();
michael@0 388 all_ws();
michael@0 389 consume(OTHER, "(") || error("No arguments in callback");
michael@0 390 ret["arguments"] = argument_list();
michael@0 391 all_ws();
michael@0 392 consume(OTHER, ")") || error("Unterminated callback");
michael@0 393 all_ws();
michael@0 394 consume(OTHER, ";") || error("Unterminated callback");
michael@0 395 return ret;
michael@0 396 };
michael@0 397
michael@0 398 var attribute = function () {
michael@0 399 all_ws();
michael@0 400 var grabbed = []
michael@0 401 , ret = {
michael@0 402 type: "attribute"
michael@0 403 , "static": false
michael@0 404 , stringifier: false
michael@0 405 , inherit: false
michael@0 406 , readonly: false
michael@0 407 };
michael@0 408 if (consume(ID, "static")) {
michael@0 409 ret["static"] = true;
michael@0 410 grabbed.push(last_token);
michael@0 411 }
michael@0 412 else if (consume(ID, "stringifier")) {
michael@0 413 ret.stringifier = true;
michael@0 414 grabbed.push(last_token);
michael@0 415 }
michael@0 416 var w = all_ws();
michael@0 417 if (w) grabbed.push(w);
michael@0 418 if (consume(ID, "inherit")) {
michael@0 419 if (ret["static"] || ret.stringifier) error("Cannot have a static or stringifier inherit");
michael@0 420 ret.inherit = true;
michael@0 421 grabbed.push(last_token);
michael@0 422 var w = all_ws();
michael@0 423 if (w) grabbed.push(w);
michael@0 424 }
michael@0 425 if (consume(ID, "readonly")) {
michael@0 426 ret.readonly = true;
michael@0 427 grabbed.push(last_token);
michael@0 428 var w = all_ws();
michael@0 429 if (w) grabbed.push(w);
michael@0 430 }
michael@0 431 if (!consume(ID, "attribute")) {
michael@0 432 tokens = grabbed.concat(tokens);
michael@0 433 return;
michael@0 434 }
michael@0 435 all_ws();
michael@0 436 ret.idlType = type() || error("No type in attribute");
michael@0 437 if (ret.idlType.sequence) error("Attributes cannot accept sequence types");
michael@0 438 all_ws();
michael@0 439 var name = consume(ID) || error("No name in attribute");
michael@0 440 ret.name = name.value;
michael@0 441 all_ws();
michael@0 442 consume(OTHER, ";") || error("Unterminated attribute");
michael@0 443 return ret;
michael@0 444 };
michael@0 445
michael@0 446 var return_type = function () {
michael@0 447 var typ = type();
michael@0 448 if (!typ) {
michael@0 449 if (consume(ID, "void")) {
michael@0 450 return "void";
michael@0 451 }
michael@0 452 else error("No return type");
michael@0 453 }
michael@0 454 return typ;
michael@0 455 };
michael@0 456
michael@0 457 var operation = function () {
michael@0 458 all_ws();
michael@0 459 var ret = {
michael@0 460 type: "operation"
michael@0 461 , getter: false
michael@0 462 , setter: false
michael@0 463 , creator: false
michael@0 464 , deleter: false
michael@0 465 , legacycaller: false
michael@0 466 , "static": false
michael@0 467 , stringifier: false
michael@0 468 };
michael@0 469 while (true) {
michael@0 470 all_ws();
michael@0 471 if (consume(ID, "getter")) ret.getter = true;
michael@0 472 else if (consume(ID, "setter")) ret.setter = true;
michael@0 473 else if (consume(ID, "creator")) ret.creator = true;
michael@0 474 else if (consume(ID, "deleter")) ret.deleter = true;
michael@0 475 else if (consume(ID, "legacycaller")) ret.legacycaller = true;
michael@0 476 else break;
michael@0 477 }
michael@0 478 if (ret.getter || ret.setter || ret.creator || ret.deleter || ret.legacycaller) {
michael@0 479 all_ws();
michael@0 480 ret.idlType = return_type();
michael@0 481 operation_rest(ret);
michael@0 482 return ret;
michael@0 483 }
michael@0 484 if (consume(ID, "static")) {
michael@0 485 ret["static"] = true;
michael@0 486 ret.idlType = return_type();
michael@0 487 operation_rest(ret);
michael@0 488 return ret;
michael@0 489 }
michael@0 490 else if (consume(ID, "stringifier")) {
michael@0 491 ret.stringifier = true;
michael@0 492 all_ws();
michael@0 493 if (consume(OTHER, ";")) return ret;
michael@0 494 ret.idlType = return_type();
michael@0 495 operation_rest(ret);
michael@0 496 return ret;
michael@0 497 }
michael@0 498 ret.idlType = return_type();
michael@0 499 all_ws();
michael@0 500 if (consume(ID, "iterator")) {
michael@0 501 all_ws();
michael@0 502 ret.type = "iterator";
michael@0 503 if (consume(ID, "object")) {
michael@0 504 ret.iteratorObject = "object";
michael@0 505 }
michael@0 506 else if (consume(OTHER, "=")) {
michael@0 507 all_ws();
michael@0 508 var name = consume(ID) || error("No right hand side in iterator");
michael@0 509 ret.iteratorObject = name.value;
michael@0 510 }
michael@0 511 all_ws();
michael@0 512 consume(OTHER, ";") || error("Unterminated iterator");
michael@0 513 return ret;
michael@0 514 }
michael@0 515 else {
michael@0 516 operation_rest(ret);
michael@0 517 return ret;
michael@0 518 }
michael@0 519 };
michael@0 520
michael@0 521 var identifiers = function (arr) {
michael@0 522 while (true) {
michael@0 523 all_ws();
michael@0 524 if (consume(OTHER, ",")) {
michael@0 525 all_ws();
michael@0 526 var name = consume(ID) || error("Trailing comma in identifiers list");
michael@0 527 arr.push(name.value);
michael@0 528 }
michael@0 529 else break;
michael@0 530 }
michael@0 531 };
michael@0 532
michael@0 533 var serialiser = function () {
michael@0 534 all_ws();
michael@0 535 if (!consume(ID, "serializer")) return;
michael@0 536 var ret = { type: "serializer" };
michael@0 537 all_ws();
michael@0 538 if (consume(OTHER, "=")) {
michael@0 539 all_ws();
michael@0 540 if (consume(OTHER, "{")) {
michael@0 541 ret.patternMap = true;
michael@0 542 all_ws();
michael@0 543 var id = consume(ID);
michael@0 544 if (id && id.value === "getter") {
michael@0 545 ret.names = ["getter"];
michael@0 546 }
michael@0 547 else if (id && id.value === "inherit") {
michael@0 548 ret.names = ["inherit"];
michael@0 549 identifiers(ret.names);
michael@0 550 }
michael@0 551 else if (id) {
michael@0 552 ret.names = [id.value];
michael@0 553 identifiers(ret.names);
michael@0 554 }
michael@0 555 else {
michael@0 556 ret.names = [];
michael@0 557 }
michael@0 558 all_ws();
michael@0 559 consume(OTHER, "}") || error("Unterminated serializer pattern map");
michael@0 560 }
michael@0 561 else if (consume(OTHER, "[")) {
michael@0 562 ret.patternList = true;
michael@0 563 all_ws();
michael@0 564 var id = consume(ID);
michael@0 565 if (id && id.value === "getter") {
michael@0 566 ret.names = ["getter"];
michael@0 567 }
michael@0 568 else if (id) {
michael@0 569 ret.names = [id.value];
michael@0 570 identifiers(ret.names);
michael@0 571 }
michael@0 572 else {
michael@0 573 ret.names = [];
michael@0 574 }
michael@0 575 all_ws();
michael@0 576 consume(OTHER, "]") || error("Unterminated serializer pattern list");
michael@0 577 }
michael@0 578 else {
michael@0 579 var name = consume(ID) || error("Invalid serializer");
michael@0 580 ret.name = name.value;
michael@0 581 }
michael@0 582 all_ws();
michael@0 583 consume(OTHER, ";") || error("Unterminated serializer");
michael@0 584 return ret;
michael@0 585 }
michael@0 586 else if (consume(OTHER, ";")) {
michael@0 587 // noop, just parsing
michael@0 588 }
michael@0 589 else {
michael@0 590 ret.idlType = return_type();
michael@0 591 all_ws();
michael@0 592 ret.operation = operation_rest();
michael@0 593 }
michael@0 594 return ret;
michael@0 595 };
michael@0 596
michael@0 597 var interface_ = function (isPartial) {
michael@0 598 all_ws();
michael@0 599 if (!consume(ID, "interface")) return;
michael@0 600 all_ws();
michael@0 601 var name = consume(ID) || error("No name for interface");
michael@0 602 var ret = {
michael@0 603 type: "interface"
michael@0 604 , name: name.value
michael@0 605 , partial: false
michael@0 606 , members: []
michael@0 607 };
michael@0 608 if (!isPartial) ret.inheritance = inheritance() || null;
michael@0 609 all_ws();
michael@0 610 consume(OTHER, "{") || error("Bodyless interface");
michael@0 611 while (true) {
michael@0 612 all_ws();
michael@0 613 if (consume(OTHER, "}")) {
michael@0 614 all_ws();
michael@0 615 consume(OTHER, ";") || error("Missing semicolon after interface");
michael@0 616 return ret;
michael@0 617 }
michael@0 618 var ea = extended_attrs();
michael@0 619 all_ws();
michael@0 620 var cnt = const_();
michael@0 621 if (cnt) {
michael@0 622 cnt.extAttrs = ea;
michael@0 623 ret.members.push(cnt);
michael@0 624 continue;
michael@0 625 }
michael@0 626 var mem = serialiser() || attribute() || operation() || error("Unknown member");
michael@0 627 mem.extAttrs = ea;
michael@0 628 ret.members.push(mem);
michael@0 629 }
michael@0 630 };
michael@0 631
michael@0 632 var partial = function () {
michael@0 633 all_ws();
michael@0 634 if (!consume(ID, "partial")) return;
michael@0 635 var thing = dictionary(true) || interface_(true) || error("Partial doesn't apply to anything");
michael@0 636 thing.partial = true;
michael@0 637 return thing;
michael@0 638 };
michael@0 639
michael@0 640 var dictionary = function (isPartial) {
michael@0 641 all_ws();
michael@0 642 if (!consume(ID, "dictionary")) return;
michael@0 643 all_ws();
michael@0 644 var name = consume(ID) || error("No name for dictionary");
michael@0 645 var ret = {
michael@0 646 type: "dictionary"
michael@0 647 , name: name.value
michael@0 648 , partial: false
michael@0 649 , members: []
michael@0 650 };
michael@0 651 if (!isPartial) ret.inheritance = inheritance() || null;
michael@0 652 all_ws();
michael@0 653 consume(OTHER, "{") || error("Bodyless dictionary");
michael@0 654 while (true) {
michael@0 655 all_ws();
michael@0 656 if (consume(OTHER, "}")) {
michael@0 657 all_ws();
michael@0 658 consume(OTHER, ";") || error("Missing semicolon after dictionary");
michael@0 659 return ret;
michael@0 660 }
michael@0 661 var ea = extended_attrs();
michael@0 662 all_ws();
michael@0 663 var typ = type() || error("No type for dictionary member");
michael@0 664 all_ws();
michael@0 665 var name = consume(ID) || error("No name for dictionary member");
michael@0 666 ret.members.push({
michael@0 667 type: "field"
michael@0 668 , name: name.value
michael@0 669 , idlType: typ
michael@0 670 , extAttrs: ea
michael@0 671 , "default": default_()
michael@0 672 });
michael@0 673 all_ws();
michael@0 674 consume(OTHER, ";") || error("Unterminated dictionary member");
michael@0 675 }
michael@0 676 };
michael@0 677
michael@0 678 var exception = function () {
michael@0 679 all_ws();
michael@0 680 if (!consume(ID, "exception")) return;
michael@0 681 all_ws();
michael@0 682 var name = consume(ID) || error("No name for exception");
michael@0 683 var ret = {
michael@0 684 type: "exception"
michael@0 685 , name: name.value
michael@0 686 , members: []
michael@0 687 };
michael@0 688 ret.inheritance = inheritance() || null;
michael@0 689 all_ws();
michael@0 690 consume(OTHER, "{") || error("Bodyless exception");
michael@0 691 while (true) {
michael@0 692 all_ws();
michael@0 693 if (consume(OTHER, "}")) {
michael@0 694 all_ws();
michael@0 695 consume(OTHER, ";") || error("Missing semicolon after exception");
michael@0 696 return ret;
michael@0 697 }
michael@0 698 var ea = extended_attrs();
michael@0 699 all_ws();
michael@0 700 var cnt = const_();
michael@0 701 if (cnt) {
michael@0 702 cnt.extAttrs = ea;
michael@0 703 ret.members.push(cnt);
michael@0 704 }
michael@0 705 else {
michael@0 706 var typ = type();
michael@0 707 all_ws();
michael@0 708 var name = consume(ID);
michael@0 709 all_ws();
michael@0 710 if (!typ || !name || !consume(OTHER, ";")) error("Unknown member in exception body");
michael@0 711 ret.members.push({
michael@0 712 type: "field"
michael@0 713 , name: name.value
michael@0 714 , idlType: typ
michael@0 715 , extAttrs: ea
michael@0 716 });
michael@0 717 }
michael@0 718 }
michael@0 719 };
michael@0 720
michael@0 721 var enum_ = function () {
michael@0 722 all_ws();
michael@0 723 if (!consume(ID, "enum")) return;
michael@0 724 all_ws();
michael@0 725 var name = consume(ID) || error("No name for enum");
michael@0 726 var ret = {
michael@0 727 type: "enum"
michael@0 728 , name: name.value
michael@0 729 , values: []
michael@0 730 };
michael@0 731 all_ws();
michael@0 732 consume(OTHER, "{") || error("No curly for enum");
michael@0 733 var saw_comma = false;
michael@0 734 while (true) {
michael@0 735 all_ws();
michael@0 736 if (consume(OTHER, "}")) {
michael@0 737 all_ws();
michael@0 738 if (saw_comma) error("Trailing comma in enum");
michael@0 739 consume(OTHER, ";") || error("No semicolon after enum");
michael@0 740 return ret;
michael@0 741 }
michael@0 742 var val = consume(STR) || error("Unexpected value in enum");
michael@0 743 ret.values.push(val.value.replace(/"/g, ""));
michael@0 744 all_ws();
michael@0 745 if (consume(OTHER, ",")) {
michael@0 746 all_ws();
michael@0 747 saw_comma = true;
michael@0 748 }
michael@0 749 else {
michael@0 750 saw_comma = false;
michael@0 751 }
michael@0 752 }
michael@0 753 };
michael@0 754
michael@0 755 var typedef = function () {
michael@0 756 all_ws();
michael@0 757 if (!consume(ID, "typedef")) return;
michael@0 758 var ret = {
michael@0 759 type: "typedef"
michael@0 760 };
michael@0 761 all_ws();
michael@0 762 ret.typeExtAttrs = extended_attrs();
michael@0 763 all_ws();
michael@0 764 ret.idlType = type() || error("No type in typedef");
michael@0 765 all_ws();
michael@0 766 var name = consume(ID) || error("No name in typedef");
michael@0 767 ret.name = name.value;
michael@0 768 all_ws();
michael@0 769 consume(OTHER, ";") || error("Unterminated typedef");
michael@0 770 return ret;
michael@0 771 };
michael@0 772
michael@0 773 var implements_ = function () {
michael@0 774 all_ws();
michael@0 775 var target = consume(ID);
michael@0 776 if (!target) return;
michael@0 777 var w = all_ws();
michael@0 778 if (consume(ID, "implements")) {
michael@0 779 var ret = {
michael@0 780 type: "implements"
michael@0 781 , target: target.value
michael@0 782 };
michael@0 783 all_ws();
michael@0 784 var imp = consume(ID) || error("Incomplete implements statement");
michael@0 785 ret["implements"] = imp.value;
michael@0 786 all_ws();
michael@0 787 consume(OTHER, ";") || error("No terminating ; for implements statement");
michael@0 788 return ret;
michael@0 789 }
michael@0 790 else {
michael@0 791 // rollback
michael@0 792 tokens.unshift(w);
michael@0 793 tokens.unshift(target);
michael@0 794 }
michael@0 795 };
michael@0 796
michael@0 797 var definition = function () {
michael@0 798 return callback() ||
michael@0 799 interface_() ||
michael@0 800 partial() ||
michael@0 801 dictionary() ||
michael@0 802 exception() ||
michael@0 803 enum_() ||
michael@0 804 typedef() ||
michael@0 805 implements_()
michael@0 806 ;
michael@0 807 };
michael@0 808
michael@0 809 var definitions = function () {
michael@0 810 if (!tokens.length) return [];
michael@0 811 var defs = [];
michael@0 812 while (true) {
michael@0 813 var ea = extended_attrs()
michael@0 814 , def = definition();
michael@0 815 if (!def) {
michael@0 816 if (ea.length) error("Stray extended attributes");
michael@0 817 break;
michael@0 818 }
michael@0 819 def.extAttrs = ea;
michael@0 820 defs.push(def);
michael@0 821 }
michael@0 822 return defs;
michael@0 823 };
michael@0 824 var res = definitions();
michael@0 825 if (tokens.length) error("Unrecognised tokens");
michael@0 826 return res;
michael@0 827 };
michael@0 828
michael@0 829 var obj = {
michael@0 830 parse: function (str) {
michael@0 831 var tokens = tokenise(str);
michael@0 832 return parse(tokens);
michael@0 833 }
michael@0 834 };
michael@0 835 if (typeof module !== "undefined" && module.exports) {
michael@0 836 module.exports = obj;
michael@0 837 }
michael@0 838 else {
michael@0 839 window.WebIDL2 = obj;
michael@0 840 }
michael@0 841 }());

mercurial