diff --git a/.github/workflows/build-majestic.yml b/.github/workflows/build-majestic.yml index 7944ffdce..4acfc244b 100644 --- a/.github/workflows/build-majestic.yml +++ b/.github/workflows/build-majestic.yml @@ -58,7 +58,7 @@ jobs: pytest - name: Run JavaScript testsuite - working-directory: ./src/runtime/python + working-directory: ./src/runtime/javascript env: LD_LIBRARY_PATH: /usr/local/lib run: | diff --git a/src/runtime/javascript/index.ts b/src/runtime/javascript/index.ts index 5d0249f02..287e42b85 100644 --- a/src/runtime/javascript/index.ts +++ b/src/runtime/javascript/index.ts @@ -1,31 +1,62 @@ import errno from './errno' import ffi from 'ffi' -import { deref } from 'ref' import ref from 'ref' import Struct from 'ref-struct' // ---------------------------------------------------------------------------- // FFI "Types" +const voidPtr = ref.refType(ref.types.void) +const prob_t = ref.types.float +const size_t = ref.types.size_t + const PgfDB = ref.types.int const PgfDBPtr = ref.refType(PgfDB) + const PgfRevision = ref.types.int const PgfRevisionPtr = ref.refType(PgfRevision) + const PgfExn = Struct({ type: ref.types.int, code: ref.types.int, msg: ref.types.CString }) const PgfExnPtr = ref.refType(PgfExn) + const PgfText = Struct({ size: ref.types.size_t, text: ref.types.char // char[] }) const PgfTextPtr = ref.refType(PgfText) +const PgfItor = Struct({ + fn: voidPtr +}) +const PgfItorPtr = ref.refType(PgfItor) + +const PgfType = ref.types.void // TODO +const PgfTypePtr = ref.refType(PgfType) + +const PgfTypeHypo = ref.types.void // TODO +const PgfTypeHypoPtr = ref.refType(PgfTypeHypo) + +const PgfExpr = ref.types.void // TODO +const PgfLiteral = ref.types.void // TODO + +const PgfPrintContext = ref.types.void // TODO +const PgfPrintContextPtr = ref.refType(PgfPrintContext) + +const PgfUnmarshaller = ref.types.void // TODO +const PgfUnmarshallerPtr = ref.refType(PgfUnmarshaller) + +const PgfMarshaller = ref.types.void // TODO +const PgfMarshallerPtr = ref.refType(PgfMarshaller) + + // ---------------------------------------------------------------------------- // TypeScript Types +// Type definitions for `ref` package don't include extensions to Buffer interface Pointer extends Buffer { deref(): any } @@ -41,8 +72,43 @@ class PGFError extends Error { const runtime = ffi.Library('libpgf', { 'pgf_read_pgf': [ PgfDBPtr, [ ref.types.CString, PgfRevisionPtr, PgfExnPtr ] ], - 'pgf_write_pgf': [ ref.types.void, [ ref.types.CString, PgfDBPtr, PgfRevisionPtr, PgfExnPtr ] ], + 'pgf_boot_ngf': [ PgfDBPtr, [ ref.types.CString, ref.types.CString, PgfRevisionPtr, PgfExnPtr ] ], + 'pgf_read_ngf': [ PgfDBPtr, [ ref.types.CString, PgfRevisionPtr, PgfExnPtr ] ], + 'pgf_new_ngf': [ PgfDBPtr, [ PgfTextPtr, ref.types.CString, PgfRevisionPtr, PgfExnPtr ] ], + 'pgf_write_pgf': [ ref.types.void, [ ref.types.CString, PgfDBPtr, PgfRevision, PgfExnPtr ] ], + + 'pgf_free_revision': [ ref.types.void, [ PgfDBPtr, PgfRevision ] ], + 'pgf_abstract_name': [ PgfTextPtr, [ PgfDBPtr, PgfRevision, PgfExnPtr ] ], + 'pgf_iter_categories': [ ref.types.void, [ PgfDBPtr, PgfRevision, PgfItorPtr, PgfExnPtr ] ], + 'pgf_start_cat': [ PgfType, [ PgfDBPtr, PgfRevision, PgfUnmarshallerPtr, PgfExnPtr ] ], + 'pgf_category_context': [ PgfTypeHypoPtr, [ PgfDBPtr, PgfRevision, PgfTextPtr, ref.refType(ref.types.size_t), PgfUnmarshallerPtr, PgfExnPtr ] ], + 'pgf_category_prob': [ prob_t, [ PgfDBPtr, PgfRevision, PgfTextPtr, PgfExnPtr ] ], + 'pgf_iter_functions': [ ref.types.void, [ PgfDBPtr, PgfRevision, PgfItorPtr, PgfExnPtr ] ], + 'pgf_iter_functions_by_cat': [ ref.types.void, [ PgfDBPtr, PgfRevision, PgfTextPtr, PgfItorPtr, PgfExnPtr ] ], + 'pgf_function_type': [ PgfType, [ PgfDBPtr, PgfRevision, PgfTextPtr, PgfUnmarshallerPtr, PgfExnPtr ] ], + 'pgf_function_is_constructor': [ ref.types.int, [ PgfDBPtr, PgfRevision, PgfTextPtr, PgfExnPtr ] ], + 'pgf_function_prob': [ prob_t, [ PgfDBPtr, PgfRevision, PgfTextPtr, PgfExnPtr ] ], + 'pgf_print_expr': [ PgfTextPtr, [ PgfExpr, PgfPrintContextPtr, ref.types.int, PgfMarshallerPtr ] ], + 'pgf_read_expr': [ PgfExpr, [ PgfTextPtr, PgfUnmarshallerPtr ] ], + 'pgf_read_expr_ex': [ PgfExpr , [ PgfTextPtr, ref.refType(ref.types.CString), PgfUnmarshallerPtr ] ], + 'pgf_expr_prob': [ prob_t , [ PgfDBPtr, PgfRevision, PgfExpr, PgfMarshallerPtr, PgfExnPtr ] ], + 'pgf_print_type': [ PgfTextPtr, [ PgfType, PgfPrintContextPtr, ref.types.int, PgfMarshallerPtr ] ], + 'pgf_print_context': [ PgfTextPtr, [ size_t, PgfTypeHypoPtr, PgfPrintContextPtr, ref.types.int, PgfMarshallerPtr ] ], + 'pgf_read_type': [ PgfType , [ PgfTextPtr, PgfUnmarshallerPtr ] ], + + 'pgf_clone_revision': [ PgfRevision , [ PgfDBPtr, PgfRevision, PgfTextPtr, PgfExnPtr ] ], + 'pgf_commit_revision': [ ref.types.void, [ PgfDBPtr, PgfRevision, PgfExnPtr ] ], + 'pgf_checkout_revision': [ PgfRevision , [ PgfDBPtr, PgfTextPtr, PgfExnPtr ] ], + 'pgf_create_function': [ ref.types.void, [ PgfDBPtr, PgfRevision, PgfTextPtr, PgfType, size_t, prob_t, PgfMarshallerPtr, PgfExnPtr ] ], + 'pgf_drop_function': [ ref.types.void, [ PgfDBPtr, PgfRevision, PgfTextPtr, PgfExnPtr ] ], + 'pgf_create_category': [ ref.types.void, [ PgfDBPtr, PgfRevision, PgfTextPtr, size_t, PgfTypeHypoPtr, prob_t, PgfMarshallerPtr, PgfExnPtr ] ], + 'pgf_drop_category': [ ref.types.void, [ PgfDBPtr, PgfRevision, PgfTextPtr, PgfExnPtr ] ], + + 'pgf_get_global_flag': [ PgfLiteral , [ PgfDBPtr, PgfRevision, PgfTextPtr, PgfUnmarshallerPtr, PgfExnPtr ] ], + 'pgf_set_global_flag': [ ref.types.void, [ PgfDBPtr, PgfRevision, PgfTextPtr, PgfLiteral, PgfMarshallerPtr, PgfExnPtr ] ], + 'pgf_get_abstract_flag': [ PgfLiteral , [ PgfDBPtr, PgfRevision, PgfTextPtr, PgfUnmarshallerPtr, PgfExnPtr ] ], + 'pgf_set_abstract_flag': [ ref.types.void, [ PgfDBPtr, PgfRevision, PgfTextPtr, PgfLiteral, PgfMarshallerPtr, PgfExnPtr ] ], }) // ---------------------------------------------------------------------------- @@ -90,6 +156,11 @@ class PGF { this.revision = 0 } + // NB the library user is responsible for calling this + destructor () { + runtime.pgf_free_revision(this.db, this.revision) + } + getAbstractName () { const err = ref.alloc(PgfExn) as Pointer const txt = runtime.pgf_abstract_name(this.db, this.revision, err) @@ -97,6 +168,32 @@ class PGF { return PgfText_AsString(txt) } + getCategories () { + const cats = new Array() + const callback = ffi.Callback(ref.types.void, [ PgfItorPtr, PgfTextPtr, voidPtr, PgfExnPtr], + function (self: Pointer, key: Pointer, value: Pointer, err: Pointer) { + const k = PgfText_AsString(key) + cats.push(k) + }) + const err = ref.alloc(PgfExn) as Pointer + runtime.pgf_iter_categories(this.db, this.revision, ref.ref(callback), err) + handleError(err) + return cats + } + + getFunctions () { + const funs = new Array() + const callback = ffi.Callback(ref.types.void, [ PgfItorPtr, PgfTextPtr, voidPtr, PgfExnPtr], + function (self: Pointer, key: Pointer, value: Pointer, err: Pointer) { + const k = PgfText_AsString(key) + funs.push(k) + }) + const err = ref.alloc(PgfExn) as Pointer + runtime.pgf_iter_functions(this.db, this.revision, ref.ref(callback), err) + handleError(err) + return funs + } + } // ---------------------------------------------------------------------------- @@ -120,3 +217,9 @@ function readPGF (path: string): PGF { export default { readPGF } + +// ---------------------------------------------------------------------------- +// Quick testing + +// const gr: PGF = readPGF('../haskell/tests/basic.pgf') +// console.log(gr.getCategories()) diff --git a/src/runtime/javascript/jest.config.js b/src/runtime/javascript/jest.config.js index cc59fa8a4..bac771abf 100644 --- a/src/runtime/javascript/jest.config.js +++ b/src/runtime/javascript/jest.config.js @@ -1,5 +1,9 @@ /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ module.exports = { preset: 'ts-jest', - testEnvironment: 'node' -}; + testEnvironment: 'node', + testPathIgnorePatterns: [ + '/dist/', + '/node_modules/' + ] +} diff --git a/src/runtime/javascript/tests/basic.test.ts b/src/runtime/javascript/tests/basic.test.ts index bc68eb29c..26bd6eeb2 100644 --- a/src/runtime/javascript/tests/basic.test.ts +++ b/src/runtime/javascript/tests/basic.test.ts @@ -1,5 +1,7 @@ import PGF from '../index' +// readPGF + test('readPGF successful', () => { let gr = PGF.readPGF('../haskell/tests/basic.pgf') }) @@ -10,7 +12,19 @@ test('readPGF missing file', () => { }).toThrow() }) +// abstract syntax + test('abstract name', () => { let gr = PGF.readPGF('../haskell/tests/basic.pgf') expect(gr.getAbstractName()).toBe('basic') }) + +test('categories', () => { + let gr = PGF.readPGF('../haskell/tests/basic.pgf') + expect(gr.getCategories()).toEqual(['Float','Int','N','P','S','String']) +}) + +test('functions', () => { + let gr = PGF.readPGF('../haskell/tests/basic.pgf') + expect(gr.getFunctions()).toEqual(['c','ind','s','z']) +})