Skip to content

Commit

Permalink
support ins/del in structured headers (#359)
Browse files Browse the repository at this point in the history
  • Loading branch information
bakkot authored Sep 14, 2021
1 parent 4786953 commit f5540e4
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 11 deletions.
11 changes: 10 additions & 1 deletion src/Clause.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,16 @@ export default class Clause extends Builder {
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();
header.innerHTML = (
currentHeader.substring(0, currentHeader.indexOf('(')) +
currentHeader.substring(currentHeader.lastIndexOf(')') + 1)
).trim();
if (
header.children.length === 1 &&
['INS', 'DEL', 'MARK'].includes(header.children[0].tagName)
) {
header.children[0].innerHTML = header.children[0].innerHTML.trim();
}
} else if (formattedHeader != null) {
header.innerHTML = formattedHeader;
}
Expand Down
41 changes: 34 additions & 7 deletions src/header-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,19 @@ export function parseStructuredHeaderH1(
): { name: string | null; formattedHeader: string | null; formattedParams: string | null } {
// parsing is intentionally permissive; the linter can do stricter checks
// TODO have the linter do checks

let wrapper = null;
let headerText = header.innerHTML;
let beforeContents = 0;
const headerWrapperMatch = headerText.match(
/^(?<beforeContents>\s*<(?<tag>ins|del|mark)>)(?<contents>.*)<\/\k<tag>>\s*$/is
);
if (headerWrapperMatch != null) {
wrapper = headerWrapperMatch.groups!.tag;
headerText = headerWrapperMatch.groups!.contents;
beforeContents = headerWrapperMatch.groups!.beforeContents.length;
}

const prefix = headerText.match(/^\s*(Static|Runtime) Semantics:\s*/);
if (prefix != null) {
headerText = headerText.substring(prefix[0].length);
Expand All @@ -28,7 +40,7 @@ export function parseStructuredHeaderH1(
return { name: null, formattedHeader: null, formattedParams: null };
}

type Param = { name: string; type: string | null };
type Param = { name: string; type: string | null; wrapper: string | null };
const name = parsed.groups!.name;
let paramText = parsed.groups!.params ?? '';
const params: Array<Param> = [];
Expand All @@ -42,13 +54,20 @@ export function parseStructuredHeaderH1(
let offset = 0;
for (const line of paramLines) {
offset += line.length;
const chunk = line.trim();
let chunk = line.trim();
if (chunk === '') {
continue;
}
const wrapperMatch = chunk.match(/^<(ins|del|mark)>(.*)<\/\1>$/i);
let paramWrapper = null;
if (wrapperMatch != null) {
paramWrapper = wrapperMatch[1];
chunk = wrapperMatch[2];
}
++index;
function getParameterOffset() {
return (
beforeContents +
(prefix?.[0].length ?? 0) +
parsed!.groups!.beforeParams.length +
1 + // `beforeParams` does not include the leading `(`
Expand Down Expand Up @@ -96,10 +115,13 @@ export function parseStructuredHeaderH1(
(optional ? optionalParams : params).push({
name: paramName,
type: paramType === 'unknown' ? null : paramType,
wrapper: paramWrapper,
});
}
const formattedPrefix = prefix == null ? '' : prefix[0].trim() + ' ';
formattedHeader = `${formattedPrefix}${name} (${params.map(n => ' ' + n.name).join(',')}`;
// prettier-ignore
const printParam = (p: Param) => ` ${p.wrapper == null ? '' : `<${p.wrapper}>`}${p.name}${p.wrapper == null ? '' : `</${p.wrapper}>`}`;
formattedHeader = `${formattedPrefix}${name} (${params.map(printParam).join(',')}`;
if (optionalParams.length > 0) {
formattedHeader +=
optionalParams
Expand Down Expand Up @@ -134,17 +156,18 @@ export function parseStructuredHeaderH1(
break;
}
paramText = text;
(optional ? optionalParams : params).push({ name: match![1], type: null });
(optional ? optionalParams : params).push({ name: match![1], type: null, wrapper: null });
({ success, text } = eat(paramText, /^(\s*\])+|,/));
if (success) {
paramText = text;
}
}
}

const printParam = (p: Param) => `${p.name}${p.type == null ? '' : ` (${p.type})`}`;
const paramsWithTypes = params.map(printParam);
const optionalParamsWithTypes = optionalParams.map(printParam);
// prettier-ignore
const printParamWithType = (p: Param) => `${p.wrapper == null ? '' : `<${p.wrapper}>`}${p.name}${p.type == null ? '' : ` (${p.type})`}${p.wrapper == null ? '' : `</${p.wrapper}>`}`;
const paramsWithTypes = params.map(printParamWithType);
const optionalParamsWithTypes = optionalParams.map(printParamWithType);
let formattedParams = '';
if (params.length === 0 && optionalParams.length === 0) {
formattedParams = 'no arguments';
Expand All @@ -165,6 +188,10 @@ export function parseStructuredHeaderH1(
}
}

if (formattedHeader != null && wrapper != null) {
formattedHeader = `<${wrapper}>${formattedHeader}</${wrapper}>`;
}

return { name, formattedHeader, formattedParams };
}

Expand Down
18 changes: 15 additions & 3 deletions test/baselines/generated-reference/structured-headers.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,27 @@ <h1><span class="secnum">1</span> ExampleAO ( param [ , param2 ] )</h1>
<emu-alg><ol><li>Algorithm steps go here.</li></ol></emu-alg>
</emu-clause>

<emu-clause id="sec-exampleao2" type="abstract operation" aoid="ExampleAO2">
<h1><span class="secnum">2</span> <ins>ExampleAO2 ( param )</ins></h1>
<p>The abstract operation ExampleAO2 takes argument param (an <emu-xref href="#integer"><a href="https://tc39.es/ecma262/#integer">integer</a></emu-xref>). 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-exampleao3" type="abstract operation" aoid="ExampleAO3">
<h1><span class="secnum">3</span> ExampleAO3 ( <del>param</del> )</h1>
<p>The abstract operation ExampleAO3 takes argument <del>param (an <emu-xref href="#integer"><a href="https://tc39.es/ecma262/#integer">integer</a></emu-xref>)</del>. 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>
<h1><span class="secnum">4</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>
<h1><span class="secnum">4.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>
Expand All @@ -33,7 +45,7 @@ <h1><span class="secnum">2.1</span> IsThis</h1>
<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>
<h1><span class="secnum">4.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>
Expand Down
26 changes: 26 additions & 0 deletions test/baselines/sources/structured-headers.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,32 @@ <h1>
</emu-alg>
</emu-clause>

<emu-clause id="sec-exampleao2" type="abstract operation">
<h1>
<ins>ExampleAO2 (
param : an integer,
)</ins>
</h1>
<dl class='header'>
</dl>
<emu-alg>
1. Algorithm steps go here.
</emu-alg>
</emu-clause>

<emu-clause id="sec-exampleao3" type="abstract operation">
<h1>
ExampleAO3 (
<del>param : an integer,</del>
)
</h1>
<dl class='header'>
</dl>
<emu-alg>
1. Algorithm steps go here.
</emu-alg>
</emu-clause>

<emu-clause id="sec-example-grammar">
<h1>This and That</h1>
<emu-grammar type="definition">
Expand Down

0 comments on commit f5540e4

Please sign in to comment.