diff options
| author | Akshay Nair <phenax5@gmail.com> | 2023-01-04 18:44:00 +0530 |
|---|---|---|
| committer | Akshay Nair <phenax5@gmail.com> | 2023-01-04 18:44:00 +0530 |
| commit | 5f5ea35c634cd97710eb953f930cc4d468c948ce (patch) | |
| tree | 99beefc65071e20e6bb5b3e3a2345d5d737603b0 /src | |
| parent | fb91cf7218f20bdb8896a2ea305aa598f9f07036 (diff) | |
| download | ts-types-lang-5f5ea35c634cd97710eb953f930cc4d468c948ce.tar.gz ts-types-lang-5f5ea35c634cd97710eb953f930cc4d468c948ce.zip | |
feat: pics send quick. runtime with effects donezo
Diffstat (limited to 'src')
| -rw-r--r-- | src/index.ts | 39 | ||||
| -rw-r--r-- | src/runtime.ts | 135 |
2 files changed, 174 insertions, 0 deletions
diff --git a/src/index.ts b/src/index.ts index e69de29..accde98 100644 --- a/src/index.ts +++ b/src/index.ts @@ -0,0 +1,39 @@ +export interface EffectAtom<T = unknown> { output: T } +export type Effect = EffectAtom[] + +export interface PrintString<_ extends string> extends EffectAtom { } +export interface Print<_ extends any> extends EffectAtom { } +// interface Debug<_ extends string, T> extends EffectAtom<T> { } + +export interface WriteFile<_Path extends string, _Content extends string> extends EffectAtom { } +export interface ReadFile<_Path extends string> extends EffectAtom<string> { } + +export interface Program<Effs extends Effect, ExitCode extends number = 0> { + effects: Effs, + exitCode: ExitCode, +} + +export interface Kind<Inp = unknown, Out = unknown> { + input: Inp + return: Out +} + +export interface WriteFileC extends Kind<string> { + return: WriteFile<"./somefile.txt", this['input']> +} + +export interface ChainIO<Eff extends EffectAtom, Fn extends Kind> extends EffectAtom { + input: Eff + chainTo: Fn +} + +export type main = Program<[ + [1, 2, 3] extends infer Res ? Print<Res> : never, + Print<"bye bye">, + ReadFile<"./.gitignore">, + + ChainIO< + ReadFile<"./default.nix">, + WriteFileC + >, +]> diff --git a/src/runtime.ts b/src/runtime.ts new file mode 100644 index 0000000..d03e7a9 --- /dev/null +++ b/src/runtime.ts @@ -0,0 +1,135 @@ +import { Project, ScriptTarget, Type, Node, StringLiteral, TypeFormatFlags, SyntaxKind } from 'ts-morph' +import path from 'path' +import fs from 'fs' + +const project = new Project({ + compilerOptions: { + target: ScriptTarget.ES3, + }, +}) + +const typeChecker = project.getTypeChecker() + +const sourceFile = project.addSourceFileAtPath(path.resolve("./src/index.ts")) + +sourceFile.addStatements('type Foobar = "aaaaaaaa";') + +const entryPoint = sourceFile.getExportedDeclarations().get('main')?.[0] + +const typeToString = (ty: Type | undefined): string => + ty ? typeChecker.compilerObject.typeToString(ty.compilerType) : '' + +const getPropertyType = (n: Node, prop: string): Type | undefined => { + const tt = typeChecker.getTypeAtLocation(n) + const propSym = tt.getProperty(prop) + const ty = propSym && typeChecker.getTypeOfSymbolAtLocation(propSym, n) + return ty +} + +const typeRefNode = entryPoint?.getLastChild() + +const RESULT_TYPE_NAME = '__$result' + +const [statement] = sourceFile.addStatements(`type ${RESULT_TYPE_NAME} = {}`) + +const addResult = (name: string, ty: string) => { + if (statement.isKind(SyntaxKind.TypeAliasDeclaration)) { + const value = statement.getChildAtIndex(3) + if (value.isKind(SyntaxKind.TypeLiteral)) { + value.addProperty({ + name: JSON.stringify(name), + type: `{ output: ${ty} }`, + }) + } + } +} + +const accumulateResults = (effTyp: Type, node: Node): string[] => { + const name = effTyp.getSymbol()?.getName() + + switch (name) { + case 'ReadFile': + const [pathTyp] = effTyp.getTypeArguments() + const filePath = JSON.parse(typeToString(pathTyp)) + const contents = fs.readFileSync(filePath, 'utf-8') + const hash = Math.random().toFixed(8).slice(2) + addResult(hash, JSON.stringify(contents)) + return [hash] + + case 'ChainIO': + const inputTyp = effTyp.getProperty('input')?.getTypeAtLocation(node) + const inputResults = inputTyp && accumulateResults(inputTyp, node) + return [...(inputResults ?? [])] + default: + return [] + } +} + +const evalAccumulator = (effNode: Node, node: Node) => { + const effTyp = effNode.getType() + const name = effTyp.getSymbol()?.getName() + + switch (name) { + case 'PrintString': + case 'Print': + console.log(...effTyp.getTypeArguments().map(typeToString)); + return null + + case 'ReadFile': + const [hash] = accumulateResults(effTyp, node) + effNode.replaceWithText(`${RESULT_TYPE_NAME}[${JSON.stringify(hash)}]`) + return null + + case 'WriteFile': + const [pathTyp, contentsTyp] = effTyp.getTypeArguments() + const filePath = JSON.parse(typeToString(pathTyp)) + const contents = JSON.parse(typeToString(contentsTyp)) + fs.writeFileSync(filePath, contents) + return null + + case 'ChainIO': + const inputTyp = effTyp.getProperty('input')?.getTypeAtLocation(node) + const chainToKind = effTyp.getProperty('chainTo')?.getTypeAtLocation(node) + console.log('wow') + + const [hashRes] = inputTyp ? accumulateResults(inputTyp, node) : [] + const chainRes = `(${typeToString(chainToKind)} & { input: ${RESULT_TYPE_NAME}[${JSON.stringify(hashRes)}]['output'] })['return']` + + const updateEffNode = effNode.replaceWithText(chainRes) + + evalAccumulator(updateEffNode, node) + return null + default: + console.log(`${name} effect is unhandled`) + } + + return null +} + +if (typeRefNode) { + const resultType = entryPoint?.getType() + + if (typeRefNode && entryPoint && resultType?.getSymbol()?.getName() === 'Program') { + const exitCodeTy = getPropertyType(typeRefNode, 'exitCode') + const effectTypes = getPropertyType(typeRefNode, 'effects') + if (effectTypes?.isTuple()) { + console.log('Effects to run', effectTypes?.getTupleElements().map(eff => typeToString(eff))) + + const effectNodes = entryPoint.getChildrenOfKind(SyntaxKind.TypeReference) + .flatMap(n => n.getChildrenOfKind(SyntaxKind.TupleType)) + .flatMap(tt => tt.getChildrenOfKind(SyntaxKind.SyntaxList)) + .flatMap(n => n.getChildren()) + .filter(n => !n.isKind(SyntaxKind.CommaToken)) + + effectNodes.flatMap(n => evalAccumulator(n, typeRefNode)) + } + + console.log('Exited with code', exitCodeTy?.getLiteralValue()) + } else { + const ty = typeChecker.getTypeAtLocation(typeRefNode) + console.log(typeToString(ty)) + } +} + +console.log(entryPoint?.print()) + |
