269 lines
8.6 KiB
JavaScript
Executable File
269 lines
8.6 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
||
/* eslint-disable no-console */
|
||
/* eslint-disable indent */
|
||
'use strict';
|
||
|
||
const fs = require('fs');
|
||
const cp = require('child_process');
|
||
const llparse = require('llparse');
|
||
|
||
|
||
const objection = lut => arg => arg.split(/\s+/).reduce((res, key) => {
|
||
if (lut[key] === undefined) {
|
||
throw new Error(key);
|
||
}
|
||
res[key] = lut[key];
|
||
return res;
|
||
}, {});
|
||
|
||
const properties = {
|
||
command: 'i8',
|
||
type: 'i8',
|
||
size: 'i32',
|
||
time: 'i64', // current simulation time
|
||
trigger: 'ptr',
|
||
triee: 'ptr', // trigger event emitter
|
||
lifee: 'ptr', // life cycle event emmiter
|
||
info: 'ptr',
|
||
value: 'ptr', // value of the signal on change event
|
||
mask: 'ptr', // mask (x, z) of the signal on change event
|
||
digitCount: 'i32',
|
||
maskCount: 'i32',
|
||
tmpStr: 'ptr',
|
||
timeStampStr: 'ptr',
|
||
idStr: 'ptr',
|
||
tmpStr2: 'ptr',
|
||
stackPointer: 'i32',
|
||
id: 'ptr',
|
||
napi_env: 'ptr'
|
||
};
|
||
|
||
const spaces = [' ', '\n', '\r', '\t'];
|
||
const lineSpaces = [' ', '\t'];
|
||
|
||
function main() {
|
||
const projectName = 'vcd_parser';
|
||
|
||
// 有关左推解析器框架 llparse 的文章,移步我之前的博客:https://kirigaya.cn/blog/article?seq=223
|
||
// 如果需要可视化项目图,可以取消下面的注释
|
||
// const { Dot } = require('llparse-dot');
|
||
// const llDot = new Dot();
|
||
// const dotCodeString = llDot.build(method);
|
||
// fs.writeFileSync(projectName + '.dot', code, { encoding: 'utf-8' });
|
||
|
||
// 项目名为 vcd_parser
|
||
const p = new llparse.LLParse(projectName);
|
||
|
||
// 注册一些属性
|
||
for (const propertyName of Object.keys(properties)) {
|
||
const propertyType = properties[propertyName];
|
||
p.property(propertyType, propertyName);
|
||
}
|
||
|
||
// 自定义 code span,对于游走到这些 span 后要如何处理,都在 vcd_span.c 文件里
|
||
// 比如对于 varNameSpan 的定义,在 vcd_span.c 中存在一个同名的函数来定义它
|
||
const varSizeSpan = p.span(p.code.span('varSizeSpan'));
|
||
const varIdSpan = p.span(p.code.span('varIdSpan'));
|
||
const varNameSpan = p.span(p.code.span('varNameSpan'));
|
||
const idSpan = p.span(p.code.span('idSpan'));
|
||
const commandSpan = p.span(p.code.span('commandSpan'));
|
||
const timeSpan = p.span(p.code.span('timeSpan'));
|
||
|
||
// 自定义 node, 他们都是具体的节点
|
||
const declaration = p.node('declaration');
|
||
const varType = p.node('varType');
|
||
const varTypeEnd = p.node('varTypeEnd');
|
||
const varSize = p.node('varSize');
|
||
const varSizeEnd = p.node('varSizeEnd');
|
||
const varId = p.node('varId');
|
||
const varIdEnd = p.node('varIdEnd');
|
||
const varName = p.node('varName');
|
||
const varNameEnd = p.node('varNameEnd');
|
||
const inDeclaration = p.node('inDeclaration');
|
||
const simulation = p.node('simulation');
|
||
const inSimulation = p.node('inSimulation');
|
||
const simulationTime = p.node('simulationTime');
|
||
const simulationVector = p.node('simulationVector');
|
||
const simulationVectorEnd = p.node('simulationVectorEnd');
|
||
const simulationVectorRecovery = p.node('simulationVectorRecovery');
|
||
const simulationId = p.node('simulationId');
|
||
const enddefinitions = p.node('inDeclarationEnd');
|
||
// scopeType scopeTypeEnd
|
||
// scopeIdentifier scopeIdentifierEnd
|
||
|
||
const cmd = objection({
|
||
$comment: 1,
|
||
$date: 2,
|
||
$scope: 3,
|
||
$timescale: 4,
|
||
$upscope: 5,
|
||
$var: 6,
|
||
$version: 7,
|
||
$enddefinitions: 8,
|
||
$dumpall: 9,
|
||
$dumpoff: 10,
|
||
$dumpon: 11,
|
||
$dumpvars: 12,
|
||
'#': 13,
|
||
'0': 14,
|
||
'1': 15,
|
||
x: 16, X: 17,
|
||
z: 18, Z: 19,
|
||
u: 20, U: 21, // VHDL states
|
||
w: 22, W: 23,
|
||
l: 24, L: 25,
|
||
h: 26, H: 27,
|
||
'-': 28,
|
||
b: 30, B: 31, r: 32, R: 33
|
||
});
|
||
|
||
|
||
// p.code.store('command') 的具体执行逻辑移步 command-handler.js 中的 commandHandler
|
||
declaration
|
||
.match(spaces, declaration)
|
||
.select(cmd('$scope $var $upscope $comment $date $timescale $version #'),
|
||
p.invoke(p.code.store('command'), commandSpan.start(inDeclaration)))
|
||
.select(cmd('$enddefinitions'),
|
||
p.invoke(p.code.store('command'), commandSpan.start(enddefinitions)))
|
||
.otherwise(p.error(1, 'Expected declaration command'));
|
||
|
||
|
||
varType.match(spaces, varType).otherwise(varTypeEnd);
|
||
varTypeEnd
|
||
.select({
|
||
event: 1,
|
||
integer: 2,
|
||
parameter: 3,
|
||
real: 4,
|
||
realtime: 5,
|
||
reg: 6,
|
||
supply0: 7,
|
||
supply1: 8,
|
||
time: 9,
|
||
tri: 10,
|
||
triand: 11,
|
||
trior: 12,
|
||
trireg: 13,
|
||
tri0: 14,
|
||
tri1: 15,
|
||
wand: 16,
|
||
wire: 17,
|
||
wor: 18
|
||
}, p.invoke(p.code.store('type'), varSize))
|
||
.otherwise(p.error(3, 'Expected var type'));
|
||
|
||
// $var reg 3 ( r_reg [2:0] $end
|
||
// ^
|
||
|
||
varSize.match(spaces, varSize).otherwise(varSizeSpan.start(varSizeEnd));
|
||
varSizeEnd.match(spaces, varSizeSpan.end(varId)).skipTo(varSizeEnd);
|
||
|
||
// $var reg 3 ( r_reg [2:0] $end
|
||
// ^
|
||
|
||
varId.match(spaces, varId).otherwise(varIdSpan.start(varIdEnd));
|
||
varIdEnd.match(spaces, varIdSpan.end(varName)).skipTo(varIdEnd);
|
||
|
||
// $var reg 3 ( r_reg [2:0] $end
|
||
// ^^^^^
|
||
|
||
varName.match(spaces, varName).otherwise(varNameSpan.start(varNameEnd));
|
||
varNameEnd.match('$end', commandSpan.end(varNameSpan.end(declaration))).skipTo(varNameEnd);
|
||
|
||
// $end
|
||
|
||
inDeclaration
|
||
.match('$end', commandSpan.end(declaration))
|
||
.skipTo(inDeclaration);
|
||
|
||
enddefinitions
|
||
.match('$end', commandSpan.end(simulation))
|
||
.skipTo(enddefinitions);
|
||
|
||
simulation
|
||
.match([' ', '\r', '\n', '\t', '$dumpvars', '$dumpall', '$end'], simulation)
|
||
.select(cmd('$dumpoff $dumpon $comment'),
|
||
p.invoke(p.code.store('command'), commandSpan.start(inSimulation)))
|
||
.select(cmd('#'),
|
||
p.invoke(p.code.store('command'), timeSpan.start(simulationTime)))
|
||
.select(cmd('0 1 x X z Z u U w W l L h H -'),
|
||
p.invoke(p.code.store('command'), idSpan.start(simulationId)))
|
||
.select(cmd('b B r R'),
|
||
p.invoke(p.code.store('command'), simulationVector))
|
||
.otherwise(p.error(4, 'Expected simulation command'));
|
||
|
||
inSimulation
|
||
.match('$end', commandSpan.end(simulation))
|
||
.skipTo(inSimulation);
|
||
|
||
simulationTime
|
||
.match(spaces, timeSpan.end(p.invoke(p.code.span('onTime'), simulation)))
|
||
.skipTo(simulationTime);
|
||
|
||
simulationVector
|
||
.select(
|
||
{
|
||
0: 0,
|
||
1: 1,
|
||
x: 2, X: 2,
|
||
z: 3, Z: 3,
|
||
u: 3, U: 3, // VHDL states
|
||
w: 3, W: 3,
|
||
l: 3, L: 3,
|
||
h: 3, H: 3,
|
||
'-': 3
|
||
},
|
||
p.invoke(
|
||
// p.code.mulAdd('value', {base: 2, signed: false}),
|
||
p.code.value('onDigit'),
|
||
{ 1: p.error(5, 'Content-Length overflow') },
|
||
simulationVector
|
||
)
|
||
)
|
||
.otherwise(simulationVectorEnd);
|
||
|
||
simulationVectorEnd
|
||
.match(lineSpaces, idSpan.start(simulationId))
|
||
.skipTo(simulationVectorRecovery);
|
||
|
||
simulationVectorRecovery
|
||
.select(
|
||
{
|
||
'\n': 1, '\r': 1
|
||
},
|
||
p.invoke(
|
||
p.code.value('onRecover'),
|
||
{ 1: p.error(6, 'recover') },
|
||
simulation
|
||
)
|
||
)
|
||
.skipTo(simulationVectorRecovery);
|
||
|
||
simulationId
|
||
.match(spaces, idSpan.end(p.invoke(p.code.span('onId'), simulation)))
|
||
.skipTo(simulationId);
|
||
|
||
const artifacts = p.build(declaration);
|
||
|
||
fs.writeFileSync(projectName + '.h', artifacts.header);
|
||
// fs.writeFileSync('verilog_preprocessor.bc', artifacts.bitcode);
|
||
fs.writeFileSync(projectName + '.c', artifacts.c);
|
||
|
||
// const dot = new llparseDot.Dot();
|
||
// fs.writeFileSync(projectName + '.dot', dot.build(declaration));
|
||
|
||
// 使用 binding.gyp 把生成的 c 和 h 进行编译
|
||
console.log('build');
|
||
const proc = cp.spawn('node-gyp', ['configure', 'build']);
|
||
proc.stderr.on('data', data => {
|
||
console.error(data.toString());
|
||
});
|
||
proc.on('close', () => {
|
||
console.log('done');
|
||
});
|
||
};
|
||
|
||
main();
|
||
|
||
/* eslint camelcase: 0 */
|