Skip to content

Commit e6e10d7

Browse files
authored
add redefinition support to structured headers (#428)
1 parent df40811 commit e6e10d7

File tree

7 files changed

+179
-32
lines changed

7 files changed

+179
-32
lines changed

spec/index.html

+1
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ <h1>Structured Headers</h1>
196196
<ul>
197197
<li><b>description:</b> An explanation of the operation's behaviour.</li>
198198
<li><b>for:</b> The type of value to which a clause of type "concrete method" or "internal method" applies.</li>
199+
<li><b>redefinition:</b> If "true", the name of the operation will not automatically link (i.e., it will not automatically be given an aoid).</li>
199200
</ul>
200201
</li>
201202
</ol>

src/Clause.ts

+30-23
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,12 @@ export default class Clause extends Builder {
182182
header.innerHTML = formattedHeader;
183183
}
184184

185-
const { description, for: _for, effects } = parseStructuredHeaderDl(this.spec, type, dl);
185+
const {
186+
description,
187+
for: _for,
188+
effects,
189+
redefinition,
190+
} = parseStructuredHeaderDl(this.spec, type, dl);
186191

187192
const paras = formatPreamble(
188193
this.spec,
@@ -197,28 +202,30 @@ export default class Clause extends Builder {
197202
);
198203
dl.replaceWith(...paras);
199204

200-
if (this.node.hasAttribute('aoid')) {
201-
this.spec.warn({
202-
type: 'attr',
203-
ruleId: 'header-format',
204-
message: `nodes with structured headers should not include an AOID`,
205-
node: this.node,
206-
attr: 'aoid',
207-
});
208-
} else if (
209-
name != null &&
210-
type != null &&
211-
[
212-
'abstract operation',
213-
'sdo',
214-
'syntax-directed operation',
215-
'host-defined abstract operation',
216-
'implementation-defined abstract operation',
217-
'numeric method',
218-
].includes(type)
219-
) {
220-
this.node.setAttribute('aoid', name);
221-
this.aoid = name;
205+
if (!redefinition) {
206+
if (this.node.hasAttribute('aoid')) {
207+
this.spec.warn({
208+
type: 'attr',
209+
ruleId: 'header-format',
210+
message: `nodes with structured headers should not include an AOID`,
211+
node: this.node,
212+
attr: 'aoid',
213+
});
214+
} else if (
215+
name != null &&
216+
type != null &&
217+
[
218+
'abstract operation',
219+
'sdo',
220+
'syntax-directed operation',
221+
'host-defined abstract operation',
222+
'implementation-defined abstract operation',
223+
'numeric method',
224+
].includes(type)
225+
) {
226+
this.node.setAttribute('aoid', name);
227+
this.aoid = name;
228+
}
222229
}
223230

224231
this.effects.push(...effects);

src/header-parser.ts

+31-2
Original file line numberDiff line numberDiff line change
@@ -401,9 +401,10 @@ export function parseStructuredHeaderDl(
401401
spec: Spec,
402402
type: string | null,
403403
dl: Element
404-
): { description: Element | null; for: Element | null; effects: string[] } {
404+
): { description: Element | null; for: Element | null; effects: string[]; redefinition: boolean } {
405405
let description = null;
406406
let _for = null;
407+
let redefinition: boolean | null = null;
407408
let effects: string[] = [];
408409
for (let i = 0; i < dl.children.length; ++i) {
409410
const dt = dl.children[i];
@@ -474,6 +475,34 @@ export function parseStructuredHeaderDl(
474475
}
475476
break;
476477
}
478+
case 'redefinition': {
479+
if (redefinition != null) {
480+
spec.warn({
481+
type: 'node',
482+
ruleId: 'header-format',
483+
message: `duplicate "redefinition" attribute`,
484+
node: dt,
485+
});
486+
}
487+
const contents = (dd.textContent ?? '').trim();
488+
if (contents === 'true') {
489+
redefinition = true;
490+
} else if (contents === 'false') {
491+
redefinition = false;
492+
} else {
493+
spec.warn({
494+
type: 'contents',
495+
ruleId: 'header-format',
496+
message: `unknown value for "redefinition" attribute (expected "true" or "false", got ${JSON.stringify(
497+
contents
498+
)})`,
499+
node: dd,
500+
nodeRelativeLine: 1,
501+
nodeRelativeColumn: 1,
502+
});
503+
}
504+
break;
505+
}
477506
case '': {
478507
spec.warn({
479508
type: 'node',
@@ -494,7 +523,7 @@ export function parseStructuredHeaderDl(
494523
}
495524
}
496525
}
497-
return { description, for: _for, effects };
526+
return { description, for: _for, effects, redefinition: redefinition ?? false };
498527
}
499528

500529
export function formatPreamble(

test/baselines/generated-reference/structured-headers.html

+13-7
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,26 @@ <h1><span class="secnum">1</span> ExampleAO ( param [ , param2 ] )</h1>
1717
<emu-alg><ol><li>Algorithm steps go here.</li></ol></emu-alg>
1818
</emu-clause>
1919

20+
<emu-clause id="sec-exampleao" type="abstract operation">
21+
<h1><span class="secnum">2</span> ExampleAO ( )</h1>
22+
<p>The abstract operation ExampleAO takes no arguments. It redefines an existing algorithm. It performs the following steps when called:</p>
23+
<emu-alg><ol><li>Algorithm steps go here.</li></ol></emu-alg>
24+
</emu-clause>
25+
2026
<emu-clause id="sec-exampleao2" type="abstract operation" aoid="ExampleAO2">
21-
<h1><span class="secnum">2</span> <ins>ExampleAO2 ( param )</ins></h1>
27+
<h1><span class="secnum">3</span> <ins>ExampleAO2 ( param )</ins></h1>
2228
<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>
2329
<emu-alg><ol><li>Algorithm steps go here.</li></ol></emu-alg>
2430
</emu-clause>
2531

2632
<emu-clause id="sec-exampleao3" type="abstract operation" aoid="ExampleAO3">
27-
<h1><span class="secnum">3</span> ExampleAO3 ( <del>param</del> )</h1>
33+
<h1><span class="secnum">4</span> ExampleAO3 ( <del>param</del> )</h1>
2834
<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>
2935
<emu-alg><ol><li>Algorithm steps go here.</li></ol></emu-alg>
3036
</emu-clause>
3137

3238
<emu-clause id="sec-example-grammar">
33-
<h1><span class="secnum">4</span> This and That</h1>
39+
<h1><span class="secnum">5</span> This and That</h1>
3440
<emu-grammar type="definition"><emu-production name="PrimaryExpression" id="prod-PrimaryExpression">
3541
<emu-nt><a href="#prod-PrimaryExpression">PrimaryExpression</a></emu-nt> <emu-geq>:</emu-geq> <emu-rhs a="jo4mwtvh" id="prod-6iVAqhjf">
3642
<emu-t>this</emu-t>
@@ -41,7 +47,7 @@ <h1><span class="secnum">4</span> This and That</h1>
4147
</emu-production>
4248
</emu-grammar>
4349
<emu-clause id="sec-isthis" type="sdo" aoid="IsThis">
44-
<h1><span class="secnum">4.1</span> IsThis</h1>
50+
<h1><span class="secnum">5.1</span> IsThis</h1>
4551
<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>
4652
<emu-grammar><emu-production name="PrimaryExpression" collapsed="">
4753
<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>
@@ -55,7 +61,7 @@ <h1><span class="secnum">4.1</span> IsThis</h1>
5561
<emu-alg><ol><li>Return <emu-val>false</emu-val>.</li></ol></emu-alg>
5662
</emu-clause>
5763
<emu-clause id="sec-isthat" type="sdo" aoid="IsThat">
58-
<h1><span class="secnum">4.2</span> IsThat</h1>
64+
<h1><span class="secnum">5.2</span> IsThat</h1>
5965
<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>
6066
<emu-grammar><emu-production name="PrimaryExpression" collapsed="">
6167
<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>
@@ -71,13 +77,13 @@ <h1><span class="secnum">4.2</span> IsThat</h1>
7177
</emu-clause>
7278

7379
<emu-clause id="sec-example-return-type" type="abstract operation" aoid="ExampleAO3">
74-
<h1><span class="secnum">5</span> ExampleAO3 ( param )</h1>
80+
<h1><span class="secnum">6</span> ExampleAO3 ( param )</h1>
7581
<p>The abstract operation <emu-xref aoid="ExampleAO3" id="_ref_0"><a href="#sec-exampleao3">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>
7682
<emu-alg><ol><li>Algorithm steps go here.</li></ol></emu-alg>
7783
</emu-clause>
7884

7985
<emu-clause id="sec-example-single-line-return-type" type="abstract operation" aoid="ExampleA4">
80-
<h1><span class="secnum">6</span> ExampleA4 ( )</h1>
86+
<h1><span class="secnum">7</span> ExampleA4 ( )</h1>
8187
<p>The abstract operation ExampleA4 takes no arguments and returns <emu-val>false</emu-val>. It performs the following steps when called:</p>
8288
<emu-alg><ol><li>Return <emu-val>false</emu-val>.</li></ol></emu-alg>
8389
</emu-clause>

test/baselines/sources/structured-headers.html

+13
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,19 @@ <h1>
2323
</emu-alg>
2424
</emu-clause>
2525

26+
<emu-clause id="sec-exampleao" type="abstract operation">
27+
<h1>ExampleAO ( )</h1>
28+
<dl class='header'>
29+
<dt>description</dt>
30+
<dd>It redefines an existing algorithm.</dd>
31+
<dt>redefinition</dt>
32+
<dd>true</dd>
33+
</dl>
34+
<emu-alg>
35+
1. Algorithm steps go here.
36+
</emu-alg>
37+
</emu-clause>
38+
2639
<emu-clause id="sec-exampleao2" type="abstract operation">
2740
<h1>
2841
<ins>ExampleAO2 (

test/errors.js

+41
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,26 @@ ${M} </pre>
660660
},
661661
{ lintSpec: true }
662662
);
663+
664+
await assertError(
665+
positioned`
666+
<emu-clause id="sec-test" type="abstract operation">
667+
<h1>Example ( )</h1>
668+
<dl class='header'>
669+
<dt>redefinition</dt>
670+
<dd>true</dd>
671+
${M}<dt>redefinition</dt>
672+
<dd>false</dd>
673+
</dl>
674+
</emu-clause>
675+
`,
676+
{
677+
ruleId: 'header-format',
678+
nodeType: 'dt',
679+
message: 'duplicate "redefinition" attribute',
680+
},
681+
{ lintSpec: true }
682+
);
663683
});
664684

665685
it('"for" for AO', async () => {
@@ -700,6 +720,27 @@ ${M} </pre>
700720
);
701721
});
702722

723+
it('unknown value for redefinition', async () => {
724+
await assertError(
725+
positioned`
726+
<emu-clause id="sec-test" type="abstract operation">
727+
<h1>Example ( )</h1>
728+
<dl class='header'>
729+
<dt>redefinition</dt>
730+
<dd>${M}not true</dd>
731+
</dl>
732+
</emu-clause>
733+
`,
734+
{
735+
ruleId: 'header-format',
736+
nodeType: 'dd',
737+
message:
738+
'unknown value for "redefinition" attribute (expected "true" or "false", got "not true")',
739+
},
740+
{ lintSpec: true }
741+
);
742+
});
743+
703744
it('empty attribute', async () => {
704745
await assertError(
705746
positioned`

test/lint.js

+50
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ describe('linting whole program', () => {
504504
}
505505
);
506506
});
507+
507508
it('for clauses', async () => {
508509
await assertLint(
509510
positioned`
@@ -562,5 +563,54 @@ describe('linting whole program', () => {
562563
}
563564
);
564565
});
566+
567+
it('is present when using structured headers without explict AOIDs', async () => {
568+
await assertLint(
569+
positioned`
570+
<emu-clause id="sec-example" type="abstract operation">
571+
<h1>Foo ( _param_ )</h1>
572+
<dl class='header'>
573+
<dt>description</dt>
574+
<dd>It is an example.</dd>
575+
</dl>
576+
</emu-clause>
577+
578+
${M}<emu-clause id="sec-example-redef" type="abstract operation">
579+
<h1>Foo ( _param_ )</h1>
580+
<dl class='header'>
581+
<dt>description</dt>
582+
<dd>It redefines an existing algorithm.</dd>
583+
</dl>
584+
</emu-clause>
585+
`,
586+
{
587+
ruleId: 'duplicate-definition',
588+
nodeType: 'emu-clause',
589+
message: 'duplicate definition "Foo"',
590+
}
591+
);
592+
});
593+
594+
it('is suppressed when using `redefinition: true`', async () => {
595+
await assertLintFree(`
596+
<emu-clause id="sec-example" type="abstract operation">
597+
<h1>Foo ( _param_ )</h1>
598+
<dl class='header'>
599+
<dt>description</dt>
600+
<dd>It is an example.</dd>
601+
</dl>
602+
</emu-clause>
603+
604+
<emu-clause id="sec-example-redef" type="abstract operation">
605+
<h1>Foo ( _param_ )</h1>
606+
<dl class='header'>
607+
<dt>description</dt>
608+
<dd>It redefines an existing algorithm.</dd>
609+
<dt>redefinition</dt>
610+
<dd>true</dd>
611+
</dl>
612+
</emu-clause>
613+
`);
614+
});
565615
});
566616
});

0 commit comments

Comments
 (0)