circuitscript 0.0.29 → 0.0.31

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 (60) hide show
  1. package/dist/cjs/BaseVisitor.js +6 -1
  2. package/dist/cjs/antlr/CircuitScriptLexer.js +204 -200
  3. package/dist/cjs/antlr/CircuitScriptParser.js +1066 -1173
  4. package/dist/cjs/draw_symbols.js +330 -87
  5. package/dist/cjs/execute.js +39 -14
  6. package/dist/cjs/geometry.js +79 -18
  7. package/dist/cjs/globals.js +36 -6
  8. package/dist/cjs/helpers.js +40 -2
  9. package/dist/cjs/layout.js +72 -39
  10. package/dist/cjs/main.js +10 -4
  11. package/dist/cjs/objects/ClassComponent.js +2 -0
  12. package/dist/cjs/objects/ExecutionScope.js +1 -1
  13. package/dist/cjs/objects/Net.js +3 -2
  14. package/dist/cjs/objects/PinTypes.js +7 -1
  15. package/dist/cjs/objects/types.js +11 -1
  16. package/dist/cjs/regenerate-tests.js +64 -3
  17. package/dist/cjs/render.js +20 -14
  18. package/dist/cjs/sizing.js +4 -6
  19. package/dist/cjs/utils.js +29 -5
  20. package/dist/cjs/visitor.js +176 -10
  21. package/dist/esm/BaseVisitor.mjs +6 -1
  22. package/dist/esm/antlr/CircuitScriptLexer.mjs +204 -200
  23. package/dist/esm/antlr/CircuitScriptParser.mjs +1061 -1171
  24. package/dist/esm/antlr/CircuitScriptVisitor.mjs +3 -0
  25. package/dist/esm/draw_symbols.mjs +324 -85
  26. package/dist/esm/execute.mjs +40 -15
  27. package/dist/esm/geometry.mjs +79 -17
  28. package/dist/esm/globals.mjs +35 -5
  29. package/dist/esm/helpers.mjs +38 -1
  30. package/dist/esm/layout.mjs +75 -42
  31. package/dist/esm/main.mjs +11 -5
  32. package/dist/esm/objects/ClassComponent.mjs +6 -0
  33. package/dist/esm/objects/ExecutionScope.mjs +1 -1
  34. package/dist/esm/objects/Net.mjs +3 -2
  35. package/dist/esm/objects/PinTypes.mjs +6 -0
  36. package/dist/esm/objects/types.mjs +14 -0
  37. package/dist/esm/regenerate-tests.mjs +64 -3
  38. package/dist/esm/render.mjs +21 -15
  39. package/dist/esm/sizing.mjs +3 -4
  40. package/dist/esm/utils.mjs +26 -4
  41. package/dist/esm/visitor.mjs +179 -13
  42. package/dist/types/antlr/CircuitScriptLexer.d.ts +42 -41
  43. package/dist/types/antlr/CircuitScriptParser.d.ts +144 -133
  44. package/dist/types/antlr/CircuitScriptVisitor.d.ts +6 -0
  45. package/dist/types/draw_symbols.d.ts +15 -2
  46. package/dist/types/execute.d.ts +5 -4
  47. package/dist/types/geometry.d.ts +4 -3
  48. package/dist/types/globals.d.ts +31 -3
  49. package/dist/types/helpers.d.ts +12 -0
  50. package/dist/types/layout.d.ts +2 -1
  51. package/dist/types/objects/ClassComponent.d.ts +8 -0
  52. package/dist/types/objects/PinTypes.d.ts +1 -0
  53. package/dist/types/objects/Wire.d.ts +4 -2
  54. package/dist/types/objects/types.d.ts +8 -0
  55. package/dist/types/sizing.d.ts +0 -4
  56. package/dist/types/utils.d.ts +3 -0
  57. package/dist/types/visitor.d.ts +8 -1
  58. package/fonts/Arial.ttf +0 -0
  59. package/libs/lib.cst +58 -41
  60. package/package.json +1 -1
package/dist/esm/main.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  import { program } from 'commander';
3
3
  import figlet from 'figlet';
4
4
  import path from 'path';
5
- import { readFileSync, watch } from 'fs';
5
+ import { readFileSync, watch, existsSync } from 'fs';
6
6
  import { prepareSVGEnvironment } from './sizing.mjs';
