From 0e011955be501fb9e485fd1caa0d124a6eb26fbf Mon Sep 17 00:00:00 2001 From: "John J. Camilleri" Date: Fri, 8 Oct 2021 15:06:34 +0200 Subject: [PATCH] Add tests for reading & equality of various expressions --- src/runtime/javascript/expr.ts | 5 +- src/runtime/javascript/ffi.ts | 10 +- src/runtime/javascript/index.ts | 66 ++++++++- src/runtime/javascript/tests/basic.test.ts | 149 +++++++++++++++++---- 4 files changed, 193 insertions(+), 37 deletions(-) diff --git a/src/runtime/javascript/expr.ts b/src/runtime/javascript/expr.ts index 663c92ae9..e598b55c5 100644 --- a/src/runtime/javascript/expr.ts +++ b/src/runtime/javascript/expr.ts @@ -71,9 +71,10 @@ export class ExprLit extends Expr { export class ExprMeta extends Expr { id: number - constructor (i: number) { + constructor (i?: number) { super() - this.id = i + if (i != null) this.id = i + else this.id = 0 } } diff --git a/src/runtime/javascript/ffi.ts b/src/runtime/javascript/ffi.ts index b5190a547..4dd78a416 100644 --- a/src/runtime/javascript/ffi.ts +++ b/src/runtime/javascript/ffi.ts @@ -159,9 +159,9 @@ export function PgfText_FromString (str: string): Pointer { // Un/marshalling const eabs = ffi.Callback(PgfExpr, [PgfUnmarshaller, ref.types.bool, PgfTextPtr, PgfExpr], - function (self, btype: boolean, name: Pointer, body: Pointer): Pointer { + function (self, btype: boolean, name: Pointer, body: Pointer): Pointer { const jsname = PgfText_AsString(name) - const obj = new ExprAbs(btype, jsname, body) // TODO check body + const obj = new ExprAbs(btype, jsname, body.deref()) const buf = ref.alloc(ref.types.Object) as Pointer ref.writeObject(buf, 0, obj) return buf @@ -169,7 +169,7 @@ const eabs = ffi.Callback(PgfExpr, [PgfUnmarshaller, ref.types.bool, PgfTextPtr, const eapp = ffi.Callback(PgfExpr, [PgfUnmarshaller, PgfExpr, PgfExpr], function (self, fun: Pointer, arg: Pointer): Pointer { - const obj = new ExprApp(fun, arg) // TODO check + const obj = new ExprApp(fun.deref(), arg.deref()) const buf = ref.alloc(ref.types.Object) as Pointer ref.writeObject(buf, 0, obj) return buf @@ -211,7 +211,7 @@ const evar = ffi.Callback(PgfExpr, [PgfUnmarshaller, ref.types.int], const etyped = ffi.Callback(PgfExpr, [PgfUnmarshaller, PgfExpr, PgfType], function (self, expr: Pointer, type: Pointer): Pointer { - const obj = new ExprTyped(expr, type.deref()) // TODO check + const obj = new ExprTyped(expr.deref(), type.deref()) const buf = ref.alloc(ref.types.Object) as Pointer ref.writeObject(buf, 0, obj) return buf @@ -219,7 +219,7 @@ const etyped = ffi.Callback(PgfExpr, [PgfUnmarshaller, PgfExpr, PgfType], const eimplarg = ffi.Callback(PgfExpr, [PgfUnmarshaller, PgfExpr], function (self, expr: Pointer): Pointer { - const obj = new ExprImplArg(expr) // TODO check + const obj = new ExprImplArg(expr.deref()) const buf = ref.alloc(ref.types.Object) as Pointer ref.writeObject(buf, 0, obj) return buf diff --git a/src/runtime/javascript/index.ts b/src/runtime/javascript/index.ts index d44c92a07..753484eeb 100644 --- a/src/runtime/javascript/index.ts +++ b/src/runtime/javascript/index.ts @@ -1,7 +1,20 @@ /* eslint-disable @typescript-eslint/naming-convention */ import errno from './errno' -import { Expr, ExprLit } from './expr' +import { + Type, + Hypo, + Expr, + ExprAbs, + ExprApp, + ExprLit, + ExprMeta, + ExprFun, + ExprVar, + ExprTyped, + ExprImplArg, + Literal +} from './expr' import { voidPtr, PgfRevisionPtr, @@ -67,7 +80,7 @@ function handleError (err: StructObject): void { // ---------------------------------------------------------------------------- // PGF grammar object -export class PGFGrammar { +class PGFGrammar { readonly db: Pointer readonly revision: Pointer @@ -198,6 +211,15 @@ function newNGF (abstract_name: string, path?: string): PGFGrammar { return new PGFGrammar(db, rev) } +function readType (str: string): Type { + const txt = PgfText_FromString(str) + const type = runtime.pgf_read_type(txt, unmarshaller.ref()) + if (ref.isNull(type)) { + throw new PGFError('unable to parse type') + } + return ref.readObject(type) as Type +} + function readExpr (str: string): Expr { const txt = PgfText_FromString(str) const expr = runtime.pgf_read_expr(txt, unmarshaller.ref()) @@ -210,15 +232,51 @@ function readExpr (str: string): Expr { // ---------------------------------------------------------------------------- // Exposed library API -export default { +export { + PGFGrammar, PGFError, readPGF, bootNGF, readNGF, newNGF, + readType, readExpr, + Type, + Hypo, Expr, - ExprLit + ExprAbs, + ExprApp, + ExprLit, + ExprMeta, + ExprFun, + ExprVar, + ExprTyped, + ExprImplArg, + Literal +} +export default { + PGFGrammar, + PGFError, + readPGF, + bootNGF, + readNGF, + newNGF, + + readType, + readExpr, + + Type, + Hypo, + Expr, + ExprAbs, + ExprApp, + ExprLit, + ExprMeta, + ExprFun, + ExprVar, + ExprTyped, + ExprImplArg, + Literal } diff --git a/src/runtime/javascript/tests/basic.test.ts b/src/runtime/javascript/tests/basic.test.ts index 3090dd360..a01046fe2 100644 --- a/src/runtime/javascript/tests/basic.test.ts +++ b/src/runtime/javascript/tests/basic.test.ts @@ -1,4 +1,19 @@ -import PGF, { PGFGrammar } from '../index' +import PGF, { + PGFError, + PGFGrammar, + // Type, + // Hypo, + // Expr, + // ExprAbs, + ExprApp, + ExprLit, + ExprMeta, + ExprFun, + // ExprVar, + // ExprTyped, + ExprImplArg + // Literal +} from '../index' import fs from 'fs' // ---------------------------------------------------------------------------- @@ -17,7 +32,7 @@ describe('readPGF', () => { test('GF', () => { expect(() => { PGF.readPGF('../haskell/tests/basic.gf') - }).toThrow(PGF.PGFError) + }).toThrow(PGFError) }) test('NGF', () => { @@ -29,7 +44,7 @@ describe('readPGF', () => { PGF.bootNGF('../haskell/tests/basic.pgf', './basic.ngf') expect(() => { PGF.readPGF('./basic.ngf') - }).toThrow(PGF.PGFError) + }).toThrow(PGFError) }) }) @@ -57,13 +72,13 @@ describe('bootNGF', () => { test('GF', () => { expect(() => { PGF.bootNGF('../haskell/tests/basic.gf', './abc.ngf') - }).toThrow(PGF.PGFError) + }).toThrow(PGFError) }) test('NGF', () => { expect(() => { PGF.bootNGF('./basic.ngf', './abc.ngf') - }).toThrow(PGF.PGFError) + }).toThrow(PGFError) }) test('existing', () => { @@ -98,13 +113,13 @@ describe('readNGF', () => { test('GF', () => { expect(() => { PGF.readNGF('../haskell/tests/basic.gf') - }).toThrow(PGF.PGFError) + }).toThrow(PGFError) }) test('PGF', () => { expect(() => { PGF.readNGF('../haskell/tests/basic.pgf') - }).toThrow(PGF.PGFError) + }).toThrow(PGFError) }) }) @@ -209,88 +224,170 @@ describe('abstract syntax', () => { // ---------------------------------------------------------------------------- +describe('types', () => { + test('invalid', () => { + expect(() => { + PGF.readType('->') + }).toThrow(PGFError) + }) +}) + +// ---------------------------------------------------------------------------- + describe('expressions', () => { test('invalid', () => { expect(() => { PGF.readExpr('->') - }).toThrow(PGF.PGFError) + }).toThrow(PGFError) }) describe('literals', () => { test('small integer', () => { const e1 = PGF.readExpr('123') - const e2 = new PGF.ExprLit(123) - const e3 = new PGF.ExprLit(456) + const e2 = new ExprLit(123) + const e3 = new ExprLit(456) expect(e1).toEqual(e2) expect(e1).not.toEqual(e3) }) test('negative integer', () => { const e1 = PGF.readExpr('-123') - const e2 = new PGF.ExprLit(-123) - const e3 = new PGF.ExprLit(-456) + const e2 = new ExprLit(-123) + const e3 = new ExprLit(-456) expect(e1).toEqual(e2) expect(e1).not.toEqual(e3) }) test('big integer', () => { const e1 = PGF.readExpr('774763251095801167872') - const e2 = new PGF.ExprLit(BigInt('774763251095801167872')) + const e2 = new ExprLit(BigInt('774763251095801167872')) expect(e1).toEqual(e2) }) test('negative big integer', () => { const e1 = PGF.readExpr('-774763251095801167872') - const e2 = new PGF.ExprLit(BigInt('-774763251095801167872')) + const e2 = new ExprLit(BigInt('-774763251095801167872')) expect(e1).toEqual(e2) }) test('really big integer', () => { const e1 = PGF.readExpr('7747632510958011678729003251095801167999') - const e2 = new PGF.ExprLit(BigInt('7747632510958011678729003251095801167999')) - const e3 = new PGF.ExprLit(BigInt('7747632510958011678729003251095801167990')) + const e2 = new ExprLit(BigInt('7747632510958011678729003251095801167999')) + const e3 = new ExprLit(BigInt('7747632510958011678729003251095801167990')) expect(e1).toEqual(e2) expect(e1).not.toEqual(e3) }) test('negative really big integer', () => { const e1 = PGF.readExpr('-7747632510958011678729003251095801167999') - const e2 = new PGF.ExprLit(BigInt('-7747632510958011678729003251095801167999')) - const e3 = new PGF.ExprLit(BigInt('-7747632510958011678729003251095801167990')) + const e2 = new ExprLit(BigInt('-7747632510958011678729003251095801167999')) + const e3 = new ExprLit(BigInt('-7747632510958011678729003251095801167990')) expect(e1).toEqual(e2) expect(e1).not.toEqual(e3) }) test('float', () => { const e1 = PGF.readExpr('3.142') - const e2 = new PGF.ExprLit(3.142) - const e3 = new PGF.ExprLit(2.014) + const e2 = new ExprLit(3.142) + const e3 = new ExprLit(2.014) expect(e1).toEqual(e2) expect(e1).not.toEqual(e3) }) test('negative float', () => { const e1 = PGF.readExpr('-3.142') - const e2 = new PGF.ExprLit(-3.142) - const e3 = new PGF.ExprLit(-2.014) + const e2 = new ExprLit(-3.142) + const e3 = new ExprLit(-2.014) expect(e1).toEqual(e2) expect(e1).not.toEqual(e3) }) test('string', () => { const e1 = PGF.readExpr('"abc"') - const e2 = new PGF.ExprLit('abc') - const e3 = new PGF.ExprLit('def') + const e2 = new ExprLit('abc') + const e3 = new ExprLit('def') expect(e1).toEqual(e2) expect(e1).not.toEqual(e3) }) test('string unicode', () => { const e1 = PGF.readExpr('"açġħ"') - const e2 = new PGF.ExprLit('açġħ') - const e3 = new PGF.ExprLit('acgh') + const e2 = new ExprLit('açġħ') + const e3 = new ExprLit('acgh') expect(e1).toEqual(e2) expect(e1).not.toEqual(e3) }) }) + + describe('functions', () => { + test('simple', () => { + const e1 = PGF.readExpr('f') + expect(e1).toBeInstanceOf(ExprFun) + expect((e1 as ExprFun).name).toEqual('f') + + const e2 = new ExprFun('f') + const e3 = new ExprFun('g') + expect(e1).toEqual(e2) + expect(e1).not.toEqual(e3) + }) + + test('application 1', () => { + const e1 = PGF.readExpr('f x y') + expect(e1).toBeInstanceOf(ExprApp) + expect((e1 as ExprApp).arg).toEqual(new ExprFun('y')) + + const e2 = new ExprApp( + new ExprApp( + new ExprFun('f'), + new ExprFun('x') + ), new ExprFun('y') + ) + expect(e1).toEqual(e2) + }) + + test('application 2', () => { + const e1 = PGF.readExpr('f (g x)') + const e2 = new ExprApp( + new ExprFun('f'), + new ExprApp( + new ExprFun('g'), + new ExprFun('x') + ) + ) + expect(e1).toEqual(e2) + }) + + test('application 3', () => { + const e1 = PGF.readExpr('f {g x}') + const e2 = new ExprApp( + new ExprFun('f'), + new ExprImplArg( + new ExprApp( + new ExprFun('g'), + new ExprFun('x') + ) + ) + ) + expect(e1).toEqual(e2) + }) + }) + + describe('variables', () => { + test('meta 1', () => { + const e1 = PGF.readExpr('?') + expect(e1).toBeInstanceOf(ExprMeta) + const e2 = new ExprMeta() + const e3 = new ExprMeta(0) + const e4 = new ExprMeta(1) + expect(e1).toEqual(e2) + expect(e1).toEqual(e3) + expect(e1).not.toEqual(e4) + }) + + test('meta 2', () => { + const e1 = PGF.readExpr('?42') + const e2 = new ExprMeta(42) + expect(e1).toEqual(e2) + }) + }) })