Skip to content

Commit

Permalink
add support for return types in AO header DSL (#363)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelficarra authored Sep 16, 2021
1 parent f00e0e1 commit 57501c9
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 10 deletions.
6 changes: 5 additions & 1 deletion src/Clause.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,10 @@ export default class Clause extends Builder {

const type = this.node.getAttribute('type');

const { name, formattedHeader, formattedParams } = parseStructuredHeaderH1(this.spec, header);
const { name, formattedHeader, formattedParams, formattedReturnType } = parseStructuredHeaderH1(
this.spec,
header
);
if (type === 'numeric method' && name != null && !name.includes('::')) {
this.spec.warn({
type: 'contents',
Expand Down Expand Up @@ -126,6 +129,7 @@ export default class Clause extends Builder {
type,
name ?? 'UNKNOWN',
formattedParams ?? 'UNPARSEABLE ARGUMENTS',
formattedReturnType,
_for,
description
);
Expand Down
48 changes: 39 additions & 9 deletions src/header-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { offsetToLineAndColumn } from './utils';
export function parseStructuredHeaderH1(
spec: Spec,
header: Element
): { name: string | null; formattedHeader: string | null; formattedParams: string | null } {
): {
name: string | null;
formattedHeader: string | null;
formattedParams: string | null;
formattedReturnType: string | null;
} {
// parsing is intentionally permissive; the linter can do stricter checks
// TODO have the linter do checks

Expand All @@ -26,7 +31,7 @@ export function parseStructuredHeaderH1(
}

const parsed = headerText.match(
/^(?<beforeParams>\s*(?<name>[^(\s]+)\s*)(?:\((?<params>.*)\)\s*)?$/s
/^(?<beforeParams>\s*(?<name>[^(\s]+)\s*)(?:\((?<params>[^)]*)\)(?:\s*:(?<returnType>.*))?\s*)?$/s
);
if (parsed == null) {
spec.warn({
Expand All @@ -37,12 +42,32 @@ export function parseStructuredHeaderH1(
nodeRelativeColumn: 1,
nodeRelativeLine: 1,
});
return { name: null, formattedHeader: null, formattedParams: null };
return { name: null, formattedHeader: null, formattedParams: null, formattedReturnType: null };
}

type Param = { name: string; type: string | null; wrapper: string | null };
const name = parsed.groups!.name;
let paramText = parsed.groups!.params ?? '';
const returnType = parsed.groups!.returnType?.trim() ?? null;

if (returnType === '') {
const { column, line } = offsetToLineAndColumn(
header.innerHTML,
beforeContents +
(prefix?.[0].length ?? 0) +
(headerText.length - headerText.match(/\s*$/)![0].length) -
1
);
spec.warn({
type: 'contents',
ruleId: 'header-format',
message: `if a return type is given, it must not be empty`,
node: header,
nodeRelativeColumn: column,
nodeRelativeLine: line,
});
}

const params: Array<Param> = [];
const optionalParams: Array<Param> = [];
let formattedHeader = null;
Expand Down Expand Up @@ -192,7 +217,7 @@ export function parseStructuredHeaderH1(
formattedHeader = `<${wrapper}>${formattedHeader}</${wrapper}>`;
}

return { name, formattedHeader, formattedParams };
return { name, formattedHeader, formattedParams, formattedReturnType: returnType };
}

export function parseStructuredHeaderDl(
Expand Down Expand Up @@ -290,6 +315,7 @@ export function formatPreamble(
type: string | null,
name: string,
formattedParams: string,
formattedReturnType: string | null,
_for: Element | null,
description: Element | null
): Array<Element> {
Expand All @@ -300,20 +326,20 @@ export function formatPreamble(
case 'numeric method':
case 'abstract operation': {
// TODO tests (for each type of parametered thing) which have HTML in the parameter type
para.innerHTML += `The abstract operation ${name} takes ${formattedParams}.`;
para.innerHTML += `The abstract operation ${name}`;
break;
}
case 'host-defined abstract operation': {
para.innerHTML += `The host-defined abstract operation ${name} takes ${formattedParams}.`;
para.innerHTML += `The host-defined abstract operation ${name}`;
break;
}
case 'implementation-defined abstract operation': {
para.innerHTML += `The implementation-defined abstract operation ${name} takes ${formattedParams}.`;
para.innerHTML += `The implementation-defined abstract operation ${name}`;
break;
}
case 'sdo':
case 'syntax-directed operation': {
para.innerHTML += `The syntax-directed operation ${name} takes ${formattedParams}.`;
para.innerHTML += `The syntax-directed operation ${name}`;
break;
}
case 'internal method':
Expand All @@ -330,7 +356,6 @@ export function formatPreamble(
_for = spec.doc.createElement('div');
}
para.append(`The ${name} ${type} of `, ..._for.childNodes);
para.innerHTML += ` takes ${formattedParams}.`;
break;
}
default: {
Expand All @@ -352,6 +377,11 @@ export function formatPreamble(
}
}
}
para.innerHTML += ` takes ${formattedParams}`;
if (formattedReturnType != null) {
para.innerHTML += ` and returns ${formattedReturnType}`;
}
para.innerHTML += '.';
if (description != null) {
const isJustElements = [...description.childNodes].every(
n => n.nodeType === 1 || (n.nodeType === 3 && n.textContent?.trim() === '')
Expand Down
6 changes: 6 additions & 0 deletions test/baselines/generated-reference/structured-headers.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,10 @@ <h1><span class="secnum">4.2</span> IsThat</h1>
<emu-alg><ol><li>Return <emu-val>true</emu-val>.</li></ol></emu-alg>
</emu-clause>
</emu-clause>

<emu-clause id="sec-example-return-type" type="abstract operation" aoid="ExampleAO3">
<h1><span class="secnum">5</span> ExampleAO3 ( param )</h1>
<p>The abstract operation <emu-xref aoid="ExampleAO3" id="_ref_0"><a href="#sec-example-return-type">ExampleAO3</a></emu-xref> takes argument param (an <emu-xref href="#integer"><a href="https://tc39.es/ecma262/#integer">integer</a></emu-xref>) and returns the return type. It performs the following steps when called:</p>
<emu-alg><ol><li>Algorithm steps go here.</li></ol></emu-alg>
</emu-clause>
</div></body>
13 changes: 13 additions & 0 deletions test/baselines/sources/structured-headers.html
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,16 @@ <h1>
</emu-alg>
</emu-clause>
</emu-clause>

<emu-clause id="sec-example-return-type" type="abstract operation">
<h1>
ExampleAO3 (
param : an integer,
): the return type
</h1>
<dl class='header'>
</dl>
<emu-alg>
1. Algorithm steps go here.
</emu-alg>
</emu-clause>
18 changes: 18 additions & 0 deletions test/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -794,4 +794,22 @@ ${M} </pre>
);
});
});

it('empty return types', async () => {
await assertError(
positioned`
<emu-clause id="sec-test" type="abstract operation">
<h1>Foo ( )${M}:</h1>
<dl class='header'>
</dl>
</emu-clause>
`,
{
ruleId: 'header-format',
nodeType: 'h1',
message: 'if a return type is given, it must not be empty',
},
{ lintSpec: true }
);
});
});

0 comments on commit 57501c9

Please sign in to comment.