circuitscript 0.3.2 → 0.4.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.
Files changed (147) hide show
  1. package/dist/cjs/BaseVisitor.js +394 -262
  2. package/dist/cjs/LexerDiagnosticListener.js +375 -0
  3. package/dist/cjs/{ComponentAnnotater.js → annotate/ComponentAnnotater.js} +29 -15
  4. package/dist/cjs/annotate/DefaultPostAnnotationCallback.js +126 -0
  5. package/dist/cjs/{RefdesAnnotationVisitor.js → annotate/RefdesAnnotationVisitor.js} +8 -82
  6. package/dist/cjs/annotate/utils.js +70 -0
  7. package/dist/cjs/antlr/CircuitScriptLexer.js +279 -286
  8. package/dist/cjs/antlr/CircuitScriptParser.js +1954 -3535
  9. package/dist/cjs/antlr/CircuitScriptParserVisitor.js +7 -0
  10. package/dist/cjs/cache/deserializer.js +34 -0
  11. package/dist/cjs/cache/hash.js +8 -0
  12. package/dist/cjs/cache/serializer.js +122 -0
  13. package/dist/cjs/cache/storage.js +45 -0
  14. package/dist/cjs/cache/types.js +4 -0
  15. package/dist/cjs/{environment.js → environment/environment.js} +18 -6
  16. package/dist/cjs/environment/esm-environment.js +21 -0
  17. package/dist/cjs/environment/helpers.js +8 -0
  18. package/dist/cjs/execute.js +49 -15
  19. package/dist/cjs/globals.js +9 -1
  20. package/dist/cjs/helpers.js +3 -485
  21. package/dist/cjs/importResolver.js +102 -0
  22. package/dist/cjs/index.js +7 -6
  23. package/dist/cjs/lexer.js +48 -12
  24. package/dist/cjs/main.js +14 -4
  25. package/dist/cjs/objects/ClassComponent.js +1 -1
  26. package/dist/cjs/objects/ExecutionScope.js +0 -1
  27. package/dist/cjs/objects/types.js +17 -1
  28. package/dist/cjs/parser.js +18 -4
  29. package/dist/cjs/pipeline.js +284 -0
  30. package/dist/cjs/regenerate-tests.js +4 -3
  31. package/dist/cjs/render/KiCadNetListOutputHandler.js +30 -0
  32. package/dist/cjs/render/PaperSizes.js +46 -0
  33. package/dist/cjs/{draw_symbols.js → render/draw_symbols.js} +58 -36
  34. package/dist/cjs/{export.js → render/export.js} +2 -2
  35. package/dist/cjs/{geometry.js → render/geometry.js} +5 -5
  36. package/dist/cjs/{graph.js → render/graph.js} +7 -7
  37. package/dist/cjs/{layout.js → render/layout.js} +8 -8
  38. package/dist/cjs/{render.js → render/render.js} +9 -8
  39. package/dist/cjs/rules-check/no-connect-on-connected-pin.js +1 -1
  40. package/dist/cjs/rules-check/unconnected-pins.js +1 -1
  41. package/dist/cjs/{SemanticTokenVisitor.js → semantic-tokens/SemanticTokenVisitor.js} +12 -14
  42. package/dist/cjs/semantic-tokens/getSemanticTokens.js +55 -0
  43. package/dist/cjs/sizing.js +2 -2
  44. package/dist/cjs/utils.js +2 -2
  45. package/dist/cjs/validate/SymbolValidatorResolveVisitor.js +6 -0
  46. package/dist/cjs/validate/SymbolValidatorVisitor.js +34 -39
  47. package/dist/cjs/validate/validateScript.js +54 -0
  48. package/dist/cjs/validate.js +5 -4
  49. package/dist/cjs/visitor.js +140 -204
  50. package/dist/esm/BaseVisitor.js +396 -264
  51. package/dist/esm/LexerDiagnosticListener.js +371 -0
  52. package/dist/esm/{ComponentAnnotater.js → annotate/ComponentAnnotater.js} +29 -15
  53. package/dist/esm/annotate/DefaultPostAnnotationCallback.js +122 -0
  54. package/dist/esm/{RefdesAnnotationVisitor.js → annotate/RefdesAnnotationVisitor.js} +8 -82
  55. package/dist/esm/annotate/utils.js +66 -0
  56. package/dist/esm/antlr/CircuitScriptLexer.js +279 -286
  57. package/dist/esm/antlr/CircuitScriptParser.js +1962 -3522
  58. package/dist/esm/antlr/{CircuitScriptVisitor.js → CircuitScriptParserVisitor.js} +14 -35
  59. package/dist/esm/cache/deserializer.js +30 -0
  60. package/dist/esm/cache/hash.js +4 -0
  61. package/dist/esm/cache/serializer.js +118 -0
  62. package/dist/esm/cache/storage.js +39 -0
  63. package/dist/esm/cache/types.js +1 -0
  64. package/dist/esm/{environment.js → environment/environment.js} +18 -6
  65. package/dist/esm/environment/esm-environment.js +17 -0
  66. package/dist/esm/environment/helpers.js +4 -0
  67. package/dist/esm/execute.js +49 -15
  68. package/dist/esm/globals.js +8 -0
  69. package/dist/esm/helpers.js +5 -474
  70. package/dist/esm/importResolver.js +96 -0
  71. package/dist/esm/index.js +7 -6
  72. package/dist/esm/lexer.js +51 -12
  73. package/dist/esm/main.js +13 -3
  74. package/dist/esm/objects/ClassComponent.js +1 -1
  75. package/dist/esm/objects/ExecutionScope.js +0 -1
  76. package/dist/esm/objects/types.js +21 -1
  77. package/dist/esm/parser.js +19 -5
  78. package/dist/esm/pipeline.js +276 -0
  79. package/dist/esm/regenerate-tests.js +3 -2
  80. package/dist/esm/render/KiCadNetListOutputHandler.js +20 -0
  81. package/dist/esm/render/PaperSizes.js +41 -0
  82. package/dist/esm/{draw_symbols.js → render/draw_symbols.js} +58 -36
  83. package/dist/esm/{export.js → render/export.js} +2 -2
  84. package/dist/esm/{geometry.js → render/geometry.js} +5 -5
  85. package/dist/esm/{graph.js → render/graph.js} +7 -7
  86. package/dist/esm/{layout.js → render/layout.js} +8 -8
  87. package/dist/esm/{render.js → render/render.js} +8 -7
  88. package/dist/esm/rules-check/no-connect-on-connected-pin.js +1 -1
  89. package/dist/esm/rules-check/unconnected-pins.js +1 -1
  90. package/dist/esm/{SemanticTokenVisitor.js → semantic-tokens/SemanticTokenVisitor.js} +12 -14
  91. package/dist/esm/semantic-tokens/getSemanticTokens.js +51 -0
  92. package/dist/esm/sizing.js +2 -2
  93. package/dist/esm/utils.js +2 -2
  94. package/dist/esm/validate/SymbolValidatorResolveVisitor.js +3 -0
  95. package/dist/esm/validate/SymbolValidatorVisitor.js +36 -41
  96. package/dist/esm/validate/validateScript.js +50 -0
  97. package/dist/esm/validate.js +4 -3
  98. package/dist/esm/visitor.js +142 -206
  99. package/dist/libs/std.cst +15 -19
  100. package/dist/types/BaseVisitor.d.ts +25 -18
  101. package/dist/types/BomGeneration.d.ts +1 -1
  102. package/dist/types/LexerDiagnosticListener.d.ts +85 -0
  103. package/dist/types/{ComponentAnnotater.d.ts → annotate/ComponentAnnotater.d.ts} +1 -1
  104. package/dist/types/annotate/DefaultPostAnnotationCallback.d.ts +7 -0
  105. package/dist/types/{RefdesAnnotationVisitor.d.ts → annotate/RefdesAnnotationVisitor.d.ts} +6 -8
  106. package/dist/types/annotate/utils.d.ts +6 -0
  107. package/dist/types/antlr/CircuitScriptLexer.d.ts +71 -70
  108. package/dist/types/antlr/CircuitScriptParser.d.ts +357 -515
  109. package/dist/types/antlr/{CircuitScriptVisitor.d.ts → CircuitScriptParserVisitor.d.ts} +27 -69
  110. package/dist/types/cache/deserializer.d.ts +5 -0
  111. package/dist/types/cache/hash.d.ts +1 -0
  112. package/dist/types/cache/serializer.d.ts +3 -0
  113. package/dist/types/cache/storage.d.ts +4 -0
  114. package/dist/types/cache/types.d.ts +20 -0
  115. package/dist/types/{environment.d.ts → environment/environment.d.ts} +5 -4
  116. package/dist/types/environment/esm-environment.d.ts +4 -0
  117. package/dist/types/environment/helpers.d.ts +2 -0
  118. package/dist/types/execute.d.ts +3 -2
  119. package/dist/types/globals.d.ts +1 -0
  120. package/dist/types/helpers.d.ts +31 -36
  121. package/dist/types/importResolver.d.ts +4 -0
  122. package/dist/types/index.d.ts +7 -6
  123. package/dist/types/lexer.d.ts +9 -5
  124. package/dist/types/objects/ClassComponent.d.ts +1 -1
  125. package/dist/types/objects/ExecutionScope.d.ts +1 -4
  126. package/dist/types/objects/types.d.ts +16 -2
  127. package/dist/types/parser.d.ts +9 -2
  128. package/dist/types/pipeline.d.ts +9 -0
  129. package/dist/types/render/KiCadNetListOutputHandler.d.ts +10 -0
  130. package/dist/types/render/PaperSizes.d.ts +12 -0
  131. package/dist/types/{draw_symbols.d.ts → render/draw_symbols.d.ts} +4 -4
  132. package/dist/types/{export.d.ts → render/export.d.ts} +1 -1
  133. package/dist/types/{geometry.d.ts → render/geometry.d.ts} +2 -2
  134. package/dist/types/{graph.d.ts → render/graph.d.ts} +6 -6
  135. package/dist/types/{layout.d.ts → render/layout.d.ts} +10 -10
  136. package/dist/types/{render.d.ts → render/render.d.ts} +1 -1
  137. package/dist/types/{SemanticTokenVisitor.d.ts → semantic-tokens/SemanticTokenVisitor.d.ts} +6 -6
  138. package/dist/types/semantic-tokens/getSemanticTokens.d.ts +6 -0
  139. package/dist/types/sizing.d.ts +1 -1
  140. package/dist/types/utils.d.ts +1 -1
  141. package/dist/types/validate/SymbolValidatorResolveVisitor.d.ts +3 -0
  142. package/dist/types/validate/SymbolValidatorVisitor.d.ts +8 -8
  143. package/dist/types/validate/validateScript.d.ts +3 -0
  144. package/dist/types/visitor.d.ts +8 -14
  145. package/libs/std.cst +15 -19
  146. package/package.json +3 -6
  147. package/dist/cjs/antlr/CircuitScriptVisitor.js +0 -7
