From cd11925707c42843df195d9b2efb3c77b5de793b Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sat, 14 Jan 2023 17:12:48 +0530 Subject: feat: adds cleanup of result nodes --- src/context.ts | 10 ++++ src/eval-env/builtins.ts | 71 +++--------------------- src/eval.ts | 4 +- src/types.ts | 1 + src/util.ts | 63 +++++++++++++++++++++- stdlib/test.ts | 7 +-- tests/builtins.spec.ts | 138 +++++++++++++++++++++++------------------------ 7 files changed, 152 insertions(+), 142 deletions(-) diff --git a/src/context.ts b/src/context.ts index fe33731..fab6653 100644 --- a/src/context.ts +++ b/src/context.ts @@ -56,6 +56,15 @@ export const createContext = (options: CtxOptions): Ctx => { return [resultKey, node] } + const removeResult = (key?: string) => { + if (!key) return + resultTypeNode + ?.asKind(SyntaxKind.TypeAliasDeclaration) + ?.getChildAtIndexIfKind(3, SyntaxKind.TypeLiteral) + ?.getProperty(key) + ?.remove() + } + const customEffects: Record any> = {} const getTypeValue = (ty: Type | undefined): any => { @@ -97,6 +106,7 @@ export const createContext = (options: CtxOptions): Ctx => { createResult, getResultExpr, + removeResult, printResultNode: () => console.log(resultTypeNode?.print()), addCustomEffect: (name, expr) => { diff --git a/src/eval-env/builtins.ts b/src/eval-env/builtins.ts index 35f8485..c5b0c82 100644 --- a/src/eval-env/builtins.ts +++ b/src/eval-env/builtins.ts @@ -1,63 +1,6 @@ -import { SyntaxKind, Type } from 'ts-morph' +import { Type } from 'ts-morph' import { Ctx } from '../types' -import { evalList } from '../util' - -const applyFunc = (ctx: Ctx, fn: Type | undefined, val: string): Type => { - const resultType = (() => { - const baseTypes = fn - ?.getBaseTypes() - .flatMap((t) => t.getSymbol()?.getName() ?? []) - - if (!!fn?.getProperty('return') || baseTypes?.includes('Kind1')) { - const [_key, resultNode] = ctx.createResult( - `(${ctx.typeToString(fn)} & { input: ${val} })['return']` - ) - return resultNode - ?.getType() - .getProperty('output') - ?.getTypeAtLocation(resultNode) - } else { - const [_key, resultNode] = ctx.createResult( - `ReturnType<${ctx.typeToString(fn)}>` - ) - - const resValueNode = resultNode - ?.asKind(SyntaxKind.PropertySignature) - ?.getChildAtIndexIfKind(2, SyntaxKind.TypeLiteral) - ?.getProperty('output') - ?.getChildAtIndexIfKind(2, SyntaxKind.TypeReference) - - const functionNode = resValueNode - ?.getChildAtIndexIfKind(1, SyntaxKind.SyntaxList) - ?.getFirstChildIfKind(SyntaxKind.FunctionType) - - if (functionNode) { - const typeParameters = functionNode.getTypeParameters() ?? [] - - if (typeParameters.length > 0) { - const constraint = typeParameters[0]?.getConstraint() - if (constraint) { - constraint?.replaceWithText(val) - } else { - typeParameters[0]?.setConstraint(val) - } - } - - return resValueNode?.getType() - } - } - - return undefined - })() - - // TODO: Cleanup unwanted result node values - - if (!resultType) { - throw new Error('Couldnt get result for function application') - } - - return resultType -} +import { applyFunc, evalList } from '../util' export default (ctx: Ctx, args: Type[]) => ({ SetEvalEnvironment: async () => { @@ -129,14 +72,12 @@ export default (ctx: Ctx, args: Type[]) => ({ Bind: ctx.withScope(async () => { const [inputTyp, chainToKind] = args - const [resultKey] = inputTyp ? await ctx.evaluateType(ctx, inputTyp) : [] - - // TODO: Handle resultKey undefined case + let [resultKey] = inputTyp ? await ctx.evaluateType(ctx, inputTyp) : [] const resultType = applyFunc( ctx, chainToKind, - `(${ctx.getResultExpr(resultKey)})['output']` + resultKey ? `(${ctx.getResultExpr(resultKey)})['output']` : 'never' ) return ctx.evaluateType(ctx, resultType) }), @@ -186,7 +127,7 @@ export default (ctx: Ctx, args: Type[]) => ({ return [resultKey] }, - Seq: async () => { + Seq: ctx.withScope(async () => { const [effectTyps] = args const effectResults = await evalList( ctx, @@ -196,7 +137,7 @@ export default (ctx: Ctx, args: Type[]) => ({ ${effectResults.map(ctx.getResultExpr).join(', ')} ]`) return [resultKey] - }, + }), Do: ctx.withScope(async () => { const [effectTyps] = args diff --git a/src/eval.ts b/src/eval.ts index 6eb72a1..37a8008 100644 --- a/src/eval.ts +++ b/src/eval.ts @@ -32,7 +32,7 @@ export const evaluateType = async ( const name = effTyp.getSymbol()?.getName() const args = effTyp.getTypeArguments() - // console.log('>>>>>', name) + //console.log('>>>>>', name) // console.log(ctx.typeToString(effTyp)) // console.log(name, args.map(ctx.typeToString)) @@ -67,7 +67,7 @@ export const evaluateType = async ( return match(name, { ...envEffects(ctx, args), _: async () => { - console.log(ctx.typeToString(effTyp)) + console.log('\n[EffectType]', ctx.typeToString(effTyp), '\n') throw new Error(`${name} effect is not handled`) }, }) diff --git a/src/types.ts b/src/types.ts index 6ea282e..55d2cce 100644 --- a/src/types.ts +++ b/src/types.ts @@ -14,6 +14,7 @@ export interface Ctx { getTypeValue: (ty: Type | undefined) => any createResult: (ty: string) => [string, Node | undefined] + removeResult: (key?: string) => void, getResultExpr: (key?: string) => string printResultNode: () => void diff --git a/src/util.ts b/src/util.ts index f7bb20a..2746746 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,4 +1,4 @@ -import { Type } from 'ts-morph' +import { SyntaxKind, Type } from 'ts-morph' import { Ctx } from './types' export const match = ( @@ -13,3 +13,64 @@ export const evalList = async (ctx: Ctx, effectTyps: Type[]) => { } return effectResults } + +export const applyFunc = (ctx: Ctx, fn: Type | undefined, val: string): Type => { + const resultType = (() => { + const baseTypes = fn + ?.getBaseTypes() + .flatMap((t) => t.getSymbol()?.getName() ?? []) + + if (!!fn?.getProperty('return') || baseTypes?.includes('Kind1')) { + const [key, resultNode] = ctx.createResult( + `(${ctx.typeToString(fn)} & { input: ${val} })['return']` + ) + ctx.removeResult(key) + + return resultNode + ?.getType() + .getProperty('output') + ?.getTypeAtLocation(resultNode) + } else { + const [key, resultNode] = ctx.createResult( + `ReturnType<${ctx.typeToString(fn)}>` + ) + ctx.removeResult(key) + + const resValueNode = resultNode + ?.asKind(SyntaxKind.PropertySignature) + ?.getChildAtIndexIfKind(2, SyntaxKind.TypeLiteral) + ?.getProperty('output') + ?.getChildAtIndexIfKind(2, SyntaxKind.TypeReference) + + const functionNode = resValueNode + ?.getChildAtIndexIfKind(1, SyntaxKind.SyntaxList) + ?.getFirstChildIfKind(SyntaxKind.FunctionType) + + if (functionNode) { + const typeParameters = functionNode.getTypeParameters() ?? [] + + if (typeParameters.length > 0) { + const constraint = typeParameters[0]?.getConstraint() + if (constraint) { + constraint?.replaceWithText(val) + } else { + typeParameters[0]?.setConstraint(val) + } + } + + return resValueNode?.getType() + } + } + + return undefined + })() + + // TODO: Cleanup unwanted result node values + + if (!resultType) { + throw new Error('Couldnt get result for function application') + } + + return resultType +} + diff --git a/stdlib/test.ts b/stdlib/test.ts index a592d87..86ed6a9 100644 --- a/stdlib/test.ts +++ b/stdlib/test.ts @@ -15,15 +15,12 @@ export interface ShowAssertionError<_L extends unknown, _R extends unknown> exte export type AssertEquals = Try< Assert>, - () => Do<[ + () => Do<[ ShowAssertionError, - Throw, + Throw, ]> > export interface AssertEqualsK extends Kind1 { return: AssertEquals } - -// TODO: export interface AssertFails? - diff --git a/tests/builtins.spec.ts b/tests/builtins.spec.ts index cd4b5c4..cbac0d3 100644 --- a/tests/builtins.spec.ts +++ b/tests/builtins.spec.ts @@ -4,93 +4,93 @@ import { PutStringLn } from '../stdlib/stdio' import { DefineEffect, JsExpr, SetEvalEnvironment } from '../stdlib/sys' import { Test, AssertEqualsK, AssertEquals } from '../stdlib/test' -type testBind = Do<[ - PutStringLn<'Bind & BindTo'>, +interface MyEffect<_a extends number, _b extends string, _c extends unknown> extends Effect {} - Test<'should bind value to function and kind', [ - Bind, () => - AssertEquals>, +export type main = [ + SetEvalEnvironment<'test.node'>, - Bind, AssertEqualsK<[1,2,3]>>, - ]>, + PutStringLn<'====================='>, + PutStringLn<'=== Running tests ==='>, + PutStringLn<'=====================\n\n'>, - Test<'should bind label correctly', [ - BindTo<"value", Pure<{ a: 'b', c: { d: 1 } }>>, + Do<[ + PutStringLn<'Bind & BindTo'>, - Bind, AssertEqualsK<{ a: 'b', c: { d: 1 } }>>, - ]>, + Test<'should bind value to function and kind', [ + Bind, () => + AssertEquals>, - Test<'should bind only inside Do scope', [ - Do<[ - BindTo<"value", Pure<"some value">>, - Bind, AssertEqualsK<"some value">>, + Bind, AssertEqualsK<[1,2,3]>>, ]>, - Bind< - Try, <_>() => Pure<"none">>, - AssertEqualsK<"none"> - >, - ]>, + Test<'should bind label correctly', [ + BindTo<"value", Pure<{ a: 'b', c: { d: 1 } }>>, - Test<'should bind only inside Bind scope', [ - Bind< - BindTo<"value", Pure<"some value">>, - <_>() => Bind, AssertEqualsK<"some value">> - >, + Bind, AssertEqualsK<{ a: 'b', c: { d: 1 } }>>, + ]>, - Bind< - Try, <_>() => Pure<"none">>, - AssertEqualsK<"none"> - >, - ]>, -]> + Test<'should bind only inside Do scope', [ + Do<[ + BindTo<"value", Pure<"some value">>, + Bind, AssertEqualsK<"some value">>, + ]>, -type testExcpt = Do<[ - PutStringLn<'Try/Throw'>, + Bind< + Try, <_>() => Pure<"none">>, + AssertEqualsK<"none"> + >, + ]>, + + Test<'should bind only inside Bind scope', [ + Bind< + BindTo<"value", Pure<"some value">>, + <_>() => Bind, AssertEqualsK<"some value">> + >, - Test<'should return the result of effect', [ - Bind, <_>() => Pure<0>>, - AssertEqualsK<20>>, + Bind< + Try, <_>() => Pure<"none">>, + AssertEqualsK<"none"> + >, + ]>, ]>, - Test<'should return the result of error handler', [ - Bind, Pure<20> ]>, <_>() => Pure<0>>, - AssertEqualsK<0>>, + Do<[ + PutStringLn<'Try/Throw'>, - Bind, Pure<20> ]>, () => Pure>, - AssertEqualsK<"wow">>, - ]>, -]> + Test<'should return the result of effect', [ + Bind, <_>() => Pure<0>>, + AssertEqualsK<20>>, + ]>, -interface MyEffect<_a extends number, _b extends string, _c extends unknown> extends Effect {} -type testFFI = Do<[ - PutStringLn<'FFI'>, + Test<'should return the result of error handler', [ + Bind, Pure<20> ]>, <_>() => Pure<0>>, + AssertEqualsK<0>>, - Test<'should return the result of js expression', [ - Bind, AssertEqualsK<28980>>, + Bind, Pure<20> ]>, () => Pure>, + AssertEqualsK<"wow">>, + ]>, ]>, - Test<'should test custom effect', [ - DefineEffect<'MyEffect', `([a, b, c], ctx) => { - return [ - ctx.getTypeValue(a) * 2, - ctx.getTypeValue(b) + '!', - [...ctx.getTypeValue(c), 'wow'], - ] - }`>, - Bind, AssertEqualsK<[ - 8, "hey!", [1, 2, 'wow'], - ]>>, - ]>, -]> + Do<[ + PutStringLn<'FFI'>, -export type main = [ - SetEvalEnvironment<'test.node'>, + Test<'should return the result of js expression', [ + Bind, AssertEqualsK<28980>>, + ]>, - PutStringLn<'====================='>, - PutStringLn<'=== Running tests ==='>, - PutStringLn<'=====================\n\n'>, - testBind, - testExcpt, - testFFI, + Test<'should test custom effect', [ + DefineEffect<'MyEffect', `([a, b, c], ctx) => { + return [ + ctx.getTypeValue(a) * 2, + ctx.getTypeValue(b) + '!', + [...ctx.getTypeValue(c), 'wow'], + ] + }`>, + Bind, AssertEqualsK<[ + 8, "hey!", [1, 2, 'wow'], + ]>>, + ]>, + ]>, + + PutStringLn<"">, ] -- cgit v1.3.1