diff --git a/lib/elixir/lib/kernel.ex b/lib/elixir/lib/kernel.ex index 0821c82572..3b86384ee6 100644 --- a/lib/elixir/lib/kernel.ex +++ b/lib/elixir/lib/kernel.ex @@ -2173,8 +2173,8 @@ defmodule Kernel do "This will" end """ - defmacro cond([do: { :->, _, pairs }]) do - [{ [condition], meta, clause }|t] = :lists.reverse pairs + defmacro cond([do: pairs]) do + [{ :->, meta, [[condition], clause] }|t] = :lists.reverse pairs new_acc = case condition do @@ -2205,22 +2205,20 @@ defmodule Kernel do # end # end # - defp build_cond_clauses([{ [condition], new, clause }|t], acc, old) do - stab = { :->, [], [falsy_clause(old, acc), truthy_clause(new, clause)] } - acc = quote do - case unquote(condition), do: unquote(stab) - end + defp build_cond_clauses([{ :->, new, [[condition], clause] }|t], acc, old) do + clauses = [falsy_clause(old, acc), truthy_clause(new, clause)] + acc = quote do: (case unquote(condition), do: unquote(clauses)) build_cond_clauses(t, acc, new) end defp build_cond_clauses([], acc, _), do: acc defp falsy_clause(meta, acc) do - { [quote(do: unquote(cond_var) when unquote(cond_var) in [false, nil])], meta, acc } + { :->, meta, [[quote(do: unquote(cond_var) when unquote(cond_var) in [false, nil])], acc] } end defp truthy_clause(meta, clause) do - { [quote(do: _)], meta, clause } + { :->, meta, [[quote(do: _)], clause] } end # Setting cond: true in metadata turns on a small optimization diff --git a/lib/elixir/lib/kernel/typespec.ex b/lib/elixir/lib/kernel/typespec.ex index 42217cdc2a..48958803a9 100644 --- a/lib/elixir/lib/kernel/typespec.ex +++ b/lib/elixir/lib/kernel/typespec.ex @@ -552,11 +552,11 @@ defmodule Kernel.Typespec do defp typespec_to_ast({ :type, line, :fun, [{:type, _, :product, args}, result] }) do args = lc arg inlist args, do: typespec_to_ast(arg) - { :->, [line: line], [{args, [line: line], typespec_to_ast(result)}] } + [{ :->, [line: line], [args, typespec_to_ast(result)] }] end defp typespec_to_ast({ :type, line, :fun, [args, result] }) do - { :->, [line: line], [{[typespec_to_ast(args)], [line: line], typespec_to_ast(result)}] } + [{ :->, [line: line], [[typespec_to_ast(args)], typespec_to_ast(result)] }] end defp typespec_to_ast({ :type, line, :fun, [] }) do @@ -669,11 +669,7 @@ defmodule Kernel.Typespec do end # Handle funs - defp typespec({:->, meta, [{[{:fun, _, arguments}], cmeta, return}]}, vars, caller) when is_list(arguments) do - typespec({:->, meta, [{arguments, cmeta, return}]}, vars, caller) - end - - defp typespec({:->, meta, [{arguments, _, return}]}, vars, caller) when is_list(arguments) do + defp typespec([{:->, meta, [arguments, return]}], vars, caller) when is_list(arguments) do args = fn_args(meta, arguments, return, vars, caller) { :type, line(meta), :fun, args } end diff --git a/lib/elixir/lib/macro.ex b/lib/elixir/lib/macro.ex index 1ab626bc73..100f2e503d 100644 --- a/lib/elixir/lib/macro.ex +++ b/lib/elixir/lib/macro.ex @@ -5,8 +5,11 @@ defmodule Macro do Conveniences for working with macros. """ - @typedoc "Abstract Syntax Tree (AST) node" - @type t :: { t, t } | { t, Keyword.t, t } | atom | number | binary | list + @typedoc "Abstract Syntax Tree (AST)" + @type t :: node | { t, t } | atom | number | binary | [t] + + @typedoc "The AST node (remaining ones are literals)" + @type node :: { node | atom, Keyword.t, atom | [t] } @binary_ops [ :===, :!==, :==, :!=, :<=, :>=, @@ -309,22 +312,22 @@ defmodule Macro do end # Fn keyword - def to_string({ :fn, _, [{ :->, _, [{_, _, tuple}] } = arrow] } = ast, fun) + def to_string({ :fn, _, [{ :->, _, [_, tuple] }] = arrow } = ast, fun) when not is_tuple(tuple) or elem(tuple, 0) != :__block__ do fun.(ast, "fn " <> arrow_to_string(arrow, fun) <> " end") end - def to_string({ :fn, _, [{ :->, _, [_] } = block] } = ast, fun) do + def to_string({ :fn, _, [{ :->, _, _ }] = block } = ast, fun) do fun.(ast, "fn " <> block_to_string(block, fun) <> "\nend") end - def to_string({ :fn, _, [block] } = ast, fun) do + def to_string({ :fn, _, block } = ast, fun) do block = adjust_new_lines block_to_string(block, fun), "\n " fun.(ast, "fn\n " <> block <> "\nend") end # left -> right - def to_string({ :->, _, _ } = ast, fun) do + def to_string([{ :->, _, _ }|_] = ast, fun) do fun.(ast, "(" <> arrow_to_string(ast, fun, true) <> ")") end @@ -442,8 +445,8 @@ defmodule Macro do atom_to_binary(key) <> "\n " <> block <> "\n" end - defp block_to_string({ :->, _, exprs }, fun) do - Enum.map_join(exprs, "\n", fn({ left, _, right }) -> + defp block_to_string([{ :->, _, _ }|_] = block, fun) do + Enum.map_join(block, "\n", fn({ :->, _, [left, right] }) -> left = comma_join_or_empty_paren(left, fun, false) left <> "->\n " <> adjust_new_lines block_to_string(right, fun), "\n " end) @@ -483,8 +486,8 @@ defmodule Macro do defp op_to_string(expr, fun, _, _), do: to_string(expr, fun) - defp arrow_to_string({ :->, _, pairs }, fun, paren // false) do - Enum.map_join(pairs, "; ", fn({ left, _, right }) -> + defp arrow_to_string(pairs, fun, paren // false) do + Enum.map_join(pairs, "; ", fn({ :->, _, [left, right] }) -> left = comma_join_or_empty_paren(left, fun, paren) left <> "-> " <> to_string(right, fun) end) @@ -707,7 +710,9 @@ defmodule Macro do { tree, cache } end - @doc false # Used internally by Elixir + # TODO: Rewrite this function to use elixir_exp + # For such, there is no need for a cache. + @doc false def expand_all(tree, env, cache) do expand_all_until(expand(tree, env, cache), env) end diff --git a/lib/elixir/src/elixir_clauses.erl b/lib/elixir/src/elixir_clauses.erl index fdd5ab332b..0276dfa626 100644 --- a/lib/elixir/src/elixir_clauses.erl +++ b/lib/elixir/src/elixir_clauses.erl @@ -13,16 +13,24 @@ get_pairs(Meta, Key, Clauses, S) -> get_pairs(Meta, Key, Clauses, S, AllowNil) -> case lists:keyfind(Key, 1, Clauses) of - { Key, { '->', _, Pairs } } -> - [{ Key, PMeta, Left, Right } || { Left, PMeta, Right } <- Pairs]; + { Key, Pairs } when is_list(Pairs) -> + [get_pair(Meta, Key, Pair, S) || Pair <- Pairs]; { Key, nil } when AllowNil -> []; { Key, _ } -> - elixir_errors:syntax_error(Meta, S#elixir_scope.file, "expected pairs with -> for key ~ts", [Key]); + get_pairs_error(Meta, Key, S); _ -> [] end. +get_pair(_Meta, Key, { '->', Meta, [Left, Right] }, _S) -> + { Key, Meta, Left, Right }; +get_pair(Meta, Key, _Other, S) -> + get_pairs_error(Meta, Key, S). + +get_pairs_error(Meta, Key, S) -> + elixir_errors:compile_error(Meta, S#elixir_scope.file, "expected -> clauses for key ~ts", [Key]). + % Function for translating assigns. assigns(Fun, Args, #elixir_scope{context=Context, temp_vars=TempVars, diff --git a/lib/elixir/src/elixir_exp.erl b/lib/elixir/src/elixir_exp.erl index 97e110aaf4..770d523cbb 100644 --- a/lib/elixir/src/elixir_exp.erl +++ b/lib/elixir/src/elixir_exp.erl @@ -203,6 +203,10 @@ expand({ quote, Meta, [_, _] }, E) -> %% Functions +%% TODO: Remove me. Temporary during refactoring. +expand({ '&', _, [Arg] } = Original, E) when is_integer(Arg) -> + { Original, E }; + expand({ '&', Meta, [Arg] }, E) -> % assert_no_match_or_guard_scope(Meta, '&', S), case elixir_fn:capture(Meta, Arg, E) of diff --git a/lib/elixir/src/elixir_fn.erl b/lib/elixir/src/elixir_fn.erl index f0ae033cbf..f48642a89c 100644 --- a/lib/elixir/src/elixir_fn.erl +++ b/lib/elixir/src/elixir_fn.erl @@ -5,7 +5,7 @@ -include("elixir.hrl"). fn(Meta, Clauses, S) -> - Transformer = fun({ ArgsWithGuards, CMeta, Expr }, Acc) -> + Transformer = fun({ '->', CMeta, [ArgsWithGuards, Expr] }, Acc) -> { Args, Guards } = elixir_clauses:extract_splat_guards(ArgsWithGuards), elixir_clauses:assigns_block(?line(CMeta), fun translate_fn_match/2, Args, [Expr], Guards, umergec(S, Acc)) end, @@ -25,6 +25,8 @@ translate_fn_match(Arg, S) -> { TArg, TS } = elixir_translator:translate(Arg, S#elixir_scope{extra=fn_match}), { TArg, TS#elixir_scope{extra=S#elixir_scope.extra} }. +%% Capture + capture(Meta, { '/', _, [{ { '.', _, [_, F] } = Dot, RequireMeta , [] }, A] }, E) when is_atom(F), is_integer(A) -> Args = [{ '&', [], [X] } || X <- lists:seq(1, A)], capture_require(Meta, { Dot, RequireMeta, Args }, E, true); @@ -97,7 +99,7 @@ do_capture(Meta, Expr, E, Sequential) -> invalid_capture(Meta, Expr, E); { EExpr, EDict } -> EVars = validate(Meta, EDict, 1, E), - Fn = { fn, Meta, [{ '->', [], [{ EVars, Meta, EExpr }]}]}, + Fn = { fn, Meta, [{ '->', Meta, [EVars, EExpr]}]}, { expanded, Fn, E#elixir_env{macro_counter=E#elixir_env.macro_counter+1} } end. diff --git a/lib/elixir/src/elixir_parser.yrl b/lib/elixir/src/elixir_parser.yrl index 9e1cb49d9e..6276446483 100644 --- a/lib/elixir/src/elixir_parser.yrl +++ b/lib/elixir/src/elixir_parser.yrl @@ -578,7 +578,7 @@ extract_identifier(Other) -> Other. %% Fn build_fn(Op, Stab) -> - { fn, meta(Op), [Stab] }. + { fn, meta(Op), Stab }. %% Access @@ -632,20 +632,20 @@ string_tokens_parse(Line, Tokens) -> %% Keywords build_stab([{ '->', Meta, [Left, Right] }|T]) -> - { '->', Meta, build_stab(Meta, T, Left, [Right], []) }; + build_stab(Meta, T, Left, [Right], []); build_stab(Else) -> build_block(Else). build_stab(Old, [{ '->', New, [Left, Right] }|T], Marker, Temp, Acc) -> - H = { Marker, Old, build_block(lists:reverse(Temp)) }, + H = { '->', Old, [Marker, build_block(lists:reverse(Temp))] }, build_stab(New, T, Left, [Right], [H|Acc]); build_stab(Meta, [H|T], Marker, Temp, Acc) -> build_stab(Meta, T, Marker, [H|Temp], Acc); build_stab(Meta, [], Marker, Temp, Acc) -> - H = { Marker, Meta, build_block(lists:reverse(Temp)) }, + H = { '->', Meta, [Marker, build_block(lists:reverse(Temp))] }, lists:reverse([H|Acc]). %% Every time the parser sees a (unquote_splicing()) diff --git a/lib/elixir/src/elixir_translator.erl b/lib/elixir/src/elixir_translator.erl index bed0f5cbd2..627e506e41 100644 --- a/lib/elixir/src/elixir_translator.erl +++ b/lib/elixir/src/elixir_translator.erl @@ -243,9 +243,9 @@ translate_each({ '&', Meta, [Arg] }, S) -> translate_each(TE, S#elixir_scope{macro_counter=MacroCounter}) end; -translate_each({ fn, Meta, [{ '->', _, Pairs }] }, S) -> +translate_each({ fn, Meta, Clauses }, S) -> assert_no_match_or_guard_scope(Meta, 'fn', S), - elixir_fn:fn(Meta, Pairs, S); + elixir_fn:fn(Meta, Clauses, S); %% Case diff --git a/lib/elixir/test/elixir/kernel/quote_test.exs b/lib/elixir/test/elixir/kernel/quote_test.exs index d32dd3aa04..8518288902 100644 --- a/lib/elixir/test/elixir/kernel/quote_test.exs +++ b/lib/elixir/test/elixir/kernel/quote_test.exs @@ -112,22 +112,22 @@ defmodule Kernel.QuoteTest do end test :when do - assert {:->,_,[{[{:when,_,[1,2,3,4]}],_,5}]} = quote(do: (1, 2, 3 when 4 -> 5)) - assert {:->,_,[{[{:when,_,[1,2,3,4]}],_,5}]} = quote(do: ((1, 2, 3) when 4 -> 5)) + assert [{:->,_,[[{:when,_,[1,2,3,4]}],5]}] = quote(do: (1, 2, 3 when 4 -> 5)) + assert [{:->,_,[[{:when,_,[1,2,3,4]}],5]}] = quote(do: ((1, 2, 3) when 4 -> 5)) - assert {:->,_,[{[{:when,_,[1,2,3,{:when,_,[4,5]}]}],_,6}]} = + assert [{:->,_,[[{:when,_,[1,2,3,{:when,_,[4,5]}]}],6]}] = quote(do: ((1, 2, 3) when 4 when 5 -> 6)) end test :stab do - assert { :->, _, [{[], _, _}] } = (quote do -> end) - assert { :->, _, [{[], _, _}] } = (quote do: (->)) + assert [{ :->, _, [[], nil] }] = (quote do -> end) + assert [{ :->, _, [[], nil] }] = (quote do: (->)) - assert { :->, _, [{[1], _, _}] } = (quote do 1 -> end) - assert { :->, _, [{[1], _, _}] } = (quote do: (1 ->)) + assert [{ :->, _, [[1], nil] }] = (quote do 1 -> end) + assert [{ :->, _, [[1], nil] }] = (quote do: (1 ->)) - assert { :->, _, [{[], _, 1}] } = (quote do -> 1 end) - assert { :->, _, [{[], _, 1}] } = (quote do: (-> 1)) + assert [{ :->, _, [[], 1] }] = (quote do -> 1 end) + assert [{ :->, _, [[], 1] }] = (quote do: (-> 1)) end test :bind_quoted do