From 89cbe93bba82fb0262e9ee66bc7c8fdd07751bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 21 Jan 2013 15:42:03 -0700 Subject: [PATCH] The AST now allows metadata to be attached to each node This means the second item in the AST is no longer an integer (representing the line), but a keywords list. Code that relies on the line information from AST or that manually generate AST nodes need to be properly updated. --- CHANGELOG.md | 3 + lib/eex/lib/eex.ex | 2 +- lib/eex/lib/eex/compiler.ex | 2 +- lib/elixir/include/elixir.hrl | 9 +- lib/elixir/lib/code.ex | 2 +- lib/elixir/lib/kernel.ex | 8 +- lib/elixir/lib/kernel/special_forms.ex | 20 +- lib/elixir/lib/kernel/typespec.ex | 170 ++++----- lib/elixir/lib/macro.ex | 10 +- lib/elixir/lib/protocol.ex | 6 +- lib/elixir/lib/record.ex | 6 +- lib/elixir/src/elixir.erl | 2 +- lib/elixir/src/elixir_clauses.erl | 63 ++-- lib/elixir/src/elixir_compiler.erl | 3 +- lib/elixir/src/elixir_def.erl | 17 +- lib/elixir/src/elixir_def_defaults.erl | 6 +- lib/elixir/src/elixir_def_overridable.erl | 13 +- lib/elixir/src/elixir_dispatch.erl | 86 +++-- lib/elixir/src/elixir_errors.erl | 75 ++-- lib/elixir/src/elixir_import.erl | 74 ++-- lib/elixir/src/elixir_interpolation.erl | 5 +- lib/elixir/src/elixir_literal.erl | 103 +++--- lib/elixir/src/elixir_macros.erl | 186 +++++----- lib/elixir/src/elixir_module.erl | 12 +- lib/elixir/src/elixir_parser.yrl | 87 ++--- lib/elixir/src/elixir_partials.erl | 28 +- lib/elixir/src/elixir_quote.erl | 134 +++++--- lib/elixir/src/elixir_scope.erl | 21 +- lib/elixir/src/elixir_tokenizer.erl | 2 +- lib/elixir/src/elixir_translator.erl | 344 ++++++++++--------- lib/elixir/src/elixir_tree_helpers.erl | 14 +- lib/elixir/src/elixir_try.erl | 244 ++++++------- lib/elixir/test/elixir/code_test.exs | 1 + lib/elixir/test/elixir/kernel/doc_test.exs | 4 +- lib/elixir/test/elixir/kernel/quote_test.exs | 10 +- lib/elixir/test/elixir/keyword_test.exs | 2 +- lib/elixir/test/elixir/macro_test.exs | 6 +- lib/elixir/test/elixir/typespec_test.exs | 22 +- lib/elixir/test/erlang/module_test.erl | 6 +- lib/elixir/test/erlang/string_test.erl | 21 +- lib/elixir/test/erlang/tokenizer_test.erl | 8 +- lib/elixir/test/erlang/translator_test.erl | 72 ++-- lib/ex_unit/lib/ex_unit/assertions.ex | 4 +- 43 files changed, 1009 insertions(+), 904 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a51ccbad1..a4a6362eb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,9 @@ * [String] `String.rstrip` and `String.lstrip` now verify if argument is a binary * [Typespec] Support `...` inside typespec's lists +* backwards incompatible changes + * [Kernel] The AST now allows metadata to be attached to each node. This means the second item in the AST is no longer an integer (representing the line), but a keywords list. Code that relies on the line information from AST or that manually generate AST nodes need to be properly updated + * deprecations * [Dict] Deprecate `Binary.Dict` and `OrdDict` in favor of `HashDict` and `List.Dict` * [File] Deprecate path related functions in favor of the module `Path` diff --git a/lib/eex/lib/eex.ex b/lib/eex/lib/eex.ex index 8d78a3f07d..3f14c7a396 100644 --- a/lib/eex/lib/eex.ex +++ b/lib/eex/lib/eex.ex @@ -189,7 +189,7 @@ defmodule EEx do @doc false def function_from_quoted(module, kind, name, args, source, info) do - args = Enum.map args, fn arg -> { arg, 0, nil } end + args = Enum.map args, fn arg -> { arg, [], nil } end quote = quote do unquote(kind).(unquote(name).(unquote_splicing(args)), do: unquote(source)) end diff --git a/lib/eex/lib/eex/compiler.ex b/lib/eex/lib/eex/compiler.ex index 3e6b936bb0..67e1582527 100644 --- a/lib/eex/lib/eex/compiler.ex +++ b/lib/eex/lib/eex/compiler.ex @@ -90,7 +90,7 @@ defmodule EEx.Compiler do defp maybe_block([]), do: nil defp maybe_block([h]), do: h - defp maybe_block(other), do: { :__block__, 0, other } + defp maybe_block(other), do: { :__block__, [], other } # Changes placeholder to real expression diff --git a/lib/elixir/include/elixir.hrl b/lib/elixir/include/elixir.hrl index 2452a13dc4..9cb221f16a 100644 --- a/lib/elixir/include/elixir.hrl +++ b/lib/elixir/include/elixir.hrl @@ -1,11 +1,12 @@ --define(ELIXIR_WRAP_CALL(Line, Module, Function, Args), +-define(wrap_call(Line, Module, Function, Args), { call, Line, { remote, Line, { atom, Line, Module }, { atom, Line, Function } }, Args }). --define(ELIXIR_ATOM_CONCAT(Atoms), list_to_atom(lists:concat(Atoms))). --define(ELIXIR_MACRO(Macro), list_to_atom(lists:concat(['MACRO-',Macro]))). +-define(atom_concat(Atoms), list_to_atom(lists:concat(Atoms))). +-define(elixir_macro(Macro), list_to_atom(lists:concat(['MACRO-',Macro]))). +-define(line(Opts), elixir_tree_helpers:get_line(Opts)). -record(elixir_scope, { context=nil, %% can be assign, guards or nil @@ -37,7 +38,7 @@ unquote=true }). -%% used in tokinization and interpolation +%% Used in tokenization and interpolation -define(is_digit(S), S >= $0 andalso S =< $9). -define(is_hex(S), ?is_digit(S) orelse (S >= $A andalso S =< $F) orelse (S >= $a andalso S =< $f)). diff --git a/lib/elixir/lib/code.ex b/lib/elixir/lib/code.ex index 961ef3cdda..00a4f36ae1 100644 --- a/lib/elixir/lib/code.ex +++ b/lib/elixir/lib/code.ex @@ -182,7 +182,7 @@ defmodule Code do defp unpack_ast(_line, []), do: nil defp unpack_ast(_line, [forms]) when not is_list(forms), do: forms - defp unpack_ast(line, forms), do: { :__block__, line, forms } + defp unpack_ast(line, forms), do: { :__block__, [line: line], forms } @doc """ Loads the given `file`. Accepts `relative_to` as an argument diff --git a/lib/elixir/lib/kernel.ex b/lib/elixir/lib/kernel.ex index 3ac9f1e8e2..a366d4be08 100644 --- a/lib/elixir/lib/kernel.ex +++ b/lib/elixir/lib/kernel.ex @@ -2584,7 +2584,7 @@ defmodule Kernel do """ defmacro left <> right do - concats = extract_concatenations({ :<>, 0, [left, right] }) + concats = extract_concatenations({ :<>, [], [left, right] }) quote do: << unquote_splicing(concats) >> end @@ -2601,7 +2601,7 @@ defmodule Kernel do """ defmacro first .. last do - { :{}, 0, [Elixir.Range, first, last] } + { :{}, [], [Elixir.Range, first, last] } end @doc """ @@ -2759,7 +2759,7 @@ defmodule Kernel do end defp pipeline_op(left, atom) when is_atom(atom) do - { { :., 0, [left, atom] }, 0, [] } + { { :., [], [left, atom] }, [], [] } end defp pipeline_op(_, other) do @@ -3180,7 +3180,7 @@ defmodule Kernel do end defp wrap_concatenation(other) do - { :::, 0, [other, { :binary, 0, nil }] } + { :::, [], [other, { :binary, [], nil }] } end # Builds cond clauses by nesting them recursively. diff --git a/lib/elixir/lib/kernel/special_forms.ex b/lib/elixir/lib/kernel/special_forms.ex index fcbe7ce6b5..1ef9872b96 100644 --- a/lib/elixir/lib/kernel/special_forms.ex +++ b/lib/elixir/lib/kernel/special_forms.ex @@ -326,7 +326,7 @@ defmodule Kernel.SpecialForms do ## Examples quote do: sum(1, 2, 3) - #=> { :sum, 0, [1, 2, 3] } + #=> { :sum, [], [1, 2, 3] } ## Explanation @@ -486,8 +486,8 @@ defmodule Kernel.SpecialForms do One of Elixir goals is to provide proper stacktrace whenever there is an exception. In order to work properly with macros, the default behavior - in quote is to set the line to 0. When a macro is invoked and the quoted - expressions is expanded, 0 is replaced by the line of the call site. + in quote is to not set a line. When a macro is invoked and the quoted + expressions is expanded, the call site line is inserted. This is a good behavior for the majority of the cases, except if the macro is defining new functions. Consider this example: @@ -549,13 +549,13 @@ defmodule Kernel.SpecialForms do Which would then return: - { :sum, 0, [1, { :value, 0, quoted }, 3] } + { :sum, [], [1, { :value, [], quoted }, 3] } Which is not the expected result. For this, we use unquote: value = 13 quote do: sum(1, unquote(value), 3) - #=> { :sum, 0, [1, 13, 3] } + #=> { :sum, [], [1, 13, 3] } """ defmacro unquote(expr) @@ -568,7 +568,7 @@ defmodule Kernel.SpecialForms do values = [2,3,4] quote do: sum(1, unquote_splicing(values), 5) - #=> { :sum, 0, [1, 2, 3, 4, 5] } + #=> { :sum, [], [1, 2, 3, 4, 5] } """ defmacro unquote_splicing(expr) @@ -634,7 +634,7 @@ defmodule Kernel.SpecialForms do and should not be invoked directly: quote do: (1; 2; 3) - #=> { :__block__, 0, [1,2,3] } + #=> { :__block__, [], [1,2,3] } """ defmacro __block__(args) @@ -657,13 +657,13 @@ defmodule Kernel.SpecialForms do It is usually compiled to an atom: quote do: Foo.Bar - { :__aliases__, 0, [:Foo,:Bar] } + { :__aliases__, [], [:Foo,:Bar] } Elixir represents `Foo.Bar` as `__aliases__` so calls can be unambiguously identified by the operator `:.`. For example: quote do: Foo.bar - {{:.,0,[{:__aliases__,0,[:Foo]},:bar]},0,[]} + {{:.,[],[{:__aliases__,[],[:Foo]},:bar]},[],[]} Whenever an expression iterator sees a `:.` as the tuple key, it can be sure that it represents a call and the second argument @@ -680,7 +680,7 @@ defmodule Kernel.SpecialForms do 4) When the head element of aliases is not an atom, it is expanded at runtime: quote do: some_var.Foo - {:__aliases__,0,[{:some_var,0,:quoted},:Bar]} + {:__aliases__,[],[{:some_var,[],:quoted},:Bar]} Since `some_var` is not available at compilation time, the compiler expands such expression to: diff --git a/lib/elixir/lib/kernel/typespec.ex b/lib/elixir/lib/kernel/typespec.ex index ee31db100f..ad9d21204c 100644 --- a/lib/elixir/lib/kernel/typespec.ex +++ b/lib/elixir/lib/kernel/typespec.ex @@ -57,13 +57,13 @@ defmodule Kernel.Typespec do (... -> any) Function with arity of zero: - + (fun() -> type) or (() -> type) Function with some arity: - + (fun(type, type) -> type) or ((type, type) -> type) @@ -73,7 +73,7 @@ defmodule Kernel.Typespec do ## Notes Elixir discourages the use of type `string()` as it might be confused - with binaries which are referred to as "strings" in Elixir (as opposed to + with binaries which are referred to as "strings" in Elixir (as opposed to character lists). In order to use the type that is called `string()` in Erlang, one has to use the `char_list()` type which is a synonym to `string()`. If yu use `string()`, you'll get a warning from the compiler. @@ -96,7 +96,7 @@ defmodule Kernel.Typespec do """ defmacro deftype(type) do quote do - Kernel.Typespec.deftype(:type, (quote line: :keep, do: unquote(type)), __ENV__) + Kernel.Typespec.deftype(:type, unquote(Macro.escape type), __ENV__) end end @@ -111,7 +111,7 @@ defmodule Kernel.Typespec do """ defmacro defopaque(type) do quote do - Kernel.Typespec.deftype(:opaque, (quote line: :keep, do: unquote(type)), __ENV__) + Kernel.Typespec.deftype(:opaque, unquote(Macro.escape type), __ENV__) end end @@ -126,7 +126,7 @@ defmodule Kernel.Typespec do """ defmacro deftypep(type) do quote do - Kernel.Typespec.deftype(:typep, (quote line: :keep, do: unquote(type)), __ENV__) + Kernel.Typespec.deftype(:typep, unquote(Macro.escape type), __ENV__) end end @@ -141,7 +141,7 @@ defmodule Kernel.Typespec do """ defmacro defspec(spec) do quote do - Kernel.Typespec.defspec(:spec, (quote line: :keep, do: unquote spec), __ENV__) + Kernel.Typespec.defspec(:spec, unquote(Macro.escape spec), __ENV__) end end @@ -156,7 +156,7 @@ defmodule Kernel.Typespec do """ defmacro defcallback(spec) do quote do - Kernel.Typespec.defspec(:callback, (quote line: :keep, do: unquote(spec)), __ENV__) + Kernel.Typespec.defspec(:callback, unquote(Macro.escape spec), __ENV__) end end @@ -227,23 +227,23 @@ defmodule Kernel.Typespec do """ def spec_to_ast(name, { :type, line, :fun, [{:type, _, :product, args},result] }) do args = lc arg inlist args, do: typespec_to_ast(arg) - { :::, line, [{ name, line, args }, typespec_to_ast(result)] } + { :::, [line: line], [{ name, [line: line], args }, typespec_to_ast(result)] } end def spec_to_ast(name, { :type, line, :fun, [] }) do - { :::, line, [{ name, line, [] }, quote(do: term)] } + { :::, [line: line], [{ name, [line: line], [] }, quote(do: term)] } end def spec_to_ast(name, { :type, line, :bounded_fun, [{ :type, _, :fun, [{ :type, _, :product, args }, result] }, constraints] }) do [h|t] = lc {:type, line, :constraint, [{:atom, _, :is_subtype}, [var, type]]} inlist constraints do - { :is_subtype, line, [typespec_to_ast(var), typespec_to_ast(type)] } + { :is_subtype, [line: line], [typespec_to_ast(var), typespec_to_ast(type)] } end args = lc arg inlist args, do: typespec_to_ast(arg) guards = Enum.reduce t, h, fn(x, acc) -> { :and, line, [acc, x] } end - { :::, line, [{ :when, line, [{ name, line, args }, guards] }, typespec_to_ast(result)] } + { :::, [line: line], [{ :when, [line: line], [{ name, [line: line], args }, guards] }, typespec_to_ast(result)] } end @doc """ @@ -252,7 +252,7 @@ defmodule Kernel.Typespec do def type_to_ast({ { :record, record }, fields, args }) when is_atom(record) do fields = lc field inlist fields, do: typespec_to_ast(field) args = lc arg inlist args, do: typespec_to_ast(arg) - type = { :{}, 0, [record|fields] } + type = { :{}, [], [record|fields] } quote do: unquote(record)(unquote_splicing(args)) :: unquote(type) end @@ -351,7 +351,7 @@ defmodule Kernel.Typespec do end def deftype(kind, type, caller) do - do_deftype(kind, type, { :term, caller.line, nil }, caller) + do_deftype(kind, type, { :term, [line: caller.line], nil }, caller) end defp do_deftype(kind, { name, _, args }, definition, caller) do @@ -372,25 +372,26 @@ defmodule Kernel.Typespec do end @doc false - def defspec(type, {:::, _, [{ :when, _, [{ name, line, args }, constraints_guard] }, return] }, caller) do + def defspec(type, {:::, _, [{ :when, _, [{ name, meta, args }, constraints_guard] }, return] }, caller) do if is_atom(args), do: args = [] constraints = guard_to_constraints(constraints_guard, caller) - spec = { :type, line, :fun, fn_args(line, args, return, Keyword.keys(constraints), caller) } - spec = { :type, line, :bounded_fun, [spec, Keyword.values(constraints)] } + spec = { :type, line(meta), :fun, fn_args(meta, args, return, Keyword.keys(constraints), caller) } + spec = { :type, line(meta), :bounded_fun, [spec, Keyword.values(constraints)] } code = { { name, Kernel.length(args) }, spec } Module.compile_typespec(caller.module, type, code) code end - def defspec(type, {:::, _, [{ name, line, args }, return]}, caller) do + def defspec(type, {:::, _, [{ name, meta, args }, return]}, caller) do if is_atom(args), do: args = [] - spec = { :type, line, :fun, fn_args(line, args, return, [], caller) } + spec = { :type, line(meta), :fun, fn_args(meta, args, return, [], caller) } code = { { name, Kernel.length(args) }, spec } Module.compile_typespec(caller.module, type, code) code end - defp guard_to_constraints({ :is_subtype, line, [{ name, _, _ }, type] }, caller) do + defp guard_to_constraints({ :is_subtype, meta, [{ name, _, _ }, type] }, caller) do + line = line(meta) contraints = [{ :atom, line, :is_subtype }, [{:var, line, name}, typespec(type, [], caller)]] [{ name, { :type, line, :constraint, contraints } }] end @@ -407,7 +408,7 @@ defmodule Kernel.Typespec do defp typespec_to_ast({ :type, line, :tuple, args }) do args = lc arg inlist args, do: typespec_to_ast(arg) - { :{}, line, args } + { :{}, [line: line], args } end defp typespec_to_ast({ :type, line, :binary, [arg1, arg2] }) do @@ -425,16 +426,16 @@ defmodule Kernel.Typespec do defp typespec_to_ast({ :type, line, :union, args }) do args = lc arg inlist args, do: typespec_to_ast(arg) Enum.reduce tl(args), hd(args), - fn(arg, expr) -> { :|, line, [expr, arg] } end + fn(arg, expr) -> { :|, [line: line], [expr, arg] } end end defp typespec_to_ast({ :type, line, :fun, [{:type, _, :product, args},result] }) do args = lc arg inlist args, do: typespec_to_ast(arg) - { :"->", line, [{args, typespec_to_ast(result)}] } + { :"->", [line: line], [{args, typespec_to_ast(result)}] } end defp typespec_to_ast({ :type, line, :fun, [args, result] }) do - { :"->", line, [{[typespec_to_ast(args)], typespec_to_ast(result)}] } + { :"->", [line: line], [{[typespec_to_ast(args)], typespec_to_ast(result)}] } end defp typespec_to_ast({ :type, line, :fun, [] }) do @@ -442,18 +443,18 @@ defmodule Kernel.Typespec do end defp typespec_to_ast({ :type, line, :range, [left, right] }) do - { :"..", line, [typespec_to_ast(left), typespec_to_ast(right)] } + { :"..", [line: line], [typespec_to_ast(left), typespec_to_ast(right)] } end defp typespec_to_ast({ :type, line, name, args }) do args = lc arg inlist args, do: typespec_to_ast(arg) - { name, line, args } + { name, [line: line], args } end defp typespec_to_ast({ :var, line, var }) do var = case atom_to_binary(var) do - <<"_", c :: [binary, size(1)], rest :: binary>> -> + <<"_", c :: [binary, size(1)], rest :: binary>> -> binary_to_atom("_#{String.downcase(c)}#{rest}") <> -> binary_to_atom("#{String.downcase(c)}#{rest}") @@ -468,15 +469,15 @@ defmodule Kernel.Typespec do defp typespec_to_ast({ :remote_type, line, [mod, name, args] }) do args = lc arg inlist args, do: typespec_to_ast(arg) - dot = { :., line, [typespec_to_ast(mod), typespec_to_ast(name)] } - { dot, line, args } + dot = { :., [line: line], [typespec_to_ast(mod), typespec_to_ast(name)] } + { dot, [line: line], args } end defp typespec_to_ast({ :ann_type, line, [var, type] }) do - { :::, line, [typespec_to_ast(var), typespec_to_ast(type)] } + { :::, [line: line], [typespec_to_ast(var), typespec_to_ast(type)] } end - defp typespec_to_ast({ :typed_record_field, + defp typespec_to_ast({ :typed_record_field, { :record_field, line, { :atom, line1, name }}, type }) do typespec_to_ast({ :ann_type, line, [{ :var, line1, name }, type] }) @@ -498,29 +499,36 @@ defmodule Kernel.Typespec do ## From AST conversion + defp line(meta) do + case :lists.keyfind(:line, 1, meta) do + { :line, line } -> line + false -> 0 + end + end + # Handle unions - defp typespec({ :|, line, [_,_] } = exprs, vars, caller) do + defp typespec({ :|, meta, [_,_] } = exprs, vars, caller) do exprs = :lists.reverse(collect_union(exprs)) union = lc e inlist exprs, do: typespec(e, vars, caller) - { :type, line, :union, union } + { :type, line(meta), :union, union } end # Handle binaries - defp typespec({:<<>>, line, []}, _,_) do - {:type, line, :binary, [{:integer, line, 0}, {:integer, line, 0}]} + defp typespec({:<<>>, meta, []}, _,_) do + {:type, line(meta), :binary, [{:integer, line(meta), 0}, {:integer, line(meta), 0}]} end - defp typespec({:<<>>, line, [{:::, _, [{:_, line1, atom}, {:*, _, [{:_, line2, atom}, unit]}]}]}, _, _) when is_atom(atom) do - {:type, line, :binary, [{:integer, line1, 0}, {:integer, line2, unit}]} + defp typespec({:<<>>, meta, [{:::, _, [{:_, meta1, atom}, {:*, _, [{:_, meta2, atom}, unit]}]}]}, _, _) when is_atom(atom) do + {:type, line(meta), :binary, [{:integer, line(meta1), 0}, {:integer, line(meta2), unit}]} end - defp typespec({:<<>>, line, [{:::, line1, [{:_, line2, atom}, base]}]}, _, _) when is_atom(atom) do - {:type, line, :binary, [{:integer, line1, base}, {:integer, line2, 0}]} + defp typespec({:<<>>, meta, [{:::, meta1, [{:_, meta2, atom}, base]}]}, _, _) when is_atom(atom) do + {:type, line(meta), :binary, [{:integer, line(meta1), base}, {:integer, line(meta2), 0}]} end # Handle ranges - defp typespec({:"..", line, args}, vars, caller) do - typespec({:range, line, args}, vars, caller) + defp typespec({:"..", meta, args}, vars, caller) do + typespec({:range, meta, args}, vars, caller) end # Handle special forms @@ -534,79 +542,79 @@ defmodule Kernel.Typespec do end # Handle funs - defp typespec({:->, line, [{[{:fun, _, arguments}], return}]}, vars, caller) when is_list(arguments) do - typespec({:->, line, [{arguments, return}]}, vars, caller) + defp typespec({:->, meta, [{[{:fun, _, arguments}], return}]}, vars, caller) when is_list(arguments) do + typespec({:->, meta, [{arguments, return}]}, vars, caller) end - defp typespec({:->, line, [{arguments, return}]}, vars, caller) when is_list(arguments) do - args = fn_args(line, arguments, return, vars, caller) - { :type, line, :fun, args } + 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 # Handle type operator - defp typespec({:"::", line, [var, expr] }, vars, caller) do + defp typespec({:"::", meta, [var, expr] }, vars, caller) do left = typespec(var, [elem(var, 0)|vars], caller) right = typespec(expr, vars, caller) - { :ann_type, line, [left, right] } + { :ann_type, line(meta), [left, right] } end # Handle unary ops - defp typespec({op, line, [integer]}, _, _) when op in [:+, :-] and is_integer(integer) do - { :op, line, op, {:integer, line, integer} } + defp typespec({op, meta, [integer]}, _, _) when op in [:+, :-] and is_integer(integer) do + { :op, line(meta), op, {:integer, line(meta), integer} } end # Handle access macro - defp typespec({{:., line, [Kernel, :access]}, line1, [target, args]}, vars, caller) do - access = {{:., line, [Kernel, :access]}, line1, + defp typespec({{:., meta, [Kernel, :access]}, meta1, [target, args]}, vars, caller) do + access = {{:., meta, [Kernel, :access]}, meta1, [target, args ++ [_: (quote hygiene: false, do: any)]]} typespec(Macro.expand(access, caller), vars, caller) end # Handle remote calls - defp typespec({{:., line, [remote, name]}, _, args}, vars, caller) do + defp typespec({{:., meta, [remote, name]}, _, args}, vars, caller) do remote = Macro.expand remote, caller unless is_atom(remote), do: raise ArgumentError, message: "invalid remote in typespec" - remote_type({typespec(remote, vars, caller), line, typespec(name, vars, caller), args}, vars, caller) + remote_type({typespec(remote, vars, caller), meta, typespec(name, vars, caller), args}, vars, caller) end # Handle tuples - defp typespec({:tuple, line, atom}, vars, caller) when is_atom(atom) do - typespec({:{}, line, []}, vars, caller) + defp typespec({:tuple, meta, atom}, vars, caller) when is_atom(atom) do + typespec({:{}, meta, []}, vars, caller) end - defp typespec({:{}, line, []}, _, _) do - { :type, line, :tuple, :any } + defp typespec({:{}, meta, []}, _, _) do + { :type, line(meta), :tuple, :any } end - defp typespec({:{}, line, t}, vars, caller) when is_list(t) do + defp typespec({:{}, meta, t}, vars, caller) when is_list(t) do args = lc e inlist t, do: typespec(e, vars, caller) - { :type, line, :tuple, args } + { :type, line(meta), :tuple, args } end # Handle variables or local calls - defp typespec({name, line, atom}, vars, caller) when is_atom(atom) do + defp typespec({name, meta, atom}, vars, caller) when is_atom(atom) do if List.member?(vars, name) do - { :var, line, name } + { :var, line(meta), name } else - typespec({name, line, []}, vars, caller) + typespec({name, meta, []}, vars, caller) end end # Handle local calls - defp typespec({:string, line, arguments}, vars, caller) do + defp typespec({:string, meta, arguments}, vars, caller) do IO.write "warning: string() type use is discouraged. For character lists, use " <> "char_list() type, for strings, String.t()\n#{Exception.format_stacktrace(caller.stacktrace)}" arguments = lc arg inlist arguments, do: typespec(arg, vars, caller) - { :type, line, :string, arguments } + { :type, line(meta), :string, arguments } end - defp typespec({:char_list, _line, arguments}, vars, caller) do + defp typespec({:char_list, _meta, arguments}, vars, caller) do typespec((quote do: :elixir.char_list(unquote_splicing(arguments))), vars, caller) end - defp typespec({name, line, arguments}, vars, caller) do + defp typespec({name, meta, arguments}, vars, caller) do arguments = lc arg inlist arguments, do: typespec(arg, vars, caller) - { :type, line, name, arguments } + { :type, line(meta), name, arguments } end # Handle literals @@ -619,15 +627,15 @@ defmodule Kernel.Typespec do end defp typespec([], vars, caller) do - typespec({ nil, 0, [] }, vars, caller) + typespec({ nil, [], [] }, vars, caller) end defp typespec([spec], vars, caller) do - typespec({ :list, 0, [spec] }, vars, caller) + typespec({ :list, [], [spec] }, vars, caller) end defp typespec([spec, {:"...", _, quoted}], vars, caller) when is_atom(quoted) do - typespec({ :nonempty_list, 0, [spec] }, vars, caller) + typespec({ :nonempty_list, [], [spec] }, vars, caller) end defp typespec(l, _, _) when is_list(l) do @@ -641,31 +649,31 @@ defmodule Kernel.Typespec do ## Helpers - defp remote_type({remote, line, name, arguments}, vars, caller) do + defp remote_type({remote, meta, name, arguments}, vars, caller) do arguments = lc arg inlist arguments, do: typespec(arg, vars, caller) - { :remote_type, line, [ remote, name, arguments ] } + { :remote_type, line(meta), [ remote, name, arguments ] } end defp collect_union({ :|, _, [a, b] }), do: [b|collect_union(a)] defp collect_union(v), do: [v] - defp fn_args(line, args, return, vars, caller) do - case [fn_args(line, args, vars, caller), typespec(return, vars, caller)] do + defp fn_args(meta, args, return, vars, caller) do + case [fn_args(meta, args, vars, caller), typespec(return, vars, caller)] do [{:type,_,:any},{:type,_,:any,[]}] -> [] x -> x end end - defp fn_args(line, [{:"...", _, _}], _vars, _caller) do - { :type, line, :any } + defp fn_args(meta, [{:"...", _, _}], _vars, _caller) do + { :type, line(meta), :any } end - defp fn_args(line, args, vars, caller) do + defp fn_args(meta, args, vars, caller) do args = lc arg inlist args, do: typespec(arg, vars, caller) - { :type, line, :product, args } + { :type, line(meta), :product, args } end - defp variable({name, line, _}) do - {:var, line, name} + defp variable({name, meta, _}) do + {:var, line(meta), name} end end \ No newline at end of file diff --git a/lib/elixir/lib/macro.ex b/lib/elixir/lib/macro.ex index b3f0e25ed1..f90f2ed936 100644 --- a/lib/elixir/lib/macro.ex +++ b/lib/elixir/lib/macro.ex @@ -62,14 +62,14 @@ defmodule Macro do #=> :foo Macro.escape({ :a, :b, :c }) - #=> { :{}, 0, [:a, :b, :c] } + #=> { :{}, [], [:a, :b, :c] } """ def escape({ left, right }) do { escape(left), escape(right) } end def escape(tuple) when is_tuple(tuple) do - { :{}, 0, escape(tuple_to_list(tuple)) } + { :{}, [], escape(tuple_to_list(tuple)) } end def escape(list) when is_list(list) do @@ -266,12 +266,12 @@ defmodule Macro do # Two-item tuples def to_binary({ left, right }) do - to_binary({ :{}, 0, [left, right] }) + to_binary({ :{}, [], [left, right] }) end # Lists def to_binary(list) when is_list(list) do - to_binary({ :[], 0, list }) + to_binary({ :[], [], list }) end # All other structures @@ -408,7 +408,7 @@ defmodule Macro do The compilation will fail because `My.Module` when quoted is not an atom, but a syntax tree as follow: - {:__aliases__, 0, [:My, :Module] } + {:__aliases__, [], [:My, :Module] } That said, we need to expand the aliases node above to an atom, so we can retrieve its length. Expanding the node is diff --git a/lib/elixir/lib/protocol.ex b/lib/elixir/lib/protocol.ex index 1e2da2ff5d..d08108dfca 100644 --- a/lib/elixir/lib/protocol.ex +++ b/lib/elixir/lib/protocol.ex @@ -215,7 +215,7 @@ defmodule Protocol do end defp generate_type(conversions, false) do - or_function = fn({ _, _, x }, acc) -> { :|, 0, [acc, x] } end + or_function = fn({ _, _, x }, acc) -> { :|, [], [acc, x] } end { _, _, first } = hd(conversions) :lists.foldl(or_function, first, tl(conversions)) end @@ -307,7 +307,7 @@ defmodule Protocol.DSL do # interpolation to generate the arguments because of compile # dependencies, so we use the <<>> instead. args = lc i inlist :lists.seq(1, arity) do - { binary_to_atom(<>), 0, __MODULE__ } + { binary_to_atom(<>), [], __MODULE__ } end { conversions, fallback, returns_nil } = conversions_for(module) @@ -323,7 +323,7 @@ defmodule Protocol.DSL do body = quote do - case __raw_impl__(xA), do: unquote({ :->, 0, clauses }) + case __raw_impl__(xA), do: unquote({ :->, [], clauses }) end { args, body } diff --git a/lib/elixir/lib/record.ex b/lib/elixir/lib/record.ex index 1210c20917..4aa5a07296 100644 --- a/lib/elixir/lib/record.ex +++ b/lib/elixir/lib/record.ex @@ -196,7 +196,7 @@ defmodule Record do in_match = caller.in_match? has_underscore_value = Keyword.has_key?(keyword, :_) - underscore_value = Keyword.get(keyword, :_, { :_, 0, nil }) + underscore_value = Keyword.get(keyword, :_, { :_, [], nil }) keyword = Keyword.delete keyword, :_ iterator = fn({field, default}, each_keyword) -> @@ -216,7 +216,7 @@ defmodule Record do { match, remaining } = :lists.mapfoldl(iterator, keyword, fields) case remaining do - [] -> { :{}, caller.line, [atom|match] } + [] -> { :{}, [line: caller.line], [atom|match] } _ -> keys = lc { key, _ } inlist remaining, do: key raise "record #{inspect atom} does not have the keys: #{inspect keys}" @@ -499,7 +499,7 @@ defmodule Record do end end - contents = { :{}, 0, [(quote do: __MODULE__)|fields] } + contents = { :{}, [], [(quote do: __MODULE__)|fields] } quote do @doc false diff --git a/lib/elixir/src/elixir.erl b/lib/elixir/src/elixir.erl index 92b1988ce6..4c4675c6b7 100644 --- a/lib/elixir/src/elixir.erl +++ b/lib/elixir/src/elixir.erl @@ -107,7 +107,7 @@ eval_quoted(Tree, Binding, Opts) -> end, eval_quoted(Tree, Binding, Line, scope_for_eval(Opts)). -eval_quoted(Tree, Binding, Line, #elixir_scope{} = S) -> +eval_quoted(Tree, Binding, Line, #elixir_scope{} = S) when is_integer(Line) -> eval_forms(elixir_quote:linify(Line, Tree), Binding, S). %% Handle forms evaluation internally, it is an diff --git a/lib/elixir/src/elixir_clauses.erl b/lib/elixir/src/elixir_clauses.erl index 6204b1e2ef..3933479578 100644 --- a/lib/elixir/src/elixir_clauses.erl +++ b/lib/elixir/src/elixir_clauses.erl @@ -9,17 +9,17 @@ %% Get pairs from a clause. -get_pairs(Line, Key, Clauses, S) -> - get_pairs(Line, Key, Clauses, S, false). +get_pairs(Meta, Key, Clauses, S) -> + get_pairs(Meta, Key, Clauses, S, false). -get_pairs(Line, Key, Clauses, S, AllowNil) -> +get_pairs(Meta, Key, Clauses, S, AllowNil) -> case lists:keyfind(Key, 1, Clauses) of { Key, { '->', _, Pairs } } -> [{ Key, Left, Right } || { Left, Right } <- Pairs]; { Key, nil } when AllowNil -> []; { Key, _ } -> - elixir_errors:syntax_error(Line, S#elixir_scope.file, "expected pairs with -> for key ~s", [Key]); + elixir_errors:syntax_error(Meta, S#elixir_scope.file, "expected pairs with -> for key ~s", [Key]); _ -> [] end. @@ -39,7 +39,7 @@ assigns_block(Line, Fun, BareArgs, Exprs, S) -> { Args, Guards } = extract_guards(BareArgs), assigns_block(Line, Fun, Args, Exprs, Guards, S). -assigns_block(Line, Fun, Args, Exprs, Guards, S) -> +assigns_block(Line, Fun, Args, Exprs, Guards, S) when is_integer(Line) -> { TArgs, SA } = assigns(Fun, Args, S#elixir_scope{extra_guards=[]}), { TExprs, SE } = elixir_translator:translate(Exprs, SA#elixir_scope{extra_guards=nil}), @@ -88,23 +88,23 @@ extract_last_guards(Args) -> % Function for translating macros with match style like case and receive. -match(Line, Clauses, #elixir_scope{clause_vars=C1} = S) -> - { TC, TS } = do_match(Line, Clauses, S#elixir_scope{clause_vars=orddict:new()}), +match(Meta, Clauses, #elixir_scope{clause_vars=C1} = S) -> + { TC, TS } = do_match(Meta, Clauses, S#elixir_scope{clause_vars=orddict:new()}), C2 = TS#elixir_scope.clause_vars, { TC, TS#elixir_scope{clause_vars=elixir_scope:merge_clause_vars(C1, C2)} }. -do_match(_Line, [], S) -> +do_match(_Meta, [], S) -> { [], S }; -do_match(Line, [DecoupledClause], S) -> - { TDecoupledClause, TS } = each_clause(Line, DecoupledClause, S), +do_match(Meta, [DecoupledClause], S) -> + { TDecoupledClause, TS } = each_clause(Meta, DecoupledClause, S), { [TDecoupledClause], TS }; -do_match(Line, DecoupledClauses, S) -> +do_match(Meta, DecoupledClauses, S) -> % Transform tree just passing the variables counter forward % and storing variables defined inside each clause. Transformer = fun(X, {Acc, CV}) -> - { TX, TAcc } = each_clause(Line, X, Acc), + { TX, TAcc } = each_clause(Meta, X, Acc), { TX, { umergec(S, TAcc), [TAcc#elixir_scope.clause_vars|CV] } } end, @@ -127,18 +127,19 @@ do_match(Line, DecoupledClauses, S) -> { FinalVars, FS } = lists:mapfoldl(fun(X, Acc) -> normalize_vars(X, SharedVars, Acc) end, TS, AllVars), % Defines a tuple that will be used as left side of the match operator + Line = ?line(Meta), LeftVars = [{var, Line, NewValue} || { _, _, NewValue, _ } <- FinalVars], % Expand all clauses by adding a match operation at the end % that assigns variables missing in one clause to the others. - expand_clauses(Line, TClauses, CV, LeftVars, FinalVars, [], FS) + expand_clauses(Meta, TClauses, CV, LeftVars, FinalVars, [], FS) end. -expand_clauses(Line, [Clause|T], [ClauseVars|V], LeftVars, FinalVars, Acc, S) -> +expand_clauses(Meta, [Clause|T], [ClauseVars|V], LeftVars, FinalVars, Acc, S) -> RightVars = [normalize_clause_var(Var, Kind, OldValue, ClauseVars) || { Var, Kind, _, OldValue } <- FinalVars], - AssignExpr = generate_match(Line, LeftVars, RightVars), + AssignExpr = generate_match(Meta, LeftVars, RightVars), ClauseExprs = element(5, Clause), [Final|RawClauseExprs] = lists:reverse(ClauseExprs), @@ -151,6 +152,7 @@ expand_clauses(Line, [Clause|T], [ClauseVars|V], LeftVars, FinalVars, Acc, S) -> { match, _, { var, _, UserVarName } = UserVar, _ } when UserVarName /= '_' -> { [UserVar,AssignExpr,Final|RawClauseExprs], S }; _ -> + Line = ?line(Meta), { StorageVar, SS } = elixir_scope:build_erl_var(Line, S), StorageExpr = { match, Line, StorageVar, Final }, { [StorageVar,AssignExpr,StorageExpr|RawClauseExprs], SS } @@ -160,29 +162,29 @@ expand_clauses(Line, [Clause|T], [ClauseVars|V], LeftVars, FinalVars, Acc, S) -> end, FinalClause = setelement(5, Clause, lists:reverse(FinalClauseExprs)), - expand_clauses(Line, T, V, LeftVars, FinalVars, [FinalClause|Acc], FS); + expand_clauses(Meta, T, V, LeftVars, FinalVars, [FinalClause|Acc], FS); -expand_clauses(_Line, [], [], _LeftVars, _FinalVars, Acc, S) -> +expand_clauses(_Meta, [], [], _LeftVars, _FinalVars, Acc, S) -> { lists:reverse(Acc), S }. % Handle each key/value clause pair and translate them accordingly. -each_clause(Line, { do, [Condition], Expr }, S) -> - assigns_block(Line, fun elixir_translator:translate_each/2, Condition, [Expr], S); +each_clause(Meta, { do, [Condition], Expr }, S) -> + assigns_block(?line(Meta), fun elixir_translator:translate_each/2, Condition, [Expr], S); -each_clause(Line, { else, [Condition], Expr }, S) -> - assigns_block(Line, fun elixir_translator:translate_each/2, Condition, [Expr], S); +each_clause(Meta, { else, [Condition], Expr }, S) -> + assigns_block(?line(Meta), fun elixir_translator:translate_each/2, Condition, [Expr], S); -each_clause(Line, { 'after', [Condition], Expr }, S) -> +each_clause(Meta, { 'after', [Condition], Expr }, S) -> { TCondition, SC } = elixir_translator:translate_each(Condition, S), { TBody, SB } = elixir_translator:translate([Expr], SC), - { { clause, Line, [TCondition], [], TBody }, SB }; + { { clause, ?line(Meta), [TCondition], [], TBody }, SB }; -each_clause(Line, { Key, [_|_], _ }, S) when Key == do; Key == 'after' -> - elixir_errors:syntax_error(Line, S#elixir_scope.file, "too many arguments given for ~s", [Key]); +each_clause(Meta, { Key, [_|_], _ }, S) when Key == do; Key == 'after' -> + elixir_errors:syntax_error(Meta, S#elixir_scope.file, "too many arguments given for ~s", [Key]); -each_clause(Line, { Key, _, _ }, S) -> - elixir_errors:syntax_error(Line, S#elixir_scope.file, "invalid key ~s", [Key]). +each_clause(Meta, { Key, _, _ }, S) -> + elixir_errors:syntax_error(Meta, S#elixir_scope.file, "invalid key ~s", [Key]). % Check if the given expression is a match tuple. % This is a small optimization to allow us to change @@ -246,10 +248,11 @@ normalize_clause_var(Var, Kind, OldValue, ClauseVars) -> %% generate_match -generate_match(Line, [Left], [Right]) -> - { match, Line, Left, Right }; +generate_match(Meta, [Left], [Right]) -> + { match, ?line(Meta), Left, Right }; -generate_match(Line, LeftVars, RightVars) -> +generate_match(Meta, LeftVars, RightVars) -> + Line = ?line(Meta), { match, Line, { tuple, Line, LeftVars }, { tuple, Line, RightVars } }. %% Listify diff --git a/lib/elixir/src/elixir_compiler.erl b/lib/elixir/src/elixir_compiler.erl index 405b610ca8..6a5df8061b 100644 --- a/lib/elixir/src/elixir_compiler.erl +++ b/lib/elixir/src/elixir_compiler.erl @@ -120,8 +120,7 @@ module_form_fun(nil) -> '__FILE__'; module_form_fun(_) -> '__MODULE__'. module_form(Fun, Exprs, Line, File, Module, Vars) when - is_binary(File), is_list(Exprs), is_integer(Line), is_atom(Module) -> - + is_binary(File), is_integer(Line), is_list(Exprs), is_atom(Module) -> Cons = lists:foldr(fun({ _, _, Var, _ }, Acc) -> { cons, Line, { var, Line, Var }, Acc } end, { nil, Line }, Vars), diff --git a/lib/elixir/src/elixir_def.erl b/lib/elixir/src/elixir_def.erl index 18812c8481..6fc755c478 100644 --- a/lib/elixir/src/elixir_def.erl +++ b/lib/elixir/src/elixir_def.erl @@ -14,7 +14,7 @@ %% Table management functions. Called internally. -table(Module) -> ?ELIXIR_ATOM_CONCAT([f, Module]). +table(Module) -> ?atom_concat([f, Module]). build_table(Module) -> FunctionTable = table(Module), @@ -44,7 +44,8 @@ reset_last(Module) -> %% %% If we just analyzed the compiled structure (i.e. the function availables %% before evaluating the function body), we would see both definitions. -wrap_definition(Kind, Line, Name, Args, Guards, Expr, S) -> +wrap_definition(Kind, Meta, Name, Args, Guards, Expr, S) -> + Line = ?line(Meta), MetaS = elixir_scope:serialize(S), Invoke = [ @@ -58,7 +59,7 @@ wrap_definition(Kind, Line, Name, Args, Guards, Expr, S) -> MetaS ], - ?ELIXIR_WRAP_CALL(Line, ?MODULE, store_definition, Invoke). + ?wrap_call(Line, ?MODULE, store_definition, Invoke). % Invoked by the wrap definition with the function abstract tree. % Each function is then added to the function table. @@ -99,7 +100,7 @@ store_definition(Kind, Line, Module, Name, Args, Guards, Body, RawS) -> def_body(_Line, nil) -> nil; def_body(_Line, [{ do, Expr }]) -> Expr; -def_body(Line, Else) -> { 'try', Line, [Else] }. +def_body(Line, Else) -> { 'try', [{line,Line}], [Else] }. %% @on_definition @@ -137,7 +138,7 @@ compile_super(_Module, _S) -> ok. %% Translate the given call and expression given %% and then store it in memory. -translate_definition(Kind, Line, Name, RawArgs, RawGuards, RawExpr, S) -> +translate_definition(Kind, Line, Name, RawArgs, RawGuards, RawExpr, S) when is_integer(Line) -> Args = elixir_quote:linify(Line, RawArgs), Guards = elixir_quote:linify(Line, RawGuards), Expr = elixir_quote:linify(Line, RawExpr), @@ -148,7 +149,7 @@ translate_definition(Kind, Line, Name, RawArgs, RawGuards, RawExpr, S) -> %% not affect the arity of the stored function, but the clause %% already contains it. ExtendedArgs = case IsMacro of - true -> [{ '_@CALLER', Line, nil }|Args]; + true -> [{ '_@CALLER', [{line,Line}], nil }|Args]; false -> Args end, @@ -170,7 +171,7 @@ translate_definition(Kind, Line, Name, RawArgs, RawGuards, RawExpr, S) -> true -> FBody = { 'match', Line, { 'var', Line, '__CALLER__' }, - ?ELIXIR_WRAP_CALL(Line, elixir_scope, to_ex_env, [{ var, Line, '_@CALLER' }]) + ?wrap_call(Line, elixir_scope, to_ex_env, [{ var, Line, '_@CALLER' }]) }, setelement(5, NClause, [FBody|element(5, NClause)]); false -> NClause @@ -200,7 +201,7 @@ unwrap_stored_definition([Fun|T], File, Exports, Private, Def, Defmacro, Defmacr unwrap_stored_definition([Fun|T], File, Exports, Private, Def, Defmacro, Defmacrop, Functions) when element(2, Fun) == defmacro -> { Name, Arity } = Tuple = element(1, Fun), - Macro = { ?ELIXIR_MACRO(Name), Arity + 1 }, + Macro = { ?elixir_macro(Name), Arity + 1 }, unwrap_stored_definition( T, File, [Macro|Exports], Private, Def, [Tuple|Defmacro], Defmacrop, diff --git a/lib/elixir/src/elixir_def_defaults.erl b/lib/elixir/src/elixir_def_defaults.erl index 8ffd828173..a66861f400 100644 --- a/lib/elixir/src/elixir_def_defaults.erl +++ b/lib/elixir/src/elixir_def_defaults.erl @@ -38,7 +38,7 @@ extract_defaults([{'//', _, [_Expr, Default]}|T], Line, Counter, NewArgs, NewInv extract_defaults(T, Line, Counter, NewArgs, [Default|NewInvoke]); extract_defaults([_|T], Line, Counter, NewArgs, NewInvoke) -> - H = { ?ELIXIR_ATOM_CONCAT(["_@D", Counter]), Line, nil }, + H = { ?atom_concat(["_@D", Counter]), Line, nil }, extract_defaults(T, Line, Counter + 1, [H|NewArgs], [H|NewInvoke]); extract_defaults([], _Line, _Counter, NewArgs, NewInvoke) -> @@ -49,10 +49,10 @@ extract_defaults([], _Line, _Counter, NewArgs, NewInvoke) -> build_match([], _Line, Acc) -> Acc; build_match([_|T], Line, Acc) -> - Var = { ?ELIXIR_ATOM_CONCAT(["_@D", length(T)]), Line, nil }, + Var = { ?atom_concat(["_@D", length(T)]), Line, nil }, build_match(T, Line, [Var|Acc]). % Given the invoked function name based on the kind -name_for_kind(Kind, Name) when Kind == defmacro; Kind == defmacrop -> ?ELIXIR_MACRO(Name); +name_for_kind(Kind, Name) when Kind == defmacro; Kind == defmacrop -> ?elixir_macro(Name); name_for_kind(_Kind, Name) -> Name. \ No newline at end of file diff --git a/lib/elixir/src/elixir_def_overridable.erl b/lib/elixir/src/elixir_def_overridable.erl index e7a0829649..0449a9f629 100644 --- a/lib/elixir/src/elixir_def_overridable.erl +++ b/lib/elixir/src/elixir_def_overridable.erl @@ -20,15 +20,16 @@ is_defined(Module, Tuple) -> _ -> false end. -ensure_defined(Line, Module, Tuple, S) -> +ensure_defined(Meta, Module, Tuple, S) -> case is_defined(Module, Tuple) of true -> []; - _ -> elixir_errors:form_error(Line, S#elixir_scope.file, ?MODULE, { no_super, Module, Tuple }) + _ -> elixir_errors:form_error(Meta, S#elixir_scope.file, ?MODULE, { no_super, Module, Tuple }) end. %% Retrieve args defined for the given arity. -retrieve_args(Line, Arity, S) -> +retrieve_args(Meta, Arity, S) -> + Line = ?line(Meta), { [ { var, Line, super_arg(X) } || X <- lists:seq(1, Arity) ], S#elixir_scope{name_args=true} @@ -36,7 +37,7 @@ retrieve_args(Line, Arity, S) -> %% Assign pseudo variables to the given vars. -assign_args(Line, Args, S) -> +assign_args(Line, Args, S) when is_integer(Line) -> { FArgs, _ } = lists:mapfoldl(fun(X, Acc) -> assign_args(Line, X, Acc, S) end, 1, Args), FArgs. @@ -45,7 +46,7 @@ assign_args(Line, X, Acc, _) -> { Match, Acc + 1 }. super_arg(Counter) -> - ?ELIXIR_ATOM_CONCAT(['_@S', Counter]). + ?atom_concat(['_@S', Counter]). %% Gets the name based on the function and stored overridables @@ -54,7 +55,7 @@ name(Module, Function) -> name(_Module, { Name, _ } = Function, Overridable) -> { Count, _, _ } = orddict:fetch(Function, Overridable), - ?ELIXIR_ATOM_CONCAT([Name, " (overridable ", Count, ")"]). + ?atom_concat([Name, " (overridable ", Count, ")"]). %% Store diff --git a/lib/elixir/src/elixir_dispatch.erl b/lib/elixir/src/elixir_dispatch.erl index 2170bd0d74..c6ac167204 100644 --- a/lib/elixir/src/elixir_dispatch.erl +++ b/lib/elixir/src/elixir_dispatch.erl @@ -5,7 +5,7 @@ -export([default_macros/0, default_functions/0, default_requires/0, dispatch_require/6, dispatch_import/5, require_function/5, import_function/4, - expand_import/8, expand_require/8, + expand_import/8, expand_require/8, find_import/4, format_error/1, in_erlang_functions/0, in_erlang_macros/0]). -include("elixir.hrl"). -compile({parse_transform, elixir_transform}). @@ -20,47 +20,58 @@ default_macros() -> default_requires() -> [ ?BUILTIN, 'Elixir.Kernel.Typespec' ]. -%% Function retrieval - -import_function(Line, Name, Arity, S) -> +find_import(_Meta, Name, Arity, S) -> Tuple = { Name, Arity }, case find_dispatch(Tuple, S#elixir_scope.functions) of false -> case find_dispatch(Tuple, S#elixir_scope.macros) of - false -> { { 'fun', Line, { function, Name, Arity } }, S }; + false -> false; + Receiver -> Receiver + end; + Receiver -> Receiver + end. + +%% Function retrieval + +import_function(Meta, Name, Arity, S) -> + Tuple = { Name, Arity }, + case find_dispatch(Tuple, S#elixir_scope.functions) of + false -> + case find_dispatch(Tuple, S#elixir_scope.macros) of + false -> { { 'fun', ?line(Meta), { function, Name, Arity } }, S }; _ -> false end; Receiver -> elixir_import:record(import, Tuple, Receiver, S#elixir_scope.module), - remote_function(Line, Receiver, Name, Arity, S) + remote_function(Meta, Receiver, Name, Arity, S) end. -require_function(Line, Receiver, Name, Arity, S) -> +require_function(Meta, Receiver, Name, Arity, S) -> Tuple = { Name, Arity }, case is_element(Tuple, get_optional_macros(Receiver)) of true -> false; - false -> remote_function(Line, Receiver, Name, Arity, S) + false -> remote_function(Meta, Receiver, Name, Arity, S) end. %% Function dispatch -dispatch_import(Line, Name, Args, S, Callback) -> +dispatch_import(Meta, Name, Args, S, Callback) -> Module = S#elixir_scope.module, Arity = length(Args), Tuple = { Name, Arity }, case find_dispatch(Tuple, S#elixir_scope.functions) of false -> - case expand_import(Line, Tuple, Args, Module, S#elixir_scope.function, + case expand_import(Meta, Tuple, Args, Module, S#elixir_scope.function, S#elixir_scope.requires, S#elixir_scope.macros, S) of { error, noexpansion } -> Callback(); { error, internal } -> elixir_import:record(import, Tuple, ?BUILTIN, Module), - elixir_macros:translate({ Name, Line, Args }, S); + elixir_macros:translate({ Name, Meta, Args }, S); { ok, _Receiver, Tree } -> - translate_expansion(Line, Tree, S) + translate_expansion(Meta, Tree, S) end; Receiver -> elixir_import:record(import, Tuple, Receiver, Module), @@ -68,32 +79,32 @@ dispatch_import(Line, Name, Args, S, Callback) -> true -> erlang; false -> Receiver end, - elixir_translator:translate_each({ { '.', Line, [Endpoint, Name] }, Line, Args }, S) + elixir_translator:translate_each({ { '.', Meta, [Endpoint, Name] }, Meta, Args }, S) end. -dispatch_require(Line, Receiver, Name, Args, S, Callback) -> +dispatch_require(Meta, Receiver, Name, Args, S, Callback) -> Module = S#elixir_scope.module, Arity = length(Args), Tuple = { Name, Arity }, - case (Receiver == Module) andalso is_element(Tuple, in_erlang_functions()) of + case (Receiver == ?BUILTIN) andalso is_element(Tuple, in_erlang_functions()) of true -> - elixir_translator:translate_each({ { '.', Line, [erlang, Name] }, Line, Args }, S); + elixir_translator:translate_each({ { '.', Meta, [erlang, Name] }, Meta, Args }, S); false -> - case expand_require(Line, Receiver, Tuple, Args, Module, + case expand_require(Meta, Receiver, Tuple, Args, Module, S#elixir_scope.function, S#elixir_scope.requires, S) of { error, noexpansion } -> Callback(); { error, internal } -> - elixir_macros:translate({ Name, Line, Args }, S); + elixir_macros:translate({ Name, Meta, Args }, S); { ok, Tree } -> - translate_expansion(Line, Tree, S) + translate_expansion(Meta, Tree, S) end end. %% Macros expansion -expand_import(Line, { Name, Arity } = Tuple, Args, Module, Function, Requires, Macros, SEnv) -> +expand_import(Meta, { Name, Arity } = Tuple, Args, Module, Function, Requires, Macros, SEnv) -> case find_dispatch(Tuple, Macros) of false -> Fun = (Function /= Tuple) andalso @@ -102,55 +113,56 @@ expand_import(Line, { Name, Arity } = Tuple, Args, Module, Function, Requires, M false -> { error, noexpansion }; _ -> elixir_import:record(import, Tuple, Module, Module), - { ok, Module, expand_macro_fun(Line, Fun, Module, Name, Args, Module, Requires, SEnv) } + { ok, Module, expand_macro_fun(Meta, Fun, Module, Name, Args, Module, Requires, SEnv) } end; ?BUILTIN -> case is_element(Tuple, in_erlang_macros()) of true -> { error, internal }; false -> elixir_import:record(import, Tuple, ?BUILTIN, Module), - { ok, ?BUILTIN, expand_macro_named(Line, ?BUILTIN, Name, Arity, Args, Module, Requires, SEnv) } + { ok, ?BUILTIN, expand_macro_named(Meta, ?BUILTIN, Name, Arity, Args, Module, Requires, SEnv) } end; Receiver -> elixir_import:record(import, Tuple, Receiver, Module), - { ok, Receiver, expand_macro_named(Line, Receiver, Name, Arity, Args, Module, Requires, SEnv) } + { ok, Receiver, expand_macro_named(Meta, Receiver, Name, Arity, Args, Module, Requires, SEnv) } end. -expand_require(Line, ?BUILTIN, { Name, Arity } = Tuple, Args, Module, _Function, Requires, SEnv) -> +expand_require(Meta, ?BUILTIN, { Name, Arity } = Tuple, Args, Module, _Function, Requires, SEnv) -> case is_element(Tuple, in_erlang_macros()) of true -> { error, internal }; false -> case is_element(Tuple, in_elixir_macros()) of - true -> { ok, expand_macro_named(Line, ?BUILTIN, Name, Arity, Args, Module, Requires, SEnv) }; + true -> { ok, expand_macro_named(Meta, ?BUILTIN, Name, Arity, Args, Module, Requires, SEnv) }; false -> { error, noexpansion } end end; -expand_require(Line, Receiver, { Name, Arity } = Tuple, Args, Module, Function, Requires, SEnv) -> +expand_require(Meta, Receiver, { Name, Arity } = Tuple, Args, Module, Function, Requires, SEnv) -> Fun = (Module == Receiver) andalso (Function /= Tuple) andalso elixir_def_local:macro_for(Tuple, false, Module), case Fun of false -> case is_element(Tuple, get_optional_macros(Receiver)) of - true -> { ok, expand_macro_named(Line, Receiver, Name, Arity, Args, Module, Requires, SEnv) }; + true -> { ok, expand_macro_named(Meta, Receiver, Name, Arity, Args, Module, Requires, SEnv) }; false -> { error, noexpansion } end; _ -> elixir_import:record(import, Tuple, Receiver, Module), - { ok, expand_macro_fun(Line, Fun, Receiver, Name, Args, Module, Requires, SEnv) } + { ok, expand_macro_fun(Meta, Fun, Receiver, Name, Args, Module, Requires, SEnv) } end. %% Expansion helpers -expand_macro_fun(Line, Fun, Receiver, Name, Args, Module, Requires, SEnv) -> +expand_macro_fun(Meta, Fun, Receiver, Name, Args, Module, Requires, SEnv) -> case (Receiver == Module) or is_element(Receiver, Requires) of true -> ok; false -> Tuple = { unrequired_module, { Receiver, Name, length(Args), Requires } }, - elixir_errors:form_error(Line, elixir_scope:filename(SEnv), ?MODULE, Tuple) + elixir_errors:form_error(Meta, elixir_scope:filename(SEnv), ?MODULE, Tuple) end, + Line = ?line(Meta), SArg = {Line,SEnv}, try @@ -161,15 +173,15 @@ expand_macro_fun(Line, Fun, Receiver, Name, Args, Module, Requires, SEnv) -> erlang:raise(Kind, Reason, munge_stacktrace(Info, erlang:get_stacktrace(), SArg)) end. -expand_macro_named(Line, Receiver, Name, Arity, Args, Module, Requires, SEnv) -> - ProperName = ?ELIXIR_MACRO(Name), +expand_macro_named(Meta, Receiver, Name, Arity, Args, Module, Requires, SEnv) -> + ProperName = ?elixir_macro(Name), ProperArity = Arity + 1, Fun = fun Receiver:ProperName/ProperArity, - expand_macro_fun(Line, Fun, Receiver, Name, Args, Module, Requires, SEnv). + expand_macro_fun(Meta, Fun, Receiver, Name, Args, Module, Requires, SEnv). -translate_expansion(Line, Tree, S) -> +translate_expansion(Meta, Tree, S) -> { TR, TS } = elixir_translator:translate_each( - elixir_quote:linify(Line, Tree), + elixir_quote:linify(?line(Meta), Tree), S#elixir_scope{check_clauses=false} ), { TR, TS#elixir_scope{check_clauses=S#elixir_scope.check_clauses} }. @@ -205,7 +217,9 @@ format_error({ unrequired_module,{Receiver, Name, Arity, Required }}) -> %% INTROSPECTION -remote_function(Line, Receiver, Name, Arity, S) -> +remote_function(Meta, Receiver, Name, Arity, S) -> + Line = ?line(Meta), + Final = case Receiver == ?BUILTIN andalso is_element({ Name, Arity }, in_erlang_functions()) of true -> erlang; diff --git a/lib/elixir/src/elixir_errors.erl b/lib/elixir/src/elixir_errors.erl index 1957fa9208..c4082e4c59 100644 --- a/lib/elixir/src/elixir_errors.erl +++ b/lib/elixir/src/elixir_errors.erl @@ -26,34 +26,34 @@ to_dot(L) -> L. %% Raised during macros translation. --spec syntax_error(non_neg_integer(), file:filename(), binary() | string()) -> no_return(). --spec syntax_error(non_neg_integer(), file:filename(), binary() | string(), list()) -> no_return(). +-spec syntax_error(non_neg_integer() | list(), file:filename(), binary() | string()) -> no_return(). +-spec syntax_error(non_neg_integer() | list(), file:filename(), binary() | string(), list()) -> no_return(). -syntax_error(Line, File, Message) when is_list(Message) -> - syntax_error(Line, File, iolist_to_binary(Message)); +syntax_error(Meta, File, Message) when is_list(Message) -> + syntax_error(Meta, File, iolist_to_binary(Message)); -syntax_error(Line, File, Message) when is_binary(Message) -> - raise(Line, File, 'Elixir.SyntaxError', Message). +syntax_error(Meta, File, Message) when is_binary(Message) -> + raise(Meta, File, 'Elixir.SyntaxError', Message). -syntax_error(Line, File, Format, Args) -> +syntax_error(Meta, File, Format, Args) -> Message = io_lib:format(Format, Args), - raise(Line, File, 'Elixir.SyntaxError', iolist_to_binary(Message)). + raise(Meta, File, 'Elixir.SyntaxError', iolist_to_binary(Message)). %% Raised on tokenizing/parsing --spec parse_error(non_neg_integer(), file:filename(), iolist() | atom(), [] | iolist()) -> no_return(). +-spec parse_error(non_neg_integer() | list(), file:filename(), iolist() | atom(), [] | iolist()) -> no_return(). -parse_error(Line, File, Error, []) -> +parse_error(Meta, File, Error, []) -> Message = case Error of "syntax error before: " -> <<"syntax error: expression is incomplete">>; _ -> iolist_to_binary(Error) end, - raise(Line, File, 'Elixir.TokenMissingError', Message); + raise(Meta, File, 'Elixir.TokenMissingError', Message); -parse_error(Line, File, "syntax error before: ", "'end'") -> - raise(Line, File, 'Elixir.SyntaxError', <<"unexpected token: end">>); +parse_error(Meta, File, "syntax error before: ", "'end'") -> + raise(Meta, File, 'Elixir.SyntaxError', <<"unexpected token: end">>); -parse_error(Line, File, Error, Token) -> +parse_error(Meta, File, Error, Token) -> BinError = if is_atom(Error) -> atom_to_binary(Error, utf8); true -> iolist_to_binary(Error) @@ -65,15 +65,15 @@ parse_error(Line, File, Error, Token) -> end, Message = <>, - raise(Line, File, 'Elixir.SyntaxError', Message). + raise(Meta, File, 'Elixir.SyntaxError', Message). %% Raised during compilation -spec form_error(non_neg_integer(), file:filename(), module(), any()) -> no_return(). -form_error(Line, File, Module, Desc) -> +form_error(Meta, File, Module, Desc) -> Message = iolist_to_binary(format_error(Module, Desc)), - raise(Line, File, 'Elixir.CompileError', Message). + raise(Meta, File, 'Elixir.CompileError', Message). %% Shows a deprecation message @@ -132,33 +132,36 @@ handle_file_error(File, {Line,Module,Desc}) -> %% Assertions -assert_no_function_scope(_Line, _Kind, #elixir_scope{function=nil}) -> []; -assert_no_function_scope(Line, Kind, S) -> - syntax_error(Line, S#elixir_scope.file, "cannot invoke ~s inside a function", [Kind]). +assert_no_function_scope(_Meta, _Kind, #elixir_scope{function=nil}) -> []; +assert_no_function_scope(Meta, Kind, S) -> + syntax_error(Meta, S#elixir_scope.file, "cannot invoke ~s inside a function", [Kind]). -assert_no_assign_or_guard_scope(Line, Kind, S) -> - assert_no_assign_scope(Line, Kind, S), - assert_no_guard_scope(Line, Kind, S). +assert_no_assign_or_guard_scope(Meta, Kind, S) -> + assert_no_assign_scope(Meta, Kind, S), + assert_no_guard_scope(Meta, Kind, S). -assert_no_assign_scope(Line, Kind, #elixir_scope{context=assign} = S) -> - syntax_error(Line, S#elixir_scope.file, "cannot invoke ~s inside assign", [Kind]); -assert_no_assign_scope(_Line, _Kind, _S) -> []. +assert_no_assign_scope(Meta, Kind, #elixir_scope{context=assign} = S) -> + syntax_error(Meta, S#elixir_scope.file, "cannot invoke ~s inside assign", [Kind]); +assert_no_assign_scope(_Meta, _Kind, _S) -> []. -assert_no_guard_scope(Line, Kind, #elixir_scope{context=guard} = S) -> - syntax_error(Line, S#elixir_scope.file, "cannot invoke ~s inside guard", [Kind]); -assert_no_guard_scope(_Line, _Kind, _S) -> []. +assert_no_guard_scope(Meta, Kind, #elixir_scope{context=guard} = S) -> + syntax_error(Meta, S#elixir_scope.file, "cannot invoke ~s inside guard", [Kind]); +assert_no_guard_scope(_Meta, _Kind, _S) -> []. -assert_module_scope(Line, Kind, #elixir_scope{module=nil,file=File}) -> - syntax_error(Line, File, "cannot invoke ~s outside module", [Kind]); -assert_module_scope(_Line, _Kind, #elixir_scope{module=Module}) -> Module. +assert_module_scope(Meta, Kind, #elixir_scope{module=nil,file=File}) -> + syntax_error(Meta, File, "cannot invoke ~s outside module", [Kind]); +assert_module_scope(_Meta, _Kind, #elixir_scope{module=Module}) -> Module. -assert_function_scope(Line, Kind, #elixir_scope{function=nil,file=File}) -> - syntax_error(Line, File, "cannot invoke ~s outside function", [Kind]); -assert_function_scope(_Line, _Kind, #elixir_scope{function=Function}) -> Function. +assert_function_scope(Meta, Kind, #elixir_scope{function=nil,file=File}) -> + syntax_error(Meta, File, "cannot invoke ~s outside function", [Kind]); +assert_function_scope(_Meta, _Kind, #elixir_scope{function=Function}) -> Function. %% Helpers -raise(Line, File, Kind, Message) -> +raise(Meta, File, Kind, Message) when is_list(Meta) -> + raise(?line(Meta), File, Kind, Message); + +raise(Line, File, Kind, Message) when is_integer(Line) -> Stacktrace = erlang:get_stacktrace(), Exception = Kind:new([{description, Message}, {file, iolist_to_binary(File)}, {line, Line}]), erlang:raise(error, Exception, Stacktrace). diff --git a/lib/elixir/src/elixir_import.erl b/lib/elixir/src/elixir_import.erl index 407131c4d4..2ed412b5b0 100644 --- a/lib/elixir/src/elixir_import.erl +++ b/lib/elixir/src/elixir_import.erl @@ -6,7 +6,7 @@ build_table/1, delete_table/1, record/4]). -include("elixir.hrl"). -table(Module) -> ?ELIXIR_ATOM_CONCAT([i, Module]). +table(Module) -> ?atom_concat([i, Module]). build_table(Module) -> ets:new(table(Module), [set, named_table, public]). @@ -34,14 +34,14 @@ recorded_locals(Module) -> %% Update the scope to consider the imports for aliases %% based on the given options and selector. -import(Line, Ref, Opts, Selector, S) -> +import(Meta, Ref, Opts, Selector, S) -> IncludeAll = (Selector == all) or (Selector == default), SF = case IncludeAll or (Selector == functions) of false -> S; true -> FunctionsFun = fun(K) -> remove_underscored(K andalso Selector, get_functions(Ref)) end, - Functions = calculate(Line, Ref, Opts, + Functions = calculate(Meta, Ref, Opts, S#elixir_scope.functions, FunctionsFun, S), S#elixir_scope{functions=Functions} end, @@ -52,10 +52,10 @@ import(Line, Ref, Opts, Selector, S) -> MacrosFun = fun(K) -> case IncludeAll of true -> remove_underscored(K andalso Selector, get_optional_macros(Ref)); - false -> get_macros(Line, Ref, SF) + false -> get_macros(Meta, Ref, SF) end end, - Macros = calculate(Line, Ref, Opts, + Macros = calculate(Meta, Ref, Opts, SF#elixir_scope.macros, MacrosFun, SF), SF#elixir_scope{macros=Macros} end, @@ -66,17 +66,17 @@ import(Line, Ref, Opts, Selector, S) -> %% Calculates the imports based on only and except -calculate(Line, Key, Opts, Old, AvailableFun, S) -> +calculate(Meta, Key, Opts, Old, AvailableFun, S) -> File = S#elixir_scope.file, All = keydelete(Key, Old), New = case keyfind(only, Opts) of { only, RawOnly } -> - Only = expand_fun_arity(Line, only, RawOnly, S), + Only = expand_fun_arity(Meta, only, RawOnly, S), case Only -- get_exports(Key) of [{Name,Arity}|_] -> Tuple = { invalid_import, { Key, Name, Arity } }, - elixir_errors:form_error(Line, File, ?MODULE, Tuple); + elixir_errors:form_error(Meta, File, ?MODULE, Tuple); _ -> intersection(Only, AvailableFun(false)) end; @@ -85,7 +85,7 @@ calculate(Line, Key, Opts, Old, AvailableFun, S) -> false -> AvailableFun(true); { except, [] } -> AvailableFun(true); { except, RawExcept } -> - Except = expand_fun_arity(Line, except, RawExcept, S), + Except = expand_fun_arity(Meta, except, RawExcept, S), case keyfind(Key, Old) of false -> AvailableFun(true) -- Except; {Key,ToRemove} -> ToRemove -- Except @@ -100,33 +100,33 @@ calculate(Line, Key, Opts, Old, AvailableFun, S) -> case Final of [] -> All; _ -> - ensure_no_conflicts(Line, File, Final, keydelete(Key, S#elixir_scope.macros)), - ensure_no_conflicts(Line, File, Final, keydelete(Key, S#elixir_scope.functions)), - ensure_no_in_erlang_macro_conflict(Line, File, Key, Final, internal_conflict), + ensure_no_conflicts(Meta, File, Final, keydelete(Key, S#elixir_scope.macros)), + ensure_no_conflicts(Meta, File, Final, keydelete(Key, S#elixir_scope.functions)), + ensure_no_in_erlang_macro_conflict(Meta, File, Key, Final, internal_conflict), [{ Key, Final }|All] end. %% Ensure we are expanding macros and stuff -expand_fun_arity(Line, Kind, Value, S) -> +expand_fun_arity(Meta, Kind, Value, S) -> { TValue, _S } = elixir_translator:translate_each(Value, S), - cons_to_keywords(Line, Kind, TValue, S). + cons_to_keywords(Meta, Kind, TValue, S). -cons_to_keywords(Line, Kind, { cons, _, Left, { nil, _ } }, S) -> - [tuple_to_fun_arity(Line, Kind, Left, S)]; +cons_to_keywords(Meta, Kind, { cons, _, Left, { nil, _ } }, S) -> + [tuple_to_fun_arity(Meta, Kind, Left, S)]; -cons_to_keywords(Line, Kind, { cons, _, Left, Right }, S) -> - [tuple_to_fun_arity(Line, Kind, Left, S)|cons_to_keywords(Line, Kind, Right, S)]; +cons_to_keywords(Meta, Kind, { cons, _, Left, Right }, S) -> + [tuple_to_fun_arity(Meta, Kind, Left, S)|cons_to_keywords(Meta, Kind, Right, S)]; -cons_to_keywords(Line, Kind, _, S) -> - elixir_errors:syntax_error(Line, S#elixir_scope.file, +cons_to_keywords(Meta, Kind, _, S) -> + elixir_errors:syntax_error(Meta, S#elixir_scope.file, "invalid value for :~s, expected a list with functions and arities", [Kind]). -tuple_to_fun_arity(_Line, _Kind, { tuple, _, [{ atom, _, Atom }, { integer, _, Integer }] }, _S) -> +tuple_to_fun_arity(_Meta, _Kind, { tuple, _, [{ atom, _, Atom }, { integer, _, Integer }] }, _S) -> { Atom, Integer }; -tuple_to_fun_arity(Line, Kind, _, S) -> - elixir_errors:syntax_error(Line, S#elixir_scope.file, +tuple_to_fun_arity(Meta, Kind, _, S) -> + elixir_errors:syntax_error(Meta, S#elixir_scope.file, "invalid value for :~s, expected a list with functions and arities", [Kind]). %% Retrieve functions and macros from modules @@ -145,13 +145,13 @@ get_functions(Module) -> error:undef -> Module:module_info(exports) end. -get_macros(Line, Module, S) -> +get_macros(Meta, Module, S) -> try Module:'__info__'(macros) catch error:undef -> Tuple = { no_macros, Module }, - elixir_errors:form_error(Line, S#elixir_scope.file, ?MODULE, Tuple) + elixir_errors:form_error(Meta, S#elixir_scope.file, ?MODULE, Tuple) end. get_optional_macros(Module) -> @@ -171,13 +171,13 @@ get_optional_macros(Module) -> %% Elixir "implemented in Erlang" macro. Checking if a local %% conflicts with an import is automatically done by Erlang. -ensure_no_local_conflict(Line, File, Module, AllDefined) -> - ensure_no_in_erlang_macro_conflict(Line, File, Module, AllDefined, local_conflict). +ensure_no_local_conflict(Meta, File, Module, AllDefined) -> + ensure_no_in_erlang_macro_conflict(Meta, File, Module, AllDefined, local_conflict). %% Find conlicts in the given list of functions with %% the recorded set of imports. -ensure_no_import_conflict(Line, File, Module, AllDefined) -> +ensure_no_import_conflict(Meta, File, Module, AllDefined) -> Table = table(Module), Matches = [X || X <- AllDefined, ets:member(Table, X)], @@ -185,7 +185,7 @@ ensure_no_import_conflict(Line, File, Module, AllDefined) -> [{Name,Arity}|_] -> Key = ets:lookup_element(Table, {Name, Arity }, 2), Tuple = { import_conflict, { Key, Name, Arity } }, - elixir_errors:form_error(Line, File, ?MODULE, Tuple); + elixir_errors:form_error(Meta, File, ?MODULE, Tuple); [] -> ok end. @@ -193,7 +193,7 @@ ensure_no_import_conflict(Line, File, Module, AllDefined) -> %% Ensure the given functions don't clash with any %% of Elixir non overridable macros. -ensure_no_in_erlang_macro_conflict(Line, File, Key, [{Name,Arity}|T], Reason) -> +ensure_no_in_erlang_macro_conflict(Meta, File, Key, [{Name,Arity}|T], Reason) -> Values = lists:filter(fun({X,Y}) -> (Name == X) andalso ((Y == '*') orelse (Y == Arity)) end, non_overridable_macros()), @@ -201,27 +201,27 @@ ensure_no_in_erlang_macro_conflict(Line, File, Key, [{Name,Arity}|T], Reason) -> case Values /= [] of true -> Tuple = { Reason, { Key, Name, Arity } }, - elixir_errors:form_error(Line, File, ?MODULE, Tuple); - false -> ensure_no_in_erlang_macro_conflict(Line, File, Key, T, Reason) + elixir_errors:form_error(Meta, File, ?MODULE, Tuple); + false -> ensure_no_in_erlang_macro_conflict(Meta, File, Key, T, Reason) end; -ensure_no_in_erlang_macro_conflict(_Line, _File, _Key, [], _) -> ok. +ensure_no_in_erlang_macro_conflict(_Meta, _File, _Key, [], _) -> ok. %% Find conlicts in the given list of functions with the set of imports. %% Used internally to ensure a newly imported fun or macro does not %% conflict with an already imported set. -ensure_no_conflicts(Line, File, Functions, [{Key,Value}|T]) -> +ensure_no_conflicts(Meta, File, Functions, [{Key,Value}|T]) -> Filtered = lists:filter(fun(X) -> lists:member(X, Functions) end, Value), case Filtered of [{Name,Arity}|_] -> Tuple = { already_imported, { Key, Name, Arity } }, - elixir_errors:form_error(Line, File, ?MODULE, Tuple); + elixir_errors:form_error(Meta, File, ?MODULE, Tuple); [] -> - ensure_no_conflicts(Line, File, Functions, T) + ensure_no_conflicts(Meta, File, Functions, T) end; -ensure_no_conflicts(_Line, _File, _Functions, _S) -> ok. +ensure_no_conflicts(_Meta, _File, _Functions, _S) -> ok. %% ERROR HANDLING diff --git a/lib/elixir/src/elixir_interpolation.erl b/lib/elixir/src/elixir_interpolation.erl index 7e60501aab..f15dfb6cca 100644 --- a/lib/elixir/src/elixir_interpolation.erl +++ b/lib/elixir/src/elixir_interpolation.erl @@ -169,12 +169,13 @@ wrap_interpol(_Line, Form) when is_binary(Form) -> Form; wrap_interpol(Line, Form) -> - { '::', Line, [{ { '.', Line, ['Elixir.Binary.Chars', to_binary] }, Line, [Form]}, { binary, Line, nil }]}. + Meta = [{line,Line}], + { '::', Meta, [{ { '.', Meta, ['Elixir.Binary.Chars', to_binary] }, Meta, [Form]}, { binary, Meta, nil }]}. forms(String, StartLine, File) -> case elixir_translator:forms(String, StartLine, File, []) of { ok, [] } -> nil; { ok, [Forms] } when not is_list(Forms) -> Forms; - { ok, Forms } -> { '__block__', StartLine, Forms }; + { ok, Forms } -> { '__block__', [{line,StartLine}], Forms }; { error, Tuple } -> throw({ interpolation_error, Tuple }) end. diff --git a/lib/elixir/src/elixir_literal.erl b/lib/elixir/src/elixir_literal.erl index 54d08d5612..6c6fd17a84 100644 --- a/lib/elixir/src/elixir_literal.erl +++ b/lib/elixir/src/elixir_literal.erl @@ -5,31 +5,31 @@ -include("elixir.hrl"). -compile({parse_transform, elixir_transform}). -translate({ '<<>>', Line, Args } = Original, S) when is_list(Args) -> +translate({ '<<>>', Meta, Args } = Original, S) when is_list(Args) -> case elixir_partials:handle(Original, S, allow_tail) of error -> case S#elixir_scope.context of assign -> - build_bitstr(fun elixir_translator:translate_each/2, Args, Line, S); + build_bitstr(fun elixir_translator:translate_each/2, Args, Meta, S); _ -> - { TArgs, { SC, SV } } = build_bitstr(fun elixir_translator:translate_arg/2, Args, Line, { S, S }), + { TArgs, { SC, SV } } = build_bitstr(fun elixir_translator:translate_arg/2, Args, Meta, { S, S }), { TArgs, umergec(SV, SC) } end; Else -> Else end; -translate({ '{}', Line, Args } = Original, S) when is_list(Args) -> +translate({ '{}', Meta, Args } = Original, S) when is_list(Args) -> case elixir_partials:handle(Original, S) of error -> { TArgs, SE } = translate_args(Args, S), - { {tuple, Line, TArgs}, SE }; + { { tuple, ?line(Meta), TArgs }, SE }; Else -> Else end; -translate({ '[]', _Line, [] }, S) -> +translate({ '[]', _Meta, [] }, S) -> { { nil, 0 }, S }; -translate({ '[]', _Line, Args } = Original, S) when is_list(Args) -> +translate({ '[]', _Meta, Args } = Original, S) when is_list(Args) -> case elixir_partials:handle(Original, S, allow_tail) of error -> [RTail|RArgs] = lists:reverse(Args), @@ -60,10 +60,10 @@ translate({ '[]', _Line, Args } = Original, S) when is_list(Args) -> end; translate({ Left, Right }, S) -> - translate({ '{}', 0, [Left, Right]}, S); + translate({ '{}', [], [Left, Right]}, S); translate(Args, S) when is_list(Args) -> - translate({ '[]', 0, Args }, S); + translate({ '[]', [], Args }, S); translate(Number, S) when is_integer(Number) -> { { integer, 0, Number }, S }; @@ -85,14 +85,14 @@ translate(Bitstring, S) when is_bitstring(Bitstring) -> % * If '::' is given, extract the bitstring information % * All the other types are simply translated and handled with Erlang's default % -build_bitstr(Fun, Exprs, Line, S) -> - { Final, FinalS } = build_bitstr_each(Fun, Exprs, Line, S, []), - { { bin, Line, lists:reverse(Final) }, FinalS }. +build_bitstr(Fun, Exprs, Meta, S) -> + { Final, FinalS } = build_bitstr_each(Fun, Exprs, Meta, S, []), + { { bin, ?line(Meta), lists:reverse(Final) }, FinalS }. -build_bitstr_each(_Fun, [], _Line, S, Acc) -> +build_bitstr_each(_Fun, [], _Meta, S, Acc) -> { Acc, S }; -build_bitstr_each(Fun, [{'::',_,[H,V]}|T], Line, S, Acc) -> +build_bitstr_each(Fun, [{'::',_,[H,V]}|T], Meta, S, Acc) -> %% Variables defined outside the binary can be accounted %% on subparts, however we can't assign new variables. case S of @@ -100,42 +100,43 @@ build_bitstr_each(Fun, [{'::',_,[H,V]}|T], Line, S, Acc) -> _ -> ES = S#elixir_scope{context=nil} %% translate_each, revert assigns end, - { Size, Types } = extract_bit_values(Line, V, ES), - build_bitstr_each(Fun, T, Line, S, Acc, H, Size, Types); + { Size, Types } = extract_bit_values(Meta, V, ES), + build_bitstr_each(Fun, T, Meta, S, Acc, H, Size, Types); -build_bitstr_each(Fun, [H|T], Line, S, Acc) -> - build_bitstr_each(Fun, T, Line, S, Acc, H, default, default). +build_bitstr_each(Fun, [H|T], Meta, S, Acc) -> + build_bitstr_each(Fun, T, Meta, S, Acc, H, default, default). -build_bitstr_each(Fun, T, Line, S, Acc, H, Size, Types) when is_list(H) -> +build_bitstr_each(Fun, T, Meta, S, Acc, H, Size, Types) when is_list(H) -> case is_default_or_utf(Types) of true -> { NewAcc, NewS } = lists:foldl(fun(L, { LA, LS }) -> { FL, FS } = Fun(L, LS), - { [{ bin_element, Line, FL, Size, Types }|LA], FS } + { [{ bin_element, ?line(Meta), FL, Size, Types }|LA], FS } end, { Acc, S }, H), - build_bitstr_each(Fun, T, Line, NewS, NewAcc); + build_bitstr_each(Fun, T, Meta, NewS, NewAcc); false -> - build_bitstr_default(Fun, T, Line, S, Acc, H, Size, Types) + build_bitstr_default(Fun, T, Meta, S, Acc, H, Size, Types) end; -build_bitstr_each(Fun, T, Line, S, Acc, H, Size, Types) when is_bitstring(H) -> +build_bitstr_each(Fun, T, Meta, S, Acc, H, Size, Types) when is_bitstring(H) -> case is_default_or_utf(Types) of true -> + Line = ?line(Meta), { bin, _, Elements } = elixir_tree_helpers:abstract_syntax(H), NewAcc = lists:foldl(fun({ bin_element, _, Expr, _, _ }, FinalAcc) -> [{ bin_element, Line, Expr, Size, Types }|FinalAcc] end, Acc, Elements), - build_bitstr_each(Fun, T, Line, S, NewAcc); + build_bitstr_each(Fun, T, Meta, S, NewAcc); false -> - build_bitstr_default(Fun, T, Line, S, Acc, H, Size, Types) + build_bitstr_default(Fun, T, Meta, S, Acc, H, Size, Types) end; -build_bitstr_each(Fun, T, Line, S, Acc, H, Size, Types) -> - build_bitstr_default(Fun, T, Line, S, Acc, H, Size, Types). +build_bitstr_each(Fun, T, Meta, S, Acc, H, Size, Types) -> + build_bitstr_default(Fun, T, Meta, S, Acc, H, Size, Types). -build_bitstr_default(Fun, T, Line, S, Acc, H, Size, Types) -> +build_bitstr_default(Fun, T, Meta, S, Acc, H, Size, Types) -> { Expr, NS } = Fun(H, S), - build_bitstr_each(Fun, T, Line, NS, [{ bin_element, Line, Expr, Size, Types }|Acc]). + build_bitstr_each(Fun, T, Meta, NS, [{ bin_element, ?line(Meta), Expr, Size, Types }|Acc]). is_default_or_utf(default) -> true; is_default_or_utf([UTF|_]) when UTF == utf8; UTF == utf16; UTF == utf32 -> true; @@ -144,43 +145,43 @@ is_default_or_utf([]) -> false. %% Extra bitstring specifiers -extract_bit_values(Line, V, S) when is_list(V) -> - extract_bit_values(Line, V, default, [], S); +extract_bit_values(Meta, V, S) when is_list(V) -> + extract_bit_values(Meta, V, default, [], S); -extract_bit_values(Line, V, S) -> - extract_bit_values(Line, [V], S). +extract_bit_values(Meta, V, S) -> + extract_bit_values(Meta, [V], S). -extract_bit_values(Line, [{ Value, _CallLine, Atom }|T] = All, Size, Types, S) +extract_bit_values(Meta, [{ Value, _Meta, Atom }|T] = All, Size, Types, S) when is_atom(Value) andalso (is_atom(Atom) orelse Atom == []) -> case extract_bit_type(Value, Types) of { error, unknown } -> - handle_unknown_specifier(Line, All, Size, Types, S); + handle_unknown_specifier(Meta, All, Size, Types, S); NewTypes when is_list(NewTypes) -> - extract_bit_values(Line, T, Size, NewTypes, S) + extract_bit_values(Meta, T, Size, NewTypes, S) end; -extract_bit_values(Line, [{ Value, CallLine, [_] = Args }|T] = All, Size, Types, S) when is_atom(Value) -> +extract_bit_values(Meta, [{ Value, CallMeta, [_] = Args }|T] = All, Size, Types, S) when is_atom(Value) -> { TArgs, _ } = elixir_translator:translate_args(Args, S), case extract_bit_type_or_size(Value, TArgs, Size, Types) of { error, unknown } -> - handle_unknown_specifier(Line, All, Size, Types, S); + handle_unknown_specifier(Meta, All, Size, Types, S); { error, Msg } -> - elixir_errors:syntax_error(CallLine, S#elixir_scope.file, Msg); + elixir_errors:syntax_error(CallMeta, S#elixir_scope.file, Msg); { NewSize, NewTypes } -> - extract_bit_values(Line, T, NewSize, NewTypes, S) + extract_bit_values(Meta, T, NewSize, NewTypes, S) end; -extract_bit_values(Line, [{ '|', _, [Left, Right] }], Size, Types, S) -> - Expanded = 'Elixir.Macro':expand(Right, elixir_scope:to_ex_env({ Line, S })), - extract_bit_values(Line, [Left|join_expansion(Expanded,[])], Size, Types, S); +extract_bit_values(Meta, [{ '|', _, [Left, Right] }], Size, Types, S) -> + Expanded = 'Elixir.Macro':expand(Right, elixir_scope:to_ex_env({ ?line(Meta), S })), + extract_bit_values(Meta, [Left|join_expansion(Expanded,[])], Size, Types, S); -extract_bit_values(Line, [_|_] = All, Size, Types, S) -> - handle_unknown_specifier(Line, All, Size, Types, S); +extract_bit_values(Meta, [_|_] = All, Size, Types, S) -> + handle_unknown_specifier(Meta, All, Size, Types, S); -extract_bit_values(_Line, [], Size, [], _S) -> +extract_bit_values(_Meta, [], Size, [], _S) -> { Size, default }; -extract_bit_values(_Line, [], Size, Types, _S) -> +extract_bit_values(_Meta, [], Size, Types, _S) -> { Size, lists:reverse(Types) }. extract_bit_type(Value, Types) when @@ -212,12 +213,12 @@ extract_bit_type_or_size(unit, _Args, _Other, _Types) -> extract_bit_type_or_size(_Value, _Args, _Other, _Types) -> { error, unknown }. -handle_unknown_specifier(Line, [H|T], Size, Types, S) -> - case 'Elixir.Macro':expand(H, elixir_scope:to_ex_env({ Line, S })) of +handle_unknown_specifier(Meta, [H|T], Size, Types, S) -> + case 'Elixir.Macro':expand(H, elixir_scope:to_ex_env({ ?line(Meta), S })) of H -> - elixir_errors:syntax_error(Line, S#elixir_scope.file, "unknown bitstring specifier ~s", ['Elixir.Macro':to_binary(H)]); + elixir_errors:syntax_error(Meta, S#elixir_scope.file, "unknown bitstring specifier ~s", ['Elixir.Macro':to_binary(H)]); E -> - extract_bit_values(Line, join_expansion(E,T), Size, Types, S) + extract_bit_values(Meta, join_expansion(E,T), Size, Types, S) end. join_expansion({ '__block__', _, [Expanded] }, Tail) -> join_expansion(Expanded, Tail); diff --git a/lib/elixir/src/elixir_macros.erl b/lib/elixir/src/elixir_macros.erl index d86286d1db..73720a46df 100644 --- a/lib/elixir/src/elixir_macros.erl +++ b/lib/elixir/src/elixir_macros.erl @@ -15,127 +15,127 @@ %% Operators -translate({ '+', _Line, [Expr] }, S) when is_number(Expr) -> +translate({ '+', _Meta, [Expr] }, S) when is_number(Expr) -> translate_each(Expr, S); -translate({ '-', _Line, [Expr] }, S) when is_number(Expr) -> +translate({ '-', _Meta, [Expr] }, S) when is_number(Expr) -> translate_each(-1 * Expr, S); -translate({ Op, Line, Exprs }, S) when is_list(Exprs), +translate({ Op, Meta, Exprs }, S) when is_list(Exprs), Op == '<-' orelse Op == '--' -> - assert_no_assign_or_guard_scope(Line, Op, S), - translate_each({ '__op__', Line, [Op|Exprs] }, S); + assert_no_assign_or_guard_scope(Meta, Op, S), + translate_each({ '__op__', Meta, [Op|Exprs] }, S); -translate({ Op, Line, Exprs }, S) when is_list(Exprs), +translate({ Op, Meta, Exprs }, S) when is_list(Exprs), Op == '+' orelse Op == '-' orelse Op == '*' orelse Op == '/' orelse Op == '++' orelse Op == 'not' orelse Op == 'and' orelse Op == 'or' orelse Op == 'xor' orelse Op == '<' orelse Op == '>' orelse Op == '<=' orelse Op == '>=' orelse Op == '==' orelse Op == '!=' orelse Op == '===' orelse Op == '!==' -> - translate_each({ '__op__', Line, [Op|Exprs] }, S); + translate_each({ '__op__', Meta, [Op|Exprs] }, S); -translate({ '!', Line, [{ '!', _, [Expr] }] }, S) -> +translate({ '!', Meta, [{ '!', _, [Expr] }] }, S) -> { TExpr, SE } = translate_each(Expr, S), - elixir_tree_helpers:convert_to_boolean(Line, TExpr, true, S#elixir_scope.context == guard, SE); + elixir_tree_helpers:convert_to_boolean(?line(Meta), TExpr, true, S#elixir_scope.context == guard, SE); -translate({ '!', Line, [Expr] }, S) -> +translate({ '!', Meta, [Expr] }, S) -> { TExpr, SE } = translate_each(Expr, S), - elixir_tree_helpers:convert_to_boolean(Line, TExpr, false, S#elixir_scope.context == guard, SE); + elixir_tree_helpers:convert_to_boolean(?line(Meta), TExpr, false, S#elixir_scope.context == guard, SE); -translate({ in, Line, [Left, Right] }, #elixir_scope{extra_guards=nil} = S) -> - { _, TExpr, TS } = translate_in(Line, Left, Right, S), +translate({ in, Meta, [Left, Right] }, #elixir_scope{extra_guards=nil} = S) -> + { _, TExpr, TS } = translate_in(Meta, Left, Right, S), { TExpr, TS }; -translate({ in, Line, [Left, Right] }, #elixir_scope{extra_guards=Extra} = S) -> - { TVar, TExpr, TS } = translate_in(Line, Left, Right, S), +translate({ in, Meta, [Left, Right] }, #elixir_scope{extra_guards=Extra} = S) -> + { TVar, TExpr, TS } = translate_in(Meta, Left, Right, S), { TVar, TS#elixir_scope{extra_guards=[TExpr|Extra]} }; %% Functions -translate({ function, Line, [[{do,{ '->',_,Pairs}}]] }, S) -> - assert_no_assign_or_guard_scope(Line, 'function', S), - elixir_translator:translate_fn(Line, Pairs, S); +translate({ function, Meta, [[{do,{ '->',_,Pairs}}]] }, S) -> + assert_no_assign_or_guard_scope(Meta, 'function', S), + elixir_translator:translate_fn(Meta, Pairs, S); -translate({ function, Line, [{ '/', _, [{{ '.', _ ,[M, F] }, _ , [] }, A]}] }, S) -> - translate({ function, Line, [M, F, A] }, S); +translate({ function, Meta, [{ '/', _, [{{ '.', _ ,[M, F] }, _ , [] }, A]}] }, S) -> + translate({ function, Meta, [M, F, A] }, S); -translate({ function, Line, [{ '/', _, [{F, _, Q}, A]}] }, S) when is_atom(Q) -> - translate({ function, Line, [F, A] }, S); +translate({ function, Meta, [{ '/', _, [{F, _, Q}, A]}] }, S) when is_atom(Q) -> + translate({ function, Meta, [F, A] }, S); -translate({ function, Line, [_] }, S) -> - assert_no_assign_or_guard_scope(Line, 'function', S), - syntax_error(Line, S#elixir_scope.file, "invalid args for function"); +translate({ function, Meta, [_] }, S) -> + assert_no_assign_or_guard_scope(Meta, 'function', S), + syntax_error(Meta, S#elixir_scope.file, "invalid args for function"); -translate({ function, Line, [_, _] = Args }, S) -> - assert_no_assign_or_guard_scope(Line, 'function', S), +translate({ function, Meta, [_, _] = Args }, S) -> + assert_no_assign_or_guard_scope(Meta, 'function', S), case translate_args(Args, S) of { [{atom,_,Name}, {integer,_,Arity}], SA } -> - case elixir_dispatch:import_function(Line, Name, Arity, SA) of - false -> syntax_error(Line, S#elixir_scope.file, "cannot convert a macro to a function"); + case elixir_dispatch:import_function(Meta, Name, Arity, SA) of + false -> syntax_error(Meta, S#elixir_scope.file, "cannot convert a macro to a function"); Else -> Else end; _ -> - syntax_error(Line, S#elixir_scope.file, "cannot dynamically retrieve local function. use function(module, fun, arity) instead") + syntax_error(Meta, S#elixir_scope.file, "cannot dynamically retrieve local function. use function(module, fun, arity) instead") end; -translate({ function, Line, [_,_,_] = Args }, S) when is_list(Args) -> - assert_no_assign_or_guard_scope(Line, 'function', S), +translate({ function, Meta, [_,_,_] = Args }, S) when is_list(Args) -> + assert_no_assign_or_guard_scope(Meta, 'function', S), { [A,B,C], SA } = translate_args(Args, S), - { { 'fun', Line, { function, A, B, C } }, SA }; + { { 'fun', ?line(Meta), { function, A, B, C } }, SA }; %% @ -translate({'@', Line, [{ Name, _, [Arg] }]}, S) when Name == typep; Name == type; Name == spec; Name == callback; Name == opaque -> +translate({'@', Meta, [{ Name, _, [Arg] }]}, S) when Name == typep; Name == type; Name == spec; Name == callback; Name == opaque -> case elixir_compiler:get_opt(internal) of - true -> { { nil, Line }, S }; + true -> { { nil, ?line(Meta) }, S }; false -> - Call = { { '.', Line, ['Elixir.Kernel.Typespec', spec_to_macro(Name)] }, Line, [Arg] }, + Call = { { '.', Meta, ['Elixir.Kernel.Typespec', spec_to_macro(Name)] }, Meta, [Arg] }, translate_each(Call, S) end; -translate({'@', Line, [{ Name, _, Args }]}, S) -> - assert_module_scope(Line, '@', S), +translate({'@', Meta, [{ Name, _, Args }]}, S) -> + assert_module_scope(Meta, '@', S), case is_reserved_data(Name) andalso elixir_compiler:get_opt(internal) of true -> - { { nil, Line }, S }; + { { nil, ?line(Meta) }, S }; _ -> case Args of [Arg] -> case S#elixir_scope.function of nil -> translate_each({ - { '.', Line, ['Elixir.Module', put_attribute] }, - Line, - [ { '__MODULE__', Line, false }, Name, Arg ] + { '.', Meta, ['Elixir.Module', put_attribute] }, + Meta, + [ { '__MODULE__', Meta, false }, Name, Arg ] }, S); _ -> - syntax_error(Line, S#elixir_scope.file, + syntax_error(Meta, S#elixir_scope.file, "cannot dynamically set attribute @~s inside a function", [Name]) end; _ when is_atom(Args) or (Args == []) -> case S#elixir_scope.function of nil -> translate_each({ - { '.', Line, ['Elixir.Module', get_attribute] }, - Line, - [ { '__MODULE__', Line, false }, Name ] + { '.', Meta, ['Elixir.Module', get_attribute] }, + Meta, + [ { '__MODULE__', Meta, false }, Name ] }, S); _ -> Contents = 'Elixir.Module':get_attribute(S#elixir_scope.module, Name), { elixir_tree_helpers:abstract_syntax(Contents), S } end; _ -> - syntax_error(Line, S#elixir_scope.file, "expected 0 or 1 argument for @~s, got: ~p", [Name, length(Args)]) + syntax_error(Meta, S#elixir_scope.file, "expected 0 or 1 argument for @~s, got: ~p", [Name, length(Args)]) end end; %% Case -translate({'case', Line, [Expr, KV]}, S) -> - assert_no_assign_or_guard_scope(Line, 'case', S), - Clauses = elixir_clauses:get_pairs(Line, do, KV, S), +translate({'case', Meta, [Expr, KV]}, S) -> + assert_no_assign_or_guard_scope(Meta, 'case', S), + Clauses = elixir_clauses:get_pairs(Meta, do, KV, S), { TExpr, NS } = translate_each(Expr, S), RClauses = case elixir_tree_helpers:returns_boolean(TExpr) of @@ -143,55 +143,55 @@ translate({'case', Line, [Expr, KV]}, S) -> false -> Clauses end, - { TClauses, TS } = elixir_clauses:match(Line, RClauses, NS), - { { 'case', Line, TExpr, TClauses }, TS }; + { TClauses, TS } = elixir_clauses:match(Meta, RClauses, NS), + { { 'case', ?line(Meta), TExpr, TClauses }, TS }; %% Try -translate({'try', Line, [Clauses]}, RawS) -> +translate({'try', Meta, [Clauses]}, RawS) -> S = RawS#elixir_scope{noname=true}, - assert_no_assign_or_guard_scope(Line, 'try', S), + assert_no_assign_or_guard_scope(Meta, 'try', S), Do = proplists:get_value('do', Clauses, nil), { TDo, SB } = elixir_translator:translate_each(Do, S), Catch = [Tuple || { X, _ } = Tuple <- Clauses, X == 'rescue' orelse X == 'catch'], - { TCatch, SC } = elixir_try:clauses(Line, Catch, umergec(S, SB)), + { TCatch, SC } = elixir_try:clauses(Meta, Catch, umergec(S, SB)), After = proplists:get_value('after', Clauses, nil), { TAfter, SA } = elixir_translator:translate_each(After, umergec(S, SC)), - Else = elixir_clauses:get_pairs(Line, else, Clauses, S), - { TElse, SE } = elixir_clauses:match(Line, Else, umergec(S, SA)), + Else = elixir_clauses:get_pairs(Meta, else, Clauses, S), + { TElse, SE } = elixir_clauses:match(Meta, Else, umergec(S, SA)), - { { 'try', Line, pack(TDo), TElse, TCatch, pack(TAfter) }, umergec(RawS, SE) }; + { { 'try', ?line(Meta), pack(TDo), TElse, TCatch, pack(TAfter) }, umergec(RawS, SE) }; %% Receive -translate({'receive', Line, [KV] }, S) -> - assert_no_assign_or_guard_scope(Line, 'receive', S), - Do = elixir_clauses:get_pairs(Line, do, KV, S, true), +translate({'receive', Meta, [KV] }, S) -> + assert_no_assign_or_guard_scope(Meta, 'receive', S), + Do = elixir_clauses:get_pairs(Meta, do, KV, S, true), case lists:keyfind('after', 1, KV) of false -> - { TClauses, SC } = elixir_clauses:match(Line, Do, S), - { { 'receive', Line, TClauses }, SC }; + { TClauses, SC } = elixir_clauses:match(Meta, Do, S), + { { 'receive', ?line(Meta), TClauses }, SC }; _ -> - After = elixir_clauses:get_pairs(Line, 'after', KV, S), - { TClauses, SC } = elixir_clauses:match(Line, Do ++ After, S), + After = elixir_clauses:get_pairs(Meta, 'after', KV, S), + { TClauses, SC } = elixir_clauses:match(Meta, Do ++ After, S), { FClauses, TAfter } = elixir_tree_helpers:split_last(TClauses), { _, _, [FExpr], _, FAfter } = TAfter, - { { 'receive', Line, FClauses, FExpr, FAfter }, SC } + { { 'receive', ?line(Meta), FClauses, FExpr, FAfter }, SC } end; %% Definitions -translate({defmodule, Line, [Ref, KV]}, S) -> +translate({defmodule, Meta, [Ref, KV]}, S) -> { TRef, _ } = translate_each(Ref, S), Block = case lists:keyfind(do, 1, KV) of { do, DoValue } -> DoValue; - false -> syntax_error(Line, S#elixir_scope.file, "expected do: argument in defmodule") + false -> syntax_error(Meta, S#elixir_scope.file, "expected do: argument in defmodule") end, { FRef, FS } = case TRef of @@ -202,66 +202,68 @@ translate({defmodule, Line, [Ref, KV]}, S) -> true -> S; false -> element(2, translate_each({ - alias, Line, [NewModule, [{as, elixir_aliases:first(Module)}]] + alias, Meta, [NewModule, [{as, elixir_aliases:first(Module)}]] }, S)) end, { - { atom, Line, NewModule }, + { atom, Meta, NewModule }, RS#elixir_scope{scheduled=[NewModule|S#elixir_scope.scheduled]} }; _ -> { TRef, S } end, - { elixir_module:translate(Line, FRef, Block, S#elixir_scope{check_clauses=true}), FS }; + { elixir_module:translate(Meta, FRef, Block, S#elixir_scope{check_clauses=true}), FS }; -translate({Kind, Line, [Call]}, S) when ?FUNS(Kind) -> - translate({Kind, Line, [Call, nil]}, S); +translate({Kind, Meta, [Call]}, S) when ?FUNS(Kind) -> + translate({Kind, Meta, [Call, nil]}, S); -translate({Kind, Line, [Call, Expr]}, S) when ?FUNS(Kind) -> - assert_module_scope(Line, Kind, S), - assert_no_function_scope(Line, Kind, S), +translate({Kind, Meta, [Call, Expr]}, S) when ?FUNS(Kind) -> + assert_module_scope(Meta, Kind, S), + assert_no_function_scope(Meta, Kind, S), { TCall, Guards } = elixir_clauses:extract_guards(Call), { Name, Args } = case elixir_clauses:extract_args(TCall) of - error -> syntax_error(Line, S#elixir_scope.file, + error -> syntax_error(Meta, S#elixir_scope.file, "invalid syntax in ~s ~s", [Kind, 'Elixir.Macro':to_binary(TCall)]); Tuple -> Tuple end, - assert_no_aliases_name(Line, Name, Args, S), + assert_no_aliases_name(Meta, Name, Args, S), TName = elixir_tree_helpers:abstract_syntax(Name), TArgs = elixir_tree_helpers:abstract_syntax(Args), TGuards = elixir_tree_helpers:abstract_syntax(Guards), TExpr = elixir_tree_helpers:abstract_syntax(Expr), - { elixir_def:wrap_definition(Kind, Line, TName, TArgs, TGuards, TExpr, S), S }; + { elixir_def:wrap_definition(Kind, Meta, TName, TArgs, TGuards, TExpr, S), S }; -translate({Kind, Line, [Name, Args, Guards, Expr]}, S) when ?FUNS(Kind) -> - assert_module_scope(Line, Kind, S), - assert_no_function_scope(Line, Kind, S), +translate({Kind, Meta, [Name, Args, Guards, Expr]}, S) when ?FUNS(Kind) -> + assert_module_scope(Meta, Kind, S), + assert_no_function_scope(Meta, Kind, S), { TName, NS } = translate_each(Name, S), { TArgs, AS } = translate_each(Args, NS), { TGuards, GS } = translate_each(Guards, AS), { TExpr, ES } = translate_each(Expr, GS), - { elixir_def:wrap_definition(Kind, Line, TName, TArgs, TGuards, TExpr, ES), ES }; + { elixir_def:wrap_definition(Kind, Meta, TName, TArgs, TGuards, TExpr, ES), ES }; %% Apply - Optimize apply by checking what doesn't need to be dispatched dynamically -translate({ apply, Line, [Left, Right, Args] }, S) when is_list(Args) -> +translate({ apply, Meta, [Left, Right, Args] }, S) when is_list(Args) -> { TLeft, SL } = translate_each(Left, S), { TRight, SR } = translate_each(Right, umergec(S, SL)), - translate_apply(Line, TLeft, TRight, Args, S, SL, SR); + translate_apply(Meta, TLeft, TRight, Args, S, SL, SR); -translate({ apply, Line, Args }, S) -> +translate({ apply, Meta, Args }, S) -> { TArgs, NS } = translate_args(Args, S), - { ?ELIXIR_WRAP_CALL(Line, erlang, apply, TArgs), NS }. + { ?wrap_call(?line(Meta), erlang, apply, TArgs), NS }. %% Helpers -translate_in(Line, Left, Right, S) -> +translate_in(Meta, Left, Right, S) -> + Line = ?line(Meta), + { TLeft, SL } = case Left of { '_', _, Atom } when is_atom(Atom) -> elixir_scope:build_erl_var(Line, S); @@ -304,7 +306,7 @@ translate_in(Line, Left, Right, S) -> decreasing_compare(Line, Var, Start, End) } } end; _ -> - syntax_error(Line, S#elixir_scope.file, "invalid args for operator in, it expects an explicit array or an explicit range on the right side") + syntax_error(Meta, S#elixir_scope.file, "invalid args for operator in, it expects an explicit array or an explicit range on the right side") end, case Cache of @@ -351,9 +353,9 @@ spec_to_macro(callback) -> defcallback. pack({ 'block', _, Exprs }) -> Exprs; pack(Expr) -> [Expr]. -assert_no_aliases_name(Line, '__aliases__', [Atom], #elixir_scope{file=File}) when is_atom(Atom) -> +assert_no_aliases_name(Meta, '__aliases__', [Atom], #elixir_scope{file=File}) when is_atom(Atom) -> Message = "function names should start with lowercase characters or underscore, invalid name ~s", - syntax_error(Line, File, Message, [atom_to_binary(Atom, utf8)]); + syntax_error(Meta, File, Message, [atom_to_binary(Atom, utf8)]); -assert_no_aliases_name(_Line, _Aliases, _Args, _S) -> +assert_no_aliases_name(_Meta, _Aliases, _Args, _S) -> ok. \ No newline at end of file diff --git a/lib/elixir/src/elixir_module.erl b/lib/elixir/src/elixir_module.erl index 6416e1ffda..95f1a57c62 100644 --- a/lib/elixir/src/elixir_module.erl +++ b/lib/elixir/src/elixir_module.erl @@ -31,7 +31,7 @@ data_table(Module) -> Module. docs_table(Module) -> - ?ELIXIR_ATOM_CONCAT([o, Module]). + ?atom_concat([o, Module]). %% TRANSFORMATION FUNCTIONS @@ -39,12 +39,13 @@ docs_table(Module) -> %% The abstract form for extra arguments may be given and they %% will be passed to the invoked function. -translate(Line, Ref, Block, S) -> +translate(Meta, Ref, Block, S) -> + Line = ?line(Meta), MetaBlock = elixir_tree_helpers:abstract_syntax(Block), { MetaS, Vars } = elixir_scope:serialize_with_vars(Line, S), Args = [{integer, Line, Line}, Ref, MetaBlock, Vars, MetaS], - ?ELIXIR_WRAP_CALL(Line, ?MODULE, compile, Args). + ?wrap_call(Line, ?MODULE, compile, Args). %% The compilation hook. @@ -220,7 +221,7 @@ translate_spec({ Spec, Rest }, Defmacro, Defmacrop) -> case ordsets:is_element(Spec, Defmacro) of true -> { Name, Arity } = Spec, - { { ?ELIXIR_MACRO(Name), Arity + 1 }, spec_for_macro(Rest) }; + { { ?elixir_macro(Name), Arity + 1 }, spec_for_macro(Rest) }; false -> { Spec, Rest } end @@ -324,9 +325,10 @@ eval_callbacks(Line, Module, Name, Args, RawS) -> Binding = binding_for_eval(Module, []), Callbacks = lists:reverse(ets:lookup_element(data_table(Module), Name, 2)), Requires = S#elixir_scope.requires, + Meta = [{line,Line}], lists:foreach(fun({M,F}) -> - Expr = { { '.', Line, [M,F] }, Line, Args }, + Expr = { { '.', Meta, [M,F] }, Meta, Args }, Scope = case ordsets:is_element(M, Requires) of true -> S; false -> S#elixir_scope{requires=ordsets:add_element(M, Requires)} diff --git a/lib/elixir/src/elixir_parser.yrl b/lib/elixir/src/elixir_parser.yrl index 4ebe0a2d03..3d02c12f9d 100644 --- a/lib/elixir/src/elixir_parser.yrl +++ b/lib/elixir/src/elixir_parser.yrl @@ -23,7 +23,7 @@ Nonterminals Terminals identifier kw_identifier punctuated_identifier bracket_identifier paren_identifier do_identifier block_identifier - fn 'end' '__aliases__' + fn 'end' aliases number signed_number atom bin_string list_string sigil dot_call_op comp_op op_identifier 'not' 'and' 'or' 'xor' 'when' 'in' 'inlist' 'inbits' 'do' @@ -141,13 +141,13 @@ matched_op_expr -> colon_colon_op matched_expr : { '$1', '$2' }. matched_op_expr -> comp_expr_op matched_expr : { '$1', '$2' }. block_expr -> parens_call call_args_parens do_block : build_identifier('$1', '$2' ++ '$3'). -block_expr -> parens_call call_args_parens call_args_parens do_block : { build_identifier('$1', '$2'), ?line('$1'), '$3' ++ '$4' }. +block_expr -> parens_call call_args_parens call_args_parens do_block : build_nested_parens('$1', '$2', '$3' ++ '$4'). block_expr -> dot_punctuated_identifier call_args_no_parens do_block : build_identifier('$1', '$2' ++ '$3'). block_expr -> dot_do_identifier do_block : build_identifier('$1', '$2'). block_expr -> dot_identifier call_args_no_parens do_block : build_identifier('$1', '$2' ++ '$3'). fn_expr -> fn_eol stab_expr_list end_eol : build_fn('$1', build_stab(lists:reverse('$2'))). -fn_expr -> fn_eol '->' grammar 'end' : build_fn('$1', { '->', ?line('$2'), [{ [], build_block('$3') }] }). +fn_expr -> fn_eol '->' grammar 'end' : build_fn('$1', { '->', [{line,?line('$2')}], [{ [], build_block('$3') }] }). fn_expr -> call_expr : '$1'. call_expr -> dot_punctuated_identifier call_args_no_parens : build_identifier('$1', '$2'). @@ -160,7 +160,7 @@ call_expr -> max_expr : '$1'. max_expr -> bracket_expr : '$1'. max_expr -> parens_call call_args_parens : build_identifier('$1', '$2'). -max_expr -> parens_call call_args_parens call_args_parens : { build_identifier('$1', '$2'), ?line('$1'), '$3' }. +max_expr -> parens_call call_args_parens call_args_parens : build_nested_parens('$1', '$2', '$3'). max_expr -> dot_ref : '$1'. max_expr -> base_expr : '$1'. max_expr -> open_paren stab_expr_list close_paren : build_stab(lists:reverse('$2')). @@ -173,19 +173,19 @@ bracket_at_expr -> at_op max_expr bracket_access : build_access(build_unary_op(' bracket_at_expr -> bracket_at_expr bracket_access : build_access('$1', '$2'). base_expr -> number : ?exprs('$1'). -base_expr -> signed_number : { element(4, '$1'), ?line('$1'), ?exprs('$1') }. +base_expr -> signed_number : { element(4, '$1'), [{line,?line('$1')}], ?exprs('$1') }. base_expr -> atom : build_atom('$1'). base_expr -> list : '$1'. base_expr -> tuple : '$1'. base_expr -> 'true' : ?op('$1'). base_expr -> 'false' : ?op('$1'). base_expr -> 'nil' : ?op('$1'). -base_expr -> '__aliases__' : '$1'. +base_expr -> aliases : { '__aliases__', [{line,?line('$1')}], ?exprs('$1') }. base_expr -> bin_string : build_bin_string('$1'). base_expr -> list_string : build_list_string('$1'). base_expr -> bit_string : '$1'. -base_expr -> '&' : '$1'. -base_expr -> '...' : { ?op('$1'), ?line('$1'), [] }. +base_expr -> '&' : { '&', [{line,?line('$1')}], ?exprs('$1') }. +base_expr -> '...' : { ?op('$1'), [{line,?line('$1')}], [] }. base_expr -> sigil : build_sigil('$1'). %% Blocks @@ -359,7 +359,7 @@ dot_op -> '.' eol : '$1'. dot_identifier -> identifier : '$1'. dot_identifier -> matched_expr dot_op identifier : build_dot('$2', '$1', '$3'). -dot_ref -> matched_expr dot_op '__aliases__' : build_dot_ref('$2', '$1', '$3'). +dot_ref -> matched_expr dot_op aliases : build_dot_ref('$2', '$1', '$3'). dot_op_identifier -> op_identifier : '$1'. dot_op_identifier -> matched_expr dot_op op_identifier : build_dot('$2', '$1', '$3'). @@ -377,7 +377,7 @@ dot_punctuated_identifier -> punctuated_identifier : '$1'. dot_punctuated_identifier -> matched_expr dot_op punctuated_identifier : build_dot('$2', '$1', '$3'). parens_call -> dot_paren_identifier : '$1'. -parens_call -> matched_expr dot_call_op : { '.', ?line('$2'), ['$1'] }. % Fun/local calls +parens_call -> matched_expr dot_call_op : { '.', [{line,?line('$2')}], ['$1'] }. % Fun/local calls % Function calls @@ -447,8 +447,8 @@ tuple -> open_curly paren_expr ',' call_args close_curly : build_tuple('$1', [' % Bitstrings -bit_string -> open_bit '>>' : { '<<>>', ?line('$1'), [] }. -bit_string -> open_bit call_args close_bit : { '<<>>', ?line('$1'), '$2' }. +bit_string -> open_bit '>>' : { '<<>>', [{line,?line('$1')}], [] }. +bit_string -> open_bit call_args close_bit : { '<<>>', [{line,?line('$1')}], '$2' }. Erlang code. @@ -466,58 +466,62 @@ Erlang code. %% Operators build_op({ _, _, _ } = Op, Left, Right) -> - { ?exprs(Op), ?line(Op), [Left, Right] }; + { ?exprs(Op), [{line,?line(Op)}], [Left, Right] }; build_op({ BOp, Line }, { UOp, _, [Left] }, Right) when ?rearrange_bop(BOp), ?rearrange_uop(UOp) -> - { UOp, Line, [{ BOp, Line, [Left, Right] }] }; + { UOp, [{line,Line}], [{ BOp, [{line,Line}], [Left, Right] }] }; build_op(Op, Left, Right) -> - { ?op(Op), ?line(Op), [Left, Right] }. + { ?op(Op), [{line,?line(Op)}], [Left, Right] }. build_unary_op(Op, Expr) -> - { ?op(Op), ?line(Op), [Expr] }. + { ?op(Op), [{line,?line(Op)}], [Expr] }. build_tuple(_Marker, [Left, Right]) -> { Left, Right }; build_tuple(Marker, Args) -> - { '{}', ?line(Marker), Args }. + { '{}', [{line,?line(Marker)}], Args }. %% Blocks -build_block([nil]) -> { '__block__', 0, [nil] }; -build_block([{Op,_,[_]}]=Exprs) when ?rearrange_uop(Op) -> { '__block__', 0, Exprs }; +build_block([nil]) -> { '__block__', [], [nil] }; +build_block([{Op,_,[_]}]=Exprs) when ?rearrange_uop(Op) -> { '__block__', [], Exprs }; build_block([Expr]) when not is_list(Expr) -> Expr; -build_block(Exprs) -> { '__block__', 0, Exprs }. +build_block(Exprs) -> { '__block__', [], Exprs }. %% Dots -build_dot_ref(Dot, { '__aliases__', _, Left }, { '__aliases__', _, Right }) -> - { '__aliases__', ?line(Dot), Left ++ Right }; +build_dot_ref(Dot, { '__aliases__', _, Left }, { 'aliases', _, Right }) -> + { '__aliases__', [{line,?line(Dot)}], Left ++ Right }; -build_dot_ref(Dot, Other, { '__aliases__', _, Right }) -> - { '__aliases__', ?line(Dot), [Other|Right] }. +build_dot_ref(Dot, Other, { 'aliases', _, Right }) -> + { '__aliases__', [{line,?line(Dot)}], [Other|Right] }. build_dot(Dot, Left, Right) -> - { '.', ?line(Dot), [Left, extract_identifier(Right)] }. + { '.', [{line,?line(Dot)}], [Left, extract_identifier(Right)] }. %% Identifiers -build_identifier({ '.', Line, _ } = Dot, Args) -> +build_nested_parens(Dot, Args1, Args2) -> + Identifier = build_identifier(Dot, Args1), + { Identifier, ?line(Identifier), Args2 }. + +build_identifier({ '.', Meta, _ } = Dot, Args) -> FArgs = case Args of nil -> []; _ -> Args end, - { Dot, Line, FArgs }; + { Dot, Meta, FArgs }; build_identifier({ Keyword, Line }, Args) when Keyword == fn -> - { fn, Line, Args }; + { fn, [{line,Line}], Args }; build_identifier({ op_identifier, Line, Identifier }, Args) -> - { '__ambiguousop__', Line, [{ Identifier, Line, nil }|Args] }; + { '__ambiguousop__', [{line,Line}], [{ Identifier, [{line,Line}], nil }|Args] }; build_identifier({ _, Line, Identifier }, Args) -> - { Identifier, Line, Args }. + { Identifier, [{line,Line}], Args }. extract_identifier({ Kind, _, Identifier }) when Kind == identifier; Kind == punctuated_identifier; Kind == bracket_identifier; @@ -529,32 +533,37 @@ extract_identifier(Other) -> Other. %% Fn build_fn(Op, Stab) -> - { fn, ?line(Op), [[{ do, Stab }]] }. + { fn, [{line,?line(Op)}], [[{ do, Stab }]] }. %% Access build_access(Expr, Access) -> - Line = ?line(Access), - { { '.', Line, ['Elixir-Kernel', access] }, ?line(Access), [ Expr, ?op(Access) ] }. + Meta = [{line,?line(Access)}], + { { '.', Meta, ['Elixir-Kernel', access] }, Meta, [ Expr, ?op(Access) ] }. %% Interpolation aware build_sigil({ sigil, Line, Sigil, Parts, Modifiers }) -> - { list_to_atom([$_,$_,Sigil,$_,$_]), Line, [ { '<<>>', Line, Parts }, Modifiers ] }. + Meta = [{line,Line}], + { list_to_atom([$_,$_,Sigil,$_,$_]), Meta, [ { '<<>>', Meta, Parts }, Modifiers ] }. build_bin_string({ bin_string, _Line, [H] }) when is_binary(H) -> H; -build_bin_string({ bin_string, Line, Args }) -> { '<<>>', Line, Args }. +build_bin_string({ bin_string, Line, Args }) -> { '<<>>', [{line,Line}], Args }. build_list_string({ list_string, _Line, [H] }) when is_binary(H) -> binary_to_list(H); -build_list_string({ list_string, Line, Args }) -> { { '.', Line, [erlang, binary_to_list] }, Line, [{ '<<>>', Line, Args}] }. +build_list_string({ list_string, Line, Args }) -> + Meta = [{line,Line}], + { { '.', Meta, [erlang, binary_to_list] }, Meta, [{ '<<>>', Meta, Args}] }. build_atom({ atom, _Line, Atom }) when is_atom(Atom) -> Atom; -build_atom({ atom, Line, Args }) -> { { '.', Line, [erlang, binary_to_atom] }, Line, [{ '<<>>', Line, Args }, utf8] }. +build_atom({ atom, Line, Args }) -> + Meta = [{line,Line}], + { { '.', Meta, [erlang, binary_to_atom] }, Meta, [{ '<<>>', Meta, Args }, utf8] }. %% Keywords -build_stab([{ '->', Line, [Left, Right] }|T]) -> - { '->', Line, build_stab(T, Left, [Right], []) }; +build_stab([{ '->', Meta, [Left, Right] }|T]) -> + { '->', Meta, build_stab(T, Left, [Right], []) }; build_stab(Else) -> build_block(Else). diff --git a/lib/elixir/src/elixir_partials.erl b/lib/elixir/src/elixir_partials.erl index b70bb941c1..7782c87188 100644 --- a/lib/elixir/src/elixir_partials.erl +++ b/lib/elixir/src/elixir_partials.erl @@ -6,28 +6,28 @@ handle(Original, S) -> handle(Original, S, default). -handle({ _, Line, Args } = Original, S, Opt) when is_list(Args), S#elixir_scope.context /= assign -> +handle({ _, Meta, Args } = Original, S, Opt) when is_list(Args), S#elixir_scope.context /= assign -> case convert(Args, S, Opt) of { Call, Def, SC } when Def /= [] -> - Final = validate(Line, Def, SC), + Final = validate(Meta, Def, SC), Block = setelement(3, Original, Call), - elixir_translator:translate_fn(Line, [{ Final, Block }], SC); + elixir_translator:translate_fn(Meta, [{ Final, Block }], SC); _ -> error end; handle(_Original, _S, _Opt) -> error. -validate(Line, Def, S) -> - validate(Line, lists:sort(Def), 1, S). +validate(Meta, Def, S) -> + validate(Meta, lists:sort(Def), 1, S). -validate(Line, [{ Pos, Item }|T], Pos, S) -> - [Item|validate(Line, T, Pos + 1, S)]; +validate(Meta, [{ Pos, Item }|T], Pos, S) -> + [Item|validate(Meta, T, Pos + 1, S)]; -validate(Line, [{ Pos, _ }|_], Expected, S) -> - elixir_errors:syntax_error(Line, S#elixir_scope.file, "partial variable &~w cannot be defined without &~w", [Pos, Expected]); +validate(Meta, [{ Pos, _ }|_], Expected, S) -> + elixir_errors:syntax_error(Meta, S#elixir_scope.file, "partial variable &~w cannot be defined without &~w", [Pos, Expected]); -validate(_Line, [], _Pos, _S) -> +validate(_Meta, [], _Pos, _S) -> []. %% This function receives arguments and then checks @@ -37,14 +37,14 @@ validate(_Line, [], _Pos, _S) -> %% function definition and the third one is the new scope. convert(List, S, Opt) -> convert(List, S, Opt, [], []). -convert([{'|', Line, [_, _] = Args}|T], S, allow_tail, CallAcc, DefAcc) -> +convert([{'|', Meta, [_, _] = Args}|T], S, allow_tail, CallAcc, DefAcc) -> { NewArgs, NewDef, NewS } = convert(Args, S, allow_tail, [], DefAcc), - convert(T, NewS, allow_tail, [{ '|', Line, NewArgs}|CallAcc], NewDef); + convert(T, NewS, allow_tail, [{ '|', Meta, NewArgs}|CallAcc], NewDef); -convert([{'&', Line, [Pos]}|T], S, Opt, CallAcc, DefAcc) -> +convert([{'&', Meta, [Pos]}|T], S, Opt, CallAcc, DefAcc) -> case lists:keyfind(Pos, 1, DefAcc) of false -> - { Var, SC } = elixir_scope:build_ex_var(Line, S), + { Var, SC } = elixir_scope:build_ex_var(?line(Meta), S), convert(T, SC, Opt, [Var|CallAcc], [{Pos,Var}|DefAcc]); {Pos,Var} -> convert(T, S, Opt, [Var|CallAcc], DefAcc) diff --git a/lib/elixir/src/elixir_quote.erl b/lib/elixir/src/elixir_quote.erl index 886741fec5..512aa96ec3 100644 --- a/lib/elixir/src/elixir_quote.erl +++ b/lib/elixir/src/elixir_quote.erl @@ -3,87 +3,94 @@ -include("elixir.hrl"). %% Apply the line from site call on quoted contents. +linify(Line, Exprs) when is_integer(Line) -> + do_linify(Line, Exprs). -linify(Line, { Left, 0, Right }) -> - { linify(Line, Left), Line, linify(Line, Right) }; +do_linify(Line, { Left, Meta, Right }) -> + NewMeta = case ?line(Meta) of + 0 -> lists:keystore(line, 1, Meta, { line, Line }); + _ -> Meta + end, + { do_linify(Line, Left), NewMeta, do_linify(Line, Right) }; -linify(Line, Tuple) when is_tuple(Tuple) -> - list_to_tuple(linify(Line, tuple_to_list(Tuple))); +do_linify(Line, { Left, Right }) -> + { do_linify(Line, Left), do_linify(Line, Right) }; -linify(Line, List) when is_list(List) -> - [linify(Line, X) || X <- List]; +do_linify(Line, List) when is_list(List) -> + [do_linify(Line, X) || X <- List]; -linify(_, Else) -> Else. +do_linify(_, Else) -> Else. %% Join quoted checks for quote arguments at runtime %% in order to properly insert them into the tree -join_quoted(Line, Left, { '__aliases__', _, Args }, nil, _File) -> - { '__aliases__', Line, [Left|Args] }; +join_quoted(Meta, Left, { '__aliases__', _, Args }, nil, _File) -> + { '__aliases__', Meta, [Left|Args] }; -join_quoted(Line, Left, Right, nil, _File) when is_atom(Right) -> +join_quoted(Meta, Left, Right, nil, _File) when is_atom(Right) -> case atom_to_list(Right) of "Elixir-" ++ _ -> - { '__aliases__', Line, [Left, Right] }; + { '__aliases__', Meta, [Left, Right] }; _ -> - { { '.', Line, [Left, Right] }, Line, [] } + { { '.', Meta, [Left, Right] }, Meta, [] } end; -join_quoted(Line, Left, Right, Args, _File) when is_atom(Right) -> - { { '.', Line, [Left, Right] }, Line, Args }; +join_quoted(Meta, Left, Right, Args, _File) when is_atom(Right) -> + { { '.', Meta, [Left, Right] }, Meta, Args }; -join_quoted(Line, _Left, _Right, _Args, File) -> - elixir_errors:syntax_error(Line, File, "expected unquote after dot to return an atom or an alias"). +join_quoted(Meta, _Left, _Right, _Args, File) -> + elixir_errors:syntax_error(Meta, File, "expected unquote after dot to return an atom or an alias"). %% Translation -quote({ 'unquote_splicing', Line, _ } = Expr, #elixir_quote{unquote=true} = Q, S) -> - do_quote({ '__block__', Line, [Expr] }, Q, S); +quote({ 'unquote_splicing', Meta, _ } = Expr, #elixir_quote{unquote=true} = Q, S) -> + do_quote({ '__block__', Meta, [Expr] }, Q, S); quote(Else, Q, S) -> do_quote(Else, Q, S). -do_quote({ unquote, _Line, [Expr] }, #elixir_quote{unquote=true}, S) -> +do_quote({ unquote, _Meta, [Expr] }, #elixir_quote{unquote=true}, S) -> elixir_translator:translate_each(Expr, S); -do_quote({ 'alias!', _Line, [Expr] }, Q, S) -> +do_quote({ 'alias!', _Meta, [Expr] }, Q, S) -> do_quote(Expr, Q#elixir_quote{expand_aliases=false}, S); -do_quote({ '__aliases__', Line, [H|_] = Aliases }, #elixir_quote{expand_aliases=true} = Q, S) when is_atom(H) and H /= 'Elixir' -> +do_quote({ '__aliases__', Meta, [H|_] = Aliases }, #elixir_quote{expand_aliases=true} = Q, S) when is_atom(H) and H /= 'Elixir' -> + Line = ?line(Meta), { TAliases, SA } = do_quote(['Elixir'|Aliases], Q, S), { { tuple, Line, [ { atom, Line, '__aliases__' }, - line(Line, Q), + meta(Meta, Q), TAliases ] }, SA }; -do_quote({ { { '.', Line, [Left, unquote] }, _, [Expr] }, _, Args }, #elixir_quote{unquote=true} = Q, S) -> - All = [Left, { unquote, Line, [Expr] }, Args, S#elixir_scope.file], +do_quote({ { { '.', Meta, [Left, unquote] }, _, [Expr] }, _, Args }, #elixir_quote{unquote=true} = Q, S) -> + All = [Left, { unquote, Meta, [Expr] }, Args, S#elixir_scope.file], { TAll, TS } = lists:mapfoldl(fun(X, Acc) -> do_quote(X, Q, Acc) end, S, All), - { ?ELIXIR_WRAP_CALL(Line, elixir_quote, join_quoted, [line(Line, Q)|TAll]), TS }; + { ?wrap_call(?line(Meta), elixir_quote, join_quoted, [meta(Meta, Q)|TAll]), TS }; -do_quote({ { '.', Line, [Left, unquote] }, _, [Expr] }, #elixir_quote{unquote=true} = Q, S) -> - All = [Left, { unquote, Line, [Expr] }, nil, S#elixir_scope.file], +do_quote({ { '.', Meta, [Left, unquote] }, _, [Expr] }, #elixir_quote{unquote=true} = Q, S) -> + All = [Left, { unquote, Meta, [Expr] }, nil, S#elixir_scope.file], { TAll, TS } = lists:mapfoldl(fun(X, Acc) -> do_quote(X, Q, Acc) end, S, All), - { ?ELIXIR_WRAP_CALL(Line, elixir_quote, join_quoted, [line(Line, Q)|TAll]), TS }; + { ?wrap_call(?line(Meta), elixir_quote, join_quoted, [meta(Meta, Q)|TAll]), TS }; -do_quote({ Left, Line, nil }, Q, S) when is_atom(Left) -> +do_quote({ Left, Meta, nil }, Q, S) when is_atom(Left) -> + Line = ?line(Meta), Tuple = { tuple, Line, [ { atom, Line, Left }, - line(Line, Q), + meta(Meta, Q), { atom, Line, Q#elixir_quote.var_context } ] }, { Tuple, S }; -do_quote({ Left, Line, Right }, Q, S) -> - { TLeft, LS } = do_quote(Left, Q, S), - { TRight, RS } = do_quote(Right, Q, LS), +% do_quote({ Name, Line, Args } = Tuple, #elixir_quote{expand_imports=true} = Q, S) when is_atom(Name), is_list(Args) -> +% case elixir_dispatch:find_import(Line, Name, length(Args), S) of +% false -> do_quote_tuple(Tuple, Q, S); +% Receiver -> do_quote_tuple({ { '.', Line, [Receiver, Name] }, Line, Args }, Q, S) +% end; - %% We need to remove line numbers from quoted exprs - %% otherwise the line number quotes in the macro will - %% get mixed with the unquoted contents - Tuple = { tuple, Line, [TLeft, line(Line, Q), TRight] }, - { Tuple, RS }; +do_quote({ _, _, _ } = Tuple, Q, S) -> + do_quote_tuple(Tuple, Q, S); % Handle two item tuples but still allow them to be spliced. do_quote({ Left, Right }, Q, S) when @@ -91,26 +98,36 @@ do_quote({ Left, Right }, Q, S) when not is_tuple(Right) orelse (element(1, Right) /= unquote_splicing) -> { TLeft, LS } = do_quote(Left, Q, S), { TRight, RS } = do_quote(Right, Q, LS), - { { tuple, int_line(Q), [TLeft, TRight] }, RS }; + { { tuple, line(Q), [TLeft, TRight] }, RS }; do_quote({ Left, Right }, Q, S) -> - do_quote({ '{}', int_line(Q), [Left, Right] }, Q, S); + do_quote({ '{}', [{line,line(Q)}], [Left, Right] }, Q, S); do_quote(List, Q, S) when is_list(List) -> splice(List, Q, [], [], S); do_quote(Number, Q, S) when is_integer(Number) -> - { { integer, int_line(Q), Number }, S }; + { { integer, line(Q), Number }, S }; do_quote(Number, Q, S) when is_float(Number) -> - { { float, int_line(Q), Number }, S }; + { { float, line(Q), Number }, S }; do_quote(Atom, Q, S) when is_atom(Atom) -> - { { atom, int_line(Q), Atom }, S }; + { { atom, line(Q), Atom }, S }; do_quote(Bitstring, _Q, S) when is_bitstring(Bitstring) -> { elixir_tree_helpers:abstract_syntax(Bitstring), S }. +do_quote_tuple({ Left, Meta, Right }, Q, S) -> + { TLeft, LS } = do_quote(Left, Q, S), + { TRight, RS } = do_quote(Right, Q, LS), + + %% We need to remove line numbers from quoted exprs + %% otherwise the line number quotes in the macro will + %% get mixed with the unquoted contents + Tuple = { tuple, ?line(Meta), [TLeft, meta(Meta, Q), TRight] }, + { Tuple, RS }. + % Loop through the list finding each unquote_splicing entry. splice([{ unquote_splicing, _, [Args] }|T], #elixir_quote{unquote=true} = Q, Buffer, Acc, S) -> @@ -118,10 +135,10 @@ splice([{ unquote_splicing, _, [Args] }|T], #elixir_quote{unquote=true} = Q, Buf { TArgs, TS } = elixir_translator:translate_each(Args, NewS), splice(T, Q, [], [TArgs|NewAcc], TS); -splice([{ '|', Line, [{ unquote_splicing, _, [_] } = Left, Right] }], #elixir_quote{unquote=true} = Q, Buffer, Acc, S) -> +splice([{ '|', Meta, [{ unquote_splicing, _, [_] } = Left, Right] }], #elixir_quote{unquote=true} = Q, Buffer, Acc, S) -> { TLeft, SL } = splice([Left], Q, Buffer, Acc, S), { TRight, SR } = do_quote(Right, Q, SL), - { { op, Line, '++', TLeft, TRight }, SR }; + { { op, ?line(Meta), '++', TLeft, TRight }, SR }; splice([H|T], Q, Buffer, Acc, S) -> splice(T, Q, [H|Buffer], Acc, S); @@ -130,12 +147,12 @@ splice([], Q, Buffer, Acc, S) -> { NewAcc, NewS } = from_buffer_to_acc(Buffer, Q, Acc, S), case NewAcc of [] -> - { { nil, int_line(Q) }, NewS }; + { { nil, line(Q) }, NewS }; [List] -> { List, NewS }; _ -> - List = elixir_tree_helpers:build_simple_reverse_list(int_line(Q), NewAcc), - { ?ELIXIR_WRAP_CALL(int_line(Q), lists, append, [List]), NewS } + List = elixir_tree_helpers:build_simple_reverse_list(line(Q), NewAcc), + { ?wrap_call(line(Q), lists, append, [List]), NewS } end. from_buffer_to_acc([], _Q, Acc, S) -> @@ -143,14 +160,19 @@ from_buffer_to_acc([], _Q, Acc, S) -> from_buffer_to_acc(Buffer, Q, Acc, S) -> { New, NewS } = elixir_tree_helpers:build_reverse_list( - fun(X, AccS) -> do_quote(X, Q, AccS) end, Buffer, int_line(Q), S), + fun(X, AccS) -> do_quote(X, Q, AccS) end, Buffer, line(Q), S), { [New|Acc], NewS }. -line(Line, #elixir_quote{line=keep}) -> { integer, Line, Line }; -line(_Line, #elixir_quote{line=Line}) -> Line. +meta(Meta, #elixir_quote{line=keep}) -> + elixir_tree_helpers:abstract_syntax(Meta); +meta(Meta, #elixir_quote{line=nil}) -> + elixir_tree_helpers:abstract_syntax(lists:keydelete(line, 1, Meta)); +meta(Meta, #elixir_quote{line=Line} = Q) -> + { cons, + line(Q), + { tuple, line(Q), [{ atom, line(Q), line }, Line] }, + elixir_tree_helpers:abstract_syntax(lists:keydelete(line, 1, Meta)) + }. -int_line(Q) -> - case line(0, Q) of - { integer, _, Line } -> Line; - _ -> 0 - end. \ No newline at end of file +line(#elixir_quote{line={ integer, _, Line }}) -> Line; +line(_) -> 0. \ No newline at end of file diff --git a/lib/elixir/src/elixir_scope.erl b/lib/elixir/src/elixir_scope.erl index ec56d94b3a..580ac7629b 100644 --- a/lib/elixir/src/elixir_scope.erl +++ b/lib/elixir/src/elixir_scope.erl @@ -13,7 +13,8 @@ -include("elixir.hrl"). -compile({parse_transform, elixir_transform}). -translate_var(Line, Name, Kind, S) -> +translate_var(Meta, Name, Kind, S) -> + Line = ?line(Meta), Vars = S#elixir_scope.vars, case Name of @@ -47,7 +48,7 @@ translate_var(Line, Name, Kind, S) -> _ -> case orddict:find({ Name, Kind }, Vars) of { ok, VarName } -> { { var, Line, VarName }, S }; - error -> elixir_translator:translate_each({ Name, Line, [] }, S) + error -> elixir_translator:translate_each({ Name, Meta, [] }, S) end end end. @@ -64,19 +65,19 @@ build_var_counter(Key, #elixir_scope{counter=Counter} = S) -> New = orddict:update_counter(Key, 1, Counter), { orddict:fetch(Key, New), S#elixir_scope{counter=New} }. -build_erl_var(Line, Key, Name, S) -> +build_erl_var(Line, Key, Name, S) when is_integer(Line) -> { Counter, NS } = build_var_counter(Key, S), - Var = { var, Line, ?ELIXIR_ATOM_CONCAT([Name, "@", Counter]) }, + Var = { var, Line, ?atom_concat([Name, "@", Counter]) }, { Var, NS }. -build_ex_var(Line, Key, Name, S) -> +build_ex_var(Line, Key, Name, S) when is_integer(Line) -> Context = case S#elixir_scope.module of nil -> 'Elixir'; Mod -> Mod end, { Counter, NS } = build_var_counter(Key, S), - Var = { ?ELIXIR_ATOM_CONCAT([Name, "@", Counter]), Line, Context }, + Var = { ?atom_concat([Name, "@", Counter]), [{line,Line}], Context }, { Var, NS }. % Handle Macro.Env conversion @@ -84,12 +85,12 @@ build_ex_var(Line, Key, Name, S) -> to_erl_env(Scope) -> elixir_tree_helpers:abstract_syntax(to_ex_env(Scope)). -to_ex_env({ Line, Tuple }) when element(1, Tuple) == 'Elixir.Macro.Env' -> +to_ex_env({ Line, Tuple }) when element(1, Tuple) == 'Elixir.Macro.Env', is_integer(Line) -> setelement(4, Tuple, Line); to_ex_env({ Line, #elixir_scope{module=Module,file=File, function=Function,aliases=Aliases,context=Context, - requires=Requires,macros=Macros,functions=Functions} }) -> + requires=Requires,macros=Macros,functions=Functions} }) when is_integer(Line) -> { 'Elixir.Macro.Env', Module, File, Line, Function, Aliases, Context, Requires, Functions, Macros }. filename(#elixir_scope{file=File}) -> File; @@ -103,12 +104,12 @@ serialize(S) -> S#elixir_scope.requires, S#elixir_scope.macros, S#elixir_scope.aliases, S#elixir_scope.scheduled } ). -serialize_with_vars(Line, S) -> +serialize_with_vars(Line, S) when is_integer(Line) -> { Vars, _ } = orddict:fold(fun({ Key, Kind }, Value, { Acc, Counter }) -> { { cons, Line, { tuple, Line, [ { atom, Line, Key }, { atom, Line, Kind }, - { atom, Line, ?ELIXIR_ATOM_CONCAT(["_@", Counter]) }, + { atom, Line, ?atom_concat(["_@", Counter]) }, { var, Line, Value } ] }, Acc }, Counter + 1 } end, { { nil, Line }, 0 }, S#elixir_scope.vars), diff --git a/lib/elixir/src/elixir_tokenizer.erl b/lib/elixir/src/elixir_tokenizer.erl index 9050a6706b..2b985dd1ee 100644 --- a/lib/elixir/src/elixir_tokenizer.erl +++ b/lib/elixir/src/elixir_tokenizer.erl @@ -386,7 +386,7 @@ tokenize([H|_] = String, Line, Scope, Tokens) when ?is_upcase(H) -> [$:|T] when hd(T) /= $: -> tokenize(T, Line, Scope, [{ kw_identifier, Line, Atom }|Tokens]); _ -> - tokenize(Rest, Line, Scope, [{ '__aliases__', Line, [Atom] }|Tokens]) + tokenize(Rest, Line, Scope, [{ aliases, Line, [Atom] }|Tokens]) end; % Identifier diff --git a/lib/elixir/src/elixir_translator.erl b/lib/elixir/src/elixir_translator.erl index ac1e030a56..56b8f12448 100644 --- a/lib/elixir/src/elixir_translator.erl +++ b/lib/elixir/src/elixir_translator.erl @@ -38,11 +38,11 @@ translate(Forms, S) -> %% Assignment operator -translate_each({'=', Line, [Left, Right]}, S) -> - assert_no_guard_scope(Line, '=', S), +translate_each({'=', Meta, [Left, Right]}, S) -> + assert_no_guard_scope(Meta, '=', S), { TRight, SR } = translate_each(Right, S), { TLeft, SL } = elixir_clauses:assigns(fun translate_each/2, Left, SR), - { { match, Line, TLeft, TRight }, SL }; + { { match, ?line(Meta), TLeft, TRight }, SL }; %% Containers @@ -51,51 +51,51 @@ translate_each({ C, _, _ } = Original, S) when C == '[]'; C == '{}'; C == '<<>>' %% Blocks and scope rewriters -translate_each({ '__block__', Line, [] }, S) -> - { { atom, Line, nil }, S }; +translate_each({ '__block__', Meta, [] }, S) -> + { { atom, ?line(Meta), nil }, S }; translate_each({ '__block__', _Line, [Arg] }, S) -> translate_each(Arg, S); -translate_each({ '__block__', Line, Args }, S) when is_list(Args) -> +translate_each({ '__block__', Meta, Args }, S) when is_list(Args) -> { TArgs, NS } = translate(Args, S), - { { block, Line, TArgs }, NS }; + { { block, ?line(Meta), TArgs }, NS }; -translate_each({ '__scope__', _Line, [[{file,File}],[{do,Expr}]] }, S) -> +translate_each({ '__scope__', _Meta, [[{file,File}],[{do,Expr}]] }, S) -> Old = S#elixir_scope.file, { TExpr, TS } = translate_each(Expr, S#elixir_scope{file=File}), { TExpr, TS#elixir_scope{file=Old} }; %% Erlang op -translate_each({ '__op__', Line, [Op, Expr] }, S) when is_atom(Op) -> +translate_each({ '__op__', Meta, [Op, Expr] }, S) when is_atom(Op) -> { TExpr, NS } = translate_each(Expr, S), - { { op, Line, convert_op(Op), TExpr }, NS }; + { { op, ?line(Meta), convert_op(Op), TExpr }, NS }; -translate_each({ '__op__', Line, [Op, Left, Right] }, S) when is_atom(Op) -> +translate_each({ '__op__', Meta, [Op, Left, Right] }, S) when is_atom(Op) -> { [TLeft, TRight], NS } = translate_args([Left, Right], S), - { { op, Line, convert_op(Op), TLeft, TRight }, NS }; + { { op, ?line(Meta), convert_op(Op), TLeft, TRight }, NS }; -translate_each({ '__ambiguousop__', Line, [Var, H|T] }, S) -> +translate_each({ '__ambiguousop__', Meta, [Var, H|T] }, S) -> { Name, _, Kind } = Var, case orddict:find({ Name, Kind }, S#elixir_scope.vars) of - error -> translate_each({ Name, Line, [H|T] }, S); + error -> translate_each({ Name, Meta, [H|T] }, S); _ -> case T of [] -> translate_each(rellocate_ambiguous_op(H, Var), S); - _ -> syntax_error(Line, S#elixir_scope.file, "Many arguments given to ~s, but ~s is a variable. Use even spaces to solve ambiguity.", [Name, Name]) + _ -> syntax_error(Meta, S#elixir_scope.file, "Many arguments given to ~s, but ~s is a variable. Use even spaces to solve ambiguity.", [Name, Name]) end end; %% Lexical -translate_each({ alias, Line, [Ref] }, S) -> - translate_each({ alias, Line, [Ref,[]] }, S); +translate_each({ alias, Meta, [Ref] }, S) -> + translate_each({ alias, Meta, [Ref,[]] }, S); -translate_each({ alias, Line, [Ref, KV] }, S) -> - assert_no_assign_or_guard_scope(Line, alias, S), - validate_opts(Line, alias, [as], KV, S), +translate_each({ alias, Meta, [Ref, KV] }, S) -> + assert_no_assign_or_guard_scope(Meta, alias, S), + validate_opts(Meta, alias, [as], KV, S), { TRef, SR } = translate_each(Ref, S), @@ -110,7 +110,7 @@ translate_each({ alias, Line, [Ref, KV] }, S) -> { TOther, SA } = translate_each(Other, SR#elixir_scope{aliases=[]}), case TOther of { atom, _, Atom } -> { Atom, SA }; - _ -> syntax_error(Line, S#elixir_scope.file, + _ -> syntax_error(Meta, S#elixir_scope.file, "invalid args for alias, expected an atom or alias as argument") end end, @@ -118,28 +118,28 @@ translate_each({ alias, Line, [Ref, KV] }, S) -> %% Avoid creating aliases if first == last %% unecessarily polluting the aliases dict case New == Old of - true -> { { nil, Line }, SF }; + true -> { { nil, ?line(Meta) }, SF }; false -> case string:tokens(atom_to_list(New), "-") of [_,_] -> []; - _ -> syntax_error(Line, S#elixir_scope.file, + _ -> syntax_error(Meta, S#elixir_scope.file, "invalid args for alias, cannot create nested alias ~s", [elixir_errors:inspect(New)]) end, - { { nil, Line }, SF#elixir_scope{ + { { nil, ?line(Meta) }, SF#elixir_scope{ aliases=orddict:store(New, Old, S#elixir_scope.aliases) } } end; _ -> - syntax_error(Line, S#elixir_scope.file, "invalid args for alias, expected an atom or alias as argument") + syntax_error(Meta, S#elixir_scope.file, "invalid args for alias, expected an atom or alias as argument") end; -translate_each({ require, Line, [Ref] }, S) -> - translate_each({ require, Line, [Ref, []] }, S); +translate_each({ require, Meta, [Ref] }, S) -> + translate_each({ require, Meta, [Ref, []] }, S); -translate_each({ require, Line, [Ref, KV] }, S) -> - assert_no_assign_or_guard_scope(Line, 'require', S), - validate_opts(Line, require, [as], KV, S), +translate_each({ require, Meta, [Ref, KV] }, S) -> + assert_no_assign_or_guard_scope(Meta, 'require', S), + validate_opts(Meta, require, [as], KV, S), { TRef, SR } = translate_each(Ref, S), @@ -150,103 +150,103 @@ translate_each({ require, Line, [Ref, KV] }, S) -> case TRef of { atom, _, Old } -> - elixir_aliases:ensure_loaded(Line, Old, SR), + elixir_aliases:ensure_loaded(Meta, Old, SR), SF = SR#elixir_scope{ requires=ordsets:add_element(Old, S#elixir_scope.requires) }, - translate_each({ alias, Line, [Ref, [{ as, As }]] }, SF); + translate_each({ alias, Meta, [Ref, [{ as, As }]] }, SF); _ -> - syntax_error(Line, S#elixir_scope.file, "invalid args for require, expected an atom or alias as argument") + syntax_error(Meta, S#elixir_scope.file, "invalid args for require, expected an atom or alias as argument") end; -translate_each({ import, Line, [Left] }, S) -> - translate_each({ import, Line, [Left, []]}, S); +translate_each({ import, Meta, [Left] }, S) -> + translate_each({ import, Meta, [Left, []]}, S); -translate_each({ import, Line, [Left,Opts] }, S) when is_list(Opts) -> - translate_each({ import, Line, [default, Left, Opts]}, S); +translate_each({ import, Meta, [Left,Opts] }, S) when is_list(Opts) -> + translate_each({ import, Meta, [default, Left, Opts]}, S); -translate_each({ import, Line, [Selector, Left] }, S) -> - translate_each({ import, Line, [Selector, Left, []]}, S); +translate_each({ import, Meta, [Selector, Left] }, S) -> + translate_each({ import, Meta, [Selector, Left, []]}, S); -translate_each({ import, Line, [Left, Right, Opts] }, S) -> - assert_no_assign_or_guard_scope(Line, 'import', S), +translate_each({ import, Meta, [Left, Right, Opts] }, S) -> + assert_no_assign_or_guard_scope(Meta, 'import', S), { TSelector, SL } = translate_each(Left, S), { TRef, SR } = translate_each(Right, SL), Selector = case TSelector of { atom, _, SelectorAtom } -> SelectorAtom; - _ -> syntax_error(Line, S#elixir_scope.file, "invalid selector for import, expected an atom") + _ -> syntax_error(Meta, S#elixir_scope.file, "invalid selector for import, expected an atom") end, Ref = case TRef of { atom, _, RefAtom } -> RefAtom; - _ -> syntax_error(Line, S#elixir_scope.file, "invalid name for import, expected an atom or alias") + _ -> syntax_error(Meta, S#elixir_scope.file, "invalid name for import, expected an atom or alias") end, - validate_opts(Line, import, [as, only, except], Opts, S), + validate_opts(Meta, import, [as, only, except], Opts, S), As = case lists:keyfind(as, 1, Opts) of false -> false; { as, Value } -> Value end, - elixir_aliases:ensure_loaded(Line, Ref, SR), - SF = elixir_import:import(Line, Ref, Opts, Selector, SR), - translate_each({ require, Line, [Ref, [{ as, As }]] }, SF); + elixir_aliases:ensure_loaded(Meta, Ref, SR), + SF = elixir_import:import(Meta, Ref, Opts, Selector, SR), + translate_each({ require, Meta, [Ref, [{ as, As }]] }, SF); %% Pseudo variables -translate_each({ '__MODULE__', Line, Atom }, S) when is_atom(Atom) -> - { { atom, Line, S#elixir_scope.module }, S }; +translate_each({ '__MODULE__', Meta, Atom }, S) when is_atom(Atom) -> + { { atom, ?line(Meta), S#elixir_scope.module }, S }; -translate_each({ '__FILE__', _Line, Atom }, S) when is_atom(Atom) -> +translate_each({ '__FILE__', _Meta, Atom }, S) when is_atom(Atom) -> translate_each(S#elixir_scope.file, S); -translate_each({ '__ENV__', Line, Atom }, S) when is_atom(Atom) -> - { elixir_scope:to_erl_env({ Line, S }), S }; +translate_each({ '__ENV__', Meta, Atom }, S) when is_atom(Atom) -> + { elixir_scope:to_erl_env({ ?line(Meta), S }), S }; -translate_each({ '__CALLER__', Line, Atom }, S) when is_atom(Atom) -> - { { var, Line, '__CALLER__' }, S#elixir_scope{caller=true} }; +translate_each({ '__CALLER__', Meta, Atom }, S) when is_atom(Atom) -> + { { var, ?line(Meta), '__CALLER__' }, S#elixir_scope{caller=true} }; %% Aliases -translate_each({ '__aliases__', Line, _ } = Alias, S) -> +translate_each({ '__aliases__', Meta, _ } = Alias, S) -> case elixir_aliases:expand(Alias, S#elixir_scope.aliases) of - Atom when is_atom(Atom) -> { { atom, Line, Atom}, S }; + Atom when is_atom(Atom) -> { { atom, ?line(Meta), Atom}, S }; Aliases -> { TAliases, SA } = translate_args(Aliases, S), case lists:all(fun is_atom_tuple/1, TAliases) of true -> Atoms = [Atom || { atom, _, Atom } <- TAliases], - { { atom, Line, elixir_aliases:concat(Atoms) }, SA }; + { { atom, ?line(Meta), elixir_aliases:concat(Atoms) }, SA }; false -> - Args = [elixir_tree_helpers:build_simple_list(Line, TAliases)], - { ?ELIXIR_WRAP_CALL(Line, elixir_aliases, concat, Args), SA } + Args = [elixir_tree_helpers:build_simple_list(?line(Meta), TAliases)], + { ?wrap_call(?line(Meta), elixir_aliases, concat, Args), SA } end end; %% Quoting -translate_each({ Unquote, Line, _Args }, S) when Unquote == unquote; Unquote == unquote_splicing -> - syntax_error(Line, S#elixir_scope.file, "~p called outside quote", [Unquote]); +translate_each({ Unquote, Meta, _Args }, S) when Unquote == unquote; Unquote == unquote_splicing -> + syntax_error(Meta, S#elixir_scope.file, "~p called outside quote", [Unquote]); -translate_each({ quote, Line, [Left, Right] }, S) -> - translate_each({ quote, Line, [orddict:from_list(Left ++ Right)] }, S); +translate_each({ quote, Meta, [Left, Right] }, S) -> + translate_each({ quote, Meta, [orddict:from_list(Left ++ Right)] }, S); -translate_each({ quote, GivenLine, [T] }, S) when is_list(T) -> +translate_each({ quote, Meta, [T] }, S) when is_list(T) -> Exprs = case lists:keyfind(do, 1, T) of { do, E } -> E; false -> - syntax_error(GivenLine, S#elixir_scope.file, "invalid args for quote") + syntax_error(Meta, S#elixir_scope.file, "invalid args for quote") end, Context = case lists:keyfind(var_context, 1, T) of { var_context, VarContext } -> - expand_var_context(GivenLine, VarContext, + expand_var_context(Meta, VarContext, "invalid argument given for var_context in quote", S); false -> case lists:keyfind(hygiene, 1, T) of @@ -259,19 +259,24 @@ translate_each({ quote, GivenLine, [T] }, S) when is_list(T) -> end end, - Aliases = case lists:keyfind(expand_aliases, 1, T) of - { expand_aliases, Bool } when is_boolean(Bool) -> - Bool; - false -> - case lists:keyfind(hygiene, 1, T) of - { hygiene, Bool } when is_boolean(Bool) -> Bool; - false -> true - end + GetExpansion = fun(Key) -> + case lists:keyfind(Key, 1, T) of + { Key, Bool } when is_boolean(Bool) -> + Bool; + false -> + case lists:keyfind(hygiene, 1, T) of + { hygiene, Bool } when is_boolean(Bool) -> Bool; + false -> true + end + end end, + Aliases = GetExpansion(expand_aliases), + Imports = GetExpansion(expand_imports), + { DefaultLine, DefaultFile } = case lists:keyfind(location, 1, T) of { location, keep } -> { keep, keep }; - false -> { 0, nil } + false -> { nil, nil } end, Line = case lists:keyfind(line, 1, T) of @@ -281,6 +286,7 @@ translate_each({ quote, GivenLine, [T] }, S) when is_list(T) -> { TLine, SL } = case Line of keep -> { keep, S }; + nil -> { nil, S }; _ -> translate_each(Line, S) end, @@ -296,11 +302,11 @@ translate_each({ quote, GivenLine, [T] }, S) when is_list(T) -> TExprs = if is_binary(TFile) -> - { '__scope__', GivenLine, [[{file,TFile}],[{do,Exprs}]] }; + { '__scope__', Meta, [[{file,TFile}],[{do,Exprs}]] }; File == nil -> Exprs; true -> - syntax_error(GivenLine, S#elixir_scope.file, "invalid args for quote, expected :file to be a binary") + syntax_error(Meta, S#elixir_scope.file, "invalid args for quote, expected :file to be a binary") end, Unquote = case lists:keyfind(unquote, 1, T) of @@ -309,98 +315,98 @@ translate_each({ quote, GivenLine, [T] }, S) when is_list(T) -> end, Q = #elixir_quote{var_context=Context, line=TLine, unquote=Unquote, - expand_aliases=Aliases, expand_imports=true}, + expand_aliases=Aliases, expand_imports=Imports}, elixir_quote:quote(TExprs, Q, SL); -translate_each({ quote, GivenLine, [_] }, S) -> - syntax_error(GivenLine, S#elixir_scope.file, "invalid args for quote"); +translate_each({ quote, Meta, [_] }, S) -> + syntax_error(Meta, S#elixir_scope.file, "invalid args for quote"); -translate_each({ 'alias!', _Line, [Arg] }, S) -> +translate_each({ 'alias!', _Meta, [Arg] }, S) -> translate_each(Arg, S); -translate_each({ 'var!', Line, [Arg] }, S) -> - translate_each({ 'var!', Line, [Arg, nil] }, S); +translate_each({ 'var!', Meta, [Arg] }, S) -> + translate_each({ 'var!', Meta, [Arg, nil] }, S); -translate_each({ 'var!', Line, [{Name, _, Atom}, Kind] }, S) when is_atom(Name), is_atom(Atom) -> - Expanded = expand_var_context(Line, Kind, "invalid second argument for var!", S), - elixir_scope:translate_var(Line, Name, Expanded, S); +translate_each({ 'var!', Meta, [{Name, _, Atom}, Kind] }, S) when is_atom(Name), is_atom(Atom) -> + Expanded = expand_var_context(Meta, Kind, "invalid second argument for var!", S), + elixir_scope:translate_var(Meta, Name, Expanded, S); -translate_each({ 'var!', Line, [Name, Kind] }, S) when is_atom(Name) -> - Expanded = expand_var_context(Line, Kind, "invalid second argument for var!", S), - elixir_scope:translate_var(Line, Name, Expanded, S); +translate_each({ 'var!', Meta, [Name, Kind] }, S) when is_atom(Name) -> + Expanded = expand_var_context(Meta, Kind, "invalid second argument for var!", S), + elixir_scope:translate_var(Meta, Name, Expanded, S); -translate_each({ 'var!', Line, [_, _] }, S) -> - syntax_error(Line, S#elixir_scope.file, "invalid first argument for var!, expected a var or an atom"); +translate_each({ 'var!', Meta, [_, _] }, S) -> + syntax_error(Meta, S#elixir_scope.file, "invalid first argument for var!, expected a var or an atom"); %% Functions -translate_each({ fn, Line, [[{do, { '->', _, Pairs }}]] }, S) -> - assert_no_assign_or_guard_scope(Line, 'fn', S), - translate_fn(Line, Pairs, S); +translate_each({ fn, Meta, [[{do, { '->', _, Pairs }}]] }, S) -> + assert_no_assign_or_guard_scope(Meta, 'fn', S), + translate_fn(Meta, Pairs, S); %% Comprehensions -translate_each({ Kind, Line, Args }, S) when is_list(Args), (Kind == lc) orelse (Kind == bc) -> - translate_comprehension(Line, Kind, Args, S); +translate_each({ Kind, Meta, Args }, S) when is_list(Args), (Kind == lc) orelse (Kind == bc) -> + translate_comprehension(Meta, Kind, Args, S); %% Super (exceptionally supports partial application) -translate_each({ super, Line, Args } = Original, S) -> +translate_each({ super, Meta, Args } = Original, S) -> case elixir_partials:handle(Original, S) of error -> - assert_no_assign_or_guard_scope(Line, super, S), - Module = assert_module_scope(Line, super, S), - Function = assert_function_scope(Line, super, S), - elixir_def_overridable:ensure_defined(Line, Module, Function, S), + assert_no_assign_or_guard_scope(Meta, super, S), + Module = assert_module_scope(Meta, super, S), + Function = assert_function_scope(Meta, super, S), + elixir_def_overridable:ensure_defined(Meta, Module, Function, S), { _, Arity } = Function, { TArgs, TS } = if is_atom(Args) -> - elixir_def_overridable:retrieve_args(Line, Arity, S); + elixir_def_overridable:retrieve_args(Meta, Arity, S); length(Args) == Arity -> translate_args(Args, S); true -> - syntax_error(Line, S#elixir_scope.file, "super must be called with the same number of arguments as the current function") + syntax_error(Meta, S#elixir_scope.file, "super must be called with the same number of arguments as the current function") end, Super = elixir_def_overridable:name(Module, Function), - { { call, Line, { atom, Line, Super }, TArgs }, TS#elixir_scope{super=true} }; + { { call, ?line(Meta), { atom, ?line(Meta), Super }, TArgs }, TS#elixir_scope{super=true} }; Else -> Else end; -translate_each({ 'super?', Line, [] }, S) -> - Module = assert_module_scope(Line, 'super?', S), - Function = assert_function_scope(Line, 'super?', S), +translate_each({ 'super?', Meta, [] }, S) -> + Module = assert_module_scope(Meta, 'super?', S), + Function = assert_function_scope(Meta, 'super?', S), Bool = elixir_def_overridable:is_defined(Module, Function), - { { atom, Line, Bool }, S }; + { { atom, ?line(Meta), Bool }, S }; %% Variables -translate_each({ '^', Line, [ { Name, _, Kind } ] }, S) when is_list(Kind) -> - syntax_error(Line, S#elixir_scope.file, "cannot use ^ with expression at ^~s, ^ must be used only with variables", [Name]); +translate_each({ '^', Meta, [ { Name, _, Kind } ] }, S) when is_list(Kind) -> + syntax_error(Meta, S#elixir_scope.file, "cannot use ^ with expression at ^~s, ^ must be used only with variables", [Name]); -translate_each({ '^', Line, [ { Name, _, Kind } ] }, #elixir_scope{context=assign} = S) when is_atom(Kind) -> +translate_each({ '^', Meta, [ { Name, _, Kind } ] }, #elixir_scope{context=assign} = S) when is_atom(Kind) -> case orddict:find({ Name, Kind }, S#elixir_scope.vars) of { ok, Value } -> - { { var, Line, Value }, S }; + { { var, Meta, Value }, S }; error -> - syntax_error(Line, S#elixir_scope.file, "unbound variable ^~s", [Name]) + syntax_error(Meta, S#elixir_scope.file, "unbound variable ^~s", [Name]) end; -translate_each({ '^', Line, [ { Name, _, Kind } ] }, S) when is_atom(Kind) -> - syntax_error(Line, S#elixir_scope.file, +translate_each({ '^', Meta, [ { Name, _, Kind } ] }, S) when is_atom(Kind) -> + syntax_error(Meta, S#elixir_scope.file, "cannot access variable ^~s outside of assignment", [Name]); -translate_each({ Name, Line, Kind }, S) when is_atom(Name), is_atom(Kind) -> - elixir_scope:translate_var(Line, Name, Kind, S); +translate_each({ Name, Meta, Kind }, S) when is_atom(Name), is_atom(Kind) -> + elixir_scope:translate_var(Meta, Name, Kind, S); %% Local calls -translate_each({ Atom, Line, Args } = Original, S) when is_atom(Atom) -> +translate_each({ Atom, Meta, Args } = Original, S) when is_atom(Atom) -> case elixir_partials:is_sequential(Args) andalso - elixir_dispatch:import_function(Line, Atom, length(Args), S) of + elixir_dispatch:import_function(Meta, Atom, length(Args), S) of false -> case elixir_partials:handle(Original, S) of error -> @@ -410,13 +416,13 @@ translate_each({ Atom, Line, Args } = Original, S) when is_atom(Atom) -> Arity = length(Args), File = S#elixir_scope.file, case Arity of - 0 -> syntax_error(Line, File, "unknown variable ~s or cannot invoke local ~s/~B inside guard", [Atom, Atom, Arity]); - _ -> syntax_error(Line, File, "cannot invoke local ~s/~B inside guard", [Atom, Arity]) + 0 -> syntax_error(Meta, File, "unknown variable ~s or cannot invoke local ~s/~B inside guard", [Atom, Atom, Arity]); + _ -> syntax_error(Meta, File, "cannot invoke local ~s/~B inside guard", [Atom, Arity]) end; - _ -> translate_local(Line, Atom, Args, S) + _ -> translate_local(Meta, Atom, Args, S) end end, - elixir_dispatch:dispatch_import(Line, Atom, Args, S, Callback); + elixir_dispatch:dispatch_import(Meta, Atom, Args, S, Callback); Else -> Else end; Else -> Else @@ -424,22 +430,22 @@ translate_each({ Atom, Line, Args } = Original, S) when is_atom(Atom) -> %% Remote calls -translate_each({ { '.', _, [Left, Right] }, Line, Args } = Original, S) when is_atom(Right) -> +translate_each({ { '.', _, [Left, Right] }, Meta, Args } = Original, S) when is_atom(Right) -> { TLeft, SL } = translate_each(Left, S), Fun = (element(1, TLeft) == atom) andalso elixir_partials:is_sequential(Args) andalso - elixir_dispatch:require_function(Line, element(3, TLeft), Right, length(Args), SL), + elixir_dispatch:require_function(Meta, element(3, TLeft), Right, length(Args), SL), case Fun of false -> case elixir_partials:handle(Original, S) of error -> { TRight, SR } = translate_each(Right, umergec(S, SL)), - Callback = fun() -> translate_apply(Line, TLeft, TRight, Args, S, SL, SR) end, + Callback = fun() -> translate_apply(Meta, TLeft, TRight, Args, S, SL, SR) end, case TLeft of { atom, _, Receiver } -> - elixir_dispatch:dispatch_require(Line, Receiver, Right, Args, umergev(SL, SR), Callback); + elixir_dispatch:dispatch_require(Meta, Receiver, Right, Args, umergev(SL, SR), Callback); _ -> Callback() end; @@ -450,24 +456,24 @@ translate_each({ { '.', _, [Left, Right] }, Line, Args } = Original, S) when is_ %% Anonymous function calls -translate_each({ { '.', _, [Expr] }, Line, Args } = Original, S) -> +translate_each({ { '.', _, [Expr] }, Meta, Args } = Original, S) -> { TExpr, SE } = translate_each(Expr, S), case TExpr of { atom, _, Atom } -> - translate_each({ Atom, Line, Args }, S); + translate_each({ Atom, Meta, Args }, S); _ -> case elixir_partials:handle(Original, S) of error -> { TArgs, SA } = translate_args(Args, umergec(S, SE)), - { {call, Line, TExpr, TArgs}, umergev(SE, SA) }; + { {call, ?line(Meta), TExpr, TArgs}, umergev(SE, SA) }; Else -> Else end end; %% Invalid calls -translate_each({ Invalid, Line, _Args }, S) -> - syntax_error(Line, S#elixir_scope.file, "unexpected parenthesis after ~ts", +translate_each({ Invalid, Meta, _Args }, S) -> + syntax_error(Meta, S#elixir_scope.file, "unexpected parenthesis after ~ts", ['Elixir.Macro':to_binary(Invalid)]); %% Literals @@ -477,15 +483,17 @@ translate_each(Literal, S) -> %% Helpers -validate_opts(Line, Kind, Allowed, Opts, S) when is_list(Opts) -> +validate_opts(Meta, Kind, Allowed, Opts, S) when is_list(Opts) -> [begin - syntax_error(Line, S#elixir_scope.file, "unsupported option ~s given to ~s", [Key, Kind]) + syntax_error(Meta, S#elixir_scope.file, "unsupported option ~s given to ~s", [Key, Kind]) end || { Key, _ } <- Opts, not lists:member(Key, Allowed)]; -validate_opts(Line, Kind, _Allowed, _Opts, S) -> - syntax_error(Line, S#elixir_scope.file, "invalid options for ~s, expected a keyword list", [Kind]). +validate_opts(Meta, Kind, _Allowed, _Opts, S) -> + syntax_error(Meta, S#elixir_scope.file, "invalid options for ~s, expected a keyword list", [Kind]). + +translate_fn(Meta, Clauses, S) -> + Line = ?line(Meta), -translate_fn(Line, Clauses, S) -> Transformer = fun({ ArgsWithGuards, Expr }, Acc) -> { Args, Guards } = elixir_clauses:extract_last_guards(ArgsWithGuards), elixir_clauses:assigns_block(Line, fun elixir_translator:translate/2, Args, [Expr], Guards, umergec(S, Acc)) @@ -498,14 +506,16 @@ translate_fn(Line, Clauses, S) -> 1 -> { { 'fun', Line, { clauses, TClauses } }, umergec(S, NS) }; _ -> - syntax_error(Line, S#elixir_scope.file, "cannot mix clauses with different arities in function definition") + syntax_error(Meta, S#elixir_scope.file, "cannot mix clauses with different arities in function definition") end. -translate_local(Line, Name, Args, #elixir_scope{local=nil} = S) -> +translate_local(Meta, Name, Args, #elixir_scope{local=nil} = S) -> + Line = ?line(Meta), { TArgs, NS } = translate_args(Args, S), { { call, Line, { atom, Line, Name }, TArgs }, NS }; -translate_local(Line, Name, Args, S) -> +translate_local(Meta, Name, Args, S) -> + Line = ?line(Meta), { TArgs, NS } = translate_args(Args, S), Remote = { remote, Line, { atom, Line, S#elixir_scope.local }, @@ -513,18 +523,18 @@ translate_local(Line, Name, Args, S) -> }, { { call, Line, Remote, TArgs }, NS }. -expand_var_context(_Line, Atom, _Msg, _S) when is_atom(Atom) -> Atom; +expand_var_context(_Meta, Atom, _Msg, _S) when is_atom(Atom) -> Atom; -expand_var_context(Line, { '__aliases__', _, _ } = Alias, Msg, S) -> +expand_var_context(Meta, { '__aliases__', _, _ } = Alias, Msg, S) -> case translate_each(Alias, S) of { { atom, _, Atom }, _ } -> Atom; _ -> - syntax_error(Line, S#elixir_scope.file, "~ts, expected a compile time available alias", [Msg]) + syntax_error(Meta, S#elixir_scope.file, "~ts, expected a compile time available alias", [Msg]) end; -expand_var_context(Line, _, Msg, S) -> - syntax_error(Line, S#elixir_scope.file, "~ts, expected an atom or an alias", [Msg]). +expand_var_context(Meta, _, Msg, S) -> + syntax_error(Meta, S#elixir_scope.file, "~ts, expected an atom or an alias", [Msg]). %% Translate args @@ -557,7 +567,9 @@ translate_args(Args, S) -> %% Translate apply %% Used by both apply and external function invocation macros. -translate_apply(Line, TLeft, TRight, Args, S, SL, SR) -> +translate_apply(Meta, TLeft, TRight, Args, S, SL, SR) -> + Line = ?line(Meta), + Optimize = case (Args == []) orelse lists:last(Args) of { '|', _, _ } -> false; _ -> @@ -575,7 +587,7 @@ translate_apply(Line, TLeft, TRight, Args, S, SL, SR) -> false -> { TArgs, SA } = translate_each(Args, umergec(S, SR)), FS = umergev(SL, umergev(SR,SA)), - { ?ELIXIR_WRAP_CALL(Line, erlang, apply, [TLeft, TRight, TArgs]), FS } + { ?wrap_call(Line, erlang, apply, [TLeft, TRight, TArgs]), FS } end. %% __op__ helpers @@ -594,43 +606,43 @@ convert_op(Else) -> Else. is_atom_tuple({ atom, _, _ }) -> true; is_atom_tuple(_) -> false. -rellocate_ambiguous_op({ Op, Line, [Expr] }, Var) when Op == '+'; Op == '-' -> - { Op, Line, [Var, Expr] }; +rellocate_ambiguous_op({ Op, Meta, [Expr] }, Var) when Op == '+'; Op == '-' -> + { Op, Meta, [Var, Expr] }; -rellocate_ambiguous_op({ Call, Line, [H|T] }, Var) -> - { Call, Line, [rellocate_ambiguous_op(H, Var)|T] }. +rellocate_ambiguous_op({ Call, Meta, [H|T] }, Var) -> + { Call, Meta, [rellocate_ambiguous_op(H, Var)|T] }. %% Comprehensions -translate_comprehension(Line, Kind, Args, S) -> +translate_comprehension(Meta, Kind, Args, S) -> case elixir_tree_helpers:split_last(Args) of { Cases, [{do,Expr}] } -> - { TCases, SC } = lists:mapfoldl(fun(C, Acc) -> translate_comprehension_clause(Line, C, Acc) end, S, Cases), - { TExpr, SE } = translate_comprehension_do(Line, Kind, Expr, SC), - { { Kind, Line, TExpr, TCases }, umergec(S, SE) }; + { TCases, SC } = lists:mapfoldl(fun(C, Acc) -> translate_comprehension_clause(Meta, C, Acc) end, S, Cases), + { TExpr, SE } = translate_comprehension_do(Meta, Kind, Expr, SC), + { { Kind, ?line(Meta), TExpr, TCases }, umergec(S, SE) }; _ -> - syntax_error(Line, S#elixir_scope.file, "keyword argument :do missing for comprehension ~s", [Kind]) + syntax_error(Meta, S#elixir_scope.file, "keyword argument :do missing for comprehension ~s", [Kind]) end. -translate_comprehension_do(_Line, bc, { '<<>>', _, _ } = Expr, S) -> +translate_comprehension_do(_Meta, bc, { '<<>>', _, _ } = Expr, S) -> translate_each(Expr, S); -translate_comprehension_do(Line, bc, _Expr, S) -> - syntax_error(Line, S#elixir_scope.file, "a bit comprehension expects a bit string << >> to be returned"); +translate_comprehension_do(Meta, bc, _Expr, S) -> + syntax_error(Meta, S#elixir_scope.file, "a bit comprehension expects a bit string << >> to be returned"); -translate_comprehension_do(_Line, _Kind, Expr, S) -> +translate_comprehension_do(_Meta, _Kind, Expr, S) -> translate_each(Expr, S). -translate_comprehension_clause(_Line, {inbits, Line, [Left, Right]}, S) -> +translate_comprehension_clause(_Meta, {inbits, Meta, [Left, Right]}, S) -> { TRight, SR } = translate_each(Right, S), { TLeft, SL } = elixir_clauses:assigns(fun elixir_translator:translate_each/2, Left, SR), - { { b_generate, Line, TLeft, TRight }, SL }; + { { b_generate, ?line(Meta), TLeft, TRight }, SL }; -translate_comprehension_clause(_Line, {inlist, Line, [Left, Right]}, S) -> +translate_comprehension_clause(_Meta, {inlist, Meta, [Left, Right]}, S) -> { TRight, SR } = translate_each(Right, S), { TLeft, SL } = elixir_clauses:assigns(fun elixir_translator:translate_each/2, Left, SR), - { { generate, Line, TLeft, TRight }, SL }; + { { generate, ?line(Meta), TLeft, TRight }, SL }; -translate_comprehension_clause(Line, X, S) -> +translate_comprehension_clause(Meta, X, S) -> { TX, TS } = translate_each(X, S), - elixir_tree_helpers:convert_to_boolean(Line, TX, true, false, TS). + elixir_tree_helpers:convert_to_boolean(?line(Meta), TX, true, false, TS). diff --git a/lib/elixir/src/elixir_tree_helpers.erl b/lib/elixir/src/elixir_tree_helpers.erl index c0bf0e978e..00127e2c5d 100644 --- a/lib/elixir/src/elixir_tree_helpers.erl +++ b/lib/elixir/src/elixir_tree_helpers.erl @@ -5,11 +5,17 @@ -module(elixir_tree_helpers). -compile({parse_transform, elixir_transform}). -export([abstract_syntax/1, split_last/1, cons_to_list/1, - convert_to_boolean/5, returns_boolean/1, + convert_to_boolean/5, returns_boolean/1, get_line/1, build_list/4, build_list/5, build_simple_list/2, build_reverse_list/4, build_reverse_list/5, build_simple_reverse_list/2]). -include("elixir.hrl"). +get_line(Opts) -> + case lists:keyfind(line, 1, Opts) of + { line, Line } when is_integer(Line) -> Line; + false -> 0 + end. + split_last([]) -> { [], [] }; split_last(List) -> split_last(List, []). split_last([H], Acc) -> { lists:reverse(Acc), H }; @@ -37,14 +43,14 @@ cons_to_list({ cons, _, Left, Right }) -> build_list(Fun, Exprs, Line, S) -> build_list(Fun, Exprs, Line, S, {nil, Line}). -build_list(Fun, Exprs, Line, S, Tail) -> +build_list(Fun, Exprs, Line, S, Tail) when is_integer(Line) -> build_list_each(Fun, lists:reverse(Exprs), Line, S, Tail). % Same as build_list, but the list given is in reverse other. build_reverse_list(Fun, Exprs, Line, S) -> build_list_each(Fun, Exprs, Line, S, {nil,Line}). -build_reverse_list(Fun, Exprs, Line, S, Tail) -> +build_reverse_list(Fun, Exprs, Line, S, Tail) when is_integer(Line) -> build_list_each(Fun, Exprs, Line, S, Tail). % Builds a simple list, without translatation, just by generating the cons-cell. @@ -97,7 +103,7 @@ returns_boolean({ 'case', _, _, Clauses }) -> returns_boolean(_) -> false. -convert_to_boolean(Line, Expr, Bool, InGuard, S) -> +convert_to_boolean(Line, Expr, Bool, InGuard, S) when is_integer(Line) -> case { returns_boolean(Expr), Bool } of { true, true } -> { Expr, S }; { true, false } -> { { op, Line, 'not', Expr }, S }; diff --git a/lib/elixir/src/elixir_try.erl b/lib/elixir/src/elixir_try.erl index e7caace938..3f50a1a20f 100644 --- a/lib/elixir/src/elixir_try.erl +++ b/lib/elixir/src/elixir_try.erl @@ -4,101 +4,101 @@ -include("elixir.hrl"). -compile({parse_transform, elixir_transform}). -clauses(Line, Clauses, S) -> - Catch = elixir_clauses:get_pairs(Line, 'catch', Clauses, S), - Rescue = elixir_clauses:get_pairs(Line, rescue, Clauses, S), - Transformer = fun(X, Acc) -> each_clause(Line, X, umergec(S, Acc)) end, +clauses(Meta, Clauses, S) -> + Catch = elixir_clauses:get_pairs(Meta, 'catch', Clauses, S), + Rescue = elixir_clauses:get_pairs(Meta, rescue, Clauses, S), + Transformer = fun(X, Acc) -> each_clause(Meta, X, umergec(S, Acc)) end, lists:mapfoldl(Transformer, S, Rescue ++ Catch). -each_clause(Line, { 'catch', Raw, Expr }, S) -> +each_clause(Meta, { 'catch', Raw, Expr }, S) -> { Args, Guards } = elixir_clauses:extract_last_guards(Raw), Final = case Args of - [X] -> [throw, X, { '_', Line, nil }]; - [X,Y] -> [X, Y, { '_', Line, nil }]; + [X] -> [throw, X, { '_', Meta, nil }]; + [X,Y] -> [X, Y, { '_', Meta, nil }]; [_,_,_] -> Args; _ -> - elixir_errors:syntax_error(Line, S#elixir_scope.file, "too many arguments given for catch") + elixir_errors:syntax_error(Meta, S#elixir_scope.file, "too many arguments given for catch") end, - Condition = { '{}', Line, Final }, - elixir_clauses:assigns_block(Line, fun elixir_translator:translate_each/2, Condition, [Expr], Guards, S); + Condition = { '{}', Meta, Final }, + elixir_clauses:assigns_block(?line(Meta), fun elixir_translator:translate_each/2, Condition, [Expr], Guards, S); -each_clause(Line, { rescue, [Condition|T], Expr }, S) -> - case normalize_rescue(Line, Condition, S) of +each_clause(Meta, { rescue, [Condition|T], Expr }, S) -> + case normalize_rescue(Meta, Condition, S) of { Left, Right } -> case Left of { '_', _, _ } -> - { ClauseVar, CS } = elixir_scope:build_ex_var(Line, S), - { Clause, _ } = rescue_guards(Line, ClauseVar, Right, S), - each_clause(Line, { 'catch', [error, Clause|T], Expr }, CS); + { ClauseVar, CS } = elixir_scope:build_ex_var(?line(Meta), S), + { Clause, _ } = rescue_guards(Meta, ClauseVar, Right, S), + each_clause(Meta, { 'catch', [error, Clause|T], Expr }, CS); _ -> - { Clause, Safe } = rescue_guards(Line, Left, Right, S), + { Clause, Safe } = rescue_guards(Meta, Left, Right, S), case Safe of true -> - each_clause(Line, { 'catch', [error, Clause|T], Expr }, S); + each_clause(Meta, { 'catch', [error, Clause|T], Expr }, S); false -> - { ClauseVar, CS } = elixir_scope:build_ex_var(Line, S), - { FinalClause, _ } = rescue_guards(Line, ClauseVar, Right, S), - Match = { '=', Line, [ + { ClauseVar, CS } = elixir_scope:build_ex_var(?line(Meta), S), + { FinalClause, _ } = rescue_guards(Meta, ClauseVar, Right, S), + Match = { '=', Meta, [ Left, - { { '.', Line, ['Elixir.Exception', normalize] }, Line, [ClauseVar] } + { { '.', Meta, ['Elixir.Exception', normalize] }, Meta, [ClauseVar] } ] }, - FinalExpr = prepend_to_block(Line, Match, Expr), - each_clause(Line, { 'catch', [error, FinalClause|T], FinalExpr }, CS) + FinalExpr = prepend_to_block(Meta, Match, Expr), + each_clause(Meta, { 'catch', [error, FinalClause|T], FinalExpr }, CS) end end; _ -> - validate_rescue_access(Line, Condition, S), - each_clause(Line, { 'catch', [error, Condition|T], Expr }, S) + validate_rescue_access(Meta, Condition, S), + each_clause(Meta, { 'catch', [error, Condition|T], Expr }, S) end; -each_clause(Line, {rescue,_,_}, S) -> - elixir_errors:syntax_error(Line, S#elixir_scope.file, "too many arguments given for rescue"); +each_clause(Meta, {rescue,_,_}, S) -> + elixir_errors:syntax_error(Meta, S#elixir_scope.file, "too many arguments given for rescue"); -each_clause(Line, {Key,_,_}, S) -> - elixir_errors:syntax_error(Line, S#elixir_scope.file, "invalid key ~s in try", [Key]). +each_clause(Meta, {Key,_,_}, S) -> + elixir_errors:syntax_error(Meta, S#elixir_scope.file, "invalid key ~s in try", [Key]). %% Helpers %% rescue [Error] -> _ in [Error] -normalize_rescue(Line, List, S) when is_list(List) -> - normalize_rescue(Line, { in, Line, [{ '_', Line, nil }, List] }, S); +normalize_rescue(Meta, List, S) when is_list(List) -> + normalize_rescue(Meta, { in, Meta, [{ '_', Meta, nil }, List] }, S); %% rescue var -> var in _ -normalize_rescue(_, { Name, Line, Atom } = Rescue, S) when is_atom(Name), is_atom(Atom), Name /= '_' -> - normalize_rescue(Line, { in, Line, [Rescue, { '_', Line, nil }] }, S); +normalize_rescue(_, { Name, Meta, Atom } = Rescue, S) when is_atom(Name), is_atom(Atom), Name /= '_' -> + normalize_rescue(Meta, { in, Meta, [Rescue, { '_', Meta, nil }] }, S); %% rescue var in [Exprs] -normalize_rescue(_, { in, Line, [Left, Right] }, S) -> +normalize_rescue(_, { in, Meta, [Left, Right] }, S) -> case Right of { '_', _, _ } -> { Left, nil }; _ when is_list(Right) -> is_valid_rescue_list(Right, S) andalso { Left, Right }; _ -> - Expanded = 'Elixir.Macro':expand(Right, elixir_scope:to_ex_env({ Line, S })), + Expanded = 'Elixir.Macro':expand(Right, elixir_scope:to_ex_env({ ?line(Meta), S })), case is_valid_rescue_list(Expanded, S) of true -> { Left, Expanded }; false -> - elixir_errors:syntax_error(Line, S#elixir_scope.file, "invalid use of operator \"in\" in rescue inside try") + elixir_errors:syntax_error(Meta, S#elixir_scope.file, "invalid use of operator \"in\" in rescue inside try") end end; -normalize_rescue(_, { '=', Line, [{ '__aliases__', _, _ } = Alias, { Name, _, Atom } = Var] }, S) +normalize_rescue(_, { '=', Meta, [{ '__aliases__', _, _ } = Alias, { Name, _, Atom } = Var] }, S) when is_atom(Name) and is_atom(Atom) -> - elixir_errors:handle_file_warning(S#elixir_scope.file, { Line, ?MODULE, { rescue_no_match, Var, Alias } }), + elixir_errors:handle_file_warning(S#elixir_scope.file, { Meta, ?MODULE, { rescue_no_match, Var, Alias } }), false; -normalize_rescue(_, { '=', Line, [{ Name, _, Atom } = Var, { '__aliases__', _, _ } = Alias] }, S) +normalize_rescue(_, { '=', Meta, [{ Name, _, Atom } = Var, { '__aliases__', _, _ } = Alias] }, S) when is_atom(Name) and is_atom(Atom) -> - elixir_errors:handle_file_warning(S#elixir_scope.file, { Line, ?MODULE, { rescue_no_match, Var, Alias } }), + elixir_errors:handle_file_warning(S#elixir_scope.file, { Meta, ?MODULE, { rescue_no_match, Var, Alias } }), false; -normalize_rescue(Line, Condition, S) -> +normalize_rescue(Meta, Condition, S) -> case elixir_translator:translate_each(Condition, S#elixir_scope{context=assign}) of { { atom, _, Atom }, _ } -> - normalize_rescue(Line, { in, Line, [{ '_', Line, nil }, [Atom]] }, S); + normalize_rescue(Meta, { in, Meta, [{ '_', Meta, nil }, [Atom]] }, S); _ -> false end. @@ -106,39 +106,39 @@ normalize_rescue(Line, Condition, S) -> %% Convert rescue clauses into guards. rescue_guards(_, Var, nil, _) -> { Var, false }; -rescue_guards(Line, Var, Guards, S) -> - { RawElixir, RawErlang } = rescue_each_var(Line, Var, Guards), - { Elixir, Erlang, Safe } = rescue_each_ref(Line, Var, Guards, RawElixir, RawErlang, RawErlang == [], S), +rescue_guards(Meta, Var, Guards, S) -> + { RawElixir, RawErlang } = rescue_each_var(Meta, Var, Guards), + { Elixir, Erlang, Safe } = rescue_each_ref(Meta, Var, Guards, RawElixir, RawErlang, RawErlang == [], S), Final = case Elixir == [] of true -> Erlang; false -> - IsTuple = { is_tuple, Line, [Var] }, - IsException = { '==', Line, [ - { { '.', Line, [erlang, element] }, Line, [2, Var] }, '__exception__' + IsTuple = { is_tuple, Meta, [Var] }, + IsException = { '==', Meta, [ + { { '.', Meta, [erlang, element] }, Meta, [2, Var] }, '__exception__' ] }, - OrElse = join(Line, 'or', Elixir), - [join(Line, 'and', [IsTuple, IsException, OrElse])|Erlang] + OrElse = join(Meta, 'or', Elixir), + [join(Meta, 'and', [IsTuple, IsException, OrElse])|Erlang] end, { - { 'when', Line, [Var, reverse_join(Line, 'when', Final)] }, + { 'when', Meta, [Var, reverse_join(Meta, 'when', Final)] }, Safe }. %% Handle variables in the right side of rescue. -rescue_each_var(Line, ClauseVar, Guards) -> +rescue_each_var(Meta, ClauseVar, Guards) -> Vars = [Var || Var <- Guards, is_var(Var)], case Vars == [] of true -> { [], [] }; false -> - Elixir = [exception_compare(Line, ClauseVar, Var) || Var <- Vars], + Elixir = [exception_compare(Meta, ClauseVar, Var) || Var <- Vars], Erlang = lists:map(fun(Rescue) -> - Compares = [{ '==', Line, [Rescue, Var] } || Var <- Vars], - { 'and', Line, [ - erlang_rescue_guard_for(Line, ClauseVar, Rescue), - join(Line, 'or', Compares) + Compares = [{ '==', Meta, [Rescue, Var] } || Var <- Vars], + { 'and', Meta, [ + erlang_rescue_guard_for(Meta, ClauseVar, Rescue), + join(Meta, 'or', Compares) ] } end, erlang_rescues()), { Elixir, Erlang } @@ -149,30 +149,30 @@ rescue_each_var(Line, ClauseVar, Guards) -> %% method for optimization. %% Ignore variables -rescue_each_ref(Line, Var, [{ Name, _, Atom }|T], Elixir, Erlang, Safe, S) when is_atom(Name), is_atom(Atom) -> - rescue_each_ref(Line, Var, T, Elixir, Erlang, Safe, S); +rescue_each_ref(Meta, Var, [{ Name, _, Atom }|T], Elixir, Erlang, Safe, S) when is_atom(Name), is_atom(Atom) -> + rescue_each_ref(Meta, Var, T, Elixir, Erlang, Safe, S); -rescue_each_ref(Line, Var, [H|T], Elixir, Erlang, _Safe, S) when +rescue_each_ref(Meta, Var, [H|T], Elixir, Erlang, _Safe, S) when H == 'Elixir.UndefinedFunctionError'; H == 'Elixir.ErlangError'; H == 'Elixir.ArgumentError'; H == 'Elixir.ArithmeticError'; H == 'Elixir.BadArityError'; H == 'Elixir.BadFunctionError'; H == 'Elixir.MatchError'; H == 'Elixir.CaseClauseError'; H == 'Elixir.FunctionClauseError'; H == 'Elixir.SystemLimitError' -> - Expr = { 'or', Line, [ - erlang_rescue_guard_for(Line, Var, H), - exception_compare(Line, Var, H) + Expr = { 'or', Meta, [ + erlang_rescue_guard_for(Meta, Var, H), + exception_compare(Meta, Var, H) ] }, - rescue_each_ref(Line, Var, T, Elixir, [Expr|Erlang], false, S); + rescue_each_ref(Meta, Var, T, Elixir, [Expr|Erlang], false, S); -rescue_each_ref(Line, Var, [H|T], Elixir, Erlang, Safe, S) when is_atom(H) -> - rescue_each_ref(Line, Var, T, [exception_compare(Line, Var, H)|Elixir], Erlang, Safe, S); +rescue_each_ref(Meta, Var, [H|T], Elixir, Erlang, Safe, S) when is_atom(H) -> + rescue_each_ref(Meta, Var, T, [exception_compare(Meta, Var, H)|Elixir], Erlang, Safe, S); -rescue_each_ref(Line, Var, [H|T], Elixir, Erlang, Safe, S) -> +rescue_each_ref(Meta, Var, [H|T], Elixir, Erlang, Safe, S) -> case elixir_translator:translate_each(H, S) of { { atom, _, Atom }, _ } -> - rescue_each_ref(Line, Var, [Atom|T], Elixir, Erlang, Safe, S); + rescue_each_ref(Meta, Var, [Atom|T], Elixir, Erlang, Safe, S); _ -> - rescue_each_ref(Line, Var, T, [exception_compare(Line, Var, H)|Elixir], Erlang, Safe, S) + rescue_each_ref(Meta, Var, T, [exception_compare(Meta, Var, H)|Elixir], Erlang, Safe, S) end; rescue_each_ref(_, _, [], Elixir, Erlang, Safe, _) -> @@ -187,73 +187,73 @@ erlang_rescues() -> 'Elixir.SystemLimitError', 'Elixir.ErlangError' ]. -erlang_rescue_guard_for(Line, Var, List) when is_list(List) -> - join(Line, 'or', [erlang_rescue_guard_for(Line, Var, X) || X <- List]); +erlang_rescue_guard_for(Meta, Var, List) when is_list(List) -> + join(Meta, 'or', [erlang_rescue_guard_for(Meta, Var, X) || X <- List]); -erlang_rescue_guard_for(Line, Var, 'Elixir.UndefinedFunctionError') -> - { '==', Line, [Var, undef] }; +erlang_rescue_guard_for(Meta, Var, 'Elixir.UndefinedFunctionError') -> + { '==', Meta, [Var, undef] }; -erlang_rescue_guard_for(Line, Var, 'Elixir.FunctionClauseError') -> - { '==', Line, [Var, function_clause] }; +erlang_rescue_guard_for(Meta, Var, 'Elixir.FunctionClauseError') -> + { '==', Meta, [Var, function_clause] }; -erlang_rescue_guard_for(Line, Var, 'Elixir.SystemLimitError') -> - { '==', Line, [Var, system_limit] }; +erlang_rescue_guard_for(Meta, Var, 'Elixir.SystemLimitError') -> + { '==', Meta, [Var, system_limit] }; -erlang_rescue_guard_for(Line, Var, 'Elixir.ArithmeticError') -> - { '==', Line, [Var, badarith] }; +erlang_rescue_guard_for(Meta, Var, 'Elixir.ArithmeticError') -> + { '==', Meta, [Var, badarith] }; -erlang_rescue_guard_for(Line, Var, 'Elixir.BadArityError') -> - { 'and', Line, [ - { is_tuple, Line, [Var] }, - exception_compare(Line, Var, badarity) +erlang_rescue_guard_for(Meta, Var, 'Elixir.BadArityError') -> + { 'and', Meta, [ + { is_tuple, Meta, [Var] }, + exception_compare(Meta, Var, badarity) ] }; -erlang_rescue_guard_for(Line, Var, 'Elixir.BadFunctionError') -> - { 'and', Line, [ - { is_tuple, Line, [Var] }, - exception_compare(Line, Var, badfun) +erlang_rescue_guard_for(Meta, Var, 'Elixir.BadFunctionError') -> + { 'and', Meta, [ + { is_tuple, Meta, [Var] }, + exception_compare(Meta, Var, badfun) ] }; -erlang_rescue_guard_for(Line, Var, 'Elixir.MatchError') -> - { 'and', Line, [ - { is_tuple, Line, [Var] }, - exception_compare(Line, Var, badmatch) +erlang_rescue_guard_for(Meta, Var, 'Elixir.MatchError') -> + { 'and', Meta, [ + { is_tuple, Meta, [Var] }, + exception_compare(Meta, Var, badmatch) ] }; -erlang_rescue_guard_for(Line, Var, 'Elixir.CaseClauseError') -> - { 'and', Line, [ - { is_tuple, Line, [Var] }, - exception_compare(Line, Var, case_clause) +erlang_rescue_guard_for(Meta, Var, 'Elixir.CaseClauseError') -> + { 'and', Meta, [ + { is_tuple, Meta, [Var] }, + exception_compare(Meta, Var, case_clause) ] }; -erlang_rescue_guard_for(Line, Var, 'Elixir.ArgumentError') -> - { 'or', Line, [ - { '==', Line, [Var, badarg] }, - { 'and', Line, [ - { is_tuple, Line, [Var] }, - exception_compare(Line, Var, badarg) +erlang_rescue_guard_for(Meta, Var, 'Elixir.ArgumentError') -> + { 'or', Meta, [ + { '==', Meta, [Var, badarg] }, + { 'and', Meta, [ + { is_tuple, Meta, [Var] }, + exception_compare(Meta, Var, badarg) ] } ] }; -erlang_rescue_guard_for(Line, Var, 'Elixir.ErlangError') -> - IsNotTuple = { 'not', Line, [{ is_tuple, Line, [Var] }] }, - IsException = { '!=', Line, [ - { { '.', Line, [ erlang, element ] }, Line, [2, Var] }, '__exception__' +erlang_rescue_guard_for(Meta, Var, 'Elixir.ErlangError') -> + IsNotTuple = { 'not', Meta, [{ is_tuple, Meta, [Var] }] }, + IsException = { '!=', Meta, [ + { { '.', Meta, [ erlang, element ] }, Meta, [2, Var] }, '__exception__' ] }, - { 'or', Line, [IsNotTuple, IsException] }. + { 'or', Meta, [IsNotTuple, IsException] }. %% Validate rescue access -validate_rescue_access(Line, { '=', _, [Left, Right] }, S) -> - validate_rescue_access(Line, Left, S), - validate_rescue_access(Line, Right, S); +validate_rescue_access(Meta, { '=', _, [Left, Right] }, S) -> + validate_rescue_access(Meta, Left, S), + validate_rescue_access(Meta, Right, S); -validate_rescue_access(Line, { { '.', _, ['Elixir.Kernel', 'access'] }, _, [Element, _] }, S) -> +validate_rescue_access(Meta, { { '.', _, ['Elixir.Kernel', 'access'] }, _, [Element, _] }, S) -> case elixir_translator:translate_each(Element, S) of { { atom, _, Atom }, _ } -> case lists:member(Atom, erlang_rescues()) of false -> []; - true -> elixir_errors:syntax_error(Line, S#elixir_scope.file, "cannot (yet) pattern match against erlang exceptions") + true -> elixir_errors:syntax_error(Meta, S#elixir_scope.file, "cannot (yet) pattern match against erlang exceptions") end; _ -> [] end; @@ -278,20 +278,20 @@ is_valid_rescue_list(Right, S) when is_list(Right) -> { TRight, _ } = elixir_translator:translate(Right, S), lists:all(fun is_erl_var_or_atom/1, TRight). -exception_compare(Line, Var, Expr) -> - { '==', Line, [ - { { '.', Line, [ erlang, element ] }, Line, [1, Var] }, +exception_compare(Meta, Var, Expr) -> + { '==', Meta, [ + { { '.', Meta, [ erlang, element ] }, Meta, [1, Var] }, Expr ] }. -join(Line, Kind, [H|T]) -> - lists:foldl(fun(X, Acc) -> { Kind, Line, [Acc, X] } end, H, T). +join(Meta, Kind, [H|T]) -> + lists:foldl(fun(X, Acc) -> { Kind, Meta, [Acc, X] } end, H, T). -reverse_join(Line, Kind, [H|T]) -> - lists:foldl(fun(X, Acc) -> { Kind, Line, [X, Acc] } end, H, T). +reverse_join(Meta, Kind, [H|T]) -> + lists:foldl(fun(X, Acc) -> { Kind, Meta, [X, Acc] } end, H, T). -prepend_to_block(_Line, Expr, { '__block__', Line, Args }) -> - { '__block__', Line, [Expr|Args] }; +prepend_to_block(_Meta, Expr, { '__block__', Meta, Args }) -> + { '__block__', Meta, [Expr|Args] }; -prepend_to_block(Line, Expr, Args) -> - { '__block__', Line, [Expr, Args] }. \ No newline at end of file +prepend_to_block(Meta, Expr, Args) -> + { '__block__', Meta, [Expr, Args] }. \ No newline at end of file diff --git a/lib/elixir/test/elixir/code_test.exs b/lib/elixir/test/elixir/code_test.exs index b2334eed8e..8133c34c8c 100644 --- a/lib/elixir/test/elixir/code_test.exs +++ b/lib/elixir/test/elixir/code_test.exs @@ -62,6 +62,7 @@ defmodule CodeTest do test :string_to_ast do assert { :ok, quote line: 1, do: 1 + 2 } = Code.string_to_ast("1 + 2") + assert { :ok, quote line: 1, do: (1 + 2; 3 + 4) } = Code.string_to_ast("1 + 2; 3 + 4") assert { :error, _ } = Code.string_to_ast("a.1") end diff --git a/lib/elixir/test/elixir/kernel/doc_test.exs b/lib/elixir/test/elixir/kernel/doc_test.exs index 71ce153b8e..117c72b155 100644 --- a/lib/elixir/test/elixir/kernel/doc_test.exs +++ b/lib/elixir/test/elixir/kernel/doc_test.exs @@ -10,8 +10,8 @@ defmodule Kernel.DocTest do Code.load_file path expected = [ - {{:example,1},5,:def,[{:var,6,nil}],"Some example"}, - {{:nodoc,1},8,:def,[{:"//",8,[{:var,8,nil},0]}],nil} + {{:example,1},5,:def,[{:var,[line: 6],nil}],"Some example"}, + {{:nodoc,1},8,:def,[{:"//",[line: 8],[{:var,[line: 8],nil},0]}],nil} ] assert CompiledWithDocs.__info__(:docs) == expected diff --git a/lib/elixir/test/elixir/kernel/quote_test.exs b/lib/elixir/test/elixir/kernel/quote_test.exs index 4bfa89cceb..f1343b7ec8 100644 --- a/lib/elixir/test/elixir/kernel/quote_test.exs +++ b/lib/elixir/test/elixir/kernel/quote_test.exs @@ -21,21 +21,21 @@ defmodule Kernel.QuoteTest do test :keep_line do ## DO NOT MOVE THIS LINE - assert quote(line: :keep, do: bar(1,2,3)) == { :bar, 24, [1,2,3] } + assert quote(line: :keep, do: bar(1,2,3)) == { :bar, [line: 24], [1,2,3] } end test :fixed_line do - assert quote(line: 3, do: bar(1,2,3)) == { :bar, 3, [1,2,3] } + assert quote(line: 3, do: bar(1,2,3)) == { :bar, [line: 3], [1,2,3] } end test :keep_location do ## DO NOT MOVE THIS LINE assert quote(location: :keep, do: bar(1,2,3)) == { :__scope__, - 33, + [line: 33], [ [file: __FILE__], - [do: { :bar, 33, [1,2,3] }] + [do: { :bar, [line: 33], [1,2,3] }] ] } end @@ -43,7 +43,7 @@ defmodule Kernel.QuoteTest do test :quote_line_var do ## DO NOT MOVE THIS LINE line = __ENV__.line - assert quote(line: line, do: bar(1,2,3)) == { :bar, 45, [1,2,3] } + assert quote(line: line, do: bar(1,2,3)) == { :bar, [line: 45], [1,2,3] } end test :unquote_call do diff --git a/lib/elixir/test/elixir/keyword_test.exs b/lib/elixir/test/elixir/keyword_test.exs index 312801e9ed..a3ddb8b0c2 100644 --- a/lib/elixir/test/elixir/keyword_test.exs +++ b/lib/elixir/test/elixir/keyword_test.exs @@ -12,7 +12,7 @@ defmodule KeywordTest do test :ambiguity do # This raises a warning, so let's leave it commented # assert quote(do: [a:b]) == [a: { :b, 0, :quoted }] - assert [{ :::, 0, [{ :a, 0, _ },{ :b, 0, _ }] }] = quote(do: [a::b]) + assert [{ :::, [], [{ :a, [], _ },{ :b, [], _ }] }] = quote(do: [a::b]) end test :optional_comma do diff --git a/lib/elixir/test/elixir/macro_test.exs b/lib/elixir/test/elixir/macro_test.exs index d30f3dd4a0..71e785f9af 100644 --- a/lib/elixir/test/elixir/macro_test.exs +++ b/lib/elixir/test/elixir/macro_test.exs @@ -20,8 +20,8 @@ defmodule MacroTest do ## Escape test :escape_handle_tuples_with_size_different_than_two do - assert { :{}, 0, [:a] } == Macro.escape({ :a }) - assert { :{}, 0, [:a, :b, :c] } == Macro.escape({ :a, :b, :c }) + assert { :{}, [], [:a] } == Macro.escape({ :a }) + assert { :{}, [], [:a, :b, :c] } == Macro.escape({ :a, :b, :c }) end test :escape_simply_returns_tuples_with_size_equal_to_two do @@ -33,7 +33,7 @@ defmodule MacroTest do end test :escape_works_recursively do - assert [1,{:{}, 0, [:a,:b,:c]},3] == Macro.escape([1, { :a, :b, :c },3]) + assert [1,{:{}, [], [:a,:b,:c]},3] == Macro.escape([1, { :a, :b, :c },3]) end ## Expand aliases diff --git a/lib/elixir/test/elixir/typespec_test.exs b/lib/elixir/test/elixir/typespec_test.exs index b81d34cf2d..3dec048f15 100644 --- a/lib/elixir/test/elixir/typespec_test.exs +++ b/lib/elixir/test/elixir/typespec_test.exs @@ -297,7 +297,7 @@ defmodule Typespec.TypeTest do test "type_to_ast" do quoted = [ (quote do: @type with_ann() :: (t :: atom())), - (quote do: @type empty_tuple_type() :: {}), + (quote do: @type empty_tuple_type() :: {}), (quote do: @type imm_type_1() :: 1), (quote do: @type imm_type_2() :: :atom), (quote do: @type simple_type() :: integer()), @@ -327,7 +327,7 @@ defmodule Typespec.TypeTest do end test "type_to_ast for records" do - record_type = { { :record, :my_record }, + record_type = { { :record, :my_record }, [ { :typed_record_field, { :record_field, 0, { :atom, 0, :field1 }}, @@ -338,13 +338,25 @@ defmodule Typespec.TypeTest do ], []} assert Kernel.Typespec.type_to_ast(record_type) == - quote(hygiene: false, do: my_record() :: {:my_record, field1 :: atom(), field2 :: integer() }) + { :::,[], [ + { :my_record,[],[] }, + { :{},[], [:my_record, + { :::, [line: 0], [ + {:field1,0,nil}, + {:atom,[line: 0],[]} + ] }, + { :::, [line: 0], [ + {:field2,0,nil}, + {:integer,[line: 0],[]} + ] } + ] } + ] } end - test "type_to_ast for paren_type" do + test "type_to_ast for paren_type" do type = {:my_type, {:paren_type, 0, [{:type, 0, :integer, []}]}, []} assert Kernel.Typespec.type_to_ast(type) == - quote(hygiene: false, do: my_type() :: integer()) + { :::, [], [{:my_type,[],[]}, {:integer,[line: 0],[]}] } end test "spec_to_ast" do diff --git a/lib/elixir/test/erlang/module_test.erl b/lib/elixir/test/erlang/module_test.erl index 723d473f82..6b8e16fea3 100644 --- a/lib/elixir/test/erlang/module_test.erl +++ b/lib/elixir/test/erlang/module_test.erl @@ -35,17 +35,17 @@ dynamic_function_test() -> quote_unquote_test() -> F = fun() -> eval("defmodule Foo.Bar.Baz do\ndefmacro sum(a, b), do: quote(do: unquote(a) + unquote(b))\nend"), - {'+',0,[1,2]} = 'Elixir.Foo.Bar.Baz':'MACRO-sum'(nil, 1, 2) + {'+',[],[1,2]} = 'Elixir.Foo.Bar.Baz':'MACRO-sum'(nil, 1, 2) end, test_helper:run_and_remove(F, ['Elixir.Foo.Bar.Baz']). quote_unquote_splicing_test() -> - { { '{}', 0, [1,2,3,4,5] }, _ } = eval("x = [2,3,4]\nquote do: { 1, unquote_splicing(x), 5}"). + { { '{}', [], [1,2,3,4,5] }, _ } = eval("x = [2,3,4]\nquote do: { 1, unquote_splicing(x), 5}"). operator_macro_test() -> F = fun() -> eval("defmodule Foo.Bar.Baz do\ndefmacro :+.(a, b), do: quote(do: unquote(a) - unquote(b))\nend"), - {'-',0,[1,2]} = 'Elixir.Foo.Bar.Baz':'MACRO-+'(nil, 1, 2) + {'-',[],[1,2]} = 'Elixir.Foo.Bar.Baz':'MACRO-+'(nil, 1, 2) end, test_helper:run_and_remove(F, ['Elixir.Foo.Bar.Baz']). diff --git a/lib/elixir/test/erlang/string_test.erl b/lib/elixir/test/erlang/string_test.erl index 2943c4f28e..77c6734f71 100644 --- a/lib/elixir/test/erlang/string_test.erl +++ b/lib/elixir/test/erlang/string_test.erl @@ -19,19 +19,30 @@ extract_interpolations_with_escaped_interpolation_test() -> [<<"f#{o}o">>] = extract_interpolations("f\\#{o}o"). extract_interpolations_with_interpolation_test() -> - [<<"f">>, {'::',1,[{{'.',1,['Elixir.Binary.Chars',to_binary]},1,[o]}, { binary, _ , _ }]}, <<"o">>] = extract_interpolations("f#{:o}o"). + [<<"f">>, + {'::',[{line,1}],[{{'.',[{line,1}],['Elixir.Binary.Chars',to_binary]},[{line,1}],[o]}, { binary, _ , _ }]}, + <<"o">>] = extract_interpolations("f#{:o}o"). extract_interpolations_with_two_interpolations_test() -> - [<<"f">>, {'::',1,[{{'.',1,['Elixir.Binary.Chars',to_binary]},1,[o]}, { binary, _ , _ }]}, {'::',1,[{{'.',1,['Elixir.Binary.Chars',to_binary]},1,[o]}, { binary, _ , _ }]}, <<"o">>] = extract_interpolations("f#{:o}#{:o}o"). + [<<"f">>, + {'::',[{line,1}],[{{'.',[{line,1}],['Elixir.Binary.Chars',to_binary]},[{line,1}],[o]}, { binary, _ , _ }]}, + {'::',[{line,1}],[{{'.',[{line,1}],['Elixir.Binary.Chars',to_binary]},[{line,1}],[o]}, { binary, _ , _ }]}, + <<"o">>] = extract_interpolations("f#{:o}#{:o}o"). extract_interpolations_with_only_two_interpolations_test() -> - [{'::',1,[{{'.',1,['Elixir.Binary.Chars',to_binary]},1,[o]}, { binary, _ , _ }]}, {'::',1,[{{'.',1,['Elixir.Binary.Chars',to_binary]},1,[o]}, { binary, _ , _ }]}] = extract_interpolations("#{:o}#{:o}"). + [{'::',[{line,1}],[{{'.',[{line,1}],['Elixir.Binary.Chars',to_binary]},[{line,1}],[o]}, { binary, _ , _ }]}, + {'::',[{line,1}],[{{'.',[{line,1}],['Elixir.Binary.Chars',to_binary]},[{line,1}],[o]}, { binary, _ , _ }]}] = extract_interpolations("#{:o}#{:o}"). extract_interpolations_with_tuple_inside_interpolation_test() -> - [<<"f">>, {'::',1,[{{'.',1,['Elixir.Binary.Chars',to_binary]},1,[{'{}',1,[1]}]}, { binary, _ , _ }]}, <<"o">>] = extract_interpolations("f#{{1}}o"). + [<<"f">>, + {'::',[{line,1}],[{{'.',[{line,1}],['Elixir.Binary.Chars',to_binary]},[{line,1}],[{'{}',[{line,1}],[1]}]}, { binary, _ , _ }]}, + <<"o">>] = extract_interpolations("f#{{1}}o"). extract_interpolations_with_many_expressions_inside_interpolation_test() -> - [<<"f">>, {'::',2,[{{'.',2,['Elixir.Binary.Chars',to_binary]},2,[{'__block__',2,[1,2]}]}, { binary, _ , _ }]}, <<"o">>] = extract_interpolations("f#{1\n2}o"). + [<<"f">>, + {'::',[{line,2}],[{{'.',[{line,2}],['Elixir.Binary.Chars',to_binary]},[{line,2}],[{'__block__',[{line,2}],[1,2]}]}, + { binary, _ , _ }]}, + <<"o">>] = extract_interpolations("f#{1\n2}o"). extract_interpolations_with_string_inside_interpolation_test() -> [<<"f">>, <<"foo">>, <<"o">>] = extract_interpolations("f#{\"foo\"}o"). diff --git a/lib/elixir/test/erlang/tokenizer_test.erl b/lib/elixir/test/erlang/tokenizer_test.erl index 556cf70bd8..153a674a54 100644 --- a/lib/elixir/test/erlang/tokenizer_test.erl +++ b/lib/elixir/test/erlang/tokenizer_test.erl @@ -93,12 +93,12 @@ newline_test() -> {number,2,2}] = tokenize("1\n++2"). aliases_test() -> - [{'__aliases__',1,['Foo']}] = tokenize("Foo"), - [{'__aliases__',1,['Foo']}, + [{'aliases',1,['Foo']}] = tokenize("Foo"), + [{'aliases',1,['Foo']}, {'.',1}, - {'__aliases__',1,['Bar']}, + {'aliases',1,['Bar']}, {'.',1}, - {'__aliases__',1,['Baz']}] = tokenize("Foo.Bar.Baz"). + {'aliases',1,['Baz']}] = tokenize("Foo.Bar.Baz"). string_test() -> [{bin_string,1,[<<"foo">>]}] = tokenize("\"foo\""), diff --git a/lib/elixir/test/erlang/translator_test.erl b/lib/elixir/test/erlang/translator_test.erl index 8e1555eed7..e7e2c3a6ea 100644 --- a/lib/elixir/test/erlang/translator_test.erl +++ b/lib/elixir/test/erlang/translator_test.erl @@ -15,43 +15,43 @@ eval(Forms, Binding) -> %% Assignment Operator assignment_test() -> - {13, [{a,13}]} = eval([{'=', 1, [{a, 1, nil},13]}], []). + {13, [{a,13}]} = eval([{'=', [{line,1}], [{a, [{line,1}], nil},13]}], []). assigning_twice_test() -> - 23 = eval([{'=', 1, [{a, 1, nil},13]}, {'=', 2, [{a, 2, nil}, {'+', 2, [{a, 2, nil}, 10]}]}]). + 23 = eval([{'=', [{line,1}], [{a, [{line,1}], nil},13]}, {'=', [{line,2}], [{a, [{line,2}], nil}, {'+', [{line,2}], [{a, [{line,2}], nil}, 10]}]}]). assignment_match_test() -> - ?assertError({badmatch, 2}, eval([{'=', 1, [13, 2]}])). + ?assertError({badmatch, 2}, eval([{'=', [{line,1}], [13, 2]}])). %% Aliases single_alias_test() -> - {'Elixir.Foo', _} = eval([{'__aliases__', 1, ['Foo']}], []). + {'Elixir.Foo', _} = eval([{'__aliases__', [{line,1}], ['Foo']}], []). %% Containers tuple_test() -> - {} = eval([{'{}',1,[]}]), - {1,2,3} = eval([{'{}',1,[1,2,3]}]). + {} = eval([{'{}',[{line,1}],[]}]), + {1,2,3} = eval([{'{}',[{line,1}],[1,2,3]}]). %% Ifs if_do_test() -> - example = eval([{ 'if', 1, [true, [{do, example}]] }]). + example = eval([{ 'if', [{line,1}], [true, [{do, example}]] }]). if_do_else_test() -> - failed = eval([{ 'if', 1, [false, [{do, example},{else, failed}] ] }]). + failed = eval([{ 'if', [{line,1}], [false, [{do, example},{else, failed}] ] }]). if_vars_test() -> - {true,[{foo,1}]} = eval([{ 'if', 1, [{'=', 1, [{foo,1,nil},1]}, [{do,true},{else,false}] ] }], []). + {true,[{foo,1}]} = eval([{ 'if', [{line,1}], [{'=', [{line,1}], [{foo,[{line,1}],nil},1]}, [{do,true},{else,false}] ] }], []). %% Literals list_test() -> [] = eval([[]]), [1,2,3] = eval([[1,2,3]]), - [1,2,3,{do,foo}] = eval([[1,2,3,{'{}',1,[do,foo]}]]), - [1,2,3|4] = eval([[1,2,{'|',1,[3,4]}]]). + [1,2,3,{do,foo}] = eval([[1,2,3,{'{}',[{line,1}],[do,foo]}]]), + [1,2,3|4] = eval([[1,2,{'|',[{line,1}],[3,4]}]]). kv_test() -> {key, 1} = eval([{key, 1}]). @@ -68,54 +68,46 @@ float_test() -> %% Math Operators addition_test() -> - 3 = eval([{'+', 1, [1, 2]}]). + 3 = eval([{'+', [{line,1}], [1, 2]}]). subtraction_test() -> - 1 = eval([{'-', 1, [2, 1]}]). + 1 = eval([{'-', [{line,1}], [2, 1]}]). multiplication_test() -> - 6 = eval([{'*', 1, [2, 3]}]). + 6 = eval([{'*', [{line,1}], [2, 3]}]). division_test() -> - 1.5 = eval([{'/', 1, [3, 2]}]). + 1.5 = eval([{'/', [{line,1}], [3, 2]}]). % Unary operators plus_test() -> - 1 = eval([{'+', 1, [1]}]), - 1.2 = eval([{'+', 1, [1.2]}]), - 1.2 = eval([{'+', 1, [{'+', 1, [0.2, 1.0]}]}]). + 1 = eval([{'+', [{line,1}], [1]}]), + 1.2 = eval([{'+', [{line,1}], [1.2]}]), + 1.2 = eval([{'+', [{line,1}], [{'+', [{line,1}], [0.2, 1.0]}]}]). minus_test() -> - -1 = eval([{'-', 1, [1]}]), - -1.2 = eval([{'-', 1, [1.2]}]), - -1.2 = eval([{'-', 1, [{'+', 1, [0.2, 1.0]}]}]). + -1 = eval([{'-', [{line,1}], [1]}]), + -1.2 = eval([{'-', [{line,1}], [1.2]}]), + -1.2 = eval([{'-', [{line,1}], [{'+', [{line,1}], [0.2, 1.0]}]}]). %% Short-circuit operators andand_test() -> - true = eval([{'&&', 1, [true, true]}]), - false = eval([{'&&', 1, [true, false]}]), - false = eval([{'&&', 1, [false, true]}]), - false = eval([{'&&', 1, [false, false]}]), - false = eval([{'&&', 1, [false, {error, 1, [omg]}]}]). + true = eval([{'&&', [{line,1}], [true, true]}]), + false = eval([{'&&', [{line,1}], [true, false]}]), + false = eval([{'&&', [{line,1}], [false, true]}]), + false = eval([{'&&', [{line,1}], [false, false]}]), + false = eval([{'&&', [{line,1}], [false, {error, [{line,1}], [omg]}]}]). oror_test() -> - true = eval([{'||', 1, [true, true]}]), - true = eval([{'||', 1, [true, false]}]), - true = eval([{'||', 1, [false, true]}]), - false = eval([{'||', 1, [false, false]}]), - true = eval([{'||', 1, [true, {error, 1, [omg]}]}]). + true = eval([{'||', [{line,1}], [true, true]}]), + true = eval([{'||', [{line,1}], [true, false]}]), + true = eval([{'||', [{line,1}], [false, true]}]), + false = eval([{'||', [{line,1}], [false, false]}]), + true = eval([{'||', [{line,1}], [true, {error, [{line,1}], [omg]}]}]). %% Method calls local_call_test() -> - 42.0 = eval([{float, 1, [42]}]). - -%% Expressions - -expressions_test() -> - nil = eval([nil]), - 3 = eval([{ '__block__', 0, [1, 2, 3]}]), - 5 = eval([{'+', 1, [{'+', 1, [1, 2]}, 2]}]), - 7 = eval([{'+', 1, [{'__block__', 0, [{'+', 1, [1, 2]}, {'+', 1, [2, 3]}]}, 2]}]). + 42.0 = eval([{float, [{line,1}], [42]}]). diff --git a/lib/ex_unit/lib/ex_unit/assertions.ex b/lib/ex_unit/lib/ex_unit/assertions.ex index a5e19d4dbb..21f568e94d 100644 --- a/lib/ex_unit/lib/ex_unit/assertions.ex +++ b/lib/ex_unit/lib/ex_unit/assertions.ex @@ -61,14 +61,14 @@ defmodule ExUnit.Assertions do """ defmacro refute(expected) do - contents = translate_assertion({ :!, 0, [expected] }, fn -> + contents = translate_assertion({ :!, [], [expected] }, fn -> quote do value = unquote(expected) assert !value, "Expected #{inspect value} to be false" end end) - { :!, 0, [contents] } + { :!, [], [contents] } end ## START HELPERS