{ lib, pkgs, }: with lib; let indent = indent: text: pkgs.lib.strings.concatMapLines (line: indent + line + "\n") ( if builtins.isList text then text else pkgs.lib.strings.splitString "\n" text ); pyBool = val: if val then "True" else "False"; pyStr = val: "\"${builtins.replaceStrings ["\\" "\"" "\n"] ["\\\\" "\\\"" "\\n"] val}\""; in mkModule (self: { pythonRuntime = mkRuntime (self: { nativeFunctionTypes = [ self.func ]; nativeDataFormats = with self.formats; [ jsonObject blobStream blobString ]; python = pkgs.python3; builder = bindings: let initialState = { repositories = [ ]; bindings = [ ]; functions = [ ]; classes = [ ]; }; stateAppend = state: kind: obj: state // { ${kind} = state.${kind} ++ [ obj ]; }; findOrAssign = lst: obj: let newIndex = builtins.length lst; index = pkgs.lib.lists.findFirstIndex (obj2: obj == obj2) newIndex lst; new = index == newIndex; newLst = lst ++ (if new then [ obj ] else [ ]); in { inherit index new; lst = newLst; }; typeToAttr = { Function = "functions"; Repo = "repositories"; Class = "classes"; Binding = "bindings"; }; visit = visitors: obj: visitors.${typeToAttr.${obj.__artinixType}} obj; childrenOf = obj: visit childVisitors obj; childVisitors = { repositories = repository: initialState // { classes = [ repository.class ]; }; classes = class: initialState; functions = func: initialState; bindings = binding: initialState // { classes = [ binding.class ]; repositories = (mapAttrsToList (_: r: r) bindings.inputs) ++ (mapAttrsToList (_: r: r) bindings.outputs); functions = [ binding.function ]; }; }; explode' = state: obj: let lst = state.${typeToAttr.${obj.__artinixType}}; found = findOrAssign lst obj; in (if found.new then (let state1 = stateAppend state typeToAttr.${obj.__artinixType} obj; children = childrenOf obj; state3 = foldlAttrs (state2: _: childrenList: builtins.foldl' explode' state2 childrenList) state1 children; in state3) else state); explodeList = builtins.foldl' explode' initialState; indexOf = item: lst: findFirstIndex (obj: obj == item) (throw "Option was not found in the options list") lst; reloop = exploded: let numberVisitors = { repositories = repo: repo // { class = indexOf repo.class exploded.classes; }; classes = class: class; functions = func: func; bindings = binding: binding // { class = indexOf binding.class exploded.classes; function = indexOf binding.function exploded.functions; inputs = builtins.mapAttrs (k: v: indexOf v exploded.repositories) binding.inputs; outputs = builtins.mapAttrs (k: v: indexOf v exploded.repositories) binding.outputs; }; }; numbered = builtins.mapAttrs (k: v: builtins.map numberVisitors.${k} v) exploded; result = pkgs.lib.converge (final: let reloopVisitors = { repositories = repo: repo // { class = builtins.elemAt final.classes repo.class; }; classes = class: class; functions = func: func; bindings = binding: binding // { class = builtins.elemAt final.classes binding.class; function = builtins.elemAt final.functions binding.function; inputs = builtins.mapAttrs (k: v: builtins.elemAt final.repositories v) binding.inputs; outputs = builtins.mapAttrs (k: v: builtins.elemAt final.repositories v) binding.outputs; }; }; in builtins.mapAttrs (k: v: pkgs.lib.imap0 (i: v': (reloopVisitors.${k} v') // { __index = i; }) v) final ) numbered; in result; #index' = exploded: obj: let # options = exploded.${typeToAttr.${obj.__artinixType}}; # index = indexOf obj options; # relooped = reloop exploded; # options' = relooped.${typeToAttr.${obj.__artinixType}}; #in builtins.elemAt options' index; #indexList = lst: let exploded = explodeList lst; in builtins.map (index' exploded) lst; #index = obj: index' (explode obj) obj; #bindings = indexList bindings; finalState = reloop (explodeList bindings); inherit (pkgs.lib.attrsets) mapAttrsToList foldlAttrs; inherit (pkgs.lib.lists) findFirstIndex; allPyDeps = pkgs.lib.lists.concatMap (func: func.pythonDeps) attrs.nativeFunctions; pyEnv = self.python.withPackages ( pythonPackages: builtins.map (dep: if builtins.isString dep then pythonPackages.${dep} else dep) allPyDeps ); in pkgs.substituteAll { name = "driver.py"; src = ./src/driver.py.template; interpreter = pyEnv.mainProgram; allowDefault = pyBool TODO; allowManual = pyBool TODO; entityClassSetup = indent "" ( pkgs.lib.flip builtins.map finalState.bindings ( binding: "entity_classes.append(ArtinixEntityClass(${pyStr binding.name}))" ) ); repositorySetup = indent "" ( pkgs.lib.flip builtins.map finalState.repositories ( repo: "repositories.append(ArtinixRepository(entity_classes[${repo.class.__index}], ${pyStr repo.name}, ${pystr "dtype?"}))" ) ); functionSetup = indent "" ( pkgs.lib.flip builtins.map finalState.functions ( func: "functions.append(ArtinixPythonFunction(${pyStr func.module_path}, ${pyStr func.func_name}))" ) ); datastoreSetup = ""; }; }); # TODO integrate the typechecker right here I think? func = mkFuncType ( { module, function }: { linker = self.pythonLinker; inherit module function; } ); pythonTypeChecker = ( { module, function, inputs, outputs, }: pkgs.runCommand "typecheck-python-${builtins.baseNameOf module}-${function}" { } '' # tee hee touch $out '' ); formats = mkModule (self': { jsonObject = mkFormat { pythonConstructor = "ArtinixJsonObjectFormat()"; }; blobStream = mkFormat { pythonConstructor = "ArtinixBlobStreamFormat()"; }; blobString = mkFormat { pythonConstructor = "ArtinixBlobStringFormat()"; }; pythonObject = ty: mkFormat { inherit ty; pythonConstructor = "ArtinixPythonObjectFormat(${pyStr ty})"; }; generatorOf = subfmt: mkFormat { inherit subfmt; pythonConstructor = "ArtinixGeneratorOfFormat(${subfmt.pythonConstructor})"; }; }); dtypes = mkModule (self': { pythonClass = name: mkRepoType { inherit name; }; }); })