From 386ec8b8ae3d6549903f3dffd826a7a3909700fa Mon Sep 17 00:00:00 2001 From: Kagami Sascha Rosylight Date: Wed, 17 Apr 2019 15:14:10 +0900 Subject: [PATCH 1/3] feat: add module support --- lib/webidl2.js | 77 ++++- lib/writer.js | 1 + test/invalid/baseline/module.txt | 6 +- test/syntax/baseline/interface-inherits.json | 55 +++- test/syntax/baseline/module.json | 313 +++++++++++++++++++ test/syntax/idl/interface-inherits.widl | 6 +- test/syntax/idl/module.widl | 17 + 7 files changed, 457 insertions(+), 18 deletions(-) create mode 100644 test/syntax/baseline/module.json create mode 100644 test/syntax/idl/module.widl diff --git a/lib/webidl2.js b/lib/webidl2.js index 3f79a126..db1fc703 100644 --- a/lib/webidl2.js +++ b/lib/webidl2.js @@ -10,6 +10,7 @@ const tokenRe = { "float": /-?(?=[0-9]*\.|[0-9]+[eE])(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][-+]?[0-9]+)?|[0-9]+[Ee][-+]?[0-9]+)/y, "integer": /-?(0([Xx][0-9A-Fa-f]+|[0-7]*)|[1-9][0-9]*)/y, "identifier": /[_-]?[A-Za-z][0-9A-Z_a-z-]*/y, + "scopedidentifier": /\.[A-Za-z][0-9A-Z_a-z-]*/y, "string": /"[^"]*"/y, "whitespace": /[\t\n\r ]+/y, "comment": /((\/(\/.*|\*([^*]|\*[^/])*\*\/)[\t\n\r ]*)+)/y, @@ -61,6 +62,7 @@ const nonRegexTerminals = [ "legacyiterable", "long", "mixin", + "module", "null", "octet", "optional", @@ -119,22 +121,35 @@ function tokenise(str) { } if (result === -1) { result = attemptTokenMatch("identifier"); - const token = tokens[tokens.length - 1]; - if (result !== -1 && nonRegexTerminals.includes(token.value)) { - token.type = token.value; + if (result !== -1) { + const token = tokens[tokens.length - 1]; + if (nonRegexTerminals.includes(token.value)) { + token.type = token.value; + } else { + lastCharIndex = result; + const scopedResult = attemptTokenMatch("scopedidentifier"); + if (scopedResult !== -1) { + const scoped = tokens.pop(); + token.type = scoped.type; + token.value += scoped.value; + result = scopedResult; + } + } } } } else if (nextChar === '"') { result = attemptTokenMatch("string"); } - for (const punctuation of punctuations) { - if (str.startsWith(punctuation, lastCharIndex)) { - tokens.push({ type: punctuation, value: punctuation, trivia, line, index }); - trivia = ""; - lastCharIndex += punctuation.length; - result = lastCharIndex; - break; + if (result === -1) { + for (const punctuation of punctuations) { + if (str.startsWith(punctuation, lastCharIndex)) { + tokens.push({ type: punctuation, value: punctuation, trivia, line, index }); + trivia = ""; + lastCharIndex += punctuation.length; + result = lastCharIndex; + break; + } } } @@ -195,6 +210,7 @@ function parseByTokens(source) { const FLOAT = "float"; const INT = "integer"; const ID = "identifier"; + const SCOPED = "scopedidentifier"; const STR = "string"; function error(str) { @@ -476,7 +492,7 @@ function parseByTokens(source) { function single_type(typeName) { let ret = GenericType.parse(typeName) || primitive_type(); if (!ret) { - const base = consume(ID, ...stringTypes); + const base = consume(ID, SCOPED, ...stringTypes); if (!base) { return; } @@ -989,7 +1005,7 @@ function parseByTokens(source) { if (!colon) { return; } - const name = consume(ID) || error("No type in inheritance"); + const name = consume(ID, SCOPED) || error("No type in inheritance"); return new Inheritance({ tokens: { colon, name } }); } @@ -1122,12 +1138,46 @@ function parseByTokens(source) { } } + class Module extends Container { + static parse({ partial } = {}) { + function module_interface() { + const partial = consume("partial"); + const base = consume("interface"); + if (!base) { + if (partial) { + throw new Error("Unsupported partial declaration in a module"); + } + return; + } + return Interface.parse(base, { partial }); + } + const tokens = { partial }; + tokens.base = consume("module"); + if (!tokens.base) { + return; + } + return Container.parse(new Module({ tokens }), { + type: "module", + allowedMembers: [ + [module_interface], + [Attribute.parse, { noInherit: true, readonly: true }], + [Operation.parse, { regular: true }] + ] + }); + } + + get type() { + return "module"; + } + } + function partial() { const partial = optional_consume("partial"); if (!partial) return; return Dictionary.parse({ partial }) || interface_({ partial }) || Namespace.parse({ partial }) || + Module.parse({ partial }) || error("Partial doesn't apply to anything"); } @@ -1301,7 +1351,8 @@ function parseByTokens(source) { Enum.parse() || Typedef.parse() || Includes.parse() || - Namespace.parse(); + Namespace.parse() || + Module.parse(); } function definitions() { diff --git a/lib/writer.js b/lib/writer.js index 05e5ae4d..b2f6120a 100644 --- a/lib/writer.js +++ b/lib/writer.js @@ -274,6 +274,7 @@ export function write(ast, { templates: ts = templates } = {}) { interface: container, "interface mixin": container, namespace: container, + module: container, operation, attribute, dictionary: container, diff --git a/test/invalid/baseline/module.txt b/test/invalid/baseline/module.txt index f31c2f1b..f347c3b3 100644 --- a/test/invalid/baseline/module.txt +++ b/test/invalid/baseline/module.txt @@ -1,3 +1,3 @@ -Syntax error at line 2: -module gfx { -^ Unrecognised tokens +Syntax error at line 4, since `module gfx`: + module geom { + ^ Missing return type diff --git a/test/syntax/baseline/interface-inherits.json b/test/syntax/baseline/interface-inherits.json index 725e7b21..7afb39d1 100644 --- a/test/syntax/baseline/interface-inherits.json +++ b/test/syntax/baseline/interface-inherits.json @@ -151,9 +151,62 @@ "termination": "" } }, + { + "type": "interface", + "name": "Bonsai", + "escapedName": "Bonsai", + "inheritance": { + "name": "Tree.Art", + "escapedName": "Tree.Art", + "trivia": { + "colon": " ", + "name": " " + } + }, + "members": [ + { + "type": "attribute", + "name": "name", + "escapedName": "name", + "idlType": { + "type": "attribute-type", + "extAttrs": null, + "generic": null, + "nullable": null, + "union": false, + "idlType": "DOMString", + "baseName": "DOMString", + "escapedBaseName": "DOMString", + "prefix": null, + "postfix": null, + "separator": null, + "trivia": { + "base": " " + } + }, + "extAttrs": null, + "special": null, + "readonly": null, + "trivia": { + "base": "\n ", + "name": " ", + "termination": "" + } + } + ], + "extAttrs": null, + "partial": null, + "trivia": { + "base": "\n\n", + "name": " ", + "open": " ", + "close": "\n", + "termination": "" + } + }, { "type": "eof", "value": "", - "trivia": "" + "trivia": "\n" } ] diff --git a/test/syntax/baseline/module.json b/test/syntax/baseline/module.json new file mode 100644 index 00000000..d3f22675 --- /dev/null +++ b/test/syntax/baseline/module.json @@ -0,0 +1,313 @@ +[ + { + "type": "module", + "name": "temporal", + "escapedName": "temporal", + "members": [ + { + "type": "interface", + "name": "Timezone", + "escapedName": "Timezone", + "inheritance": null, + "members": [ + { + "type": "attribute", + "name": "name", + "escapedName": "name", + "idlType": { + "type": "attribute-type", + "extAttrs": null, + "generic": null, + "nullable": null, + "union": false, + "idlType": "USVString", + "baseName": "USVString", + "escapedBaseName": "USVString", + "prefix": null, + "postfix": null, + "separator": null, + "trivia": { + "base": " " + } + }, + "extAttrs": null, + "special": null, + "readonly": { + "trivia": "\r\n " + }, + "trivia": { + "base": " ", + "name": " ", + "termination": "" + } + }, + { + "type": "operation", + "name": "offsetMs", + "body": { + "name": { + "value": "offsetMs", + "escaped": "offsetMs", + "trivia": " " + }, + "idlType": { + "type": "return-type", + "extAttrs": null, + "generic": null, + "nullable": null, + "union": false, + "idlType": "long long", + "baseName": "long", + "escapedBaseName": "long", + "prefix": null, + "postfix": { + "value": "long", + "trivia": " " + }, + "separator": null, + "trivia": { + "base": "\r\n " + } + }, + "arguments": [ + { + "name": "unixTime", + "escapedName": "unixTime", + "idlType": { + "type": "argument-type", + "extAttrs": null, + "generic": null, + "nullable": null, + "union": false, + "idlType": "long long", + "baseName": "long", + "escapedBaseName": "long", + "prefix": null, + "postfix": { + "value": "long", + "trivia": " " + }, + "separator": null, + "trivia": { + "base": "" + } + }, + "default": null, + "optional": null, + "variadic": null, + "separator": null, + "trivia": { + "name": " " + } + } + ], + "trivia": { + "open": "", + "close": "" + } + }, + "extAttrs": null, + "special": null, + "trivia": { + "termination": "" + } + } + ], + "extAttrs": null, + "partial": null, + "trivia": { + "base": "\r\n ", + "name": " ", + "open": " ", + "close": "\r\n ", + "termination": "" + } + }, + { + "type": "operation", + "name": "getCurrentTimezone", + "body": { + "name": { + "value": "getCurrentTimezone", + "escaped": "getCurrentTimezone", + "trivia": " " + }, + "idlType": { + "type": "return-type", + "extAttrs": null, + "generic": null, + "nullable": null, + "union": false, + "idlType": "temporal.Timezone", + "baseName": "temporal.Timezone", + "escapedBaseName": "temporal.Timezone", + "prefix": null, + "postfix": null, + "separator": null, + "trivia": { + "base": "\r\n " + } + }, + "arguments": [], + "trivia": { + "open": "", + "close": "" + } + }, + "extAttrs": null, + "special": null, + "trivia": { + "termination": "" + } + }, + { + "type": "attribute", + "name": "initialTimezone", + "escapedName": "initialTimezone", + "idlType": { + "type": "attribute-type", + "extAttrs": null, + "generic": null, + "nullable": null, + "union": false, + "idlType": "temporal.Timezone", + "baseName": "temporal.Timezone", + "escapedBaseName": "temporal.Timezone", + "prefix": null, + "postfix": null, + "separator": null, + "trivia": { + "base": " " + } + }, + "extAttrs": null, + "special": null, + "readonly": { + "trivia": "\r\n " + }, + "trivia": { + "base": " ", + "name": " ", + "termination": "" + } + } + ], + "extAttrs": { + "items": [ + { + "type": "extended-attribute", + "name": "Exposed", + "rhs": { + "type": "identifier", + "value": "Window", + "trivia": { + "assign": "", + "value": "" + } + }, + "signature": null, + "separator": { + "value": ",", + "trivia": "" + }, + "trivia": { + "name": "" + } + }, + { + "type": "extended-attribute", + "name": "SecureContext", + "rhs": null, + "signature": null, + "separator": null, + "trivia": { + "name": " " + } + } + ], + "trivia": { + "open": "// Copied from https://github.com/heycam/webidl/pull/675\r\n\r\n", + "close": "" + } + }, + "partial": null, + "trivia": { + "base": "\r\n", + "name": " ", + "open": " ", + "close": "\r\n", + "termination": "" + } + }, + { + "type": "module", + "name": "temporal", + "escapedName": "temporal", + "members": [ + { + "type": "interface", + "name": "Timezone", + "escapedName": "Timezone", + "members": [ + { + "type": "attribute", + "name": "zero", + "escapedName": "zero", + "idlType": { + "type": "attribute-type", + "extAttrs": null, + "generic": null, + "nullable": null, + "union": false, + "idlType": "float", + "baseName": "float", + "escapedBaseName": "float", + "prefix": null, + "postfix": null, + "separator": null, + "trivia": { + "base": " " + } + }, + "extAttrs": null, + "special": null, + "readonly": null, + "trivia": { + "base": "\r\n ", + "name": " ", + "termination": "" + } + } + ], + "extAttrs": null, + "partial": { + "trivia": "\r\n " + }, + "trivia": { + "partial": "\r\n ", + "base": " ", + "name": " ", + "open": " ", + "close": "\r\n ", + "termination": "" + } + } + ], + "extAttrs": null, + "partial": { + "trivia": "\r\n\r\n" + }, + "trivia": { + "base": " ", + "name": " ", + "open": " ", + "close": "\r\n", + "termination": "" + } + }, + { + "type": "eof", + "value": "", + "trivia": "\r\n" + } +] diff --git a/test/syntax/idl/interface-inherits.widl b/test/syntax/idl/interface-inherits.widl index 7921def7..86072163 100644 --- a/test/syntax/idl/interface-inherits.widl +++ b/test/syntax/idl/interface-inherits.widl @@ -9,4 +9,8 @@ interface Human : Animal { interface Dog : Animal { attribute Human owner; -}; \ No newline at end of file +}; + +interface Bonsai : Tree.Art { + attribute DOMString name; +}; diff --git a/test/syntax/idl/module.widl b/test/syntax/idl/module.widl new file mode 100644 index 00000000..f2aa18e7 --- /dev/null +++ b/test/syntax/idl/module.widl @@ -0,0 +1,17 @@ +// Copied from https://github.com/heycam/webidl/pull/675 + +[Exposed=Window, SecureContext] +module temporal { + interface Timezone { + readonly attribute USVString name; + long long offsetMs(long long unixTime); + }; + temporal.Timezone getCurrentTimezone(); + readonly attribute temporal.Timezone initialTimezone; +}; + +partial module temporal { + partial interface Timezone { + attribute float zero; + }; +}; From 68dc7dc39a498d6f51a8486cb55990fb2b5ec594 Mon Sep 17 00:00:00 2001 From: Kagami Sascha Rosylight Date: Wed, 17 Apr 2019 15:21:52 +0900 Subject: [PATCH 2/3] fix test --- test/syntax/baseline/module.json | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/syntax/baseline/module.json b/test/syntax/baseline/module.json index d3f22675..daf348e0 100644 --- a/test/syntax/baseline/module.json +++ b/test/syntax/baseline/module.json @@ -33,7 +33,7 @@ "extAttrs": null, "special": null, "readonly": { - "trivia": "\r\n " + "trivia": "\n " }, "trivia": { "base": " ", @@ -66,7 +66,7 @@ }, "separator": null, "trivia": { - "base": "\r\n " + "base": "\n " } }, "arguments": [ @@ -116,10 +116,10 @@ "extAttrs": null, "partial": null, "trivia": { - "base": "\r\n ", + "base": "\n ", "name": " ", "open": " ", - "close": "\r\n ", + "close": "\n ", "termination": "" } }, @@ -145,7 +145,7 @@ "postfix": null, "separator": null, "trivia": { - "base": "\r\n " + "base": "\n " } }, "arguments": [], @@ -183,7 +183,7 @@ "extAttrs": null, "special": null, "readonly": { - "trivia": "\r\n " + "trivia": "\n " }, "trivia": { "base": " ", @@ -226,16 +226,16 @@ } ], "trivia": { - "open": "// Copied from https://github.com/heycam/webidl/pull/675\r\n\r\n", + "open": "// Copied from https://github.com/heycam/webidl/pull/675\n\n", "close": "" } }, "partial": null, "trivia": { - "base": "\r\n", + "base": "\n", "name": " ", "open": " ", - "close": "\r\n", + "close": "\n", "termination": "" } }, @@ -273,7 +273,7 @@ "special": null, "readonly": null, "trivia": { - "base": "\r\n ", + "base": "\n ", "name": " ", "termination": "" } @@ -281,33 +281,33 @@ ], "extAttrs": null, "partial": { - "trivia": "\r\n " + "trivia": "\n " }, "trivia": { - "partial": "\r\n ", + "partial": "\n ", "base": " ", "name": " ", "open": " ", - "close": "\r\n ", + "close": "\n ", "termination": "" } } ], "extAttrs": null, "partial": { - "trivia": "\r\n\r\n" + "trivia": "\n\n" }, "trivia": { "base": " ", "name": " ", "open": " ", - "close": "\r\n", + "close": "\n", "termination": "" } }, { "type": "eof", "value": "", - "trivia": "\r\n" + "trivia": "\n" } ] From 51b16eafd0476180a497e2e39a2732b2708c0d51 Mon Sep 17 00:00:00 2001 From: Kagami Sascha Rosylight Date: Sat, 20 Apr 2019 20:01:26 +0900 Subject: [PATCH 3/3] update baseline --- test/syntax/baseline/interface-inherits.json | 41 +--- test/syntax/baseline/module.json | 239 ++++--------------- 2 files changed, 47 insertions(+), 233 deletions(-) diff --git a/test/syntax/baseline/interface-inherits.json b/test/syntax/baseline/interface-inherits.json index b3294e7c..5b5c19bb 100644 --- a/test/syntax/baseline/interface-inherits.json +++ b/test/syntax/baseline/interface-inherits.json @@ -78,55 +78,28 @@ { "type": "interface", "name": "Bonsai", - "escapedName": "Bonsai", "inheritance": { - "name": "Tree.Art", - "escapedName": "Tree.Art", - "trivia": { - "colon": " ", - "name": " " - } + "name": "Tree.Art" }, "members": [ { "type": "attribute", "name": "name", - "escapedName": "name", "idlType": { "type": "attribute-type", "extAttrs": null, - "generic": null, - "nullable": null, + "generic": "", + "nullable": false, "union": false, - "idlType": "DOMString", - "baseName": "DOMString", - "escapedBaseName": "DOMString", - "prefix": null, - "postfix": null, - "separator": null, - "trivia": { - "base": " " - } + "idlType": "DOMString" }, "extAttrs": null, - "special": null, - "readonly": null, - "trivia": { - "base": "\n ", - "name": " ", - "termination": "" - } + "special": "", + "readonly": false } ], "extAttrs": null, - "partial": null, - "trivia": { - "base": "\n\n", - "name": " ", - "open": " ", - "close": "\n", - "termination": "" - } + "partial": false }, { "type": "eof", diff --git a/test/syntax/baseline/module.json b/test/syntax/baseline/module.json index daf348e0..581c1616 100644 --- a/test/syntax/baseline/module.json +++ b/test/syntax/baseline/module.json @@ -2,194 +2,96 @@ { "type": "module", "name": "temporal", - "escapedName": "temporal", "members": [ { "type": "interface", "name": "Timezone", - "escapedName": "Timezone", "inheritance": null, "members": [ { "type": "attribute", "name": "name", - "escapedName": "name", "idlType": { "type": "attribute-type", "extAttrs": null, - "generic": null, - "nullable": null, + "generic": "", + "nullable": false, "union": false, - "idlType": "USVString", - "baseName": "USVString", - "escapedBaseName": "USVString", - "prefix": null, - "postfix": null, - "separator": null, - "trivia": { - "base": " " - } + "idlType": "USVString" }, "extAttrs": null, - "special": null, - "readonly": { - "trivia": "\n " - }, - "trivia": { - "base": " ", - "name": " ", - "termination": "" - } + "special": "", + "readonly": true }, { "type": "operation", "name": "offsetMs", "body": { - "name": { - "value": "offsetMs", - "escaped": "offsetMs", - "trivia": " " - }, + "name": "offsetMs", "idlType": { "type": "return-type", "extAttrs": null, - "generic": null, - "nullable": null, + "generic": "", + "nullable": false, "union": false, - "idlType": "long long", - "baseName": "long", - "escapedBaseName": "long", - "prefix": null, - "postfix": { - "value": "long", - "trivia": " " - }, - "separator": null, - "trivia": { - "base": "\n " - } + "idlType": "long long" }, "arguments": [ { "name": "unixTime", - "escapedName": "unixTime", "idlType": { "type": "argument-type", "extAttrs": null, - "generic": null, - "nullable": null, + "generic": "", + "nullable": false, "union": false, - "idlType": "long long", - "baseName": "long", - "escapedBaseName": "long", - "prefix": null, - "postfix": { - "value": "long", - "trivia": " " - }, - "separator": null, - "trivia": { - "base": "" - } + "idlType": "long long" }, "default": null, - "optional": null, - "variadic": null, - "separator": null, - "trivia": { - "name": " " - } + "optional": false, + "variadic": false } - ], - "trivia": { - "open": "", - "close": "" - } + ] }, "extAttrs": null, - "special": null, - "trivia": { - "termination": "" - } + "special": "" } ], "extAttrs": null, - "partial": null, - "trivia": { - "base": "\n ", - "name": " ", - "open": " ", - "close": "\n ", - "termination": "" - } + "partial": false }, { "type": "operation", "name": "getCurrentTimezone", "body": { - "name": { - "value": "getCurrentTimezone", - "escaped": "getCurrentTimezone", - "trivia": " " - }, + "name": "getCurrentTimezone", "idlType": { "type": "return-type", "extAttrs": null, - "generic": null, - "nullable": null, + "generic": "", + "nullable": false, "union": false, - "idlType": "temporal.Timezone", - "baseName": "temporal.Timezone", - "escapedBaseName": "temporal.Timezone", - "prefix": null, - "postfix": null, - "separator": null, - "trivia": { - "base": "\n " - } + "idlType": "temporal.Timezone" }, - "arguments": [], - "trivia": { - "open": "", - "close": "" - } + "arguments": [] }, "extAttrs": null, - "special": null, - "trivia": { - "termination": "" - } + "special": "" }, { "type": "attribute", "name": "initialTimezone", - "escapedName": "initialTimezone", "idlType": { "type": "attribute-type", "extAttrs": null, - "generic": null, - "nullable": null, + "generic": "", + "nullable": false, "union": false, - "idlType": "temporal.Timezone", - "baseName": "temporal.Timezone", - "escapedBaseName": "temporal.Timezone", - "prefix": null, - "postfix": null, - "separator": null, - "trivia": { - "base": " " - } + "idlType": "temporal.Timezone" }, "extAttrs": null, - "special": null, - "readonly": { - "trivia": "\n " - }, - "trivia": { - "base": " ", - "name": " ", - "termination": "" - } + "special": "", + "readonly": true } ], "extAttrs": { @@ -199,111 +101,50 @@ "name": "Exposed", "rhs": { "type": "identifier", - "value": "Window", - "trivia": { - "assign": "", - "value": "" - } - }, - "signature": null, - "separator": { - "value": ",", - "trivia": "" + "value": "Window" }, - "trivia": { - "name": "" - } + "arguments": [] }, { "type": "extended-attribute", "name": "SecureContext", "rhs": null, - "signature": null, - "separator": null, - "trivia": { - "name": " " - } + "arguments": [] } - ], - "trivia": { - "open": "// Copied from https://github.com/heycam/webidl/pull/675\n\n", - "close": "" - } + ] }, - "partial": null, - "trivia": { - "base": "\n", - "name": " ", - "open": " ", - "close": "\n", - "termination": "" - } + "partial": false }, { "type": "module", "name": "temporal", - "escapedName": "temporal", "members": [ { "type": "interface", "name": "Timezone", - "escapedName": "Timezone", "members": [ { "type": "attribute", "name": "zero", - "escapedName": "zero", "idlType": { "type": "attribute-type", "extAttrs": null, - "generic": null, - "nullable": null, + "generic": "", + "nullable": false, "union": false, - "idlType": "float", - "baseName": "float", - "escapedBaseName": "float", - "prefix": null, - "postfix": null, - "separator": null, - "trivia": { - "base": " " - } + "idlType": "float" }, "extAttrs": null, - "special": null, - "readonly": null, - "trivia": { - "base": "\n ", - "name": " ", - "termination": "" - } + "special": "", + "readonly": false } ], "extAttrs": null, - "partial": { - "trivia": "\n " - }, - "trivia": { - "partial": "\n ", - "base": " ", - "name": " ", - "open": " ", - "close": "\n ", - "termination": "" - } + "partial": true } ], "extAttrs": null, - "partial": { - "trivia": "\n\n" - }, - "trivia": { - "base": " ", - "name": " ", - "open": " ", - "close": "\n", - "termination": "" - } + "partial": true }, { "type": "eof",