circuitscript 0.1.7 → 0.1.9

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.
@@ -1,8 +1,8 @@
1
- import pkg from '@dagrejs/graphlib';
2
- const { Graph, alg } = pkg;
1
+ import graphlib, { Graph } from '@dagrejs/graphlib';
2
+ const { alg } = graphlib;
3
3
  import { SymbolCustom, SymbolDrawing, SymbolCustomModule, SymbolPlaceholder, SymbolText, PlaceHolderCommands } from "./draw_symbols.js";
4
4
  import { FrameAction, SequenceAction } from "./objects/ExecutionScope.js";
5
- import { ComponentTypes, defaultFrameTitleTextSize, defaultGridSizeUnits, FrameType, ParamKeys, SymbolPinSide, WireAutoDirection } from './globals.js';
5
+ import { ComponentTypes, defaultFrameTitleTextSize, defaultGridSizeUnits, FrameType, ParamKeys, WireAutoDirection } from './globals.js';
6
6
  import { Geometry, HorizontalAlign, VerticalAlign } from './geometry.js';
7
7
  import { Logger } from './logger.js';
8
8
  import { FixedFrameIds, Frame, FrameParamKeys, FramePlotDirection } from './objects/Frame.js';
@@ -30,7 +30,7 @@ export class LayoutEngine {
30
30
  return "[" + value + "]" + padding;
31
31
  }
