aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAkshay Nair <phenax5@gmail.com>2023-01-04 18:44:00 +0530
committerAkshay Nair <phenax5@gmail.com>2023-01-04 18:44:00 +0530
commit5f5ea35c634cd97710eb953f930cc4d468c948ce (patch)
tree99beefc65071e20e6bb5b3e3a2345d5d737603b0 /src
parentfb91cf7218f20bdb8896a2ea305aa598f9f07036 (diff)
downloadts-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.ts39
-rw-r--r--src/runtime.ts135
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())
+