/** * Copyright (C) 2022, Dmitriy Pleshevskiy * * nix2lua is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * nix2lua is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with nix2lua. If not, see . */ let inherit (builtins) isString isFloat isInt isBool isList isAttrs isNull isPath; inherit (builtins) concatStringsSep filter mapAttrs attrValues; isNotNull = v: !(isNull v); excludeNull = expr: filter isNotNull expr; ################################################################################ # Utils ################################################################################ error = message: throw "[nix2lua] ${message}"; warn = msg: builtins.trace "[nix2lua] warning: ${msg}"; deprecated = before: now: warn "`${before}` is deprecated. Use `${now}` instead"; getType = expr: if isAttrs expr && expr ? _type then expr._type else null; validString = expr: if isRaw expr then validString expr.raw else if isString expr || isPath expr then toString expr else error "Value '${toString expr}' is not a valid string"; validFuncName = fnName: if isString fnName && builtins.stringLength fnName > 0 then raw fnName else error "Value '${toString fnName}' is not a valid function name"; ################################################################################ # Low-Level ################################################################################ isRaw = expr: getType expr == "raw"; raw = expr: if isRaw expr then { _type = "raw"; raw = expr.raw; } else if isString expr then { _type = "raw"; raw = expr; } else error "Value '${toString expr}' is not supported for a raw type"; isJoin = expr: getType expr == "_join"; join = sep: expr: if isList expr then { _type = "_join"; sep = validString sep; parts = expr; } else error "Value '${toString expr}' is not supported for a join type"; concat = join ""; concatLines = lines: join "\n" ( lines # add an empty line at the end ++ [ (raw "") ] ); pipe = join "."; spaceBetween = join " "; wrap = start: end: expr: concat [ (raw start) expr (raw end) ]; wrapParen = wrap "(" ")"; call = fnName: args: concat [ (validFuncName fnName) (wrapParen (join ", " args)) ]; require = name: call "require" [ name ]; kw_and = raw "and"; kw_or = raw "or"; kw_not = raw "not"; kw_function = raw "function"; kw_end = raw "end"; kw_break = raw "break"; kw_return = raw "return"; kw_if = raw "if"; kw_in = raw "in"; kw_then = raw "then"; kw_else = raw "else"; kw_local = raw "local"; kw_for = raw "for"; kw_do = raw "do"; kw_until = raw "until"; kw_while = raw "while"; kw_repeat = raw "repeat"; op = operation: left: right: wrapParen (join " ${validString operation} " [ left right ]); add = op "+"; sub = op "-"; mul = op "*"; div = op "/"; mod = op "%"; exp = op "^"; eq = op "=="; ne = op "~="; gt = op ">"; lt = op "<"; gte = op ">="; lte = op "<="; and = op kw_and; or = op kw_or; not = expr: spaceBetween [ kw_not expr ]; local = expr: spaceBetween [ kw_local expr ]; set = variable: value: join " = " [ (raw variable) value ]; func = fnName: params: body: (concatLines ([ (concat [ (spaceBetween [ kw_function (validFuncName fnName) ]) (wrapParen (join ", " (map raw params))) ]) ] ++ body ++ [ kw_end ]) ); return = expr: spaceBetween ([ kw_return expr ]); return_void = return null; ifelse = condition: trueBody: falseBody: (concatLines ([ (spaceBetween [ kw_if condition kw_then ]) ] ++ trueBody ++ (if falseBody != [ ] then [ kw_else ] ++ falseBody else [ ]) ++ [ kw_end ]) ); if' = condition: trueBody: ifelse condition trueBody [ ]; isLuaNil = expr: getType expr == "nil"; LuaNil = { _type = "nil"; }; isNamedField = expr: getType expr == "table_field"; namedField = name: expr: { _type = "table_field"; name = validString name; value = toLua expr; }; toLuaBool = expr: if expr then "true" else "false"; toLuaNumber = toString; toLuaString = expr: "\"${validString expr}\""; toLuaList = onValue: expr: let wrapObj = expr: "{ ${concatStringsSep ", " expr} }"; in wrapObj (excludeNull (map onValue expr)); toLuaNamedField = name: expr: if isNull expr then null else "[${toLuaString name}] = ${expr}"; toLuaTable = onValue: expr: onValue (attrValues (mapAttrs namedField expr)); toLuaInternal = depth: expr: let nextDepth = depth + 1; in if isJoin expr then concatStringsSep expr.sep (map (toLuaInternal depth) (excludeNull expr.parts)) else if isLuaNil expr then "nil" else if isRaw expr then expr.raw else if isNamedField expr then if depth > 0 then toLuaNamedField expr.name expr.value else error "You cannot render table field at the top level" else if isAttrs expr then toLuaTable (toLuaInternal nextDepth) expr else if isList expr then toLuaList (toLuaInternal nextDepth) expr else if isString expr || isPath expr then toLuaString expr else if isFloat expr || isInt expr then toLuaNumber expr else if isBool expr then toLuaBool expr else if isNull expr then null else error "Value '${toString expr}' is not supported yet"; toLua = val: toLuaInternal 0 val; in { # Deprecated mkLuaNil = deprecated "mkLuaNil" "Nil" LuaNil; mkLuaRaw = deprecated "mkLuaRaw" "raw" raw; mkCall = deprecated "mkCall" "call" call; mkNamedField = deprecated "mkNamedField" "namedField" namedField; inherit toLua; inherit LuaNil; inherit raw join concat concatLines pipe; inherit namedField call require local set func ifelse if'; inherit op; inherit eq ne gt lt gte lte; inherit add sub mul div mod exp; inherit and or not; inherit return return_void; # useful aliases var = raw; vars = map raw; inherit validString; }