artinix/python/arti.nix

196 lines
7.1 KiB
Nix

{
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; };
});
})