circuitscript 0.0.7 → 0.0.12
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.
- package/.gitlab-ci.yml +22 -17
- package/README.md +1 -1
- package/__tests__/helpers.ts +18 -16
- package/__tests__/testMathOps.ts +1 -1
- package/__tests__/testRender.ts +8 -8
- package/build/src/main.js +23 -14
- package/build/src/sizing.js +2 -2
- package/build/src/visitor.js +34 -15
- package/jest.config.js +23 -0
- package/libs/lib.cst +190 -0
- package/package.json +2 -22
- package/src/antlr/CircuitScriptLexer.ts +2 -0
- package/src/antlr/CircuitScriptParser.ts +2 -0
- package/src/antlr/CircuitScriptVisitor.ts +2 -0
- package/src/main.ts +25 -14
- package/src/sizing.ts +2 -2
- package/src/visitor.ts +45 -16
- package/tsconfig.json +4 -1
package/.gitlab-ci.yml
CHANGED
|
@@ -10,7 +10,8 @@ variables:
|
|
|
10
10
|
|
|
11
11
|
stages:
|
|
12
12
|
- prepare
|
|
13
|
-
|
|
13
|
+
- test_jest
|
|
14
|
+
- test_build
|
|
14
15
|
- publish
|
|
15
16
|
|
|
16
17
|
# Installs all dependencies.
|
|
@@ -26,27 +27,32 @@ install:
|
|
|
26
27
|
when: on_success
|
|
27
28
|
expire_in: 12h
|
|
28
29
|
script:
|
|
29
|
-
- npm config set -- '//registry.npmjs.org/:_authToken' "${NPM_AUTH_TOKEN}"
|
|
30
30
|
- npm install --no-progress --no-audit --no-fund --verbose
|
|
31
|
-
- npm run build:release || true
|
|
32
31
|
# only:
|
|
33
32
|
# - merge_requests
|
|
34
33
|
# - develop
|
|
35
34
|
# - main
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
36
|
+
test_jest:
|
|
37
|
+
stage: test_jest
|
|
38
|
+
interruptible: true
|
|
39
|
+
retry: 1
|
|
40
|
+
dependencies:
|
|
41
|
+
- install
|
|
42
|
+
script:
|
|
43
|
+
- npm run test
|
|
44
|
+
|
|
45
|
+
test_build:
|
|
46
|
+
stage: test_build
|
|
47
|
+
interruptible: true
|
|
48
|
+
retry: 1
|
|
49
|
+
dependencies:
|
|
50
|
+
- install
|
|
51
|
+
script:
|
|
52
|
+
- npm run build:release || true
|
|
53
|
+
- ls
|
|
54
|
+
- ls build
|
|
55
|
+
- node build/src/main.js
|
|
50
56
|
|
|
51
57
|
# Publishes the package to npm.
|
|
52
58
|
publish:
|
|
@@ -57,7 +63,6 @@ publish:
|
|
|
57
63
|
script:
|
|
58
64
|
- npm config set -- '//registry.npmjs.org/:_authToken' "${NPM_AUTH_TOKEN}"
|
|
59
65
|
- npm run build:release || true
|
|
60
|
-
- ls build/src
|
|
61
66
|
- npm publish --verbose
|
|
62
67
|
resource_group: 'deployment-$CI_COMMIT_REF_SLUG'
|
|
63
68
|
# rules:
|
package/README.md
CHANGED
package/__tests__/helpers.ts
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import
|
|
2
|
-
import fs from 'fs';
|
|
1
|
+
import { dirname } from 'path';
|
|
3
2
|
|
|
4
|
-
import CircuitScriptParser from '../src/antlr/CircuitScriptParser';
|
|
3
|
+
import CircuitScriptParser from '../src/antlr/CircuitScriptParser.js';
|
|
5
4
|
|
|
6
5
|
import { CharStream, CommonTokenStream } from 'antlr4';
|
|
7
|
-
import { MainVisitor } from '../src/visitor';
|
|
8
|
-
import { ComponentPinNet } from '../src/objects/types';
|
|
9
|
-
import { CircuitscriptParserErrorListener
|
|
10
|
-
import { ClassComponent } from '../src/objects/ClassComponent';
|
|
11
|
-
import { MainLexer } from '../src/lexer';
|
|
6
|
+
import { MainVisitor } from '../src/visitor.js';
|
|
7
|
+
import { ComponentPinNet } from '../src/objects/types.js';
|
|
8
|
+
import { CircuitscriptParserErrorListener } from '../src/parser.js';
|
|
9
|
+
import { ClassComponent } from '../src/objects/ClassComponent.js';
|
|
10
|
+
import { MainLexer } from '../src/lexer.js';
|
|
12
11
|
|
|
13
12
|
|
|
14
|
-
export async function runScript(script: string): Promise<{
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
export async function runScript(script: string): Promise<{
|
|
14
|
+
visitor: MainVisitor,
|
|
15
|
+
hasError: boolean,
|
|
16
|
+
componentPinNets: ComponentPinNet[]
|
|
17
|
+
}> {
|
|
18
|
+
|
|
18
19
|
const chars = new CharStream(script);
|
|
19
20
|
const lexer = new MainLexer(chars);
|
|
20
21
|
const tokens = new CommonTokenStream(lexer);
|
|
@@ -25,21 +26,22 @@ export async function runScript(script: string): Promise<{visitor: MainVisitor,
|
|
|
25
26
|
|
|
26
27
|
const errorListener = new CircuitscriptParserErrorListener();
|
|
27
28
|
parser.addErrorListener(errorListener);
|
|
28
|
-
|
|
29
|
+
|
|
29
30
|
const tree = parser.script();
|
|
30
31
|
|
|
31
32
|
const scriptPath = "./examples/helpers.ts";
|
|
33
|
+
const defaultLibsPath = "./libs";
|
|
32
34
|
|
|
33
35
|
const visitor = new MainVisitor(true);
|
|
34
36
|
visitor.printToConsole = false; // do not clutter the console log
|
|
35
37
|
|
|
36
|
-
const currentDirectory =
|
|
37
|
-
visitor.onImportFile = visitor.createImportFileHandler(currentDirectory);
|
|
38
|
+
const currentDirectory = dirname(scriptPath);
|
|
39
|
+
visitor.onImportFile = visitor.createImportFileHandler(currentDirectory, defaultLibsPath);
|
|
38
40
|
|
|
39
41
|
let hasError = false;
|
|
40
42
|
try {
|
|
41
43
|
visitor.visit(tree);
|
|
42
|
-
} catch (err){
|
|
44
|
+
} catch (err) {
|
|
43
45
|
// Error should be internally handled in visitor
|
|
44
46
|
err.print(script);
|
|
45
47
|
hasError = true;
|
package/__tests__/testMathOps.ts
CHANGED
package/__tests__/testRender.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
2
|
|
|
3
|
-
import { LayoutEngine } from "../src/layout";
|
|
4
|
-
import { generateSVG2 } from "../src/render";
|
|
5
|
-
import { runScript } from "./helpers";
|
|
3
|
+
import { LayoutEngine } from "../src/layout.js";
|
|
4
|
+
import { generateSVG2 } from "../src/render.js";
|
|
5
|
+
import { runScript } from "./helpers.js";
|
|
6
6
|
|
|
7
7
|
const mainPath = '__tests__/renderData/';
|
|
8
8
|
|
|
@@ -13,11 +13,11 @@ describe('Render tests', () => {
|
|
|
13
13
|
['simple function', 'script2.cst'],
|
|
14
14
|
['simple frame', 'script3.cst'],
|
|
15
15
|
['drawing functions for graphics', 'script4.cst']
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
])('render - %s (%s)', async (title, scriptPath) => {
|
|
18
18
|
|
|
19
|
-
const script =
|
|
20
|
-
const {hasError, visitor} = await runScript(script);
|
|
19
|
+
const script = readFileSync(mainPath + scriptPath, { encoding: 'utf8' });
|
|
20
|
+
const { hasError, visitor } = await runScript(script);
|
|
21
21
|
expect(hasError).toBe(false);
|
|
22
22
|
visitor.annotateComponents();
|
|
23
23
|
|
|
@@ -28,7 +28,7 @@ describe('Render tests', () => {
|
|
|
28
28
|
|
|
29
29
|
const svgOutput = generateSVG2(graph);
|
|
30
30
|
|
|
31
|
-
const expectedSvgOutput =
|
|
31
|
+
const expectedSvgOutput = readFileSync(mainPath + scriptPath + ".svg", { encoding: 'utf8' });
|
|
32
32
|
expect(svgOutput).toBe(expectedSvgOutput);
|
|
33
33
|
});
|
|
34
34
|
});
|
package/build/src/main.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#! /usr/bin/env node
|
|
2
2
|
import { program } from 'commander';
|
|
3
3
|
import figlet from 'figlet';
|
|
4
|
-
import fs from 'fs';
|
|
5
4
|
import path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { readFileSync, watch, writeFileSync } from 'fs';
|
|
6
7
|
import { MainVisitor } from './visitor.js';
|
|
7
8
|
import { prepareSizing } from './sizing.js';
|
|
8
9
|
import { LayoutEngine } from './layout.js';
|
|
@@ -12,9 +13,14 @@ import { parseFileWithVisitor } from './parser.js';
|
|
|
12
13
|
import { generateKiCADNetList } from './export.js';
|
|
13
14
|
import { SimpleStopwatch } from './utils.js';
|
|
14
15
|
export default async function main() {
|
|
16
|
+
const toolSrcPath = fileURLToPath(import.meta.url);
|
|
17
|
+
const toolDirectory = path.dirname(toolSrcPath) + '/../../';
|
|
18
|
+
const packageJson = JSON.parse(readFileSync(toolDirectory + 'package.json').toString());
|
|
19
|
+
;
|
|
20
|
+
const { version } = packageJson;
|
|
15
21
|
program
|
|
16
22
|
.description('generate graphical output from circuitscript files')
|
|
17
|
-
.version(
|
|
23
|
+
.version(version)
|
|
18
24
|
.option('-i, --input text <input text>', 'Input text directly')
|
|
19
25
|
.option('-f, --input-file <path>', 'Input file')
|
|
20
26
|
.option('-o, --output <path>', 'Output path')
|
|
@@ -38,10 +44,12 @@ export default async function main() {
|
|
|
38
44
|
const dumpData = options.dumpData;
|
|
39
45
|
const kicadNetlist = options.kicadNetlist;
|
|
40
46
|
let currentDirectory = options.currentDirectory ?? null;
|
|
47
|
+
const fontsPath = toolDirectory + '/fonts';
|
|
48
|
+
const defaultLibsPath = toolDirectory + '/libs';
|
|
41
49
|
if (watchFileChanges) {
|
|
42
50
|
console.log('watching for file changes...');
|
|
43
51
|
}
|
|
44
|
-
await prepareSizing();
|
|
52
|
+
await prepareSizing(fontsPath);
|
|
45
53
|
let inputFilePath = null;
|
|
46
54
|
let scriptData;
|
|
47
55
|
if (options.input) {
|
|
@@ -49,13 +57,14 @@ export default async function main() {
|
|
|
49
57
|
}
|
|
50
58
|
else {
|
|
51
59
|
inputFilePath = options.inputFile;
|
|
52
|
-
scriptData =
|
|
60
|
+
scriptData = readFileSync(inputFilePath, { encoding: 'utf-8' });
|
|
53
61
|
if (currentDirectory === null) {
|
|
54
62
|
currentDirectory = path.dirname(inputFilePath);
|
|
55
63
|
}
|
|
56
64
|
}
|
|
57
65
|
const renderOptions = {
|
|
58
66
|
currentDirectory,
|
|
67
|
+
defaultLibsPath,
|
|
59
68
|
dumpNets,
|
|
60
69
|
dumpData,
|
|
61
70
|
kicadNetlistPath: kicadNetlist,
|
|
@@ -66,9 +75,9 @@ export default async function main() {
|
|
|
66
75
|
console.log(output);
|
|
67
76
|
}
|
|
68
77
|
if (watchFileChanges) {
|
|
69
|
-
|
|
78
|
+
watch(inputFilePath, (event, targetFile) => {
|
|
70
79
|
if (event === 'change') {
|
|
71
|
-
const scriptData =
|
|
80
|
+
const scriptData = readFileSync(inputFilePath, { encoding: 'utf-8' });
|
|
72
81
|
renderScript(scriptData, outputPath, renderOptions);
|
|
73
82
|
console.log('done');
|
|
74
83
|
}
|
|
@@ -76,9 +85,9 @@ export default async function main() {
|
|
|
76
85
|
}
|
|
77
86
|
}
|
|
78
87
|
export function renderScript(scriptData, outputPath, options) {
|
|
79
|
-
const { currentDirectory = null, dumpNets = false, dumpData = false, kicadNetlistPath = null, showStats = false } = options;
|
|
88
|
+
const { currentDirectory = null, defaultLibsPath, dumpNets = false, dumpData = false, kicadNetlistPath = null, showStats = false } = options;
|
|
80
89
|
const visitor = new MainVisitor(true);
|
|
81
|
-
visitor.onImportFile = visitor.createImportFileHandler(currentDirectory);
|
|
90
|
+
visitor.onImportFile = visitor.createImportFileHandler(currentDirectory, defaultLibsPath);
|
|
82
91
|
visitor.print('reading file');
|
|
83
92
|
visitor.print('done reading file');
|
|
84
93
|
const { tree, parser, hasParseError, hasError, parserTimeTaken, lexerTimeTaken } = parseFileWithVisitor(visitor, scriptData);
|
|
@@ -87,8 +96,8 @@ export function renderScript(scriptData, outputPath, options) {
|
|
|
87
96
|
if (dumpNets) {
|
|
88
97
|
console.log(visitor.dumpNets());
|
|
89
98
|
}
|
|
90
|
-
dumpData &&
|
|
91
|
-
dumpData &&
|
|
99
|
+
dumpData && writeFileSync('dump/tree.lisp', tree.toStringTree(null, parser));
|
|
100
|
+
dumpData && writeFileSync('dump/raw-parser.txt', visitor.logger.dump());
|
|
92
101
|
if (hasError || hasParseError) {
|
|
93
102
|
console.log('Error while parsing');
|
|
94
103
|
return;
|
|
@@ -96,7 +105,7 @@ export function renderScript(scriptData, outputPath, options) {
|
|
|
96
105
|
visitor.annotateComponents();
|
|
97
106
|
if (kicadNetlistPath) {
|
|
98
107
|
const kicadNetList = generateKiCADNetList(visitor.getNetList());
|
|
99
|
-
|
|
108
|
+
writeFileSync(kicadNetlistPath, kicadNetList);
|
|
100
109
|
console.log('Generated KiCad netlist file');
|
|
101
110
|
}
|
|
102
111
|
const { sequence, nets } = visitor.getGraph();
|
|
@@ -116,7 +125,7 @@ export function renderScript(scriptData, outputPath, options) {
|
|
|
116
125
|
}
|
|
117
126
|
return tmp.join(" | ");
|
|
118
127
|
});
|
|
119
|
-
dumpData &&
|
|
128
|
+
dumpData && writeFileSync('dump/raw-sequence.txt', tmpSequence.join('\n'));
|
|
120
129
|
let svgOutput = null;
|
|
121
130
|
try {
|
|
122
131
|
const layoutEngine = new LayoutEngine();
|
|
@@ -124,12 +133,12 @@ export function renderScript(scriptData, outputPath, options) {
|
|
|
124
133
|
const graph = layoutEngine.runLayout(sequence, nets);
|
|
125
134
|
layoutEngine.printWarnings();
|
|
126
135
|
showStats && console.log('Layout took:', layoutTimer.lap());
|
|
127
|
-
dumpData &&
|
|
136
|
+
dumpData && writeFileSync('dump/raw-layout.txt', layoutEngine.logger.dump());
|
|
128
137
|
const generateSvgTimer = new SimpleStopwatch();
|
|
129
138
|
svgOutput = generateSVG2(graph);
|
|
130
139
|
showStats && console.log('Render took:', generateSvgTimer.lap());
|
|
131
140
|
if (outputPath) {
|
|
132
|
-
|
|
141
|
+
writeFileSync(outputPath, svgOutput);
|
|
133
142
|
}
|
|
134
143
|
}
|
|
135
144
|
catch (err) {
|
package/build/src/sizing.js
CHANGED
|
@@ -6,8 +6,8 @@ const supportedFonts = {
|
|
|
6
6
|
'Inter': 'Inter-Regular.ttf',
|
|
7
7
|
'Inter-Bold': 'Inter-Bold.ttf',
|
|
8
8
|
};
|
|
9
|
-
export async function prepareSizing() {
|
|
10
|
-
await config.setFontDir(
|
|
9
|
+
export async function prepareSizing(fontsPath) {
|
|
10
|
+
await config.setFontDir(fontsPath)
|
|
11
11
|
.setFontFamilyMappings(supportedFonts)
|
|
12
12
|
.preloadFonts();
|
|
13
13
|
}
|
package/build/src/visitor.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ParseTreeVisitor } from 'antlr4';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
4
|
import { ExecutionContext } from './execute.js';
|
|
5
5
|
import { ClassComponent } from './objects/ClassComponent.js';
|
|
6
6
|
import { NumericValue, ParamDefinition, PercentageValue, PinBlankValue, } from './objects/ParamDefinition.js';
|
|
@@ -731,23 +731,42 @@ export class MainVisitor extends ParseTreeVisitor {
|
|
|
731
731
|
PinTypes.Output,
|
|
732
732
|
PinTypes.Power,
|
|
733
733
|
];
|
|
734
|
-
createImportFileHandler(directory) {
|
|
734
|
+
createImportFileHandler(directory, defaultLibsPath) {
|
|
735
735
|
return (visitor, importPath) => {
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
736
|
+
let importResult;
|
|
737
|
+
importResult = this.importLib(visitor, directory, importPath);
|
|
738
|
+
if (!importResult.pathExists && importPath == 'lib') {
|
|
739
|
+
importResult = this.importLib(visitor, defaultLibsPath, importPath);
|
|
740
|
+
}
|
|
741
|
+
return importResult;
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
importLib(visitor, directory, filename) {
|
|
745
|
+
const tmpFilePath = join(directory, filename + ".cst");
|
|
746
|
+
visitor.print('importing path:', tmpFilePath);
|
|
747
|
+
let pathExists = false;
|
|
748
|
+
let fileData = null;
|
|
749
|
+
try {
|
|
750
|
+
fileData = readFileSync(tmpFilePath, { encoding: 'utf8' });
|
|
751
|
+
pathExists = true;
|
|
752
|
+
}
|
|
753
|
+
catch (err) {
|
|
754
|
+
pathExists = false;
|
|
755
|
+
}
|
|
756
|
+
try {
|
|
757
|
+
if (pathExists) {
|
|
740
758
|
visitor.print('done reading imported file data');
|
|
741
759
|
const { hasError, hasParseError } = parseFileWithVisitor(visitor, fileData);
|
|
742
|
-
return { hasError, hasParseError };
|
|
760
|
+
return { hasError, hasParseError, pathExists };
|
|
743
761
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
762
|
+
}
|
|
763
|
+
catch (err) {
|
|
764
|
+
visitor.print('Failed to import file: ', err.message);
|
|
765
|
+
}
|
|
766
|
+
return {
|
|
767
|
+
hasError: true,
|
|
768
|
+
hasParseError: true,
|
|
769
|
+
pathExists,
|
|
751
770
|
};
|
|
752
771
|
}
|
|
753
772
|
parseCreateComponentPins(pinData) {
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
testEnvironment: 'node',
|
|
3
|
+
preset: 'ts-jest/presets/default-esm',
|
|
4
|
+
transform: {
|
|
5
|
+
'^.+\\.m?[tj]s?$': ['ts-jest', {
|
|
6
|
+
useESM: true,
|
|
7
|
+
diagnostics: false
|
|
8
|
+
}],
|
|
9
|
+
},
|
|
10
|
+
moduleNameMapper: {
|
|
11
|
+
'^(\\.{1,2}/.*)\\.(m)?js$': '$1',
|
|
12
|
+
},
|
|
13
|
+
testMatch: [
|
|
14
|
+
"<rootDir>/__tests__/test**.(ts|tsx)",
|
|
15
|
+
],
|
|
16
|
+
coverageDirectory: 'coverage',
|
|
17
|
+
collectCoverageFrom: [
|
|
18
|
+
'src/**/*.ts',
|
|
19
|
+
'src/**/*.mts',
|
|
20
|
+
'!src/**/*.d.ts',
|
|
21
|
+
'!src/**/*.d.mts',
|
|
22
|
+
],
|
|
23
|
+
};
|
package/libs/lib.cst
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// Circuitscript default lib
|
|
2
|
+
|
|
3
|
+
def net(net_name):
|
|
4
|
+
return create component:
|
|
5
|
+
pins: 1
|
|
6
|
+
display: create graphic:
|
|
7
|
+
hline: -15, 0, 30
|
|
8
|
+
pin: 1, 0, 10, 0, 0
|
|
9
|
+
label: "net_name", 0, -5, net_name, fontSize=10, anchor="middle"
|
|
10
|
+
type: "net"
|
|
11
|
+
params:
|
|
12
|
+
__is_net: 1
|
|
13
|
+
net_name: net_name
|
|
14
|
+
priority: 10
|
|
15
|
+
|
|
16
|
+
def supply(net_name):
|
|
17
|
+
return create component:
|
|
18
|
+
pins: 1
|
|
19
|
+
display: create graphic:
|
|
20
|
+
hline: -15, 0, 30
|
|
21
|
+
pin: 1, 0, 10, 0, 0
|
|
22
|
+
label: "net_name", 0, -5, net_name, fontSize=10, anchor="middle"
|
|
23
|
+
type: "net"
|
|
24
|
+
params:
|
|
25
|
+
__is_net: 1
|
|
26
|
+
net_name: net_name
|
|
27
|
+
priority: 20
|
|
28
|
+
|
|
29
|
+
def label(value):
|
|
30
|
+
return create component:
|
|
31
|
+
pins: 1
|
|
32
|
+
display: create graphic:
|
|
33
|
+
label: "value", 0, -2, fontSize=10, anchor="left"
|
|
34
|
+
pin: 1, 0, 0, 0, 0
|
|
35
|
+
type: "label"
|
|
36
|
+
params:
|
|
37
|
+
__is_net: 1
|
|
38
|
+
net_name: value
|
|
39
|
+
value: value
|
|
40
|
+
priority: 5
|
|
41
|
+
|
|
42
|
+
def res(value):
|
|
43
|
+
width = 40
|
|
44
|
+
height = 20
|
|
45
|
+
|
|
46
|
+
return create component:
|
|
47
|
+
pins: 2
|
|
48
|
+
display: create graphic:
|
|
49
|
+
rect: 0, 0, width, height
|
|
50
|
+
pin: 1, -width/2 - 20, 0, -width / 2, 0
|
|
51
|
+
pin: 2, width/2 + 20, 0, width/2, 0
|
|
52
|
+
label: ("value", 0, 1, value, fontSize=10, anchor="middle", vanchor="middle")
|
|
53
|
+
label: ("refdes", -width/2, -height/2 -5 , "?", fontSize=10, anchor="left")
|
|
54
|
+
|
|
55
|
+
type: "res"
|
|
56
|
+
params:
|
|
57
|
+
value: value
|
|
58
|
+
size: "0402"
|
|
59
|
+
footprint: "Resistor_SMD:R_0402_1005Metric"
|
|
60
|
+
|
|
61
|
+
def cap(value):
|
|
62
|
+
width = 20
|
|
63
|
+
height = 40
|
|
64
|
+
|
|
65
|
+
return create component:
|
|
66
|
+
pins: 2
|
|
67
|
+
display: create graphic:
|
|
68
|
+
hline: -width/2, -3, width
|
|
69
|
+
hline: -width/2, 3, width
|
|
70
|
+
pin: 1, 0, -height/2, 0, -3
|
|
71
|
+
pin: 2, 0, height/2, 0, 3
|
|
72
|
+
label: "refdes", width/2+2, 0, "?", fontSize = 10, anchor="left"
|
|
73
|
+
label: "value", width/2+2, 5, value, fontSize = 10, anchor = "left", vanchor="top"
|
|
74
|
+
type: "cap"
|
|
75
|
+
params:
|
|
76
|
+
value: value
|
|
77
|
+
size: "0402"
|
|
78
|
+
footprint: "Capacitor_SMD:C_0402_1005Metric"
|
|
79
|
+
|
|
80
|
+
def ind(value):
|
|
81
|
+
width = 40
|
|
82
|
+
height = 20
|
|
83
|
+
|
|
84
|
+
return create component:
|
|
85
|
+
pins: 2
|
|
86
|
+
type: "ind"
|
|
87
|
+
display: create graphic:
|
|
88
|
+
arc: -15, 0, 5, 180, 360
|
|
89
|
+
arc: -5, 0, 5, 180, 360
|
|
90
|
+
arc: 5, 0, 5, 180, 360
|
|
91
|
+
arc: 15, 0, 5, 180, 360
|
|
92
|
+
pin: 1, -width/2 - 20, 0, -width / 2, 0
|
|
93
|
+
pin: 2, width/2 + 20, 0, width/2, 0
|
|
94
|
+
label: ("value", 0, 10, value, fontSize=10, anchor="middle", vanchor="middle")
|
|
95
|
+
label: ("refdes", -width/2, -height/2 -5 , "?", fontSize=10, anchor="left")
|
|
96
|
+
params:
|
|
97
|
+
value: value
|
|
98
|
+
|
|
99
|
+
def diode():
|
|
100
|
+
width = 20
|
|
101
|
+
height = 20
|
|
102
|
+
|
|
103
|
+
# Diode is drawn horizontally
|
|
104
|
+
# -|>|-
|
|
105
|
+
return create component:
|
|
106
|
+
pins: 2
|
|
107
|
+
type: "diode"
|
|
108
|
+
display: create graphic:
|
|
109
|
+
path: ("M", width/2, -height/2, "L", width/2, height/2,
|
|
110
|
+
"M", -width/2, -height/2, "L", -width/2, height/2,
|
|
111
|
+
"L", width/2, 0, "L", -width/2, -height/2)
|
|
112
|
+
pin: 1, -width/2-20, 0, -width/2, 0 # anode
|
|
113
|
+
pin: 2, width/2 + 20, 0, width/2, 0 # cathode
|
|
114
|
+
label: "refdes", width/2 + 5, 5, "?", fontSize=10, anchor="left", vanchor="top"
|
|
115
|
+
|
|
116
|
+
def led(color):
|
|
117
|
+
width = 20
|
|
118
|
+
height = 20
|
|
119
|
+
|
|
120
|
+
return create component:
|
|
121
|
+
pins:
|
|
122
|
+
1: "cathode"
|
|
123
|
+
2: "anode"
|
|
124
|
+
type: "diode"
|
|
125
|
+
display: create graphic:
|
|
126
|
+
path: ("M", width/2, -height/2, "L", width/2, height/2,
|
|
127
|
+
"M", -width/2, -height/2, "L", -width/2, height/2,
|
|
128
|
+
"L", width/2, 0, "L", -width/2, -height/2)
|
|
129
|
+
path: ("M", 0, 8, "L", 5, 18,
|
|
130
|
+
"M", 3, 8, "L", 8, 18)
|
|
131
|
+
pin: 1, -width/2-20, 0, -width/2, 0 # anode
|
|
132
|
+
pin: 2, width/2 + 20, 0, width/2, 0 # cathode
|
|
133
|
+
label: "refdes", width/2 + 5, 5, "?", fontSize=10, anchor="left", vanchor="top"
|
|
134
|
+
params:
|
|
135
|
+
size: "0603"
|
|
136
|
+
footprint: "LED_SMD:LED_0603_1608Metric_Pad1.05x0.95mm_HandSolder"
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def cgnd():
|
|
140
|
+
net_name = "gnd"
|
|
141
|
+
return create component:
|
|
142
|
+
pins: 1
|
|
143
|
+
display: create graphic:
|
|
144
|
+
hline: -15, 0, 30
|
|
145
|
+
hline: -10, 5, 20
|
|
146
|
+
hline: -5, 10, 10
|
|
147
|
+
pin: 1, 0, -10, 0, 0
|
|
148
|
+
label: "net_name", 0, 22, net_name, fontSize=10, anchor="middle"
|
|
149
|
+
type: "net"
|
|
150
|
+
params:
|
|
151
|
+
__is_net: 1
|
|
152
|
+
net_name: net_name
|
|
153
|
+
priority: 100
|
|
154
|
+
|
|
155
|
+
def dgnd():
|
|
156
|
+
height = 10
|
|
157
|
+
width = 20
|
|
158
|
+
|
|
159
|
+
net_name = "GND"
|
|
160
|
+
return create component:
|
|
161
|
+
pins: 1
|
|
162
|
+
display: create graphic:
|
|
163
|
+
path: ("M", -width/2, 0,
|
|
164
|
+
"L", width/2, 0,
|
|
165
|
+
"L", 0, height,
|
|
166
|
+
"Z")
|
|
167
|
+
pin: 1, 0, -10, 0, 0
|
|
168
|
+
label: "net_name", 0, height + 10, net_name, fontSize=10, anchor="middle"
|
|
169
|
+
type: "net"
|
|
170
|
+
params:
|
|
171
|
+
__is_net: 1
|
|
172
|
+
net_name: net_name
|
|
173
|
+
priority: 100
|
|
174
|
+
|
|
175
|
+
def marker_point():
|
|
176
|
+
return create component:
|
|
177
|
+
pins: 1
|
|
178
|
+
display: create graphic:
|
|
179
|
+
hline: -5, 0, 10
|
|
180
|
+
vline: 0, -5, 10
|
|
181
|
+
pin: 1, 0, 0, 0, 0
|
|
182
|
+
type: "net"
|
|
183
|
+
|
|
184
|
+
def arrow_point():
|
|
185
|
+
return create component:
|
|
186
|
+
pins: 1
|
|
187
|
+
display: create graphic:
|
|
188
|
+
path: ("M", 15, -5, "L", 20, 0, "L", 15, 5)
|
|
189
|
+
pin: 1, 0, 0, 20, 0
|
|
190
|
+
type: "net"
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "circuitscript",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"description": "Interpreter for the circuitscript language",
|
|
5
|
+
"homepage": "https://circuitscript.net",
|
|
5
6
|
"engines": {
|
|
6
7
|
"node": ">=16.0"
|
|
7
8
|
},
|
|
@@ -45,27 +46,6 @@
|
|
|
45
46
|
"profile-flame": "node --prof-process --preprocess -j isolate*.log | flamebearer",
|
|
46
47
|
"server": "node server.js"
|
|
47
48
|
},
|
|
48
|
-
"jest": {
|
|
49
|
-
"preset": "ts-jest",
|
|
50
|
-
"verbose": true,
|
|
51
|
-
"testMatch": [
|
|
52
|
-
"**/__tests__/test**.ts"
|
|
53
|
-
],
|
|
54
|
-
"transform": {
|
|
55
|
-
"^.+\\.tsx?$": [
|
|
56
|
-
"ts-jest",
|
|
57
|
-
{
|
|
58
|
-
"diagnostics": false
|
|
59
|
-
}
|
|
60
|
-
]
|
|
61
|
-
},
|
|
62
|
-
"collectCoverageFrom": [
|
|
63
|
-
"src/**/*.ts"
|
|
64
|
-
],
|
|
65
|
-
"coveragePathIgnorePatterns": [
|
|
66
|
-
"src/antlr"
|
|
67
|
-
]
|
|
68
|
-
},
|
|
69
49
|
"boilerplate_author": "Jakub Synowiec <jsynowiec@users.noreply.github.com>",
|
|
70
50
|
"license": "MIT",
|
|
71
51
|
"dependencies": {
|
package/src/main.ts
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import { program } from 'commander';
|
|
4
4
|
import figlet from 'figlet';
|
|
5
|
-
|
|
6
|
-
import fs from 'fs';
|
|
7
5
|
import path from 'path';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import { readFileSync, watch, writeFileSync } from 'fs';
|
|
8
8
|
|
|
9
9
|
import { MainVisitor } from './visitor.js';
|
|
10
10
|
import { prepareSizing } from './sizing.js';
|
|
@@ -16,10 +16,15 @@ import { generateKiCADNetList } from './export.js';
|
|
|
16
16
|
import { SimpleStopwatch } from './utils.js';
|
|
17
17
|
|
|
18
18
|
export default async function main(): Promise<void> {
|
|
19
|
+
const toolSrcPath = fileURLToPath(import.meta.url);
|
|
20
|
+
const toolDirectory = path.dirname(toolSrcPath) + '/../../';
|
|
21
|
+
|
|
22
|
+
const packageJson = JSON.parse(readFileSync(toolDirectory + 'package.json').toString());;
|
|
23
|
+
const {version} = packageJson;
|
|
19
24
|
|
|
20
25
|
program
|
|
21
26
|
.description('generate graphical output from circuitscript files')
|
|
22
|
-
.version(
|
|
27
|
+
.version(version)
|
|
23
28
|
.option('-i, --input text <input text>', 'Input text directly')
|
|
24
29
|
.option('-f, --input-file <path>', 'Input file')
|
|
25
30
|
.option('-o, --output <path>', 'Output path')
|
|
@@ -51,11 +56,15 @@ export default async function main(): Promise<void> {
|
|
|
51
56
|
|
|
52
57
|
let currentDirectory = options.currentDirectory ?? null;
|
|
53
58
|
|
|
59
|
+
|
|
60
|
+
const fontsPath = toolDirectory + '/fonts';
|
|
61
|
+
const defaultLibsPath = toolDirectory + '/libs';
|
|
62
|
+
|
|
54
63
|
if (watchFileChanges) {
|
|
55
64
|
console.log('watching for file changes...');
|
|
56
65
|
}
|
|
57
66
|
|
|
58
|
-
await prepareSizing();
|
|
67
|
+
await prepareSizing(fontsPath);
|
|
59
68
|
|
|
60
69
|
let inputFilePath: string = null;
|
|
61
70
|
|
|
@@ -64,7 +73,7 @@ export default async function main(): Promise<void> {
|
|
|
64
73
|
scriptData = options.input;
|
|
65
74
|
} else {
|
|
66
75
|
inputFilePath = options.inputFile; // this should be provided
|
|
67
|
-
scriptData =
|
|
76
|
+
scriptData = readFileSync(inputFilePath, { encoding: 'utf-8' });
|
|
68
77
|
|
|
69
78
|
if (currentDirectory === null) {
|
|
70
79
|
currentDirectory = path.dirname(inputFilePath);
|
|
@@ -73,6 +82,7 @@ export default async function main(): Promise<void> {
|
|
|
73
82
|
|
|
74
83
|
const renderOptions = {
|
|
75
84
|
currentDirectory,
|
|
85
|
+
defaultLibsPath,
|
|
76
86
|
dumpNets,
|
|
77
87
|
dumpData,
|
|
78
88
|
kicadNetlistPath: kicadNetlist,
|
|
@@ -87,9 +97,9 @@ export default async function main(): Promise<void> {
|
|
|
87
97
|
}
|
|
88
98
|
|
|
89
99
|
if (watchFileChanges) {
|
|
90
|
-
|
|
100
|
+
watch(inputFilePath, (event, targetFile) => {
|
|
91
101
|
if (event === 'change') {
|
|
92
|
-
const scriptData =
|
|
102
|
+
const scriptData = readFileSync(inputFilePath,
|
|
93
103
|
{encoding: 'utf-8'});
|
|
94
104
|
|
|
95
105
|
renderScript(scriptData, outputPath, renderOptions);
|
|
@@ -104,6 +114,7 @@ export function renderScript(scriptData: string, outputPath: string, options): s
|
|
|
104
114
|
|
|
105
115
|
const {
|
|
106
116
|
currentDirectory = null,
|
|
117
|
+
defaultLibsPath,
|
|
107
118
|
dumpNets = false,
|
|
108
119
|
dumpData = false,
|
|
109
120
|
kicadNetlistPath = null,
|
|
@@ -111,7 +122,7 @@ export function renderScript(scriptData: string, outputPath: string, options): s
|
|
|
111
122
|
|
|
112
123
|
const visitor = new MainVisitor(true);
|
|
113
124
|
|
|
114
|
-
visitor.onImportFile = visitor.createImportFileHandler(currentDirectory);
|
|
125
|
+
visitor.onImportFile = visitor.createImportFileHandler(currentDirectory, defaultLibsPath);
|
|
115
126
|
|
|
116
127
|
visitor.print('reading file');
|
|
117
128
|
visitor.print('done reading file');
|
|
@@ -129,8 +140,8 @@ export function renderScript(scriptData: string, outputPath: string, options): s
|
|
|
129
140
|
}
|
|
130
141
|
// console.log(visitor.dumpUniqueNets());
|
|
131
142
|
|
|
132
|
-
dumpData &&
|
|
133
|
-
dumpData &&
|
|
143
|
+
dumpData && writeFileSync('dump/tree.lisp', tree.toStringTree(null, parser));
|
|
144
|
+
dumpData && writeFileSync('dump/raw-parser.txt', visitor.logger.dump());
|
|
134
145
|
|
|
135
146
|
if (hasError || hasParseError) {
|
|
136
147
|
console.log('Error while parsing');
|
|
@@ -141,7 +152,7 @@ export function renderScript(scriptData: string, outputPath: string, options): s
|
|
|
141
152
|
|
|
142
153
|
if(kicadNetlistPath){
|
|
143
154
|
const kicadNetList = generateKiCADNetList(visitor.getNetList());
|
|
144
|
-
|
|
155
|
+
writeFileSync(kicadNetlistPath, kicadNetList);
|
|
145
156
|
console.log('Generated KiCad netlist file');
|
|
146
157
|
}
|
|
147
158
|
|
|
@@ -176,7 +187,7 @@ export function renderScript(scriptData: string, outputPath: string, options): s
|
|
|
176
187
|
return tmp.join(" | ");
|
|
177
188
|
});
|
|
178
189
|
|
|
179
|
-
dumpData &&
|
|
190
|
+
dumpData && writeFileSync('dump/raw-sequence.txt', tmpSequence.join('\n'));
|
|
180
191
|
let svgOutput: string = null;
|
|
181
192
|
|
|
182
193
|
try {
|
|
@@ -189,14 +200,14 @@ export function renderScript(scriptData: string, outputPath: string, options): s
|
|
|
189
200
|
|
|
190
201
|
showStats && console.log('Layout took:', layoutTimer.lap());
|
|
191
202
|
|
|
192
|
-
dumpData &&
|
|
203
|
+
dumpData && writeFileSync('dump/raw-layout.txt', layoutEngine.logger.dump());
|
|
193
204
|
|
|
194
205
|
const generateSvgTimer = new SimpleStopwatch();
|
|
195
206
|
svgOutput = generateSVG2(graph);
|
|
196
207
|
showStats && console.log('Render took:', generateSvgTimer.lap());
|
|
197
208
|
|
|
198
209
|
if (outputPath){
|
|
199
|
-
|
|
210
|
+
writeFileSync(outputPath, svgOutput);
|
|
200
211
|
}
|
|
201
212
|
} catch (err) {
|
|
202
213
|
console.log('Failed to render:');
|
package/src/sizing.ts
CHANGED
|
@@ -10,8 +10,8 @@ const supportedFonts = {
|
|
|
10
10
|
'Inter-Bold': 'Inter-Bold.ttf',
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export async function prepareSizing(): Promise<void> {
|
|
14
|
-
await config.setFontDir(
|
|
13
|
+
export async function prepareSizing(fontsPath): Promise<void> {
|
|
14
|
+
await config.setFontDir(fontsPath)
|
|
15
15
|
.setFontFamilyMappings(supportedFonts)
|
|
16
16
|
.preloadFonts()
|
|
17
17
|
}
|
package/src/visitor.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParseTreeVisitor, ParserRuleContext } from 'antlr4';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import { readFileSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
Add_component_exprContext,
|
|
@@ -1093,31 +1093,60 @@ export class MainVisitor extends ParseTreeVisitor<any> {
|
|
|
1093
1093
|
PinTypes.Power,
|
|
1094
1094
|
];
|
|
1095
1095
|
|
|
1096
|
-
createImportFileHandler(directory: string):
|
|
1096
|
+
createImportFileHandler(directory: string, defaultLibsPath: string):
|
|
1097
1097
|
((visitor: MainVisitor, importPath: string) =>
|
|
1098
|
-
{ hasError: boolean, hasParseError: boolean }) {
|
|
1098
|
+
{ hasError: boolean, hasParseError: boolean, pathExists: boolean }) {
|
|
1099
|
+
|
|
1099
1100
|
return (visitor: MainVisitor, importPath: string) => {
|
|
1100
1101
|
// Check if different files exist first
|
|
1101
|
-
|
|
1102
|
-
|
|
1102
|
+
let importResult: {
|
|
1103
|
+
hasError: boolean,
|
|
1104
|
+
hasParseError: boolean,
|
|
1105
|
+
pathExists: boolean,
|
|
1106
|
+
};
|
|
1103
1107
|
|
|
1104
|
-
|
|
1105
|
-
|
|
1108
|
+
importResult = this.importLib(visitor, directory, importPath);
|
|
1109
|
+
|
|
1110
|
+
if (!importResult.pathExists && importPath == 'lib') {
|
|
1111
|
+
// Load default path
|
|
1112
|
+
importResult = this.importLib(visitor, defaultLibsPath, importPath);
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
return importResult;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
private importLib(visitor: MainVisitor, directory: string, filename: string): {hasError: boolean, hasParseError: boolean, pathExists: boolean} {
|
|
1120
|
+
const tmpFilePath = join(directory, filename + ".cst");
|
|
1121
|
+
visitor.print('importing path:', tmpFilePath);
|
|
1122
|
+
let pathExists = false;
|
|
1123
|
+
|
|
1124
|
+
let fileData: string = null;
|
|
1125
|
+
|
|
1126
|
+
try {
|
|
1127
|
+
fileData = readFileSync(tmpFilePath, { encoding: 'utf8' });
|
|
1128
|
+
pathExists = true;
|
|
1129
|
+
} catch (err) {
|
|
1130
|
+
pathExists = false;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
try {
|
|
1134
|
+
if (pathExists){
|
|
1106
1135
|
visitor.print('done reading imported file data');
|
|
1107
1136
|
|
|
1108
1137
|
const { hasError, hasParseError } =
|
|
1109
1138
|
parseFileWithVisitor(visitor, fileData);
|
|
1110
1139
|
|
|
1111
|
-
return { hasError, hasParseError }
|
|
1112
|
-
|
|
1113
|
-
} catch (err) {
|
|
1114
|
-
console.log('Failed to import file: ', err.message);
|
|
1140
|
+
return { hasError, hasParseError, pathExists }
|
|
1115
1141
|
}
|
|
1142
|
+
} catch (err) {
|
|
1143
|
+
visitor.print('Failed to import file: ', err.message);
|
|
1144
|
+
}
|
|
1116
1145
|
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1146
|
+
return {
|
|
1147
|
+
hasError: true,
|
|
1148
|
+
hasParseError: true,
|
|
1149
|
+
pathExists,
|
|
1121
1150
|
}
|
|
1122
1151
|
}
|
|
1123
1152
|
|
package/tsconfig.json
CHANGED
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
"noUnusedParameters": true,
|
|
18
18
|
"noImplicitAny": false,
|
|
19
19
|
"noImplicitThis": false,
|
|
20
|
-
"strictNullChecks": false
|
|
20
|
+
"strictNullChecks": false,
|
|
21
|
+
"resolveJsonModule": true,
|
|
22
|
+
"skipLibCheck": true,
|
|
23
|
+
"esModuleInterop":true,
|
|
21
24
|
},
|
|
22
25
|
"include": ["src/**/*", "__tests__/**/*"]
|
|
23
26
|
}
|