improve call and conditions api

This commit is contained in:
Dmitriy Pleshevskiy 2024-04-20 19:11:24 +03:00
parent f3796d29b4
commit ce62a78f5a
Signed by: pleshevskiy
GPG key ID: 17041163DA10A9A2
2 changed files with 76 additions and 120 deletions

68
lib.nix
View file

@ -26,6 +26,9 @@ let
################################################################################ ################################################################################
error = message: throw "[nix2lua] ${message}"; error = message: throw "[nix2lua] ${message}";
validationError = expr: message:
let val = if builtins.isFunction expr then "Function" else "Value '${toString expr}'"; in
error "${val} ${message}";
warn = msg: builtins.trace "[nix2lua] warning: ${msg}"; warn = msg: builtins.trace "[nix2lua] warning: ${msg}";
deprecated = before: now: warn "`${before}` is deprecated. Use `${now}` instead"; deprecated = before: now: warn "`${before}` is deprecated. Use `${now}` instead";
@ -34,11 +37,11 @@ let
validString = expr: validString = expr:
if isRaw expr then validString expr.raw if isRaw expr then validString expr.raw
else if isString expr || isPath expr then toString expr else if isString expr || isPath expr then toString expr
else error "Value '${toString expr}' is not a valid string"; else validationError expr "is not a valid string";
validFuncName = fnName: validFuncName = fnName:
if isString fnName && builtins.stringLength fnName > 0 then raw fnName if isString fnName && builtins.stringLength fnName > 0 then raw fnName
else error "Value '${toString fnName}' is not a valid function name"; else validationError fnName "is not a valid function name";
################################################################################ ################################################################################
# Low-Level # Low-Level
@ -46,25 +49,16 @@ let
isRaw = expr: getType expr == "raw"; isRaw = expr: getType expr == "raw";
raw = expr: raw = expr:
if isRaw expr then if isRaw expr then { _type = "raw"; raw = expr.raw; }
{ _type = "raw"; raw = expr.raw; } else if isString expr then { _type = "raw"; raw = expr; }
else if isString expr then else validationError expr "is not supported for a raw type";
{ _type = "raw"; raw = expr; }
else
error "Value '${toString expr}' is not supported for a raw type";
isJoin = expr: getType expr == "_join"; isJoin = expr: getType expr == "_join";
join = sep: expr: join = sep: expr:
if isList expr then { _type = "_join"; sep = validString sep; parts = expr; } if isList expr then { _type = "_join"; sep = validString sep; parts = expr; }
else error "Value '${toString expr}' is not supported for a join type"; else validationError expr "is not supported for a join type";
concat = join ""; concat = join "";
concatLines = lines:
join "\n" (
lines
# add an empty line at the end
++ [ (raw "") ]
);
pipe = join "."; pipe = join ".";
pipe1 = lft: rgt: pipe [ lft rgt ]; pipe1 = lft: rgt: pipe [ lft rgt ];
spaceBetween = join " "; spaceBetween = join " ";
@ -74,10 +68,17 @@ let
wrapParen = wrap "(" ")"; wrapParen = wrap "(" ")";
call = fnName: args: call = fnName: args:
concat [ (validFuncName fnName) (wrapParen (join ", " args)) ]; concat [
(validFuncName fnName)
(wrapParen (
if isList args then join ", " args
else if builtins.isFunction args then validationError args "is not supported to call args"
else args
))
];
call0 = fnName: call fnName [ ]; call0 = fnName: call fnName [ ];
call1 = fnName: arg: call fnName [ arg ]; call1 = fnName: arg: call fnName [ arg ];
require = name: call1 "require" name; require = name: call "require" name;
kw_and = raw "and"; kw_and = raw "and";
kw_or = raw "or"; kw_or = raw "or";
@ -117,36 +118,38 @@ let
or = op kw_or; or = op kw_or;
not = expr: spaceBetween [ kw_not expr ]; not = expr: spaceBetween [ kw_not expr ];
# Type: validBlockBody :: a -> [b]
validBlockBody = body:
if isList body then body
else if isAttrs body then [ body ]
else validationError body "is not valid block body";
local = expr: spaceBetween [ kw_local expr ]; local = expr: spaceBetween [ kw_local expr ];
set = variable: value: join " = " [ (raw variable) value ]; set = variable: value: join " = " [ (raw variable) value ];
func = fnName: params: body: func = fnName: params: body:
let (spaceBetween
f =
if isList body then concatLines
else if isAttrs body then spaceBetween
else error "Value ${toString body} is not a valid function body";
in
(f
([ ([
(concat [ (concat [
(spaceBetween [ kw_function (validFuncName fnName) ]) (spaceBetween [ kw_function (validFuncName fnName) ])
(wrapParen (join ", " (map raw params))) (wrapParen (join ", " (map raw params)))
]) ])
] ]
++ (if isList body then body else if isAttrs body then [ body ] else [ ]) ++ (validBlockBody body)
++ [ kw_end ]) ++ [ kw_end ])
); );
func0 = fnName: func fnName [ ];
lambda = func ""; lambda = func "";
lambda0 = lambda [ ]; lambda0 = lambda [ ];
return = expr: spaceBetween ([ kw_return expr ]); return = expr: spaceBetween ([ kw_return expr ]);
return_void = return null; return_void = return
null;
ifelse = condition: trueBody: falseBody: ifelse = condition: trueBody: falseBody:
(concatLines (spaceBetween
([ (spaceBetween [ kw_if condition kw_then ]) ] ([ kw_if condition kw_then ]
++ trueBody ++ (validBlockBody trueBody)
++ (if falseBody != [ ] then [ kw_else ] ++ falseBody else [ ]) ++ (if falseBody != [ ] then [ kw_else ] ++ (validBlockBody falseBody) else [ ])
++ [ kw_end ]) ++ [ kw_end ])
); );
if' = condition: trueBody: ifelse condition trueBody [ ]; if' = condition: trueBody: ifelse condition trueBody [ ];
@ -198,17 +201,18 @@ in
mkLuaRaw = deprecated "mkLuaRaw" "raw" raw; mkLuaRaw = deprecated "mkLuaRaw" "raw" raw;
mkCall = deprecated "mkCall" "call" call; mkCall = deprecated "mkCall" "call" call;
mkNamedField = deprecated "mkNamedField" "namedField" namedField; mkNamedField = deprecated "mkNamedField" "namedField" namedField;
concatLines = deprecated "concatLines" "spaceBetween" spaceBetween;
inherit toLua; inherit toLua;
inherit LuaNil; inherit LuaNil;
inherit raw join concat concatLines; inherit raw join concat spaceBetween;
inherit pipe pipe1; inherit pipe pipe1;
inherit namedField require local set ifelse if'; inherit namedField require local set ifelse if';
inherit call call0 call1; inherit call call0 call1;
inherit func lambda lambda0; inherit func func0 lambda lambda0;
inherit op; inherit op;
inherit eq ne gt lt gte lte; inherit eq ne gt lt gte lte;

