Skip to content
This repository has been archived by the owner on Jun 7, 2024. It is now read-only.

Commit

Permalink
fix(core/inline-idl-parser): support nullable IDL types (speced#3632)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcoscaceres authored Jun 15, 2021
1 parent 8bdb284 commit 10b8942
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 10 deletions.
34 changes: 25 additions & 9 deletions src/core/inline-idl-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@

import { htmlJoinComma, showError } from "./utils.js";
import { html } from "./import-maps.js";
const idlPrimitiveRegex = /^[a-z]+(\s+[a-z]+)+$/; // {{unrestricted double}} {{ double }}
const idlPrimitiveRegex = /^[a-z]+(\s+[a-z]+)+\??$/; // {{unrestricted double?}} {{ double }}
const exceptionRegex = /\B"([^"]*)"\B/; // {{ "SomeException" }}
const methodRegex = /(\w+)\((.*)\)$/;
const slotRegex = /^\[\[(\w+)\]\]$/;
// matches: `value` or `[[value]]`
// NOTE: [[value]] is actually a slot, but database has this as type="attribute"
const attributeRegex = /^((?:\[\[)?(?:\w+)(?:\]\])?)$/;
const baseRegex = /^(?:\w+)\??$/;
const enumRegex = /^(\w+)\["([\w- ]*)"\]$/;
// TODO: const splitRegex = /(?<=\]\]|\b)\./
// https://github.com/w3c/respec/pull/1848/files#r225087385
Expand All @@ -21,6 +22,7 @@ const methodSplitRegex = /\.?(\w+\(.*\)$)/;
* @property {"base"} type
* @property {string} identifier
* @property {boolean} renderParent
* @property {boolean} nullable
* @property {InlineIdl | null} [parent]
*
* @typedef {object} IdlAttribute
Expand Down Expand Up @@ -56,6 +58,7 @@ const methodSplitRegex = /\.?(\w+\(.*\)$)/;
*
* @typedef {object} IdlPrimitive
* @property {"idl-primitive"} type
* @property {boolean} nullable
* @property {string} identifier
* @property {boolean} renderParent
* @property {InlineIdl | null} [parent]
Expand Down Expand Up @@ -116,12 +119,21 @@ function parseInlineIDL(str) {
continue;
}
if (idlPrimitiveRegex.test(value)) {
results.push({ type: "idl-primitive", identifier: value, renderParent });
const nullable = value.endsWith("?");
const identifier = nullable ? value.slice(0, -1) : value;
results.push({
type: "idl-primitive",
identifier,
renderParent,
nullable,
});
continue;
}
// base, always final token
if (attributeRegex.test(value) && tokens.length === 0) {
results.push({ type: "base", identifier: value, renderParent });
if (baseRegex.test(value) && tokens.length === 0) {
const nullable = value.endsWith("?");
const identifier = nullable ? value.slice(0, -1) : value;
results.push({ type: "base", identifier, renderParent, nullable });
continue;
}
throw new SyntaxError(`IDL micro-syntax parsing error in \`{{ ${str} }}\``);
Expand All @@ -139,10 +151,13 @@ function parseInlineIDL(str) {
*/
function renderBase(details) {
// Check if base is a local variable in a section
const { identifier, renderParent } = details;
const { identifier, renderParent, nullable } = details;
if (renderParent) {
return html`<a data-xref-type="_IDL_" data-link-type="idl"
><code>${identifier}</code></a
return html`<a
data-xref-type="_IDL_"
data-link-type="idl"
data-lt="${identifier}"
><code>${identifier + (nullable ? "?" : "")}</code></a
>`;
}
}
Expand Down Expand Up @@ -244,12 +259,13 @@ function renderException(details) {
* @param {IdlPrimitive} details
*/
function renderIdlPrimitiveType(details) {
const { identifier } = details;
const { identifier, nullable } = details;
const element = html`<a
data-link-type="idl"
data-cite="WebIDL"
data-xref-type="interface"
><code>${identifier}</code></a
data-lt="${identifier}"
><code>${identifier + (nullable ? "?" : "")}</code></a
>`;
return element;
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/inlines.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const l10n = getIntlData(localizationStrings);
// TODO: Replace (?!`) at the end with (?:<!`) at the start when Firefox + Safari
// add support.
const inlineCodeRegExp = /(?:`[^`]+`)(?!`)/; // `code`
const inlineIdlReference = /(?:{{[^}]+}})/; // {{ WebIDLThing }}
const inlineIdlReference = /(?:{{[^}]+\?*}})/; // {{ WebIDLThing }}, {{ WebIDLThing? }}
const inlineVariable = /\B\|\w[\w\s]*(?:\s*:[\w\s&;<>]+\??)?\|\B/; // |var : Type?|
const inlineCitation = /(?:\[\[(?:!|\\|\?)?[\w.-]+(?:|[^\]]+)?\]\])/; // [[citation]]
const inlineExpansion = /(?:\[\[\[(?:!|\\|\?)?#?[\w-.]+\]\]\])/; // [[[expand]]]
Expand Down
50 changes: 50 additions & 0 deletions tests/spec/core/inlines-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,56 @@ describe("Core - Inlines", () => {
);
});

it("supports {{ Nullable? }} types and primitives", async () => {
const body = `
<section>
<pre class="idl">
[Exposed=Window]
interface InterFace{};
dictionary Dict{};
</pre>
<p id="interface">{{ InterFace? }}</p>
<p id="dictionary">{{ Dict? }}</p>
<p id="primitive">{{ unsigned short? }}</p>
</section>
`;
const config = { xref: ["WebIDL"] };
const doc = await makeRSDoc(makeStandardOps(config, body));

const [interfaceDfn, dictDfn] = doc.querySelectorAll(".idl dfn");

// {{ Interface? }}
const interfaceAnchor = doc.querySelector("#interface a");
expect(interfaceAnchor.textContent).toBe("InterFace?");
expect(interfaceAnchor.hash.endsWith(interfaceDfn.id)).toBeTrue();

const interfaceData = interfaceAnchor.dataset;
expect(interfaceData.xrefType).toBe("_IDL_");
expect(interfaceData.linkType).toBe("idl");
expect(interfaceData.lt).toBe("InterFace");

// {{ Dict? }}
const dictAnchor = doc.querySelector("#dictionary a");
expect(dictAnchor.textContent).toBe("Dict?");
expect(dictAnchor.hash.endsWith(dictDfn.id)).toBeTrue();

const dictData = dictAnchor.dataset;
expect(dictData.xrefType).toBe("_IDL_");
expect(dictData.linkType).toBe("idl");
expect(dictData.lt).toBe("Dict");

// {{ unsigned short? }}
const primitiveAnchor = doc.querySelector("#primitive a");
expect(primitiveAnchor.textContent).toBe("unsigned short?");
expect(primitiveAnchor.hash).toBe("#idl-unsigned-short");

const primitiveData = primitiveAnchor.dataset;
expect(primitiveData.linkType).toBe("idl");
expect(primitiveData.cite).toBe("webidl");
expect(primitiveData.xrefType).toBe("interface");
expect(primitiveData.lt).toBe("unsigned short");
});

it("doesn't link processed inline WebIDL if inside a definition", async () => {
const body = `
<section>
Expand Down

0 comments on commit 10b8942

Please sign in to comment.