Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for structured headers for SDOs #358

Merged
merged 4 commits into from
Sep 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/Clause.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ export default class Clause extends Builder {
// if we find such a DL, treat this as a structured header

const type = this.node.getAttribute('type');
this.node.removeAttribute('type'); // TODO maybe leave it in; this is just to minimize the diff

const { name, formattedHeader, formattedParams } = parseStructuredHeaderH1(this.spec, header);
if (type === 'numeric method' && name != null && !name.includes('::')) {
Expand All @@ -101,7 +100,11 @@ export default class Clause extends Builder {
nodeRelativeColumn: 1,
});
}
if (formattedHeader != null) {
if (type === 'sdo' && (formattedHeader ?? header.innerHTML).includes('(')) {
// SDOs are rendered without parameter lists in the header, for the moment
const currentHeader = formattedHeader ?? header.innerHTML;
header.innerHTML = currentHeader.substring(0, currentHeader.indexOf('(')).trim();
} else if (formattedHeader != null) {
header.innerHTML = formattedHeader;
}

Expand Down Expand Up @@ -132,6 +135,8 @@ export default class Clause extends Builder {
type != null &&
[
'abstract operation',
'sdo',
'syntax-directed operation',
'host-defined abstract operation',
'implementation-defined abstract operation',
'numeric method',
Expand Down
4 changes: 3 additions & 1 deletion src/Spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1235,7 +1235,9 @@ ${await utils.readFile(path.join(__dirname, '../js/multipage.js'))}
}
}

const sdos = this.doc.querySelectorAll('emu-clause[type=sdo]');
const sdos = this.doc.querySelectorAll(
'emu-clause[type=sdo],emu-clause[type="syntax-directed operation"]'
);
outer: for (const sdo of sdos) {
let header: Element | undefined;
for (const child of sdo.children) {
Expand Down
28 changes: 22 additions & 6 deletions src/header-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ export function parseStructuredHeaderH1(
headerText = headerText.substring(prefix[0].length);
}

const parsed = headerText.match(/^(?<beforeParams>\s*(?<name>[^(\s]+)\s*\()(?<params>.*)\)\s*$/s);
const parsed = headerText.match(
/^(?<beforeParams>\s*(?<name>[^(\s]+)\s*)(?:\((?<params>.*)\)\s*)?$/s
);
if (parsed == null) {
spec.warn({
type: 'contents',
Expand All @@ -28,7 +30,7 @@ export function parseStructuredHeaderH1(

type Param = { name: string; type: string | null };
const name = parsed.groups!.name;
let paramText = parsed.groups!.params;
let paramText = parsed.groups!.params ?? '';
const params: Array<Param> = [];
const optionalParams: Array<Param> = [];
let formattedHeader = null;
Expand All @@ -49,6 +51,7 @@ export function parseStructuredHeaderH1(
return (
(prefix?.[0].length ?? 0) +
parsed!.groups!.beforeParams.length +
1 + // `beforeParams` does not include the leading `(`
(offset - line.length) + // we've already updated offset to include line.length at this point
index + // to account for the `\n`s eaten by the .split
line.match(/^\s*/)![0].length
Expand Down Expand Up @@ -265,7 +268,8 @@ export function formatPreamble(
): Array<Element> {
const para = spec.doc.createElement('p');
const paras = [para];
switch (type?.toLowerCase()) {
type = (type ?? '').toLowerCase();
switch (type) {
case 'numeric method':
case 'abstract operation': {
// TODO tests (for each type of parametered thing) which have HTML in the parameter type
Expand All @@ -280,6 +284,11 @@ export function formatPreamble(
para.innerHTML += `The implementation-defined abstract operation ${name} takes ${formattedParams}.`;
break;
}
case 'sdo':
case 'syntax-directed operation': {
para.innerHTML += `The syntax-directed operation ${name} takes ${formattedParams}.`;
break;
}
case 'internal method':
case 'concrete method': {
if (_for == null) {
Expand Down Expand Up @@ -326,23 +335,30 @@ export function formatPreamble(
para.append(' ', ...description.childNodes);
}
}
const isSdo = type === 'sdo' || type === 'syntax-directed operation';
const lastSentence = isSdo
? 'It is defined piecewise over the following productions:'
: 'It performs the following steps when called:';
let next = dl.nextElementSibling;
while (next != null && next.tagName === 'EMU-NOTE') {
next = next.nextElementSibling;
}
if (next?.tagName == 'EMU-ALG' && !next.hasAttribute('replaces-step')) {
if (
(isSdo && next?.tagName === 'EMU-GRAMMAR') ||
(!isSdo && next?.tagName === 'EMU-ALG' && !next.hasAttribute('replaces-step'))
) {
if (paras.length > 1 || next !== dl.nextElementSibling) {
const whitespace = next.previousSibling;
const after = spec.doc.createElement('p');
after.append('It performs the following steps when called:');
after.append(lastSentence);
next.parentElement!.insertBefore(after, next);

// fix up the whitespace in the generated HTML
if (whitespace?.nodeType === 3 /* TEXT_NODE */ && /^\s+$/.test(whitespace.nodeValue!)) {
next.parentElement!.insertBefore(whitespace.cloneNode(), next);
}
} else {
para.append(' It performs the following steps when called:');
para.append(' ' + lastSentence);
}
}
return paras;
Expand Down
39 changes: 38 additions & 1 deletion test/baselines/generated-reference/structured-headers.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!doctype html>
<head><meta charset="utf-8"></head><body><div id="spec-container">

<emu-clause id="sec-exampleao" aoid="ExampleAO">
<emu-clause id="sec-exampleao" type="abstract operation" aoid="ExampleAO">
<h1><span class="secnum">1</span> ExampleAO ( param [ , param2 ] )</h1>
<p>The abstract operation ExampleAO takes argument param (an <emu-xref href="#integer"><a href="https://tc39.es/ecma262/#integer">integer</a></emu-xref>) and optional argument param2 (a String). It is an example.</p>
<emu-note><span class="note">Note</span><div class="note-contents">
Expand All @@ -10,4 +10,41 @@ <h1><span class="secnum">1</span> ExampleAO ( param [ , param2 ] )</h1>
<p>It performs the following steps when called:</p>
<emu-alg><ol><li>Algorithm steps go here.</li></ol></emu-alg>
</emu-clause>

<emu-clause id="sec-example-grammar">
<h1><span class="secnum">2</span> This and That</h1>
<emu-grammar type="definition"><emu-production name="PrimaryExpression" id="prod-PrimaryExpression">
<emu-nt><a href="#prod-PrimaryExpression">PrimaryExpression</a></emu-nt> <emu-geq>:</emu-geq> <emu-rhs a="jo4mwtvh" id="prod-Joc9-5i9"><emu-t>this</emu-t></emu-rhs>
<emu-rhs a="_ditw-4g" id="prod-QC7Z2SC4"><emu-t>that</emu-t></emu-rhs>
</emu-production>
</emu-grammar>
<emu-clause id="sec-isthis" type="sdo" aoid="IsThis">
<h1><span class="secnum">2.1</span> IsThis</h1>
<p>The <emu-xref href="#sec-algorithm-conventions-syntax-directed-operations"><a href="https://tc39.es/ecma262/#sec-algorithm-conventions-syntax-directed-operations">syntax-directed operation</a></emu-xref> IsThis takes no arguments. It is an example. It is defined piecewise over the following productions:</p>
<emu-grammar><emu-production name="PrimaryExpression" collapsed="">
<emu-nt><a href="#prod-PrimaryExpression">PrimaryExpression</a></emu-nt> <emu-geq>:</emu-geq> <emu-rhs a="jo4mwtvh" id="prod-oDKnYE2s"><emu-t>this</emu-t></emu-rhs>
</emu-production>
</emu-grammar>
<emu-alg><ol><li>Return <emu-val>true</emu-val>.</li></ol></emu-alg>
<emu-grammar><emu-production name="PrimaryExpression" collapsed="">
<emu-nt><a href="#prod-PrimaryExpression">PrimaryExpression</a></emu-nt> <emu-geq>:</emu-geq> <emu-rhs a="_ditw-4g" id="prod-_G-zA61Z"><emu-t>that</emu-t></emu-rhs>
</emu-production>
</emu-grammar>
<emu-alg><ol><li>Return <emu-val>false</emu-val>.</li></ol></emu-alg>
</emu-clause>
<emu-clause id="sec-isthat" type="sdo" aoid="IsThat">
<h1><span class="secnum">2.2</span> IsThat</h1>
<p>The <emu-xref href="#sec-algorithm-conventions-syntax-directed-operations"><a href="https://tc39.es/ecma262/#sec-algorithm-conventions-syntax-directed-operations">syntax-directed operation</a></emu-xref> IsThat takes argument <var>ignored</var> (an example). It is defined piecewise over the following productions:</p>
<emu-grammar><emu-production name="PrimaryExpression" collapsed="">
<emu-nt><a href="#prod-PrimaryExpression">PrimaryExpression</a></emu-nt> <emu-geq>:</emu-geq> <emu-rhs a="jo4mwtvh" id="prod-QDgm4qgx"><emu-t>this</emu-t></emu-rhs>
</emu-production>
</emu-grammar>
<emu-alg><ol><li>Return <emu-val>false</emu-val>.</li></ol></emu-alg>
<emu-grammar><emu-production name="PrimaryExpression" collapsed="">
<emu-nt><a href="#prod-PrimaryExpression">PrimaryExpression</a></emu-nt> <emu-geq>:</emu-geq> <emu-rhs a="_ditw-4g" id="prod-6qiWXvFG"><emu-t>that</emu-t></emu-rhs>
</emu-production>
</emu-grammar>
<emu-alg><ol><li>Return <emu-val>true</emu-val>.</li></ol></emu-alg>
</emu-clause>
</emu-clause>
</div></body>
41 changes: 41 additions & 0 deletions test/baselines/sources/structured-headers.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,44 @@ <h1>
1. Algorithm steps go here.
</emu-alg>
</emu-clause>

<emu-clause id="sec-example-grammar">
<h1>This and That</h1>
<emu-grammar type="definition">
PrimaryExpression :
`this`
`that`
</emu-grammar>
<emu-clause id="sec-isthis" type="sdo">
<h1>IsThis</h1>
<dl class='header'>
<dt>description</dt>
<dd>It is an example.</dd>
</dl>
<emu-grammar>PrimaryExpression : `this`</emu-grammar>
<emu-alg>
1. Return *true*.
</emu-alg>
<emu-grammar>PrimaryExpression : `that`</emu-grammar>
<emu-alg>
1. Return *false*.
</emu-alg>
</emu-clause>
<emu-clause id="sec-isthat" type="sdo">
<h1>
IsThat (
_ignored_: an example,
)
</h1>
<dl class='header'>
</dl>
<emu-grammar>PrimaryExpression : `this`</emu-grammar>
<emu-alg>
1. Return *false*.
</emu-alg>
<emu-grammar>PrimaryExpression : `that`</emu-grammar>
<emu-alg>
1. Return *true*.
</emu-alg>
</emu-clause>
</emu-clause>