@@ -0,0 +1,371 @@
1
+ import { CircuitScriptParser } from "./antlr/CircuitScriptParser.js";
2
+ export class LexerDiagnosticCollector {
3
+ startTime = 0n;
4
+ tokenStartTime = 0n;
5
+ totalTokens = 0;
6
+ totalCharactersProcessed = 0;
7
+ tokenTypeStats = new Map();
8
+ slowestTokens = [];
9
+ peakCharacterPosition = 0;
10
+ indentCount = 0;
11
+ dedentCount = 0;
12
+ newlineCount = 0;
13
+ queueSizeSum = 0;
14
+ queueSizeCount = 0;
15
+ maxQueueSize = 0;
16
+ largeQueueLocations = [];
17
+ tokenStream = [];
18
+ verboseLogging = false;
19
+ recordTokenStream = false;
20
+ sourceText = '';
21
+ tokenTypeNameCache = new Map();
22
+ enabled = true;
23
+ constructor() {
24
+ this.reset();
25
+ }
26
+ setEnabled(enabled) {
27
+ this.enabled = enabled;
28
+ }
29
+ setVerboseLogging(enabled) {
30
+ this.verboseLogging = enabled;
31
+ if (enabled)
32
+ this.recordTokenStream = true;
33
+ }
34
+ setRecordTokenStream(enabled) {
35
+ this.recordTokenStream = enabled;
36
+ }
37
+ setSourceText(sourceText) {
38
+ this.sourceText = sourceText;
39
+ }
40
+ reset() {
41
+ this.startTime = process.hrtime.bigint();
42
+ this.tokenStartTime = 0n;
43
+ this.totalTokens = 0;
44
+ this.totalCharactersProcessed = 0;
45
+ this.tokenTypeStats.clear();
46
+ this.slowestTokens = [];
47
+ this.peakCharacterPosition = 0;
48
+ this.indentCount = 0;
49
+ this.dedentCount = 0;
50
+ this.newlineCount = 0;
51
+ this.queueSizeSum = 0;
52
+ this.queueSizeCount = 0;
53
+ this.maxQueueSize = 0;
54
+ this.largeQueueLocations = [];
55
+ this.tokenStream = [];
56
+ this.tokenTypeNameCache.clear();
57
+ }
58
+ onTokenStart() {
59
+ if (!this.enabled)
60
+ return;
61
+ this.tokenStartTime = process.hrtime.bigint();
62
+ }
63
+ onTokenGenerated(token, queueSize) {
64
+ if (!this.enabled)
65
+ return;
66
+ const endTime = process.hrtime.bigint();
67
+ const elapsedNs = endTime - this.tokenStartTime;
68
+ const elapsedUs = Number(elapsedNs) / 1000;
69
+ this.totalTokens++;
70
+ if (token.stop > this.peakCharacterPosition) {
71
+ this.peakCharacterPosition = token.stop;
72
+ }
73
+ const tokenLength = token.stop - token.start + 1;
74
+ this.totalCharactersProcessed += tokenLength;
75
+ const tokenTypeName = this.getTokenTypeName(token.type);
76
+ let stats = this.tokenTypeStats.get(tokenTypeName);
77
+ if (!stats) {
78
+ stats = {
79
+ count: 0,
80
+ totalTimeUs: 0,
81
+ averageTimeUs: 0,
82
+ totalCharacters: 0
83
+ };
84
+ this.tokenTypeStats.set(tokenTypeName, stats);
85
+ }
86
+ stats.count++;
87
+ stats.totalTimeUs += elapsedUs;
88
+ stats.averageTimeUs = stats.totalTimeUs / stats.count;
89
+ stats.totalCharacters += tokenLength;
90
+ if (token.type === CircuitScriptParser.INDENT) {
91
+ this.indentCount++;
92
+ }
93
+ else if (token.type === CircuitScriptParser.DEDENT) {
94
+ this.dedentCount++;
95
+ }
96
+ else if (token.type === CircuitScriptParser.NEWLINE) {
97
+ this.newlineCount++;
98
+ }
99
+ if (elapsedUs > 10) {
100
+ this.slowestTokens.push({
101
+ type: tokenTypeName,
102
+ timeUs: elapsedUs,
103
+ text: token.text?.substring(0, 50) || '',
104
+ line: token.line,
105
+ column: token.column
106
+ });
107
+ this.slowestTokens.sort((a, b) => b.timeUs - a.timeUs);
108
+ if (this.slowestTokens.length > 20) {
109
+ this.slowestTokens = this.slowestTokens.slice(0, 20);
110
+ }
111
+ }
112
+ this.queueSizeSum += queueSize;
113
+ this.queueSizeCount++;
114
+ if (queueSize > this.maxQueueSize) {
115
+ this.maxQueueSize = queueSize;
116
+ }
117
+ if (queueSize > 50) {
118
+ this.largeQueueLocations.push({
119
+ queueSize,
120
+ tokenIndex: this.totalTokens,
121
+ line: token.line,
122
+ column: token.column,
123
+ tokenType: tokenTypeName,
124
+ text: token.text?.substring(0, 50) || ''
125
+ });
126
+ this.largeQueueLocations.sort((a, b) => b.queueSize - a.queueSize);
127
+ if (this.largeQueueLocations.length > 50) {
128
+ this.largeQueueLocations = this.largeQueueLocations.slice(0, 50);
129
+ }
130
+ }
131
+ if (this.recordTokenStream) {
132
+ const tokenEntry = {
133
+ index: this.totalTokens - 1,
134
+ type: tokenTypeName,
135
+ text: token.text || '',
136
+ line: token.line,
137
+ column: token.column,
138
+ startPos: token.start,
139
+ stopPos: token.stop,
140
+ channel: token.channel,
141
+ timeUs: elapsedUs
142
+ };
143
+ this.tokenStream.push(tokenEntry);
144
+ if (this.verboseLogging) {
145
+ this.logToken(tokenEntry);
146
+ }
147
+ }
148
+ }
149
+ getTokenTypeName(tokenType) {
150
+ let name = this.tokenTypeNameCache.get(tokenType);
151
+ if (name !== undefined)
152
+ return name;
153
+ const symbolicName = CircuitScriptParser.vocabulary.getSymbolicName(tokenType);
154
+ if (symbolicName) {
155
+ this.tokenTypeNameCache.set(tokenType, symbolicName);
156
+ return symbolicName;
157
+ }
158
+ const literalName = CircuitScriptParser.vocabulary.getLiteralName(tokenType);
159
+ if (literalName) {
160
+ this.tokenTypeNameCache.set(tokenType, literalName);
161
+ return literalName;
162
+ }
163
+ name = `TOKEN_TYPE_${tokenType}`;
164
+ this.tokenTypeNameCache.set(tokenType, name);
165
+ return name;
166
+ }
167
+ logToken(token) {
168
+ const textPreview = token.text.length > 40
169
+ ? token.text.substring(0, 37) + '...'
170
+ : token.text;
171
+ const displayText = textPreview.replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\t/g, '\\t');
172
+ console.log(`[${token.index.toString().padStart(5)}] ` +
173
+ `${token.type.padEnd(18)} ` +
174
+ `@${token.line}:${token.column.toString().padStart(3)} ` +
175
+ `[${token.startPos}-${token.stopPos}] ` +
176
+ `"${displayText}"`);
177
+ }
178
+ getDiagnostics() {
179
+ const endTime = process.hrtime.bigint();
180
+ const totalTimeNs = endTime - this.startTime;
181
+ const totalTimeUs = Number(totalTimeNs) / 1000;
182
+ const averageQueueSize = this.queueSizeCount > 0
183
+ ? this.queueSizeSum / this.queueSizeCount
184
+ : 0;
185
+ return {
186
+ totalTokens: this.totalTokens,
187
+ totalCharactersProcessed: this.totalCharactersProcessed,
188
+ totalTimeMicroseconds: totalTimeUs,
189
+ tokenTypeStats: this.tokenTypeStats,
190
+ averageTokenTimeUs: this.totalTokens > 0 ? totalTimeUs / this.totalTokens : 0,
191
+ slowestTokens: this.slowestTokens,
192
+ peakCharacterPosition: this.peakCharacterPosition,
193
+ indentCount: this.indentCount,
194
+ dedentCount: this.dedentCount,
195
+ newlineCount: this.newlineCount,
196
+ maxQueueSize: this.maxQueueSize,
197
+ averageQueueSize: averageQueueSize,
198
+ largeQueueLocations: this.largeQueueLocations,
199
+ tokenStream: this.tokenStream
200
+ };
201
+ }
202
+ printReport() {
203
+ const diagnostics = this.getDiagnostics();
204
+ console.log('\n=== Lexer Diagnostic Report ===\n');
205
+ console.log('Overall Statistics:');
206
+ console.log(` Total tokens generated: ${diagnostics.totalTokens}`);
207
+ console.log(` Total characters processed: ${diagnostics.totalCharactersProcessed}`);
208
+ console.log(` Total time: ${(diagnostics.totalTimeMicroseconds / 1000).toFixed(2)} ms`);
209
+ console.log(` Average time per token: ${diagnostics.averageTokenTimeUs.toFixed(2)} μs`);
210
+ console.log(` Tokens per second: ${(diagnostics.totalTokens / (diagnostics.totalTimeMicroseconds / 1000000)).toFixed(0)}`);
211
+ console.log('\nIndentation Statistics:');
212
+ console.log(` INDENT tokens: ${diagnostics.indentCount}`);
213
+ console.log(` DEDENT tokens: ${diagnostics.dedentCount}`);
214
+ console.log(` NEWLINE tokens: ${diagnostics.newlineCount}`);
215
+ console.log('\nToken Queue Statistics:');
216
+ console.log(` Max queue size: ${diagnostics.maxQueueSize}`);
217
+ console.log(` Average queue size: ${diagnostics.averageQueueSize.toFixed(2)}`);
218
+ console.log('\nTop Token Types by Count:');
219
+ const sortedByCount = Array.from(diagnostics.tokenTypeStats.entries())
220
+ .sort((a, b) => b[1].count - a[1].count)
221
+ .slice(0, 10);
222
+ sortedByCount.forEach(([type, stats]) => {
223
+ const percentage = (stats.count / diagnostics.totalTokens * 100).toFixed(1);
224
+ console.log(` ${type.padEnd(20)} ${stats.count.toString().padStart(6)} (${percentage}%) avg: ${stats.averageTimeUs.toFixed(2)} μs`);
225
+ });
226
+ console.log('\nTop Token Types by Time:');
227
+ const sortedByTime = Array.from(diagnostics.tokenTypeStats.entries())
228
+ .sort((a, b) => b[1].totalTimeUs - a[1].totalTimeUs)
229
+ .slice(0, 10);
230
+ sortedByTime.forEach(([type, stats]) => {
231
+ const percentage = (stats.totalTimeUs / diagnostics.totalTimeMicroseconds * 100).toFixed(1);
232
+ console.log(` ${type.padEnd(20)} ${(stats.totalTimeUs / 1000).toFixed(2).padStart(8)} ms (${percentage}%) count: ${stats.count}`);
233
+ });
234
+ if (diagnostics.slowestTokens.length > 0) {
235
+ console.log('\nSlowest Individual Tokens:');
236
+ diagnostics.slowestTokens.slice(0, 10).forEach((token, idx) => {
237
+ const textPreview = token.text.length > 30
238
+ ? token.text.substring(0, 27) + '...'
239
+ : token.text;
240
+ console.log(` ${(idx + 1).toString().padStart(2)}. ${token.type.padEnd(15)} ${token.timeUs.toFixed(2).padStart(8)} μs at ${token.line}:${token.column} "${textPreview}"`);
241
+ });
242
+ }
243
+ if (diagnostics.largeQueueLocations.length > 0) {
244
+ const earlyThreshold = Math.min(100, diagnostics.totalTokens * 0.05);
245
+ const earlyQueues = diagnostics.largeQueueLocations.filter(loc => loc.tokenIndex <= earlyThreshold);
246
+ const laterQueues = diagnostics.largeQueueLocations.filter(loc => loc.tokenIndex > earlyThreshold);
247
+ if (earlyQueues.length > 0) {
248
+ console.log('\nLarge Token Queue Locations (Early - Expected from script rule):');
249
+ console.log(` Max queue: ${earlyQueues[0]?.queueSize || 0} at token ${earlyQueues[0]?.tokenIndex || 0}`);
250
+ }
251
+ if (laterQueues.length > 0) {
252
+ console.log('\nLarge Token Queue Locations (Later - Potentially Problematic):');
253
+ laterQueues.slice(0, 15).forEach((location, idx) => {
254
+ const textPreview = location.text.length > 30
255
+ ? location.text.substring(0, 27) + '...'
256
+ : location.text;
257
+ console.log(` ${(idx + 1).toString().padStart(2)}. Queue: ${location.queueSize.toString().padStart(4)} Token #${location.tokenIndex.toString().padStart(6)} ${location.tokenType.padEnd(15)} at ${location.line}:${location.column} "${textPreview}"`);
258
+ });
259
+ }
260
+ else if (earlyQueues.length > 0) {
261
+ console.log('\nNo large token queues detected after initial parsing (Good!)');
262
+ }
263
+ }
264
+ console.log('\n=== End of Report ===\n');
265
+ }
266
+ printTokenStream(limit) {
267
+ console.log('\n=== Token Stream ===\n');
268
+ const tokens = limit ? this.tokenStream.slice(0, limit) : this.tokenStream;
269
+ tokens.forEach(token => {
270
+ this.logToken(token);
271
+ });
272
+ if (limit && this.tokenStream.length > limit) {
273
+ console.log(`\n... and ${this.tokenStream.length - limit} more tokens`);
274
+ }
275
+ console.log(`\nTotal: ${this.tokenStream.length} tokens\n`);
276
+ }
277
+ printCharacterToTokenMapping(startLine, endLine) {
278
+ if (!this.sourceText) {
279
+ console.log('\nNo source text available. Call setSourceText() before lexing.\n');
280
+ return;
281
+ }
282
+ console.log('\n=== Character-to-Token Mapping ===\n');
283
+ const lines = this.sourceText.split('\n');
284
+ const start = startLine !== undefined ? Math.max(0, startLine - 1) : 0;
285
+ const end = endLine !== undefined ? Math.min(lines.length, endLine) : lines.length;
286
+ for (let lineNum = start; lineNum < end; lineNum++) {
287
+ const line = lines[lineNum];
288
+ const lineTokens = this.tokenStream.filter(t => t.line === lineNum + 1);
289
+ console.log(`Line ${(lineNum + 1).toString().padStart(4)}: ${line}`);
290
+ if (lineTokens.length > 0) {
291
+ const charMap = new Array(line.length).fill(' ');
292
+ const tokenLabels = [];
293
+ lineTokens.forEach((token, idx) => {
294
+ const char = String.fromCharCode(65 + (idx % 26));
295
+ const startCol = token.column;
296
+ const endCol = Math.min(token.column + token.text.length, line.length);
297
+ for (let i = startCol; i < endCol; i++) {
298
+ charMap[i] = char;
299
+ }
300
+ tokenLabels.push({
301
+ col: startCol,
302
+ label: `${char}=${token.type}`
303
+ });
304
+ });
305
+ console.log(' ' + charMap.join(''));
306
+ tokenLabels.forEach(({ col, label }) => {
307
+ console.log(' ' + ' '.repeat(col) + '└─ ' + label);
308
+ });
309
+ }
310
+ console.log('');
311
+ }
312
+ }
313
+ printLexerOperationSummary() {
314
+ console.log('\n=== Lexer Operation Summary ===\n');
315
+ console.log('Input Processing:');
316
+ console.log(` Source characters: ${this.sourceText.length}`);
317
+ console.log(` Characters tokenized: ${this.totalCharactersProcessed}`);
318
+ console.log(` Peak position: ${this.peakCharacterPosition}`);
319
+ console.log(` Coverage: ${((this.totalCharactersProcessed / Math.max(this.sourceText.length, 1)) * 100).toFixed(1)}%`);
320
+ console.log('\nToken Generation:');
321
+ console.log(` Total tokens: ${this.totalTokens}`);
322
+ console.log(` Unique token types: ${this.tokenTypeStats.size}`);
323
+ console.log(` Average token length: ${(this.totalCharactersProcessed / Math.max(this.totalTokens, 1)).toFixed(2)} chars`);
324
+ console.log('\nToken Type Distribution:');
325
+ const topTypes = Array.from(this.tokenTypeStats.entries())
326
+ .sort((a, b) => b[1].count - a[1].count)
327
+ .slice(0, 8);
328
+ topTypes.forEach(([type, stats]) => {
329
+ const percentage = (stats.count / this.totalTokens * 100).toFixed(1);
330
+ const bar = '█'.repeat(Math.floor(stats.count / this.totalTokens * 40));
331
+ console.log(` ${type.padEnd(18)} ${stats.count.toString().padStart(5)} (${percentage.padStart(5)}%) ${bar}`);
332
+ });
333
+ console.log('\nLexing Process:');
334
+ console.log(` INDENT tokens: ${this.indentCount}`);
335
+ console.log(` DEDENT tokens: ${this.dedentCount}`);
336
+ console.log(` NEWLINE tokens: ${this.newlineCount}`);
337
+ console.log(` Max token queue: ${this.maxQueueSize}`);
338
+ console.log(` Avg token queue: ${this.queueSizeSamples.length > 0 ? (this.queueSizeSamples.reduce((a, b) => a + b, 0) / this.queueSizeSamples.length).toFixed(1) : 0}`);
339
+ console.log('\n=== End of Summary ===\n');
340
+ }
341
+ getRecommendations() {
342
+ const diagnostics = this.getDiagnostics();
343
+ const recommendations = [];
344
+ const indentationTokens = diagnostics.indentCount + diagnostics.dedentCount + diagnostics.newlineCount;
345
+ const indentationPercentage = (indentationTokens / diagnostics.totalTokens) * 100;
346
+ if (indentationPercentage > 20) {
347
+ recommendations.push(`High percentage of indentation tokens (${indentationPercentage.toFixed(1)}%). Consider optimizing INDENT/DEDENT/NEWLINE processing.`);
348
+ }
349
+ const earlyThreshold = Math.min(100, diagnostics.totalTokens * 0.05);
350
+ const laterQueues = diagnostics.largeQueueLocations.filter(loc => loc.tokenIndex > earlyThreshold);
351
+ if (laterQueues.length > 0 && laterQueues[0].queueSize > 100) {
352
+ recommendations.push(`Large token queue detected after initial parsing (max: ${laterQueues[0].queueSize} at line ${laterQueues[0].line}:${laterQueues[0].column}). This may indicate excessive lookahead or buffering in specific grammar rules.`);
353
+ }
354
+ else if (diagnostics.maxQueueSize > 200) {
355
+ recommendations.push(`Very large token queue detected during initial parsing (max: ${diagnostics.maxQueueSize}). This is expected for the script rule but consider if it's excessive.`);
356
+ }
357
+ const slowTokenTypes = Array.from(diagnostics.tokenTypeStats.entries())
358
+ .filter(([, stats]) => stats.averageTimeUs > 50)
359
+ .sort((a, b) => b[1].averageTimeUs - a[1].averageTimeUs);
360
+ if (slowTokenTypes.length > 0) {
361
+ slowTokenTypes.forEach(([type, stats]) => {
362
+ recommendations.push(`Token type '${type}' has high average generation time (${stats.averageTimeUs.toFixed(2)} μs). Review lexer rule.`);
363
+ });
364
+ }
365
+ const tokensPerSecond = diagnostics.totalTokens / (diagnostics.totalTimeMicroseconds / 1000000);
366
+ if (tokensPerSecond < 100000) {
367
+ recommendations.push(`Low token generation rate (${tokensPerSecond.toFixed(0)} tokens/sec). Consider lexer optimization.`);
368
+ }
369
+ return recommendations;
370
+ }
371
+ }
@@ -1,4 +1,4 @@
1
- import { ComponentRefDesPrefixes } from './visitor.js';
1
+ import { ComponentRefDesPrefixes } from './../visitor.js';
2
2
  export class ComponentAnnotater {
3
3
  counter = {};
4
4
  indexedContextPrefix = new Map();
@@ -9,20 +9,34 @@ export class ComponentAnnotater {
9
9
  }
10
10
  }
