/* eslint-disable @typescript-eslint/naming-convention */ import errno from './errno' import { Expr } from './expr' import { unmarshaller, PgfUnmarshaller, PgfExpr, PgfLiteral } from './ffi' import ffi from 'ffi-napi' import ref, { Pointer } from 'ref-napi' import ref_struct, { StructObject } from 'ref-struct-di' const Struct = ref_struct(ref) // ---------------------------------------------------------------------------- // 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.void const PgfDBPtr = ref.refType(PgfDB) const PgfRevision = ref.refType(ref.types.void) 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 PgfItorPtr = ref.refType(ref.types.void) 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 interface PgfExnType { type: number code: number msg: string | null } class PGFError extends Error { constructor (message: string) { super(message) // https://stackoverflow.com/a/65243177/98600 Object.setPrototypeOf(this, PGFError.prototype) } } // ---------------------------------------------------------------------------- // FFI const runtime = ffi.Library('libpgf', { pgf_read_pgf: [PgfDBPtr, [ref.types.CString, 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]] }) // ---------------------------------------------------------------------------- // Helpers function handleError (err: StructObject): void { switch (err.type) { // PGF_EXN_NONE case 0: return // PGF_EXN_SYSTEM_ERROR case 1: { const desc = errno.lookup(err.code) throw new Error(`${desc as string}: ${err.msg as string}`) } // PGF_EXN_PGF_ERROR case 2: throw new PGFError(err.msg as string) // PGF_EXN_OTHER_ERROR case 3: throw new Error(err.msg as string) default: throw new Error(`unknown error type: ${err.type}`) } } function PgfText_AsString (txtPtr: Pointer): string { const txtSize = txtPtr.deref().size const charPtr = ref.reinterpret(txtPtr, txtSize, ref.types.size_t.size) return charPtr.toString('utf8') } function PgfText_FromString (str: string): Pointer { const strbuf = Buffer.from(str, 'utf8') const size = strbuf.length // size in bytes, not chars const sizebuf = ref.alloc(ref.types.size_t, size) const txtbuf = Buffer.alloc(ref.types.size_t.size + size + 1) sizebuf.copy(txtbuf, 0, 0) strbuf.copy(txtbuf, ref.types.size_t.size, 0) return txtbuf as Pointer } // ---------------------------------------------------------------------------- // PGF grammar object export class PGFGrammar { readonly db: Pointer readonly revision: Pointer constructor (db: Pointer, revision: Pointer) { this.db = db this.revision = revision } // NB the library user is responsible for calling this release (): void { runtime.pgf_free_revision(this.db, this.revision.deref()) } getAbstractName (): string { const err = new PgfExn() const txt = runtime.pgf_abstract_name(this.db, this.revision.deref(), err.ref()) handleError(err) return PgfText_AsString(txt) } getCategories (): string[] { const cats: string[] = [] const callback = ffi.Callback(ref.types.void, [PgfItorPtr, PgfTextPtr, voidPtr, PgfExnPtr], function (self: Pointer, key: Pointer, value: Pointer, err: Pointer) { // eslint-disable-line @typescript-eslint/no-unused-vars const k = PgfText_AsString(key) cats.push(k) }) const err = new PgfExn() runtime.pgf_iter_categories(this.db, this.revision.deref(), callback.ref() as Pointer, err.ref()) handleError(err) return cats } getFunctions (): string[] { const funs: string[] = [] const callback = ffi.Callback(ref.types.void, [PgfItorPtr, PgfTextPtr, voidPtr, PgfExnPtr], function (self: Pointer, key: Pointer, value: Pointer, err: Pointer) { // eslint-disable-line @typescript-eslint/no-unused-vars const k = PgfText_AsString(key) funs.push(k) }) const err = new PgfExn() runtime.pgf_iter_functions(this.db, this.revision.deref(), callback.ref() as Pointer, err.ref()) handleError(err) return funs } categoryProbability (cat: string): number | undefined { const catname = PgfText_FromString(cat) const err = new PgfExn() const prob = runtime.pgf_category_prob(this.db, this.revision.deref(), catname, err.ref()) handleError(err) if (prob === Infinity) { return undefined } else { return prob } } functionProbability (fun: string): number | undefined { const funname = PgfText_FromString(fun) const err = new PgfExn() const prob = runtime.pgf_function_prob(this.db, this.revision.deref(), funname, err.ref()) handleError(err) if (prob === Infinity) { return undefined } else { return prob } } functionIsConstructor (fun: string): boolean { const funname = PgfText_FromString(fun) const err = new PgfExn() const isCon = runtime.pgf_function_is_constructor(this.db, this.revision.deref(), funname, err.ref()) handleError(err) return Boolean(isCon) } functionsByCategory (cat: string): string[] { const catname = PgfText_FromString(cat) const funs: string[] = [] const callback = ffi.Callback(ref.types.void, [PgfItorPtr, PgfTextPtr, voidPtr, PgfExnPtr], function (self: Pointer, key: Pointer, value: Pointer, err: Pointer) { // eslint-disable-line @typescript-eslint/no-unused-vars const k = PgfText_AsString(key) funs.push(k) }) const err = new PgfExn() runtime.pgf_iter_functions_by_cat(this.db, this.revision.deref(), catname, callback.ref() as Pointer, err.ref()) handleError(err) return funs } } // ---------------------------------------------------------------------------- // PGF module functions function readPGF (path: string): PGFGrammar { const rev = ref.alloc(PgfRevisionPtr) const err = new PgfExn() const db = runtime.pgf_read_pgf(path, rev, err.ref()) handleError(err) return new PGFGrammar(db, rev) } function bootNGF (pgf_path: string, ngf_path: string): PGFGrammar { const rev = ref.alloc(PgfRevisionPtr) const err = new PgfExn() const db = runtime.pgf_boot_ngf(pgf_path, ngf_path, rev, err.ref()) handleError(err) return new PGFGrammar(db, rev) } function readNGF (path: string): PGFGrammar { const rev = ref.alloc(PgfRevisionPtr) const err = new PgfExn() const db = runtime.pgf_read_ngf(path, rev, err.ref()) handleError(err) return new PGFGrammar(db, rev) } function newNGF (abstract_name: string, path?: string): PGFGrammar { const absname = PgfText_FromString(abstract_name) const fpath = path != null ? path : null const rev = ref.alloc(PgfRevisionPtr) const err = new PgfExn() const db = runtime.pgf_new_ngf(absname, fpath, rev, err.ref()) handleError(err) return new PGFGrammar(db, rev) } function readExpr (str: string): Expr { const txt = PgfText_FromString(str) const expr = runtime.pgf_read_expr(txt, unmarshaller.ref()) console.log('expr', expr) return new Expr() } // ---------------------------------------------------------------------------- // Exposed library API export default { PGFError, readPGF, bootNGF, readNGF, newNGF, readExpr }