harmonyc 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/model/model.js DELETED
@@ -1,526 +0,0 @@
1
- import { Routers } from "./Router.js";
2
- export class Feature {
3
- constructor(name) {
4
- this.name = name;
5
- this.root = new Section();
6
- this.prelude = '';
7
- }
8
- get tests() {
9
- return makeTests(this.root);
10
- }
11
- get testGroups() {
12
- return makeGroups(this.tests);
13
- }
14
- toCode(cg) {
15
- cg.feature(this);
16
- }
17
- }
18
- export class Node {
19
- at([startToken, endToken]) {
20
- while (startToken && startToken.kind === 'newline') {
21
- startToken = startToken.next;
22
- }
23
- if (startToken) {
24
- this.start = {
25
- line: startToken.pos.rowBegin,
26
- column: startToken.pos.columnBegin - 1,
27
- };
28
- }
29
- if (startToken && endToken) {
30
- let t = startToken;
31
- while (t.next && t.next !== endToken) {
32
- t = t.next;
33
- }
34
- this.end = {
35
- line: t.pos.rowEnd,
36
- column: t.pos.columnEnd - 1,
37
- };
38
- }
39
- return this;
40
- }
41
- atSameAs(other) {
42
- this.start = other.start;
43
- this.end = other.end;
44
- return this;
45
- }
46
- }
47
- export class Branch extends Node {
48
- constructor(children = []) {
49
- super();
50
- this.isFork = false;
51
- this.isEnd = false;
52
- this.children = children;
53
- children.forEach((child) => (child.parent = this));
54
- }
55
- setFork(isFork) {
56
- this.isFork = isFork;
57
- return this;
58
- }
59
- setFeature(feature) {
60
- for (const child of this.children)
61
- child.setFeature(feature);
62
- return this;
63
- }
64
- addChild(child, index = this.children.length) {
65
- this.children.splice(index, 0, child);
66
- child.parent = this;
67
- return child;
68
- }
69
- get isLeaf() {
70
- return this.children.length === 0;
71
- }
72
- get successors() {
73
- if (!this.isLeaf)
74
- return this.children.filter((c, i) => i === 0 || c.isFork);
75
- else {
76
- if (this.isEnd)
77
- return [];
78
- const next = this.nextNonForkAncestorSibling;
79
- if (next)
80
- return [next];
81
- return [];
82
- }
83
- }
84
- get nextNonForkAncestorSibling() {
85
- if (!this.parent)
86
- return undefined;
87
- const { nextSibling } = this;
88
- if (nextSibling && !nextSibling.isFork)
89
- return nextSibling;
90
- return this.parent.nextNonForkAncestorSibling;
91
- }
92
- get nextSibling() {
93
- if (!this.parent)
94
- return undefined;
95
- return this.parent.children[this.siblingIndex + 1];
96
- }
97
- get siblingIndex() {
98
- var _a, _b;
99
- return (_b = (_a = this.parent) === null || _a === void 0 ? void 0 : _a.children.indexOf(this)) !== null && _b !== void 0 ? _b : -1;
100
- }
101
- toString() {
102
- return this.children
103
- .map((c) => (c.isFork ? '+ ' : '- ') + c.toString())
104
- .join('\n');
105
- }
106
- replaceWith(newBranch) {
107
- if (!this.parent)
108
- throw new Error('cannot replace root');
109
- this.parent.children.splice(this.siblingIndex, 1, newBranch);
110
- newBranch.parent = this.parent;
111
- this.parent = undefined;
112
- return this;
113
- }
114
- switch(_i) {
115
- return this;
116
- }
117
- }
118
- export class Step extends Branch {
119
- constructor(action, responses = [], children, isFork = false) {
120
- super(children);
121
- this.action = action;
122
- this.responses = responses;
123
- this.isFork = isFork;
124
- }
125
- get phrases() {
126
- return [this.action, ...this.responses];
127
- }
128
- toCode(cg) {
129
- if (this.responses[0] instanceof ErrorResponse) {
130
- cg.errorStep(this.action, this.responses[0]);
131
- }
132
- else {
133
- cg.step(this.action, this.responses);
134
- }
135
- }
136
- setFeature(feature) {
137
- this.action.setFeature(feature);
138
- for (const response of this.responses)
139
- response.setFeature(feature);
140
- return super.setFeature(feature);
141
- }
142
- headToString() {
143
- return this.phrases.join(' ');
144
- }
145
- toString() {
146
- return this.headToString() + indent(super.toString());
147
- }
148
- get isEmpty() {
149
- return this.phrases.every((phrase) => phrase.isEmpty);
150
- }
151
- switch(i) {
152
- return new Step(this.action.switch(i), this.responses.map((r) => r.switch(i)));
153
- }
154
- }
155
- export class State {
156
- constructor(text = '') {
157
- this.text = text;
158
- }
159
- }
160
- export class Label extends Node {
161
- constructor(text = '') {
162
- super();
163
- this.text = text;
164
- }
165
- get isEmpty() {
166
- return this.text === '';
167
- }
168
- }
169
- export class Section extends Branch {
170
- constructor(label, children, isFork = false) {
171
- super(children);
172
- this.label = label !== null && label !== void 0 ? label : new Label();
173
- this.isFork = isFork;
174
- }
175
- toString() {
176
- if (this.label.text === '')
177
- return super.toString();
178
- return this.label.text + ':' + indent(super.toString());
179
- }
180
- get isEmpty() {
181
- return this.label.isEmpty;
182
- }
183
- }
184
- export class Part {
185
- toSingleLineString() {
186
- return this.toString();
187
- }
188
- }
189
- export class DummyKeyword extends Part {
190
- constructor(text = '') {
191
- super();
192
- this.text = text;
193
- }
194
- toString() {
195
- return this.text;
196
- }
197
- }
198
- export class Word extends Part {
199
- constructor(text = '') {
200
- super();
201
- this.text = text;
202
- }
203
- toString() {
204
- return this.text;
205
- }
206
- }
207
- export class Repeater extends Part {
208
- constructor(choices) {
209
- super();
210
- this.choices = choices;
211
- }
212
- toString() {
213
- return `{${this.choices.map((ps) => ps.join(' ')).join(' & ')}}`;
214
- }
215
- toSingleLineString() {
216
- return `{${this.choices
217
- .map((ps) => ps.map((p) => p.toSingleLineString()).join(' '))
218
- .join(' & ')}}`;
219
- }
220
- }
221
- export class Switch extends Part {
222
- constructor(choices) {
223
- super();
224
- this.choices = choices;
225
- }
226
- toString() {
227
- return `{ ${this.choices.join(' / ')} }`;
228
- }
229
- toSingleLineString() {
230
- return `{ ${this.choices.map((c) => c.toSingleLineString()).join(' / ')} }`;
231
- }
232
- }
233
- export class Router extends Part {
234
- constructor(choices) {
235
- super();
236
- this.choices = choices;
237
- }
238
- toString() {
239
- return `{ ${this.choices.join(' ; ')} }`;
240
- }
241
- toSingleLineString() {
242
- return `{ ${this.choices.map((c) => c.toSingleLineString()).join(' ; ')} }`;
243
- }
244
- }
245
- export class Arg extends Part {
246
- }
247
- export class StringLiteral extends Arg {
248
- constructor(text = '') {
249
- super();
250
- this.text = text;
251
- }
252
- toString() {
253
- return JSON.stringify(this.text);
254
- }
255
- toSingleLineString() {
256
- return this.toString();
257
- }
258
- toCode(cg) {
259
- return cg.stringLiteral(this.text, { withVariables: true });
260
- }
261
- toDeclaration(cg, index) {
262
- return cg.stringParamDeclaration(index);
263
- }
264
- }
265
- export class Docstring extends StringLiteral {
266
- toCode(cg) {
267
- return cg.stringLiteral(this.text, { withVariables: false });
268
- }
269
- toString() {
270
- return this.text
271
- .split('\n')
272
- .map((l) => '| ' + l)
273
- .join('\n');
274
- }
275
- toSingleLineString() {
276
- return super.toString();
277
- }
278
- }
279
- export class CodeLiteral extends Arg {
280
- constructor(src = '') {
281
- super();
282
- this.src = src;
283
- }
284
- toString() {
285
- return '`' + this.src + '`';
286
- }
287
- toCode(cg) {
288
- return cg.codeLiteral(this.src);
289
- }
290
- toDeclaration(cg, index) {
291
- return cg.variantParamDeclaration(index);
292
- }
293
- }
294
- export class Phrase extends Node {
295
- constructor(parts) {
296
- super();
297
- this.parts = parts;
298
- }
299
- setFeature(feature) {
300
- this.feature = feature;
301
- return this;
302
- }
303
- get keyword() {
304
- return this.kind === 'action' ? 'When' : 'Then';
305
- }
306
- get args() {
307
- return this.parts.filter((c) => c instanceof Arg);
308
- }
309
- get isEmpty() {
310
- return this.parts.length === 0;
311
- }
312
- toString() {
313
- const parts = this.parts.map((p) => p.toString());
314
- const isMultiline = parts.map((p) => p.includes('\n'));
315
- return parts
316
- .map((p, i) => i === 0 ? p : isMultiline[i - 1] || isMultiline[i] ? '\n' + p : ' ' + p)
317
- .join('');
318
- }
319
- toSingleLineString() {
320
- return this.parts.map((p) => p.toSingleLineString()).join(' ');
321
- }
322
- switch(i) {
323
- return new this.constructor(this.parts.map((p) => (p instanceof Switch ? p.choices[i] : p))).setFeature(this.feature);
324
- }
325
- }
326
- export class Action extends Phrase {
327
- constructor() {
328
- super(...arguments);
329
- this.kind = 'action';
330
- }
331
- toCode(cg) {
332
- if (this.isEmpty)
333
- return;
334
- cg.phrase(this);
335
- }
336
- }
337
- export class Response extends Phrase {
338
- constructor(parts, saveToVariable) {
339
- super([...parts, ...(saveToVariable ? [saveToVariable] : [])]);
340
- this.saveToVariable = saveToVariable;
341
- this.kind = 'response';
342
- }
343
- get isEmpty() {
344
- return this.parts.length === 0 && !this.saveToVariable;
345
- }
346
- toString() {
347
- return `=> ${super.toString()}`;
348
- }
349
- toSingleLineString() {
350
- return `=> ${super.toSingleLineString()}`;
351
- }
352
- toCode(cg) {
353
- if (this.isEmpty)
354
- return;
355
- cg.phrase(this);
356
- }
357
- }
358
- export class ErrorResponse extends Response {
359
- constructor(message) {
360
- super(message ? [new DummyKeyword('!!'), message] : [new DummyKeyword('!!')]);
361
- this.message = message;
362
- }
363
- toCode(cg) {
364
- cg.errorStep;
365
- }
366
- }
367
- export class SetVariable extends Action {
368
- constructor(variableName, value) {
369
- super([new DummyKeyword(`\${${variableName}}`), value]);
370
- this.variableName = variableName;
371
- this.value = value;
372
- }
373
- toCode(cg) {
374
- cg.setVariable(this);
375
- }
376
- }
377
- export class SaveToVariable extends Part {
378
- constructor(variableName) {
379
- super();
380
- this.variableName = variableName;
381
- }
382
- toCode(cg) {
383
- cg.saveToVariable(this);
384
- }
385
- toString() {
386
- return `\${${this.variableName}}`;
387
- }
388
- get words() {
389
- return [];
390
- }
391
- }
392
- export class Precondition extends Branch {
393
- constructor(state = '') {
394
- super();
395
- this.state = new State();
396
- this.state.text = state;
397
- }
398
- get isEmpty() {
399
- return this.state.text === '';
400
- }
401
- }
402
- export function makeTests(root) {
403
- const routers = new Routers(root);
404
- let tests = [];
405
- let ic = routers.getIncompleteCount();
406
- let newIc;
407
- do {
408
- const newTest = new Test(routers.nextWalk());
409
- newIc = routers.getIncompleteCount();
410
- if (newIc < ic)
411
- tests.push(newTest);
412
- ic = newIc;
413
- } while (ic > 0);
414
- // sort by order of appearance of the last branch
415
- const branchIndex = new Map();
416
- let i = 0;
417
- function walk(branch) {
418
- branchIndex.set(branch, i++);
419
- for (const child of branch.children)
420
- walk(child);
421
- }
422
- walk(root);
423
- tests = tests.filter((t) => t.steps.length > 0);
424
- tests.sort((a, b) => branchIndex.get(a.last) - branchIndex.get(b.last));
425
- resolveSwitches(tests);
426
- tests.forEach((test, i) => (test.testNumber = `T${i + 1}`));
427
- return tests;
428
- }
429
- function resolveSwitches(tests) {
430
- for (let i = 0; i < tests.length; ++i) {
431
- const test = tests[i];
432
- const phrases = test.steps.flatMap((s) => s.phrases);
433
- const switches = phrases.flatMap((p) => p.parts.filter((p) => p instanceof Switch));
434
- if (switches.length === 0)
435
- continue;
436
- const count = switches[0].choices.length;
437
- if (switches.some((s) => s.choices.length !== count)) {
438
- throw new Error(`all switches in a test case must have the same number of choices: ${test.name} has ${switches.map((s) => s.choices.length)} choices`);
439
- }
440
- const newTests = switches[0].choices.map((_, j) => test.switch(j));
441
- tests.splice(i, 1, ...newTests);
442
- i += count - 1;
443
- }
444
- }
445
- export class Test {
446
- constructor(branches) {
447
- this.branches = branches;
448
- this.branches = this.branches.filter((b) => !b.isEmpty);
449
- this.labels = this.branches
450
- .filter((b) => b instanceof Section)
451
- .filter((s) => !s.isEmpty)
452
- .map((s) => s.label);
453
- }
454
- get steps() {
455
- return this.branches.filter((b) => b instanceof Step);
456
- }
457
- get last() {
458
- return this.steps.at(-1);
459
- }
460
- get lastStrain() {
461
- // Find the last branch that has no forks after it
462
- const lastForking = this.branches.length -
463
- 1 -
464
- this.branches
465
- .slice()
466
- .reverse()
467
- .findIndex((b) => b.successors.length > 1);
468
- if (lastForking === this.branches.length)
469
- return this.branches.at(0);
470
- return this.branches.at(lastForking + 1);
471
- }
472
- get name() {
473
- return `${[this.testNumber, ...this.labels.map((x) => x.text)].join(' - ')}`;
474
- }
475
- toCode(cg) {
476
- cg.test(this);
477
- }
478
- toString() {
479
- return `+ ${this.name}:\n${this.steps
480
- .map((s) => ` - ${s.headToString()}`)
481
- .join('\n')}`;
482
- }
483
- switch(j) {
484
- return new Test(this.branches.map((b) => b.switch(j)));
485
- }
486
- }
487
- export function makeGroups(tests) {
488
- if (tests.length === 0)
489
- return [];
490
- if (tests[0].labels.length === 0)
491
- return [tests[0], ...makeGroups(tests.slice(1))];
492
- const label = tests[0].labels[0];
493
- let count = tests.findIndex((t) =>
494
- // using identity instead of text equality, which means identically named labels will not be grouped together
495
- t.labels[0] !== label);
496
- if (count === -1)
497
- count = tests.length;
498
- if (count === 1)
499
- return [tests[0], ...makeGroups(tests.slice(1))];
500
- tests.slice(0, count).forEach((test) => test.labels.shift());
501
- return [
502
- new TestGroup(label, makeGroups(tests.slice(0, count))),
503
- ...makeGroups(tests.slice(count)),
504
- ];
505
- }
506
- export class TestGroup {
507
- constructor(label, items) {
508
- this.label = label;
509
- this.items = items;
510
- }
511
- toString() {
512
- return `+ ${this.label.text}:` + indent(this.items.join('\n'));
513
- }
514
- toCode(cg) {
515
- cg.testGroup(this);
516
- }
517
- }
518
- function indent(s) {
519
- if (!s)
520
- return '';
521
- return ('\n' +
522
- s
523
- .split('\n')
524
- .map((l) => ' ' + l)
525
- .join('\n'));
526
- }
@@ -1,2 +0,0 @@
1
- import { Branch } from '../../model/model.ts';
2
- export declare function autoLabel(b: Branch): void;
@@ -1,19 +0,0 @@
1
- import { Label, Section, Step } from "../../model/model.js";
2
- export function autoLabel(b) {
3
- const forks = b.children.filter((c, i) => c.isFork || i === 0);
4
- if (forks.length > 1) {
5
- forks
6
- .filter((child) => child instanceof Step)
7
- .forEach((step) => {
8
- const label = step.action.toSingleLineString();
9
- const autoLabel = new Label(label);
10
- autoLabel.atSameAs(step.action);
11
- const autoSection = new Section(autoLabel, [], step.isFork);
12
- // todo this is some redundancy with both section and label storing the position
13
- autoSection.atSameAs(step);
14
- step.replaceWith(autoSection);
15
- autoSection.addChild(step);
16
- });
17
- }
18
- b.children.forEach((c) => autoLabel(c));
19
- }
package/parser/lexer.d.ts DELETED
@@ -1,21 +0,0 @@
1
- import { Token, Lexer, TokenPosition } from 'typescript-parsec';
2
- export { T } from './lexer_rules.js';
3
- declare class TokenImpl<T> implements Token<T> {
4
- private readonly lexer;
5
- private readonly input;
6
- kind: T;
7
- text: string;
8
- pos: TokenPosition;
9
- keep: boolean;
10
- private nextToken;
11
- constructor(lexer: LexerImpl<T>, input: string, kind: T, text: string, pos: TokenPosition, keep: boolean);
12
- get next(): Token<T> | undefined;
13
- }
14
- declare class LexerImpl<T> implements Lexer<T> {
15
- rules: [boolean, RegExp, T][];
16
- constructor(rules: [boolean, RegExp, T][]);
17
- parse(input: string): TokenImpl<T> | undefined;
18
- parseNext(input: string, indexStart: number, rowBegin: number, columnBegin: number): TokenImpl<T> | undefined;
19
- parseNextAvailable(input: string, index: number, rowBegin: number, columnBegin: number): TokenImpl<T> | undefined;
20
- }
21
- export declare const lexer: LexerImpl<import("./lexer_rules.js").T>;
package/parser/lexer.js DELETED
@@ -1,123 +0,0 @@
1
- import { TokenError } from 'typescript-parsec';
2
- import rules from './lexer_rules.js';
3
- export { T } from './lexer_rules.js';
4
- // based on https://github.com/microsoft/ts-parsec/blob/3350fcb/packages/ts-parsec/src/Lexer.ts
5
- /*
6
- MIT License
7
-
8
- Copyright (c) Microsoft Corporation.
9
-
10
- Permission is hereby granted, free of charge, to any person obtaining a copy
11
- of this software and associated documentation files (the "Software"), to deal
12
- in the Software without restriction, including without limitation the rights
13
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
- copies of the Software, and to permit persons to whom the Software is
15
- furnished to do so, subject to the following conditions:
16
-
17
- The above copyright notice and this permission notice shall be included in all
18
- copies or substantial portions of the Software.
19
-
20
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
- SOFTWARE
27
- */
28
- class TokenImpl {
29
- constructor(lexer, input, kind, text, pos, keep) {
30
- this.lexer = lexer;
31
- this.input = input;
32
- this.kind = kind;
33
- this.text = text;
34
- this.pos = pos;
35
- this.keep = keep;
36
- }
37
- get next() {
38
- if (this.nextToken === undefined) {
39
- this.nextToken = this.lexer.parseNextAvailable(this.input, this.pos.index + this.text.length, this.pos.rowEnd, this.pos.columnEnd);
40
- if (this.nextToken === undefined) {
41
- this.nextToken = null;
42
- }
43
- }
44
- return this.nextToken === null ? undefined : this.nextToken;
45
- }
46
- }
47
- class LexerImpl {
48
- constructor(rules) {
49
- this.rules = rules;
50
- for (const rule of this.rules) {
51
- if (!rule[1].sticky) {
52
- throw new Error(`Regular expression patterns for a tokenizer should be sticky: ${rule[1].source}`);
53
- }
54
- }
55
- }
56
- parse(input) {
57
- return this.parseNextAvailable(input, 0, 1, 1);
58
- }
59
- parseNext(input, indexStart, rowBegin, columnBegin) {
60
- if (indexStart === input.length) {
61
- return undefined;
62
- }
63
- // changed here: instead of slicing the input string, we use a running index
64
- const lastIndex = indexStart;
65
- // let result: TokenImpl<T> | undefined
66
- for (const [keep, regexp, kind] of this.rules) {
67
- // changed here: instead of slicing the input string, we use a running index
68
- regexp.lastIndex = lastIndex;
69
- if (regexp.test(input)) {
70
- // changed here: instead of slicing the input string, we use a running index
71
- const text = input.slice(lastIndex, regexp.lastIndex);
72
- let rowEnd = rowBegin;
73
- let columnEnd = columnBegin;
74
- for (const c of text) {
75
- switch (c) {
76
- case '\r':
77
- break;
78
- case '\n':
79
- rowEnd++;
80
- columnEnd = 1;
81
- break;
82
- default:
83
- columnEnd++;
84
- }
85
- }
86
- const newResult = new TokenImpl(this, input, kind, text, { index: indexStart, rowBegin, columnBegin, rowEnd, columnEnd }, keep);
87
- // changed here: instead of keeping the longest token, we keep the first one
88
- return newResult;
89
- // if (
90
- // result === undefined ||
91
- // result.text.length < newResult.text.length
92
- // ) {
93
- // result = newResult
94
- // }
95
- }
96
- }
97
- // changed here: instead of keeping the longest token, we keep the first one
98
- // if (result === undefined) {
99
- throw new TokenError({
100
- index: indexStart,
101
- rowBegin,
102
- columnBegin,
103
- rowEnd: rowBegin,
104
- columnEnd: columnBegin,
105
- }, `Unable to tokenize the rest of the input: ${input.substr(indexStart)}`);
106
- // } else {
107
- // return result
108
- // }
109
- }
110
- parseNextAvailable(input, index, rowBegin, columnBegin) {
111
- let token;
112
- while (true) {
113
- token = this.parseNext(input, token === undefined ? index : token.pos.index + token.text.length, token === undefined ? rowBegin : token.pos.rowEnd, token === undefined ? columnBegin : token.pos.columnEnd);
114
- if (token === undefined) {
115
- return undefined;
116
- }
117
- else if (token.keep) {
118
- return token;
119
- }
120
- }
121
- }
122
- }
123
- export const lexer = new LexerImpl(rules);