Skip to content

Commit

Permalink
support for xrefs to steps and labeled algorithms
Browse files Browse the repository at this point in the history
  • Loading branch information
bakkot committed Jun 17, 2020
1 parent de7431f commit 5690549
Show file tree
Hide file tree
Showing 12 changed files with 361 additions and 32 deletions.
45 changes: 36 additions & 9 deletions css/elements.css
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,43 @@ emu-const {
emu-val {
font-weight: bold;
}
emu-alg ol, emu-alg ol ol ol ol {
list-style-type: decimal;
}

emu-alg ol ol, emu-alg ol ol ol ol ol {
list-style-type: lower-alpha;
}

emu-alg ol ol ol, ol ol ol ol ol ol {
list-style-type: lower-roman;
/* depth 1 */
emu-alg ol,
/* depth 4 */
emu-alg ol ol ol ol,
emu-alg ol.nested-thrice,
emu-alg ol.nested-twice ol,
emu-alg ol.nested-once ol ol {
list-style-type: decimal;
}

/* depth 2 */
emu-alg ol ol,
emu-alg ol.nested-once,
/* depth 5 */
emu-alg ol ol ol ol ol,
emu-alg ol.nested-four-times,
emu-alg ol.nested-thrice ol,
emu-alg ol.nested-twice ol ol,
emu-alg ol.nested-once ol ol ol {
list-style-type: lower-alpha;
}

/* depth 3 */
emu-alg ol ol ol,
emu-alg ol.nested-twice,
emu-alg ol.nested-once ol,
/* depth 6 */
emu-alg ol ol ol ol ol ol,
emu-alg ol.nested-lots,
emu-alg ol.nested-four-times ol,
emu-alg ol.nested-thrice ol ol,
emu-alg ol.nested-twice ol ol ol,
emu-alg ol.nested-once ol ol ol ol,
/* depth 7+ */
emu-alg ol.nested-lots ol {
list-style-type: lower-roman;
}

emu-eqn {
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"dependencies": {
"bluebird": "^3.7.2",
"chalk": "^1.1.3",
"ecmarkdown": "^5.0.2",
"ecmarkdown": "^5.1.0",
"eslint": "^6.8.0",
"grammarkdown": "^2.2.0",
"highlight.js": "^9.17.1",
Expand Down
48 changes: 41 additions & 7 deletions spec/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/styles/solarized_light.min.css">
<title>Ecmarkup</title>
<pre class=metadata>
repository: https://github.com/bterlson/ecmarkup
repository: https://github.com/tc39/ecmarkup
assets: none
copyright: false
</pre>
<emu-biblio href="./biblio.json"></emu-biblio>
<emu-intro id="intro">
<h1>Ecmarkup</h1>
<p><a href="https://github.com/bterlson/ecmarkup">Ecmarkup</a> is a number of custom elements and a toolchain suitable for specifying semantics for ECMAScript and other programming languages.</p>
<p><a href="https://github.com/tc39/ecmarkup">Ecmarkup</a> is a number of custom elements and a toolchain suitable for specifying semantics for ECMAScript and other programming languages.</p>
<p>Ecmarkup offers the following conveniences (among many others):</p>
<ul>
<li><a href="https://github.com/domenic/ecmarkdown">Ecmarkdown</a> syntax for paragraphs and algorithms</li>
<li><a href="https://github.com/tc39/ecmarkdown">Ecmarkdown</a> syntax for paragraphs and algorithms</li>
<li><a href="https://github.com/rbuckton/grammarkdown">Grammarkdown</a> for specifying grammar</li>
<li>Terse markup for the authoring conventions used in ECMAScript specifications including clauses, notes, examples, figures, tables, and more.</li>
<li>Easy cross-references by element ID to clauses, abstract operations, examples, figures, tables, and etc. in the current document, ES6, and other specs.</li>
Expand All @@ -24,7 +24,7 @@ <h1>Ecmarkup</h1>
<li>Auto-links abstract operations and terms based on name with support for external bibliographies for cross-referencing between specs</li>
<li>Source code syntax highlighting inside pre code blocks</li>
</ul>
<p>This document is itself written using Ecmarkup. Its source can be viewed <a href="https://github.com/bterlson/ecmarkup/blob/master/spec/index.html">on github</a>.</p>
<p>This document is itself written using Ecmarkup. Its source can be viewed <a href="https://github.com/tc39/ecmarkup/blob/master/spec/index.html">on github</a>.</p>
</emu-intro>

<emu-clause id="getting-started">
Expand Down Expand Up @@ -93,7 +93,7 @@ <h1>Editorial Conventions</h1>
</table>
</emu-table>

<p>There are also a number of elements and corresponding Ecmarkdown syntax to give inline styles to various parts of your document in accordance with ECMAScript conventions. More detail on Ecmarkdown syntax can be found <a href="https://github.com/domenic/ecmarkdown">on its readme</a>.</p>
<p>There are also a number of elements and corresponding Ecmarkdown syntax to give inline styles to various parts of your document in accordance with ECMAScript conventions. More detail on Ecmarkdown syntax can be found <a href="https://github.com/tc39/ecmarkdown">on its readme</a>.</p>
<emu-table id="emd-overview">
<emu-caption>Inline styles/conventions</emu-caption>
<table>
Expand Down Expand Up @@ -198,6 +198,36 @@ <h3>Result</h3>
1. let _recurse_ be the result of calling EmuAlgExample(`true`)
2. Return the result of evaluating this |NonTerminalProduction|
</emu-alg>

<h2>Replacement algorithms</h2>
<p>Algorithms may be specified to replace a labeled step, in which case the algorithm will adopt the numbering of that step. For example:</p>

<h3>Element</h3>
<pre><code class="language-html">
&lt;emu-alg>
1. Step.
1. Step 2.
1. [label="replace-me"] Replaced.
1. Substep.
&lt;/emu-alg>
&lt;p>The following is an alernative definition of step &lt;emu-xref href="#step-replace-me">&lt;/emu-xref>.&lt;/p>
&lt;emu-alg replaces-step="replace-me">
1. Replacement.
1. Substep.
&lt;/emu-alg>
</code></pre>
<h3>Result</h3>
<emu-alg>
1. Step.
1. Step 2.
1. [label="replace-me"] Replaced.
1. Substep.
</emu-alg>
<p>The following is an alernative definition of step <emu-xref href="#step-replace-me"></emu-xref>.</p>
<emu-alg replaces-step="replace-me">
1. Replacement.
1. Substep.
</emu-alg>
</emu-clause>

<emu-clause id="emu-eqn">
Expand All @@ -223,8 +253,8 @@ <h2>Example</h2>

<emu-clause id="emu-xref">
<h1>emu-xref</h1>
<p>Cross-reference another clause, production, note, example, or abstract operation. If the text content of this element is empty, a suitable default is used. The `title` attribute controls this default - when present, clauses are referred to using their title rather than number. This also applies to examples which are indexed first by their containing clause and then their example number.</p>
<p>Cross-references to an id check for clauses, productions, and examples in this order. For each type, the local document is consulted before looking for external sources including the default ES6 biblio.</p>
<p>Cross-reference another clause, production, note, example, abstract operation, or labeled step. If the text content of this element is empty, a suitable default is used. The `title` attribute controls this default - when present, clauses are referred to using their title rather than number. This also applies to examples which are indexed first by their containing clause and then their example number.</p>
<p>Cross-references to an id check for clauses, productions, examples, and steps in this order. For each type, the local document is consulted before looking for external sources including the default ES6 biblio.</p>

<h2>Attributes</h2>
<p><b>href:</b> Optional: URL of the target clause, production, or example to cross-reference.</p>
Expand All @@ -239,13 +269,17 @@ <h2>Example</h2>
&lt;p>See &lt;emu-xref href="#emu-note" title>&lt;/emu-xref> for information on the emu-note element.&lt;/p>
&lt;p>The &lt;emu-xref href="#emu-biblio">biblio element&lt;/emu-xref> supports xref to external specs.&lt;/p>
&lt;p>&lt;emu-xref aoid="Get">&lt;/emu-xref> is an abstract operation from ES6&lt;/p>
&lt;p>&lt;emu-alg>1. [label="example-step-label"] Example labeled step.&lt;/emu-alg>&lt;/p>
&lt;p>You can reference step &lt;emu-xref href="#step-example-step-label">&lt;/emu-xref> like this&lt;/p>
</code></pre>

<b>Result</b>
<p>The clause element is specified in <emu-xref href="#emu-clause"></emu-xref>.</p>
<p>See <emu-xref href="#emu-note" title></emu-xref> for information on the emu-note element.</p>
<p>The <emu-xref href="#emu-biblio">biblio element</emu-xref> will eventually support xref to external specs.</p>
<p><emu-xref aoid="Get"></emu-xref> is an abstract operation from ES6</p>
<p><emu-alg>1. [label="example-step-label"] Example labeled step.</emu-alg></p>
<p>You can reference step <emu-xref href="#step-example-step-label"></emu-xref> like this</p>
</emu-clause>

<emu-clause id="emu-not-ref" namespace="emu-not-ref">
Expand Down
59 changes: 50 additions & 9 deletions src/Algorithm.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { Context } from './Context';
import type { StepBiblioEntry } from './Biblio';

import { logWarning } from './utils';
import Builder from './Builder';
import * as emd from 'ecmarkdown';

Expand All @@ -9,22 +11,61 @@ export default class Algorithm extends Builder {
context.inAlg = true;
const { node } = context;

if ('ecmarkdownOut' in node) {
// i.e., we already parsed this during the lint phase
// @ts-ignore
node.innerHTML = node.ecmarkdownOut.replace(/((?:\s+|>)[!?])\s+(\w+\s*\()/g, '$1&nbsp;$2');
return;
}
// prettier-ignore
const rawHtml =
'ecmarkdownOut' in node
? (node as any).ecmarkdownOut
: emd.algorithm(node.innerHTML);

// replace spaces after !/? with &nbsp; to prevent bad line breaking
const html = emd
.algorithm(node.innerHTML)
.replace(/((?:\s+|>)[!?])\s+(\w+\s*\()/g, '$1&nbsp;$2');
const html = rawHtml.replace(/((?:\s+|>)[!?])\s+(\w+\s*\()/g, '$1&nbsp;$2');
node.innerHTML = html;

let labeledStepEntries: StepBiblioEntry[] = [];
let replaces = node.getAttribute('replaces-step');
if (replaces) {
replaces = 'step-' + replaces;
context.spec.replacementAlgorithms.push({
element: node,
target: replaces,
});
context.spec.replacementAlgorithmToContainedLabeledStepEntries.set(node, labeledStepEntries);
}

let labeledSteps = Array.from(node.querySelectorAll('li[id]'));
if (replaces && labeledSteps.length > 0 && node.firstElementChild!.children.length > 1) {
logWarning(
'You should not label a step in a replacement algorithm which has multiple top-level steps because the resulting step number could be ambiguous.'
);
}

for (const step of labeledSteps) {
let entry: StepBiblioEntry = {
type: 'step',
id: step.id,
stepNumbers: getStepNumbers(step as Element),
referencingIds: [],
};
context.spec.biblio.add(entry);
if (replaces) {
// The biblio entries for labeled steps in replacement algorithms will be modified in-place by a subsequent pass
labeledStepEntries.push(entry);
context.spec.labeledStepsToBeRectified.add(step.id);
}
}
}

static exit(context: Context) {
context.inAlg = false;
}
static elements = ['EMU-ALG'];
}

function getStepNumbers(item: Element) {
let counts = [];
while (item.parentElement?.tagName === 'OL') {
counts.unshift(1 + Array.from(item.parentElement.children).indexOf(item));
item = item.parentElement.parentElement!;
}
return counts;
}
14 changes: 13 additions & 1 deletion src/Biblio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ export default class Biblio {
entry.namespace = ns;
env!.push(entry);
if (entry.id) {
if ({}.hasOwnProperty.call(this, entry.id)) {
throw new Error('Duplicate biblio entry ' + JSON.stringify(entry.id) + '.');
}
this._byId[entry.id] = entry;
}
}
Expand Down Expand Up @@ -263,12 +266,19 @@ export interface FigureBiblioEntry extends BiblioEntryBase {
caption?: string;
}

export interface StepBiblioEntry extends BiblioEntryBase {
type: 'step';
id: string;
stepNumbers: number[];
}

export type BiblioEntry =
| AlgorithmBiblioEntry
| ProductionBiblioEntry
| ClauseBiblioEntry
| TermBiblioEntry
| FigureBiblioEntry;
| FigureBiblioEntry
| StepBiblioEntry;

function dumpEnv(env: EnvRec) {
console.log('## ' + env._namespace);
Expand Down Expand Up @@ -308,6 +318,8 @@ function getKey(item: BiblioEntry) {
case 'example':
case 'note':
return item.caption;
case 'step':
return item.id;
default:
throw new Error("Can't get key for " + (<BiblioEntry>item).type);
}
Expand Down
Loading

0 comments on commit 5690549

Please sign in to comment.