context-mapper-mcp 1.0.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/LICENSE +21 -0
- package/README.md +238 -0
- package/dist/generators/plantuml.d.ts +17 -0
- package/dist/generators/plantuml.d.ts.map +1 -0
- package/dist/generators/plantuml.js +244 -0
- package/dist/generators/plantuml.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +897 -0
- package/dist/index.js.map +1 -0
- package/dist/model/parser.d.ts +51 -0
- package/dist/model/parser.d.ts.map +1 -0
- package/dist/model/parser.js +934 -0
- package/dist/model/parser.js.map +1 -0
- package/dist/model/types.d.ts +121 -0
- package/dist/model/types.d.ts.map +1 -0
- package/dist/model/types.js +12 -0
- package/dist/model/types.js.map +1 -0
- package/dist/model/validation.d.ts +36 -0
- package/dist/model/validation.d.ts.map +1 -0
- package/dist/model/validation.js +411 -0
- package/dist/model/validation.js.map +1 -0
- package/dist/model/writer.d.ts +30 -0
- package/dist/model/writer.d.ts.map +1 -0
- package/dist/model/writer.js +305 -0
- package/dist/model/writer.js.map +1 -0
- package/dist/tools/aggregate-tools.d.ts +295 -0
- package/dist/tools/aggregate-tools.d.ts.map +1 -0
- package/dist/tools/aggregate-tools.js +965 -0
- package/dist/tools/aggregate-tools.js.map +1 -0
- package/dist/tools/context-tools.d.ts +60 -0
- package/dist/tools/context-tools.d.ts.map +1 -0
- package/dist/tools/context-tools.js +166 -0
- package/dist/tools/context-tools.js.map +1 -0
- package/dist/tools/generation-tools.d.ts +29 -0
- package/dist/tools/generation-tools.d.ts.map +1 -0
- package/dist/tools/generation-tools.js +62 -0
- package/dist/tools/generation-tools.js.map +1 -0
- package/dist/tools/model-tools.d.ts +72 -0
- package/dist/tools/model-tools.d.ts.map +1 -0
- package/dist/tools/model-tools.js +186 -0
- package/dist/tools/model-tools.js.map +1 -0
- package/dist/tools/query-tools.d.ts +170 -0
- package/dist/tools/query-tools.d.ts.map +1 -0
- package/dist/tools/query-tools.js +322 -0
- package/dist/tools/query-tools.js.map +1 -0
- package/dist/tools/relationship-tools.d.ts +68 -0
- package/dist/tools/relationship-tools.d.ts.map +1 -0
- package/dist/tools/relationship-tools.js +178 -0
- package/dist/tools/relationship-tools.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,934 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CML Parser using Chevrotain
|
|
3
|
+
* Parses Context Mapper Language (CML) files into typed AST
|
|
4
|
+
*/
|
|
5
|
+
import { createToken, Lexer, CstParser } from 'chevrotain';
|
|
6
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
7
|
+
// ============================================
|
|
8
|
+
// LEXER TOKENS
|
|
9
|
+
// ============================================
|
|
10
|
+
// Whitespace and comments
|
|
11
|
+
const WhiteSpace = createToken({ name: 'WhiteSpace', pattern: /\s+/, group: Lexer.SKIPPED });
|
|
12
|
+
const LineComment = createToken({ name: 'LineComment', pattern: /\/\/[^\n\r]*/, group: Lexer.SKIPPED });
|
|
13
|
+
const BlockComment = createToken({ name: 'BlockComment', pattern: /\/\*[\s\S]*?\*\//, group: Lexer.SKIPPED });
|
|
14
|
+
// Identifier must be defined first for longer_alt references
|
|
15
|
+
const Identifier = createToken({ name: 'Identifier', pattern: /[a-zA-Z_][a-zA-Z0-9_]*/ });
|
|
16
|
+
// Keywords - use longer_alt so identifiers like "ServiceNowPlatform" aren't split
|
|
17
|
+
const ContextMap = createToken({ name: 'ContextMap', pattern: /ContextMap/, longer_alt: Identifier });
|
|
18
|
+
const BoundedContext = createToken({ name: 'BoundedContext', pattern: /BoundedContext/, longer_alt: Identifier });
|
|
19
|
+
const Aggregate = createToken({ name: 'Aggregate', pattern: /Aggregate/, longer_alt: Identifier });
|
|
20
|
+
const Entity = createToken({ name: 'Entity', pattern: /Entity/, longer_alt: Identifier });
|
|
21
|
+
const ValueObject = createToken({ name: 'ValueObject', pattern: /ValueObject/, longer_alt: Identifier });
|
|
22
|
+
const DomainEvent = createToken({ name: 'DomainEvent', pattern: /DomainEvent/, longer_alt: Identifier });
|
|
23
|
+
const Command = createToken({ name: 'Command', pattern: /Command/, longer_alt: Identifier });
|
|
24
|
+
const Service = createToken({ name: 'Service', pattern: /Service/, longer_alt: Identifier });
|
|
25
|
+
const Module = createToken({ name: 'Module', pattern: /Module/, longer_alt: Identifier });
|
|
26
|
+
// Relationship keywords
|
|
27
|
+
const Partnership = createToken({ name: 'Partnership', pattern: /Partnership/, longer_alt: Identifier });
|
|
28
|
+
const SharedKernel = createToken({ name: 'SharedKernel', pattern: /SharedKernel/, longer_alt: Identifier });
|
|
29
|
+
const CustomerSupplier = createToken({ name: 'CustomerSupplier', pattern: /Customer-Supplier/ });
|
|
30
|
+
// Relationship patterns
|
|
31
|
+
const UpstreamKw = createToken({ name: 'UpstreamKw', pattern: /[Uu]pstream/, longer_alt: Identifier });
|
|
32
|
+
const DownstreamKw = createToken({ name: 'DownstreamKw', pattern: /[Dd]ownstream/, longer_alt: Identifier });
|
|
33
|
+
const UpstreamShort = createToken({ name: 'UpstreamShort', pattern: /U(?![a-zA-Z0-9_])/ });
|
|
34
|
+
const DownstreamShort = createToken({ name: 'DownstreamShort', pattern: /D(?![a-zA-Z0-9_])/ });
|
|
35
|
+
const OHS = createToken({ name: 'OHS', pattern: /OHS/, longer_alt: Identifier });
|
|
36
|
+
const PL = createToken({ name: 'PL', pattern: /PL/, longer_alt: Identifier });
|
|
37
|
+
const ACL = createToken({ name: 'ACL', pattern: /ACL/, longer_alt: Identifier });
|
|
38
|
+
const CF = createToken({ name: 'CF', pattern: /CF/, longer_alt: Identifier });
|
|
39
|
+
// Property keywords
|
|
40
|
+
const Contains = createToken({ name: 'Contains', pattern: /contains/, longer_alt: Identifier });
|
|
41
|
+
const Implements = createToken({ name: 'Implements', pattern: /implements/, longer_alt: Identifier });
|
|
42
|
+
const Type = createToken({ name: 'Type', pattern: /type/, longer_alt: Identifier });
|
|
43
|
+
const State = createToken({ name: 'State', pattern: /state/, longer_alt: Identifier });
|
|
44
|
+
const DomainVisionStatement = createToken({ name: 'DomainVisionStatement', pattern: /domainVisionStatement/, longer_alt: Identifier });
|
|
45
|
+
const Responsibilities = createToken({ name: 'Responsibilities', pattern: /responsibilities/, longer_alt: Identifier });
|
|
46
|
+
const ImplementationTechnology = createToken({ name: 'ImplementationTechnology', pattern: /implementationTechnology/, longer_alt: Identifier });
|
|
47
|
+
const KnowledgeLevel = createToken({ name: 'KnowledgeLevel', pattern: /knowledgeLevel/, longer_alt: Identifier });
|
|
48
|
+
const AggregateRoot = createToken({ name: 'AggregateRoot', pattern: /aggregateRoot/, longer_alt: Identifier });
|
|
49
|
+
const Def = createToken({ name: 'Def', pattern: /def/, longer_alt: Identifier });
|
|
50
|
+
const Key = createToken({ name: 'Key', pattern: /key/, longer_alt: Identifier });
|
|
51
|
+
const Nullable = createToken({ name: 'Nullable', pattern: /nullable/, longer_alt: Identifier });
|
|
52
|
+
const ExposedAggregates = createToken({ name: 'ExposedAggregates', pattern: /exposedAggregates/, longer_alt: Identifier });
|
|
53
|
+
// Collection types
|
|
54
|
+
const ListType = createToken({ name: 'ListType', pattern: /List/, longer_alt: Identifier });
|
|
55
|
+
const SetType = createToken({ name: 'SetType', pattern: /Set/, longer_alt: Identifier });
|
|
56
|
+
const LAngle = createToken({ name: 'LAngle', pattern: /</ });
|
|
57
|
+
const RAngle = createToken({ name: 'RAngle', pattern: />/ });
|
|
58
|
+
// State keywords
|
|
59
|
+
const AsIs = createToken({ name: 'AsIs', pattern: /AS_IS/, longer_alt: Identifier });
|
|
60
|
+
const ToBe = createToken({ name: 'ToBe', pattern: /TO_BE/, longer_alt: Identifier });
|
|
61
|
+
const Meta = createToken({ name: 'Meta', pattern: /META/, longer_alt: Identifier });
|
|
62
|
+
const Concrete = createToken({ name: 'Concrete', pattern: /CONCRETE/, longer_alt: Identifier });
|
|
63
|
+
// Symbols
|
|
64
|
+
const LCurly = createToken({ name: 'LCurly', pattern: /{/ });
|
|
65
|
+
const RCurly = createToken({ name: 'RCurly', pattern: /}/ });
|
|
66
|
+
const LParen = createToken({ name: 'LParen', pattern: /\(/ });
|
|
67
|
+
const RParen = createToken({ name: 'RParen', pattern: /\)/ });
|
|
68
|
+
const LSquare = createToken({ name: 'LSquare', pattern: /\[/ });
|
|
69
|
+
const RSquare = createToken({ name: 'RSquare', pattern: /\]/ });
|
|
70
|
+
const Comma = createToken({ name: 'Comma', pattern: /,/ });
|
|
71
|
+
const Colon = createToken({ name: 'Colon', pattern: /:/ });
|
|
72
|
+
const Semicolon = createToken({ name: 'Semicolon', pattern: /;/ });
|
|
73
|
+
const Equals = createToken({ name: 'Equals', pattern: /=/ });
|
|
74
|
+
const Arrow = createToken({ name: 'Arrow', pattern: /->/ });
|
|
75
|
+
const DoubleArrow = createToken({ name: 'DoubleArrow', pattern: /<->/ });
|
|
76
|
+
const UpstreamDownstreamArrow = createToken({ name: 'UpstreamDownstreamArrow', pattern: /U->D|D<-U/ });
|
|
77
|
+
const DownstreamUpstreamArrow = createToken({ name: 'DownstreamUpstreamArrow', pattern: /D->U|U<-D/ });
|
|
78
|
+
// Literals
|
|
79
|
+
const StringLiteral = createToken({ name: 'StringLiteral', pattern: /"[^"]*"/ });
|
|
80
|
+
// Note: Identifier is defined earlier for longer_alt references
|
|
81
|
+
// All tokens in order (order matters for lexer)
|
|
82
|
+
const allTokens = [
|
|
83
|
+
WhiteSpace,
|
|
84
|
+
LineComment,
|
|
85
|
+
BlockComment,
|
|
86
|
+
// Multi-char operators first
|
|
87
|
+
DoubleArrow,
|
|
88
|
+
UpstreamDownstreamArrow,
|
|
89
|
+
DownstreamUpstreamArrow,
|
|
90
|
+
Arrow,
|
|
91
|
+
// Keywords before Identifier
|
|
92
|
+
ContextMap,
|
|
93
|
+
BoundedContext,
|
|
94
|
+
Aggregate,
|
|
95
|
+
Entity,
|
|
96
|
+
ValueObject,
|
|
97
|
+
DomainEvent,
|
|
98
|
+
Command,
|
|
99
|
+
Service,
|
|
100
|
+
Module,
|
|
101
|
+
Partnership,
|
|
102
|
+
SharedKernel,
|
|
103
|
+
CustomerSupplier,
|
|
104
|
+
UpstreamKw,
|
|
105
|
+
DownstreamKw,
|
|
106
|
+
UpstreamShort,
|
|
107
|
+
DownstreamShort,
|
|
108
|
+
OHS,
|
|
109
|
+
PL,
|
|
110
|
+
ACL,
|
|
111
|
+
CF,
|
|
112
|
+
Contains,
|
|
113
|
+
Implements,
|
|
114
|
+
Type,
|
|
115
|
+
State,
|
|
116
|
+
DomainVisionStatement,
|
|
117
|
+
Responsibilities,
|
|
118
|
+
ImplementationTechnology,
|
|
119
|
+
KnowledgeLevel,
|
|
120
|
+
AggregateRoot,
|
|
121
|
+
Def,
|
|
122
|
+
Key,
|
|
123
|
+
Nullable,
|
|
124
|
+
ExposedAggregates,
|
|
125
|
+
ListType,
|
|
126
|
+
SetType,
|
|
127
|
+
AsIs,
|
|
128
|
+
ToBe,
|
|
129
|
+
Meta,
|
|
130
|
+
Concrete,
|
|
131
|
+
// Symbols
|
|
132
|
+
LCurly,
|
|
133
|
+
RCurly,
|
|
134
|
+
LParen,
|
|
135
|
+
RParen,
|
|
136
|
+
LSquare,
|
|
137
|
+
RSquare,
|
|
138
|
+
LAngle,
|
|
139
|
+
RAngle,
|
|
140
|
+
Comma,
|
|
141
|
+
Colon,
|
|
142
|
+
Semicolon,
|
|
143
|
+
Equals,
|
|
144
|
+
// Literals
|
|
145
|
+
StringLiteral,
|
|
146
|
+
Identifier,
|
|
147
|
+
];
|
|
148
|
+
const CMLLexer = new Lexer(allTokens);
|
|
149
|
+
// ============================================
|
|
150
|
+
// PARSER
|
|
151
|
+
// ============================================
|
|
152
|
+
class CMLParserClass extends CstParser {
|
|
153
|
+
constructor() {
|
|
154
|
+
super(allTokens);
|
|
155
|
+
this.performSelfAnalysis();
|
|
156
|
+
}
|
|
157
|
+
// Entry rule
|
|
158
|
+
cmlModel = this.RULE('cmlModel', () => {
|
|
159
|
+
this.MANY(() => {
|
|
160
|
+
this.OR([
|
|
161
|
+
{ ALT: () => this.SUBRULE(this.contextMapDecl) },
|
|
162
|
+
{ ALT: () => this.SUBRULE(this.boundedContextDecl) },
|
|
163
|
+
]);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
// Context Map declaration
|
|
167
|
+
contextMapDecl = this.RULE('contextMapDecl', () => {
|
|
168
|
+
this.CONSUME(ContextMap);
|
|
169
|
+
this.OPTION(() => this.CONSUME(Identifier));
|
|
170
|
+
this.CONSUME(LCurly);
|
|
171
|
+
this.MANY(() => {
|
|
172
|
+
this.OR([
|
|
173
|
+
{ ALT: () => this.SUBRULE(this.contextMapType) },
|
|
174
|
+
{ ALT: () => this.SUBRULE(this.contextMapState) },
|
|
175
|
+
{ ALT: () => this.SUBRULE(this.contextMapContains) },
|
|
176
|
+
{ ALT: () => this.SUBRULE(this.relationshipDecl) },
|
|
177
|
+
]);
|
|
178
|
+
});
|
|
179
|
+
this.CONSUME(RCurly);
|
|
180
|
+
});
|
|
181
|
+
// type = SYSTEM_LANDSCAPE | ORGANIZATIONAL
|
|
182
|
+
contextMapType = this.RULE('contextMapType', () => {
|
|
183
|
+
this.CONSUME(Type);
|
|
184
|
+
this.OPTION(() => this.CONSUME(Equals));
|
|
185
|
+
this.CONSUME(Identifier); // SYSTEM_LANDSCAPE or ORGANIZATIONAL
|
|
186
|
+
});
|
|
187
|
+
// state = AS_IS | TO_BE
|
|
188
|
+
contextMapState = this.RULE('contextMapState', () => {
|
|
189
|
+
this.CONSUME(State);
|
|
190
|
+
this.OPTION(() => this.CONSUME(Equals));
|
|
191
|
+
this.OR([
|
|
192
|
+
{ ALT: () => this.CONSUME(AsIs) },
|
|
193
|
+
{ ALT: () => this.CONSUME(ToBe) },
|
|
194
|
+
]);
|
|
195
|
+
});
|
|
196
|
+
contextMapContains = this.RULE('contextMapContains', () => {
|
|
197
|
+
this.CONSUME(Contains);
|
|
198
|
+
this.AT_LEAST_ONE_SEP({
|
|
199
|
+
SEP: Comma,
|
|
200
|
+
DEF: () => this.CONSUME(Identifier),
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
// Relationship declarations
|
|
204
|
+
relationshipDecl = this.RULE('relationshipDecl', () => {
|
|
205
|
+
this.OR([
|
|
206
|
+
{ ALT: () => this.SUBRULE(this.symmetricRelationship) },
|
|
207
|
+
{ ALT: () => this.SUBRULE(this.upstreamDownstreamRelationship) },
|
|
208
|
+
]);
|
|
209
|
+
});
|
|
210
|
+
symmetricRelationship = this.RULE('symmetricRelationship', () => {
|
|
211
|
+
this.CONSUME(Identifier);
|
|
212
|
+
this.OR([
|
|
213
|
+
{ ALT: () => this.CONSUME(Partnership) },
|
|
214
|
+
{ ALT: () => this.CONSUME(SharedKernel) },
|
|
215
|
+
]);
|
|
216
|
+
this.CONSUME2(Identifier);
|
|
217
|
+
});
|
|
218
|
+
// Upstream-downstream: Context1 [U,OHS,PL] -> [D,ACL] Context2
|
|
219
|
+
upstreamDownstreamRelationship = this.RULE('upstreamDownstreamRelationship', () => {
|
|
220
|
+
// First context name
|
|
221
|
+
this.CONSUME(Identifier);
|
|
222
|
+
// Optional upstream patterns [U,OHS,PL]
|
|
223
|
+
this.OPTION(() => {
|
|
224
|
+
this.CONSUME(LSquare);
|
|
225
|
+
this.SUBRULE(this.upstreamPatterns);
|
|
226
|
+
this.CONSUME(RSquare);
|
|
227
|
+
});
|
|
228
|
+
// Arrow
|
|
229
|
+
this.CONSUME(Arrow);
|
|
230
|
+
// Optional downstream patterns [D,ACL]
|
|
231
|
+
this.OPTION2(() => {
|
|
232
|
+
this.CONSUME2(LSquare);
|
|
233
|
+
this.SUBRULE(this.downstreamPatterns);
|
|
234
|
+
this.CONSUME2(RSquare);
|
|
235
|
+
});
|
|
236
|
+
// Second context name
|
|
237
|
+
this.CONSUME2(Identifier);
|
|
238
|
+
// Optional properties block
|
|
239
|
+
this.OPTION3(() => {
|
|
240
|
+
this.CONSUME(LCurly);
|
|
241
|
+
this.MANY(() => this.SUBRULE(this.relationshipProperty));
|
|
242
|
+
this.CONSUME(RCurly);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
// [U,OHS,PL] or [U] or [Upstream,OHS]
|
|
246
|
+
upstreamPatterns = this.RULE('upstreamPatterns', () => {
|
|
247
|
+
this.OR([
|
|
248
|
+
{ ALT: () => this.CONSUME(UpstreamShort) },
|
|
249
|
+
{ ALT: () => this.CONSUME(UpstreamKw) },
|
|
250
|
+
]);
|
|
251
|
+
this.MANY(() => {
|
|
252
|
+
this.CONSUME(Comma);
|
|
253
|
+
this.OR2([
|
|
254
|
+
{ ALT: () => this.CONSUME(OHS) },
|
|
255
|
+
{ ALT: () => this.CONSUME(PL) },
|
|
256
|
+
]);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
// [D,ACL] or [D] or [Downstream,CF]
|
|
260
|
+
downstreamPatterns = this.RULE('downstreamPatterns', () => {
|
|
261
|
+
this.OR([
|
|
262
|
+
{ ALT: () => this.CONSUME(DownstreamShort) },
|
|
263
|
+
{ ALT: () => this.CONSUME(DownstreamKw) },
|
|
264
|
+
]);
|
|
265
|
+
this.MANY(() => {
|
|
266
|
+
this.CONSUME(Comma);
|
|
267
|
+
this.OR2([
|
|
268
|
+
{ ALT: () => this.CONSUME(ACL) },
|
|
269
|
+
{ ALT: () => this.CONSUME(CF) },
|
|
270
|
+
]);
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
relationshipProperty = this.RULE('relationshipProperty', () => {
|
|
274
|
+
this.OR([
|
|
275
|
+
{ ALT: () => this.SUBRULE(this.exposedAggregatesProperty) },
|
|
276
|
+
]);
|
|
277
|
+
});
|
|
278
|
+
exposedAggregatesProperty = this.RULE('exposedAggregatesProperty', () => {
|
|
279
|
+
this.CONSUME(ExposedAggregates);
|
|
280
|
+
this.CONSUME(Equals);
|
|
281
|
+
this.CONSUME(Identifier);
|
|
282
|
+
this.MANY(() => {
|
|
283
|
+
this.CONSUME(Comma);
|
|
284
|
+
this.CONSUME2(Identifier);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
// Bounded Context declaration
|
|
288
|
+
boundedContextDecl = this.RULE('boundedContextDecl', () => {
|
|
289
|
+
this.CONSUME(BoundedContext);
|
|
290
|
+
this.CONSUME(Identifier);
|
|
291
|
+
this.OPTION(() => {
|
|
292
|
+
this.CONSUME(Implements);
|
|
293
|
+
this.CONSUME2(Identifier);
|
|
294
|
+
});
|
|
295
|
+
this.CONSUME(LCurly);
|
|
296
|
+
this.MANY(() => {
|
|
297
|
+
this.OR([
|
|
298
|
+
{ ALT: () => this.SUBRULE(this.domainVisionStatementDecl) },
|
|
299
|
+
{ ALT: () => this.SUBRULE(this.responsibilitiesDecl) },
|
|
300
|
+
{ ALT: () => this.SUBRULE(this.implementationTechnologyDecl) },
|
|
301
|
+
{ ALT: () => this.SUBRULE(this.knowledgeLevelDecl) },
|
|
302
|
+
{ ALT: () => this.SUBRULE(this.aggregateDecl) },
|
|
303
|
+
{ ALT: () => this.SUBRULE(this.moduleDecl) },
|
|
304
|
+
]);
|
|
305
|
+
});
|
|
306
|
+
this.CONSUME(RCurly);
|
|
307
|
+
});
|
|
308
|
+
domainVisionStatementDecl = this.RULE('domainVisionStatementDecl', () => {
|
|
309
|
+
this.CONSUME(DomainVisionStatement);
|
|
310
|
+
this.CONSUME(Equals);
|
|
311
|
+
this.CONSUME(StringLiteral);
|
|
312
|
+
});
|
|
313
|
+
responsibilitiesDecl = this.RULE('responsibilitiesDecl', () => {
|
|
314
|
+
this.CONSUME(Responsibilities);
|
|
315
|
+
this.CONSUME(Equals);
|
|
316
|
+
this.CONSUME(StringLiteral);
|
|
317
|
+
this.MANY(() => {
|
|
318
|
+
this.CONSUME(Comma);
|
|
319
|
+
this.CONSUME2(StringLiteral);
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
implementationTechnologyDecl = this.RULE('implementationTechnologyDecl', () => {
|
|
323
|
+
this.CONSUME(ImplementationTechnology);
|
|
324
|
+
this.CONSUME(Equals);
|
|
325
|
+
this.CONSUME(StringLiteral);
|
|
326
|
+
});
|
|
327
|
+
knowledgeLevelDecl = this.RULE('knowledgeLevelDecl', () => {
|
|
328
|
+
this.CONSUME(KnowledgeLevel);
|
|
329
|
+
this.CONSUME(Equals);
|
|
330
|
+
this.OR([
|
|
331
|
+
{ ALT: () => this.CONSUME(Meta) },
|
|
332
|
+
{ ALT: () => this.CONSUME(Concrete) },
|
|
333
|
+
]);
|
|
334
|
+
});
|
|
335
|
+
// Module declaration
|
|
336
|
+
moduleDecl = this.RULE('moduleDecl', () => {
|
|
337
|
+
this.CONSUME(Module);
|
|
338
|
+
this.CONSUME(Identifier);
|
|
339
|
+
this.CONSUME(LCurly);
|
|
340
|
+
this.MANY(() => this.SUBRULE(this.aggregateDecl));
|
|
341
|
+
this.CONSUME(RCurly);
|
|
342
|
+
});
|
|
343
|
+
// Aggregate declaration
|
|
344
|
+
aggregateDecl = this.RULE('aggregateDecl', () => {
|
|
345
|
+
this.CONSUME(Aggregate);
|
|
346
|
+
this.CONSUME(Identifier);
|
|
347
|
+
this.CONSUME(LCurly);
|
|
348
|
+
this.MANY(() => {
|
|
349
|
+
this.OR([
|
|
350
|
+
{ ALT: () => this.SUBRULE(this.responsibilitiesDecl) },
|
|
351
|
+
{ ALT: () => this.SUBRULE(this.knowledgeLevelDecl) },
|
|
352
|
+
{ ALT: () => this.SUBRULE(this.entityDecl) },
|
|
353
|
+
{ ALT: () => this.SUBRULE(this.valueObjectDecl) },
|
|
354
|
+
{ ALT: () => this.SUBRULE(this.domainEventDecl) },
|
|
355
|
+
{ ALT: () => this.SUBRULE(this.commandDecl) },
|
|
356
|
+
{ ALT: () => this.SUBRULE(this.serviceDecl) },
|
|
357
|
+
]);
|
|
358
|
+
});
|
|
359
|
+
this.CONSUME(RCurly);
|
|
360
|
+
});
|
|
361
|
+
// Entity declaration
|
|
362
|
+
entityDecl = this.RULE('entityDecl', () => {
|
|
363
|
+
this.CONSUME(Entity);
|
|
364
|
+
this.CONSUME(Identifier);
|
|
365
|
+
this.OPTION(() => {
|
|
366
|
+
this.CONSUME(LCurly);
|
|
367
|
+
this.MANY(() => {
|
|
368
|
+
this.OR([
|
|
369
|
+
{ ALT: () => this.SUBRULE(this.aggregateRootDecl) },
|
|
370
|
+
{ ALT: () => this.SUBRULE(this.attributeDecl) },
|
|
371
|
+
{ ALT: () => this.SUBRULE(this.operationDecl) },
|
|
372
|
+
]);
|
|
373
|
+
});
|
|
374
|
+
this.CONSUME(RCurly);
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
aggregateRootDecl = this.RULE('aggregateRootDecl', () => {
|
|
378
|
+
this.CONSUME(AggregateRoot);
|
|
379
|
+
});
|
|
380
|
+
// Value Object declaration
|
|
381
|
+
valueObjectDecl = this.RULE('valueObjectDecl', () => {
|
|
382
|
+
this.CONSUME(ValueObject);
|
|
383
|
+
this.CONSUME(Identifier);
|
|
384
|
+
this.OPTION(() => {
|
|
385
|
+
this.CONSUME(LCurly);
|
|
386
|
+
this.MANY(() => this.SUBRULE(this.attributeDecl));
|
|
387
|
+
this.CONSUME(RCurly);
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
// Domain Event declaration
|
|
391
|
+
domainEventDecl = this.RULE('domainEventDecl', () => {
|
|
392
|
+
this.CONSUME(DomainEvent);
|
|
393
|
+
this.CONSUME(Identifier);
|
|
394
|
+
this.OPTION(() => {
|
|
395
|
+
this.CONSUME(LCurly);
|
|
396
|
+
this.MANY(() => this.SUBRULE(this.attributeDecl));
|
|
397
|
+
this.CONSUME(RCurly);
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
// Command declaration
|
|
401
|
+
commandDecl = this.RULE('commandDecl', () => {
|
|
402
|
+
this.CONSUME(Command);
|
|
403
|
+
this.CONSUME(Identifier);
|
|
404
|
+
this.OPTION(() => {
|
|
405
|
+
this.CONSUME(LCurly);
|
|
406
|
+
this.MANY(() => this.SUBRULE(this.attributeDecl));
|
|
407
|
+
this.CONSUME(RCurly);
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
// Service declaration
|
|
411
|
+
serviceDecl = this.RULE('serviceDecl', () => {
|
|
412
|
+
this.CONSUME(Service);
|
|
413
|
+
this.CONSUME(Identifier);
|
|
414
|
+
this.OPTION(() => {
|
|
415
|
+
this.CONSUME(LCurly);
|
|
416
|
+
this.MANY(() => this.SUBRULE(this.operationDecl));
|
|
417
|
+
this.CONSUME(RCurly);
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
// Attribute declaration: Type name key? nullable? ;?
|
|
421
|
+
// Also supports: List<Type> name or Set<Type> name
|
|
422
|
+
// Attribute name - can be identifier or certain keywords used as names
|
|
423
|
+
attributeName = this.RULE('attributeName', () => {
|
|
424
|
+
this.OR([
|
|
425
|
+
{ ALT: () => this.CONSUME(Identifier) },
|
|
426
|
+
{ ALT: () => this.CONSUME(Type) }, // 'type' is often used as attr name
|
|
427
|
+
{ ALT: () => this.CONSUME(State) }, // 'state' is often used as attr name
|
|
428
|
+
{ ALT: () => this.CONSUME(Key) }, // 'key' can be an attr name
|
|
429
|
+
{ ALT: () => this.CONSUME(Service) }, // 'service' can be an attr name
|
|
430
|
+
{ ALT: () => this.CONSUME(Module) }, // 'module' can be an attr name
|
|
431
|
+
]);
|
|
432
|
+
});
|
|
433
|
+
attributeDecl = this.RULE('attributeDecl', () => {
|
|
434
|
+
// Type (can be collection type like List<String>)
|
|
435
|
+
this.OR([
|
|
436
|
+
{
|
|
437
|
+
ALT: () => {
|
|
438
|
+
this.OR2([
|
|
439
|
+
{ ALT: () => this.CONSUME(ListType) },
|
|
440
|
+
{ ALT: () => this.CONSUME(SetType) },
|
|
441
|
+
]);
|
|
442
|
+
this.CONSUME(LAngle);
|
|
443
|
+
this.CONSUME(Identifier); // inner type
|
|
444
|
+
this.CONSUME(RAngle);
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
{ ALT: () => this.CONSUME2(Identifier) }, // simple type
|
|
448
|
+
]);
|
|
449
|
+
// Name - can be identifier or keyword
|
|
450
|
+
this.SUBRULE(this.attributeName);
|
|
451
|
+
// Optional modifiers (after name)
|
|
452
|
+
this.OPTION(() => this.CONSUME2(Key));
|
|
453
|
+
this.OPTION2(() => this.CONSUME(Nullable));
|
|
454
|
+
// Optional semicolon
|
|
455
|
+
this.OPTION3(() => this.CONSUME(Semicolon));
|
|
456
|
+
});
|
|
457
|
+
// Operation declaration
|
|
458
|
+
operationDecl = this.RULE('operationDecl', () => {
|
|
459
|
+
this.CONSUME(Def);
|
|
460
|
+
this.CONSUME(Identifier); // return type or void
|
|
461
|
+
this.CONSUME2(Identifier); // method name
|
|
462
|
+
this.CONSUME(LParen);
|
|
463
|
+
this.OPTION(() => {
|
|
464
|
+
this.SUBRULE(this.parameterDecl);
|
|
465
|
+
this.MANY(() => {
|
|
466
|
+
this.CONSUME(Comma);
|
|
467
|
+
this.SUBRULE2(this.parameterDecl);
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
this.CONSUME(RParen);
|
|
471
|
+
});
|
|
472
|
+
parameterDecl = this.RULE('parameterDecl', () => {
|
|
473
|
+
this.CONSUME(Identifier); // type
|
|
474
|
+
this.CONSUME2(Identifier); // name
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
// Create parser instance
|
|
478
|
+
const parser = new CMLParserClass();
|
|
479
|
+
function getToken(children, tokenName, index = 0) {
|
|
480
|
+
const tokens = children[tokenName];
|
|
481
|
+
if (tokens && tokens.length > index) {
|
|
482
|
+
return tokens[index];
|
|
483
|
+
}
|
|
484
|
+
return undefined;
|
|
485
|
+
}
|
|
486
|
+
function getNode(children, nodeName, index = 0) {
|
|
487
|
+
const nodes = children[nodeName];
|
|
488
|
+
if (nodes && nodes.length > index) {
|
|
489
|
+
return nodes[index];
|
|
490
|
+
}
|
|
491
|
+
return undefined;
|
|
492
|
+
}
|
|
493
|
+
function getAllNodes(children, nodeName) {
|
|
494
|
+
const nodes = children[nodeName];
|
|
495
|
+
return (nodes || []);
|
|
496
|
+
}
|
|
497
|
+
function getAllTokens(children, tokenName) {
|
|
498
|
+
const tokens = children[tokenName];
|
|
499
|
+
return (tokens || []);
|
|
500
|
+
}
|
|
501
|
+
function stripQuotes(str) {
|
|
502
|
+
return str.replace(/^"|"$/g, '');
|
|
503
|
+
}
|
|
504
|
+
// Visitor to build typed model from CST
|
|
505
|
+
function visitCmlModel(cst) {
|
|
506
|
+
const model = {
|
|
507
|
+
name: 'Untitled',
|
|
508
|
+
boundedContexts: [],
|
|
509
|
+
};
|
|
510
|
+
const children = cst.children;
|
|
511
|
+
// Process context maps
|
|
512
|
+
const contextMapNodes = getAllNodes(children, 'contextMapDecl');
|
|
513
|
+
if (contextMapNodes.length > 0) {
|
|
514
|
+
model.contextMap = visitContextMap(contextMapNodes[0]);
|
|
515
|
+
}
|
|
516
|
+
// Process bounded contexts
|
|
517
|
+
const bcNodes = getAllNodes(children, 'boundedContextDecl');
|
|
518
|
+
for (const bcNode of bcNodes) {
|
|
519
|
+
model.boundedContexts.push(visitBoundedContext(bcNode));
|
|
520
|
+
}
|
|
521
|
+
return model;
|
|
522
|
+
}
|
|
523
|
+
function visitContextMap(cst) {
|
|
524
|
+
const children = cst.children;
|
|
525
|
+
const nameToken = getToken(children, 'Identifier');
|
|
526
|
+
const contextMap = {
|
|
527
|
+
id: uuidv4(),
|
|
528
|
+
name: nameToken?.image || 'MainContextMap',
|
|
529
|
+
boundedContexts: [],
|
|
530
|
+
relationships: [],
|
|
531
|
+
};
|
|
532
|
+
// Process state
|
|
533
|
+
const stateNodes = getAllNodes(children, 'contextMapState');
|
|
534
|
+
if (stateNodes.length > 0) {
|
|
535
|
+
const stateChildren = stateNodes[0].children;
|
|
536
|
+
if (stateChildren['AsIs']) {
|
|
537
|
+
contextMap.state = 'AS_IS';
|
|
538
|
+
}
|
|
539
|
+
else if (stateChildren['ToBe']) {
|
|
540
|
+
contextMap.state = 'TO_BE';
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
// Process contains
|
|
544
|
+
const containsNodes = getAllNodes(children, 'contextMapContains');
|
|
545
|
+
for (const containsNode of containsNodes) {
|
|
546
|
+
const identifiers = getAllTokens(containsNode.children, 'Identifier');
|
|
547
|
+
for (const id of identifiers) {
|
|
548
|
+
contextMap.boundedContexts.push(id.image);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
// Process relationships
|
|
552
|
+
const relNodes = getAllNodes(children, 'relationshipDecl');
|
|
553
|
+
for (const relNode of relNodes) {
|
|
554
|
+
const rel = visitRelationship(relNode);
|
|
555
|
+
if (rel) {
|
|
556
|
+
contextMap.relationships.push(rel);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return contextMap;
|
|
560
|
+
}
|
|
561
|
+
function visitRelationship(cst) {
|
|
562
|
+
const children = cst.children;
|
|
563
|
+
// Symmetric relationship
|
|
564
|
+
const symRelNode = getNode(children, 'symmetricRelationship');
|
|
565
|
+
if (symRelNode) {
|
|
566
|
+
const symChildren = symRelNode.children;
|
|
567
|
+
const identifiers = getAllTokens(symChildren, 'Identifier');
|
|
568
|
+
let type = 'Partnership';
|
|
569
|
+
if (symChildren['SharedKernel']) {
|
|
570
|
+
type = 'SharedKernel';
|
|
571
|
+
}
|
|
572
|
+
return {
|
|
573
|
+
id: uuidv4(),
|
|
574
|
+
type,
|
|
575
|
+
participant1: identifiers[0]?.image || '',
|
|
576
|
+
participant2: identifiers[1]?.image || '',
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
// Upstream-Downstream relationship
|
|
580
|
+
// Format: Context1 [U,OHS,PL] -> [D,ACL] Context2
|
|
581
|
+
const udRelNode = getNode(children, 'upstreamDownstreamRelationship');
|
|
582
|
+
if (udRelNode) {
|
|
583
|
+
const udChildren = udRelNode.children;
|
|
584
|
+
const identifiers = getAllTokens(udChildren, 'Identifier');
|
|
585
|
+
// First identifier is upstream, second is downstream
|
|
586
|
+
const rel = {
|
|
587
|
+
id: uuidv4(),
|
|
588
|
+
type: 'UpstreamDownstream',
|
|
589
|
+
upstream: identifiers[0]?.image || '',
|
|
590
|
+
downstream: identifiers[1]?.image || '',
|
|
591
|
+
upstreamPatterns: [],
|
|
592
|
+
downstreamPatterns: [],
|
|
593
|
+
};
|
|
594
|
+
// Get patterns
|
|
595
|
+
const upstreamPatternsNode = getNode(udChildren, 'upstreamPatterns');
|
|
596
|
+
if (upstreamPatternsNode) {
|
|
597
|
+
const upChildren = upstreamPatternsNode.children;
|
|
598
|
+
const ohsTokens = getAllTokens(upChildren, 'OHS');
|
|
599
|
+
const plTokens = getAllTokens(upChildren, 'PL');
|
|
600
|
+
if (ohsTokens.length > 0)
|
|
601
|
+
rel.upstreamPatterns.push('OHS');
|
|
602
|
+
if (plTokens.length > 0)
|
|
603
|
+
rel.upstreamPatterns.push('PL');
|
|
604
|
+
}
|
|
605
|
+
const downstreamPatternsNode = getNode(udChildren, 'downstreamPatterns');
|
|
606
|
+
if (downstreamPatternsNode) {
|
|
607
|
+
const downChildren = downstreamPatternsNode.children;
|
|
608
|
+
const aclTokens = getAllTokens(downChildren, 'ACL');
|
|
609
|
+
const cfTokens = getAllTokens(downChildren, 'CF');
|
|
610
|
+
if (aclTokens.length > 0)
|
|
611
|
+
rel.downstreamPatterns.push('ACL');
|
|
612
|
+
if (cfTokens.length > 0)
|
|
613
|
+
rel.downstreamPatterns.push('CF');
|
|
614
|
+
}
|
|
615
|
+
// Get exposed aggregates
|
|
616
|
+
const relPropNodes = getAllNodes(udChildren, 'relationshipProperty');
|
|
617
|
+
for (const propNode of relPropNodes) {
|
|
618
|
+
const expAggNode = getNode(propNode.children, 'exposedAggregatesProperty');
|
|
619
|
+
if (expAggNode) {
|
|
620
|
+
const aggIds = getAllTokens(expAggNode.children, 'Identifier');
|
|
621
|
+
rel.exposedAggregates = aggIds.map(t => t.image);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
return rel;
|
|
625
|
+
}
|
|
626
|
+
return undefined;
|
|
627
|
+
}
|
|
628
|
+
function visitBoundedContext(cst) {
|
|
629
|
+
const children = cst.children;
|
|
630
|
+
const nameToken = getToken(children, 'Identifier');
|
|
631
|
+
const bc = {
|
|
632
|
+
id: uuidv4(),
|
|
633
|
+
name: nameToken?.image || 'UnnamedContext',
|
|
634
|
+
aggregates: [],
|
|
635
|
+
modules: [],
|
|
636
|
+
};
|
|
637
|
+
// Domain vision statement
|
|
638
|
+
const dvsNodes = getAllNodes(children, 'domainVisionStatementDecl');
|
|
639
|
+
if (dvsNodes.length > 0) {
|
|
640
|
+
const strToken = getToken(dvsNodes[0].children, 'StringLiteral');
|
|
641
|
+
if (strToken) {
|
|
642
|
+
bc.domainVisionStatement = stripQuotes(strToken.image);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
// Responsibilities
|
|
646
|
+
const respNodes = getAllNodes(children, 'responsibilitiesDecl');
|
|
647
|
+
if (respNodes.length > 0) {
|
|
648
|
+
const strTokens = getAllTokens(respNodes[0].children, 'StringLiteral');
|
|
649
|
+
bc.responsibilities = strTokens.map(t => stripQuotes(t.image));
|
|
650
|
+
}
|
|
651
|
+
// Implementation technology
|
|
652
|
+
const implTechNodes = getAllNodes(children, 'implementationTechnologyDecl');
|
|
653
|
+
if (implTechNodes.length > 0) {
|
|
654
|
+
const strToken = getToken(implTechNodes[0].children, 'StringLiteral');
|
|
655
|
+
if (strToken) {
|
|
656
|
+
bc.implementationTechnology = stripQuotes(strToken.image);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
// Knowledge level
|
|
660
|
+
const klNodes = getAllNodes(children, 'knowledgeLevelDecl');
|
|
661
|
+
if (klNodes.length > 0) {
|
|
662
|
+
const klChildren = klNodes[0].children;
|
|
663
|
+
if (klChildren['Meta']) {
|
|
664
|
+
bc.knowledgeLevel = 'META';
|
|
665
|
+
}
|
|
666
|
+
else if (klChildren['Concrete']) {
|
|
667
|
+
bc.knowledgeLevel = 'CONCRETE';
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
// Aggregates
|
|
671
|
+
const aggNodes = getAllNodes(children, 'aggregateDecl');
|
|
672
|
+
for (const aggNode of aggNodes) {
|
|
673
|
+
bc.aggregates.push(visitAggregate(aggNode));
|
|
674
|
+
}
|
|
675
|
+
// Modules
|
|
676
|
+
const modNodes = getAllNodes(children, 'moduleDecl');
|
|
677
|
+
for (const modNode of modNodes) {
|
|
678
|
+
const modChildren = modNode.children;
|
|
679
|
+
const modName = getToken(modChildren, 'Identifier')?.image || 'UnnamedModule';
|
|
680
|
+
const module = {
|
|
681
|
+
id: uuidv4(),
|
|
682
|
+
name: modName,
|
|
683
|
+
aggregates: [],
|
|
684
|
+
};
|
|
685
|
+
const modAggNodes = getAllNodes(modChildren, 'aggregateDecl');
|
|
686
|
+
for (const aggNode of modAggNodes) {
|
|
687
|
+
module.aggregates.push(visitAggregate(aggNode));
|
|
688
|
+
}
|
|
689
|
+
bc.modules.push(module);
|
|
690
|
+
}
|
|
691
|
+
return bc;
|
|
692
|
+
}
|
|
693
|
+
function visitAggregate(cst) {
|
|
694
|
+
const children = cst.children;
|
|
695
|
+
const nameToken = getToken(children, 'Identifier');
|
|
696
|
+
const agg = {
|
|
697
|
+
id: uuidv4(),
|
|
698
|
+
name: nameToken?.image || 'UnnamedAggregate',
|
|
699
|
+
entities: [],
|
|
700
|
+
valueObjects: [],
|
|
701
|
+
domainEvents: [],
|
|
702
|
+
commands: [],
|
|
703
|
+
services: [],
|
|
704
|
+
};
|
|
705
|
+
// Responsibilities
|
|
706
|
+
const respNodes = getAllNodes(children, 'responsibilitiesDecl');
|
|
707
|
+
if (respNodes.length > 0) {
|
|
708
|
+
const strTokens = getAllTokens(respNodes[0].children, 'StringLiteral');
|
|
709
|
+
agg.responsibilities = strTokens.map(t => stripQuotes(t.image));
|
|
710
|
+
}
|
|
711
|
+
// Knowledge level
|
|
712
|
+
const klNodes = getAllNodes(children, 'knowledgeLevelDecl');
|
|
713
|
+
if (klNodes.length > 0) {
|
|
714
|
+
const klChildren = klNodes[0].children;
|
|
715
|
+
if (klChildren['Meta']) {
|
|
716
|
+
agg.knowledgeLevel = 'META';
|
|
717
|
+
}
|
|
718
|
+
else if (klChildren['Concrete']) {
|
|
719
|
+
agg.knowledgeLevel = 'CONCRETE';
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
// Entities
|
|
723
|
+
const entityNodes = getAllNodes(children, 'entityDecl');
|
|
724
|
+
for (const entityNode of entityNodes) {
|
|
725
|
+
const entity = visitEntity(entityNode);
|
|
726
|
+
if (entity.aggregateRoot) {
|
|
727
|
+
agg.aggregateRoot = entity;
|
|
728
|
+
}
|
|
729
|
+
agg.entities.push(entity);
|
|
730
|
+
}
|
|
731
|
+
// Value Objects
|
|
732
|
+
const voNodes = getAllNodes(children, 'valueObjectDecl');
|
|
733
|
+
for (const voNode of voNodes) {
|
|
734
|
+
agg.valueObjects.push(visitValueObject(voNode));
|
|
735
|
+
}
|
|
736
|
+
// Domain Events
|
|
737
|
+
const eventNodes = getAllNodes(children, 'domainEventDecl');
|
|
738
|
+
for (const eventNode of eventNodes) {
|
|
739
|
+
agg.domainEvents.push(visitDomainEvent(eventNode));
|
|
740
|
+
}
|
|
741
|
+
// Commands
|
|
742
|
+
const cmdNodes = getAllNodes(children, 'commandDecl');
|
|
743
|
+
for (const cmdNode of cmdNodes) {
|
|
744
|
+
agg.commands.push(visitCommand(cmdNode));
|
|
745
|
+
}
|
|
746
|
+
// Services
|
|
747
|
+
const svcNodes = getAllNodes(children, 'serviceDecl');
|
|
748
|
+
for (const svcNode of svcNodes) {
|
|
749
|
+
agg.services.push(visitService(svcNode));
|
|
750
|
+
}
|
|
751
|
+
return agg;
|
|
752
|
+
}
|
|
753
|
+
function visitEntity(cst) {
|
|
754
|
+
const children = cst.children;
|
|
755
|
+
const nameToken = getToken(children, 'Identifier');
|
|
756
|
+
const entity = {
|
|
757
|
+
id: uuidv4(),
|
|
758
|
+
name: nameToken?.image || 'UnnamedEntity',
|
|
759
|
+
attributes: [],
|
|
760
|
+
operations: [],
|
|
761
|
+
};
|
|
762
|
+
// Check for aggregate root
|
|
763
|
+
const aggRootNodes = getAllNodes(children, 'aggregateRootDecl');
|
|
764
|
+
if (aggRootNodes.length > 0) {
|
|
765
|
+
entity.aggregateRoot = true;
|
|
766
|
+
}
|
|
767
|
+
// Attributes
|
|
768
|
+
const attrNodes = getAllNodes(children, 'attributeDecl');
|
|
769
|
+
for (const attrNode of attrNodes) {
|
|
770
|
+
entity.attributes.push(visitAttribute(attrNode));
|
|
771
|
+
}
|
|
772
|
+
// Operations
|
|
773
|
+
const opNodes = getAllNodes(children, 'operationDecl');
|
|
774
|
+
for (const opNode of opNodes) {
|
|
775
|
+
entity.operations.push(visitOperation(opNode));
|
|
776
|
+
}
|
|
777
|
+
return entity;
|
|
778
|
+
}
|
|
779
|
+
function visitValueObject(cst) {
|
|
780
|
+
const children = cst.children;
|
|
781
|
+
const nameToken = getToken(children, 'Identifier');
|
|
782
|
+
const vo = {
|
|
783
|
+
id: uuidv4(),
|
|
784
|
+
name: nameToken?.image || 'UnnamedValueObject',
|
|
785
|
+
attributes: [],
|
|
786
|
+
};
|
|
787
|
+
// Attributes
|
|
788
|
+
const attrNodes = getAllNodes(children, 'attributeDecl');
|
|
789
|
+
for (const attrNode of attrNodes) {
|
|
790
|
+
vo.attributes.push(visitAttribute(attrNode));
|
|
791
|
+
}
|
|
792
|
+
return vo;
|
|
793
|
+
}
|
|
794
|
+
function visitDomainEvent(cst) {
|
|
795
|
+
const children = cst.children;
|
|
796
|
+
const nameToken = getToken(children, 'Identifier');
|
|
797
|
+
const event = {
|
|
798
|
+
id: uuidv4(),
|
|
799
|
+
name: nameToken?.image || 'UnnamedEvent',
|
|
800
|
+
attributes: [],
|
|
801
|
+
};
|
|
802
|
+
// Attributes
|
|
803
|
+
const attrNodes = getAllNodes(children, 'attributeDecl');
|
|
804
|
+
for (const attrNode of attrNodes) {
|
|
805
|
+
event.attributes.push(visitAttribute(attrNode));
|
|
806
|
+
}
|
|
807
|
+
return event;
|
|
808
|
+
}
|
|
809
|
+
function visitCommand(cst) {
|
|
810
|
+
const children = cst.children;
|
|
811
|
+
const nameToken = getToken(children, 'Identifier');
|
|
812
|
+
const cmd = {
|
|
813
|
+
id: uuidv4(),
|
|
814
|
+
name: nameToken?.image || 'UnnamedCommand',
|
|
815
|
+
attributes: [],
|
|
816
|
+
};
|
|
817
|
+
// Attributes
|
|
818
|
+
const attrNodes = getAllNodes(children, 'attributeDecl');
|
|
819
|
+
for (const attrNode of attrNodes) {
|
|
820
|
+
cmd.attributes.push(visitAttribute(attrNode));
|
|
821
|
+
}
|
|
822
|
+
return cmd;
|
|
823
|
+
}
|
|
824
|
+
function visitService(cst) {
|
|
825
|
+
const children = cst.children;
|
|
826
|
+
const nameToken = getToken(children, 'Identifier');
|
|
827
|
+
const svc = {
|
|
828
|
+
id: uuidv4(),
|
|
829
|
+
name: nameToken?.image || 'UnnamedService',
|
|
830
|
+
operations: [],
|
|
831
|
+
};
|
|
832
|
+
// Operations
|
|
833
|
+
const opNodes = getAllNodes(children, 'operationDecl');
|
|
834
|
+
for (const opNode of opNodes) {
|
|
835
|
+
svc.operations.push(visitOperation(opNode));
|
|
836
|
+
}
|
|
837
|
+
return svc;
|
|
838
|
+
}
|
|
839
|
+
function visitAttribute(cst) {
|
|
840
|
+
const children = cst.children;
|
|
841
|
+
const identifiers = getAllTokens(children, 'Identifier');
|
|
842
|
+
let type;
|
|
843
|
+
let name;
|
|
844
|
+
// Extract attribute name from attributeName subrule
|
|
845
|
+
const attrNameNode = getNode(children, 'attributeName');
|
|
846
|
+
if (attrNameNode) {
|
|
847
|
+
const nameChildren = attrNameNode.children;
|
|
848
|
+
// Try to get name from any of the possible tokens
|
|
849
|
+
const nameToken = getToken(nameChildren, 'Identifier') ||
|
|
850
|
+
getToken(nameChildren, 'Type') ||
|
|
851
|
+
getToken(nameChildren, 'State') ||
|
|
852
|
+
getToken(nameChildren, 'Key') ||
|
|
853
|
+
getToken(nameChildren, 'Service') ||
|
|
854
|
+
getToken(nameChildren, 'Module');
|
|
855
|
+
name = nameToken?.image || 'unnamed';
|
|
856
|
+
}
|
|
857
|
+
else {
|
|
858
|
+
// Fallback for old structure
|
|
859
|
+
name = identifiers[1]?.image || 'unnamed';
|
|
860
|
+
}
|
|
861
|
+
// Check for collection types (List<Type> or Set<Type>)
|
|
862
|
+
if (children['ListType'] || children['SetType']) {
|
|
863
|
+
const collectionType = children['ListType'] ? 'List' : 'Set';
|
|
864
|
+
const innerType = identifiers[0]?.image || 'Object';
|
|
865
|
+
type = `${collectionType}<${innerType}>`;
|
|
866
|
+
}
|
|
867
|
+
else {
|
|
868
|
+
// Simple type: Type name
|
|
869
|
+
type = identifiers[0]?.image || 'String';
|
|
870
|
+
}
|
|
871
|
+
const attr = { type, name };
|
|
872
|
+
if (children['Key']) {
|
|
873
|
+
attr.key = true;
|
|
874
|
+
}
|
|
875
|
+
if (children['Nullable']) {
|
|
876
|
+
attr.nullable = true;
|
|
877
|
+
}
|
|
878
|
+
return attr;
|
|
879
|
+
}
|
|
880
|
+
function visitOperation(cst) {
|
|
881
|
+
const children = cst.children;
|
|
882
|
+
const identifiers = getAllTokens(children, 'Identifier');
|
|
883
|
+
const op = {
|
|
884
|
+
returnType: identifiers[0]?.image || 'void',
|
|
885
|
+
name: identifiers[1]?.image || 'unnamed',
|
|
886
|
+
parameters: [],
|
|
887
|
+
};
|
|
888
|
+
// Parameters
|
|
889
|
+
const paramNodes = getAllNodes(children, 'parameterDecl');
|
|
890
|
+
for (const paramNode of paramNodes) {
|
|
891
|
+
const paramIds = getAllTokens(paramNode.children, 'Identifier');
|
|
892
|
+
op.parameters.push({
|
|
893
|
+
type: paramIds[0]?.image || 'Object',
|
|
894
|
+
name: paramIds[1]?.image || 'param',
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
return op;
|
|
898
|
+
}
|
|
899
|
+
export function parseCML(text) {
|
|
900
|
+
// Lexing
|
|
901
|
+
const lexResult = CMLLexer.tokenize(text);
|
|
902
|
+
if (lexResult.errors.length > 0) {
|
|
903
|
+
return {
|
|
904
|
+
success: false,
|
|
905
|
+
errors: lexResult.errors.map(e => ({
|
|
906
|
+
message: e.message,
|
|
907
|
+
line: e.line,
|
|
908
|
+
column: e.column,
|
|
909
|
+
})),
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
// Parsing
|
|
913
|
+
parser.input = lexResult.tokens;
|
|
914
|
+
const cst = parser.cmlModel();
|
|
915
|
+
if (parser.errors.length > 0) {
|
|
916
|
+
return {
|
|
917
|
+
success: false,
|
|
918
|
+
errors: parser.errors.map(e => ({
|
|
919
|
+
message: e.message,
|
|
920
|
+
line: e.token?.startLine,
|
|
921
|
+
column: e.token?.startColumn,
|
|
922
|
+
})),
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
// Build AST
|
|
926
|
+
const model = visitCmlModel(cst);
|
|
927
|
+
return {
|
|
928
|
+
success: true,
|
|
929
|
+
model,
|
|
930
|
+
errors: [],
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
export { CMLLexer, CMLParserClass as CMLParser };
|
|
934
|
+
//# sourceMappingURL=parser.js.map
|