32
32
  runLayout(sequence, nets) {
33
- const logNodesAndEdges = false;
33
+ const logNodesAndEdges = true;
34
34
  this.print('===== creating graph and populating with nodes =====');
35
35
  const { graph, containerFrames } = this.generateLayoutGraph(sequence, nets);
36
36
  this.print('===== done populating graph =====');
@@ -47,7 +47,7 @@ export class LayoutEngine {
47
47
  this.print('===== graph nodes =====');
48
48
  const nodes = graph.nodes();
49
49
  nodes.forEach(node => {
50
- this.print(node, graph.node(node));
50
+ this.print(`name:${node}, value:${graph.node(node)}`);
51
51
  });
52
52
  this.print('===== end nodes =====');
53
53
  this.print('');
@@ -606,7 +606,7 @@ export class LayoutEngine {
606
606
  let previousNode = null;
607
607
  let previousPin = null;
608
608
  const graph = new Graph({
609
- directed: false,
609
+ directed: true,
610
610
  compound: true,
611
611
  });
612
612
  this.print('sequence length:', sequence.length);
@@ -624,7 +624,7 @@ export class LayoutEngine {
624
624
  const tmpInstanceName = component.instanceName;
625
625
  if (!graph.hasNode(tmpInstanceName)) {
626
626
  this.print('create instance', tmpInstanceName);
627
- const { displayProp = null, widthProp = null, heightProp = null } = component;
627
+ const { displayProp = null } = component;
628
628
  let tmpSymbol;
629
629
  if (displayProp instanceof SymbolDrawing) {
630
630
  tmpSymbol = new SymbolPlaceholder(displayProp);
@@ -640,26 +640,6 @@ export class LayoutEngine {
640
640
  }
641
641
  }
642
642
  applyComponentParamsToSymbol(component, tmpSymbol);
643
- if (component.parameters.has(ParamKeys.angle)) {
644
- const value = component.parameters.get(ParamKeys.angle).toNumber();
645
- tmpSymbol.angle = value;
646
- }
647
- if (component.parameters.has(ParamKeys.flipX)) {
648
- tmpSymbol.flipX =
649
- component.parameters.get(ParamKeys.flipX);
650
- }
651
- if (component.parameters.has(ParamKeys.flipY)) {
652
- tmpSymbol.flipY =
653
- component.parameters.get(ParamKeys.flipY);
654
- }
655
- if (tmpSymbol instanceof SymbolCustom) {
656
- if (widthProp) {
657
- tmpSymbol.bodyWidth = milsToMM(widthProp);
658
- }
659
- if (heightProp) {
660
- tmpSymbol.bodyHeight = milsToMM(heightProp);
661
- }
662
- }
663
643
  tmpSymbol.refreshDrawing();
664
644
  const { width: useWidth, height: useHeight } = tmpSymbol.size();
665
645
  tmpComponent = new RenderComponent(component, useWidth, useHeight);
@@ -765,7 +745,11 @@ export class LayoutEngine {
765
745
  };
766
746
  }
767
747
  setGraphEdge(graph, node1, node2, edgeValue) {
748
+ if (!graph.isDirected && graph.hasEdge(node1, node2)) {
749
+ this.print(`Warning: edge already exists ${node1} ${node2}`);
750
+ }
768
751
  graph.setEdge(node1, node2, edgeValue);
752
+ this.print(`created edge: node1:${node1} node2:${node2} edgeValue:${edgeValue}`);
769
753
  }
770
754
  sizeSubGraphs(graph) {
771
755
  const subGraphs = alg.components(graph);
@@ -822,7 +806,6 @@ export class LayoutEngine {
822
806
  this.placeSubgraphV2(graph, firstNodeId, subgraphEdges);
823
807
  }
824
808
  placeSubgraphV2(graph, firstNodeId, subgraphEdges) {
825
- let firstNodePlaced = false;
826
809
  const originNodes = [];
827
810
  const originNodeGroups = new Map();
828
811
  function findOriginNode(node) {
@@ -848,14 +831,6 @@ export class LayoutEngine {
848
831
  const [nodeId1, pin1, nodeId2, pin2] = graph.edge(edge);
849
832
  const [, node1] = graph.node(nodeId1);
850
833
  const [, node2] = graph.node(nodeId2);
851
- if (nodeId1 === firstNodeId && !firstNodePlaced) {
852
- this.print('first node placed at origin');
853
- this.placeNodeAtPosition(numeric(0), numeric(0), node1, pin1);
854
- firstNodePlaced = true;
855
- node1.isFloating = false;
856
- originNodes.push(node1);
857
- originNodeGroups.set(node1.toString(), [node1]);
858
- }
859
834
  this.print('edge:', '[', node1, pin1, node1.isFloating, ']', '[', node2, pin2, node2.isFloating, ']');
860
835
  if (!node1.isFloating && node2.isFloating) {
861
836
  fixedNode = node1;
@@ -933,6 +908,7 @@ export class LayoutEngine {
933
908
  this.print(`set wire auto end at: ${untilX} ${untilY}`);
934
909
  }
935
910
  });
911
+ this.print('----');
936
912
  });
937
913
  }
938
914
  mergeOriginNodes(node1, pin1, node2, pin2, originNode1, originNode2, originNodes, originNodeGroups) {
@@ -1082,61 +1058,69 @@ function generateLayoutPinDefinition(component) {
1082
1058
  const pins = component.pins;
1083
1059
  const symbolPinDefinitions = [];
1084
1060
  const existingPinIds = Array.from(pins.keys());
1085
- if (component.arrangeProps === null) {
1086
- for (let i = 0; i < existingPinIds.length; i++) {
1087
- const pinPosition = Math.floor(i / 2);
1088
- const pin = pins.get(existingPinIds[i]);
1089
- symbolPinDefinitions.push({
1090
- side: (i % 2 === 0) ? SymbolPinSide.Left : SymbolPinSide.Right,
1091
- pinId: existingPinIds[i],
1092
- text: pin.name,
1093
- position: pinPosition,
1094
- pinType: pin.pinType,
1095
- });
1061
+ const arrangeProps = component.arrangeProps ?? [];
1062
+ const addedPins = [];
1063
+ for (const [key, items] of arrangeProps) {
1064
+ let useItems;
1065
+ if (!Array.isArray(items)) {
1066
+ useItems = [items];
1096
1067
  }
1097
- }
1098
- else {
1099
- const addedPins = [];
1100
- for (const [key, items] of component.arrangeProps) {
1101
- let useItems;
1102
- if (!Array.isArray(items)) {
1103
- useItems = [items];
1104
- }
1105
- else {
1106
- useItems = [...items];
1107
- }
1108
- useItems.forEach(pinId => {
1109
- if (pinId instanceof NumericValue) {
1110
- const pinIdValue = pinId.toNumber();
1111
- if (existingPinIds.indexOf(pinIdValue) !== -1) {
1112
- const pin = pins.get(pinIdValue);
1113
- symbolPinDefinitions.push({
1114
- side: key,
1115
- pinId: pinIdValue,
1116
- text: pin.name,
1117
- position: pin.position,
1118
- pinType: pin.pinType,
1119
- });
1120
- addedPins.push(pinIdValue);
1121
- }
1122
- }
1123
- });
1068
+ else {
1069
+ useItems = [...items];
1124
1070
  }
1125
- const unplacedPins = existingPinIds.filter(pinId => {
1126
- return addedPins.indexOf(pinId) === -1;
1071
+ useItems.forEach(pinId => {
1072
+ if (pinId instanceof NumericValue) {
1073
+ const pinIdValue = pinId.toNumber();
1074
+ if (existingPinIds.indexOf(pinIdValue) !== -1) {
1075
+ const pin = pins.get(pinIdValue);
1076
+ symbolPinDefinitions.push({
1077
+ side: key,
1078
+ pinId: pinIdValue,
1079
+ text: pin.name,
1080
+ position: pin.position,
1081
+ pinType: pin.pinType,
1082
+ });
1083
+ addedPins.push(pinIdValue);
1084
+ }
1085
+ }
1127
1086
  });
1128
- if (unplacedPins.length > 0) {
1129
- throw "'arrange' property is defined, but not all pins are specified: " + unplacedPins.join(",");
1130
- }
1087
+ }
1088
+ const unplacedPins = existingPinIds.filter(pinId => {
1089
+ return addedPins.indexOf(pinId) === -1;
1090
+ });
1091
+ if (unplacedPins.length > 0) {
1092
+ component._unplacedPins = unplacedPins;
1093
+ console.warn("Warning: There are unplaced pins: " + unplacedPins);
1131
1094
  }
1132
1095
  return symbolPinDefinitions;
1133
1096
  }
1134
1097
  export function applyComponentParamsToSymbol(component, symbol) {
1098
+ const { widthProp = null, heightProp = null } = component;
1135
1099
  const newMap = new Map(component.parameters);
1136
1100
  if (!newMap.has('refdes')) {
1137
1101
  newMap.set('refdes', component.assignedRefDes ?? "?");
1138
1102
  }
1139
1103
  symbol.drawing.variables = newMap;
1104
+ if (component.parameters.has(ParamKeys.angle)) {
1105
+ const value = component.parameters.get(ParamKeys.angle).toNumber();
1106
+ symbol.angle = value;
1107
+ }
1108
+ if (component.parameters.has(ParamKeys.flipX)) {
1109
+ symbol.flipX =
1110
+ component.parameters.get(ParamKeys.flipX);
1111
+ }
1112
+ if (component.parameters.has(ParamKeys.flipY)) {
1113
+ symbol.flipY =
1114
+ component.parameters.get(ParamKeys.flipY);
1115
+ }
1116
+ if (symbol instanceof SymbolCustom) {
1117
+ if (widthProp) {
1118
+ symbol.bodyWidth = milsToMM(widthProp);
1119
+ }
1120
+ if (heightProp) {
1121
+ symbol.bodyHeight = milsToMM(heightProp);
1122
+ }
1123
+ }
1140
1124
  }
1141
1125
  function calculateSymbolAngle(symbol, pin, direction) {
1142
1126
  let directionVector = 0;
@@ -1464,7 +1448,9 @@ export function CalculatePinPositions(component) {
1464
1448
  tmpSymbol.refreshDrawing();
1465
1449
  const pins = component.pins;
1466
1450
  pins.forEach((value, key) => {
1467
- pinPositionMapping.set(key, tmpSymbol.pinPosition(key));
1451
+ if (component._unplacedPins.indexOf(key) === -1) {
1452
+ pinPositionMapping.set(key, tmpSymbol.pinPosition(key));
1453
+ }
1468
1454
  });
1469
1455
  return pinPositionMapping;
1470
1456
  }
@@ -15,6 +15,7 @@ export class ClassComponent {
15
15
  _copyID = null;
16
16
  _copyFrom = null;
17
17
  _pointLinkComponent;
18
+ _unplacedPins = [];
18
19
  arrangeProps = null;
19
20
  displayProp = null;
20
21
  widthProp = null;
@@ -131,12 +131,13 @@ export class ExecutionScope {
131
131
  exitContext() {
132
132
  return this.contextStack.pop();
133
133
  }
134
- findPropertyKeyTree() {
134
+ findPropertyKeyTree(visitor) {
135
135
  const keyNames = [];
136
136
  for (let i = this.contextStack.length - 1; i >= 0; i--) {
137
137
  const ctx = this.contextStack[i];
138
138
  if (ctx instanceof Property_key_exprContext) {
139
- keyNames.push([ctx, ctx.getText()]);
139
+ const result = visitor.visitResult(ctx);
140
+ keyNames.push([ctx, result]);
140
141
  }
141
142
  else if (typeof ctx === 'number') {
142
143
  keyNames.push(['index', ctx]);
@@ -150,9 +151,9 @@ export class ExecutionScope {
150
151
  popOnPropertyHandler() {
151
152
  return this.onPropertyHandler.pop();
152
153
  }
153
- triggerPropertyHandler(value, valueCtx) {
154
+ triggerPropertyHandler(visitor, value, valueCtx) {
154
155
  const lastHandler = this.onPropertyHandler[this.onPropertyHandler.length - 1];
155
- const propertyTree = this.findPropertyKeyTree();
156
+ const propertyTree = this.findPropertyKeyTree(visitor);
156
157
  lastHandler && lastHandler(propertyTree, value, valueCtx);
157
158
  }
158
159
  }
@@ -5,7 +5,7 @@ const mainDir = './__tests__/renderData/';
5
5
  const env = new NodeScriptEnvironment();
6
6
  NodeScriptEnvironment.setInstance(env);
7
7
  async function regenerateTests(extra = "") {
8
- env.prepareSVGEnvironment();
8
+ await env.prepareSVGEnvironment();
9
9
  const cstFiles = [];
10
10
  const files = fs.readdirSync(mainDir);
11
11
  files.forEach(file => {
@@ -13,19 +13,20 @@ async function regenerateTests(extra = "") {
13
13
  cstFiles.push(file);
14
14
  }
15
15
  });
16
- cstFiles.forEach(file => {
16
+ for (let i = 0; i < cstFiles.length; i++) {
17
+ const file = cstFiles[i];
17
18
  const inputPath = mainDir + file;
18
19
  const scriptData = fs.readFileSync(inputPath, { encoding: 'utf-8' });
19
20
  const outputPath = mainDir + 'svgs/' + file + extra + '.svg';
20
21
  env.setModuleDirectory(mainDir);
21
- renderScript(scriptData, outputPath, {
22
+ env.setDefaultLibsPath(mainDir + '../../libs/');
23
+ await renderScript(scriptData, outputPath, {
22
24
  dumpNets: false,
23
25
  dumpData: false,
24
26
  showStats: false,
25
27
  environment: env,
26
28
  });
27
- console.log('generated ', outputPath);
28
- });
29
+ }
29
30
  return cstFiles;
30
31
  }
31
32
  (async () => {
package/dist/esm/utils.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Big } from 'big.js';
2
+ import { ParserRuleContext } from "antlr4ng";
2
3
  import { ClassComponent } from "./objects/ClassComponent.js";
3
4
  import { NumericValue } from "./objects/ParamDefinition.js";
4
5
  import { SequenceAction } from './objects/ExecutionScope.js';
@@ -243,30 +244,52 @@ export class BaseError extends Error {
243
244
  startToken;
244
245
  endToken;
245
246
  filePath;
246
- constructor(message, startToken, endToken, filePath) {
247
+ constructor(message, startTokenOrCtx, endToken, filePath) {
247
248
  super(message);
248
249
  this.message = message;
249
- this.startToken = startToken;
250
- this.endToken = endToken;
250
+ if (startTokenOrCtx instanceof ParserRuleContext) {
251
+ this.startToken = startTokenOrCtx.start;
252
+ this.endToken = startTokenOrCtx.stop;
253
+ }
254
+ else {
255
+ this.startToken = startTokenOrCtx;
256
+ this.endToken = endToken;
257
+ }
251
258
  this.filePath = filePath;
252
259
  }
253
260
  toString() {
254
261
  const parts = [this.name];
255
- if (this.startToken) {
256
- const { line, column } = this.startToken;
257
- if (this.endToken && (this.endToken.line !== this.startToken.line || this.endToken.column !== this.startToken.column)) {
258
- const endLine = this.endToken.line;
259
- const endColumn = this.endToken.column + (this.endToken.stop - this.endToken.start);
260
- parts.push(` at ${line}:${column}-${endLine}:${endColumn}`);
261
- }
262
- else {
263
- parts.push(` at ${line}:${column}`);
264
- }
262
+ const linePosition = getLinePositionAsString({
263
+ start: this.startToken,
264
+ stop: this.endToken
265
+ });
266
+ if (linePosition !== null) {
267
+ parts.push(linePosition);
265
268
  }
266
269
  parts.push(`: ${this.message}`);
267
270
  return parts.join('');
268
271
  }
269
272
  }
273
+ export function getLinePositionAsString(ctx) {
274
+ if (ctx === null || ctx === undefined) {
275
+ return null;
276
+ }
277
+ const startToken = ctx.start;
278
+ const endToken = ctx.stop;
279
+ let result = null;
280
+ if (startToken) {
281
+ const { line, column } = startToken;
282
+ if (endToken && (endToken.line !== startToken.line || endToken.column !== startToken.column)) {
283
+ const endLine = endToken.line;
284
+ const endColumn = endToken.column + (endToken.stop - endToken.start);
285
+ result = ` at ${line}:${column}-${endLine}:${endColumn}`;
286
+ }
287
+ else {
288
+ result = ` at ${line}:${column}`;
289
+ }
290
+ }
291
+ return result;
292
+ }
270
293
  export class ParseSyntaxError extends BaseError {
271
294
  name = 'ParseSyntaxError';
272
295
  }
@@ -284,3 +307,21 @@ export class RenderError extends Error {
284
307
  this.stage = stage;
285
308
  }
286
309
  }
310
+ export function printWarnings(warnings) {
311
+ const warningMessages = [];
312
+ warnings.forEach(item => {
313
+ const { message } = item;
314
+ const linePosition = getLinePositionAsString(item.ctx);
315
+ const parts = [message];
316
+ if (linePosition !== null) {
317
+ parts.push(linePosition);
318
+ }
319
+ const finalMessage = parts.join('');
320
+ if (warningMessages.indexOf(finalMessage) === -1) {
321
+ warningMessages.push(finalMessage);
322
+ }
323
+ });
324
+ warningMessages.forEach(message => {
325
+ console.log(`Warning: ${message}`);
326
+ });
327
+ }
@@ -67,6 +67,7 @@ export default async function validate() {
67
67
  };
68
68
  const visitor = await validateScript(inputFilePath, scriptData, scriptOptions);
69
69
  const symbols = visitor.getSymbols().getSymbols();
70
+ const undefinedSymbols = [];
70
71
  symbols.forEach((value, key) => {
71
72
  if (value.type !== ParseSymbolType.Undefined) {
72
73
  value = value;
@@ -76,11 +77,29 @@ export default async function validate() {
76
77
  console.log(" " + instance.line + ":" + instance.column + " " + instance.start);
77
78
  });
78
79
  }
80
+ else {
81
+ undefinedSymbols.push(value);
82
+ }
79
83
  });
80
84
  const { parsedTokens } = await getSemanticTokens(scriptData, scriptOptions);
81
85
  parsedTokens.forEach(item => {
82
86
  const { line, column, tokenType, tokenModifiers, textValue } = item;
83
87
  console.log(`${line}:${column} - ${textValue} - ${tokenType} | ${tokenModifiers.join(',')}`);
84
88
  });
89
+ console.log('--- undefined ---');
90
+ undefinedSymbols.forEach(symbol => {
91
+ const { token } = symbol;
92
+ const info = {
93
+ start: {
94
+ line: (token?.line || 1),
95
+ character: token?.column || 0
96
+ },
97
+ end: {
98
+ line: (token?.line || 1),
99
+ character: (token?.column || 0) + ((token?.stop || 0) - (token?.start || 0) + 1)
100
+ }
101
+ };
102
+ console.log(token.text, info);
103
+ });
85
104
  }
86
105
  validate();
@@ -47,7 +47,7 @@ export class ParserVisitor extends BaseVisitor {
47
47
  });
48
48
  }
49
49
  catch (err) {
50
- throw new RuntimeExecutionError(err.message, ctx.start, ctx.stop);
50
+ throw new RuntimeExecutionError(err.message, ctx);
51
51
  }
52
52
  });
53
53
  return this.getExecutor().getCurrentPoint();
@@ -115,6 +115,24 @@ export class ParserVisitor extends BaseVisitor {
115
115
  };
116
116
  visitCreate_component_expr = (ctx) => {
117
117
  const scope = this.getScope();
118
+ const definedPinIds = [];
119
+ const arrangedPinIds = [];
120
+ const checkPinExistsAndNotDuplicated = (pinId, ctx) => {
121
+ if (definedPinIds.indexOf(pinId) === -1) {
122
+ this.warnings.push({
123
+ message: `Invalid pin ${pinId}`, ctx
124
+ });
125
+ }
126
+ if (arrangedPinIds.indexOf(pinId) !== -1) {
127
+ this.warnings.push({
128
+ message: `Pin ${pinId} specified more than once`,
129
+ ctx,
130
+ });
131
+ }
132
+ arrangedPinIds.push(pinId);
133
+ };
134
+ let didDefineArrangeProp = false;
135
+ let didDefineDisplayProp = false;
118
136
  scope.setOnPropertyHandler((path, value, ctx) => {
119
137
  if (path.length === 1) {
120
138
  const [, keyName] = path[0];
@@ -127,9 +145,25 @@ export class ParserVisitor extends BaseVisitor {
127
145
  case 'height':
128
146
  this.validateNumeric(value, ctx);
129
147
  break;
148
+ case 'display':
149
+ if (didDefineArrangeProp) {
150
+ throw new RuntimeExecutionError("arrange property has already been defined", ctx);
151
+ }
152
+ didDefineDisplayProp = true;
153
+ break;
154
+ case 'arrange':
155
+ if (didDefineDisplayProp) {
156
+ throw new RuntimeExecutionError("display property already defined", ctx);
157
+ }
158
+ didDefineArrangeProp = true;
159
+ break;
130
160
  case 'pins':
131
161
  if (!(value instanceof Map)) {
132
162
  this.validateNumeric(value, ctx);
163
+ const numPins = value.toNumber();
164
+ for (let i = 0; i < numPins; i++) {
165
+ definedPinIds.push(i + 1);
166
+ }
133
167
  }
134
168
  break;
135
169
  case 'copy':
@@ -140,7 +174,7 @@ export class ParserVisitor extends BaseVisitor {
140
174
  this.validateBoolean(value, ctx);
141
175
  }
142
176
  else {
143
- throw new RuntimeExecutionError("Invalid value for 'copy' property", ctx.start, ctx.end);
177
+ throw new RuntimeExecutionError("Invalid value for 'copy' property", ctx);
144
178
  }
145
179
  break;
146
180
  }
@@ -150,20 +184,26 @@ export class ParserVisitor extends BaseVisitor {
150
184
  if (keyName === 'arrange') {
151
185
  const [sideKeyCtx, sideKeyName] = path[1];
152
186
  if (ValidPinSides.indexOf(sideKeyName) === -1) {
153
- throw new RuntimeExecutionError(`Invalid side ${sideKeyName} in arrange`, sideKeyCtx.start, sideKeyCtx.stop);
187
+ throw new RuntimeExecutionError(`Invalid side ${sideKeyName} in arrange`, sideKeyCtx);
154
188
  }
155
189
  else {
156
- if (path.length > 2 && path[2][0] === 'index') {
190
+ if (path.length === 2 && value instanceof NumericValue) {
191
+ checkPinExistsAndNotDuplicated(value.toNumber(), ctx);
192
+ }
193
+ else if (path.length > 2 && path[2][0] === 'index') {
157
194
  if (Array.isArray(value)) {
158
195
  const goodBlank = value.length === 1 &&
159
196
  value[0] instanceof NumericValue;
160
197
  if (!goodBlank) {
161
- throw new RuntimeExecutionError(`Invalid blank specifier`, ctx.start, ctx.stop);
198
+ throw new RuntimeExecutionError(`Invalid blank specifier`, ctx);
162
199
  }
163
200
  }
164
201
  else {
165
202
  if (!(value instanceof NumericValue)) {
166
- throw new RuntimeExecutionError(`Invalid numeric value for arrange.${sideKeyName}`, ctx.start, ctx.stop);
203
+ throw new RuntimeExecutionError(`Invalid numeric value for arrange.${sideKeyName}`, ctx);
204
+ }
205
+ else {
206
+ checkPinExistsAndNotDuplicated(value.toNumber(), ctx);
167
207
  }
168
208
  }
169
209
  }
@@ -184,10 +224,12 @@ export class ParserVisitor extends BaseVisitor {
184
224
  }
185
225
  else if (keyName === 'pins') {
186
226
  if (path.length === 2) {
227
+ const idName = path[1][1];
228
+ definedPinIds.push(idName);
187
229
  if (value.length === 2) {
188
230
  const [pinType,] = value;
189
231
  if (pinType instanceof UndeclaredReference) {
190
- throw new RuntimeExecutionError(`Invalid pin type: ${pinType.reference.name}`, ctx.start, ctx.end);
232
+ throw new RuntimeExecutionError(`Invalid pin type: ${pinType.reference.name}`, ctx);
191
233
  }
192
234
  }
193
235
  }
@@ -416,7 +458,7 @@ export class ParserVisitor extends BaseVisitor {
416
458
  this.getScope().enterContext(ctxValue);
417
459
  const keyName = this.visitResult(ctxKey);
418
460
  const value = this.visitResult(ctxValue);
419
- scope.triggerPropertyHandler(value, ctxValue);
461
+ scope.triggerPropertyHandler(this, value, ctxValue);
420
462
  this.getScope().exitContext();
421
463
  this.getScope().exitContext();
422
464
  if (value instanceof UndeclaredReference && (value.reference.parentValue === undefined
@@ -437,7 +479,7 @@ export class ParserVisitor extends BaseVisitor {
437
479
  value = ctx.data_expr().map((item, index) => {
438
480
  this.getScope().enterContext(index);
439
481
  const result = this.visitResult(item);
440
- this.getScope().triggerPropertyHandler(result, item);
482
+ this.getScope().triggerPropertyHandler(this, result, item);
441
483
  this.getScope().exitContext();
442
484
  return result;
443
485
  });
@@ -1063,45 +1105,46 @@ export class ParserVisitor extends BaseVisitor {
1063
1105
  parseCreateComponentPins(pinData) {
1064
1106
  const pins = [];
1065
1107
  if (pinData instanceof NumericValue) {
1108
+ const tmpMap = new Map();
1066
1109
  const lastPin = pinData.toNumber();
1067
1110
  for (let i = 0; i < lastPin; i++) {
1068
1111
  const pinId = i + 1;
1069
- pins.push(new PinDefinition(pinId, PinIdType.Int, pinId.toString()));
1112
+ tmpMap.set(pinId, numeric(pinId));
1070
1113
  }
1071
- }
1072
- else if (pinData instanceof Map) {
1073
- for (const [pinId, pinDef] of pinData) {
1074
- let pinIdType = PinIdType.Int;
1075
- let pinType = PinTypes.Any;
1076
- let pinName = null;
1077
- let altPinNames = [];
1078
- if (typeof pinId === 'string') {
1079
- pinIdType = PinIdType.Str;
1080
- }
1081
- if (Array.isArray(pinDef)) {
1082
- const firstValue = pinDef[0];
1083
- if (firstValue.type
1084
- && firstValue.type === ReferenceTypes.pinType
1085
- && this.pinTypes.indexOf(firstValue.value) !== -1) {
1086
- pinType = firstValue.value;
1087
- pinName = pinDef[1];
1088
- if (pinDef.length > 2) {
1089
- altPinNames = pinDef.slice(2);
1090
- }
1091
- }
1092
- else {
1093
- pinName = pinDef[0];
1094
- if (pinDef.length > 1) {
1095
- altPinNames = pinDef.slice(1);
1096
- }
1114
+ pinData = tmpMap;
1115
+ }
1116
+ pinData = pinData ?? [];
1117
+ for (const [pinId, pinDef] of pinData) {
1118
+ let pinIdType = PinIdType.Int;
1119
+ let pinType = PinTypes.Any;
1120
+ let pinName = null;
1121
+ let altPinNames = [];
1122
+ if (typeof pinId === 'string') {
1123
+ pinIdType = PinIdType.Str;
1124
+ }
1125
+ if (Array.isArray(pinDef)) {
1126
+ const firstValue = pinDef[0];
1127
+ if (firstValue.type
1128
+ && firstValue.type === ReferenceTypes.pinType
1129
+ && this.pinTypes.indexOf(firstValue.value) !== -1) {
1130
+ pinType = firstValue.value;
1131
+ pinName = pinDef[1];
1132
+ if (pinDef.length > 2) {
1133
+ altPinNames = pinDef.slice(2);
1097
1134
  }
1098
1135
  }
1099
1136
  else {
1100
- pinName = pinDef;
1137
+ pinName = pinDef[0];
1138
+ if (pinDef.length > 1) {
1139
+ altPinNames = pinDef.slice(1);
1140
+ }
1101
1141
  }
1102
- this.log('pins', pinId, pinIdType, pinName, pinType, altPinNames);
1103
- pins.push(new PinDefinition(pinId, pinIdType, pinName, pinType, altPinNames));
1104
1142
  }
1143
+ else {
1144
+ pinName = pinDef;
1145
+ }
1146
+ this.log('pins', pinId, pinIdType, pinName, pinType, altPinNames);
1147
+ pins.push(new PinDefinition(pinId, pinIdType, pinName, pinType, altPinNames));
1105
1148
  }
1106
1149
  return pins;
1107
1150
  }
@@ -1310,6 +1353,9 @@ export class ParserVisitor extends BaseVisitor {
1310
1353
  });
1311
1354
  return properties;
1312
1355
  }
1356
+ getWarnings() {
1357
+ return this.warnings;
1358
+ }
1313
1359
  }
1314
1360
  const ComponentRefDesPrefixes = {
1315
1361
  res: 'R',