diff --git a/nt/primitives/mix/default.nix b/nt/primitives/mix/default.nix new file mode 100644 index 0000000..9f35f16 --- /dev/null +++ b/nt/primitives/mix/default.nix @@ -0,0 +1,154 @@ +{this, ...}: let + inherit + (builtins) + attrNames + attrValues + hasAttr + listToAttrs + removeAttrs + ; + + inherit + (this.util) + filterAttrs + hasInfix + mergeAttrsList + nameValuePair + projectOnto + removeSuffix + Wrap + ; + + modNameFromPath = path: let + name = baseNameOf path |> removeSuffix ".nix"; + in + assert (! hasInfix "." name) + || throw '' + Mix module ${path} has invalid name \"${name}\". + Module names must not contain the . (period) character. + ''; name; +in rec { + # by default the imported module is given the basename of its path + # but you can set it manually by using the `mix.mod` function. + importMods = list: inputs: + list + |> map (path: nameValuePair (modNameFromPath path) (import path inputs)) + |> listToAttrs; + + importMergeMods = list: inputs: + list + |> map (path: (import path inputs)) + |> mergeAttrsList; + + # create a new and empty mixture + newMixture' = let + self = { + # trapdoor attribute + _' = { + path = []; + modName = null; + }; + }; + in + self; + + # a splash of this, a splash of that ^_^ + add = ingredients: mixture: let + sidedish = mergeAttrsList ingredients; + in + # bone apple tea ;-; + mixture // filterAttrs (x: _: ! hasAttr x mixture) sidedish; + + newMixture = inputs: modBuilder: let + inputs' = removeAttrs inputs ["this"]; + inputsWithThis = inputs' // {this = mixture;}; + + # mixture components are ordered based on shadowing + mixture = + inputs' + // importMods meta.submods.public inputsWithThis + // importMergeMods meta.includes.public inputsWithThis + // content; + + # this = { + # # trapdoor attribute + # _' = { + # path = []; + # }; + # parent' = throw "Mix: The mixture's root module has no parent by definition."; + # }; + + # partition modAttrs' into metadata and content + modAttrs' = modBuilder mixture; + content = removeAttrs modAttrs' (attrNames meta); + # attributes expected by and that directly modify mix's behaviour + meta = + modAttrs' + |> projectOnto + { + includes = { + public = []; + private = []; + protected = []; + }; + submods = { + public = []; + private = []; + protected = []; + }; + options = Wrap {}; + config = Wrap {}; + }; + in + mixture; + + mkMod = mixture: modBuilder: let + # XXX: TODO + # modAttrs = modBuilder privateMixture; + modAttrs = modBuilder mixture; + + # attributes expected by and that directly modify mix's behaviour + meta = + modAttrs + |> projectOnto + { + includes = { + public = []; + private = []; + protected = []; + }; + submods = { + public = []; + private = []; + protected = []; + }; + options = Wrap {}; + config = Wrap {}; + }; + + # XXX: TODO + # protectedMixture = add [public protected] mixture; + # privateMixture = add [private] protectedMixture; + + mkInterface = name: mixture: base: + mergeAttrsList + (attrValues <| importMods meta.includes.${name} mixture) + ++ [ + base + (importMods meta.submods.${name} mixture) + ]; + # XXX: TODO + # NOTE: public submodules are still DESCENDENTS + # NOTE: and should be able to access protected values :) + # public = mkInterface "public" protectedMixture content; + # protected = mkInterface "protected" protectedMixture public; + # private = mkInterface "private" privateMixture protected; + content = throw "TODO"; + public = mkInterface "public" mixture content; + protected = mkInterface "protected" mixture public; + private = mkInterface "private" mixture protected; + in + # XXX: TODO + # public; + modAttrs; +} diff --git a/nt/primitives/util/util.nix b/nt/primitives/util/util.nix index 478c7c0..39cbda7 100644 --- a/nt/primitives/util/util.nix +++ b/nt/primitives/util/util.nix @@ -2,6 +2,7 @@ {...}: let inherit (builtins) + attrNames elem elemAt filter @@ -10,8 +11,10 @@ genList length mapAttrs + match partition removeAttrs + replaceStrings stringLength substring tail @@ -97,6 +100,14 @@ in rec { stringInit = x: x |> stringTake 1; stringLast = x: stringElem (stringLength x - 1); + stringToCharacters = s: genList (p: substring p 1 s) (stringLength s); + + escape = list: replaceStrings list (map (c: "\\${c}") list); + escapeRegex = escape (stringToCharacters "\\[{()^$?*+|."); + + hasInfix = infix: content: + match ".*${escapeRegex infix}.*" "${content}" != null; + countEvensLeq = n: n / 2; countOddsLeq = n: (n + 1) / 2; @@ -151,4 +162,11 @@ in rec { |> filter (x: head x == name) |> map tail |> removeAttrsRec); + + filterAttrs = pred: xs: + attrNames xs + |> filter (name: ! pred name xs.${name}) + |> removeAttrs xs; + + nameValuePair = name: value: {inherit name value;}; }