circuitscript 0.3.2 → 0.4.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.
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 +12 -7
  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 +12 -7
  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 +12 -7
  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
package/dist/esm/lexer.js CHANGED
@@ -1,31 +1,46 @@
1
1
  import { CommonToken } from "antlr4ng";
2
2
  import { CircuitScriptParser } from "./antlr/CircuitScriptParser.js";
3
3
  import { CircuitScriptLexer } from "./antlr/CircuitScriptLexer.js";
4
+ import { LexerDiagnosticCollector } from "./LexerDiagnosticListener.js";
4
5
  export class MainLexer extends CircuitScriptLexer {
5
6
  tokens;
7
+ tokensHead;
6
8
  indents;
7
9
  opened;
8
- constructor(input) {
10
+ diagnosticCollector;
11
+ lineOffset;
12
+ constructor(input, enableDiagnostics = false) {
9
13
  super(input);
10
14
  this.tokens = [];
15
+ this.tokensHead = 0;
11
16
  this.indents = [];
12
17
  this.opened = 0;
18
+ this.lineOffset = 0;
19
+ this.diagnosticCollector = new LexerDiagnosticCollector();
20
+ this.diagnosticCollector.setEnabled(enableDiagnostics);
13
21
  }
14
22
  reset() {
15
23
  this.tokens = [];
24
+ this.tokensHead = 0;
16
25
  this.indents = [];
17
26
  this.opened = 0;
18
27
  super.reset();
19
28
  }
20
29
  emitToken(token) {
30
+ this.diagnosticCollector.onTokenStart();
21
31
  super.emitToken(token);
22
32
  this.tokens.push(token);
33
+ this.diagnosticCollector.onTokenGenerated(token, this.tokens.length - this.tokensHead);
23
34
  }
24
35
  nextToken() {
25
36
  if (this.inputStream.LA(1) === CircuitScriptParser.EOF && this.indents.length) {
26
- this.tokens = this.tokens.filter(function (val) {
27
- return val.type !== CircuitScriptParser.EOF;
28
- });
37
+ let writeIdx = this.tokensHead;
38
+ for (let i = this.tokensHead; i < this.tokens.length; i++) {
39
+ if (this.tokens[i].type !== CircuitScriptParser.EOF) {
40
+ this.tokens[writeIdx++] = this.tokens[i];
41
+ }
42
+ }
43
+ this.tokens.length = writeIdx;
29
44
  const fillerNewLine = this.commonToken(CircuitScriptParser.NEWLINE, "");
30
45
  this.emitToken(fillerNewLine);
31
46
  fillerNewLine.__skip = true;
@@ -36,7 +51,19 @@ export class MainLexer extends CircuitScriptLexer {
36
51
  this.emitToken(this.commonToken(CircuitScriptParser.EOF, ""));
37
52
  }
38
53
  const next = super.nextToken();
39
- return this.tokens.length ? this.tokens.shift() : next;
54
+ let returnToken;
55
+ if (this.tokensHead < this.tokens.length) {
56
+ returnToken = this.tokens[this.tokensHead++];
57
+ if (this.tokensHead > 64) {
58
+ this.tokens = this.tokens.slice(this.tokensHead);
59
+ this.tokensHead = 0;
60
+ }
61
+ }
62
+ else {
63
+ returnToken = next;
64
+ }
65
+ returnToken.line += this.lineOffset;
66
+ return returnToken;
40
67
  }
41
68
  createDedent() {
42
69
  return this.commonToken(CircuitScriptParser.DEDENT, "");
@@ -80,9 +107,6 @@ export class MainLexer extends CircuitScriptLexer {
80
107
  }
81
108
  return count;
82
109
  }
83
- atStartOfInput() {
84
- return this.getCharIndex() === 0;
85
- }
86
110
  openBrace() {
87
111
  this.opened++;
88
112
  }
@@ -90,8 +114,19 @@ export class MainLexer extends CircuitScriptLexer {
90
114
  this.opened--;
91
115
  }
92
116
  onNewLine() {
93
- const newLine = this.text.replace(/[^\r\n]+/g, '');
94
- const spaces = this.text.replace(/[\r\n]+/g, '');
117
+ const text = this.text;
118
+ let nlLen = 0;
119
+ while (nlLen < text.length) {
120
+ const c = text.charCodeAt(nlLen);
121
+ if (c === 13 || c === 10 || c === 12) {
122
+ nlLen++;
123
+ }
124
+ else {
125
+ break;
126
+ }
127
+ }
128
+ const newLine = text.substring(0, nlLen);
129
+ const spaces = text.substring(nlLen);
95
130
  const next = this.inputStream.LA(1);
96
131
  const nextnext = this.inputStream.LA(2);
97
132
  if (this.opened > 0 || (nextnext != -1 &&
@@ -99,8 +134,9 @@ export class MainLexer extends CircuitScriptLexer {
99
134
  this.skip();
100
135
  }
101
136
  else {
102
- const start = this.getCharIndex() - this.text.length;
103
- const stop = this.getCharIndex() - 1;
137
+ const charIndex = this.getCharIndex();
138
+ const start = charIndex - this.text.length;
139
+ const stop = charIndex - 1;
104
140
  this.emitToken(this.commonToken(CircuitScriptParser.NEWLINE, newLine, start, start));
105
141
  const indent = this.getIndentationCount(spaces);
106
142
  const previous = this.indents.length ? this.indents[this.indents.length - 1] : 0;
@@ -119,4 +155,7 @@ export class MainLexer extends CircuitScriptLexer {
119
155
  }
120
156
  }
121
157
  }
158
+ setLineOffset(offset) {
159
+ this.lineOffset = offset;
160
+ }
122
161
  }
package/dist/esm/main.js CHANGED
@@ -2,8 +2,8 @@
2
2
  import { program } from 'commander';
3
3
  import figlet from 'figlet';
4
4
  import { watch } from 'fs';
5
- import { renderScript } from './helpers.js';
6
- import { NodeScriptEnvironment } from "./environment.js";
5
+ import { renderScript } from "./pipeline.js";
6
+ import { NodeScriptEnvironment } from "./environment/environment.js";
7
7
  export default async function main() {
8
8
  const env = new NodeScriptEnvironment();
9
9
  NodeScriptEnvironment.setInstance(env);
@@ -22,7 +22,12 @@ export default async function main() {
22
22
  .option('-s, --stats', 'Show stats during generation')
23
23
  .option('-x, --skip-output', 'Skip output generation')
24
24
  .option('-e, --erc', 'Enable ERC output')
25
- .option('-b, --bom [output-path]', 'Generate Bill of Materials in csv format');
25
+ .option('-b, --bom [output-path]', 'Generate Bill of Materials in csv format')
26
+ .option('-l, --lexer-diagnostics', 'Enable lexer performance diagnostics')
27
+ .option('--lexer-verbose', 'Log each token as it is generated (requires -l)')
28
+ .option('--lexer-tokens [limit]', 'Print token stream (optionally limit number of tokens, requires -l)')
29
+ .option('--lexer-mapping [lines]', 'Print character-to-token mapping (optionally specify line range like "1-10", requires -l)')
30
+ .option('--lexer-summary', 'Print lexer operation summary (requires -l)');
26
31
  program.addHelpText('before', figlet.textSync('circuitscript', {
27
32
  font: 'Small Slant'
28
33
  }));
@@ -87,6 +92,11 @@ export default async function main() {
87
92
  enableBom,
88
93
  bomOutputPath,
89
94
  environment: env,
95
+ lexerDiagnostics: options.lexerDiagnostics,
96
+ lexerVerbose: options.lexerVerbose,
97
+ lexerTokens: options.lexerTokens,
98
+ lexerMapping: options.lexerMapping,
99
+ lexerSummary: options.lexerSummary,
90
100
  inputPath: inputFilePath,
91
101
  updateSource,
92
102
  saveAnnotatedCopy: saveAnnotatedCopyPath,
@@ -1,4 +1,4 @@
1
- import { SymbolDrawingCommands } from '../draw_symbols.js';
1
+ import { SymbolDrawingCommands } from '../render/draw_symbols.js';
2
2
  import { PinDefinition, PinId, PinIdType } from './PinDefinition.js';
3
3
  import { PinTypes } from './PinTypes.js';
4
4
  import { DefaultComponentUnit, ParamKeys } from '../globals.js';
@@ -8,7 +8,6 @@ export class ExecutionScope {
8
8
  functions = new Map();
9
9
  functionCounter = new Map();
10
10
  variables = new Map();
11
- symbols = new Map();
12
11
  libraries = new Map();
13
12
  blockStack = new Map();
14
13
  contextStack = [];
@@ -7,6 +7,10 @@ export class CFunctionEntry {
7
7
  execute;
8
8
  uniqueId;
9
9
  source;
10
+ lazyLoaded = false;
11
+ lazyLoader = null;
12
+ tokens;
13
+ tree;
10
14
  constructor(namespace, name, execute, source, uniqueId) {
11
15
  this.name = name;
12
16
  this.namespace = namespace;
@@ -18,6 +22,9 @@ export class CFunctionEntry {
18
22
  toString() {
19
23
  return `[Function: ${this.name}]`;
20
24
  }
25
+ getFunctionPath() {
26
+ return `${this.namespace}${this.name}`;
27
+ }
21
28
  }
22
29
  ;
23
30
  export class AnyReference {
@@ -128,7 +135,13 @@ export class ImportedLibrary {
128
135
  enableRefdesAnnotationFile = false;
129
136
  tree;
130
137
  tokens;
131
- constructor(libraryName, libraryNamespace, libraryFilePath, tree, tokens, context, flag, specifiedImports) {
138
+ referencedTokens = [];
139
+ fileHash;
140
+ writeToCache = false;
141
+ refdesAnnotations = new Map;
142
+ parseError = false;
143
+ importStatement;
144
+ constructor(libraryName, libraryNamespace, libraryFilePath, tree, tokens, context, flag, specifiedImports, fileHash, importStatement) {
132
145
  this.libraryName = libraryName;
133
146
  this.libraryNamespace = libraryNamespace;
134
147
  this.libraryFilePath = libraryFilePath;
@@ -137,6 +150,13 @@ export class ImportedLibrary {
137
150
  this.context = context;
138
151
  this.importHandlingFlag = flag;
139
152
  this.specifiedImports = specifiedImports;
153
+ this.fileHash = fileHash;
154
+ this.importStatement = importStatement;
155
+ }
156
+ addRefdesModifications(mods) {
157
+ for (const [ctx, modification] of mods) {
158
+ this.refdesAnnotations.set(ctx, modification);
159
+ }
140
160
  }
141
161
  }
142
162
  export var ImportFunctionHandling;
@@ -1,28 +1,41 @@
1
1
  import { CircuitScriptParser } from './antlr/CircuitScriptParser.js';
2
2
  import { MainLexer } from './lexer.js';
3
3
  import { ParseSyntaxError, RuntimeExecutionError, SimpleStopwatch } from './utils.js';
4
- import { CharStream, CommonTokenStream } from 'antlr4ng';
5
- export async function parseFileWithVisitor(visitor, data) {
4
+ import { CharStream, CommonTokenStream, PredictionMode } from 'antlr4ng';
5
+ export function parseFileWithVisitor(visitor, data, options) {
6
6
  const lexerErrorListener = new CircuitscriptParserErrorListener(visitor.onErrorHandler);
7
7
  const parserErrorListener = new CircuitscriptParserErrorListener(visitor.onErrorHandler);
8
8
  const chars = CharStream.fromString(data);
9
- const lexer = new MainLexer(chars);
9
+ const enableDiagnostics = options?.enableLexerDiagnostics ?? false;
10
+ const enableVerbose = options?.enableLexerVerbose ?? false;
11
+ const enableTokenStream = options?.enableLexerTokenStream ?? false;
12
+ const lineOffset = options?.lineOffset ?? 0;
13
+ const lexer = new MainLexer(chars, enableDiagnostics);
14
+ lexer.setLineOffset(lineOffset);
15
+ if (enableDiagnostics) {
16
+ lexer.diagnosticCollector.setSourceText(data);
17
+ lexer.diagnosticCollector.setVerboseLogging(enableVerbose);
18
+ if (enableTokenStream) {
19
+ lexer.diagnosticCollector.setRecordTokenStream(true);
20
+ }
21
+ }
10
22
  lexer.removeErrorListeners();
11
23
  lexer.addErrorListener(lexerErrorListener);
12
24
  const lexerTimer = new SimpleStopwatch();
13
25
  const tokens = new CommonTokenStream(lexer);
14
26
  tokens.fill();
15
27
  const lexerTimeTaken = lexerTimer.lap();
16
- const parserTimer = new SimpleStopwatch();
17
28
  const parser = new CircuitScriptParser(tokens);
29
+ parser.interpreter.predictionMode = PredictionMode.SLL;
18
30
  parser.removeErrorListeners();
19
31
  parser.addErrorListener(parserErrorListener);
32
+ const parserTimer = new SimpleStopwatch();
20
33
  const tree = parser.script();
21
34
  let throwError;
22
35
  let hasError = false;
23
36
  let hasParseError = false;
24
37
  try {
25
- await visitor.visitAsync(tree);
38
+ visitor.visit(tree);
26
39
  }
27
40
  catch (error) {
28
41
  if (visitor.onErrorHandler) {
@@ -40,6 +53,7 @@ export async function parseFileWithVisitor(visitor, data) {
40
53
  return {
41
54
  tree, parser,
42
55
  tokens,
56
+ lexer,
43
57
  hasParseError,
44
58
  hasError,
45
59
  parserTimeTaken,
@@ -0,0 +1,276 @@
1
+ import path from "path";
2
+ import PDFDocument from "pdfkit";
3
+ import { RecognitionException } from "antlr4ng";
4
+ import { DefaultPostAnnotationCallback } from "./annotate/DefaultPostAnnotationCallback.js";
5
+ import { generateBom, generateBomCSV, saveBomOutputCsv } from "./BomGeneration.js";
6
+ import { defaultZoomScale } from "./globals.js";
7
+ import { NetGraph } from "./render/graph.js";
8
+ import { LayoutEngine } from "./render/layout.js";
9
+ import { Logger } from "./logger.js";
10
+ import { FrameParamKeys } from "./objects/Frame.js";
11
+ import { parseFileWithVisitor } from "./parser.js";
12
+ import { KiCadNetListOutputHandler } from "./render/KiCadNetListOutputHandler.js";
13
+ import { renderSheetsToSVG, generateSvgOutput, generatePdfOutput } from "./render/render.js";
14
+ import { EvaluateERCRules } from "./rules-check/rules.js";
15
+ import { RuntimeExecutionError, ParseSyntaxError, ParseError, printWarnings, RenderError, generateDebugSequenceAction, sequenceActionString, SimpleStopwatch } from "./utils.js";
16
+ import { ParserVisitor } from "./visitor.js";
17
+ export async function renderScript(scriptData, outputPath, options) {
18
+ const parseHandlers = [
19
+ new KiCadNetListOutputHandler(),
20
+ ];
21
+ return renderScriptCustom(scriptData, outputPath, options, parseHandlers, [DefaultPostAnnotationCallback]);
22
+ }
23
+ export async function renderScriptCustom(scriptData, outputPath, options, parseHandlers, postAnnotationCallbacks) {
24
+ const { dumpNets = false, dumpData = false, showStats = false, enableErc = false, enableBom = false, lexerDiagnostics = false, lexerVerbose = false, lexerTokens = false, lexerMapping = false, lexerSummary = false, inputPath = '', bomOutputPath = undefined, environment } = options;
25
+ const errors = [];
26
+ const onErrorHandler = (message, context, error) => {
27
+ if (error && error instanceof RuntimeExecutionError) {
28
+ errors.push(error);
29
+ }
30
+ else if (error && error instanceof RecognitionException) {
31
+ if (context !== null) {
32
+ errors.push(new ParseSyntaxError(message, context.start, context.stop));
33
+ }
34
+ else {
35
+ if (error.recognizer) {
36
+ const recognizer = error.recognizer;
37
+ errors.push(new ParseSyntaxError(message, {
38
+ line: recognizer.currentTokenStartLine,
39
+ column: recognizer.currentTokenColumn
40
+ }));
41
+ }
42
+ else {
43
+ errors.push(new ParseSyntaxError(message));
44
+ }
45
+ }
46
+ }
47
+ else {
48
+ errors.push(new ParseError(message, context.start, context.stop));
49
+ }
50
+ };
51
+ const visitor = new ParserVisitor(true, onErrorHandler, environment);
52
+ environment.setCurrentFile(inputPath);
53
+ visitor.log(`current file: ${inputPath}`);
54
+ visitor.onImportFile = (visitor, filePath, fileData, errorHandler, fileLineOffset = 0) => {
55
+ visitor.enterFile(filePath);
56
+ errors.splice(0, errors.length);
57
+ const result = parseFileWithVisitor(visitor, fileData, {
58
+ enableLexerDiagnostics: lexerDiagnostics,
59
+ enableLexerVerbose: lexerVerbose,
60
+ lineOffset: fileLineOffset,
61
+ });
62
+ const { throwError, tree, tokens } = result;
63
+ let { hasError, hasParseError } = result;
64
+ if (errors.length > 0) {
65
+ hasError = true;
66
+ hasParseError = true;
67
+ }
68
+ visitor.exitFile();
69
+ if (hasError || hasParseError) {
70
+ let importErrorMsg = "";
71
+ if (throwError) {
72
+ importErrorMsg = ": " + throwError.message;
73
+ }
74
+ throw new ParseError(`Error parsing imported file: ${filePath}${importErrorMsg}`, undefined, undefined, filePath);
75
+ }
76
+ return { hasError, hasParseError, tree, tokens };
77
+ };
78
+ const dumpDirectory = environment.getRelativeToModule('/dump/');
79
+ if (dumpData) {
80
+ console.log('Dump data to:', dumpDirectory);
81
+ if (!environment.existsSync(dumpDirectory)) {
82
+ environment.mkdirSync(dumpDirectory);
83
+ }
84
+ }
85
+ if (inputPath !== '') {
86
+ visitor.enterFile(inputPath);
87
+ }
88
+ await visitor.resolveImportsAndLoad(inputPath, scriptData);
89
+ const { tree, parser, tokens, lexer, parserTimeTaken, lexerTimeTaken, throwError } = await parseFileWithVisitor(visitor, scriptData, {
90
+ enableLexerDiagnostics: lexerDiagnostics,
91
+ enableLexerVerbose: lexerVerbose,
92
+ enableLexerTokenStream: lexerTokens !== false || lexerMapping !== false,
93
+ });
94
+ printWarnings(visitor.getWarnings());
95
+ showStats && console.log('Lexing took:', lexerTimeTaken);
96
+ showStats && console.log('Parsing took:', parserTimeTaken);
97
+ if (lexerDiagnostics && lexer.diagnosticCollector) {
98
+ console.log('\n');
99
+ if (lexerSummary) {
100
+ lexer.diagnosticCollector.printLexerOperationSummary();
101
+ }
102
+ if (lexerTokens !== false) {
103
+ const limit = typeof lexerTokens === 'number' ? lexerTokens : undefined;
104
+ lexer.diagnosticCollector.printTokenStream(limit);
105
+ }
106
+ if (lexerMapping !== false) {
107
+ if (typeof lexerMapping === 'string') {
108
+ const match = lexerMapping.match(/^(\d+)-(\d+)$/);
109
+ if (match) {
110
+ const startLine = parseInt(match[1], 10);
111
+ const endLine = parseInt(match[2], 10);
112
+ lexer.diagnosticCollector.printCharacterToTokenMapping(startLine, endLine);
113
+ }
114
+ else {
115
+ console.log('Invalid line range format. Use format like "1-10"');
116
+ }
117
+ }
118
+ else {
119
+ lexer.diagnosticCollector.printCharacterToTokenMapping();
120
+ }
121
+ }
122
+ lexer.diagnosticCollector.printReport();
123
+ const recommendations = lexer.diagnosticCollector.getRecommendations();
124
+ if (recommendations.length > 0) {
125
+ console.log('Performance Recommendations:');
126
+ recommendations.forEach((rec, idx) => {
127
+ console.log(` ${idx + 1}. ${rec}`);
128
+ });
129
+ console.log('');
130
+ }
131
+ }
132
+ try {
133
+ visitor.annotateComponents();
134
+ }
135
+ catch (err) {
136
+ throw new RenderError(`Error during component annotation: ${err}`, 'annotation');
137
+ }
138
+ const componentLinks = visitor.getComponentCtxLinks();
139
+ const importedLibraries = Array.from(visitor.getScope().libraries.values());
140
+ for (let i = 0; i < postAnnotationCallbacks.length; i++) {
141
+ await postAnnotationCallbacks[i](options, scriptData, tree, tokens, componentLinks, importedLibraries, environment);
142
+ }
143
+ visitor.cacheLibraries();
144
+ if (dumpNets) {
145
+ const nets = visitor.dumpNets();
146
+ nets.forEach(item => console.log(item.join(" | ")));
147
+ }
148
+ dumpData && environment.writeFileSync(dumpDirectory + 'tree.lisp', tree.toStringTree(null, parser));
149
+ dumpData && environment.writeFileSync(dumpDirectory + 'raw-parser.txt', visitor.logger.dump());
150
+ if (throwError) {
151
+ errors.push(throwError);
152
+ }
153
+ let svgOutput = "";
154
+ if (errors.length === 0 && throwError === undefined) {
155
+ const { frameComponent } = visitor.applySheetFrameComponent();
156
+ const { sequence, nets } = visitor.getGraph();
157
+ if (enableBom && bomOutputPath) {
158
+ const documentVariable = visitor.getScope().variables.get('document');
159
+ const bomConfig = documentVariable.bom;
160
+ const bomData = generateBom(bomConfig, visitor.getScope().getInstances());
161
+ const bomCsvOutput = generateBomCSV(bomData);
162
+ await saveBomOutputCsv(environment, bomCsvOutput, bomOutputPath);
163
+ console.log('Generated BOM file', bomOutputPath);
164
+ }
165
+ const tmpSequence = generateDebugSequenceAction(sequence).map(item => sequenceActionString(item));
166
+ dumpData && environment.writeFileSync(dumpDirectory + 'raw-sequence.txt', tmpSequence.join('\n'));
167
+ try {
168
+ let fileExtension = null;
169
+ let outputDefaultZoom = defaultZoomScale;
170
+ if (outputPath) {
171
+ fileExtension = path.extname(outputPath).substring(1);
172
+ }
173
+ for (let i = 0; i < parseHandlers.length; i++) {
174
+ const handler = parseHandlers[i];
175
+ if (handler.beforeRender) {
176
+ const keepParsing = handler.parse(visitor, outputPath, fileExtension);
177
+ if (!keepParsing) {
178
+ return {
179
+ svgOutput: null,
180
+ errors
181
+ };
182
+ }
183
+ }
184
+ }
185
+ const logger = new Logger();
186
+ const graphEngine = new NetGraph(logger);
187
+ const layoutEngine = new LayoutEngine(logger);
188
+ const layoutTimer = new SimpleStopwatch();
189
+ let sheetFrames;
190
+ try {
191
+ const { graph, containerFrames } = graphEngine.generateLayoutGraph(sequence, nets);
192
+ sheetFrames = layoutEngine.runLayout(graph, containerFrames, nets);
193
+ if (enableErc) {
194
+ const ercResults = EvaluateERCRules(visitor, graph, nets);
195
+ if (ercResults.length > 0) {
196
+ console.log(`ERC found ${ercResults.length} items:`);
197
+ ercResults.forEach((item, index) => {
198
+ console.log(`${(index + 1).toString().padStart(3)}. line ${item.start.line}, column ${item.start.column}: ${item.type} - ${item.message}`);
199
+ });
200
+ }
201
+ else {
202
+ console.log('No ERC issues found');
203
+ }
204
+ }
205
+ }
206
+ catch (err) {
207
+ throw new RenderError(`Error during layout generation: ${err}`, 'layout');
208
+ }
209
+ layoutEngine.printWarnings();
210
+ showStats && console.log('Layout took:', layoutTimer.lap());
211
+ dumpData && environment.writeFileSync(dumpDirectory + 'raw-layout.txt', layoutEngine.logger.dump());
212
+ const generateSvgTimer = new SimpleStopwatch();
213
+ const renderLogger = new Logger();
214
+ let svgCanvas;
215
+ try {
216
+ svgCanvas = renderSheetsToSVG(sheetFrames, renderLogger);
217
+ }
218
+ catch (err) {
219
+ throw new RenderError(`Error during SVG generation: ${err}`, 'svg_generation');
220
+ }
221
+ showStats && console.log('Render took:', generateSvgTimer.lap());
222
+ dumpData && environment.writeFileSync(dumpDirectory + 'raw-render.txt', renderLogger.dump());
223
+ try {
224
+ if (fileExtension === "pdf") {
225
+ outputDefaultZoom = 1;
226
+ }
227
+ svgOutput = generateSvgOutput(svgCanvas, outputDefaultZoom);
228
+ }
229
+ catch (err) {
230
+ throw new RenderError(`Error generating SVG output: ${err}`, 'svg_output');
231
+ }
232
+ if (outputPath) {
233
+ if (fileExtension === 'svg') {
234
+ try {
235
+ environment.writeFileSync(outputPath, svgOutput);
236
+ }
237
+ catch (err) {
238
+ throw new RenderError(`Error writing SVG file: ${err}`, 'file_output');
239
+ }
240
+ }
241
+ else if (fileExtension === 'pdf') {
242
+ let sheetSize = "A4";
243
+ let sheetSizeDefined = false;
244
+ if (frameComponent) {
245
+ sheetSize = frameComponent.getParam(FrameParamKeys.PaperSize);
246
+ sheetSizeDefined = true;
247
+ }
248
+ try {
249
+ const doc = new PDFDocument({
250
+ layout: 'landscape',
251
+ size: sheetSize
252
+ });
253
+ const outputStream = environment.createWriteStream(outputPath);
254
+ generatePdfOutput(doc, svgCanvas, sheetSize, sheetSizeDefined, outputDefaultZoom);
255
+ doc.pipe(outputStream);
256
+ doc.end();
257
+ }
258
+ catch (err) {
259
+ throw new RenderError(`Error generating PDF file: ${err}`, 'pdf_output');
260
+ }
261
+ }
262
+ else {
263
+ throw new RenderError(`Invalid output format: ${fileExtension}`, 'file_output');
264
+ }
265
+ console.log('Generated file', outputPath);
266
+ }
267
+ }
268
+ catch (err) {
269
+ throw new RenderError(`Error during rendering: ${err}`, 'output_generation');
270
+ }
271
+ }
272
+ return {
273
+ svgOutput,
274
+ errors
275
+ };
276
+ }
@@ -1,6 +1,6 @@
1
1
  import fs from 'fs';
2
- import { renderScript } from './helpers.js';
3
- import { NodeScriptEnvironment } from "./environment.js";
2
+ import { renderScript } from "./pipeline.js";
3
+ import { NodeScriptEnvironment } from "./environment/environment.js";
4
4
  const mainDir = './__tests__/testData/renderData/';
5
5
  const env = new NodeScriptEnvironment();
6
6
  NodeScriptEnvironment.setInstance(env);
@@ -21,6 +21,7 @@ async function regenerateTests(extra = "") {
21
21
  env.setModuleDirectory(mainDir);
22
22
  env.setDefaultLibsPath(mainDir + '../../../libs/');
23
23
  await renderScript(scriptData, outputPath, {
24
+ inputPath,
24
25
  dumpNets: false,
25
26
  dumpData: false,
26
27
  showStats: false,
@@ -0,0 +1,20 @@
1
+ import { generateKiCadNetList, printTree } from "./export.js";
2
+ export class ParseOutputHandler {
3
+ beforeRender = false;
4
+ afterRender = false;
5
+ }
6
+ export class KiCadNetListOutputHandler extends ParseOutputHandler {
7
+ beforeRender = true;
8
+ parse(visitor, outputPath, fileExtension) {
9
+ if (outputPath !== null && fileExtension === "net") {
10
+ const { tree: kiCadNetList, missingFootprints } = generateKiCadNetList(visitor.getNetList());
11
+ missingFootprints.forEach(entry => {
12
+ console.log(`${entry.refdes} (${entry.instanceName}) does not have footprint`);
13
+ });
14
+ visitor.environment.writeFileSync(outputPath, printTree(kiCadNetList));
15
+ console.log('Generated file', outputPath);
16
+ return false;
17
+ }
18
+ return true;
19
+ }
20
+ }
@@ -0,0 +1,41 @@
1
+ import { defaultPageMarginMM, MilsToMM } from "../globals.js";
2
+ const PaperSizes = {
3
+ 'A0': [1189, 841],
4
+ 'A1': [841, 594],
5
+ 'A2': [594, 420],
6
+ 'A3': [420, 297],
7
+ 'A4': [297, 210],
8
+ 'A5': [210, 148],
9
+ 'A6': [148, 105],
10
+ };
11
+ export const PaperGridReferences = {
12
+ 'A0': [16, 24],
13
+ 'A1': [12, 16],
14
+ 'A2': [8, 12],
15
+ 'A3': [6, 8],
16
+ 'A4': [4, 6],
17
+ };
18
+ export function isSupportedPaperSize(type) {
19
+ if (PaperSizes[type]) {
20
+ return true;
21
+ }
22
+ return false;
23
+ }
24
+ export function getPaperSize(type, margin = defaultPageMarginMM) {
25
+ if (PaperSizes[type]) {
26
+ const [width, height] = PaperSizes[type];
27
+ const useWidth = width - margin * 2;
28
+ const useHeight = height - margin * 2;
29
+ return {
30
+ width: Math.floor(useWidth * (1 / MilsToMM)),
31
+ height: Math.floor(useHeight * (1 / MilsToMM)),
32
+ widthMM: useWidth,
33
+ heightMM: useHeight,
34
+ originalWidthMM: width,
35
+ originalHeightMM: height,
36
+ };
37
+ }
38
+ else {
39
+ return getPaperSize('A4');
40
+ }
41
+ }