1
0
forked from GitHub/gf-core
Files
gf-core/src/runtime/javascript/index.ts
2021-10-04 15:07:23 +02:00

234 lines
8.4 KiB
TypeScript

import errno from './errno'
import ffi from 'ffi'
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
}
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 (errPtr: Pointer): void {
const err = errPtr.deref()
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}: ${err.msg}`)
// PGF_EXN_PGF_ERROR
case 2:
throw new PGFError(err.msg)
// PGF_EXN_OTHER_ERROR
case 3:
throw new Error(err.msg)
default:
throw new Error(`unknown error type: ${err.type}`)
}
}
function PgfText_AsString (txtPtr: Pointer) {
const txtSize = txtPtr.deref().size
const charPtr = ref.reinterpret(txtPtr, txtSize, ref.types.size_t.size)
return charPtr.toString('utf8')
}
// ----------------------------------------------------------------------------
// PGF grammar object
export class PGFGrammar {
readonly db: Pointer
readonly revision: number
constructor (db: Pointer, revision: number) {
this.db = db
this.revision = revision
}
// NB the library user is responsible for calling this
release () {
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)
handleError(err)
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
}
}
// ----------------------------------------------------------------------------
// PGF module functions
function readPGF (path: string): PGFGrammar {
const rev = ref.alloc(PgfRevision) as Pointer
const err = ref.alloc(PgfExn) as Pointer
const db = runtime.pgf_read_pgf(path, rev, err)
handleError(err)
return new PGFGrammar(db, rev.deref())
}
function bootNGF (pgf_path: string, ngf_path: string): PGFGrammar {
const rev = ref.alloc(PgfRevision) as Pointer
const err = ref.alloc(PgfExn) as Pointer
const db = runtime.pgf_boot_ngf(pgf_path, ngf_path, rev, err)
handleError(err)
return new PGFGrammar(db, rev.deref())
}
// ----------------------------------------------------------------------------
// Exposed library API
export default {
PGFError,
readPGF,
bootNGF
}
// ----------------------------------------------------------------------------
// Quick testing
// const gr: PGF = readPGF('../haskell/tests/basic.pgf')
// console.log(gr.getCategories())