From fd06dc41045a2fe1dd5600a367c4ac4d67c49cea Mon Sep 17 00:00:00 2001 From: Aliaksei Chapyzhenka Date: Wed, 6 Nov 2019 15:32:02 -0800 Subject: [PATCH] better API --- bin/build.js | 76 +++++++++++++++++++++++++++-------------- bin/find.js | 48 +++++++++++--------------- index.js | 36 ++++++++++++++++++-- test/basic.js | 73 +++++++++++++++++----------------------- vcd.c | 93 ++++++++++++++++++++++++++++++++++++++------------- vcd_spans.c | 92 +++++++++++++++++++++++++++++--------------------- 6 files changed, 256 insertions(+), 162 deletions(-) diff --git a/bin/build.js b/bin/build.js index 97c1e0a..e5cb784 100755 --- a/bin/build.js +++ b/bin/build.js @@ -15,11 +15,14 @@ p.property('i32', 'size'); p.property('i32', 'time'); p.property('ptr', 'trigger'); p.property('ptr', 'info'); -p.property('ptr', 'emit'); +p.property('ptr', 'triee'); +p.property('ptr', 'lifee'); +p.property('ptr', 'hier'); p.property('ptr', 'napi_env'); const scopeIdentifierSpan = p.span(p.code.span('scopeIdentifierSpan')); const varSizeSpan = p.span(p.code.span('varSizeSpan')); +const varIdSpan = p.span(p.code.span('varIdSpan')); const idSpan = p.span(p.code.span('idSpan')); const commandSpan = p.span(p.code.span('commandSpan')); const timeSpan = p.span(p.code.span('timeSpan')); @@ -53,16 +56,42 @@ const simulationId = p.node('simulationId'); const spaces = [' ', '\n', '\r', '\t']; +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 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, + b: 19, B: 20, r: 21, R: 22 +}); + declaration .match(spaces, declaration) - .select({$scope: 3}, p.invoke(p.code.store('command'), commandSpan.start(scopeType))) - .select({$var: 6}, p.invoke(p.code.store('command'), commandSpan.start(varType))) - .select({ - $comment: 1, $date: 2, $timescale: 4, $upscope: 5, $version: 7 - }, p.invoke(p.code.store('command'), commandSpan.start(inDeclaration))) - .select({ - $enddefinitions: 8 - }, p.invoke(p.code.store('command'), commandSpan.start(enddefinitions))) + .select(cmd('$scope'), + p.invoke(p.code.store('command'), commandSpan.start(scopeType))) + .select(cmd('$var'), + p.invoke(p.code.store('command'), commandSpan.start(varType))) + .select(cmd('$comment $date $timescale $upscope $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')); // $scope @@ -72,9 +101,8 @@ scopeType .otherwise(scopeTypeEnd); scopeTypeEnd - .select({ - begin: 1, fork: 2, function: 3, module: 4, task: 5 - }, p.invoke(p.code.store('type'), scopeIdentifier)) + .select({begin: 1, fork: 2, function: 3, module: 4, task: 5}, + p.invoke(p.code.store('type'), scopeIdentifier)) .otherwise(p.error(2, 'Expected scope type')); scopeIdentifier @@ -124,10 +152,10 @@ varSizeEnd varId .match(spaces, varId) - .otherwise(idSpan.start(varIdEnd)); + .otherwise(varIdSpan.start(varIdEnd)); varIdEnd - .match(spaces, idSpan.end(inDeclaration)) + .match(spaces, varIdSpan.end(inDeclaration)) .skipTo(varIdEnd); // $end @@ -142,18 +170,14 @@ enddefinitions simulation .match([' ', '\n', '\t'], simulation) - .select({ - $dumpall: 9, $dumpoff: 10, $dumpon: 11, $dumpvars: 12, $comment: 1 - }, p.invoke(p.code.store('command'), commandSpan.start(inSimulation))) - .select({ - '#': 13 - }, p.invoke(p.code.store('command'), timeSpan.start(simulationTime))) - .select({ - '0': 14, '1': 15, x: 16, X: 17, Z: 18 - }, p.invoke(p.code.store('command'), idSpan.start(simulationId))) - .select({ - b: 19, B: 20, r: 21, R: 22 - }, p.invoke(p.code.store('command'), vectorSpan.start(simulationVector))) + .select(cmd('$dumpall $dumpoff $dumpon $dumpvars $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'), + p.invoke(p.code.store('command'), idSpan.start(simulationId))) + .select(cmd('b B r R'), + p.invoke(p.code.store('command'), vectorSpan.start(simulationVector))) .otherwise(p.error(4, 'Expected simulation command')); inSimulation diff --git a/bin/find.js b/bin/find.js index 36f4f83..7730ba5 100755 --- a/bin/find.js +++ b/bin/find.js @@ -1,54 +1,44 @@ #!/usr/bin/env node 'use strict'; -const EventEmitter = require('events').EventEmitter; - const fs = require('fs-extra'); const async = require('async'); -const lib = require('../index.js'); +const vcd = require('../index.js'); const dir = './tmp/'; fs.readdir(dir).then(files => { const tt0 = Date.now(); async.eachLimit(files, 1, (fileName, callback) => { - let len = 0; - let chunks = 0; - let goodChunks = 0; + let start = 0; let stop = 0; const t0 = Date.now(); - const ee = new EventEmitter(); + let inst = vcd(); - const cxt = lib.init(); - lib.setTrigger(cxt, 'D1'); - ee.on('trigger', time => { - if (time < 10) { - return 0; - } - if (start == 0) { - start = time; - } else { - stop = time; + // inst.on('$enddefinitions', () => { + // console.log(res); + // console.log(inst.info); + // }); + + inst.onTrigger('D1', time => { + if (time > 10) { + if (start == 0) { + start = time; + } else { + stop = time; + } } }); const s = fs.createReadStream(dir + fileName); - s.on('data', chunk => { - if (lib.execute(cxt, ee.emit.bind(ee), chunk) === 0) { - goodChunks++; - } - len += chunk.length; - chunks++; - }); + + s.on('data', inst.execute); + s.on('end', () => { - // const info = lib.getInfo(cxt); - // console.log(info); - console.log(fileName, chunks, len, goodChunks, (stop - start), - ((Date.now() - t0) / 1000 + 's') - ); + console.log(fileName, (stop - start), ((Date.now() - t0) / 1000 + 's')); callback(); }); }, () => { diff --git a/index.js b/index.js index 2528f70..e9a3d3e 100644 --- a/index.js +++ b/index.js @@ -1,8 +1,38 @@ 'use strict'; +const EventEmitter = require('events').EventEmitter; const pkg = require('./package.json'); - let lib = require('bindings')('vcd.node'); -lib.version = pkg.version; -module.exports = lib; +const version = pkg.version; + +module.exports = () => { + const lifee = new EventEmitter(); + const triee = new EventEmitter(); + + const lifemit = lifee.emit.bind(lifee); + const triemit = triee.emit.bind(triee); + + const info = {path: []}; + + const cxt = lib.init(lifemit, triemit, info); + + const execute = chunk => lib.execute(cxt, lifemit, triemit, info, chunk); + + const on = (id, fn) => { + lifee.on(id, fn); + }; + + const onTrigger = (id, fn) => { + lib.setTrigger(cxt, id); + triee.on(id, fn); + }; + + return { + version, + info, + execute, + on, + onTrigger + }; +}; diff --git a/test/basic.js b/test/basic.js index a6780ed..f4be5b0 100644 --- a/test/basic.js +++ b/test/basic.js @@ -1,52 +1,45 @@ 'use strict'; const expect = require('chai').expect; -const lib = require('../index.js'); +const vcd = require('../index.js'); describe('basic', () => { - it('type', done => { - expect(lib).to.be.an('object'); + + it('typeof vcd', done => { + expect(vcd).to.be.an('function'); done(); }); - it('type', done => { - const cxt = lib.init(); - expect(cxt).to.be.an('object'); + + it('typeof vcd instance', done => { + expect(vcd()).to.be.an('object'); done(); }); + it('fail: foo bar', done => { - const cxt = lib.init(); - expect(lib.execute(cxt, () => {}, Buffer.from(' foo bar ???'))).to.eq(1); - expect(lib.getInfo(cxt)).to.deep.eq({ - error: 1, - reason: 'Expected declaration command', - command: 0, - type: 0, - size: 0, - time: 0, - trigger: '' + const inst = vcd(); + expect(inst.execute(Buffer.from(' foo bar ???'))).to.eq(1); + expect(inst.info).to.deep.eq({ + path: [], + status: 'declaration' }); done(); }); + it('$comment', done => { - const cxt = lib.init(); - expect(lib.execute(cxt, () => {}, Buffer.from( + const inst = vcd(); + expect(inst.execute(Buffer.from( ' \n $comment some text $end $comment more text $end ???' ))).to.eq(1); - expect(lib.getInfo(cxt)).to.deep.eq({ - error: 1, - reason: 'Expected declaration command', - command: 1, - type: 0, - size: 0, - time: 0, - trigger: '' + expect(inst.info).to.deep.eq({ + path: [], + status: 'declaration' }); done(); }); + it('$version', done => { - const cxt = lib.init(); - expect(lib.execute(cxt, () => {}, Buffer.from( -` + const inst = vcd(); + expect(inst.execute(Buffer.from(` $version Generated by VerilatedVcd $end $date Wed Sep 18 22:59:07 2019 $end @@ -61,8 +54,9 @@ $timescale 1ns $end $enddefinitions $end ` -))).to.eq(0); expect(lib.execute(cxt, () => {}, Buffer.from( - ` + ))).to.eq(0); + + expect(inst.execute(Buffer.from(` #1 0"}G @@ -79,17 +73,12 @@ b0000000011110000 {u b0000000000001111 {u ` ))).to.eq(0); - // expect(lib.getInfo(cxt)).to.deep.eq({ - // error: 0, - // reason: 'NO REASON', - // command: 19, - // type: 17, - // size: 64, - // time: 303, - // start: 300, - // stop: 303, - // trigger: '' - // }); + + expect(inst.info).to.deep.eq({ + path: [], + status: 'simulation' + }); + done(); }); }); diff --git a/vcd.c b/vcd.c index 05ea1ef..c636b4a 100644 --- a/vcd.c +++ b/vcd.c @@ -90,39 +90,88 @@ var = name; \ } -// napi_value * var; +#define ASSERT_OBJECT(name, var) { \ + napi_valuetype valuetype; \ + if (napi_typeof(env, name, &valuetype) != napi_ok) { \ + napi_throw(env, name); \ + return 0; \ + } \ + if (valuetype != napi_object) { \ + napi_throw_type_error(env, 0, "Wrong arguments"); \ + return 0; \ + } \ + var = name; \ +} + + METHOD(init) { + napi_value res; + struct vcd_parser_s *state = malloc(sizeof *state); const int32_t error = vcd_parser_init(state); + if (error) { + ASSERT(res, napi_create_int32(env, error, &res)) + return res; + } + + ASSERT_ARGC(3) + ASSERT_FUNCTION(args[0], state->lifee) + ASSERT_FUNCTION(args[1], state->triee) + ASSERT_OBJECT(args[2], state->hier) static char triggerString [256]; state->trigger = triggerString; state->reason = "NO REASON"; + state->napi_env = env; + napi_value status; + ASSERT(status, napi_create_string_latin1(env, "declaration", NAPI_AUTO_LENGTH, &status)) + ASSERT(state->hier, napi_set_named_property(env, state->hier, "status", status)) + + // napi_value hierObj; + // ASSERT(hierObj, napi_create_object(env, &hierObj)) + // state->hier = hierObj; + + // ASSERT(state->hier, napi_create_object(env, &state->hier)) + + ASSERT(res, napi_create_external(env, state, 0, 0, &res)) + return res; +} + +METHOD(done) { + ASSERT_ARGC(4) + struct vcd_parser_s *state; + // last use of all external objects + napi_value lifee, triee, hier; + ASSERT_EXTERNAL(args[0], state) + ASSERT_FUNCTION(args[1], lifee) + ASSERT_FUNCTION(args[2], triee) + ASSERT_OBJECT(args[3], hier) + + // FIXME destroy parser state + + const int32_t error = 0; napi_value res; - if (error) { - ASSERT(res, napi_create_int32(env, error, &res)) - } else { - ASSERT(res, napi_create_external(env, state, 0, 0, &res)) - } + ASSERT(res, napi_create_int32(env, error, &res)) return res; } METHOD(execute) { - ASSERT_ARGC(3) + ASSERT_ARGC(5) struct vcd_parser_s *state; ASSERT_EXTERNAL(args[0], state) - ASSERT_FUNCTION(args[1], state->emit) - ASSERT_BUFFER(args[2], p, plen) + ASSERT_FUNCTION(args[1], state->lifee) + ASSERT_FUNCTION(args[2], state->triee) + ASSERT_OBJECT(args[3], state->hier) + ASSERT_BUFFER(args[4], p, plen) state->napi_env = env; const int32_t error = vcd_parser_execute(state, p, p + plen); - napi_value res; ASSERT(res, napi_create_int32(env, error, &res)) return res; @@ -134,27 +183,24 @@ METHOD(getInfo) { ASSERT_EXTERNAL(args[0], state) napi_value infObj, error, reason, command, type, size, time, trigger; + ASSERT(infObj, napi_create_object(env, &infObj)) - ASSERT(error, napi_create_int32(env, state->error, &error)) - ASSERT(infObj, napi_set_named_property(env, infObj, "error", error)) - ASSERT(reason, napi_create_string_latin1(env, state->reason, NAPI_AUTO_LENGTH, &reason)) - ASSERT(infObj, napi_set_named_property(env, infObj, "reason", reason)) - ASSERT(command, napi_create_int32(env, state->command, &command)) - ASSERT(infObj, napi_set_named_property(env, infObj, "command", command)) - ASSERT(type, napi_create_int32(env, state->type, &type)) - ASSERT(infObj, napi_set_named_property(env, infObj, "type", type)) - ASSERT(size, napi_create_int32(env, state->size, &size)) - ASSERT(infObj, napi_set_named_property(env, infObj, "size", size)) - ASSERT(time, napi_create_int32(env, state->time, &time)) - ASSERT(infObj, napi_set_named_property(env, infObj, "time", time)) - ASSERT(trigger, napi_create_string_latin1(env, state->trigger, NAPI_AUTO_LENGTH, &trigger)) + + // ASSERT(state->hier, napi_create_object(env, &state->hier)) + ASSERT(infObj, napi_set_named_property(env, infObj, "hier", state->hier)) + ASSERT(infObj, napi_set_named_property(env, infObj, "error", error)) + ASSERT(infObj, napi_set_named_property(env, infObj, "reason", reason)) + ASSERT(infObj, napi_set_named_property(env, infObj, "command", command)) + ASSERT(infObj, napi_set_named_property(env, infObj, "type", type)) + ASSERT(infObj, napi_set_named_property(env, infObj, "size", size)) + ASSERT(infObj, napi_set_named_property(env, infObj, "time", time)) ASSERT(infObj, napi_set_named_property(env, infObj, "trigger", trigger)) return infObj; @@ -173,6 +219,7 @@ METHOD(setTrigger) { napi_value Init(napi_env env, napi_value exports) { DECLARE_NAPI_METHOD("init", init) + DECLARE_NAPI_METHOD("done", done) DECLARE_NAPI_METHOD("execute", execute) DECLARE_NAPI_METHOD("getInfo", getInfo) DECLARE_NAPI_METHOD("setTrigger", setTrigger) diff --git a/vcd_spans.c b/vcd_spans.c index 3ecaaa8..5a633b3 100644 --- a/vcd_spans.c +++ b/vcd_spans.c @@ -3,6 +3,11 @@ #include "vcd_parser.h" #include +#define ASSERT(val, expr) \ + if (expr != napi_ok) { \ + napi_throw(env, val); \ + } + int stringEq ( const unsigned char* gold, const unsigned char* p, @@ -19,56 +24,65 @@ int stringEq ( return 1; } -int commandSpan(vcd_parser_t* s, const unsigned char* p, const unsigned char* endp) { - return 0; -}; +int commandSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char* endp) { + napi_env env = state->napi_env; -int scopeIdentifierSpan(vcd_parser_t* s, const unsigned char* p, const unsigned char* endp) { - return 0; -}; + if (state->command == 5) { + // printf(")"); + return 0; + } + + if (state->command == 8) { // $enddefinitions + napi_value status; + ASSERT(status, napi_create_string_latin1(env, "simulation", NAPI_AUTO_LENGTH, &status)) + ASSERT(state->hier, napi_set_named_property(env, state->hier, "status", status)) + + napi_value undefined, eventName, eventPayload, return_val; + ASSERT(undefined, napi_get_undefined(env, &undefined)) + ASSERT(eventName, napi_create_string_latin1(env, "$enddefinitions", NAPI_AUTO_LENGTH, &eventName)) + // ASSERT(eventPayload, napi_create_string_latin1(env, "payload", NAPI_AUTO_LENGTH, &eventPayload)) + napi_value* argv[] = { &eventName }; // , &eventPayload }; + ASSERT(state->lifee, napi_call_function(env, undefined, state->lifee, 1, *argv, &return_val)) + return 0; + } -int varSizeSpan(vcd_parser_t* s, const unsigned char* p, const unsigned char* endp) { - s->size = strtol((const char *)p, (char **)&endp, 10); return 0; -}; +} + +int scopeIdentifierSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char* endp) { + // printf("(%d:%d", state->type, state->size); + return 0; +} + +int varSizeSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char* endp) { + state->size = strtol((const char *)p, (char **)&endp, 10); + return 0; +} + +int varIdSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char* endp) { + // printf(" %d", state->type); + return 0; +} int idSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char* endp) { + napi_env env = state->napi_env; if (stringEq((state->trigger), p, endp)) { - napi_value undefined; - if (napi_get_undefined(state->napi_env, &undefined) != napi_ok) { - napi_throw(state->napi_env, undefined); - return 0; - } - - napi_value eventName; - if (napi_create_string_latin1(state->napi_env, "trigger", NAPI_AUTO_LENGTH, &eventName) != napi_ok) { - napi_throw(state->napi_env, eventName); - return 0; - }; - - napi_value eventPayload; - if (napi_create_int32(state->napi_env, state->time, &eventPayload) != napi_ok) { - napi_throw(state->napi_env, eventPayload); - return 0; - }; - + napi_value undefined, eventName, eventPayload, return_val; + ASSERT(undefined, napi_get_undefined(env, &undefined)) + ASSERT(eventName, napi_create_string_latin1(env, state->trigger, NAPI_AUTO_LENGTH, &eventName)) + ASSERT(eventPayload, napi_create_int32(env, state->time, &eventPayload)) napi_value* argv[] = { &eventName, &eventPayload }; - - napi_value return_val; - if (napi_call_function(state->napi_env, undefined, state->emit, 2, *argv, &return_val) != napi_ok) { - napi_throw(state->napi_env, state->emit); - return 0; - } + ASSERT(state->triee, napi_call_function(env, undefined, state->triee, 2, *argv, &return_val)) } return 0; -}; +} -int vectorSpan(vcd_parser_t* s, const unsigned char* p, const unsigned char* endp) { +int vectorSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char* endp) { // printf("{%.*s}", (int)(endp - p - 1), p); return 0; -}; +} -int timeSpan(vcd_parser_t* s, const unsigned char* p, const unsigned char* endp) { - s->time = strtol((const char *)p, (char **)&endp, 10); +int timeSpan(vcd_parser_t* state, const unsigned char* p, const unsigned char* endp) { + state->time = strtol((const char *)p, (char **)&endp, 10); return 0; -}; +}