diff --git a/src/Clause.ts b/src/Clause.ts index 7751deb6..103a96fd 100644 --- a/src/Clause.ts +++ b/src/Clause.ts @@ -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('::')) { @@ -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; } @@ -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', diff --git a/src/Spec.ts b/src/Spec.ts index e9af542e..e1e3130f 100644 --- a/src/Spec.ts +++ b/src/Spec.ts @@ -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) { diff --git a/src/header-parser.ts b/src/header-parser.ts index 1387ad8b..92c1c674 100644 --- a/src/header-parser.ts +++ b/src/header-parser.ts @@ -13,7 +13,9 @@ export function parseStructuredHeaderH1( headerText = headerText.substring(prefix[0].length); } - const parsed = headerText.match(/^(?\s*(?[^(\s]+)\s*\()(?.*)\)\s*$/s); + const parsed = headerText.match( + /^(?\s*(?[^(\s]+)\s*)(?:\((?.*)\)\s*)?$/s + ); if (parsed == null) { spec.warn({ type: 'contents', @@ -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 = []; const optionalParams: Array = []; let formattedHeader = null; @@ -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 @@ -265,7 +268,8 @@ export function formatPreamble( ): Array { 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 @@ -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) { @@ -326,15 +335,22 @@ 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 @@ -342,7 +358,7 @@ export function formatPreamble( next.parentElement!.insertBefore(whitespace.cloneNode(), next); } } else { - para.append(' It performs the following steps when called:'); + para.append(' ' + lastSentence); } } return paras; diff --git a/test/baselines/generated-reference/structured-headers.html b/test/baselines/generated-reference/structured-headers.html index ab834fd7..10f72cc6 100644 --- a/test/baselines/generated-reference/structured-headers.html +++ b/test/baselines/generated-reference/structured-headers.html @@ -1,7 +1,7 @@
- +

1 ExampleAO ( param [ , param2 ] )

The abstract operation ExampleAO takes argument param (an integer) and optional argument param2 (a String). It is an example.

Note
@@ -10,4 +10,41 @@

1 ExampleAO ( param [ , param2 ] )

It performs the following steps when called:

  1. Algorithm steps go here.
+ + +

2 This and That

+ + PrimaryExpression : this + that + + + +

2.1 IsThis

+

The syntax-directed operation IsThis takes no arguments. It is an example. It is defined piecewise over the following productions:

+ + PrimaryExpression : this + + +
  1. Return true.
+ + PrimaryExpression : that + + +
  1. Return false.
+
+ +

2.2 IsThat

+

The syntax-directed operation IsThat takes argument ignored (an example). It is defined piecewise over the following productions:

+ + PrimaryExpression : this + + +
  1. Return false.
+ + PrimaryExpression : that + + +
  1. Return true.
+
+
\ No newline at end of file diff --git a/test/baselines/sources/structured-headers.html b/test/baselines/sources/structured-headers.html index de7b3346..9c11e202 100644 --- a/test/baselines/sources/structured-headers.html +++ b/test/baselines/sources/structured-headers.html @@ -22,3 +22,44 @@

1. Algorithm steps go here. + + +

This and That

+ + PrimaryExpression : + `this` + `that` + + +

IsThis

+
+
description
+
It is an example.
+
+ PrimaryExpression : `this` + + 1. Return *true*. + + PrimaryExpression : `that` + + 1. Return *false*. + +
+ +

+ IsThat ( + _ignored_: an example, + ) +

+
+
+ PrimaryExpression : `this` + + 1. Return *false*. + + PrimaryExpression : `that` + + 1. Return *true*. + +
+