From 2a0815eeb378b0140ef2885514715fb00db7a11e Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sat, 7 Jan 2023 00:53:23 +0530 Subject: refactor: moves stdlib around + package stuff --- src/index.ts | 218 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 src/index.ts (limited to 'src/index.ts') diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..34038bf --- /dev/null +++ b/src/index.ts @@ -0,0 +1,218 @@ +import { Project, ScriptTarget, Type, Node, SyntaxKind } from 'ts-morph' +import path from 'path' +import { promises as fs } from 'fs' +import readline from 'readline'; +import { v4 as uuid } from 'uuid'; +import { match } from './util'; + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + terminal: false +}); + +const readLineFromStdin = (): Promise => new Promise((res) => { + rl.on('line', res) +}) + +const project = new Project({ + compilerOptions: { + target: ScriptTarget.ES3, + }, +}) + +const typeChecker = project.getTypeChecker() + +const [filePath] = process.argv.slice(2) + +if (!filePath) { + throw new Error('Must specify runtime file') +} + +const sourceFile = project.addSourceFileAtPath(path.resolve(filePath)) + +const entryPoint = sourceFile.getExportedDeclarations().get('main')?.[0] + +const typeToString = (ty: Type | undefined): string => + ty ? typeChecker.compilerObject.typeToString(ty.compilerType) : '' + +const typeRefNode = entryPoint?.getLastChild() + +const RESULT_TYPE_NAME = '__$result' + +const [resultTypeNode] = sourceFile.addStatements(`type ${RESULT_TYPE_NAME} = {}`) + +const addResult = (name: string, ty: string): Node | undefined => { + if (resultTypeNode?.isKind(SyntaxKind.TypeAliasDeclaration)) { + const value = resultTypeNode.getChildAtIndex(3) + if (value.isKind(SyntaxKind.TypeLiteral)) { + return value.addProperty({ + name: JSON.stringify(name), + type: `{ output: ${ty} }`, + }) + } + } + return +} + +const customEffects: Record any> = {} + +const evaluateType = async (effTyp: Type, node: Node): Promise => { + const name = effTyp.getSymbol()?.getName() + // console.log(name) + + return match(name, { + DefineEffect: async () => { + const [nameTyp, exprTyp] = effTyp.getTypeArguments() + const name = nameTyp?.getLiteralValue() as string + const exprStr = exprTyp?.getLiteralValue() as string + const func = eval(exprStr) + + Object.assign(customEffects, { [name]: func }) + return [] + }, + + Print: async () => { + console.log(...effTyp.getTypeArguments().map(typeToString)); + return [] + }, + + PutString: async () => { + const [strinTyp] = effTyp.getTypeArguments() + const typString = 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(typeToString(labelTyp)) + const value = typeToString(valueTyp) + console.log(label, value) + // TODO: Return value + return [] + }, + + ReadFile: async () => { + const [pathTyp] = effTyp.getTypeArguments() + const filePath = JSON.parse(typeToString(pathTyp)) + const contents = await fs.readFile(filePath, 'utf-8') + const hash = uuid() + addResult(hash, JSON.stringify(contents)) + return [hash] + }, + + WriteFile: async () => { + const [pathTyp, contentsTyp] = effTyp.getTypeArguments() + const filePath = JSON.parse(typeToString(pathTyp)) + const contents = JSON.parse(typeToString(contentsTyp)) + await fs.writeFile(filePath, contents) + return [] + }, + + Bind: async () => { + const [inputTyp, chainToKind] = effTyp.getTypeArguments() + const [resultKey] = inputTyp ? await evaluateType(inputTyp, node) : [] + + const hash = uuid() + const compNode = addResult(hash, + `(${typeToString(chainToKind)} & { input: ${RESULT_TYPE_NAME}[${JSON.stringify(resultKey)}]['output'] })['return']`) + const compTyp = compNode?.getType().getProperty('output')?.getTypeAtLocation(node) + + return compTyp ? await evaluateType(compTyp, node) : [] + }, + + GetEnv: async () => { + const [envTyp] = effTyp.getTypeArguments() + const envName = JSON.parse(typeToString(envTyp)) + const hash = uuid() + addResult(hash, `${JSON.stringify(process.env[envName] ?? '')}`) + return [hash] + }, + + GetArgs: async () => { + const hash = uuid() + addResult(hash, `${JSON.stringify(process.argv.slice(2))}`) + return [hash] + }, + + ReadLine: async () => { + const line = await readLineFromStdin() + const hash = uuid() + addResult(hash, `${JSON.stringify(line)}`) + return [hash] + }, + + JsExpr: async () => { + const [exprTyp] = effTyp.getTypeArguments() + const exprStr = JSON.parse(typeToString(exprTyp)) + const result = eval(`JSON.stringify(${exprStr})`) + const hash = uuid() + addResult(hash, `${result}`) + return [hash] + }, + + Seq: async () => { + const [effectTyps] = effTyp.getTypeArguments() + const effectResults = await evalList(effectTyps?.getTupleElements() ?? [], node) + const hash = uuid() + addResult(hash, `[ + ${effectResults.map(r => `${RESULT_TYPE_NAME}[${JSON.stringify(r)}]`).join(', ')} + ]`) + return [hash] + }, + + Do: async () => { + const [effectTyps] = effTyp.getTypeArguments() + const effectResults = await evalList(effectTyps?.getTupleElements() ?? [], node) + const resultKey = effectResults[effectResults.length - 1] + const hash = uuid() + addResult(hash, `${RESULT_TYPE_NAME}[${JSON.stringify(resultKey)}]['output']`) + return [hash] + }, + + _: async () => { + if (name && customEffects[name]) { + const out = await customEffects[name]?.(...effTyp.getTypeArguments()) + if (out) { + const hash = uuid() + addResult(hash, `${JSON.stringify(out)}`) + return [hash] + } + } else { + console.log(`${name} result effect is unhandled`) + } + return [] + }, + }) +} + +const evalList = async (effectTyps: Type[], node: Node) => { + const effectResults: string[] = [] + for (const item of effectTyps ?? []) { + effectResults.push(...(await evaluateType(item, node))) + } + return effectResults +} + +const main = async () => { + if (typeRefNode) { + const resultType = entryPoint?.getType() + + if (resultType) { + const effects = resultType.isTuple() ? resultType.getTupleElements() : [resultType] + await evalList(effects, typeRefNode) + } + } +} + +main() + .then(() => { + // console.log(entryPoint?.print()) + // console.log(resultTypeNode?.print()) + rl.close() + process.exit(0) + }) + .catch(e => (console.error(e), process.exit(1))) + -- cgit v1.3.1