7
7
  import { getDefaultLibsPath, getFontsPath, getPackageVersion, renderScript } from './helpers.mjs';
8
8
  export default async function main() {
@@ -47,16 +47,22 @@ export default async function main() {
47
47
  let scriptData;
48
48
  if (args.length > 0 && args[0]) {
49
49
  inputFilePath = args[0];
50
- scriptData = readFileSync(inputFilePath, { encoding: 'utf-8' });
51
- if (currentDirectory === null) {
52
- currentDirectory = path.dirname(inputFilePath);
50
+ if (existsSync(inputFilePath)) {
51
+ scriptData = readFileSync(inputFilePath, { encoding: 'utf-8' });
52
+ if (currentDirectory === null) {
53
+ currentDirectory = path.dirname(inputFilePath);
54
+ }
55
+ }
56
+ else {
57
+ console.error("Error: File could not be found");
58
+ return;
53
59
  }
54
60
  }
55
61
  else if (options.input) {
56
62
  scriptData = options.input;
57
63
  }
58
64
  else {
59
- console.log("Error: No input provided");
65
+ console.error("Error: No input provided");
60
66
  return;
61
67
  }
62
68
  const scriptOptions = {
@@ -22,8 +22,14 @@ export class ClassComponent {
22
22
  followWireOrientationProp = true;
23
23
  wireOrientationAngle = 0;
24
24
  useWireOrientationAngle = true;
25
+ didSetWireOrientationAngle = false;
25
26
  styles = {};
26
27
  assignedRefDes = null;
28
+ moduleContainsExpressions;
29
+ moduleCounter = 0;
30
+ moduleExecutionContext;
31
+ moduleExecutionContextName;
32
+ modulePinIdToPortMap;
27
33
  constructor(instanceName, numPins, className) {
28
34
  this.instanceName = instanceName;
29
35
  this.numPins = numPins;
@@ -89,7 +89,7 @@ export class ExecutionScope {
89
89
  }
90
90
  });
91
91
  return sortedNet.map(([component, pin, net]) => {
92
- return [net.namespace + net.name, component.instanceName, pin];
92
+ return [net.toString(), component.instanceName, pin];
93
93
  });
94
94
  }
95
95
  printNets() {
@@ -18,10 +18,11 @@ export class Net {
18
18
  this.baseName = name;
19
19
  }
20
20
  toString() {
21
- return this.name;
21
+ return this.namespace + this.name;
22
22
  }
23
23
  static isSame(netA, netB) {
24
- return netA.name === netB.name &&
24
+ return netA.namespace === netB.namespace &&
25
+ netA.name === netB.name &&
25
26
  netA.baseName === netB.baseName &&
26
27
  netA.priority === netB.priority &&
27
28
  netA.type === netB.type;
@@ -6,3 +6,9 @@ export var PinTypes;
6
6
  PinTypes["IO"] = "io";
7
7
  PinTypes["Power"] = "power";
8
8
  })(PinTypes || (PinTypes = {}));
9
+ export const AllPinTypes = [
10
+ PinTypes.Any,
11
+ PinTypes.Input,
12
+ PinTypes.Output,
13
+ PinTypes.IO,
14
+ ];
@@ -4,6 +4,20 @@ export class UndeclaredReference {
4
4
  this.reference = reference;
5
5
  }
6
6
  }
7
+ export class DeclaredReference {
8
+ found;
9
+ name;
10
+ trailers;
11
+ type;
12
+ value;
13
+ constructor(refType) {
14
+ this.found = refType.found;
15
+ this.name = refType.name;
16
+ this.trailers = refType.trailers;
17
+ this.type = refType.type;
18
+ this.value = refType.value;
19
+ }
20
+ }
7
21
  export var ParseSymbolType;
8
22
  (function (ParseSymbolType) {
9
23
  ParseSymbolType["Variable"] = "variable";
@@ -4,7 +4,7 @@ import { prepareSVGEnvironment } from './sizing.mjs';
4
4
  const mainDir = './__tests__/renderData/';
5
5
  const fontsPath = getFontsPath();
6
6
  const defaultLibsPath = getDefaultLibsPath();
7
- async function regenerateTests() {
7
+ async function regenerateTests(extra = "") {
8
8
  await prepareSVGEnvironment(fontsPath);
9
9
  const cstFiles = [];
10
10
  const files = fs.readdirSync(mainDir);
@@ -16,12 +16,73 @@ async function regenerateTests() {
16
16
  cstFiles.forEach(file => {
17
17
  const inputPath = mainDir + file;
18
18
  const scriptData = fs.readFileSync(inputPath, { encoding: 'utf-8' });
19
- const outputPath = inputPath + '.svg';
19
+ const outputPath = mainDir + 'svgs/' + file + extra + '.svg';
20
20
  renderScript(scriptData, outputPath, {
21
21
  currentDirectory: mainDir,
22
22
  defaultLibsPath,
23
23
  });
24
24
  console.log('generated ', outputPath);
25
25
  });
26
+ return cstFiles;
26
27
  }
27
- regenerateTests();
28
+ (async () => {
29
+ const generateDiff = false;
30
+ const nextExtra = generateDiff ? '.next' : '';
31
+ const cstFiles = await regenerateTests(nextExtra);
32
+ const allFiles = [];
33
+ if (generateDiff) {
34
+ cstFiles.forEach(file => {
35
+ const svg1 = 'svgs/' + file + '.svg';
36
+ const svg2 = 'svgs/' + file + '.next.svg';
37
+ const cleanedName = file.replace('script', '').replace('.cst', '');
38
+ allFiles.push([file, svg1, svg2, cleanedName]);
39
+ });
40
+ const sortedFiles = allFiles.sort((a, b) => {
41
+ const nameA = a[3];
42
+ const nameB = b[3];
43
+ const indexA = Number(nameA);
44
+ const indexB = Number(nameB);
45
+ if (indexA > indexB) {
46
+ return 1;
47
+ }
48
+ else if (indexA < indexB) {
49
+ return -1;
50
+ }
51
+ else {
52
+ return 0;
53
+ }
54
+ });
55
+ const output = [];
56
+ sortedFiles.forEach(group => {
57
+ const [file, svg1, svg2] = group;
58
+ output.push(`<div class='group'>
59
+ <h3>${file}</h3>
60
+ <div class='items'>
61
+ <div style='margin-right: 10px'>
62
+ <h4>${svg1}</h4>
63
+ <img src='${svg1}'/>
64
+ </div>
65
+ <div>
66
+ <h4>${svg2}</h4>
67
+ <img src='${svg2}'/>
68
+ </div>
69
+ </div>
70
+ </div>`);
71
+ });
72
+ const result = `
73
+ <html>
74
+ <header>
75
+ <title>SVG compare</title>
76
+ <style>
77
+ img { border: solid thin #ccc; }
78
+ .items { display: flex; }
79
+ body { font-family: Arial; }
80
+ </style>
81
+ </header>
82
+ <body>
83
+ ${output.join('')}
84
+ </body>
85
+ </html>`;
86
+ fs.writeFileSync(mainDir + "compiled.html", result);
87
+ }
88
+ })();
@@ -1,9 +1,10 @@
1
1
  import { SVG, registerWindow } from '@svgdotjs/svg.js';
2
2
  import { RenderFrameType, getBounds } from "./layout.mjs";
3
3
  import { applyFontsToSVG, getCreateSVGWindow } from './sizing.mjs';
4
- import { ColorScheme, ComponentTypes, ParamKeys, junctionSize } from './globals.mjs';
4
+ import { ColorScheme, ComponentTypes, MMToPx, ParamKeys, defaultGridSizeUnits, defaultWireLineWidth, defaultZoomScale, junctionSize } from './globals.mjs';
5
5
  import { NumericValue } from './objects/ParamDefinition.mjs';
6
6
  import { getBoundsSize } from './utils.mjs';
7
+ import { milsToMM } from './helpers.mjs';
7
8
  export function generateSVG2(graph) {
8
9
  const window = getCreateSVGWindow()();
9
10
  const document = window.document;
@@ -12,10 +13,10 @@ export function generateSVG2(graph) {
12
13
  applyFontsToSVG(canvas);
13
14
  generateSVGChild(canvas, graph.components, graph.wires, graph.junctions, graph.mergedWires, graph.frameObjects, graph.textObjects);
14
15
  const { x, y, width, height } = canvas.bbox();
15
- const margin = 5;
16
+ const margin = milsToMM(10);
16
17
  const widthAndMargin = width + margin * 2;
17
18
  const heightAndMargin = height + margin * 2;
18
- const scale = 1;
19
+ const scale = MMToPx * defaultZoomScale;
19
20
  canvas.size(widthAndMargin * scale, heightAndMargin * scale);
20
21
  canvas.viewbox(x - margin, y - margin, widthAndMargin, heightAndMargin);
21
22
  return canvas.svg();
@@ -79,8 +80,11 @@ function generateSVGChild(canvas, components, wires, junctions, mergedWires, fra
79
80
  const pt1 = segment[0];
80
81
  const pt2 = segment[1];
81
82
  mergedWireGroup.line([pt1, pt2])
82
- .stroke({ width: 1, color: ColorScheme.WireColor,
83
- linecap: 'square' })
83
+ .stroke({
84
+ width: defaultWireLineWidth,
85
+ color: ColorScheme.WireColor,
86
+ linecap: 'square'
87
+ })
84
88
  .fill('none');
85
89
  });
86
90
  intersectPoints.forEach(point => {
@@ -109,7 +113,7 @@ function generateSVGChild(canvas, components, wires, junctions, mergedWires, fra
109
113
  }
110
114
  const tmpRect = frameGroup.rect(width, height)
111
115
  .fill('none')
112
- .stroke({ width: borderWidth, color: strokeColor });
116
+ .stroke({ width: milsToMM(borderWidth), color: strokeColor });
113
117
  tmpRect.translate(item.x, item.y);
114
118
  }
115
119
  });
@@ -120,13 +124,14 @@ function generateSVGChild(canvas, components, wires, junctions, mergedWires, fra
120
124
  symbol.draw(innerGroup);
121
125
  });
122
126
  const drawOrigin = false;
127
+ const originSize = milsToMM(10);
123
128
  drawOrigin && canvas.group().translate(0, 0)
124
- .circle(5)
125
- .translate(-5 / 2, -5 / 2)
129
+ .circle(originSize)
130
+ .translate(-originSize / 2, -originSize / 2)
126
131
  .stroke('none').fill('red');
127
132
  }
128
133
  function drawGrid(group, canvasSize) {
129
- const gridSize = 20;
134
+ const gridSize = defaultGridSizeUnits;
130
135
  const { x, y, x2, y2 } = canvasSize;
131
136
  const gridStartX = (Math.floor(x / gridSize) - 1) * gridSize;
132
137
  const gridStartY = (Math.floor(y / gridSize) - 1) * gridSize;
@@ -134,19 +139,20 @@ function drawGrid(group, canvasSize) {
134
139
  const gridEndY = (Math.ceil(y2 / gridSize) + 1) * gridSize;
135
140
  const numCols = Math.ceil((gridEndX - gridStartX) / gridSize);
136
141
  const lines = [];
142
+ const smallOffset = milsToMM(3);
143
+ const startY = gridStartY - smallOffset / 2;
144
+ const endY = gridEndY + smallOffset;
137
145
  for (let i = 0; i <= numCols; i++) {
138
146
  const startX = gridStartX + i * gridSize;
139
- const startY = gridStartY - 0.5;
140
- const endY = gridEndY;
141
147
  lines.push(`M ${startX} ${startY} L ${startX} ${endY}`);
142
148
  }
149
+ const strokeSize = milsToMM(3);
143
150
  group.path(lines.join(" "))
144
- .fill('none')
145
151
  .attr({
146
- 'stroke-dasharray': '1,' + (gridSize - 1),
152
+ 'stroke-dasharray': `${strokeSize},${gridSize - strokeSize}`,
147
153
  })
148
154
  .stroke({
149
- width: 1,
150
- color: '#aaa'
155
+ width: strokeSize,
156
+ color: '#000'
151
157
  });
152
158
  }
@@ -3,7 +3,9 @@ import { HorizontalAlign, VerticalAlign } from './geometry.mjs';
3
3
  import { defaultFont } from './globals.mjs';
4
4
  import { JSModuleType, detectJSModuleType } from './helpers.mjs';
5
5
  let MainCanvas = null;
6
- const supportedFonts = {};
6
+ const supportedFonts = {
7
+ 'Arial': 'Arial.ttf',
8
+ };
7
9
  let globalCreateSVGWindow;
8
10
  export async function prepareSVGEnvironment(fontsPath) {
9
11
  const moduleType = detectJSModuleType();
@@ -25,9 +27,6 @@ export function getCreateSVGWindow() {
25
27
  }
26
28
  export function applyFontsToSVG(canvas) {
27
29
  }
28
- export async function measureTextSize(text, fontFamily, fontSize) {
29
- return measureTextSize2(text, fontFamily, fontSize);
30
- }
31
30
  const measureTextSizeCache = {};
32
31
  const measureTextSizeCacheHits = {};
33
32
  export function measureTextSize2(text, fontFamily, fontSize, fontWeight = 'regular', anchor = HorizontalAlign.Left, vanchor = VerticalAlign.Bottom) {
@@ -23,11 +23,17 @@ export function printBounds(bounds) {
23
23
  return 'null';
24
24
  }
25
25
  }
26
+ function hasRemainder(value, value2) {
27
+ const tmpValue = Math.abs(value) / value2;
28
+ const flooredValue = Math.floor(tmpValue);
29
+ const diff = tmpValue - flooredValue;
30
+ return diff;
31
+ }
26
32
  export function resizeToNearestGrid(bounds, gridSize = 20) {
27
- const addXMin = (bounds.xmin % gridSize) === 0 ? -1 : 0;
28
- const addYMin = (bounds.ymin % gridSize) === 0 ? -1 : 0;
29
- const addXMax = (bounds.xmax % gridSize) === 0 ? 1 : 0;
30
- const addYMax = (bounds.ymax % gridSize) === 0 ? 1 : 0;
33
+ const addXMin = hasRemainder(bounds.xmin, gridSize) === 0 ? -1 : 0;
34
+ const addYMin = hasRemainder(bounds.ymin, gridSize) === 0 ? -1 : 0;
35
+ const addXMax = hasRemainder(bounds.xmax, gridSize) === 0 ? 1 : 0;
36
+ const addYMax = hasRemainder(bounds.ymax, gridSize) === 0 ? 1 : 0;
31
37
  return {
32
38
  xmin: Math.floor((bounds.xmin + addXMin) / gridSize) * gridSize,
33
39
  ymin: Math.floor((bounds.ymin + addYMin) / gridSize) * gridSize,
@@ -44,3 +50,19 @@ export function getBoundsSize(bounds) {
44
50
  height: bounds.ymax - bounds.ymin,
45
51
  };
46
52
  }
53
+ export function getPortType(component) {
54
+ const drawingCommands = component.displayProp;
55
+ let foundPinType = null;
56
+ const commands = drawingCommands.getCommands();
57
+ commands.some(item => {
58
+ if (item[0] === 'label' && item[2].has('portType')) {
59
+ foundPinType = item[2].get('portType');
60
+ return true;
61
+ }
62
+ return false;
63
+ });
64
+ return foundPinType;
65
+ }
66
+ export function roundValue(value) {
67
+ return +value.toFixed(7);
68
+ }
@@ -1,11 +1,13 @@
1
1
  import { ClassComponent } from './objects/ClassComponent.mjs';
2
- import { NumericValue, ParamDefinition, } from './objects/ParamDefinition.mjs';
2
+ import { NumericValue, ParamDefinition, PinBlankValue, } from './objects/ParamDefinition.mjs';
3
3
  import { PinDefinition, PinIdType } from './objects/PinDefinition.mjs';
4
4
  import { PinTypes } from './objects/PinTypes.mjs';
5
- import { UndeclaredReference } from './objects/types.mjs';
6
- import { BlockTypes, ComponentTypes, NoNetText, ReferenceTypes } from './globals.mjs';
5
+ import { DeclaredReference, UndeclaredReference } from './objects/types.mjs';
6
+ import { BlockTypes, ComponentTypes, NoNetText, ReferenceTypes, WireAutoDirection } from './globals.mjs';
7
7
  import { PlaceHolderCommands, SymbolDrawingCommands } from './draw_symbols.mjs';
8
8
  import { BaseVisitor } from './BaseVisitor.mjs';
9
+ import { getPortType } from './utils.mjs';
10
+ import { UnitDimension } from './helpers.mjs';
9
11
  export class ParserVisitor extends BaseVisitor {
10
12
  visitKeyword_assignment_expr = (ctx) => {
11
13
  const id = ctx.ID().getText();
@@ -145,7 +147,8 @@ export class ParserVisitor extends BaseVisitor {
145
147
  arrange, display, type, width, copy,
146
148
  angle, followWireOrientation
147
149
  };
148
- this.setResult(ctx, this.getExecutor().createComponent(instanceName, pins, params, props));
150
+ const createdComponent = this.getExecutor().createComponent(instanceName, pins, params, props);
151
+ this.setResult(ctx, createdComponent);
149
152
  };
150
153
  visitCreate_graphic_expr = (ctx) => {
151
154
  const commands = ctx.graphic_expr().reduce((accum, item) => {
@@ -191,8 +194,82 @@ export class ParserVisitor extends BaseVisitor {
191
194
  this.visit(ctxParameters);
192
195
  parameters = this.getResult(ctxParameters);
193
196
  }
197
+ if (commandName === 'label') {
198
+ parameters.forEach(item => {
199
+ if (item[0] == 'keyword' && item[1] === 'portType') {
200
+ if (item[2] === 'in') {
201
+ item[2] = 'input';
202
+ }
203
+ else if (item[2] === 'out') {
204
+ item[2] = 'output';
205
+ }
206
+ }
207
+ });
208
+ }
194
209
  this.setResult(ctx, [commandName, parameters]);
195
210
  };
211
+ visitCreate_module_expr = (ctx) => {
212
+ const properties = this.getPropertyExprList(ctx.property_expr());
213
+ const { left: leftPorts, right: rightPorts } = this.parseCreateModulePorts(properties.get('ports'));
214
+ const allPorts = [...leftPorts, ...rightPorts].filter(item => {
215
+ return !(item instanceof PinBlankValue);
216
+ });
217
+ const nameToPinId = new Map();
218
+ const tmpPorts = allPorts.map((portName, index) => {
219
+ nameToPinId.set(portName, index + 1);
220
+ return new PinDefinition(index + 1, PinIdType.Int, portName, PinTypes.Any);
221
+ });
222
+ const arrangeLeftItems = leftPorts.map(item => {
223
+ if (item instanceof PinBlankValue) {
224
+ return item;
225
+ }
226
+ else {
227
+ return nameToPinId.get(item);
228
+ }
229
+ });
230
+ const arrangeRightItems = rightPorts.map(item => {
231
+ if (item instanceof PinBlankValue) {
232
+ return item;
233
+ }
234
+ else {
235
+ return nameToPinId.get(item);
236
+ }
237
+ });
238
+ const arrange = new Map();
239
+ if (arrangeLeftItems.length > 0) {
240
+ arrange.set('left', arrangeLeftItems);
241
+ }
242
+ if (arrangeRightItems.length > 0) {
243
+ arrange.set('right', arrangeRightItems);
244
+ }
245
+ const width = properties.has('width') ?
246
+ properties.get('width') : null;
247
+ const blankParams = [];
248
+ const props = {
249
+ arrange, width
250
+ };
251
+ const moduleInstanceName = this.getExecutor().getUniqueInstanceName('');
252
+ const createdComponent = this.getExecutor().createComponent(moduleInstanceName, tmpPorts, blankParams, props);
253
+ createdComponent.typeProp = 'module';
254
+ const ctxPropertyBlock = ctx.property_block_expr();
255
+ if (ctxPropertyBlock) {
256
+ const [firstBlock] = ctxPropertyBlock;
257
+ this.visit(firstBlock);
258
+ const [keyName, expressionsBlock] = this.getResult(firstBlock);
259
+ if (keyName === 'contains') {
260
+ createdComponent.moduleContainsExpressions = expressionsBlock;
261
+ this.expandModuleContains(createdComponent, this.getExecutor().netNamespace);
262
+ }
263
+ }
264
+ this.setResult(ctx, createdComponent);
265
+ };
266
+ visitProperty_block_expr = (ctx) => {
267
+ const tmpCtx = ctx.property_key_expr();
268
+ this.visit(tmpCtx);
269
+ const keyName = this.getResult(tmpCtx);
270
+ const expressionsBlock = ctx.expressions_block();
271
+ this.setResult(ctx, [keyName, expressionsBlock]);
272
+ };
196
273
  visitProperty_expr = (ctx) => {
197
274
  const ctxPropertyKeyExpr = ctx.property_key_expr();
198
275
  const ctxPropertyValueExpr = ctx.property_value_expr();
@@ -270,6 +347,14 @@ export class ParserVisitor extends BaseVisitor {
270
347
  && component.copyProp) {
271
348
  component = this.getExecutor().copyComponent(component);
272
349
  }
350
+ if (component instanceof DeclaredReference
351
+ && component.found
352
+ && component.trailers
353
+ && component.trailers.length > 0
354
+ && component.trailers[0] === 'contains') {
355
+ component = component.value;
356
+ this.placeModuleContains(component);
357
+ }
273
358
  if (component && component instanceof ClassComponent) {
274
359
  const modifiers = ctx.component_modifier_expr();
275
360
  modifiers.forEach(modifier => {
@@ -328,10 +413,65 @@ export class ParserVisitor extends BaseVisitor {
328
413
  pinValue = this.getResult(ctxPinSelectExpr);
329
414
  }
330
415
  else {
331
- pinValue = component.getDefaultPin();
416
+ if (component instanceof ClassComponent) {
417
+ pinValue = component.getDefaultPin();
418
+ }
419
+ else {
420
+ const undeclaredRef = component;
421
+ throw 'Invalid component: ' + undeclaredRef.reference.name;
422
+ }
332
423
  }
333
424
  this.setResult(ctx, [component, pinValue]);
334
425
  };
426
+ expandModuleContains(component, netNamespace) {
427
+ this.getExecutor().log('expanding module `contains`');
428
+ const executionStack = this.executionStack;
429
+ const resolveNet = this.createNetResolver(executionStack);
430
+ const executionContextName = this.getExecutor().namespace + "_"
431
+ + component.instanceName
432
+ + '_' + component.moduleCounter;
433
+ const tmpNamespace = this.getNetNamespace(netNamespace, "+/" + component.instanceName + "_" + component.moduleCounter);
434
+ const newExecutor = this.enterNewChildContext(executionStack, this.getExecutor(), executionContextName, { netNamespace: tmpNamespace }, [], []);
435
+ component.moduleCounter += 1;
436
+ newExecutor.resolveNet = resolveNet;
437
+ this.visit(component.moduleContainsExpressions);
438
+ const executionContext = executionStack.pop();
439
+ component.moduleExecutionContext = executionContext;
440
+ component.moduleExecutionContextName = executionContextName;
441
+ this.linkModuleSymbolWithContains(component, executionContext);
442
+ }
443
+ linkModuleSymbolWithContains(moduleComponent, executionContext) {
444
+ this.log('link module symbol');
445
+ const modulePinMapping = new Map();
446
+ moduleComponent.pins.forEach(pin => {
447
+ const pinName = pin.name;
448
+ modulePinMapping.set(pinName, pin.id);
449
+ });
450
+ const pinIdToPortMap = new Map();
451
+ moduleComponent.modulePinIdToPortMap = pinIdToPortMap;
452
+ for (const [key, component] of executionContext.scope.instances) {
453
+ if (component._copyID !== null && component.typeProp === 'port') {
454
+ const portName = component.parameters.get('net_name');
455
+ const modulePinId = modulePinMapping.get(portName);
456
+ pinIdToPortMap.set(modulePinId, component);
457
+ const portType = getPortType(component);
458
+ const tmpPin = moduleComponent.pins.get(modulePinId);
459
+ tmpPin.pinType = portType;
460
+ }
461
+ }
462
+ }
463
+ placeModuleContains(moduleComponent) {
464
+ if (moduleComponent.typeProp === 'module'
465
+ && moduleComponent.moduleContainsExpressions) {
466
+ this.log('place module `contains`');
467
+ this.getExecutor().mergeScope(moduleComponent.moduleExecutionContext.scope, moduleComponent.moduleExecutionContextName);
468
+ this.log('connect module ports');
469
+ for (const [pinId, portComponent] of moduleComponent.modulePinIdToPortMap) {
470
+ this.getExecutor().atComponent(moduleComponent, pinId);
471
+ this.getExecutor().toComponent(portComponent, 1);
472
+ }
473
+ }
474
+ }
335
475
  visitUnaryOperatorExpr = (ctx) => {
336
476
  this.visit(ctx.data_expr());
337
477
  let value = this.getResult(ctx.data_expr());
@@ -360,6 +500,7 @@ export class ParserVisitor extends BaseVisitor {
360
500
  let value;
361
501
  const ctxCreateComponentExpr = ctx.create_component_expr();
362
502
  const ctxCreateGraphicExpr = ctx.create_graphic_expr();
503
+ const ctxCreateModuleExpr = ctx.create_module_expr();
363
504
  if (ctxCreateComponentExpr) {
364
505
  this.visit(ctxCreateComponentExpr);
365
506
  value = this.getResult(ctxCreateComponentExpr);
@@ -368,6 +509,10 @@ export class ParserVisitor extends BaseVisitor {
368
509
  this.visit(ctxCreateGraphicExpr);
369
510
  value = this.getResult(ctxCreateGraphicExpr);
370
511
  }
512
+ else if (ctxCreateModuleExpr) {
513
+ this.visit(ctxCreateModuleExpr);
514
+ value = this.getResult(ctxCreateModuleExpr);
515
+ }
371
516
  else {
372
517
  throw "Invalid data expression";
373
518
  }
@@ -536,13 +681,11 @@ export class ParserVisitor extends BaseVisitor {
536
681
  }
537
682
  };
538
683
  visitAt_block_pin_expression_complex = (ctx) => {
539
- ctx.expression().forEach(item => {
540
- this.visit(item);
541
- });
684
+ this.visit(ctx.expressions_block());
542
685
  };
543
686
  visitWire_expr_direction_only = (ctx) => {
544
687
  const value = ctx.ID().getText();
545
- if (value === 'auto' || value === 'auto_') {
688
+ if (value === WireAutoDirection.Auto || value === WireAutoDirection.Auto_) {
546
689
  this.setResult(ctx, [value]);
547
690
  }
548
691
  else {
@@ -563,7 +706,7 @@ export class ParserVisitor extends BaseVisitor {
563
706
  useValue = this.getResult(ctxDataExpr);
564
707
  }
565
708
  if (useValue !== null) {
566
- this.setResult(ctx, [direction, useValue]);
709
+ this.setResult(ctx, [direction, new UnitDimension(useValue)]);
567
710
  return;
568
711
  }
569
712
  }
@@ -597,9 +740,12 @@ export class ParserVisitor extends BaseVisitor {
597
740
  const propertyName = ctx.ID().getText();
598
741
  this.getExecutor().setProperty('..' + propertyName, result);
599
742
  };
743
+ visitExpressions_block = (ctx) => {
744
+ this.runExpressions(this.getExecutor(), ctx.expression());
745
+ };
600
746
  visitFrame_expr = (ctx) => {
601
747
  const frameId = this.getExecutor().enterFrame();
602
- this.runExpressions(this.getExecutor(), ctx.expression());
748
+ this.visit(ctx.expressions_block());
603
749
  this.getExecutor().exitFrame(frameId);
604
750
  };
605
751
  visitNet_namespace_expr = (ctx) => {
@@ -630,7 +776,7 @@ export class ParserVisitor extends BaseVisitor {
630
776
  this.visit(ctxDataExpr);
631
777
  const result = this.getResult(ctxDataExpr);
632
778
  if (result) {
633
- this.runExpressions(this.getExecutor(), ctx.expression());
779
+ this.visit(ctx.expressions_block());
634
780
  }
635
781
  else {
636
782
  const ctxInnerIfExprs = ctx.if_inner_expr();
@@ -657,7 +803,7 @@ export class ParserVisitor extends BaseVisitor {
657
803
  this.visit(ctxDataExpr);
658
804
  const result = this.getResult(ctxDataExpr);
659
805
  if (result) {
660
- this.runExpressions(this.getExecutor(), ctx.expression());
806
+ this.visit(ctx.expressions_block());
661
807
  }
662
808
  this.setResult(ctx, result);
663
809
  };
@@ -712,6 +858,26 @@ export class ParserVisitor extends BaseVisitor {
712
858
  }
713
859
  return pins;
714
860
  }
861
+ parseCreateModulePorts(portsDefinition) {
862
+ let leftItems = [];
863
+ let rightItems = [];
864
+ if (portsDefinition.has('left')) {
865
+ leftItems = portsDefinition.get('left');
866
+ if (!Array.isArray(leftItems)) {
867
+ leftItems = [leftItems];
868
+ }
869
+ }
870
+ if (portsDefinition.has('right')) {
871
+ rightItems = portsDefinition.get('right');
872
+ if (!Array.isArray(rightItems)) {
873
+ rightItems = [rightItems];
874
+ }
875
+ }
876
+ return {
877
+ left: leftItems,
878
+ right: rightItems
879
+ };
880
+ }
715
881
  parseCreateComponentParams(params) {
716
882
  const result = [];
717
883
  if (params) {