-
Notifications
You must be signed in to change notification settings - Fork 1
/
replicad.ts
executable file
·98 lines (87 loc) · 2.49 KB
/
replicad.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/usr/bin/env ts-node
// vim: filetype=typescript
import fs = require("fs")
import netlistsvg = require("netlistsvg")
import path = require("path")
import Project from "ts-simple-ast"
import * as ts from "typescript"
const inputPath = process.argv[2]
if (inputPath == null) {
console.error("USAGE: replicad <input>.ts")
process.exit(1)
}
const project = new Project()
project.addExistingSourceFiles(inputPath)
const sourceFiles = project.getSourceFiles()
addNames(sourceFiles)
const circuit = require("./" + inputPath.replace(/\.ts$/, ".js")).default
const netlist = circuit.toYosys()
const skinPath = path.join(__dirname, "node_modules/netlistsvg/lib/analog.svg")
fs.readFile(skinPath, (err, skinData) => {
if (err) {
throw err
}
netlistsvg
.render(skinData, netlist)
.then(console.log)
.catch(e => {
console.error(e)
process.exit(1)
})
})
function addNames(files) {
files.forEach(f => {
const imports = f
.getImportDeclarations()
.map(i => i.getModuleSpecifierSourceFile())
addNames(imports)
const calls = f.getDescendantsOfKind(ts.SyntaxKind.CallExpression)
calls.forEach(call => {
const e = call.getExpression()
if (
e.getKind() === ts.SyntaxKind.PropertyAccessExpression &&
(e.getName() === "connect" || e.getName() === "chain") &&
e
.getExpression()
.getType()
.getText() === "Circuit"
) {
call.getArguments().forEach(arg => {
const v = getObjectVariableName(arg)
arg.replaceWithText(`(${v}.name = '${v}', ${arg.getText()})`)
})
}
})
const emitResult = f.emit()
for (const diagnostic of emitResult.getDiagnostics()) {
console.error(diagnostic.getMessageText())
}
})
}
function getObjectVariableName(e) {
const prev = [e]
while (
e.getKind() === ts.SyntaxKind.PropertyAccessExpression ||
e.getKind() === ts.SyntaxKind.ElementAccessExpression
) {
e = e.getExpression()
prev.unshift(e)
}
// if our expression is not something that can be added to a circuit, roll
// back to the last expression that was
for (const prevExp of prev) {
const base = getBaseName(prevExp)
if (base === "Label" || base === "Component") {
return prevExp.getText()
}
}
}
function getBaseName(e) {
let t = e.getType()._compilerType
let base = t.getBaseTypes()
while (base != null && base.length > 0) {
t = base[0]
base = t.getBaseTypes()
}
return (t.symbol || {}).escapedName
}