11
11
  getAnnotation(instance) {
12
- const type = instance.typeProp ?? 'conn';
13
- if (this.counter[type] === undefined && type.length <= 2) {
14
- for (const [, value] of Object.entries(ComponentRefDesPrefixes)) {
15
- if (value === type) {
16
- throw "Refdes prefix is already in use!";
12
+ let usePrefix;
13
+ let useCounterKey;
14
+ if (instance.hasParam('refdesPrefix')) {
15
+ const prefix = instance.getParam('refdesPrefix');
16
+ if (this.counter[prefix] === undefined) {
17
+ this.counter[prefix] = 1;
18
+ }
19
+ usePrefix = prefix;
20
+ useCounterKey = prefix;
21
+ }
22
+ else {
23
+ const type = instance.typeProp ?? 'conn';
24
+ if (this.counter[type] === undefined && type.length <= 2) {
25
+ for (const [, value] of Object.entries(ComponentRefDesPrefixes)) {
26
+ if (value === type) {
27
+ throw "Refdes prefix is already in use!";
28
+ }
29
+ }
30
+ if (ComponentRefDesPrefixes[type] === undefined) {
31
+ ComponentRefDesPrefixes[type] = type;
32
+ this.counter[type] = 1;
17
33
  }
18
34
  }
19
35
  if (ComponentRefDesPrefixes[type] === undefined) {
20
- ComponentRefDesPrefixes[type] = type;
21
- this.counter[type] = 1;
36
+ return null;
22
37
  }
23
- }
24
- if (ComponentRefDesPrefixes[type] === undefined) {
25
- return null;
38
+ usePrefix = ComponentRefDesPrefixes[type];
39
+ useCounterKey = type;
26
40
  }
27
41
  let prefix = '';
28
42
  let resultRefdes = '';
@@ -39,8 +53,8 @@ export class ComponentAnnotater {
39
53
  prefix = instance.placeHolderRefDes.replaceAll('_', '');
40
54
  }
41
55
  else {
42
- const { index: nextIndex, proposedName } = this.getNextRefdesCounter(ComponentRefDesPrefixes[type], this.counter[type]);
43
- this.counter[type] = nextIndex;
56
+ const { index: nextIndex, proposedName } = this.getNextRefdesCounter(usePrefix, this.counter[useCounterKey]);
57
+ this.counter[useCounterKey] = nextIndex;
44
58
  prefix = proposedName;
45
59
  }
46
60
  this.existingRefDes.push(prefix);
@@ -53,8 +67,8 @@ export class ComponentAnnotater {
53
67
  }
54
68
  }
55
69
  else {
56
- const refdesCounter = this.getNextRefdesCounter(ComponentRefDesPrefixes[type], this.counter[type]);
57
- this.counter[type] = refdesCounter.index;
70
+ const refdesCounter = this.getNextRefdesCounter(usePrefix, this.counter[useCounterKey]);
71
+ this.counter[useCounterKey] = refdesCounter.index;
58
72
  resultRefdes = refdesCounter.proposedName;
59
73
  }
60
74
  this.existingRefDes.push(resultRefdes);
@@ -0,0 +1,122 @@
1
+ import { RefdesFileSuffix } from "../globals.js";
2
+ import { RefdesOutputType } from "../helpers.js";
3
+ import { RefdesAnnotationVisitor } from "./RefdesAnnotationVisitor.js";
4
+ export async function DefaultPostAnnotationCallback(options, scriptData, tree, tokens, componentLinks, importedLibraries, environment) {
5
+ const { inputPath = null, updateSource = false, saveAnnotatedCopy = undefined, } = options;
6
+ if (inputPath && (updateSource || saveAnnotatedCopy !== undefined)) {
7
+ const sourceAnnotatedFiles = [{
8
+ isMainFile: true,
9
+ scriptData,
10
+ tokens,
11
+ tree,
12
+ filePath: inputPath,
13
+ outputType: RefdesOutputType.WithSource
14
+ }];
15
+ const externalRefdesLibraries = [];
16
+ for (const library of importedLibraries) {
17
+ let outputType = RefdesOutputType.None;
18
+ if (library.enableRefdesAnnotation) {
19
+ outputType = RefdesOutputType.WithSource;
20
+ }
21
+ else if (library.enableRefdesAnnotationFile) {
22
+ outputType = RefdesOutputType.CreateExternalFile;
23
+ }
24
+ if (outputType !== RefdesOutputType.None) {
25
+ const { libraryFilePath, libraryName, tokens: libTokens, tree: libTree } = library;
26
+ const libraryScriptData = await environment.readFile(libraryFilePath, { encoding: 'utf8' });
27
+ const annotatedFile = {
28
+ tokens: libTokens,
29
+ tree: libTree,
30
+ filePath: libraryFilePath,
31
+ scriptData: libraryScriptData,
32
+ libraryName,
33
+ outputType,
34
+ library: library,
35
+ referencedTokens: library.referencedTokens,
36
+ };
37
+ if (outputType === RefdesOutputType.CreateExternalFile) {
38
+ externalRefdesLibraries.push(annotatedFile);
39
+ }
40
+ else {
41
+ sourceAnnotatedFiles.push(annotatedFile);
42
+ }
43
+ }
44
+ }
45
+ for (const item of sourceAnnotatedFiles) {
46
+ const { scriptData, tokens, tree, filePath, libraryName, referencedTokens = [], isMainFile = false, library } = item;
47
+ let usePath = filePath;
48
+ if (isMainFile && saveAnnotatedCopy === true) {
49
+ const dir = environment.dirname(filePath);
50
+ const ext = environment.extname(filePath);
51
+ const basename = environment.basename(filePath, ext);
52
+ usePath = environment.join(dir, `${basename}.annotated${ext}`);
53
+ }
54
+ else if (isMainFile && typeof saveAnnotatedCopy === 'string') {
55
+ usePath = saveAnnotatedCopy;
56
+ }
57
+ let updatedScriptData = scriptData;
58
+ const isCachedLibrary = tokens === null && tree === null;
59
+ let modifications;
60
+ if (isCachedLibrary && referencedTokens) {
61
+ const updatedLines = updatedScriptData.split('\n');
62
+ for (const [tokens, tree] of referencedTokens) {
63
+ const inputStream = tokens.tokenSource.inputStream;
64
+ const scriptChunk = inputStream.getTextFromRange(0, inputStream.size);
65
+ const tmpVisitor = new RefdesAnnotationVisitor(true, scriptChunk, tokens, componentLinks);
66
+ tmpVisitor.visit(tree);
67
+ modifications = tmpVisitor.getModifications();
68
+ if (modifications.size > 0 && library && !library.writeToCache) {
69
+ library.writeToCache = true;
70
+ }
71
+ const resultOutput = tmpVisitor.getOutput();
72
+ const resultLines = resultOutput.split('\n');
73
+ const replaceStartLine = tokens.get(0).line;
74
+ updatedLines.splice(replaceStartLine - 1, resultLines.length, ...resultLines);
75
+ }
76
+ updatedScriptData = updatedLines.join('\n');
77
+ }
78
+ else {
79
+ const tmpVisitor = new RefdesAnnotationVisitor(true, scriptData, tokens, componentLinks);
80
+ tmpVisitor.visit(tree);
81
+ modifications = tmpVisitor.getModifications();
82
+ updatedScriptData = tmpVisitor.getOutput();
83
+ }
84
+ library && library.addRefdesModifications(modifications);
85
+ environment.writeFileSync(usePath, updatedScriptData);
86
+ let display = 'Refdes annotations';
87
+ if (libraryName) {
88
+ display += ` for library ${libraryName}`;
89
+ }
90
+ console.log(`${display} saved to ${usePath}`);
91
+ }
92
+ if (externalRefdesLibraries.length > 0) {
93
+ const inputDir = environment.dirname(inputPath);
94
+ const inputExt = environment.extname(inputPath);
95
+ const inputBasename = environment.basename(inputPath, inputExt);
96
+ const refdesFilePath = environment.join(inputDir, `${inputBasename}${RefdesFileSuffix}`);
97
+ const libraries = [];
98
+ for (const item of externalRefdesLibraries) {
99
+ const { scriptData, tokens, tree, filePath, libraryName } = item;
100
+ const tmpVisitor = new RefdesAnnotationVisitor(true, scriptData, tokens, componentLinks);
101
+ await tmpVisitor.visit(tree);
102
+ const output = tmpVisitor.getOutputForExternalRefdesFile();
103
+ const relativeFilePath = environment.relative(inputDir, filePath);
104
+ libraries.push({
105
+ name: libraryName,
106
+ path: relativeFilePath,
107
+ items: output,
108
+ });
109
+ }
110
+ const sortedLibs = libraries.sort((a, b) => {
111
+ return a.name.localeCompare(b.name);
112
+ });
113
+ const jsonFile = {
114
+ format: 'v1',
115
+ description: 'Stores external refdes for libraries',
116
+ libraries: sortedLibs,
117
+ };
118
+ environment.writeFileSync(refdesFilePath, JSON.stringify(jsonFile, null, 4));
119
+ console.log(`External refdes annotations saved to ${refdesFilePath}`);
120
+ }
121
+ }
122
+ }