circuitscript 0.1.8 → 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.
@@ -33,6 +33,7 @@ export class BaseVisitor extends CircuitScriptVisitor {
33
33
  onErrorHandler = null;
34
34
  environment;
35
35
  importedFiles = [];
36
+ warnings = [];
36
37
  onImportFile = async (visitor, filePath, fileData, onErrorHandler) => {
37
38
  throw "Import file not implemented";
38
39
  };
@@ -41,7 +42,7 @@ export class BaseVisitor extends CircuitScriptVisitor {
41
42
  this.logger = new Logger();
42
43
  this.onErrorHandler = onErrorHandler;
43
44
  this.environment = environment;
44
- this.startingContext = new ExecutionContext(DoubleDelimiter1, `${DoubleDelimiter1}.`, '/', 0, 0, silent, this.logger, null);
45
+ this.startingContext = new ExecutionContext(DoubleDelimiter1, `${DoubleDelimiter1}.`, '/', 0, 0, silent, this.logger, this.warnings, null);
45
46
  const scope = this.startingContext.scope;
46
47
  scope.sequence.push([
47
48
  SequenceAction.At, scope.componentRoot, scope.currentPin
@@ -423,7 +424,7 @@ export class BaseVisitor extends CircuitScriptVisitor {
423
424
  this.setResult(ctx, returnList);
424
425
  };
425
426
  visitImport_expr = (ctx) => {
426
- throw new RuntimeExecutionError("Cannot parse imports here", ctx.start, ctx.stop);
427
+ throw new RuntimeExecutionError("Cannot parse imports here", ctx);
427
428
  };
428
429
  visitFunction_return_expr = (ctx) => {
429
430
  const executor = this.getExecutor();
@@ -517,7 +518,7 @@ export class BaseVisitor extends CircuitScriptVisitor {
517
518
  }
518
519
  catch (err) {
519
520
  if (ctx != null) {
520
- throw new RuntimeExecutionError("An error occurred while importing file", ctx.start, ctx.stop);
521
+ throw new RuntimeExecutionError("An error occurred while importing file", ctx);
521
522
  }
522
523
  else {
523
524
  this.log('An error occurred while importing file:', err.message);
@@ -533,7 +534,7 @@ export class BaseVisitor extends CircuitScriptVisitor {
533
534
  }
534
535
  }
535
536
  if (errorMessage !== null && ctx) {
536
- throw new RuntimeExecutionError(errorMessage, ctx.start, ctx.end);
537
+ throw new RuntimeExecutionError(errorMessage, ctx);
537
538
  }
538
539
  const newImportedFile = {
539
540
  id: name.trim(),
@@ -637,7 +638,7 @@ export class BaseVisitor extends CircuitScriptVisitor {
637
638
  const executionLevel = currentExecutionContext.executionLevel;
638
639
  const executionContextNamespace = currentExecutionContext.namespace
639
640
  + executionContextName + ".";
640
- const newExecutor = new ExecutionContext(executionContextName, executionContextNamespace, netNamespace, executionLevel + 1, this.getExecutor().scope.indentLevel + 1, currentExecutionContext.silent, currentExecutionContext.logger, parentContext);
641
+ const newExecutor = new ExecutionContext(executionContextName, executionContextNamespace, netNamespace, executionLevel + 1, this.getExecutor().scope.indentLevel + 1, currentExecutionContext.silent, currentExecutionContext.logger, currentExecutionContext.warnings, parentContext);
641
642
  executionStack.push(newExecutor);
642
643
  this.setupDefinedParameters(funcDefinedParameters, passedInParameters, newExecutor);
643
644
  return newExecutor;
@@ -654,7 +655,7 @@ export class BaseVisitor extends CircuitScriptVisitor {
654
655
  }
655
656
  const result = validateFunction(value);
656
657
  if (!result) {
657
- throw new RuntimeExecutionError(`Invalid ${expectedType}`, context.start, context.stop);
658
+ throw new RuntimeExecutionError(`Invalid ${expectedType}`, context);
658
659
  }
659
660
  return result;
660
661
  }
@@ -2,7 +2,7 @@ import { milsToMM } from "./helpers.js";
2
2
  import { ColorScheme, CustomSymbolParamTextSize, CustomSymbolPinIdSize, CustomSymbolPinTextSize, CustomSymbolRefDesSize, PortArrowSize, PortPaddingHorizontal, PortPaddingVertical, ReferenceTypes, RenderFlags, SymbolPinSide, defaultFont, defaultPinIdTextSize, defaultPinNameTextSize, defaultSymbolLineWidth, fontDisplayScale } from "./globals.js";
3
3
  import { Geometry, GeometryProp, HorizontalAlign, HorizontalAlignProp, Textbox, VerticalAlign, VerticalAlignProp } from "./geometry.js";
4
4
  import { PinTypes } from "./objects/PinTypes.js";
5
- import { roundValue, throwWithContext } from "./utils.js";
5
+ import { roundValue, RuntimeExecutionError, throwWithContext } from "./utils.js";
6
6
  import { DeclaredReference, UndeclaredReference } from "./objects/types.js";
7
7
  import { numeric, NumericValue } from "./objects/ParamDefinition.js";
8
8
  export class SymbolGraphic {
@@ -81,6 +81,9 @@ export class SymbolGraphic {
81
81
  }
82
82
  pinPosition(id) {
83
83
  const pin = this.drawing.getPinPosition(id);
84
+ if (pin === null) {
85
+ throw new RuntimeExecutionError(`Could not determine pin ${id} position`);
86
+ }
84
87
  const [x, y] = pin.start;
85
88
  const useX = roundValue(x);
86
89
  const useY = roundValue(y);
@@ -2,7 +2,7 @@ import { BlockTypes, ComponentTypes, Delimiter1, GlobalNames, NoNetText, ParamKe
2
2
  import { ClassComponent, ModuleComponent } from './objects/ClassComponent.js';
3
3
  import { ActiveObject, ExecutionScope, FrameAction, SequenceAction } from './objects/ExecutionScope.js';
4
4
  import { Net } from './objects/Net.js';
5
- import { numeric } from './objects/ParamDefinition.js';
5
+ import { numeric, NumericValue } from './objects/ParamDefinition.js';
6
6
  import { PortSide } from './objects/PinDefinition.js';
7
7
  import { DeclaredReference, Direction } from './objects/types.js';
8
8
  import { Wire } from './objects/Wire.js';
@@ -27,7 +27,8 @@ export class ExecutionContext {
27
27
  __functionCache = {};
28
28
  parentContext;
29
29
  componentAngleFollowsWire = true;
30
- constructor(name, namespace, netNamespace, executionLevel = 0, indentLevel = 0, silent = false, logger, parent) {
30
+ warnings = [];
31
+ constructor(name, namespace, netNamespace, executionLevel = 0, indentLevel = 0, silent = false, logger, warnings, parent) {
31
32
  this.name = name;
32
33
  this.namespace = namespace;
33
34
  this.netNamespace = netNamespace;
@@ -39,6 +40,14 @@ export class ExecutionContext {
39
40
  this.silent = silent;
40
41
  this.log('create new execution context', this.namespace, this.name, this.scope.indentLevel);
41
42
  this.parentContext = parent;
43
+ this.warnings = warnings;
44
+ }
45
+ logWarning(message, context, fileName) {
46
+ this.warnings.push({
47
+ message,
48
+ ctx: context,
49
+ fileName,
50
+ });
42
51
  }
43
52
  log(...params) {
44
53
  const indentOutput = ''.padStart(this.scope.indentLevel * 4, ' ');
@@ -125,7 +134,6 @@ export class ExecutionContext {
125
134
  pins.forEach((pin) => {
126
135
  component.pins.set(pin.id, pin);
127
136
  });
128
- component.arrangeProps = props.arrange ?? null;
129
137
  component.displayProp = props.display ?? null;
130
138
  component.widthProp = props.width ?? null;
131
139
  component.heightProp = props.height ?? null;
@@ -138,6 +146,33 @@ export class ExecutionContext {
138
146
  useAngle += 360;
139
147
  }
140
148
  }
149
+ if (props.display === null && props.arrange === null) {
150
+ const tmpArrangeLeft = [];
151
+ const tmpArrangeRight = [];
152
+ pins.forEach((pin, index) => {
153
+ const useArray = (index % 2 === 0) ? tmpArrangeLeft : tmpArrangeRight;
154
+ useArray.push(numeric(pin.id));
155
+ });
156
+ const arrangeProp = new Map([
157
+ [SymbolPinSide.Left, tmpArrangeLeft],
158
+ [SymbolPinSide.Right, tmpArrangeRight]
159
+ ]);
160
+ props.arrange = arrangeProp;
161
+ }
162
+ if (props.arrange !== null) {
163
+ component.arrangeProps = this.removeArrangePropDuplicates(props.arrange);
164
+ const arrangePropPins = this.getArrangePropPins(component.arrangeProps).map(item => {
165
+ return item.toNumber();
166
+ });
167
+ pins.forEach(pin => {
168
+ if (arrangePropPins.indexOf(pin.id) === -1) {
169
+ this.logWarning(`Pin ${pin.id} is not specified in arrange property`);
170
+ }
171
+ });
172
+ }
173
+ else {
174
+ component.arrangeProps = null;
175
+ }
141
176
  component.angleProp = useAngle ?? 0;
142
177
  component.followWireOrientationProp = props.followWireOrientation;
143
178
  const paramsMap = new Map();
@@ -181,6 +216,59 @@ export class ExecutionContext {
181
216
  this.log('add symbol', instanceName, '[' + pinsOutput.join(', ') + ']');
182
217
  return component;
183
218
  }
219
+ removeArrangePropDuplicates(arrangeProp) {
220
+ const sides = [
221
+ SymbolPinSide.Left,
222
+ SymbolPinSide.Right,
223
+ SymbolPinSide.Top,
224
+ SymbolPinSide.Bottom
225
+ ];
226
+ const result = new Map();
227
+ const seenIds = [];
228
+ sides.forEach(side => {
229
+ let items = arrangeProp.get(side) ?? [];
230
+ if (!Array.isArray(items)) {
231
+ items = [items];
232
+ }
233
+ const uniqueItems = [];
234
+ items.forEach(item => {
235
+ if (item instanceof NumericValue) {
236
+ if (seenIds.indexOf(item.toNumber()) === -1) {
237
+ seenIds.push(item.toNumber());
238
+ uniqueItems.push(item);
239
+ }
240
+ else {
241
+ this.logWarning(`Pin ${item.toNumber()} specified more than once in arrange property`);
242
+ }
243
+ }
244
+ else {
245
+ uniqueItems.push(item);
246
+ }
247
+ });
248
+ result.set(side, uniqueItems);
249
+ });
250
+ return result;
251
+ }
252
+ getArrangePropPins(arrangeProps) {
253
+ const pins = [];
254
+ const sides = [
255
+ SymbolPinSide.Left,
256
+ SymbolPinSide.Right,
257
+ SymbolPinSide.Top,
258
+ SymbolPinSide.Bottom
259
+ ];
260
+ sides.forEach(side => {
261
+ const items = arrangeProps.get(side);
262
+ if (items) {
263
+ items.forEach(item => {
264
+ if (item instanceof NumericValue) {
265
+ pins.push(item);
266
+ }
267
+ });
268
+ }
269
+ });
270
+ return pins;
271
+ }
184
272
  printPoint(extra = '') {
185
273
  let netString = NoNetText;
186
274
  if (this.scope.currentComponent === null || this.scope.currentPin === null) {
@@ -5,7 +5,7 @@ import { generateKiCADNetList, printTree } from "./export.js";
5
5
  import { LayoutEngine } from "./layout.js";
6
6
  import { parseFileWithVisitor } from "./parser.js";
7
7
  import { generatePdfOutput, generateSvgOutput, renderSheetsToSVG } from "./render.js";
8
- import { generateDebugSequenceAction, ParseError, ParseSyntaxError, RenderError, resolveToNumericValue, RuntimeExecutionError, sequenceActionString, SimpleStopwatch } from "./utils.js";
8
+ import { generateDebugSequenceAction, ParseError, ParseSyntaxError, printWarnings, RenderError, resolveToNumericValue, RuntimeExecutionError, sequenceActionString, SimpleStopwatch } from "./utils.js";
9
9
  import { ParserVisitor } from "./visitor.js";
10
10
  import { SymbolValidatorVisitor } from "./validate/SymbolValidatorVisitor.js";
11
11
  import { SymbolValidatorResolveVisitor } from "./validate/SymbolValidatorResolveVisitor.js";
@@ -185,6 +185,7 @@ export async function renderScript(scriptData, outputPath, options) {
185
185
  visitor.log('reading file');
186
186
  visitor.log('done reading file');
187
187
  const { tree, parser, parserTimeTaken, lexerTimeTaken } = await parseFileWithVisitor(visitor, scriptData);
188
+ printWarnings(visitor.getWarnings());
188
189
  showStats && console.log('Lexing took:', lexerTimeTaken);
189
190
  showStats && console.log('Parsing took:', parserTimeTaken);
190
191
  if (dumpNets) {
@@ -2,7 +2,7 @@ import graphlib, { Graph } from '@dagrejs/graphlib';
2
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';
@@ -1058,52 +1058,39 @@ function generateLayoutPinDefinition(component) {
1058
1058
  const pins = component.pins;
1059
1059
  const symbolPinDefinitions = [];
1060
1060
  const existingPinIds = Array.from(pins.keys());
1061
- if (component.arrangeProps === null) {
1062
- for (let i = 0; i < existingPinIds.length; i++) {
1063
- const pinPosition = Math.floor(i / 2);
1064
- const pin = pins.get(existingPinIds[i]);
1065
- symbolPinDefinitions.push({
1066
- side: (i % 2 === 0) ? SymbolPinSide.Left : SymbolPinSide.Right,
1067
- pinId: existingPinIds[i],
1068
- text: pin.name,
1069
- position: pinPosition,
1070
- pinType: pin.pinType,
1071
- });
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];
1072
1067
  }
1073
- }
1074
- else {
1075
- const addedPins = [];
1076
- for (const [key, items] of component.arrangeProps) {
1077
- let useItems;
1078
- if (!Array.isArray(items)) {
1079
- useItems = [items];
1080
- }
1081
- else {
1082
- useItems = [...items];
1083
- }
1084
- useItems.forEach(pinId => {
1085
- if (pinId instanceof NumericValue) {
1086
- const pinIdValue = pinId.toNumber();
1087
- if (existingPinIds.indexOf(pinIdValue) !== -1) {
1088
- const pin = pins.get(pinIdValue);
1089
- symbolPinDefinitions.push({
1090
- side: key,
1091
- pinId: pinIdValue,
1092
- text: pin.name,
1093
- position: pin.position,
1094
- pinType: pin.pinType,
1095
- });
1096
- addedPins.push(pinIdValue);
1097
- }
1098
- }
1099
- });
1068
+ else {
1069
+ useItems = [...items];
1100
1070
  }
1101
- const unplacedPins = existingPinIds.filter(pinId => {
1102
- 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
+ }
1103
1086
  });
1104
- if (unplacedPins.length > 0) {
1105
- throw "'arrange' property is defined, but not all pins are specified: " + unplacedPins.join(",");
1106
- }
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);
1107
1094
  }
1108
1095
  return symbolPinDefinitions;
1109
1096
  }
@@ -1461,7 +1448,9 @@ export function CalculatePinPositions(component) {
1461
1448
  tmpSymbol.refreshDrawing();
1462
1449
  const pins = component.pins;
1463
1450
  pins.forEach((value, key) => {
1464
- pinPositionMapping.set(key, tmpSymbol.pinPosition(key));
1451
+ if (component._unplacedPins.indexOf(key) === -1) {
1452
+ pinPositionMapping.set(key, tmpSymbol.pinPosition(key));
1453
+ }
1465
1454
  });
1466
1455
  return pinPositionMapping;
1467
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
  }
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
+ }