1 | "use strict";
|
2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
4 | return new (P || (P = Promise))(function (resolve, reject) {
|
5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
8 | step((generator = generator.apply(thisArg, _arguments || [])).next());
|
9 | });
|
10 | };
|
11 | Object.defineProperty(exports, "__esModule", { value: true });
|
12 | const Builder_1 = require("./Builder");
|
13 | const header_parser_1 = require("./header-parser");
|
14 |
|
15 | class Clause extends Builder_1.default {
|
16 | constructor(spec, node, parent, number) {
|
17 | super(spec, node);
|
18 | this.parentClause = parent;
|
19 | this.id = node.getAttribute('id');
|
20 | this.number = number;
|
21 | this.subclauses = [];
|
22 | this.notes = [];
|
23 | this.editorNotes = [];
|
24 | this.examples = [];
|
25 | this.effects = [];
|
26 |
|
27 | let parentNamespace = spec.namespace;
|
28 | if (parent) {
|
29 | parentNamespace = parent.namespace;
|
30 | }
|
31 | if (node.hasAttribute('namespace')) {
|
32 | this.namespace = node.getAttribute('namespace');
|
33 | spec.biblio.createNamespace(this.namespace, parentNamespace);
|
34 | }
|
35 | else {
|
36 | this.namespace = parentNamespace;
|
37 | }
|
38 | this.aoid = node.getAttribute('aoid');
|
39 | if (this.aoid === '') {
|
40 |
|
41 | this.aoid = node.id;
|
42 | }
|
43 | this.type = node.getAttribute('type');
|
44 | if (this.type === '') {
|
45 | this.type = null;
|
46 | }
|
47 | let header = this.node.firstElementChild;
|
48 | while (header != null && header.tagName === 'SPAN' && header.children.length === 0) {
|
49 |
|
50 | header = header.nextElementSibling;
|
51 | }
|
52 | if (header == null) {
|
53 | this.spec.warn({
|
54 | type: 'node',
|
55 | ruleId: 'missing-header',
|
56 | message: `could not locate header element`,
|
57 | node: this.node,
|
58 | });
|
59 | header = null;
|
60 | }
|
61 | else if (header.tagName !== 'H1') {
|
62 | this.spec.warn({
|
63 | type: 'node',
|
64 | ruleId: 'missing-header',
|
65 | message: `could not locate header element; found <${header.tagName.toLowerCase()}> before any <h1>`,
|
66 | node: header,
|
67 | });
|
68 | header = null;
|
69 | }
|
70 | else {
|
71 | this.buildStructuredHeader(header);
|
72 | }
|
73 | this.header = header;
|
74 | }
|
75 | buildStructuredHeader(header) {
|
76 | const dl = header.nextElementSibling;
|
77 | if (dl == null || dl.tagName !== 'DL' || !dl.classList.contains('header')) {
|
78 | return;
|
79 | }
|
80 |
|
81 | const type = this.type;
|
82 | const { name, formattedHeader, formattedParams, formattedReturnType } = header_parser_1.parseStructuredHeaderH1(this.spec, header);
|
83 | if (type === 'numeric method' && name != null && !name.includes('::')) {
|
84 | this.spec.warn({
|
85 | type: 'contents',
|
86 | ruleId: 'numeric-method-for',
|
87 | message: 'numeric methods should be of the form `Type::operation`',
|
88 | node: header,
|
89 | nodeRelativeLine: 1,
|
90 | nodeRelativeColumn: 1,
|
91 | });
|
92 | }
|
93 | if (type === 'sdo' && (formattedHeader !== null && formattedHeader !== void 0 ? formattedHeader : header.innerHTML).includes('(')) {
|
94 |
|
95 | const currentHeader = formattedHeader !== null && formattedHeader !== void 0 ? formattedHeader : header.innerHTML;
|
96 | header.innerHTML = (currentHeader.substring(0, currentHeader.indexOf('(')) +
|
97 | currentHeader.substring(currentHeader.lastIndexOf(')') + 1)).trim();
|
98 | if (header.children.length === 1 &&
|
99 | ['INS', 'DEL', 'MARK'].includes(header.children[0].tagName)) {
|
100 | header.children[0].innerHTML = header.children[0].innerHTML.trim();
|
101 | }
|
102 | }
|
103 | else if (formattedHeader != null) {
|
104 | header.innerHTML = formattedHeader;
|
105 | }
|
106 | const { description, for: _for, effects } = header_parser_1.parseStructuredHeaderDl(this.spec, type, dl);
|
107 | const paras = header_parser_1.formatPreamble(this.spec, this.node, dl, type, name !== null && name !== void 0 ? name : 'UNKNOWN', formattedParams !== null && formattedParams !== void 0 ? formattedParams : 'UNPARSEABLE ARGUMENTS', formattedReturnType, _for, description);
|
108 | dl.replaceWith(...paras);
|
109 | if (this.node.hasAttribute('aoid')) {
|
110 | this.spec.warn({
|
111 | type: 'attr',
|
112 | ruleId: 'header-format',
|
113 | message: `nodes with structured headers should not include an AOID`,
|
114 | node: this.node,
|
115 | attr: 'aoid',
|
116 | });
|
117 | }
|
118 | else if (name != null &&
|
119 | type != null &&
|
120 | [
|
121 | 'abstract operation',
|
122 | 'sdo',
|
123 | 'syntax-directed operation',
|
124 | 'host-defined abstract operation',
|
125 | 'implementation-defined abstract operation',
|
126 | 'numeric method',
|
127 | ].includes(type)) {
|
128 | this.node.setAttribute('aoid', name);
|
129 | this.aoid = name;
|
130 | }
|
131 | this.effects = effects;
|
132 | for (const effect of effects) {
|
133 | if (!this.spec._effectWorklist.has(effect)) {
|
134 | this.spec._effectWorklist.set(effect, []);
|
135 | }
|
136 | this.spec._effectWorklist.get(effect).push(this);
|
137 | }
|
138 | }
|
139 | buildNotes() {
|
140 | if (this.notes.length === 1) {
|
141 | this.notes[0].build();
|
142 | }
|
143 | else {
|
144 |
|
145 | this.notes.forEach((note, i) => {
|
146 | note.build(i + 1);
|
147 | });
|
148 | }
|
149 | this.editorNotes.forEach(note => note.build());
|
150 | }
|
151 | buildExamples() {
|
152 | if (this.examples.length === 1) {
|
153 | this.examples[0].build();
|
154 | }
|
155 | else {
|
156 |
|
157 | this.examples.forEach((example, i) => {
|
158 | example.build(i + 1);
|
159 | });
|
160 | }
|
161 | }
|
162 | canHaveEffect(effectName) {
|
163 |
|
164 |
|
165 |
|
166 | if (this.title !== null && this.title.startsWith('Static Semantics:')) {
|
167 | if (effectName === 'user-code')
|
168 | return false;
|
169 | }
|
170 | return true;
|
171 | }
|
172 | static enter({ spec, node, clauseStack, clauseNumberer }) {
|
173 | return __awaiter(this, void 0, void 0, function* () {
|
174 | if (!node.id) {
|
175 | spec.warn({
|
176 | type: 'node',
|
177 | ruleId: 'missing-id',
|
178 | message: "clause doesn't have an id",
|
179 | node,
|
180 | });
|
181 | }
|
182 | let nextNumber = '';
|
183 | if (node.nodeName !== 'EMU-INTRO') {
|
184 | nextNumber = clauseNumberer.next(clauseStack.length, node.nodeName === 'EMU-ANNEX').value;
|
185 | }
|
186 | const parent = clauseStack[clauseStack.length - 1] || null;
|
187 | const clause = new Clause(spec, node, parent, nextNumber);
|
188 | if (parent) {
|
189 | parent.subclauses.push(clause);
|
190 | }
|
191 | else {
|
192 | spec.subclauses.push(clause);
|
193 | }
|
194 | clauseStack.push(clause);
|
195 | });
|
196 | }
|
197 | static exit({ node, spec, clauseStack, inAlg, currentId }) {
|
198 | const clause = clauseStack[clauseStack.length - 1];
|
199 | const header = clause.header;
|
200 | if (header == null) {
|
201 | clause.title = 'UNKNOWN';
|
202 | clause.titleHTML = 'UNKNOWN';
|
203 | }
|
204 | else {
|
205 | const headerClone = header.cloneNode(true);
|
206 | for (const a of headerClone.querySelectorAll('a')) {
|
207 | a.replaceWith(...a.childNodes);
|
208 | }
|
209 | clause.titleHTML = headerClone.innerHTML;
|
210 | clause.title = headerClone.textContent;
|
211 | if (clause.number) {
|
212 | const numElem = clause.spec.doc.createElement('span');
|
213 | numElem.setAttribute('class', 'secnum');
|
214 | numElem.textContent = clause.number;
|
215 | header.insertBefore(clause.spec.doc.createTextNode(' '), header.firstChild);
|
216 | header.insertBefore(numElem, header.firstChild);
|
217 | }
|
218 | }
|
219 | clause.buildExamples();
|
220 | clause.buildNotes();
|
221 | const attributes = [];
|
222 | if (node.hasAttribute('normative-optional')) {
|
223 | attributes.push('Normative Optional');
|
224 | }
|
225 | if (node.hasAttribute('legacy')) {
|
226 | attributes.push('Legacy');
|
227 | }
|
228 | if (attributes.length > 0) {
|
229 | const tag = spec.doc.createElement('div');
|
230 | tag.className = 'clause-attributes-tag';
|
231 | const text = attributes.join(', ');
|
232 | const contents = spec.doc.createTextNode(text);
|
233 | tag.append(contents);
|
234 | node.prepend(tag);
|
235 |
|
236 | spec._textNodes[clause.namespace] = spec._textNodes[clause.namespace] || [];
|
237 | spec._textNodes[clause.namespace].push({
|
238 | node: contents,
|
239 | clause,
|
240 | inAlg,
|
241 | currentId,
|
242 | });
|
243 | }
|
244 |
|
245 | const entry = {
|
246 | type: 'clause',
|
247 | id: clause.id,
|
248 | aoid: clause.aoid,
|
249 | title: clause.title,
|
250 | titleHTML: clause.titleHTML,
|
251 | number: clause.number,
|
252 | };
|
253 | if (clause.aoid) {
|
254 | const existing = spec.biblio.keysForNamespace(spec.namespace);
|
255 | if (existing.has(clause.aoid)) {
|
256 | spec.warn({
|
257 | type: 'node',
|
258 | node,
|
259 | ruleId: 'duplicate-definition',
|
260 | message: `duplicate definition ${JSON.stringify(clause.aoid)}`,
|
261 | });
|
262 | }
|
263 | }
|
264 | spec.biblio.add(entry, spec.namespace);
|
265 | clauseStack.pop();
|
266 | }
|
267 | }
|
268 | exports.default = Clause;
|
269 | Clause.elements = ['EMU-INTRO', 'EMU-CLAUSE', 'EMU-ANNEX'];
|
270 | Clause.linkElements = Clause.elements;
|