diff --git a/src/erlyjs_compiler.erl b/src/erlyjs_compiler.erl index 27b2a24..360ecec 100644 --- a/src/erlyjs_compiler.erl +++ b/src/erlyjs_compiler.erl @@ -258,7 +258,7 @@ ast({apply, {identifier, _, Name} , {'(', Args}}, {Ctx, Trav}) -> ast({apply, Call, {'(', Args}}, {Ctx, Trav}) -> {{Ast, Inf}, {Ctx1, Trav1}} = ast(Call, {Ctx#js_ctx{action = get}, Trav}), {Args1, _, _} = p_t(Args, Ctx1, Trav1), - {{erl_syntax:application(none, Ast, Args1), Inf}, {Ctx1, Trav1}}; + {{erl_syntax:application(none, Ast, [erl_syntax:list(Args1)]), Inf}, {Ctx1, Trav1}}; ast({{identifier, _, Name}, Value}, {Ctx, Trav}) -> var_declare(Name, Value, Ctx, Trav); ast({var, DeclarationList}, {Ctx, Trav}) -> @@ -706,28 +706,69 @@ sort_vars(Vars) -> func(Params, Body, Ctx, Trav) -> - {Params1, _, Trav1} = p_t(Params, Ctx#js_ctx{action = set}, Trav), - {Ast, Inf, _} = p_t(Body, Ctx#js_ctx{action = get}, wrap_add_scope(Trav1)), - Ast1 = erl_syntax:fun_expr([erl_syntax:clause(Params1, none, Ast)]), - {{Ast1, Inf}, {Ctx, Trav1}}. + {ArgsVar, ArgsAst, Trav1} = arguments_p_t(Params, Ctx, Trav), + {BodyAst, Inf, _} = p_t(Body, Ctx#js_ctx{action = get}, wrap_add_scope(Trav1)), + Ast = erl_syntax:fun_expr([erl_syntax:clause([ArgsVar], none, append_asts(ArgsAst, BodyAst))]), + {{Ast, Inf}, {Ctx, Trav1}}. func(Name, Params, Body, Ctx, Trav) -> - {Params1, _, Trav1} = p_t(Params, Ctx#js_ctx{action = set}, Trav), - {Ast, Inf, _} = p_t(Body, Ctx#js_ctx{action = get}, wrap_add_scope(Trav1)), case Ctx#js_ctx.global of true-> - Ast1 = erl_syntax:function(erl_syntax:atom(global_prefix(Name)), - [erl_syntax:clause(Params1, none, Ast)]), - Export = erl_syntax:arity_qualifier(erl_syntax:atom(global_prefix(Name)), - erl_syntax:integer(length(Params))), + {ArgsVar, ArgsAst, Trav1} = arguments_p_t(Params, Ctx, Trav), + + {BodyAst, Inf, _} = p_t(Body, Ctx#js_ctx{action = get}, wrap_add_scope(Trav1)), + Ast = erl_syntax:function(erl_syntax:atom(global_prefix(Name)), + [erl_syntax:clause([ArgsVar], none, append_asts(ArgsAst, BodyAst))]), + Export = erl_syntax:arity_qualifier( + erl_syntax:atom(global_prefix(Name)), + erl_syntax:integer(1)), Exports = [Export | Inf#ast_inf.export_asts], - {{Ast1, Inf#ast_inf{export_asts = Exports}}, {Ctx, Trav1}}; + {{Ast, Inf#ast_inf{export_asts = Exports}}, {Ctx, Trav1}}; + _ -> - {{FunVar, _}, {_, Trav2}} = var_ast(Name, Ctx#js_ctx{action = set}, Trav1), - Ast1 = erl_syntax:fun_expr([erl_syntax:clause(Params1, none, Ast)]), - {{erl_syntax:match_expr(FunVar, Ast1), Inf}, {Ctx, Trav2}} + {{FunVar, _}, {_, Trav1}} = var_ast(Name, Ctx#js_ctx{action = set}, Trav), + {ArgsVar, ArgsAst, Trav2} = arguments_p_t(Params, Ctx, Trav1), + {BodyAst, Inf, _} = p_t(Body, Ctx#js_ctx{action = get}, wrap_add_scope(Trav2)), + Ast = erl_syntax:fun_expr([erl_syntax:clause([ArgsVar], none, append_asts(ArgsAst, BodyAst))]), + {{erl_syntax:match_expr(FunVar, Ast), Inf}, {Ctx, Trav2}} end. +arguments_p_t([], Ctx, Trav) -> + {{Args, _}, {_, Trav1}} = var_ast(arguments, Ctx#js_ctx{action = set}, Trav), + {Args, [], Trav1}; +arguments_p_t(Params, Ctx, Trav) -> + {{Args, _}, {_, Trav1}} = var_ast(arguments, Ctx#js_ctx{action = set}, Trav), + {VarName, Trav2} = build_var_name("arguments_length", Trav1), + ArgsLen = erl_syntax:variable(VarName), + {Params1, _, Trav3} = p_t(Params, Ctx#js_ctx{action = set}, Trav2), + Clauses = lists:map( + fun(I) -> + Guard = erl_syntax:infix_expr( + ArgsLen, + erl_syntax:operator('>='), + erl_syntax:integer(I)), + ArgVals = lists:map( + fun(J) -> + case J =< I of + true -> + erl_syntax:application( + erl_syntax:atom(lists), + erl_syntax:atom(nth), + [erl_syntax:integer(J), Args]); + false -> + erl_syntax:atom(undefined) + end + end, lists:seq(1, length(Params1))), + Body = erl_syntax:match_expr( + erl_syntax:list(Params1), + erl_syntax:list(ArgVals)), + erl_syntax:clause([ArgsLen], Guard, [Body]) + end, lists:seq(length(Params1), 0, -1)), + AstList = erl_syntax:case_expr( + erl_syntax:application(none, erl_syntax:atom(length), [Args]), + Clauses), + {Args, AstList, Trav3}. + call(string, String, DotSepNames, Args, Ctx, Trav) -> Arity = length(Args), @@ -745,12 +786,12 @@ call(Name, DotSepNames, Args, Ctx, Trav) -> {Mod, Func} -> call2(Mod, Func, Args, Ctx, Trav); {Mod, Func, Arg} -> - {{VarArg, _}, _} = var_ast(Arg, Ctx#js_ctx{action = get}, Trav), - call2(Mod, Func, VarArg, Args, Ctx, Trav); + {{VarArgs, _}, _} = var_ast(Arg, Ctx#js_ctx{action = get}, Trav), + call2(Mod, Func, VarArgs, Args, Ctx, Trav); _ -> - {{VarAst, _}, _} = var_ast(Name, Ctx#js_ctx{action = get}, Trav), + {{VarArgs, _}, _} = var_ast(Name, Ctx#js_ctx{action = get}, Trav), {Args1, _, Trav1} = p_t(Args, Ctx, Trav), - Ast = erl_syntax:application(none, VarAst, Args1), + Ast = erl_syntax:application(none, VarArgs, [erl_syntax:list(Args1)]), maybe_global({{Ast, #ast_inf{}}, {Ctx, Trav1}}) end. diff --git a/src/erlyjs_testsuite.erl b/src/erlyjs_testsuite.erl index 808426c..09473a8 100644 --- a/src/erlyjs_testsuite.erl +++ b/src/erlyjs_testsuite.erl @@ -87,35 +87,35 @@ run2(Dir) -> end. -recreate_scanner_parser() -> - crypto:start(), - case recreate(erlyjs:scanner_src(), scanner) of - ok -> - recreate(erlyjs:parser_src(), parser); - Err -> - Err - end. - - -recreate(File, What) -> - Func = list_to_atom(lists:concat(['create_', What])), - case file:read_file(File) of - {ok, Data} -> - CheckSum = binary_to_list(crypto:sha(Data)), - Key = list_to_atom(lists:concat([erlyjs, Func, '_checksum'])), - case get(Key) of - CheckSum -> - ok; - _ -> - erlyjs:Func(), - put(Key, CheckSum), - io:format("Recompiling: ~p ...~n",[What]), - ok - end; - _ -> - erlyjs:Func(), - recreate(File, What) - end. +%recreate_scanner_parser() -> +% crypto:start(), +% case recreate(erlyjs:scanner_src(), scanner) of +% ok -> +% recreate(erlyjs:parser_src(), parser); +% Err -> +% Err +% end. +% +% +%recreate(File, What) -> +% Func = list_to_atom(lists:concat(['create_', What])), +% case file:read_file(File) of +% {ok, Data} -> +% CheckSum = binary_to_list(crypto:sha(Data)), +% Key = list_to_atom(lists:concat([erlyjs, Func, '_checksum'])), +% case get(Key) of +% CheckSum -> +% ok; +% _ -> +% erlyjs:Func(), +% put(Key, CheckSum), +% io:format("Recompiling: ~p ...~n",[What]), +% ok +% end; +% _ -> +% erlyjs:Func(), +% recreate(File, What) +% end. fold_tests(RegExp, Verbose, Dir) -> @@ -138,10 +138,10 @@ test(File, Verbose) -> case erlyjs_compiler:compile(File, Module, Options) of ok -> M = list_to_atom(Module), - Expected = M:js_test_ok(), - Args = M:js_test_args(), + Expected = M:js_test_ok([]), + Args = M:js_test_args([]), M:jsinit(), - Result = case catch apply(M, js_test, Args) of + Result = case catch apply(M, js_test, [Args]) of Expected -> io:format("ok ~n"), ok; diff --git a/tests/functions/varargs_1.js b/tests/functions/varargs_1.js new file mode 100644 index 0000000..580fb99 --- /dev/null +++ b/tests/functions/varargs_1.js @@ -0,0 +1,28 @@ +// Mandatory. Return here a description of the test case. +function test_description() { + return "varargs"; +} + +// Mandatory. Return here an array of arguments the testsuite will use +// to invoke the test() function. For no arguments return an empty array. +function test_args() { + return []; +} + +// Mandatory. Return here the expected test result. +function test_ok() { + return undefined; +} + +// Optional. Provide here any global code. + + +// Mandatory. The actual test. +// Testsuite invokes this function with the arguments from test_args() +// and compares the return value with the expected result from test_ok(). +function test() { + function abc(a, b, c) { + return a; + } + return abc(); +} diff --git a/tests/functions/varargs_2.js b/tests/functions/varargs_2.js new file mode 100644 index 0000000..86d8aad --- /dev/null +++ b/tests/functions/varargs_2.js @@ -0,0 +1,28 @@ +// Mandatory. Return here a description of the test case. +function test_description() { + return "varargs"; +} + +// Mandatory. Return here an array of arguments the testsuite will use +// to invoke the test() function. For no arguments return an empty array. +function test_args() { + return []; +} + +// Mandatory. Return here the expected test result. +function test_ok() { + return 1; +} + +// Optional. Provide here any global code. + + +// Mandatory. The actual test. +// Testsuite invokes this function with the arguments from test_args() +// and compares the return value with the expected result from test_ok(). +function test() { + function abc(a, b, c) { + return a; + } + return abc(1, 2, 3); +} diff --git a/tests/functions/varargs_3.js b/tests/functions/varargs_3.js new file mode 100644 index 0000000..758f012 --- /dev/null +++ b/tests/functions/varargs_3.js @@ -0,0 +1,28 @@ +// Mandatory. Return here a description of the test case. +function test_description() { + return "varargs"; +} + +// Mandatory. Return here an array of arguments the testsuite will use +// to invoke the test() function. For no arguments return an empty array. +function test_args() { + return []; +} + +// Mandatory. Return here the expected test result. +function test_ok() { + return undefined; +} + +// Optional. Provide here any global code. + + +// Mandatory. The actual test. +// Testsuite invokes this function with the arguments from test_args() +// and compares the return value with the expected result from test_ok(). +function test() { + function abc(a, b, c) { + return c; + } + return abc(1, 2); +} diff --git a/tests/functions/varargs_4.js b/tests/functions/varargs_4.js new file mode 100644 index 0000000..ee626d1 --- /dev/null +++ b/tests/functions/varargs_4.js @@ -0,0 +1,28 @@ +// Mandatory. Return here a description of the test case. +function test_description() { + return "varargs"; +} + +// Mandatory. Return here an array of arguments the testsuite will use +// to invoke the test() function. For no arguments return an empty array. +function test_args() { + return []; +} + +// Mandatory. Return here the expected test result. +function test_ok() { + return 3; +} + +// Optional. Provide here any global code. + + +// Mandatory. The actual test. +// Testsuite invokes this function with the arguments from test_args() +// and compares the return value with the expected result from test_ok(). +function test() { + function abc(a, b, c) { + return c; + } + return abc(1, 2, 3, 4, 5); +}