@polagram/core 0.0.4 → 0.1.1
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/dist/index.d.ts +102 -113
- package/dist/polagram-core.js +1524 -1392
- package/dist/polagram-core.umd.cjs +23 -22
- package/package.json +3 -2
- package/dist/src/api.d.ts +0 -84
- package/dist/src/api.js +0 -183
- package/dist/src/ast/ast.test.d.ts +0 -1
- package/dist/src/ast/ast.test.js +0 -146
- package/dist/src/ast/index.d.ts +0 -119
- package/dist/src/ast/index.js +0 -2
- package/dist/src/config/index.d.ts +0 -1
- package/dist/src/config/index.js +0 -1
- package/dist/src/config/schema.d.ts +0 -198
- package/dist/src/config/schema.js +0 -82
- package/dist/src/config/schema.test.d.ts +0 -1
- package/dist/src/config/schema.test.js +0 -94
- package/dist/src/generator/base/walker.d.ts +0 -19
- package/dist/src/generator/base/walker.js +0 -56
- package/dist/src/generator/base/walker.test.d.ts +0 -1
- package/dist/src/generator/base/walker.test.js +0 -49
- package/dist/src/generator/generators/mermaid.d.ts +0 -24
- package/dist/src/generator/generators/mermaid.js +0 -140
- package/dist/src/generator/generators/mermaid.test.d.ts +0 -1
- package/dist/src/generator/generators/mermaid.test.js +0 -70
- package/dist/src/generator/generators/plantuml.d.ts +0 -17
- package/dist/src/generator/generators/plantuml.js +0 -131
- package/dist/src/generator/generators/plantuml.test.d.ts +0 -1
- package/dist/src/generator/generators/plantuml.test.js +0 -143
- package/dist/src/generator/interface.d.ts +0 -17
- package/dist/src/generator/interface.js +0 -1
- package/dist/src/index.d.ts +0 -13
- package/dist/src/index.js +0 -21
- package/dist/src/parser/base/lexer.d.ts +0 -18
- package/dist/src/parser/base/lexer.js +0 -95
- package/dist/src/parser/base/lexer.test.d.ts +0 -1
- package/dist/src/parser/base/lexer.test.js +0 -53
- package/dist/src/parser/base/parser.d.ts +0 -14
- package/dist/src/parser/base/parser.js +0 -43
- package/dist/src/parser/base/parser.test.d.ts +0 -1
- package/dist/src/parser/base/parser.test.js +0 -90
- package/dist/src/parser/base/token.d.ts +0 -18
- package/dist/src/parser/base/token.js +0 -1
- package/dist/src/parser/base/tokens.d.ts +0 -8
- package/dist/src/parser/base/tokens.js +0 -1
- package/dist/src/parser/format-detector.d.ts +0 -55
- package/dist/src/parser/format-detector.js +0 -98
- package/dist/src/parser/index.d.ts +0 -11
- package/dist/src/parser/index.js +0 -33
- package/dist/src/parser/index.test.d.ts +0 -1
- package/dist/src/parser/index.test.js +0 -23
- package/dist/src/parser/interface.d.ts +0 -8
- package/dist/src/parser/interface.js +0 -1
- package/dist/src/parser/languages/mermaid/constants.d.ts +0 -7
- package/dist/src/parser/languages/mermaid/constants.js +0 -20
- package/dist/src/parser/languages/mermaid/index.d.ts +0 -4
- package/dist/src/parser/languages/mermaid/index.js +0 -11
- package/dist/src/parser/languages/mermaid/lexer.d.ts +0 -14
- package/dist/src/parser/languages/mermaid/lexer.js +0 -152
- package/dist/src/parser/languages/mermaid/lexer.test.d.ts +0 -1
- package/dist/src/parser/languages/mermaid/lexer.test.js +0 -58
- package/dist/src/parser/languages/mermaid/parser.d.ts +0 -22
- package/dist/src/parser/languages/mermaid/parser.js +0 -340
- package/dist/src/parser/languages/mermaid/parser.test.d.ts +0 -1
- package/dist/src/parser/languages/mermaid/parser.test.js +0 -252
- package/dist/src/parser/languages/mermaid/tokens.d.ts +0 -9
- package/dist/src/parser/languages/mermaid/tokens.js +0 -1
- package/dist/src/parser/languages/plantuml/index.d.ts +0 -4
- package/dist/src/parser/languages/plantuml/index.js +0 -11
- package/dist/src/parser/languages/plantuml/lexer.d.ts +0 -15
- package/dist/src/parser/languages/plantuml/lexer.js +0 -143
- package/dist/src/parser/languages/plantuml/parser.d.ts +0 -23
- package/dist/src/parser/languages/plantuml/parser.js +0 -481
- package/dist/src/parser/languages/plantuml/parser.test.d.ts +0 -1
- package/dist/src/parser/languages/plantuml/parser.test.js +0 -236
- package/dist/src/parser/languages/plantuml/tokens.d.ts +0 -9
- package/dist/src/parser/languages/plantuml/tokens.js +0 -1
- package/dist/src/transformer/cleaners/prune-empty.d.ts +0 -9
- package/dist/src/transformer/cleaners/prune-empty.js +0 -27
- package/dist/src/transformer/cleaners/prune-empty.test.d.ts +0 -1
- package/dist/src/transformer/cleaners/prune-empty.test.js +0 -69
- package/dist/src/transformer/cleaners/prune-unused.d.ts +0 -5
- package/dist/src/transformer/cleaners/prune-unused.js +0 -48
- package/dist/src/transformer/cleaners/prune-unused.test.d.ts +0 -1
- package/dist/src/transformer/cleaners/prune-unused.test.js +0 -71
- package/dist/src/transformer/filters/focus.d.ts +0 -13
- package/dist/src/transformer/filters/focus.js +0 -71
- package/dist/src/transformer/filters/focus.test.d.ts +0 -1
- package/dist/src/transformer/filters/focus.test.js +0 -50
- package/dist/src/transformer/filters/remove.d.ts +0 -12
- package/dist/src/transformer/filters/remove.js +0 -82
- package/dist/src/transformer/filters/remove.test.d.ts +0 -1
- package/dist/src/transformer/filters/remove.test.js +0 -38
- package/dist/src/transformer/filters/resolve.d.ts +0 -9
- package/dist/src/transformer/filters/resolve.js +0 -32
- package/dist/src/transformer/filters/resolve.test.d.ts +0 -1
- package/dist/src/transformer/filters/resolve.test.js +0 -48
- package/dist/src/transformer/index.d.ts +0 -10
- package/dist/src/transformer/index.js +0 -10
- package/dist/src/transformer/lens.d.ts +0 -12
- package/dist/src/transformer/lens.js +0 -58
- package/dist/src/transformer/lens.test.d.ts +0 -1
- package/dist/src/transformer/lens.test.js +0 -60
- package/dist/src/transformer/orchestration/engine.d.ts +0 -5
- package/dist/src/transformer/orchestration/engine.js +0 -24
- package/dist/src/transformer/orchestration/engine.test.d.ts +0 -1
- package/dist/src/transformer/orchestration/engine.test.js +0 -52
- package/dist/src/transformer/orchestration/registry.d.ts +0 -10
- package/dist/src/transformer/orchestration/registry.js +0 -27
- package/dist/src/transformer/selector/matcher.d.ts +0 -9
- package/dist/src/transformer/selector/matcher.js +0 -62
- package/dist/src/transformer/selector/matcher.test.d.ts +0 -1
- package/dist/src/transformer/selector/matcher.test.js +0 -70
- package/dist/src/transformer/traverse/walker.d.ts +0 -14
- package/dist/src/transformer/traverse/walker.js +0 -67
- package/dist/src/transformer/traverse/walker.test.d.ts +0 -1
- package/dist/src/transformer/traverse/walker.test.js +0 -111
- package/dist/src/transformer/types.d.ts +0 -47
- package/dist/src/transformer/types.js +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import { BaseLexer } from '../../base/lexer';
|
|
2
|
-
export class Lexer extends BaseLexer {
|
|
3
|
-
constructor(input) {
|
|
4
|
-
super(input);
|
|
5
|
-
}
|
|
6
|
-
nextToken() {
|
|
7
|
-
this.skipWhitespace();
|
|
8
|
-
const start = this.position;
|
|
9
|
-
const startColumn = this.column;
|
|
10
|
-
let tok;
|
|
11
|
-
switch (this.ch) {
|
|
12
|
-
case '\n':
|
|
13
|
-
tok = this.newToken('NEWLINE', this.ch, start, startColumn);
|
|
14
|
-
break;
|
|
15
|
-
case '@':
|
|
16
|
-
if (this.peekString('startuml')) {
|
|
17
|
-
this.readMulti(9);
|
|
18
|
-
tok = this.newToken('START_UML', '@startuml', start, startColumn);
|
|
19
|
-
}
|
|
20
|
-
else if (this.peekString('enduml')) {
|
|
21
|
-
this.readMulti(7);
|
|
22
|
-
tok = this.newToken('END_UML', '@enduml', start, startColumn);
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
tok = this.newToken('UNKNOWN', this.ch, start, startColumn);
|
|
26
|
-
}
|
|
27
|
-
break;
|
|
28
|
-
case ',':
|
|
29
|
-
tok = this.newToken('COMMA', ',', start, startColumn);
|
|
30
|
-
break;
|
|
31
|
-
case '"':
|
|
32
|
-
const str = this.readString();
|
|
33
|
-
return this.newToken('STRING', str, start, startColumn);
|
|
34
|
-
case ':':
|
|
35
|
-
tok = this.newToken('COLON', ':', start, startColumn);
|
|
36
|
-
break;
|
|
37
|
-
case '-':
|
|
38
|
-
const arrow = this.readArrow();
|
|
39
|
-
if (arrow) {
|
|
40
|
-
tok = this.newToken('ARROW', arrow, start, startColumn);
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
tok = this.newToken('UNKNOWN', this.ch, start, startColumn);
|
|
44
|
-
}
|
|
45
|
-
break;
|
|
46
|
-
case '':
|
|
47
|
-
tok = this.newToken('EOF', '', start, startColumn);
|
|
48
|
-
break;
|
|
49
|
-
default:
|
|
50
|
-
if (this.isLetter(this.ch)) {
|
|
51
|
-
const literal = this.readIdentifier();
|
|
52
|
-
const type = this.lookupIdent(literal);
|
|
53
|
-
return this.newToken(type, literal, start, startColumn);
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
tok = this.newToken('UNKNOWN', this.ch, start, startColumn);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
this.readChar();
|
|
60
|
-
return tok;
|
|
61
|
-
}
|
|
62
|
-
newToken(type, literal, start, startColumn) {
|
|
63
|
-
const end = (this.position === start) ? start + literal.length : this.position;
|
|
64
|
-
return { type, literal, line: this.line, column: startColumn, start, end };
|
|
65
|
-
}
|
|
66
|
-
readIdentifier() {
|
|
67
|
-
const position = this.position;
|
|
68
|
-
while (this.isLetter(this.ch) || this.isDigit(this.ch)) {
|
|
69
|
-
this.readChar();
|
|
70
|
-
}
|
|
71
|
-
return this.input.slice(position, this.position);
|
|
72
|
-
}
|
|
73
|
-
readString() {
|
|
74
|
-
const position = this.position + 1;
|
|
75
|
-
this.readChar();
|
|
76
|
-
while (this.ch !== '"' && this.ch !== '' && this.ch !== '\n') {
|
|
77
|
-
this.readChar();
|
|
78
|
-
}
|
|
79
|
-
const str = this.input.slice(position, this.position);
|
|
80
|
-
this.readChar();
|
|
81
|
-
return str;
|
|
82
|
-
}
|
|
83
|
-
lookupIdent(ident) {
|
|
84
|
-
const keywords = {
|
|
85
|
-
'title': 'TITLE',
|
|
86
|
-
'participant': 'PARTICIPANT',
|
|
87
|
-
'actor': 'ACTOR',
|
|
88
|
-
'database': 'DATABASE',
|
|
89
|
-
'as': 'AS',
|
|
90
|
-
'activate': 'ACTIVATE',
|
|
91
|
-
'deactivate': 'DEACTIVATE',
|
|
92
|
-
'note': 'NOTE',
|
|
93
|
-
'left': 'LEFT',
|
|
94
|
-
'right': 'RIGHT',
|
|
95
|
-
'over': 'OVER',
|
|
96
|
-
'of': 'OF',
|
|
97
|
-
'alt': 'ALT',
|
|
98
|
-
'opt': 'OPT',
|
|
99
|
-
'loop': 'LOOP',
|
|
100
|
-
'else': 'ELSE',
|
|
101
|
-
'end': 'END',
|
|
102
|
-
'box': 'BOX',
|
|
103
|
-
'@startuml': 'START_UML',
|
|
104
|
-
'@enduml': 'END_UML'
|
|
105
|
-
};
|
|
106
|
-
return keywords[ident] || 'IDENTIFIER';
|
|
107
|
-
}
|
|
108
|
-
readArrow() {
|
|
109
|
-
// We are at '-'
|
|
110
|
-
// Check for -->
|
|
111
|
-
if (this.peekString('->')) {
|
|
112
|
-
this.readMulti(2);
|
|
113
|
-
return '-->';
|
|
114
|
-
}
|
|
115
|
-
// Check for ->
|
|
116
|
-
if (this.peekExact('>')) {
|
|
117
|
-
this.readMulti(1);
|
|
118
|
-
return '->';
|
|
119
|
-
}
|
|
120
|
-
return null;
|
|
121
|
-
}
|
|
122
|
-
peekExact(char) {
|
|
123
|
-
return this.input[this.position + 1] === char;
|
|
124
|
-
}
|
|
125
|
-
peekString(str) {
|
|
126
|
-
for (let i = 0; i < str.length; i++) {
|
|
127
|
-
if (this.input[this.position + 1 + i] !== str[i])
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
readRestOfLine() {
|
|
133
|
-
const start = this.position;
|
|
134
|
-
while (this.input[this.position] !== '\n' && this.input[this.position] !== '' && this.position < this.input.length) {
|
|
135
|
-
this.readChar();
|
|
136
|
-
}
|
|
137
|
-
return this.input.slice(start, this.position).trim();
|
|
138
|
-
}
|
|
139
|
-
readMulti(count) {
|
|
140
|
-
for (let i = 0; i < count; i++)
|
|
141
|
-
this.readChar();
|
|
142
|
-
}
|
|
143
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { PolagramRoot } from '../../../ast';
|
|
2
|
-
import { BaseParser } from '../../base/parser';
|
|
3
|
-
import { Lexer } from './lexer';
|
|
4
|
-
import { Token } from './tokens';
|
|
5
|
-
export declare class Parser extends BaseParser<Token> {
|
|
6
|
-
constructor(lexer: Lexer);
|
|
7
|
-
/**
|
|
8
|
-
* Type-safe token type checker.
|
|
9
|
-
* Helps TypeScript understand token type after advance() calls.
|
|
10
|
-
*/
|
|
11
|
-
private isTokenType;
|
|
12
|
-
parse(): PolagramRoot;
|
|
13
|
-
private parseGroup;
|
|
14
|
-
private parseFragment;
|
|
15
|
-
private parseStatement;
|
|
16
|
-
private parseNote;
|
|
17
|
-
private parseActivation;
|
|
18
|
-
private isParticipantToken;
|
|
19
|
-
private parseMessage;
|
|
20
|
-
private ensureParticipant;
|
|
21
|
-
private parseParticipant;
|
|
22
|
-
private readRestOfLine;
|
|
23
|
-
}
|
|
@@ -1,481 +0,0 @@
|
|
|
1
|
-
import { BaseParser } from '../../base/parser';
|
|
2
|
-
export class Parser extends BaseParser {
|
|
3
|
-
constructor(lexer) {
|
|
4
|
-
super(lexer);
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* Type-safe token type checker.
|
|
8
|
-
* Helps TypeScript understand token type after advance() calls.
|
|
9
|
-
*/
|
|
10
|
-
isTokenType(type) {
|
|
11
|
-
return this.currToken.type === type;
|
|
12
|
-
}
|
|
13
|
-
parse() {
|
|
14
|
-
const root = {
|
|
15
|
-
kind: 'root',
|
|
16
|
-
meta: { version: '1.0.0', source: 'plantuml' },
|
|
17
|
-
participants: [],
|
|
18
|
-
groups: [],
|
|
19
|
-
events: []
|
|
20
|
-
};
|
|
21
|
-
while (this.currToken.type !== 'EOF') {
|
|
22
|
-
if (this.currToken.type === 'START_UML') {
|
|
23
|
-
this.advance();
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
if (this.currToken.type === 'END_UML') {
|
|
27
|
-
this.advance();
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
if (this.currToken.type === 'TITLE') {
|
|
31
|
-
this.advance(); // eat title
|
|
32
|
-
root.meta.title = this.readRestOfLine().trim();
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
if (['PARTICIPANT', 'ACTOR', 'DATABASE'].includes(this.currToken.type)) {
|
|
36
|
-
this.parseParticipant(root);
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
// Implicit message/participant detection
|
|
40
|
-
// A -> B : text
|
|
41
|
-
// Identifier/String -> Arrow...
|
|
42
|
-
if (this.isParticipantToken(this.currToken)) {
|
|
43
|
-
const probMsg = this.parseMessage(root);
|
|
44
|
-
if (probMsg) {
|
|
45
|
-
root.events.push(probMsg);
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
if (this.currToken.type === 'ACTIVATE' || this.currToken.type === 'DEACTIVATE') {
|
|
50
|
-
const act = this.parseActivation(root);
|
|
51
|
-
if (act)
|
|
52
|
-
root.events.push(act);
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
if (this.currToken.type === 'NOTE') {
|
|
56
|
-
const note = this.parseNote(root);
|
|
57
|
-
if (note)
|
|
58
|
-
root.events.push(note);
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
if (['ALT', 'OPT', 'LOOP'].includes(this.currToken.type)) {
|
|
62
|
-
const fragment = this.parseFragment(root);
|
|
63
|
-
if (fragment)
|
|
64
|
-
root.events.push(fragment);
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
// Handle standalone 'end' if it appears outside (shouldn't if parsed recursively, but safeguard)
|
|
68
|
-
if (this.currToken.type === 'END') {
|
|
69
|
-
// If we are at root, 'end' might be closing a fragment.
|
|
70
|
-
// But parseFragment consumes until end.
|
|
71
|
-
// If we see it here, it's unmatched or nested logic needed.
|
|
72
|
-
// For simple recursive descent, we return to caller.
|
|
73
|
-
return root;
|
|
74
|
-
}
|
|
75
|
-
if (this.currToken.type === 'BOX') {
|
|
76
|
-
const group = this.parseGroup(root);
|
|
77
|
-
if (group)
|
|
78
|
-
root.groups.push(group);
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
this.advance();
|
|
82
|
-
}
|
|
83
|
-
return root;
|
|
84
|
-
}
|
|
85
|
-
parseGroup(root) {
|
|
86
|
-
this.advance(); // eat box
|
|
87
|
-
let name = '';
|
|
88
|
-
let backgroundColor;
|
|
89
|
-
// box "Title" #Color
|
|
90
|
-
if (this.currToken.type === 'STRING') {
|
|
91
|
-
name = this.currToken.literal;
|
|
92
|
-
this.advance();
|
|
93
|
-
}
|
|
94
|
-
// Check for color (starts with # usually, but lexer might tokenize it as UNKNOWN or need handling)
|
|
95
|
-
// PlantUML #Color is just text heavily.
|
|
96
|
-
// My lexer tokenizes # as UNKNOWN?
|
|
97
|
-
// Let's check lexer. It has no case for '#'.
|
|
98
|
-
// So it returns UNKNOWN.
|
|
99
|
-
if (this.currToken.type === 'UNKNOWN' && this.currToken.literal === '#') {
|
|
100
|
-
// Read color
|
|
101
|
-
// #LightBlue
|
|
102
|
-
// We need to read identifiers after #?
|
|
103
|
-
// Currently I don't have good color support in lexer.
|
|
104
|
-
// Quick hack: assume we are at #. Read next identifier.
|
|
105
|
-
this.advance(); // eat #
|
|
106
|
-
if (this.isTokenType('IDENTIFIER')) {
|
|
107
|
-
backgroundColor = '#' + this.currToken.literal;
|
|
108
|
-
this.advance();
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
const participantIds = [];
|
|
112
|
-
// Parse content until 'end box' (or just 'end')
|
|
113
|
-
while (this.currToken.type !== 'EOF') {
|
|
114
|
-
if (this.currToken.type === 'END') {
|
|
115
|
-
this.advance(); // eat end
|
|
116
|
-
if (this.isTokenType('BOX')) {
|
|
117
|
-
this.advance(); // eat box
|
|
118
|
-
}
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
// We expect participant declarations inside box usually.
|
|
122
|
-
if (['PARTICIPANT', 'ACTOR', 'DATABASE'].includes(this.currToken.type)) {
|
|
123
|
-
// We need to capture the ID of the participant created.
|
|
124
|
-
// parseParticipant pushes to root.participants.
|
|
125
|
-
// We can check root.participants.length before and after? Or return ID from parseParticipant.
|
|
126
|
-
const lenBefore = root.participants.length;
|
|
127
|
-
this.parseParticipant(root);
|
|
128
|
-
const lenAfter = root.participants.length;
|
|
129
|
-
if (lenAfter > lenBefore) {
|
|
130
|
-
participantIds.push(root.participants[lenAfter - 1].id);
|
|
131
|
-
}
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
// If implicit participant? 'A'
|
|
135
|
-
if (this.currToken.type === 'IDENTIFIER') {
|
|
136
|
-
// Check if it is a participant decl without keyword? (PlantUML allows it)
|
|
137
|
-
// OR check if it is start of message?
|
|
138
|
-
// If start of message, participants might be already capable of being in group?
|
|
139
|
-
// Usually checking 'participant A' is safe.
|
|
140
|
-
// But implicit participants in box:
|
|
141
|
-
// box "Foo"
|
|
142
|
-
// A
|
|
143
|
-
// end box
|
|
144
|
-
// This declares A in box.
|
|
145
|
-
// But A -> B inside box?
|
|
146
|
-
// The message is an event. The participants are in the box?
|
|
147
|
-
// Only if they are first declared here.
|
|
148
|
-
// For MVP: Support explicit 'participant' inside box, OR just parse statements.
|
|
149
|
-
// If parseStatement returns null (participant decl), we need to capture ID.
|
|
150
|
-
// Let's rely on explicit participant keywords for now as per test case.
|
|
151
|
-
this.advance(); // skip other things
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
this.advance();
|
|
155
|
-
}
|
|
156
|
-
return {
|
|
157
|
-
kind: 'group',
|
|
158
|
-
id: 'group_' + (root.groups.length + 1),
|
|
159
|
-
name,
|
|
160
|
-
type: 'box',
|
|
161
|
-
participantIds,
|
|
162
|
-
style: backgroundColor ? { backgroundColor } : undefined
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
parseFragment(root) {
|
|
166
|
-
const kind = 'fragment';
|
|
167
|
-
const operator = this.currToken.literal.toLowerCase(); // alt, opt, loop
|
|
168
|
-
this.advance(); // eat keyword
|
|
169
|
-
const condition = this.readRestOfLine().trim();
|
|
170
|
-
const branches = [];
|
|
171
|
-
let currentEvents = [];
|
|
172
|
-
const currentBranch = { condition, events: currentEvents };
|
|
173
|
-
branches.push(currentBranch);
|
|
174
|
-
// We need to parse block content until ELSE or END
|
|
175
|
-
while (this.currToken.type !== 'EOF') {
|
|
176
|
-
if (this.currToken.type === 'END') {
|
|
177
|
-
this.advance(); // eat end
|
|
178
|
-
// Check if it is 'end box' or just 'end'?
|
|
179
|
-
// PlantUML has 'end' for fragments.
|
|
180
|
-
// Also 'end note', 'end box'.
|
|
181
|
-
// For now assume 'end' closes fragment.
|
|
182
|
-
break;
|
|
183
|
-
}
|
|
184
|
-
if (this.currToken.type === 'ELSE') {
|
|
185
|
-
this.advance(); // eat else
|
|
186
|
-
// New branch
|
|
187
|
-
const elseCond = this.readRestOfLine().trim();
|
|
188
|
-
currentEvents = [];
|
|
189
|
-
branches.push({ condition: elseCond, events: currentEvents });
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
// Parse single line event or nested structure
|
|
193
|
-
// We can reuse the main loop logic effectively if we refactor 'parseBlock'
|
|
194
|
-
// For now, let's duplicate the switch logic or call a recursive 'parseStatement'
|
|
195
|
-
// Simulating parseStatement step:
|
|
196
|
-
if (this.currToken.type === 'NEWLINE') {
|
|
197
|
-
this.advance();
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
// Recursively call a helper that processes ONE statement
|
|
201
|
-
const event = this.parseStatement(root); // We need this helper!
|
|
202
|
-
if (event) {
|
|
203
|
-
currentEvents.push(event);
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
// If not returned an event (e.g. participant decl), we might still advance?
|
|
207
|
-
// parseStatement should handle everything inside block.
|
|
208
|
-
// But 'parseStatement' needs to be extracted from parse().
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
return {
|
|
212
|
-
kind,
|
|
213
|
-
id: 'frag_' + (root.events.length + 1),
|
|
214
|
-
operator,
|
|
215
|
-
branches
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
// Refactor parse() to use parseStatement
|
|
219
|
-
parseStatement(root) {
|
|
220
|
-
if (['PARTICIPANT', 'ACTOR', 'DATABASE'].includes(this.currToken.type)) {
|
|
221
|
-
this.parseParticipant(root);
|
|
222
|
-
return null; // Not an event
|
|
223
|
-
}
|
|
224
|
-
if (this.isParticipantToken(this.currToken)) {
|
|
225
|
-
const probMsg = this.parseMessage(root);
|
|
226
|
-
if (probMsg)
|
|
227
|
-
return probMsg;
|
|
228
|
-
}
|
|
229
|
-
if (this.currToken.type === 'ACTIVATE' || this.currToken.type === 'DEACTIVATE') {
|
|
230
|
-
return this.parseActivation(root);
|
|
231
|
-
}
|
|
232
|
-
if (this.currToken.type === 'NOTE') {
|
|
233
|
-
return this.parseNote(root);
|
|
234
|
-
}
|
|
235
|
-
if (['ALT', 'OPT', 'LOOP'].includes(this.currToken.type)) {
|
|
236
|
-
return this.parseFragment(root);
|
|
237
|
-
}
|
|
238
|
-
this.advance();
|
|
239
|
-
return null;
|
|
240
|
-
}
|
|
241
|
-
parseNote(root) {
|
|
242
|
-
this.advance(); // eat note
|
|
243
|
-
let position = 'over'; // default
|
|
244
|
-
// note left of A
|
|
245
|
-
// note right of A
|
|
246
|
-
// note over A
|
|
247
|
-
if (this.currToken.type === 'LEFT') {
|
|
248
|
-
position = 'left';
|
|
249
|
-
this.advance();
|
|
250
|
-
}
|
|
251
|
-
else if (this.currToken.type === 'RIGHT') {
|
|
252
|
-
position = 'right';
|
|
253
|
-
this.advance();
|
|
254
|
-
}
|
|
255
|
-
else if (this.currToken.type === 'OVER') {
|
|
256
|
-
position = 'over';
|
|
257
|
-
this.advance();
|
|
258
|
-
}
|
|
259
|
-
if (this.currToken.type === 'OF') {
|
|
260
|
-
this.advance();
|
|
261
|
-
}
|
|
262
|
-
const participantIds = [];
|
|
263
|
-
while (this.isParticipantToken(this.currToken)) {
|
|
264
|
-
participantIds.push(this.currToken.literal);
|
|
265
|
-
this.ensureParticipant(root, this.currToken.literal);
|
|
266
|
-
this.advance();
|
|
267
|
-
if (this.currToken.type === 'COMMA') {
|
|
268
|
-
this.advance();
|
|
269
|
-
}
|
|
270
|
-
else {
|
|
271
|
-
break;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
let text = '';
|
|
275
|
-
if (this.currToken.type === 'COLON') {
|
|
276
|
-
this.advance();
|
|
277
|
-
text = this.readRestOfLine().trim();
|
|
278
|
-
}
|
|
279
|
-
else {
|
|
280
|
-
// Multi-line note
|
|
281
|
-
if (this.currToken.type === 'NEWLINE') {
|
|
282
|
-
this.advance();
|
|
283
|
-
}
|
|
284
|
-
const start = this.currToken.start;
|
|
285
|
-
let end = start;
|
|
286
|
-
while (this.currToken.type !== 'EOF') {
|
|
287
|
-
if (this.currToken.type === 'END' && this.peekToken.type === 'NOTE') {
|
|
288
|
-
end = this.currToken.start;
|
|
289
|
-
this.advance(); // eat end
|
|
290
|
-
this.advance(); // eat note
|
|
291
|
-
break;
|
|
292
|
-
}
|
|
293
|
-
this.advance();
|
|
294
|
-
}
|
|
295
|
-
const input = this.lexer.getInput();
|
|
296
|
-
text = input.slice(start, end).trim();
|
|
297
|
-
}
|
|
298
|
-
return {
|
|
299
|
-
kind: 'note',
|
|
300
|
-
id: 'note_' + (root.events.length + 1),
|
|
301
|
-
position,
|
|
302
|
-
participantIds,
|
|
303
|
-
text
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
parseActivation(root) {
|
|
307
|
-
const action = this.currToken.type === 'ACTIVATE' ? 'activate' : 'deactivate';
|
|
308
|
-
this.advance(); // eat keyword
|
|
309
|
-
let participantId = '';
|
|
310
|
-
if (this.isParticipantToken(this.currToken)) {
|
|
311
|
-
participantId = this.currToken.literal;
|
|
312
|
-
this.ensureParticipant(root, participantId);
|
|
313
|
-
this.advance();
|
|
314
|
-
}
|
|
315
|
-
else {
|
|
316
|
-
return null; // Error
|
|
317
|
-
}
|
|
318
|
-
return {
|
|
319
|
-
kind: 'activation',
|
|
320
|
-
participantId,
|
|
321
|
-
action
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
isParticipantToken(tok) {
|
|
325
|
-
return tok.type === 'IDENTIFIER' || tok.type === 'STRING';
|
|
326
|
-
}
|
|
327
|
-
parseMessage(root) {
|
|
328
|
-
if (this.peekToken.type !== 'ARROW') {
|
|
329
|
-
// Maybe it's just a participant declaration implied? 'A' on its own line?
|
|
330
|
-
// PlantUML 'A' is valid. It creates participant A.
|
|
331
|
-
// But here we look for message.
|
|
332
|
-
return null;
|
|
333
|
-
}
|
|
334
|
-
const fromId = this.currToken.literal; // simple ID for now. If quoted string, use it as ID/Name.
|
|
335
|
-
this.ensureParticipant(root, fromId);
|
|
336
|
-
this.advance(); // eat from
|
|
337
|
-
const arrow = this.currToken.literal; // -> or -->
|
|
338
|
-
this.advance(); // eat arrow
|
|
339
|
-
if (!this.isParticipantToken(this.currToken)) {
|
|
340
|
-
return null; // Error?
|
|
341
|
-
}
|
|
342
|
-
const toId = this.currToken.literal;
|
|
343
|
-
this.ensureParticipant(root, toId);
|
|
344
|
-
this.advance(); // eat to
|
|
345
|
-
let text = '';
|
|
346
|
-
if (this.currToken.type === 'COLON') {
|
|
347
|
-
this.advance(); // eat colon
|
|
348
|
-
text = this.readRestOfLine().trim();
|
|
349
|
-
}
|
|
350
|
-
// Resolve arrow style
|
|
351
|
-
let type = 'sync';
|
|
352
|
-
let style = { line: 'solid', head: 'arrow' };
|
|
353
|
-
if (arrow === '-->') {
|
|
354
|
-
type = 'reply';
|
|
355
|
-
style = { line: 'dotted', head: 'arrow' };
|
|
356
|
-
}
|
|
357
|
-
else if (arrow === '->') {
|
|
358
|
-
type = 'sync';
|
|
359
|
-
style = { line: 'solid', head: 'arrow' };
|
|
360
|
-
}
|
|
361
|
-
return {
|
|
362
|
-
kind: 'message',
|
|
363
|
-
id: 'msg_' + (root.events.length + 1), // Simple ID generation
|
|
364
|
-
from: fromId,
|
|
365
|
-
to: toId,
|
|
366
|
-
text,
|
|
367
|
-
type,
|
|
368
|
-
style
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
ensureParticipant(root, id) {
|
|
372
|
-
if (!root.participants.find(p => p.id === id)) {
|
|
373
|
-
root.participants.push({
|
|
374
|
-
id,
|
|
375
|
-
name: id,
|
|
376
|
-
type: 'participant'
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
parseParticipant(root) {
|
|
381
|
-
const typeStr = this.currToken.type; // ACTOR, DATABASE, PARTICIPANT
|
|
382
|
-
let type = 'participant'; // Default
|
|
383
|
-
if (typeStr === 'ACTOR')
|
|
384
|
-
type = 'actor';
|
|
385
|
-
if (typeStr === 'DATABASE')
|
|
386
|
-
type = 'database';
|
|
387
|
-
this.advance(); // eat keyword
|
|
388
|
-
// console.log('DEBUG: parseParticipant token:', this.currToken.type, this.currToken.literal);
|
|
389
|
-
let name = '';
|
|
390
|
-
let id = '';
|
|
391
|
-
// Name/ID
|
|
392
|
-
if (this.currToken.type === 'STRING' || this.currToken.type === 'IDENTIFIER') {
|
|
393
|
-
name = this.currToken.literal;
|
|
394
|
-
id = name; // Default ID is name (unless as is used)
|
|
395
|
-
// If name has spaces (quoted), ID usually needs alias to be usable without quotes?
|
|
396
|
-
// PlantUML: participant "Long Name" as A
|
|
397
|
-
// ID = A, Name = "Long Name"
|
|
398
|
-
// PlantUML: participant A
|
|
399
|
-
// ID = A, Name = A
|
|
400
|
-
// But strict PlantUML uses the Alias as the ID for arrows.
|
|
401
|
-
this.advance();
|
|
402
|
-
}
|
|
403
|
-
if (this.currToken.type === 'AS') {
|
|
404
|
-
this.advance(); // eat as
|
|
405
|
-
if (this.isTokenType('IDENTIFIER')) {
|
|
406
|
-
id = this.currToken.literal; // "Long Name" as Svc -> Svc is ID
|
|
407
|
-
this.advance();
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
else {
|
|
411
|
-
// If "Long Name" is given without 'as', usually we treat name as ID if safe?
|
|
412
|
-
// But usually we sanitize.
|
|
413
|
-
// For now follow logic: A as B -> ID=B, Name=A.
|
|
414
|
-
// If just A -> ID=A, Name=A.
|
|
415
|
-
// If "A B" -> ID="A B", Name="A B".
|
|
416
|
-
// In Core AST, ID is the references key.
|
|
417
|
-
// If name was quoted "Service Wrapper", without ID, it is hard to reference.
|
|
418
|
-
// Unless we reference using Quotes?
|
|
419
|
-
// Let's assume input is valid alias for now.
|
|
420
|
-
// Wait, Step 93 test case:
|
|
421
|
-
// participant "Service Wrapper" as Svc
|
|
422
|
-
// name="Service Wrapper", id="Svc"
|
|
423
|
-
}
|
|
424
|
-
// If we found 'as', id was updated.
|
|
425
|
-
// If we didn't find 'as' (e.g. actor User), id = User, name = User.
|
|
426
|
-
// But wait:
|
|
427
|
-
// case: participant "Service Wrapper" as Svc
|
|
428
|
-
// 1. Keyword participant.
|
|
429
|
-
// 2. String "Service Wrapper". name = "Service Wrapper", id="Service Wrapper".
|
|
430
|
-
// 3. AS.
|
|
431
|
-
// 4. Identifier Svc. id="Svc".
|
|
432
|
-
// correct.
|
|
433
|
-
root.participants.push({
|
|
434
|
-
id,
|
|
435
|
-
name,
|
|
436
|
-
type
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
readRestOfLine() {
|
|
440
|
-
// We need to sync/consume tokens until NEWLINE
|
|
441
|
-
// But since we want raw text, we should ask lexer.
|
|
442
|
-
// However, we effectively already consumed 'current token' if we are here?
|
|
443
|
-
// Usually we call readRestOfLine AFTER consuming the label (e.g. COLON).
|
|
444
|
-
// So currToken should be the first token of the text?
|
|
445
|
-
// But lexer might have already tokenized it into multiple tokens.
|
|
446
|
-
// If we simply call lexer.readRestOfLine(), it continues from CURRENT lexer position.
|
|
447
|
-
// currToken is the token *already read*.
|
|
448
|
-
// peekToken is the next one.
|
|
449
|
-
// Parser is usually one step behind or ahead?
|
|
450
|
-
// BaseParser: this.currToken, this.peekToken.
|
|
451
|
-
// Parsing process:
|
|
452
|
-
// 1. nextToken called for currToken.
|
|
453
|
-
// 2. nextToken called for peekToken.
|
|
454
|
-
// So Lexer is at position AFTER peekToken.
|
|
455
|
-
// If we want "rest of line from currToken", we are in trouble because Lexer is far ahead.
|
|
456
|
-
// Alternative:
|
|
457
|
-
// Reconstruct text from tokens until NEWLINE.
|
|
458
|
-
// But tokens don't enforce whitespace rules strictly?
|
|
459
|
-
// We capture literal.
|
|
460
|
-
// Wait, `Token` has `start` and `end`?
|
|
461
|
-
// Yes: { type, literal, start, end }
|
|
462
|
-
// We can use the start of currToken and end of the last token before NEWLINE to slice from source?
|
|
463
|
-
// We don't have easy access to source in strict BaseParser (it's in Lexer).
|
|
464
|
-
// But we can access `(this.lexer as Lexer).input`.
|
|
465
|
-
if (this.currToken.type === 'NEWLINE' || this.currToken.type === 'EOF')
|
|
466
|
-
return '';
|
|
467
|
-
const start = this.currToken.start;
|
|
468
|
-
let end = this.currToken.end;
|
|
469
|
-
while (!this.isTokenType('NEWLINE') && !this.isTokenType('EOF')) {
|
|
470
|
-
end = this.currToken.end;
|
|
471
|
-
this.advance();
|
|
472
|
-
}
|
|
473
|
-
// We advanced past the last text token. currToken is now NEWLINE.
|
|
474
|
-
// The previous token ended at `end`.
|
|
475
|
-
// We need access to input.
|
|
476
|
-
const input = this.lexer.getInput(); // BaseLexer usually carries input?
|
|
477
|
-
// BaseLexer: protected input: string;
|
|
478
|
-
// We might need to make it public accessor or cast.
|
|
479
|
-
return input.slice(start, end).trim();
|
|
480
|
-
}
|
|
481
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|