diff options
Diffstat (limited to 'src/eval.ts')
| -rw-r--r-- | src/eval.ts | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/src/eval.ts b/src/eval.ts new file mode 100644 index 0000000..4ba7033 --- /dev/null +++ b/src/eval.ts @@ -0,0 +1,140 @@ +import { Type } from 'ts-morph' +import { promises as fs } from 'fs' +import readline from 'readline'; + +import { match } from './util'; +import { Ctx } from './types'; + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + terminal: false +}); + +const readLineFromStdin = (): Promise<string> => new Promise((res) => + rl.on('line', res)) + +export const evaluateType = async (ctx: Ctx, effTyp: Type): Promise<string[]> => { + const name = effTyp.getSymbol()?.getName() + + return match(name, { + DefineEffect: async () => { + const [nameTyp, exprTyp] = effTyp.getTypeArguments() + const name = nameTyp?.getLiteralValue() as string + const exprStr = exprTyp?.getLiteralValue() as string + + ctx.addCustomEffect(name, exprStr) + return [] + }, + + Print: async () => { + console.log(...effTyp.getTypeArguments().map(ctx.typeToString)); + return [] + }, + + PutString: async () => { + const [strinTyp] = effTyp.getTypeArguments() + const typString = ctx.typeToString(strinTyp) + const string = JSON.parse(!typString.startsWith('"') ? `"${typString}"` : typString) + process.stdout.write(string); + return [] + }, + + Debug: async () => { + const [labelTyp, valueTyp] = effTyp.getTypeArguments() + const label = JSON.parse(ctx.typeToString(labelTyp)) + const value = ctx.typeToString(valueTyp) + console.log(label, value) + const [resultKey, _] = ctx.createResult(JSON.stringify(value)) + return [resultKey] + }, + + ReadFile: async () => { + const [pathTyp] = effTyp.getTypeArguments() + const filePath = JSON.parse(ctx.typeToString(pathTyp)) + const contents = await fs.readFile(filePath, 'utf-8') + const [resultKey, _] = ctx.createResult(JSON.stringify(contents)) + return [resultKey] + }, + + WriteFile: async () => { + const [pathTyp, contentsTyp] = effTyp.getTypeArguments() + const filePath = JSON.parse(ctx.typeToString(pathTyp)) + const contents = JSON.parse(ctx.typeToString(contentsTyp)) + await fs.writeFile(filePath, contents) + return [] + }, + + Bind: async () => { + const [inputTyp, chainToKind] = effTyp.getTypeArguments() + const [resultKey] = inputTyp ? await evaluateType(ctx, inputTyp) : [] + + const [_, compNode] = ctx.createResult( + `(${ctx.typeToString(chainToKind)} & { input: (${ctx.getResultExpr(resultKey)})['output'] })['return']`) + // TODO: Avoid using getTypeAtLocation? + const compTyp = compNode?.getType().getProperty('output')?.getTypeAtLocation(ctx.entryPoint) + + return compTyp ? await evaluateType(ctx, compTyp) : [] + }, + + GetEnv: async () => { + const [envTyp] = effTyp.getTypeArguments() + const envName = JSON.parse(ctx.typeToString(envTyp)) + const [resultKey, _] = ctx.createResult(`${JSON.stringify(process.env[envName] ?? '')}`) + return [resultKey] + }, + + GetArgs: async () => { + const [resultKey, _] = ctx.createResult(`${JSON.stringify(process.argv.slice(2))}`) + return [resultKey] + }, + + ReadLine: async () => { + const line = await readLineFromStdin() + const [resultKey, _] = ctx.createResult(`${JSON.stringify(line)}`) + return [resultKey] + }, + + JsExpr: async () => { + const [exprTyp] = effTyp.getTypeArguments() + const exprStr = JSON.parse(ctx.typeToString(exprTyp)) + const result = eval(`JSON.stringify(${exprStr})`) + const [resultKey, _] = ctx.createResult(`${result}`) + return [resultKey] + }, + + Seq: async () => { + const [effectTyps] = effTyp.getTypeArguments() + const effectResults = await evalList(ctx, effectTyps?.getTupleElements() ?? []) + const [resultKey, _] = ctx.createResult(`[ + ${effectResults.map(ctx.getResultExpr).join(', ')} + ]`) + return [resultKey] + }, + + Do: async () => { + const [effectTyps] = effTyp.getTypeArguments() + const effectResults = await evalList(ctx, effectTyps?.getTupleElements() ?? []) + const lastResKey = effectResults[effectResults.length - 1] + const [resultKey, _] = ctx.createResult(`${ctx.getResultExpr(lastResKey)}['output']`) + return [resultKey] + }, + + _: async () => { + if (name && ctx.hasCustomEffect(name)) { + return ctx.runCustomEffect(name, effTyp.getTypeArguments()) + } + + console.log(`${name} result effect is unhandled`) + return [] + }, + }) +} + +export const evalList = async (ctx: Ctx, effectTyps: Type[]) => { + const effectResults: string[] = [] + for (const item of effectTyps ?? []) { + effectResults.push(...(await evaluateType(ctx, item))) + } + return effectResults +} |