View file

@ -33,11 +33,11 @@ with nix2lua; pkgs.lib.runTests {
}; };
"test returns a lua string" = { "test returns a lua string" = {
expr = toLua "hello world"; expr = toLua "hello world";
expected = "\"hello world\""; expected = ''"hello world"'';
}; };
"test returns a path as a lua string" = { "test returns a path as a lua string" = {
expr = toLua /hello/world; expr = toLua /hello/world;
expected = "\"/hello/world\""; expected = ''"/hello/world"'';
}; };
"test returns an integer number" = { "test returns an integer number" = {
expr = toLua 10; expr = toLua 10;
@ -61,11 +61,11 @@ with nix2lua; pkgs.lib.runTests {
}; };
"test returns table with all primitive types" = { "test returns table with all primitive types" = {
expr = toLua [ "hello" 10 10.1 true ]; expr = toLua [ "hello" 10 10.1 true ];
expected = "{ \"hello\", 10, 10.100000, true }"; expected = ''{ "hello", 10, 10.100000, true }'';
}; };
"test returns table without null values" = { "test returns table without null values" = {
expr = toLua [ null "hello" null 10 null 10.1 null true null ]; expr = toLua [ null "hello" null 10 null 10.1 null true null ];
expected = "{ \"hello\", 10, 10.100000, true }"; expected = ''{ "hello", 10, 10.100000, true }'';
}; };
"test returns named table" = { "test returns named table" = {
expr = toLua { expr = toLua {
@ -75,11 +75,11 @@ with nix2lua; pkgs.lib.runTests {
success = true; success = true;
fail = false; fail = false;
}; };
expected = "{ [\"fail\"] = false, [\"float\"] = 10.100000, [\"foo\"] = \"hello\", [\"int\"] = 10, [\"success\"] = true }"; expected = ''{ ["fail"] = false, ["float"] = 10.100000, ["foo"] = "hello", ["int"] = 10, ["success"] = true }'';
}; };
"test returns named table without nullable items" = { "test returns named table without nullable items" = {
expr = toLua { foo = "hello"; bar = null; }; expr = toLua { foo = "hello"; bar = null; };
expected = "{ [\"foo\"] = \"hello\" }"; expected = ''{ ["foo"] = "hello" }'';
}; };
"test returns recursive named table" = { "test returns recursive named table" = {
expr = toLua { expr = toLua {
@ -89,11 +89,11 @@ with nix2lua; pkgs.lib.runTests {
}; };
}; };
}; };
expected = "{ [\"first\"] = { [\"second\"] = { [\"last\"] = \"hello\" } } }"; expected = ''{ ["first"] = { ["second"] = { ["last"] = "hello" } } }'';
}; };
"test return recursive table" = { "test return recursive table" = {
expr = toLua [ [ [ "foo" ] "bar" ] ]; expr = toLua [ [ [ "foo" ] "bar" ] ];
expected = "{ { { \"foo\" }, \"bar\" } }"; expected = ''{ { { "foo" }, "bar" } }'';
}; };
"test returns table with one named field" = { "test returns table with one named field" = {
expr = toLua [ expr = toLua [
@ -101,7 +101,7 @@ with nix2lua; pkgs.lib.runTests {
(namedField "foo" "hello") (namedField "foo" "hello")
10 10
]; ];
expected = "{ \"foo\", [\"foo\"] = \"hello\", 10 }"; expected = ''{ "foo", ["foo"] = "hello", 10 }'';
}; };
"test returns raw string" = { "test returns raw string" = {
expr = toLua (raw "hello"); expr = toLua (raw "hello");
@ -113,7 +113,7 @@ with nix2lua; pkgs.lib.runTests {
}; };
"test returns path as string" = { "test returns path as string" = {
expr = toLua /foo/bar; expr = toLua /foo/bar;
expected = "\"/foo/bar\""; expected = ''"/foo/bar"'';
}; };
"test throws an error when you try to use named field withoun table" = { "test throws an error when you try to use named field withoun table" = {
expr = tryEval (toLua (namedField "foo" "bar")); expr = tryEval (toLua (namedField "foo" "bar"));
@ -121,17 +121,14 @@ with nix2lua; pkgs.lib.runTests {
}; };
"test returns call function with arguments" = { "test returns call function with arguments" = {
expr = toLua (call "root_pattern" [ "deno.json" "deno.jsonc" ]); expr = toLua (call "root_pattern" [ "deno.json" "deno.jsonc" ]);
expected = "root_pattern(\"deno.json\", \"deno.jsonc\")"; expected = ''root_pattern("deno.json", "deno.jsonc")'';
}; };
"test returns concated lines" = { "test returns concated expressions" = {
expr = toLua (concatLines [ expr = toLua (spaceBetween [
(call "foo" [ 1 2 ]) (call "foo" [ 1 2 ])
(call "bar" [ "baz" "biz" ]) (call "bar" [ "baz" "biz" ])
]); ]);
expected = '' expected = ''foo(1, 2) bar("baz", "biz")'';
foo(1, 2)
bar("baz", "biz")
'';
}; };
"test returns a pipe with many function call" = { "test returns a pipe with many function call" = {
expr = toLua (pipe [ expr = toLua (pipe [
@ -149,18 +146,13 @@ with nix2lua; pkgs.lib.runTests {
expected = "local parser_config.d2 = { }"; expected = "local parser_config.d2 = { }";
}; };
"test returns all operations" = { "test returns all operations" = {
expr = toLua (concatLines [ expr = toLua (spaceBetween [
(eq (mul (add 1 2) (sub 2 1)) 3) (set "a" (eq (mul (add 1 2) (sub 2 1)) 3))
(not (eq (mul (add 1 2) (sub 2 1)) 3)) (set "b" (not (eq (mul (add 1 2) (sub 2 1)) 3)))
(not 10) (set "c" (not 10))
(gt 10 5) (set "d" (gt 10 5))
]); ]);
expected = '' expected = ''a = (((1 + 2) * (2 - 1)) == 3) b = not (((1 + 2) * (2 - 1)) == 3) c = not 10 d = (10 > 5)'';
(((1 + 2) * (2 - 1)) == 3)
not (((1 + 2) * (2 - 1)) == 3)
not 10
(10 > 5)
'';
}; };
"test returns rendered condition" = { "test returns rendered condition" = {
expr = toLua (set "name" expr = toLua (set "name"
@ -168,82 +160,42 @@ with nix2lua; pkgs.lib.runTests {
); );
expected = "name = ((a >= 1) and (a <= 10))"; expected = "name = ((a >= 1) and (a <= 10))";
}; };
"test returns defined multiline function" = { "test returns defined function" = {
expr = toLua (func "hello" [ "a" "b" ] [ expr = toLua (func "hello" [ "a" "b" ] [
(eq (mul (add (var "a") 2) (sub 2 1)) 3) (set "a" (eq (mul (add 1 2) (sub 2 1)) 3))
(not (eq (mul (add (var "b") 2) (sub 2 1)) 3)) (set "b" (not (eq (mul (add 1 2) (sub 2 1)) 3)))
(not 10) (set "c" (not 10))
(gt 10 5) (set "d" (gt 10 5))
]); ]);
expected = '' expected = ''function hello(a, b) a = (((1 + 2) * (2 - 1)) == 3) b = not (((1 + 2) * (2 - 1)) == 3) c = not 10 d = (10 > 5) end'';
function hello(a, b)
(((a + 2) * (2 - 1)) == 3)
not (((b + 2) * (2 - 1)) == 3)
not 10
(10 > 5)
end
'';
}; };
"test returns defined singleline function" = { "test returns defined singleline function" = {
expr = toLua (func "hello" [ "a" ] (call1 "h.world" (var "a"))); expr = toLua (func "hello" [ "a" ] (call1 "h.world" (var "a")));
expected = "function hello(a) h.world(a) end"; expected = "function hello(a) h.world(a) end";
}; };
"test returns if statement" = { "test returns if statement" = {
expr = toLua (if' (eq 10 10) [ expr = toLua (if' (eq 10 10) (call "print" [ 10 ]));
(call "print" [ 10 ]) expected = ''if (10 == 10) then print(10) end'';
]);
expected = ''
if (10 == 10) then
print(10)
end
'';
}; };
"test returns if else statement" = { "test returns if else statement" = {
expr = toLua (ifelse (eq 10 10) [ expr = toLua (ifelse (eq 10 10) [
(call "print" [ "10 == 10" ]) (call "print" "10 == 10")
(call "print" "10 == 10")
] [ ] [
(call "print" [ "10 != 10" ]) (call "print" "10 != 10")
(call "print" "10 != 10")
]); ]);
expected = '' expected = ''if (10 == 10) then print("10 == 10") print("10 == 10") else print("10 != 10") print("10 != 10") end'';
if (10 == 10) then
print("10 == 10")
else
print("10 != 10")
end
'';
}; };
"test returns a deep if else statement" = { "test returns a deep if else statement" = {
expr = toLua (ifelse (eq 10 10) [ expr = toLua (ifelse (eq 10 10)
(if' true [ (call "print" [ "yes" ]) ]) (if' true (call "print" "yes"))
] [ (if' true (call "print" "no"))
(if' true [ (call "print" [ "no" ]) ]) );
]); expected = ''if (10 == 10) then if true then print("yes") end else if true then print("no") end end'';
expected = ''
if (10 == 10) then
if true then
print("yes")
end
else
if true then
print("no")
end
end
'';
}; };
"test returns a returns keyword" = { "test returns a returns keyword" = {
expr = toLua (ifelse (eq 10 10) [ expr = toLua (ifelse (eq 10 10) return_void (return 10));
return_void expected = ''if (10 == 10) then return else return 10 end'';
] [
(return 10)
]);
expected = ''
if (10 == 10) then
return
else
return 10
end
'';
}; };
} }