{pkgs, ...}: let inherit (builtins) attrNames concatStringsSep filter isAttrs isBool isFloat isInt isList isPath isNull isString match toJSON typeOf ; inherit (pkgs.lib) assertMsg concatStrings fix imap0 isDerivation mapAttrsToList optionalString splitString toPretty ; commaJoin = concatStringsSep ", "; renderLua = { multiline ? true, indent ? "", asBindings ? false, } @ args: v: let innerIndent = "${indent} "; introSpace = if multiline then "\n${innerIndent}" else " "; outroSpace = if multiline then "\n${indent}" else " "; innerArgs = args // { indent = if asBindings then indent else innerIndent; asBindings = false; }; ast = { render = args: data: let astType = data.__ast; in if ast ? ${astType} then ast.${astType} args data else abort ''Unknown ast type ${astType}''; raw = args: { data, __no_indent ? false, ... }: let content = if __no_indent then data else concatStringsSep "\n" (imap0 (idx: line: "${optionalString (idx != 0) args.indent}${line}") (splitString "\n" data)); in content; return = args: {data, ...}: "return ${renderLua args data}"; fn = args: { data, name, args, ... }: "function ${name}(${commaJoin args})${introSpace}${renderLua innerArgs data}${outroSpace}end"; }; generatedBindings = assert assertMsg (badBindingNames == []) "Bad Lua var names: ${toPretty {} badBindingNames}"; concatStrings (mapAttrsToList (key: value: "${indent}${key} = ${renderLua innerArgs value}\n") v); concatItems = concatStringsSep ",${introSpace}"; varNameRe = "[[:alpha:]_][[:alnum:]_]*"; matchBindingName = match "${varNameRe}(\\.${varNameRe})*"; badBindingNames = filter (name: matchBindingName name == null) (attrNames v); luaKey = key: if match varNameRe key == null then "[${toJSON key}]" else key; withSpaces = val: optionalString (val != "") "${introSpace}${val}${outroSpace}"; mkKV = key: value: "${luaKey key} = ${renderLua innerArgs value}"; in if asBindings then generatedBindings else if isNull v then "nil" else if isInt v || isFloat v || isString v || isBool v then toJSON v else if isPath v || isDerivation v then toJSON "${v}" else if isList v then "{${withSpaces (concatItems (map (renderLua innerArgs) v))}}" else if v ? __ast then ast.render args v else if isAttrs v then "{${withSpaces (concatItems (mapAttrsToList mkKV v))}}" else abort "generators.renderLua: type ${typeOf v} is unsupported"; newAst = type: set: set // {__ast = type;}; in fix (lua: { inherit renderLua; writeLua = name: value: pkgs.writeText name (lua.renderLua {} value); raw = data: newAst "raw" {inherit data;}; function = name: args: data: newAst "fn" {inherit name data args;}; lambda = lua.function ""; return = data: newAst "return" {inherit data;}; })