Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90ff71b0cc | ||
|
|
7dd0aabbe2 | ||
|
|
658d7c7913 | ||
|
|
1f491dfbe2 | ||
|
|
8ca4517563 | ||
|
|
4e8690cd8e | ||
|
|
8f57b4c9af | ||
|
|
19d402b7d5 | ||
|
|
cf6751a3c8 | ||
|
|
42a0719c8a | ||
|
|
4527c1e849 | ||
|
|
5557e620ff | ||
|
|
683153ef05 | ||
|
|
8a77e878ab | ||
|
|
a19a4b893d | ||
|
|
b8689da1f5 | ||
|
|
a05592fdd9 | ||
|
|
3147df88fa | ||
|
|
1946feb4e1 | ||
|
|
a0101bc056 | ||
|
|
129725a168 | ||
|
|
e93613bc05 | ||
|
|
7b51500a60 | ||
|
|
f6058afb26 | ||
|
|
67b5236874 | ||
|
|
1e18d8e08e | ||
|
|
9ebce19f36 | ||
|
|
fb82b0c679 | ||
|
|
9dc7f44049 | ||
|
|
5e2c6e14be | ||
|
|
e896e311c1 | ||
|
|
ced13c159f | ||
|
|
5fe1544ff3 | ||
|
|
96c7f68a62 | ||
|
|
d0dc9f5c2c | ||
|
|
8a130978b2 | ||
|
|
71e2252064 | ||
|
|
ddc9733cf2 | ||
|
|
9bcc4be3f5 | ||
|
|
f66206b4f3 | ||
|
|
28211b2714 | ||
|
|
93f1bb920b | ||
|
|
109c07e757 | ||
|
|
eb36c2b288 | ||
|
|
3537bbbbf4 | ||
|
|
cd51c80362 | ||
|
|
a1a576e9d1 | ||
|
|
1dc4bf0989 | ||
|
|
1812e652db | ||
|
|
7f73cb3128 | ||
|
|
0123edd8a3 | ||
|
|
87af330589 | ||
|
|
3ae634a13c | ||
|
|
de3bd8ec86 | ||
|
|
3fea472cc2 | ||
|
|
7cb18091d0 | ||
|
|
8994feb0a3 | ||
|
|
f4aed8e873 | ||
|
|
008ee62f8b | ||
|
|
d543650f70 | ||
|
|
febf5e46b6 | ||
|
|
7196543060 | ||
|
|
fade5452e1 | ||
|
|
9e46866851 | ||
|
|
1dd1f2b213 | ||
|
|
3137bfc614 | ||
|
|
4d3d61e173 | ||
|
|
2e66218f58 | ||
|
|
3a1fbde95d | ||
|
|
088978fa04 | ||
|
|
249f83e82b | ||
|
|
608f0a3dcb | ||
|
|
d8aa534e3a | ||
|
|
c2df5fa5ff | ||
|
|
fee8836366 | ||
|
|
35a43afb22 | ||
|
|
c359f212f8 | ||
|
|
0f2dd3f7d4 | ||
|
|
20942ee329 | ||
|
|
d2f76453d6 | ||
|
|
50d375e33b | ||
|
|
119d48e093 | ||
|
|
6960f856f4 | ||
|
|
eb11254369 | ||
|
|
f686b08e5e | ||
|
|
80bd148852 |
@@ -1,3 +1,30 @@
|
||||
# v0.8.1 (2013-02-17)
|
||||
|
||||
* enhancements
|
||||
* [ExUnit] Tests can now receive metadata set on setup/teardown callbacks
|
||||
* [ExUnit] Add support to ExUnit.CaseTemplate to share callbacks in between test cases
|
||||
* [IO] Add `IO.ANSI` to make it easy to write ANSI escape codes
|
||||
* [Kernel] Better support for Unicode lists
|
||||
* [Kernel] Reduce variables footprint in `case`/`receive` clauses
|
||||
* [Kernel] Disable native compilation when on_load attributes is present to work around an Erlang bug
|
||||
* [Macro] `Macro.expand` also considers macros from the current `__ENV__` module
|
||||
* [Mix] Improve support for compilation of `.erl` files
|
||||
* [Mix] Add support for compilation of `.yrl` and `.xrl` files
|
||||
* [OptionParser] Switches are now overridden by default but can be kept in order if chosen
|
||||
* [Typespec] Better error reporting for invalid typespecs
|
||||
|
||||
* bug fix
|
||||
* [Mix] Allow Mix projects to be generated with just one letter
|
||||
|
||||
* backwards incompatible changes
|
||||
* [Kernel] `before_compile` and `after_compile` callbacks now receive the environment as first argument instead of the module
|
||||
|
||||
* deprecations
|
||||
* [ExUnit] Explicitly defined test/setup/teardown functions are deprecated
|
||||
* [Kernel] Tidy up and clean `quote` API
|
||||
* [Kernel] Old `:local.(args)` syntax is deprecated
|
||||
* [Process] `Process.self` is deprecated in favor `Kernel.self`
|
||||
|
||||
# v0.8.0 (2013-01-28)
|
||||
|
||||
* enhancements
|
||||
|
||||
@@ -46,7 +46,7 @@ erlang:
|
||||
# file, then mix and then compile eex fully
|
||||
elixir: kernel unicode lib/eex/ebin/Elixir-EEx.beam mix ex_unit eex iex
|
||||
|
||||
kernel: $(KERNEL)
|
||||
kernel: $(KERNEL) VERSION
|
||||
$(KERNEL): lib/elixir/lib/*.ex lib/elixir/lib/*/*.ex
|
||||
@ if [ ! -f $(KERNEL) ]; then \
|
||||
echo "==> bootstrap (compile)"; \
|
||||
@@ -93,14 +93,14 @@ clean:
|
||||
|
||||
#==> Release tasks
|
||||
|
||||
docs:
|
||||
docs: compile
|
||||
mkdir -p ebin
|
||||
rm -rf docs
|
||||
cp -R -f lib/*/ebin/*.beam ./ebin
|
||||
bin/elixir ../exdoc/bin/exdoc
|
||||
rm -rf ebin
|
||||
|
||||
release_zip:
|
||||
release_zip: compile
|
||||
rm -rf v$(VERSION).zip
|
||||
zip -9 -r v$(VERSION).zip bin CHANGELOG.md LEGAL lib/*/ebin LICENSE README.md rel VERSION
|
||||
|
||||
@@ -109,7 +109,7 @@ release_docs: docs
|
||||
rm -rf ../elixir-lang.github.com/docs/master
|
||||
mv output ../elixir-lang.github.com/docs/master
|
||||
|
||||
release_erl:
|
||||
release_erl: compile
|
||||
@ rm -rf rel/elixir
|
||||
@ cd rel && ../rebar generate
|
||||
|
||||
|
||||
+2
-1
@@ -23,4 +23,5 @@ This document simply outlines the release process:
|
||||
## Places where version is mentioned
|
||||
|
||||
* VERSION
|
||||
* CHANGELOG
|
||||
* CHANGELOG
|
||||
* src/elixir.app.src
|
||||
+3
-2
@@ -69,10 +69,11 @@ SELF=$(readlink_f "$0")
|
||||
SCRIPT_PATH=$(dirname "$SELF")
|
||||
|
||||
if [ -f "$HOME/.elixirrc" ]; then . "$HOME/.elixirrc"; fi
|
||||
if [ "$ELIXIR_NO_CLI" != "1" ]; then ERL="$ERL -s elixir start_cli"; fi
|
||||
|
||||
if [ -f "$SCRIPT_PATH/../releases/RELEASES" ] && [ -f "$SCRIPT_PATH/erl" ]
|
||||
then
|
||||
exec "$SCRIPT_PATH"/erl -env ERL_LIBS $ERL_LIBS:"$SCRIPT_PATH/../lib" -boot elixir -noshell $ELIXIR_ERL_OPTS $ERL -s elixir start_cli -extra "$@"
|
||||
exec "$SCRIPT_PATH"/erl -env ERL_LIBS $ERL_LIBS:"$SCRIPT_PATH/../lib" -boot elixir -noshell $ELIXIR_ERL_OPTS $ERL -extra "$@"
|
||||
else
|
||||
exec erl -env ERL_LIBS $ERL_LIBS:"$SCRIPT_PATH/../lib" -noshell $ELIXIR_ERL_OPTS $ERL -s elixir start_cli -extra "$@"
|
||||
exec erl -env ERL_LIBS $ERL_LIBS:"$SCRIPT_PATH/../lib" -noshell $ELIXIR_ERL_OPTS $ERL -extra "$@"
|
||||
fi
|
||||
|
||||
+7
-5
@@ -1,9 +1,11 @@
|
||||
@echo off
|
||||
if "%*" == "" (
|
||||
goto documentation
|
||||
set argc=0
|
||||
for %%x in (%*) do set /A argc+=1
|
||||
if %argc% == 0 (
|
||||
goto documentation
|
||||
) else (
|
||||
goto run
|
||||
)
|
||||
goto run
|
||||
)
|
||||
:documentation
|
||||
echo Usage: %~nx0 [options] [.exs file] [data]
|
||||
echo.
|
||||
@@ -20,4 +22,4 @@ echo ** Options marked with (*) can be given more than once
|
||||
echo ** Options given after the .exs file or -- are passed down to the executed code
|
||||
echo ** Options can be passed to the erlang runtime using ELIXIR_ERL_OPTS
|
||||
:run
|
||||
erl -env ERL_LIBS %ERL_LIBS%;"%~dp0\..\lib" -noshell %ELIXIR_ERL_OPTS% -s elixir start_cli -extra %*
|
||||
erl -env ERL_LIBS %ERL_LIBS%;"%~dp0\..\lib" -noshell %ELIXIR_ERL_OPTS% -s elixir start_cli -extra %*
|
||||
|
||||
+7
-5
@@ -1,9 +1,11 @@
|
||||
@echo off
|
||||
if "%*" == "" (
|
||||
goto documentation
|
||||
set argc=0
|
||||
for %%x in (%*) do set /A argc+=1
|
||||
if %argc% == 0 (
|
||||
goto documentation
|
||||
) else (
|
||||
goto run
|
||||
)
|
||||
goto run
|
||||
)
|
||||
:documentation
|
||||
echo Usage: %~nx0 [elixir switches] [compiler switches] [.ex files]
|
||||
echo.
|
||||
@@ -16,4 +18,4 @@ echo ** Options marked with (*) can be given more than once
|
||||
echo ** Options given after -- are passed down to the executed code
|
||||
echo ** Options can be passed to the erlang runtime using ELIXIR_ERL_OPTS" >&2
|
||||
:run
|
||||
call "%~dp0\elixir.bat" --compile %*
|
||||
call "%~dp0\elixir.bat" --compile %*
|
||||
|
||||
@@ -11,4 +11,4 @@ readlink_f () {
|
||||
|
||||
SELF=$(readlink_f "$0")
|
||||
SCRIPT_PATH=$(dirname "$SELF")
|
||||
exec "$SCRIPT_PATH"/elixir --no-halt --erl "-user Elixir-IEx-CLI" "$@"
|
||||
ELIXIR_NO_CLI=1 exec "$SCRIPT_PATH"/elixir --no-halt --erl "-user Elixir-IEx-CLI" "$@"
|
||||
+2
-1
@@ -1 +1,2 @@
|
||||
call "%~dp0\elixir.bat" "%~dp0\mix" %*
|
||||
@echo off
|
||||
call "%~dp0\elixir.bat" "%~dp0\mix" %*
|
||||
|
||||
+1
-1
@@ -191,7 +191,7 @@ defmodule EEx do
|
||||
def function_from_quoted(module, kind, name, args, source, info) do
|
||||
args = Enum.map args, fn arg -> { arg, [], nil } end
|
||||
quote = quote do
|
||||
unquote(kind).(unquote(name).(unquote_splicing(args)), do: unquote(source))
|
||||
unquote(kind)(unquote(name)(unquote_splicing(args)), do: unquote(source))
|
||||
end
|
||||
Module.eval_quoted module, quote, [], info
|
||||
end
|
||||
|
||||
@@ -112,7 +112,7 @@ defmodule EEx.Tokenizer do
|
||||
else
|
||||
:middle_expr
|
||||
end
|
||||
error ->
|
||||
_error ->
|
||||
:middle_expr
|
||||
end
|
||||
end
|
||||
|
||||
@@ -25,17 +25,18 @@
|
||||
counter=[], %% a counter for the variables defined
|
||||
local=nil, %% the scope to evaluate local functions against
|
||||
scheduled=[], %% scheduled modules to be loaded
|
||||
file, %% the current scope filename
|
||||
macro_aliases=[], %% keep aliases defined inside a macro
|
||||
aliases, %% an orddict with aliases by new -> old names
|
||||
file, %% the current scope filename
|
||||
requires, %% a set with modules required
|
||||
macros, %% a list with macros imported by module
|
||||
functions}). %% a list with functions imported by module
|
||||
|
||||
-record(elixir_quote, {
|
||||
line=0,
|
||||
var_context=nil,
|
||||
expand_aliases=true,
|
||||
expand_imports=true,
|
||||
vars_hygiene=nil,
|
||||
aliases_hygiene=true,
|
||||
imports_hygiene=true,
|
||||
unquote=true
|
||||
}).
|
||||
|
||||
|
||||
@@ -261,8 +261,8 @@ defimpl Binary.Inspect, for: List do
|
||||
|
||||
def inspect(thing, opts) do
|
||||
cond do
|
||||
printable?(thing) ->
|
||||
escape(list_to_binary(thing), ?')
|
||||
:io_lib.printable_list(thing) ->
|
||||
escape(:unicode.characters_to_binary(thing), ?')
|
||||
Keyword.keyword?(thing) ->
|
||||
"[" <> join_keywords(thing, opts) <> "]"
|
||||
true ->
|
||||
@@ -282,19 +282,6 @@ defimpl Binary.Inspect, for: List do
|
||||
other -> other
|
||||
end
|
||||
end
|
||||
|
||||
## printable?
|
||||
|
||||
defp printable?([c|cs]) when is_integer(c) and c in 32..126 do
|
||||
printable?(cs)
|
||||
end
|
||||
|
||||
defp printable?([c|cs]) when c in [?\n, ?\r, ?\t, ?\v, ?\b, ?\f, ?\e, ?\a] do
|
||||
printable?(cs)
|
||||
end
|
||||
|
||||
defp printable?([]), do: true
|
||||
defp printable?(_), do: false
|
||||
end
|
||||
|
||||
defimpl Binary.Inspect, for: Tuple do
|
||||
|
||||
@@ -1,274 +0,0 @@
|
||||
defmodule Dict.Common do
|
||||
@moduledoc false
|
||||
|
||||
defmacro __using__(_) do
|
||||
quote do
|
||||
@behavior Dict
|
||||
|
||||
@doc """
|
||||
Creates a new empty dict.
|
||||
"""
|
||||
def new do
|
||||
IO.puts "#{inspect __MODULE__} is deprecated, please use HashDict instead"
|
||||
Exception.print_stacktrace
|
||||
empty(nil)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a new dict from a list of pairs.
|
||||
|
||||
## Examples
|
||||
|
||||
#{inspect(__MODULE__)}.new [{:b,1},{:a,2}]
|
||||
#=> [a: 1, b: 2]
|
||||
|
||||
"""
|
||||
def new(pairs) do
|
||||
IO.puts "#{inspect __MODULE__} is deprecated, please use HashDict instead"
|
||||
Exception.print_stacktrace
|
||||
|
||||
Enum.reduce pairs, new, fn { k, v }, dict ->
|
||||
put(dict, k, v)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a new dict from a list of elements with the
|
||||
help of the transformation function.
|
||||
|
||||
## Examples
|
||||
|
||||
#{inspect(__MODULE__)}.new ["a", "b"], fn x -> {x, x} end
|
||||
#=> ["a": "a", "b": "b"]
|
||||
"""
|
||||
def new(list, transform) when is_function(transform) do
|
||||
IO.puts "#{inspect __MODULE__} is deprecated, please use HashDict instead"
|
||||
Exception.print_stacktrace
|
||||
|
||||
Enum.reduce list, new(), fn i, dict ->
|
||||
{ k, v } = transform.(i)
|
||||
put(dict, k, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmodule OrdDict do
|
||||
@moduledoc """
|
||||
This module implements a dictionary type that stores items
|
||||
as a list of tuples. It is a simple wrapper around
|
||||
[Erlang's orddict module](http://www.erlang.org/doc/man/orddict.html)
|
||||
and exposed via the `Dict` module.
|
||||
|
||||
Check the `Dict` module for examples and documentation.
|
||||
"""
|
||||
|
||||
use Dict.Common
|
||||
|
||||
defmacrop dict(data) do
|
||||
quote do
|
||||
{ OrdDict, unquote(data) }
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def keys(dict(data)) do
|
||||
lc { k, _ } inlist data, do: k
|
||||
end
|
||||
|
||||
@doc false
|
||||
def values(dict(data)) do
|
||||
lc { _, v } inlist data, do: v
|
||||
end
|
||||
|
||||
@doc false
|
||||
def size(dict(data)) do
|
||||
length(data)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def has_key?(dict(data), key) do
|
||||
:orddict.is_key key, data
|
||||
end
|
||||
|
||||
@doc false
|
||||
def get(dict(data), key, default // nil) do
|
||||
case :orddict.find(key, data) do
|
||||
{:ok, value} -> value
|
||||
:error -> default
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def get!(dict(data), key) do
|
||||
case :orddict.find(key, data) do
|
||||
{:ok, value} -> value
|
||||
:error -> raise(KeyError, key: key)
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def put(dict(data), key, value) do
|
||||
dict(:orddict.store key, value, data)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def put_new(dict(data), key, value) do
|
||||
dict(:orddict.update key, fn(x) -> x end, value, data)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def delete(dict(data), key) do
|
||||
dict(:orddict.erase key, data)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def merge(d1, d2, fun // fn(_k, _v1, v2) -> v2 end)
|
||||
|
||||
def merge(dict(d1), dict(d2), fun) do
|
||||
dict(:orddict.merge fun, d1, d2)
|
||||
end
|
||||
|
||||
def merge(dict(_) = d1, d2, fun) do
|
||||
merge(d1, new(d2), fun)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def update(dict(data), key, fun) do
|
||||
dict(:orddict.update key, fun, data)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def update(dict(data), key, initial, fun) do
|
||||
dict(:orddict.update key, fun, initial, data)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def empty(_) do
|
||||
dict([])
|
||||
end
|
||||
|
||||
@doc false
|
||||
def to_list(dict(data)) do
|
||||
data
|
||||
end
|
||||
end
|
||||
|
||||
defimpl Enum.Iterator, for: OrdDict do
|
||||
def iterator({ OrdDict, data }), do: data
|
||||
def count({ OrdDict, data }), do: length(data)
|
||||
end
|
||||
|
||||
defimpl Access, for: OrdDict do
|
||||
def access(dict, key), do: OrdDict.get(dict, key, nil)
|
||||
end
|
||||
|
||||
defmodule Binary.Dict do
|
||||
@moduledoc """
|
||||
This module implements a dictionary that forces the keys to be
|
||||
converted to binaries on insertion. Currently it is implemented
|
||||
using an `OrdDict`, but this may change in the future.
|
||||
|
||||
Check the `Dict` module for examples and documentation.
|
||||
"""
|
||||
|
||||
use Dict.Common
|
||||
|
||||
defmacrop dict(data) do
|
||||
quote do
|
||||
{ Binary.Dict, unquote(data) }
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def keys(dict(data)) do
|
||||
lc { k, _ } inlist data, do: k
|
||||
end
|
||||
|
||||
@doc false
|
||||
def values(dict(data)) do
|
||||
lc { _, v } inlist data, do: v
|
||||
end
|
||||
|
||||
@doc false
|
||||
def size(dict(data)) do
|
||||
length(data)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def has_key?(dict(data), key) do
|
||||
:orddict.is_key to_binary(key), data
|
||||
end
|
||||
|
||||
@doc false
|
||||
def get(dict(data), key, default // nil) do
|
||||
case :orddict.find(to_binary(key), data) do
|
||||
{:ok, value} -> value
|
||||
:error -> default
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def get!(dict(data), key) do
|
||||
case :orddict.find(to_binary(key), data) do
|
||||
{:ok, value} -> value
|
||||
:error -> raise(KeyError, key: key)
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def put(dict(data), key, value) do
|
||||
dict(:orddict.store to_binary(key), value, data)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def put_new(dict(data), key, value) do
|
||||
dict(:orddict.update to_binary(key), fn(x) -> x end, value, data)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def delete(dict(data), key) do
|
||||
dict(:orddict.erase to_binary(key), data)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def merge(d1, d2, fun // fn(_k, _v1, v2) -> v2 end)
|
||||
|
||||
def merge(dict(d1), dict(d2), fun) do
|
||||
dict(:orddict.merge fun, d1, d2)
|
||||
end
|
||||
|
||||
def merge(dict(_) = d1, d2, fun) do
|
||||
merge(d1, new(d2), fun)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def update(dict(data), key, fun) do
|
||||
dict(:orddict.update to_binary(key), fun, data)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def update(dict(data), key, initial, fun) do
|
||||
dict(:orddict.update to_binary(key), fun, initial, data)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def empty(_) do
|
||||
dict([])
|
||||
end
|
||||
|
||||
@doc false
|
||||
def to_list(dict(data)) do
|
||||
data
|
||||
end
|
||||
end
|
||||
|
||||
defimpl Enum.Iterator, for: Binary.Dict do
|
||||
def iterator({ Binary.Dict, data }), do: data
|
||||
def count({ Binary.Dict, data }), do: length(data)
|
||||
end
|
||||
|
||||
defimpl Access, for: Binary.Dict do
|
||||
def access(dict, key), do: Binary.Dict.get(dict, key, nil)
|
||||
end
|
||||
@@ -93,7 +93,7 @@ defmodule Enum do
|
||||
|
||||
"""
|
||||
@spec all?(t) :: boolean
|
||||
@spec all?(t, (element -> boolean)) :: boolean
|
||||
@spec all?(t, (element -> as_boolean(term))) :: boolean
|
||||
|
||||
def all?(collection, fun // fn(x) -> x end)
|
||||
|
||||
@@ -130,7 +130,7 @@ defmodule Enum do
|
||||
|
||||
"""
|
||||
@spec any?(t) :: boolean
|
||||
@spec any?(t, (element -> boolean)) :: boolean
|
||||
@spec any?(t, (element -> as_boolean(term))) :: boolean
|
||||
|
||||
def any?(collection, fun // fn(x) -> x end)
|
||||
|
||||
@@ -191,7 +191,7 @@ defmodule Enum do
|
||||
@doc """
|
||||
Counts for how many items the function returns true.
|
||||
"""
|
||||
@spec count(t, (element -> boolean)) :: non_neg_integer
|
||||
@spec count(t, (element -> as_boolean(term))) :: non_neg_integer
|
||||
def count(collection, fun) when is_list(collection) do
|
||||
do_count(collection, fun)
|
||||
end
|
||||
@@ -244,7 +244,7 @@ defmodule Enum do
|
||||
Enum.drop_while [1,2,3,4,5], fn(x) -> x < 3 end
|
||||
#=> [3,4,5]
|
||||
"""
|
||||
@spec drop_while(t, (element -> boolean)) :: list
|
||||
@spec drop_while(t, (element -> as_boolean(term))) :: list
|
||||
def drop_while(collection, fun) when is_list(collection) do
|
||||
do_drop_while(collection, fun)
|
||||
end
|
||||
@@ -323,7 +323,7 @@ defmodule Enum do
|
||||
#=> [2]
|
||||
|
||||
"""
|
||||
@spec filter(t, (element -> boolean)) :: list
|
||||
@spec filter(t, (element -> as_boolean(term))) :: list
|
||||
def filter(collection, fun) when is_list(collection) do
|
||||
lc item inlist collection, fun.(item), do: item
|
||||
end
|
||||
@@ -346,7 +346,7 @@ defmodule Enum do
|
||||
#=> [4]
|
||||
|
||||
"""
|
||||
@spec filter_map(t, (element -> boolean), (element -> element)) :: list
|
||||
@spec filter_map(t, (element -> as_boolean(term)), (element -> element)) :: list
|
||||
def filter_map(collection, filter, mapper) when is_list(collection) do
|
||||
lc item inlist collection, filter.(item), do: mapper.(item)
|
||||
end
|
||||
@@ -497,7 +497,7 @@ defmodule Enum do
|
||||
def join(collection, joiner // "")
|
||||
|
||||
def join(collection, joiner) when is_list(joiner) do
|
||||
binary_to_list join(collection, list_to_binary(joiner))
|
||||
:unicode.characters_to_list join(collection, :unicode.characters_to_binary(joiner))
|
||||
end
|
||||
|
||||
def join(collection, joiner) when is_list(collection) and is_binary(joiner) do
|
||||
@@ -779,7 +779,7 @@ defmodule Enum do
|
||||
Enum.split_while [1,2,3,4], fn x -> x == 2 end
|
||||
#=> { [1], [2, 3, 4] }
|
||||
"""
|
||||
@spec split_while(t, (element -> boolean)) :: {list, list}
|
||||
@spec split_while(t, (element -> as_boolean(term))) :: {list, list}
|
||||
def split_while(collection, fun) when is_list(collection) do
|
||||
do_split_while(collection, fun, [])
|
||||
end
|
||||
@@ -838,7 +838,7 @@ defmodule Enum do
|
||||
#=> [1, 2]
|
||||
|
||||
"""
|
||||
@spec take_while(t, (element -> boolean)) :: list
|
||||
@spec take_while(t, (element -> as_boolean(term))) :: list
|
||||
def take_while(collection, fun) when is_list(collection) do
|
||||
do_take_while(collection, fun)
|
||||
end
|
||||
|
||||
@@ -96,11 +96,6 @@ defmodule Exception do
|
||||
"""
|
||||
def format_stacktrace(trace // nil)
|
||||
|
||||
def format_stacktrace(trace) when is_tuple(trace) do
|
||||
IO.puts "Exception.format_stacktrace is deprecated, please use format_entry instead"
|
||||
print_stacktrace
|
||||
end
|
||||
|
||||
def format_stacktrace(trace) do
|
||||
trace = trace || try do
|
||||
throw(:stacktrace)
|
||||
|
||||
+24
-102
@@ -62,7 +62,7 @@ defmodule File do
|
||||
|
||||
In order to write and read files, one must use the
|
||||
functions in the IO module. By default, a file is
|
||||
opened on binary mode which requires the functions
|
||||
opened in binary mode which requires the functions
|
||||
`IO.binread`, `IO.binwrite` and `IO.binreadline` to
|
||||
interact with the file. A developer may pass `:utf8`
|
||||
as an option when opening the file and then all other
|
||||
@@ -105,90 +105,6 @@ defmodule File do
|
||||
alias :filename, as: FN
|
||||
alias :filelib, as: FL
|
||||
|
||||
@doc false
|
||||
def expand_path(path) do
|
||||
IO.puts "File.expand_path is deprecated, please use Path.expand instead"
|
||||
Exception.print_stacktrace
|
||||
Path.expand(path)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def expand_path(path, relative_to) do
|
||||
IO.puts "File.expand_path is deprecated, please use Path.expand instead"
|
||||
Exception.print_stacktrace
|
||||
Path.expand(path, relative_to)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def basename(path) do
|
||||
IO.puts "File.basename is deprecated, please use Path.basename instead"
|
||||
Exception.print_stacktrace
|
||||
FN.basename(path)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def basename(path, extension) do
|
||||
IO.puts "File.basename is deprecated, please use Path.basename instead"
|
||||
Exception.print_stacktrace
|
||||
FN.basename(path, extension)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def dirname(path) do
|
||||
IO.puts "File.dirname is deprecated, please use Path.dirname instead"
|
||||
Exception.print_stacktrace
|
||||
FN.dirname(path)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def extname(path) do
|
||||
IO.puts "File.extname is deprecated, please use Path.extname instead"
|
||||
Exception.print_stacktrace
|
||||
FN.extension(path)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def rootname(path) do
|
||||
IO.puts "File.rootname is deprecated, please use Path.rootname instead"
|
||||
Exception.print_stacktrace
|
||||
FN.rootname(path)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def rootname(path, extension) do
|
||||
IO.puts "File.rootname is deprecated, please use Path.rootname instead"
|
||||
Exception.print_stacktrace
|
||||
FN.rootname(path, extension)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def join(paths) do
|
||||
IO.puts "File.join is deprecated, please use Path.join instead"
|
||||
Exception.print_stacktrace
|
||||
FN.join(paths)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def join(left, right) do
|
||||
IO.puts "File.join is deprecated, please use Path.join instead"
|
||||
Exception.print_stacktrace
|
||||
FN.join(left, right)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def split(path) do
|
||||
IO.puts "File.split is deprecated, please use Path.split instead"
|
||||
Exception.print_stacktrace
|
||||
FN.split(path)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def wildcard(glob) do
|
||||
IO.puts "File.wildcard is deprecated, please use Path.wildcard instead"
|
||||
Exception.print_stacktrace
|
||||
Path.wildcard(glob)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns true if the path is a regular file.
|
||||
|
||||
@@ -253,7 +169,7 @@ defmodule File do
|
||||
case mkdir(path) do
|
||||
:ok -> :ok
|
||||
{ :error, reason } ->
|
||||
raise File.Error, reason: reason, action: "make directory", path: to_binary(path)
|
||||
raise File.Error, reason: reason, action: "make directory", path: :unicode.characters_to_binary(path)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -278,7 +194,7 @@ defmodule File do
|
||||
case mkdir_p(path) do
|
||||
:ok -> :ok
|
||||
{ :error, reason } ->
|
||||
raise File.Error, reason: reason, action: "make directory (with -p)", path: to_binary(path)
|
||||
raise File.Error, reason: reason, action: "make directory (with -p)", path: :unicode.characters_to_binary(path)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -311,7 +227,7 @@ defmodule File do
|
||||
{ :ok, binary } ->
|
||||
binary
|
||||
{ :error, reason } ->
|
||||
raise File.Error, reason: reason, action: "read file", path: to_binary(path)
|
||||
raise File.Error, reason: reason, action: "read file", path: :unicode.characters_to_binary(path)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -346,7 +262,7 @@ defmodule File do
|
||||
case stat(path, opts) do
|
||||
{:ok, info} -> info
|
||||
{:error, reason} ->
|
||||
raise File.Error, reason: reason, action: "read file stats", path: to_binary(path)
|
||||
raise File.Error, reason: reason, action: "read file stats", path: :unicode.characters_to_binary(path)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -366,7 +282,7 @@ defmodule File do
|
||||
case write_stat(path, stat, opts) do
|
||||
:ok -> :ok
|
||||
{ :error, reason } ->
|
||||
raise File.Error, reason: reason, action: "write file stats", path: to_binary(path)
|
||||
raise File.Error, reason: reason, action: "write file stats", path: :unicode.characters_to_binary(path)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -389,7 +305,7 @@ defmodule File do
|
||||
case touch(path, time) do
|
||||
:ok -> :ok
|
||||
{ :error, reason } ->
|
||||
raise File.Error, reason: reason, action: "touch", path: to_binary(path)
|
||||
raise File.Error, reason: reason, action: "touch", path: :unicode.characters_to_binary(path)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -421,7 +337,7 @@ defmodule File do
|
||||
{ :ok, bytes_count } -> bytes_count
|
||||
{ :error, reason } ->
|
||||
raise File.CopyError, reason: reason, action: "copy",
|
||||
source: to_binary(source), destination: to_binary(destination)
|
||||
source: :unicode.characters_to_binary(source), destination: :unicode.characters_to_binary(destination)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -472,7 +388,7 @@ defmodule File do
|
||||
:ok -> :ok
|
||||
{ :error, reason } ->
|
||||
raise File.CopyError, reason: reason, action: "copy recursively",
|
||||
source: to_binary(source), destination: to_binary(destination)
|
||||
source: :unicode.characters_to_binary(source), destination: :unicode.characters_to_binary(destination)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -546,7 +462,7 @@ defmodule File do
|
||||
{ :ok, files } -> files
|
||||
{ :error, reason } ->
|
||||
raise File.CopyError, reason: reason, action: "copy recursively",
|
||||
source: to_binary(source), destination: to_binary(destination)
|
||||
source: :unicode.characters_to_binary(source), destination: :unicode.characters_to_binary(destination)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -651,7 +567,7 @@ defmodule File do
|
||||
case F.write_file(path, content, modes) do
|
||||
:ok -> :ok
|
||||
{ :error, reason } ->
|
||||
raise File.Error, reason: reason, action: "write to file", path: to_binary(path)
|
||||
raise File.Error, reason: reason, action: "write to file", path: :unicode.characters_to_binary(path)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -688,7 +604,7 @@ defmodule File do
|
||||
case rm(path) do
|
||||
:ok -> :ok
|
||||
{ :error, reason } ->
|
||||
raise File.Error, reason: reason, action: "remove file", path: to_binary(path)
|
||||
raise File.Error, reason: reason, action: "remove file", path: :unicode.characters_to_binary(path)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -716,7 +632,7 @@ defmodule File do
|
||||
case rmdir(path) do
|
||||
:ok -> :ok
|
||||
{ :error, reason } ->
|
||||
raise File.Error, reason: reason, action: "remove directory", path: to_binary(path)
|
||||
raise File.Error, reason: reason, action: "remove directory", path: :unicode.characters_to_binary(path)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -789,14 +705,20 @@ defmodule File do
|
||||
case rm_rf(path) do
|
||||
{ :ok, files } -> files
|
||||
{ :error, reason } ->
|
||||
raise File.Error, reason: reason, action: "remove files and directories recursively from", path: to_binary(path)
|
||||
raise File.Error, reason: reason, action: "remove files and directories recursively from", path: :unicode.characters_to_binary(path)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Opens the given `path` according to the given list of modes.
|
||||
|
||||
By default, the file is opened in read mode, as a binary with utf8 encoding.
|
||||
In order to write and read files, one must use the functions
|
||||
in the IO module. By default, a file is opened in binary mode
|
||||
which requires the functions `IO.binread`, `IO.binwrite` and
|
||||
`IO.binreadline` to interact with the file. A developer may pass
|
||||
`:utf8` as an option when opening the file and then all other
|
||||
functions from IO are available, since they work directly with
|
||||
Unicode data.
|
||||
|
||||
The allowed modes:
|
||||
|
||||
@@ -898,7 +820,7 @@ defmodule File do
|
||||
case open(path, modes) do
|
||||
{ :ok, device } -> device
|
||||
{ :error, reason } ->
|
||||
raise File.Error, reason: reason, action: "open", path: to_binary(path)
|
||||
raise File.Error, reason: reason, action: "open", path: :unicode.characters_to_binary(path)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -910,7 +832,7 @@ defmodule File do
|
||||
case open(path, modes, function) do
|
||||
{ :ok, device } -> device
|
||||
{ :error, reason } ->
|
||||
raise File.Error, reason: reason, action: "open", path: to_binary(path)
|
||||
raise File.Error, reason: reason, action: "open", path: :unicode.characters_to_binary(path)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -953,7 +875,7 @@ defmodule File do
|
||||
case F.set_cwd(path) do
|
||||
:ok -> :ok
|
||||
{ :error, reason } ->
|
||||
raise File.Error, reason: reason, action: "set current working directory to", path: to_binary(path)
|
||||
raise File.Error, reason: reason, action: "set current working directory to", path: :unicode.characters_to_binary(path)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ defmodule HashDict do
|
||||
contract_on: @contract_load,
|
||||
root: @node_template
|
||||
|
||||
|
||||
import Bitwise
|
||||
|
||||
# Let's inline common instructions
|
||||
@@ -62,6 +63,7 @@ defmodule HashDict do
|
||||
@doc """
|
||||
Creates a new empty dict.
|
||||
"""
|
||||
@spec new :: Dict.t
|
||||
def new do
|
||||
ordered()
|
||||
end
|
||||
@@ -75,6 +77,7 @@ defmodule HashDict do
|
||||
#=> HashDict[a: 1, b: 2]
|
||||
|
||||
"""
|
||||
@spec new(list({key :: term, value :: term})) :: Dict.t
|
||||
def new(pairs) do
|
||||
Enum.reduce pairs, ordered(), fn { k, v }, dict ->
|
||||
put(dict, k, v)
|
||||
@@ -91,6 +94,7 @@ defmodule HashDict do
|
||||
#=> HashDict[{ "a", "a" }, { "b", "b" }]
|
||||
|
||||
"""
|
||||
@spec new(list, (term -> {key :: term, value ::term})) :: Dict.t
|
||||
def new(list, transform) when is_function(transform) do
|
||||
Enum.reduce list, new(), fn i, dict ->
|
||||
{ k, v } = transform.(i)
|
||||
@@ -123,7 +127,7 @@ defmodule HashDict do
|
||||
case dict_put(dict, key, { :update, nil, fun }) do
|
||||
{ dict, 0 } ->
|
||||
dict
|
||||
{ dict, 1 } ->
|
||||
{ _dict, 1 } ->
|
||||
raise KeyError, key: key
|
||||
end
|
||||
end
|
||||
@@ -184,7 +188,7 @@ defmodule HashDict do
|
||||
{ _, 0 } ->
|
||||
dict
|
||||
{ root, -1 } ->
|
||||
res = if depth > 0 and trie(dict, :contract_on) == size do
|
||||
if depth > 0 and trie(dict, :contract_on) == size do
|
||||
root = node_contract(root, depth, depth - 1)
|
||||
trie(dict,
|
||||
root: root,
|
||||
|
||||
+25
-11
@@ -23,6 +23,8 @@ defmodule IO do
|
||||
|
||||
"""
|
||||
|
||||
import :erlang, only: [group_leader: 0]
|
||||
|
||||
@doc """
|
||||
Reads `count` bytes from the IO device. It returns:
|
||||
|
||||
@@ -34,7 +36,7 @@ defmodule IO do
|
||||
for instance {:error, :estale} if reading from an
|
||||
NFS file system.
|
||||
"""
|
||||
def read(device // :stdio, count) do
|
||||
def read(device // group_leader(), count) do
|
||||
:io.get_chars(map_dev(device), "", count)
|
||||
end
|
||||
|
||||
@@ -44,7 +46,7 @@ defmodule IO do
|
||||
|
||||
Check `read/2` for more information.
|
||||
"""
|
||||
def binread(device // :stdio, count) do
|
||||
def binread(device // group_leader(), count) do
|
||||
case :file.read(map_dev(device), count) do
|
||||
{ :ok, data } -> data
|
||||
other -> other
|
||||
@@ -65,7 +67,7 @@ defmodule IO do
|
||||
This function does the same as `gets/2`,
|
||||
except the prompt is not required as argument.
|
||||
"""
|
||||
def readline(device // :stdio) do
|
||||
def readline(device // group_leader()) do
|
||||
:io.get_line(map_dev(device), "")
|
||||
end
|
||||
|
||||
@@ -75,7 +77,7 @@ defmodule IO do
|
||||
|
||||
Check `readline/1` for more information.
|
||||
"""
|
||||
def binreadline(device // :stdio) do
|
||||
def binreadline(device // group_leader()) do
|
||||
case :file.read_line(map_dev(device)) do
|
||||
{ :ok, data } -> data
|
||||
other -> other
|
||||
@@ -99,7 +101,7 @@ defmodule IO do
|
||||
#=> "error"
|
||||
|
||||
"""
|
||||
def write(device // :stdio, item) do
|
||||
def write(device // group_leader(), item) do
|
||||
:io.put_chars map_dev(device), to_iodata(item)
|
||||
end
|
||||
|
||||
@@ -109,7 +111,7 @@ defmodule IO do
|
||||
|
||||
Check `write/2` for more information.
|
||||
"""
|
||||
def binwrite(device // :stdio, item) do
|
||||
def binwrite(device // group_leader(), item) do
|
||||
:file.write map_dev(device), to_iodata(item)
|
||||
end
|
||||
|
||||
@@ -118,16 +120,28 @@ defmodule IO do
|
||||
but adds a new line at the end. The argument is expected
|
||||
to be a chardata.
|
||||
"""
|
||||
def puts(device // :stdio, item) do
|
||||
def puts(device // group_leader(), item) do
|
||||
erl_dev = map_dev(device)
|
||||
:io.put_chars erl_dev, [to_iodata(item), ?\n]
|
||||
end
|
||||
|
||||
@doc """
|
||||
Inspects and writes the given argument to the device
|
||||
followed by a new line. Returns the item given.
|
||||
followed by a new line. A set of options can be given.
|
||||
|
||||
## Examples
|
||||
|
||||
IO.inspect Process.list
|
||||
|
||||
"""
|
||||
def inspect(device // :stdio, item, opts // []) do
|
||||
def inspect(item, opts // []) do
|
||||
inspect group_leader(), item, opts
|
||||
end
|
||||
|
||||
@doc """
|
||||
Inspects the item with options using the given device.
|
||||
"""
|
||||
def inspect(device, item, opts) do
|
||||
puts device, Binary.Inspect.inspect(item, opts)
|
||||
item
|
||||
end
|
||||
@@ -143,7 +157,7 @@ defmodule IO do
|
||||
for instance {:error, :estale} if reading from an
|
||||
NFS file system.
|
||||
"""
|
||||
def getb(device // :stdio, prompt, count // 1) do
|
||||
def getb(device // group_leader(), prompt, count // 1) do
|
||||
:io.get_chars(map_dev(device), to_iodata(prompt), count)
|
||||
end
|
||||
|
||||
@@ -159,7 +173,7 @@ defmodule IO do
|
||||
for instance {:error, :estale} if reading from an
|
||||
NFS file system.
|
||||
"""
|
||||
def gets(device // :stdio, prompt) do
|
||||
def gets(device // group_leader(), prompt) do
|
||||
:io.get_line(map_dev(device), to_iodata(prompt))
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
defmodule IO.ANSI.Sequence do
|
||||
@moduledoc false
|
||||
|
||||
defmacro defsequence(name, code) do
|
||||
binary_name = atom_to_binary(name)
|
||||
|
||||
quote do
|
||||
def unquote(name)() do
|
||||
"\e[#{unquote(code)}m"
|
||||
end
|
||||
|
||||
defp escape_sequence(<< unquote(binary_name), rest :: binary >>) do
|
||||
{ "\e[#{unquote(code)}m", rest }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmodule IO.ANSI do
|
||||
@moduledoc """
|
||||
This module provides functionality to render ANSI escape sequences
|
||||
(http://en.wikipedia.org/wiki/ANSI_escape_code) — characters embedded
|
||||
in the text used to control formatting, color, and other output options
|
||||
on video text terminals.
|
||||
|
||||
Please be aware that in Erlang/OTP versions prior to R16, you will not
|
||||
be able to render ANSI escape sequences in iex or erlang shell
|
||||
"""
|
||||
|
||||
import IO.ANSI.Sequence
|
||||
|
||||
@doc """
|
||||
Checks whether the default I/O device is a terminal or a file.
|
||||
|
||||
Used to identify whether printing ANSI escape sequences will likely
|
||||
be printed as intended.
|
||||
|
||||
Please note that invoked while in shell (iex) in Erlang/OTP
|
||||
prior to R16, terminal?/0 will always return false because
|
||||
Erlang shell did not support ANSI escape sequences up until
|
||||
R16.
|
||||
"""
|
||||
@spec terminal? :: boolean
|
||||
@spec terminal?(:io.device) :: boolean
|
||||
def terminal?(device // :erlang.group_leader) do
|
||||
if :erlang.system_info(:otp_release) < 'R16' and
|
||||
Process.whereis(:user) != device do
|
||||
# Shell prior to R16 doesn't support ANSI escape
|
||||
# sequences
|
||||
false
|
||||
else
|
||||
match?({:ok, _}, :io.columns(device))
|
||||
end
|
||||
end
|
||||
|
||||
@doc "Resets all attributes"
|
||||
defsequence :reset, 0
|
||||
|
||||
@doc "Bright (increased intensity) or Bold"
|
||||
defsequence :bright, 1
|
||||
|
||||
@doc "Faint (decreased intensity), not widely supported"
|
||||
defsequence :faint, 2
|
||||
|
||||
@doc "Italic: on. Not widely supported. Sometimes treated as inverse."
|
||||
defsequence :italic, 3
|
||||
|
||||
@doc "Underline: Single"
|
||||
defsequence :underline, 4
|
||||
|
||||
@doc "Blink: Slow. Less than 150 per minute"
|
||||
defsequence :blink_slow, 5
|
||||
|
||||
@doc "Blink: Rapid. MS-DOS ANSI.SYS; 150 per minute or more; not widely supported"
|
||||
defsequence :blink_rapid, 6
|
||||
|
||||
@doc "Image: Negative. Swap foreground and background"
|
||||
defsequence :inverse, 7
|
||||
|
||||
@doc "Image: Negative. Swap foreground and background"
|
||||
defsequence :reverse, 7
|
||||
|
||||
@doc "Conceal. Not widely supported"
|
||||
defsequence :conceal, 8
|
||||
|
||||
@doc "Crossed-out. Characters legible, but marked for deletion. Not widely supported."
|
||||
defsequence :crossed_out, 9
|
||||
|
||||
@doc "Sets primary (default) font"
|
||||
defsequence :primary_font, 10
|
||||
|
||||
lc font_n inlist [1,2,3,4,5,6,7,8,9] do
|
||||
Module.eval_quoted __ENV__, (quote do
|
||||
@doc "Sets alternative font #{unquote(font_n)}"
|
||||
defsequence(unquote(:"font_#{font_n}"), unquote(font_n + 10))
|
||||
end)
|
||||
end
|
||||
|
||||
@doc "Normal color or intensity"
|
||||
defsequence :normal, 22
|
||||
|
||||
@doc "Not italic"
|
||||
defsequence :not_italic, 23
|
||||
|
||||
@doc "Underline: None"
|
||||
defsequence :no_underline, 24
|
||||
|
||||
@doc "Blink: off"
|
||||
defsequence :blink_off, 25
|
||||
|
||||
colors = [:black, :red, :green, :yellow, :blue, :magenta, :cyan, :white]
|
||||
colors = Enum.zip(0..(length(colors)-1), colors)
|
||||
|
||||
lc { code, color } inlist colors do
|
||||
Module.eval_quoted __ENV__, (quote do
|
||||
@doc "Sets foreground color to #{unquote(color)}"
|
||||
defsequence unquote(color), unquote(code + 30)
|
||||
|
||||
@doc "Sets background color to #{unquote(color)}"
|
||||
defsequence unquote(:"#{color}_background"), unquote(code + 40)
|
||||
end)
|
||||
end
|
||||
|
||||
@doc "Default text color"
|
||||
defsequence :default_color, 39
|
||||
|
||||
@doc "Default background color"
|
||||
defsequence :default_background, 49
|
||||
|
||||
@doc "Framed"
|
||||
defsequence :framed, 51
|
||||
|
||||
@doc "Encircled"
|
||||
defsequence :encircled, 52
|
||||
|
||||
@doc "Overlined"
|
||||
defsequence :overlined, 53
|
||||
|
||||
@doc "Not framed or encircled"
|
||||
defsequence :not_framed_encircled, 54
|
||||
|
||||
@doc "Not overlined"
|
||||
defsequence :not_overlined, 55
|
||||
|
||||
# Catch spaces between codes
|
||||
defp escape_sequence(<< ?\s, rest :: binary >>) do
|
||||
escape_sequence(rest)
|
||||
end
|
||||
|
||||
defp escape_sequence(other) do
|
||||
[spec|_] = String.split(other, %r/(,|\})/)
|
||||
raise ArgumentError, message: "invalid ANSI sequence specification: #{spec}"
|
||||
end
|
||||
|
||||
@doc %B"""
|
||||
Escapes a string coverting named ANSI sequences into actual ANSI codes.
|
||||
|
||||
The format for referring sequences is `%{red}` and `%{red,bright}` (for
|
||||
multiple sequences)
|
||||
|
||||
It will also force a %{reset} to get appended to every string. If you don't
|
||||
want this behaviour, use `escape_fragment/1` and `escape_fragment/2`.
|
||||
|
||||
An optional boolean parameter can be passed to enable or disable
|
||||
emitting actual ANSI codes. When false, no ANSI codes will emitted.
|
||||
By default, standard output will be checked if it is a terminal capable
|
||||
of handling these sequences (using `terminal?/0` function)
|
||||
|
||||
## Example
|
||||
|
||||
IO.ANSI.escape "Hello %{red,bright,green}yes" #=>
|
||||
"Hello \e[31m\e[1m\e[32myes\e[0m"
|
||||
"""
|
||||
@spec escape(String.t, emit :: boolean) :: String.t
|
||||
def escape(string, emit // terminal?) do
|
||||
{rendered, emitted} = do_escape(string, false, emit, false, [])
|
||||
if emitted and emit do
|
||||
rendered <> reset
|
||||
else
|
||||
rendered
|
||||
end
|
||||
end
|
||||
|
||||
@doc %B"""
|
||||
Escapes a string coverting named ANSI sequences into actual ANSI codes.
|
||||
|
||||
The format for referring sequences is `%{red}` and `%{red,bright}` (for
|
||||
multiple sequences)
|
||||
|
||||
An optional boolean parameter can be passed to enable or disable
|
||||
emitting actual ANSI codes. When false, no ANSI codes will emitted.
|
||||
By default, standard output will be checked if it is a terminal capable
|
||||
of handling these sequences (using `terminal?/0` function)
|
||||
|
||||
## Example
|
||||
|
||||
IO.ANSI.escape "Hello %{red,bright,green}yes%{reset}" #=>
|
||||
"Hello \e[31m\e[1m\e[32myes\e[0m"
|
||||
|
||||
"""
|
||||
@spec escape_fragment(String.t, emit :: boolean) :: String.t
|
||||
def escape_fragment(string, emit // terminal?) do
|
||||
{rendered, _emitted} = do_escape(string, false, emit, false, [])
|
||||
rendered
|
||||
end
|
||||
|
||||
defp do_escape(<< ?%, ?{, rest :: binary >>, false, emit, _emitted, acc) do
|
||||
do_escape_sequence(rest, emit, acc)
|
||||
end
|
||||
defp do_escape(<< ?,, rest :: binary >>, true, emit, _emitted, acc) do
|
||||
do_escape_sequence(rest, emit, acc)
|
||||
end
|
||||
defp do_escape(<< ?\s, rest :: binary >>, true, emit, emitted, acc) do
|
||||
do_escape(rest, true, emit, emitted, acc)
|
||||
end
|
||||
defp do_escape(<< ?}, rest :: binary >>, true, emit, emitted, acc) do
|
||||
do_escape(rest, false, emit, emitted, acc)
|
||||
end
|
||||
defp do_escape(<< x :: [binary, size(1)], rest :: binary>>, false, emit, emitted, acc) do
|
||||
do_escape(rest, false, emit, emitted, [x|acc])
|
||||
end
|
||||
defp do_escape("", false, _emit, emitted, acc) do
|
||||
{list_to_binary(Enum.reverse(acc)), emitted}
|
||||
end
|
||||
|
||||
defp do_escape_sequence(rest, emit, acc) do
|
||||
{code, rest} = escape_sequence(rest)
|
||||
if emit do
|
||||
acc = [code|acc]
|
||||
end
|
||||
do_escape(rest, true, emit, true, acc)
|
||||
end
|
||||
end
|
||||
+63
-74
@@ -69,7 +69,7 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
process = Process.self
|
||||
process = Kernel.self
|
||||
process <- { :ok, "Sending myself a message" }
|
||||
|
||||
"""
|
||||
@@ -105,7 +105,7 @@ defmodule Kernel do
|
||||
defmacro left -- right
|
||||
|
||||
@doc """
|
||||
Boolean or. Arguments needs to necessarily be booleans.
|
||||
Boolean or. Arguments must be booleans.
|
||||
Allowed in guard clauses.
|
||||
|
||||
## Examples
|
||||
@@ -117,7 +117,7 @@ defmodule Kernel do
|
||||
defmacro left or right
|
||||
|
||||
@doc """
|
||||
Boolean and. Arguments needs to necessarily be booleans.
|
||||
Boolean and. Arguments must be booleans.
|
||||
Allowed in guard clauses.
|
||||
|
||||
## Examples
|
||||
@@ -129,7 +129,7 @@ defmodule Kernel do
|
||||
defmacro left and right
|
||||
|
||||
@doc """
|
||||
Boolean xor. Arguments needs to necessarily be booleans.
|
||||
Boolean xor. Arguments must be booleans.
|
||||
Allowed in guard clauses.
|
||||
|
||||
## Examples
|
||||
@@ -141,7 +141,7 @@ defmodule Kernel do
|
||||
defmacro left xor right
|
||||
|
||||
@doc """
|
||||
Boolean not. Argument needs to necessarily be a boolean.
|
||||
Boolean not. Argument must be a boolean.
|
||||
Allowed in guard clauses.
|
||||
|
||||
## Examples
|
||||
@@ -153,7 +153,7 @@ defmodule Kernel do
|
||||
defmacro not arg
|
||||
|
||||
@doc """
|
||||
It receives any argument and returns true if it is false
|
||||
Receives any argument and returns true if it is false
|
||||
or nil. Returns false otherwise. Not allowed in guard
|
||||
clauses.
|
||||
|
||||
@@ -168,8 +168,8 @@ defmodule Kernel do
|
||||
defmacro !arg
|
||||
|
||||
@doc """
|
||||
Return true if left is less than right.
|
||||
As Erlang, Elixir can compare any term. Allowed in guard clauses.
|
||||
Returns true if left is less than right.
|
||||
Like Erlang, Elixir can compare any term. Allowed in guard clauses.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -180,8 +180,8 @@ defmodule Kernel do
|
||||
defmacro left < right
|
||||
|
||||
@doc """
|
||||
Return true if left is more than right.
|
||||
As Erlang, Elixir can compare any term. Allowed in guard clauses.
|
||||
Returns true if left is more than right.
|
||||
Like Erlang, Elixir can compare any term. Allowed in guard clauses.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -192,8 +192,8 @@ defmodule Kernel do
|
||||
defmacro left > right
|
||||
|
||||
@doc """
|
||||
Return true if left is less than or equal to right.
|
||||
As Erlang, Elixir can compare any term. Allowed in guard clauses.
|
||||
Returns true if left is less than or equal to right.
|
||||
Like Erlang, Elixir can compare any term. Allowed in guard clauses.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -204,8 +204,8 @@ defmodule Kernel do
|
||||
defmacro left <= right
|
||||
|
||||
@doc """
|
||||
Return true if left is more than or equal to right.
|
||||
As Erlang, Elixir can compare any term. Allowed in guard clauses.
|
||||
Returns true if left is more than or equal to right.
|
||||
Like Erlang, Elixir can compare any term. Allowed in guard clauses.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -221,7 +221,7 @@ defmodule Kernel do
|
||||
This operator considers 1 and 1.0 to be equal. For strict
|
||||
comparison, use `===` instead.
|
||||
|
||||
As Erlang, Elixir can compare any term. Allowed in guard clauses.
|
||||
Like Erlang, Elixir can compare any term. Allowed in guard clauses.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -240,7 +240,7 @@ defmodule Kernel do
|
||||
This operator considers 1 and 1.0 to be equal. For strict
|
||||
comparison, use `!==` instead.
|
||||
|
||||
As Erlang, Elixir can compare any term. Allowed in guard clauses.
|
||||
Like Erlang, Elixir can compare any term. Allowed in guard clauses.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -254,7 +254,7 @@ defmodule Kernel do
|
||||
|
||||
@doc """
|
||||
Returns true if the two items are strictly equal.
|
||||
As Erlang, Elixir can compare any term. Allowed in guard clauses.
|
||||
Like Erlang, Elixir can compare any term. Allowed in guard clauses.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -269,7 +269,7 @@ defmodule Kernel do
|
||||
|
||||
@doc """
|
||||
Returns true if the two items are strictly not equal.
|
||||
As Erlang, Elixir can compare any term. Allowed in guard clauses.
|
||||
Like Erlang, Elixir can compare any term. Allowed in guard clauses.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -316,7 +316,7 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
apply List, reverse, [[1,2,3]]
|
||||
apply Enum, :reverse, [[1,2,3]]
|
||||
#=> [3,2,1]
|
||||
|
||||
"""
|
||||
@@ -411,7 +411,7 @@ defmodule Kernel do
|
||||
end
|
||||
|
||||
@doc """
|
||||
As binary_to_list/1, but returns a list of integers corresponding to the bytes
|
||||
Like binary_to_list/1, but returns a list of integers corresponding to the bytes
|
||||
from position `start` to position `stop` in `binary`. Positions in the binary
|
||||
are numbered starting from 1.
|
||||
"""
|
||||
@@ -466,7 +466,7 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
bit_size(<<433|16,3|3>>) #=> 19
|
||||
bit_size(<<433::16,3::3>>) #=> 19
|
||||
bit_size(<<1,2,3>>) #=> 24
|
||||
|
||||
"""
|
||||
@@ -494,7 +494,7 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
byte_size(<<433|16,3|3>>) #=> 3
|
||||
byte_size(<<433::16,3::3>>) #=> 3
|
||||
byte_size(<<1,2,3>>) #=> 3
|
||||
|
||||
"""
|
||||
@@ -810,10 +810,10 @@ defmodule Kernel do
|
||||
|
||||
bin1 = <<1,2,3>>
|
||||
bin2 = <<4,5>>
|
||||
bin3 = <<6,7|4>>
|
||||
bin3 = <<6,7::4>>
|
||||
|
||||
list_to_bitstring([bin1,1,[2,3,bin2],4|bin3])
|
||||
#=> <<1,2,3,1,2,3,4,5,4,6,7|4>>
|
||||
#=> <<1,2,3,1,2,3,4,5,4,6,7::size(4)>>
|
||||
|
||||
"""
|
||||
@spec list_to_bitstring(maybe_improper_list(char | binary | iolist | bitstring, binary | bitstring | [])) :: bitstring
|
||||
@@ -877,7 +877,7 @@ defmodule Kernel do
|
||||
It should not be used in application programs.
|
||||
|
||||
## Examples
|
||||
list_to_pid('<0.41>') #=> <0.4.1>
|
||||
list_to_pid('<0.4.1>') #=> #PID<0.4.1>
|
||||
"""
|
||||
@spec list_to_pid(char_list) :: pid
|
||||
def list_to_pid(char_list) do
|
||||
@@ -905,7 +905,7 @@ defmodule Kernel do
|
||||
## Examples
|
||||
|
||||
make_ref()
|
||||
#=> #Ref<0.0.0.135>
|
||||
#=> #Reference<0.0.0.135>
|
||||
|
||||
"""
|
||||
@spec make_ref() :: reference
|
||||
@@ -955,7 +955,7 @@ defmodule Kernel do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the node where the given argmuent is located.
|
||||
Returns the node where the given argument is located.
|
||||
The argument can be a pid, a reference, or a port.
|
||||
If the local node is not alive, nonode@nohost is returned.
|
||||
|
||||
@@ -1008,7 +1008,7 @@ defmodule Kernel do
|
||||
|
||||
@doc """
|
||||
Returns the size of the given argument, which must be a tuple
|
||||
or a binary. If possible, please use tuple_size or binary_size.
|
||||
or a binary. If possible, please use `tuple_size` or `binary_size`.
|
||||
"""
|
||||
@spec size(tuple|binary) :: non_neg_integer
|
||||
def size(arg) do
|
||||
@@ -1023,10 +1023,10 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
current = Process.self
|
||||
child = spawn(fn -> current <- { Process.self, 1 + 2 } end)
|
||||
current = Kernel.self
|
||||
child = spawn(fn -> current <- { Kernel.self, 1 + 2 } end)
|
||||
|
||||
receive
|
||||
receive do
|
||||
{ ^child, 3 } -> IO.puts "Received 3 back"
|
||||
end
|
||||
|
||||
@@ -1061,12 +1061,11 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
current = Process.self
|
||||
child = spawn_link(fn -> current <- { Process.self, 1 + 2 } end)
|
||||
current = Kernel.self
|
||||
child = spawn_link(fn -> current <- { Kernel.self, 1 + 2 } end)
|
||||
|
||||
receive
|
||||
{ ^child, 3 } ->
|
||||
IO.puts "Received 3 back"
|
||||
receive do
|
||||
{ ^child, 3 } -> IO.puts "Received 3 back"
|
||||
end
|
||||
|
||||
"""
|
||||
@@ -1093,7 +1092,7 @@ defmodule Kernel do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a binary data which is the result of encoding the given term
|
||||
Returns a binary which is the result of encoding the given term
|
||||
according to the Erlang external term format.
|
||||
|
||||
This can be used for a variety of purposes, for example, writing a term
|
||||
@@ -1108,8 +1107,8 @@ defmodule Kernel do
|
||||
@doc """
|
||||
The same as `term_to_binary/1` but also supports two options:
|
||||
|
||||
* compressed: the level of compression to be used from 0 to 9;
|
||||
* minor_version: used to control the details of encoding. Can be 0 or 1,
|
||||
* `compressed`: the level of compression to be used from 0 to 9;
|
||||
* `minor_version`: used to control the details of encoding. Can be 0 or 1,
|
||||
please read http://www.erlang.org/doc/man/erlang.html#term_to_binary-2
|
||||
for more details
|
||||
|
||||
@@ -1136,7 +1135,7 @@ defmodule Kernel do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns an integer by the truncating the given number.
|
||||
Returns an integer by truncating the given number.
|
||||
Allowed in guard clauses.
|
||||
|
||||
## Examples
|
||||
@@ -1260,7 +1259,7 @@ defmodule Kernel do
|
||||
defmacro defkv(keywords) do
|
||||
Enum.map keywords, fn {k,v} ->
|
||||
quote do
|
||||
def unquote(k).() do
|
||||
def unquote(k)() do
|
||||
unquote(v)
|
||||
end
|
||||
end
|
||||
@@ -1271,9 +1270,6 @@ defmodule Kernel do
|
||||
|
||||
defkv one: 1, two: 2
|
||||
|
||||
Notice in the example above, we define the function as `def unquote(k).()`
|
||||
because each entry `k` is a an atom and invoking `def unquote(k)()`
|
||||
would be invalid Elixir syntax.
|
||||
"""
|
||||
defmacro def(name, do: contents)
|
||||
|
||||
@@ -1521,7 +1517,7 @@ defmodule Kernel do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Check if the given structure is an exception.
|
||||
Checks if the given structure is an exception.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -1546,7 +1542,7 @@ defmodule Kernel do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Check if the given structure is a record. It is basically
|
||||
Checks if the given structure is a record. It is basically
|
||||
a convenient macro that checks the structure is a tuple and
|
||||
the first element matches the given kind.
|
||||
|
||||
@@ -1573,7 +1569,7 @@ defmodule Kernel do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Check if the given argument is a record.
|
||||
Checks if the given argument is a record.
|
||||
"""
|
||||
defmacro is_record(thing) do
|
||||
case __CALLER__.in_guard? do
|
||||
@@ -1590,7 +1586,7 @@ defmodule Kernel do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Check if the given argument is a regex.
|
||||
Checks if the given argument is a regex.
|
||||
"""
|
||||
defmacro is_regex(thing) do
|
||||
quote do
|
||||
@@ -1599,7 +1595,7 @@ defmodule Kernel do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Check if the given argument is a range.
|
||||
Checks if the given argument is a range.
|
||||
"""
|
||||
defmacro is_range(thing) do
|
||||
quote do
|
||||
@@ -1678,7 +1674,7 @@ defmodule Kernel do
|
||||
end
|
||||
|
||||
If the protocol is invoked with a data type that is not an Atom,
|
||||
nor Tuple, nor List, nor BitString, Elixir will now dispatch to
|
||||
a Tuple, a List, or a BitString, Elixir will now dispatch to
|
||||
Any. That said, the default behavior could be implemented as:
|
||||
|
||||
defimpl Blank, for: Any do
|
||||
@@ -1705,8 +1701,8 @@ defmodule Kernel do
|
||||
In the example above, we have implemented `blank?` for
|
||||
`RedBlack.Tree` that simply delegates to `RedBlack.empty?` passing
|
||||
the tree as argument. This implementation doesn't need to be defined
|
||||
inside the `RedBlack` tree or inside the record, but anywhere in
|
||||
the code.
|
||||
inside the `RedBlack` tree or inside the record; it can be defined
|
||||
anywhere in the code.
|
||||
|
||||
Finally, since records are simply tuples, one can add a default
|
||||
protocol implementation to any record by defining a default
|
||||
@@ -1756,8 +1752,8 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
For example, in other to write tests using the ExUnit framework,
|
||||
a developers should use the `ExUnit.Case` module:
|
||||
For example, in order to write tests using the ExUnit framework,
|
||||
a developer should use the `ExUnit.Case` module:
|
||||
|
||||
defmodule AssertionTest do
|
||||
use ExUnit.Case, async: true
|
||||
@@ -1815,9 +1811,9 @@ defmodule Kernel do
|
||||
inspect(:foo)
|
||||
#=> ":foo"
|
||||
|
||||
Notice the inspect protocol does not necessarily return a valid Elixir
|
||||
terms representation. In such cases, the inspected result must start
|
||||
with `#`. For example, inspecting a function will return:
|
||||
Note that the inspect protocol does not necessarily return a valid
|
||||
representation of an Elixir term. In such cases, the inspected result must
|
||||
start with `#`. For example, inspecting a function will return:
|
||||
|
||||
inspect &1 + &2
|
||||
#=> #Function<...>
|
||||
@@ -2006,7 +2002,7 @@ defmodule Kernel do
|
||||
x * 2
|
||||
end
|
||||
|
||||
Not only the example is shorter, it solves ambiguity issues. Since
|
||||
Not only is the example shorter, it solves ambiguity issues. Since
|
||||
`do/end` always matches the furthest call, if we used the `function`
|
||||
macro as below:
|
||||
|
||||
@@ -2037,7 +2033,7 @@ defmodule Kernel do
|
||||
The `function` macro can also be used to retrieve local or remote
|
||||
functions:
|
||||
|
||||
f = function(:is_atom, 2)
|
||||
f = function(:is_atom, 1)
|
||||
f.(:foo) #=> true
|
||||
|
||||
f = function(List, :flatten, 1)
|
||||
@@ -2390,11 +2386,10 @@ defmodule Kernel do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Allows you to destructure two lists, assigning each
|
||||
term in the right to the left. Differently from pattern
|
||||
matching via `=`, if the sizes of the left and right
|
||||
lists don't match,, structuring simply stops instead
|
||||
of raising an error.
|
||||
Allows you to destructure two lists, assigning each term in the right to the
|
||||
matching term in the left. Unlike pattern matching via `=`, if the sizes of
|
||||
the left and right lists don't match, destructuring simply stops instead of
|
||||
raising an error.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -2404,7 +2399,7 @@ defmodule Kernel do
|
||||
z #=> 3
|
||||
|
||||
Notice in the example above, even though the right
|
||||
size has more entries than the left, structuring works
|
||||
size has more entries than the left, destructuring works
|
||||
fine. If the right size is smaller, the remaining items
|
||||
are simply assigned to nil:
|
||||
|
||||
@@ -2766,12 +2761,6 @@ defmodule Kernel do
|
||||
raise ArgumentError, message: "Unsupported expression in pipeline |> operator: #{inspect other}"
|
||||
end
|
||||
|
||||
defmacro left /> right do
|
||||
IO.puts "The /> pipeline operator is deprecated, please use the |> operator instead"
|
||||
Exception.print_stacktrace __ENV__.stacktrace
|
||||
pipeline_op(left, right)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Raises an error.
|
||||
|
||||
@@ -3057,7 +3046,7 @@ defmodule Kernel do
|
||||
fun = Keyword.get(opts, :as, name)
|
||||
|
||||
quote do
|
||||
def unquote(name).(unquote_splicing(args)) do
|
||||
def unquote(name)(unquote_splicing(args)) do
|
||||
apply unquote(target), unquote(fun), [unquote_splicing(actual_args)]
|
||||
end
|
||||
end
|
||||
@@ -3120,12 +3109,12 @@ defmodule Kernel do
|
||||
# We can skip the runtime conversion if we are
|
||||
# creating a binary made solely of series of chars.
|
||||
defmacro __c__({ :<<>>, _line, [string] }, []) when is_binary(string) do
|
||||
binary_to_list(Macro.unescape_binary(string))
|
||||
:unicode.characters_to_list(Macro.unescape_binary(string))
|
||||
end
|
||||
|
||||
defmacro __c__({ :<<>>, line, pieces }, []) do
|
||||
binary = { :<<>>, line, Macro.unescape_tokens(pieces) }
|
||||
quote do: binary_to_list(unquote(binary))
|
||||
quote do: :unicode.characters_to_list(unquote(binary))
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
||||
@@ -16,23 +16,9 @@ defmodule Kernel.CLI do
|
||||
|
||||
run fn ->
|
||||
Enum.map Enum.reverse(config.commands), process_command(&1, config)
|
||||
:elixir_code_server.cast(:finished)
|
||||
end, config.halt
|
||||
end
|
||||
|
||||
@doc """
|
||||
Wait until the CLI finishes processing options.
|
||||
"""
|
||||
def wait_until_finished do
|
||||
case :elixir_code_server.call({ :wait_until_finished, self }) do
|
||||
:wait ->
|
||||
receive do
|
||||
{ :elixir_code_server, :finished } -> :ok
|
||||
end
|
||||
:ok -> :ok
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Runs the given function by catching any failure
|
||||
and printing them to stdout. `at_exit` hooks are
|
||||
@@ -104,7 +90,7 @@ defmodule Kernel.CLI do
|
||||
|
||||
defp shared_option?(list, config, callback) do
|
||||
case process_shared(list, config) do
|
||||
{ [h|t], _ } when h == hd(list) ->
|
||||
{ [h|_], _ } when h == hd(list) ->
|
||||
invalid_option h
|
||||
{ new_list, new_config } ->
|
||||
callback.(new_list, new_config)
|
||||
|
||||
@@ -91,7 +91,7 @@ defmodule Kernel.ParallelCompiler do
|
||||
# list because someone invoked try/rescue UndefinedFunctionError
|
||||
new_waiting = List.keydelete(waiting, child, 0)
|
||||
spawn_compilers(files, output, callback, new_waiting, new_queued, schedulers, result)
|
||||
{ :module_available, child, module, binary } ->
|
||||
{ :module_available, _child, module, binary } ->
|
||||
new_waiting = release_waiting_processes(module, waiting)
|
||||
new_result = [{module, binary}|result]
|
||||
wait_for_messages(files, output, callback, new_waiting, queued, schedulers, new_result)
|
||||
|
||||
@@ -55,7 +55,7 @@ defmodule Kernel.RecordRewriter do
|
||||
defp record_field_info(function) do
|
||||
case atom_to_list(function) do
|
||||
'update' -> nil
|
||||
'update_' ++ field -> nil
|
||||
'update_' ++ _field -> nil
|
||||
_ -> { :accessor, function }
|
||||
end
|
||||
end
|
||||
@@ -185,7 +185,7 @@ defmodule Kernel.RecordRewriter do
|
||||
|
||||
res =
|
||||
case args do
|
||||
[{ :atom, _, atom }|t] -> atom
|
||||
[{ :atom, _, atom }|_] -> atom
|
||||
_ -> nil
|
||||
end
|
||||
|
||||
|
||||
@@ -355,15 +355,12 @@ defmodule Kernel.SpecialForms do
|
||||
|
||||
## Options
|
||||
|
||||
* `:hygiene` - When false, disables hygiene for variables, aliases and imports;
|
||||
* `:unquote` - When false, disables unquoting. Useful when you have a quote
|
||||
inside another quote and want to control which quote is
|
||||
able to unquote;
|
||||
* `:location` - When set to `:keep`, keeps the current line and file on quotes.
|
||||
Read the Stacktrace information section below for more information;
|
||||
* `:expand_aliases` - When false, do not expand aliases;
|
||||
* `:expand_imports` - When false, do not expand imports;
|
||||
* `:var_context` - The context for quoted variables. Defaults to the current module;
|
||||
* `:hygiene` - Allows a developer to disable hygiene selectively;
|
||||
|
||||
## Macro literals
|
||||
|
||||
@@ -379,17 +376,15 @@ defmodule Kernel.SpecialForms do
|
||||
|
||||
## Hygiene
|
||||
|
||||
Elixir macros are hygienic. This means aliases and imports
|
||||
defined inside the quoted often refer to the context that
|
||||
defined the macro.
|
||||
Elixir macros are hygienic via means of deferred resolution.
|
||||
|
||||
Furthermore, variables inside quote are also hygienic. That
|
||||
said, a variable defined in a macro cannot affect the scope
|
||||
where the macro is included.
|
||||
This means aliases and imports defined inside the quoted refer
|
||||
to the context that defined the macro and not the context
|
||||
where the macro is expanded.
|
||||
|
||||
The option `hygiene` can turn off all the hygiene mechanisms
|
||||
defined below. However, they can also be changed in a one by
|
||||
one basis.
|
||||
Furthermore, variables inside quote are also hygienic: a
|
||||
variable defined in a macro cannot affect the variables where
|
||||
the macro is expanded.
|
||||
|
||||
### Hygiene in variables
|
||||
|
||||
@@ -451,14 +446,13 @@ defmodule Kernel.SpecialForms do
|
||||
# Access the variable a from Hygiene1
|
||||
quote do: var!(a, Hygiene1) = 2
|
||||
|
||||
Another option is to set the `var_context` option, affecting
|
||||
all variables in the block:
|
||||
Hygiene for variables can be disabled overall as:
|
||||
|
||||
quote var_context: Hygiene1, do: a = 2
|
||||
quote hygiene: [vars: false], do: x
|
||||
|
||||
### Hygiene in aliases
|
||||
|
||||
Aliases inside quote are expanded by default.
|
||||
Aliases inside quote are hygienic by default.
|
||||
Consider the following example:
|
||||
|
||||
defmodule Hygiene do
|
||||
@@ -470,24 +464,43 @@ defmodule Kernel.SpecialForms do
|
||||
end
|
||||
|
||||
require Hygiene
|
||||
Hygiene.no_interference #=> HashDict[]
|
||||
Hygiene.no_interference #=> #HashDict<[]>
|
||||
|
||||
Notice that, even though the alias `D` is not available
|
||||
in the context the macro is expanded, the code above works
|
||||
because `D` was expanded when the quote was generated.
|
||||
because `D` still expands to `HashDict`.
|
||||
|
||||
There are two ways to disable this behaviour. By giving
|
||||
the `expand_aliases` equals to false to quote or by using
|
||||
the `alias!` macro inside the quote:
|
||||
In some particular cases you may want to access an alias
|
||||
or a module defined in the caller. In such scenarios, you
|
||||
can access it by disabling hygiene with `hygiene: [aliases: false]`
|
||||
or by using the `alias!` macro inside the quote:
|
||||
|
||||
defmodule NoHygiene do
|
||||
defmodule Hygiene do
|
||||
# This will expand to Elixir.Nested.hello
|
||||
defmacro no_interference do
|
||||
quote do: Nested.hello
|
||||
end
|
||||
|
||||
# This will expand to Nested.hello for
|
||||
# whatever is Nested in the caller
|
||||
defmacro interference do
|
||||
quote do: alias!(D).new
|
||||
quote do: alias!(Nested).hello
|
||||
end
|
||||
end
|
||||
|
||||
require NoHygiene
|
||||
NoHygiene.interference #=> UndefinedFunctionError
|
||||
defmodule Parent do
|
||||
defmodule Nested do
|
||||
def hello, do: "world"
|
||||
end
|
||||
|
||||
require Hygiene
|
||||
Hygiene.no_interference
|
||||
#=> ** (UndefinedFunctionError) ...
|
||||
|
||||
Hygiene.interference
|
||||
#=> "world"
|
||||
end
|
||||
|
||||
|
||||
## Hygiene in imports
|
||||
|
||||
@@ -536,7 +549,7 @@ defmodule Kernel.SpecialForms do
|
||||
Lazy.return_size #=> 2
|
||||
|
||||
As in aliases, imports expansion can be explicitly disabled
|
||||
via the `expand_imports` option.
|
||||
via the `hygiene: [imports: false]` option.
|
||||
|
||||
## Stacktrace information
|
||||
|
||||
@@ -570,7 +583,7 @@ defmodule Kernel.SpecialForms do
|
||||
particular, the macro `__FILE__` and exceptions happening inside
|
||||
the quote will always point to `GenServer.Behaviour` file.
|
||||
"""
|
||||
defmacro quote(opts, do: contents)
|
||||
defmacro quote(opts, block)
|
||||
|
||||
@doc """
|
||||
When used inside quoting, marks that the variable should
|
||||
|
||||
@@ -75,7 +75,7 @@ defmodule Kernel.Typespec do
|
||||
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
|
||||
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
|
||||
one has to use the `char_list()` type which is a synonym for `string()`. If you
|
||||
use `string()`, you'll get a warning from the compiler.
|
||||
|
||||
If you want to refer to the "string" type (the one operated by functions in the
|
||||
@@ -325,7 +325,7 @@ defmodule Kernel.Typespec do
|
||||
|
||||
defp abstract_code(module) do
|
||||
case :beam_lib.chunks(abstract_code_beam(module), [:abstract_code]) do
|
||||
{:ok, { _, [{ :abstract_code, { raw_abstract_v1, abstract_code } }] } } ->
|
||||
{:ok, { _, [{ :abstract_code, { _raw_abstract_v1, abstract_code } }] } } ->
|
||||
{ :ok, abstract_code }
|
||||
_ ->
|
||||
[]
|
||||
@@ -350,10 +350,17 @@ defmodule Kernel.Typespec do
|
||||
do_deftype(kind, type, definition, caller)
|
||||
end
|
||||
|
||||
def deftype(kind, type, caller) do
|
||||
def deftype(kind, {name, _meta, args} = type, caller) when
|
||||
is_atom(name) and
|
||||
not is_list(args) do
|
||||
do_deftype(kind, type, { :term, [line: caller.line], nil }, caller)
|
||||
end
|
||||
|
||||
def deftype(_kind, other, _caller) do
|
||||
type_spec = Macro.to_binary(other)
|
||||
raise ArgumentError, message: "invalid type specification #{type_spec}"
|
||||
end
|
||||
|
||||
defp do_deftype(kind, { name, _, args }, definition, caller) do
|
||||
args =
|
||||
if is_atom(args) do
|
||||
@@ -390,6 +397,11 @@ defmodule Kernel.Typespec do
|
||||
code
|
||||
end
|
||||
|
||||
def defspec(_type, other, _caller) do
|
||||
spec = Macro.to_binary(other)
|
||||
raise ArgumentError, message: "invalid function type specification #{spec}"
|
||||
end
|
||||
|
||||
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)]]
|
||||
@@ -467,6 +479,11 @@ defmodule Kernel.Typespec do
|
||||
typespec_to_ast({:type, line, :char_list, []})
|
||||
end
|
||||
|
||||
defp typespec_to_ast({ :remote_type, line, [{:atom, _, :elixir}, {:atom, _, :as_boolean}, [arg]] }) do
|
||||
typespec_to_ast({:type, line, :as_boolean, [arg]})
|
||||
end
|
||||
|
||||
|
||||
defp typespec_to_ast({ :remote_type, line, [mod, name, args] }) do
|
||||
args = lc arg inlist args, do: typespec_to_ast(arg)
|
||||
dot = { :., [line: line], [typespec_to_ast(mod), typespec_to_ast(name)] }
|
||||
@@ -542,6 +559,7 @@ defmodule Kernel.Typespec do
|
||||
end
|
||||
|
||||
# Handle funs
|
||||
|
||||
defp typespec({:->, meta, [{[{:fun, _, arguments}], return}]}, vars, caller) when is_list(arguments) do
|
||||
typespec({:->, meta, [{arguments, return}]}, vars, caller)
|
||||
end
|
||||
@@ -566,7 +584,7 @@ defmodule Kernel.Typespec do
|
||||
# Handle access macro
|
||||
defp typespec({{:., meta, [Kernel, :access]}, meta1, [target, args]}, vars, caller) do
|
||||
access = {{:., meta, [Kernel, :access]}, meta1,
|
||||
[target, args ++ [_: (quote hygiene: false, do: any)]]}
|
||||
[target, args ++ [_: { :any, [], [] }]]}
|
||||
typespec(Macro.expand(access, caller), vars, caller)
|
||||
end
|
||||
|
||||
@@ -591,6 +609,12 @@ defmodule Kernel.Typespec do
|
||||
{ :type, line(meta), :tuple, args }
|
||||
end
|
||||
|
||||
# Handle blocks
|
||||
|
||||
defp typespec({:__block__, _meta, [arg]}, vars, caller) do
|
||||
typespec(arg, vars, caller)
|
||||
end
|
||||
|
||||
# Handle variables or local calls
|
||||
defp typespec({name, meta, atom}, vars, caller) when is_atom(atom) do
|
||||
if List.member?(vars, name) do
|
||||
@@ -612,6 +636,10 @@ defmodule Kernel.Typespec do
|
||||
typespec((quote do: :elixir.char_list(unquote_splicing(arguments))), vars, caller)
|
||||
end
|
||||
|
||||
defp typespec({:as_boolean, _meta, arguments}, vars, caller) do
|
||||
typespec((quote do: :elixir.as_boolean(unquote_splicing(arguments))), vars, caller)
|
||||
end
|
||||
|
||||
defp typespec({name, meta, arguments}, vars, caller) do
|
||||
arguments = lc arg inlist arguments, do: typespec(arg, vars, caller)
|
||||
{ :type, line(meta), name, arguments }
|
||||
@@ -676,4 +704,4 @@ defmodule Kernel.Typespec do
|
||||
defp variable({name, meta, _}) do
|
||||
{:var, line(meta), name}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+10
-2
@@ -443,7 +443,7 @@ defmodule Macro do
|
||||
def expand(aliases, env)
|
||||
|
||||
def expand({ :__aliases__, _, _ } = original, env) do
|
||||
case :elixir_aliases.expand(original, env.aliases) do
|
||||
case :elixir_aliases.expand(original, env.aliases, []) do
|
||||
atom when is_atom(atom) -> atom
|
||||
aliases ->
|
||||
aliases = lc alias inlist aliases, do: expand(alias, env)
|
||||
@@ -478,8 +478,16 @@ defmodule Macro do
|
||||
case not is_partial?(args) do
|
||||
false -> original
|
||||
true ->
|
||||
module = env.module
|
||||
|
||||
extra = if function_exported?(module, :__info__, 1) do
|
||||
[{ module, module.__info__(:macros) }]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
expand = :elixir_dispatch.expand_import(line, { atom, length(args) }, args,
|
||||
env.module, env.function, env.requires, env.macros, env)
|
||||
env.module, env.function, env.requires, extra ++ env.macros, env)
|
||||
case expand do
|
||||
{ :ok, _, expanded } -> expanded
|
||||
{ :error, _ } -> original
|
||||
|
||||
@@ -210,7 +210,7 @@ defmodule Module do
|
||||
[] ->
|
||||
ETS.insert(table, { tuple, line, kind, signature, doc })
|
||||
:ok
|
||||
[{ tuple, line, old_kind, old_sign, old_doc }] when old_doc == nil or doc == nil or old_doc == doc ->
|
||||
[{ tuple, line, _old_kind, old_sign, old_doc }] when old_doc == nil or doc == nil or old_doc == doc ->
|
||||
ETS.insert(table, {
|
||||
tuple,
|
||||
line,
|
||||
|
||||
+13
-1
@@ -74,11 +74,23 @@ defmodule Node do
|
||||
|
||||
See http://www.erlang.org/doc/man/erlang.html#disconnect_node-1 for more info.
|
||||
"""
|
||||
@spec disconnect(t) :: boolean | :ginored
|
||||
@spec disconnect(t) :: boolean | :ignored
|
||||
def disconnect(node) do
|
||||
:erlang.disconnect_node(node)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Establishes a connection to Node. Returns true if successful,
|
||||
false if not, and the atom `:ignored` if the local node is not
|
||||
alive.
|
||||
|
||||
See http://erlang.org/doc/man/net_kernel.html#connect_node-1 for more info.
|
||||
"""
|
||||
@spec connect(t) :: boolean | :ignored
|
||||
def connect(node) do
|
||||
:net_kernel.connect_node(node)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the pid of a new process started by the application of `fun`
|
||||
on `node`. If `node` does not exist, a useless pid is returned.
|
||||
|
||||
@@ -23,11 +23,19 @@ defmodule OptionParser do
|
||||
|
||||
## Switches
|
||||
|
||||
Extra information about switches can be given as argument too. This is useful
|
||||
in order to say a switch must behave as a boolean, list, etc. The following
|
||||
types are supported:
|
||||
Extra information about switches can be given as argument too.
|
||||
This is useful in order to say a switch must behave as a boolean
|
||||
or if duplicated switches should be kept, overriden or accumulated.
|
||||
|
||||
* `:boolean` - They never consume the next value unless it is true/false;
|
||||
The following types are supported:
|
||||
|
||||
* `:boolean` - Mark the given switch as boolean. Boolean switches
|
||||
never consumes the following value unless it is
|
||||
true or false;
|
||||
|
||||
The following extra options are supported:
|
||||
|
||||
* `:keep` - Keep duplicated items in the list instead of overriding;
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -69,11 +77,6 @@ defmodule OptionParser do
|
||||
## Helpers
|
||||
|
||||
defp parse(argv, opts, bool) do
|
||||
if flags = opts[:flags] do
|
||||
IO.write "OptionParser option :flags is deprecated, please use :switches instead\n#{Exception.format_stacktrace}"
|
||||
opts = Keyword.put(opts, :switches, lc(k inlist flags, do: { k, :boolean }))
|
||||
end
|
||||
|
||||
aliases = opts[:aliases] || []
|
||||
switches = opts[:switches] || []
|
||||
parse(argv, aliases, switches, bool)
|
||||
@@ -85,16 +88,18 @@ defmodule OptionParser do
|
||||
|
||||
defp parse(["-" <> option|t], aliases, switches, dict, args, all) do
|
||||
{ option, value } = normalize_option(option, aliases)
|
||||
kind = switches[option]
|
||||
|
||||
if value == nil do
|
||||
{ value, t } =
|
||||
case switch_type(switches, option) do
|
||||
:boolean -> boolean_from_tail(t)
|
||||
_ -> value_from_tail(t)
|
||||
if is_switch_a? :boolean, kind do
|
||||
boolean_from_tail(t)
|
||||
else
|
||||
value_from_tail(t)
|
||||
end
|
||||
end
|
||||
|
||||
dict = store_option dict, option, value
|
||||
dict = store_option dict, option, value, kind
|
||||
parse(t, aliases, switches, dict, args, all)
|
||||
end
|
||||
|
||||
@@ -117,16 +122,22 @@ defmodule OptionParser do
|
||||
defp value_from_tail([h|t]), do: { h, t }
|
||||
defp value_from_tail([]), do: { true, [] }
|
||||
|
||||
defp store_option(dict, option, value) when value in ["false", "true"] do
|
||||
store_option(dict, option, binary_to_atom(value))
|
||||
defp store_option(dict, option, value, switches) when value in ["true", "false"] do
|
||||
store_option dict, option, binary_to_atom(value), switches
|
||||
end
|
||||
|
||||
defp store_option(dict, option, value) do
|
||||
[{ option, value }|dict]
|
||||
defp store_option(dict, option, value, kind) do
|
||||
if is_switch_a? :keep, kind do
|
||||
[{ option, value }|dict]
|
||||
else
|
||||
[{ option, value }|Keyword.delete(dict, option)]
|
||||
end
|
||||
end
|
||||
|
||||
defp reverse_dict(dict, switches) do
|
||||
switches = lc { k, :boolean } inlist switches, not Keyword.has_key?(dict, k), do: { k, false }
|
||||
switches = lc { k, v } inlist switches,
|
||||
is_switch_a?(:boolean, v),
|
||||
not Keyword.has_key?(dict, k), do: { k, false }
|
||||
Enum.reverse switches ++ dict
|
||||
end
|
||||
|
||||
@@ -155,7 +166,7 @@ defmodule OptionParser do
|
||||
defp is_no?("no-" <> _), do: true
|
||||
defp is_no?(_), do: false
|
||||
|
||||
defp switch_type(switches, option) do
|
||||
switches[option] || :default
|
||||
end
|
||||
defp is_switch_a?(kind, list) when is_list(list), do: List.member?(list, kind)
|
||||
defp is_switch_a?(kind, kind), do: true
|
||||
defp is_switch_a?(_, _), do: false
|
||||
end
|
||||
|
||||
@@ -456,12 +456,12 @@ defmodule Path do
|
||||
end
|
||||
|
||||
def wildcard(glob) when is_list(glob) do
|
||||
Enum.map wildcard(list_to_binary(glob)), binary_to_list(&1)
|
||||
:elixir_glob.wildcard glob
|
||||
end
|
||||
|
||||
## Helpers
|
||||
|
||||
defp get_cwd(path) when is_list(path), do: System.cwd! |> binary_to_list
|
||||
defp get_cwd(path) when is_list(path), do: System.cwd! |> binary_to_filename_string
|
||||
defp get_cwd(_), do: System.cwd!
|
||||
|
||||
defp binary_to_filename_string(binary) do
|
||||
@@ -491,7 +491,7 @@ defmodule Path do
|
||||
end
|
||||
|
||||
defp do_normalize(['~'|t]) do
|
||||
do_normalize t, [System.user_home! |> binary_to_list]
|
||||
do_normalize t, [System.user_home! |> binary_to_filename_string]
|
||||
end
|
||||
|
||||
defp do_normalize(t) do
|
||||
|
||||
@@ -19,11 +19,10 @@ defmodule Process do
|
||||
:erlang.is_process_alive(pid)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the current process.
|
||||
"""
|
||||
@spec self :: pid
|
||||
@doc false
|
||||
def self do
|
||||
IO.puts "Process.self is deprecated, please use Kernel.self instead"
|
||||
Exception.print_stacktrace
|
||||
:erlang.self()
|
||||
end
|
||||
|
||||
|
||||
@@ -287,7 +287,7 @@ defmodule Protocol do
|
||||
# Generate all others protocols.
|
||||
defp each_impl_for({ kind, fun, _ }, _, _) do
|
||||
quote do
|
||||
defp __raw_impl__(arg) when unquote(fun).(arg) do
|
||||
defp __raw_impl__(arg) when unquote(fun)(arg) do
|
||||
__MODULE__.unquote(kind)
|
||||
end
|
||||
end
|
||||
@@ -404,7 +404,7 @@ defmodule Protocol.DSL do
|
||||
|
||||
# Generate a fake definition with the user
|
||||
# signature that will be used by docs
|
||||
Kernel.def unquote(name).(unquote_splicing(args))
|
||||
Kernel.def unquote(name)(unquote_splicing(args))
|
||||
|
||||
{ args, body } = Protocol.DSL.args_and_body(__MODULE__, name, arity)
|
||||
Kernel.def unquote(name), args, [], do: body
|
||||
|
||||
@@ -131,23 +131,23 @@ defmodule Record do
|
||||
end
|
||||
|
||||
contents = quote do
|
||||
defmacrop unquote(name).() do
|
||||
defmacrop unquote(name)() do
|
||||
Record.access(__MODULE__, unquote(escaped), [], __CALLER__)
|
||||
end
|
||||
|
||||
defmacrop unquote(name).(record) when is_tuple(record) do
|
||||
defmacrop unquote(name)(record) when is_tuple(record) do
|
||||
Record.to_keywords(__MODULE__, unquote(escaped), record)
|
||||
end
|
||||
|
||||
defmacrop unquote(name).(args) do
|
||||
defmacrop unquote(name)(args) do
|
||||
Record.access(__MODULE__, unquote(escaped), args, __CALLER__)
|
||||
end
|
||||
|
||||
defmacrop unquote(name).(record, key) when is_atom(key) do
|
||||
defmacrop unquote(name)(record, key) when is_atom(key) do
|
||||
Record.get(__MODULE__, unquote(escaped), record, key)
|
||||
end
|
||||
|
||||
defmacrop unquote(name).(record, args) do
|
||||
defmacrop unquote(name)(record, args) do
|
||||
Record.dispatch(__MODULE__, unquote(escaped), record, args, __CALLER__)
|
||||
end
|
||||
end
|
||||
@@ -465,17 +465,17 @@ defmodule Record do
|
||||
|
||||
contents = quote do
|
||||
@doc false
|
||||
def unquote(key).(record) do
|
||||
def unquote(key)(record) do
|
||||
:erlang.element(unquote(i + 1), record)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def unquote(key).(value, record) do
|
||||
def unquote(key)(value, record) do
|
||||
:erlang.setelement(unquote(i + 1), record, value)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def unquote(update).(function, record) do
|
||||
def unquote(update)(function, record) do
|
||||
:erlang.setelement(unquote(i + 1), record,
|
||||
function.(:erlang.element(unquote(i + 1), record)))
|
||||
end
|
||||
|
||||
@@ -54,8 +54,7 @@ defmodule Regex do
|
||||
It returns `{ :ok, regex }` in case of success,
|
||||
`{ :error, reason }` otherwise.
|
||||
"""
|
||||
def compile(source, options // "") do
|
||||
source = to_binary(source)
|
||||
def compile(source, options // "") when is_binary(source) do
|
||||
options = to_binary(options)
|
||||
opts = translate_options(options)
|
||||
re_opts = opts -- [:groups]
|
||||
@@ -302,7 +301,7 @@ defmodule Regex do
|
||||
lc result inlist results do
|
||||
case result do
|
||||
[t] -> t
|
||||
[h|t] -> t
|
||||
[_|t] -> t
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -24,4 +24,4 @@
|
||||
{verbose, false}
|
||||
]}.
|
||||
|
||||
{require_otp_vsn,"R15"}.
|
||||
{require_otp_vsn,"(R15|R16).*"}.
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
-compile({parse_transform, elixir_transform}).
|
||||
|
||||
%% Top level types
|
||||
-export_type([char_list/0]).
|
||||
-export_type([char_list/0, as_boolean/1]).
|
||||
-type char_list() :: string().
|
||||
-type as_boolean(T) :: T.
|
||||
|
||||
% OTP APPLICATION API
|
||||
|
||||
|
||||
@@ -1,26 +1,51 @@
|
||||
-module(elixir_aliases).
|
||||
-export([nesting/2, last/1, concat/1, safe_concat/1, lookup/2,
|
||||
format_error/1, ensure_loaded/3, expand/2]).
|
||||
-export([nesting_alias/2, last/1, concat/1, safe_concat/1,
|
||||
format_error/1, ensure_loaded/3, expand/3]).
|
||||
-include("elixir.hrl").
|
||||
-compile({parse_transform, elixir_transform}).
|
||||
|
||||
%% Expand an alias returning an atom if possible.
|
||||
%% Otherwise returns the list of aliases args.
|
||||
%% Expand an alias. It returns an atom (meaning that there
|
||||
%% was an expansion) or a list of atoms.
|
||||
|
||||
expand({ '__aliases__', _Line, [H] }, Aliases) when H /= 'Elixir' ->
|
||||
expand_alias(H, Aliases);
|
||||
expand({ '__aliases__', Meta, _ } = Alias, Aliases, MacroAliases) ->
|
||||
case lists:keyfind(alias, 1, Meta) of
|
||||
{ alias, false } ->
|
||||
expand(Alias, MacroAliases);
|
||||
{ alias, Atom } when is_atom(Atom) ->
|
||||
case expand(Alias, MacroAliases) of
|
||||
OtherAtom when is_atom(OtherAtom) -> OtherAtom;
|
||||
OtherAliases when is_list(OtherAliases) -> Atom
|
||||
end;
|
||||
false ->
|
||||
expand(Alias, Aliases)
|
||||
end.
|
||||
|
||||
expand({ '__aliases__', _Line, [H|T] }, Aliases) when is_atom(H) ->
|
||||
concat(case H of
|
||||
'Elixir' -> [H|T];
|
||||
_ -> [expand_alias(H, Aliases)|T]
|
||||
end);
|
||||
expand({ '__aliases__', _Meta, [H] }, Aliases) when H /= 'Elixir' ->
|
||||
case expand_one(H, Aliases) of
|
||||
false -> [H];
|
||||
Atom -> Atom
|
||||
end;
|
||||
|
||||
expand({ '__aliases__', _Line, List }, _Aliases) ->
|
||||
expand({ '__aliases__', _Meta, [H|T] }, Aliases) when is_atom(H) ->
|
||||
case H of
|
||||
'Elixir' ->
|
||||
concat(T);
|
||||
_ ->
|
||||
case expand_one(H, Aliases) of
|
||||
false -> [H|T];
|
||||
Atom -> concat([Atom|T])
|
||||
end
|
||||
end;
|
||||
|
||||
expand({ '__aliases__', _Meta, List }, _Aliases) ->
|
||||
List.
|
||||
|
||||
expand_alias(H, Aliases) ->
|
||||
lookup(list_to_atom("Elixir-" ++ atom_to_list(H)), Aliases).
|
||||
expand_one(H, Aliases) ->
|
||||
Lookup = list_to_atom("Elixir-" ++ atom_to_list(H)),
|
||||
case lookup(Lookup, Aliases) of
|
||||
Lookup -> false;
|
||||
Else -> Else
|
||||
end.
|
||||
|
||||
%% Ensure a module is loaded before its usage.
|
||||
|
||||
@@ -39,7 +64,7 @@ ensure_loaded(Line, Ref, S) ->
|
||||
elixir_errors:form_error(Line, S#elixir_scope.file, ?MODULE, { Kind, Ref })
|
||||
end.
|
||||
|
||||
%% Receives an atom and returns the last alias.
|
||||
%% Receives an atom and returns the last bit as an alias.
|
||||
|
||||
last(Atom) ->
|
||||
Last = last(lists:reverse(atom_to_list(Atom)), []),
|
||||
@@ -49,27 +74,43 @@ last([$-|_], Acc) -> Acc;
|
||||
last([H|T], Acc) -> last(T, [H|Acc]);
|
||||
last([], Acc) -> Acc.
|
||||
|
||||
%% Returns the nesting between two aliases.
|
||||
%% Gets two modules names and return an alias
|
||||
%% which can be passed down to the alias directive
|
||||
%% and it will create a proper shortcut representing
|
||||
%% the given nesting.
|
||||
%%
|
||||
%% Examples:
|
||||
%%
|
||||
%% nesting_alias('Elixir.Foo.Bar', 'Elixir.Foo.Bar.Baz.Bat')
|
||||
%% { 'Elixir.Baz', 'Elixir.Foo.Bar.Baz' }
|
||||
%%
|
||||
%% When passed to alias, the example above will generate an
|
||||
%% alias like:
|
||||
%%
|
||||
%% 'Elixir.Baz' => 'Elixir.Foo.Bar.Baz'
|
||||
%%
|
||||
nesting_alias(nil, _Full) -> false;
|
||||
|
||||
nesting(nil, _Full) -> false;
|
||||
|
||||
nesting(Prefix, Full) ->
|
||||
nesting_alias(Prefix, Full) ->
|
||||
PrefixList = list_nesting(Prefix),
|
||||
FullList = list_nesting(Full),
|
||||
(PrefixList /= []) andalso do_nesting(PrefixList, FullList, []).
|
||||
|
||||
(PrefixList /= []) andalso
|
||||
(PrefixList /= FullList) andalso
|
||||
lists:prefix(PrefixList, FullList) andalso
|
||||
binary_to_atom(<<"Elixir-", (hd(FullList -- PrefixList))/binary>>, utf8).
|
||||
do_nesting([X|PreTail], [X|Tail], Acc) ->
|
||||
do_nesting(PreTail, Tail, [X|Acc]);
|
||||
do_nesting([], [H|_], Acc) ->
|
||||
{ list_to_atom("Elixir-" ++ H), concat(lists:reverse([H|Acc])) };
|
||||
do_nesting(_, _, _Acc) ->
|
||||
false.
|
||||
|
||||
list_nesting(Atom) ->
|
||||
case binary:split(atom_to_binary(Atom, utf8), <<$->>, [global]) of
|
||||
[<<"Elixir">>|T] -> T;
|
||||
case string:tokens(atom_to_list(Atom), "-") of
|
||||
["Elixir"|T] -> T;
|
||||
_ -> []
|
||||
end.
|
||||
|
||||
%% Receives a list of atoms representing modules
|
||||
%% and concatenate them.
|
||||
%% Receives a list of atoms, binaries or lists
|
||||
%% representing modules and concatenates them.
|
||||
|
||||
concat(Args) -> list_to_atom(raw_concat(Args)).
|
||||
safe_concat(Args) -> list_to_existing_atom(raw_concat(Args)).
|
||||
@@ -93,7 +134,7 @@ dot_to_dash(List) ->
|
||||
_ -> X
|
||||
end || X <- List].
|
||||
|
||||
%% Lookup an alias in the current scope
|
||||
%% Lookup an alias in the current scope.
|
||||
|
||||
lookup(Else, Dict) ->
|
||||
case orddict:find(Else, Dict) of
|
||||
|
||||
@@ -108,63 +108,58 @@ do_match(Meta, DecoupledClauses, S) ->
|
||||
{ TX, { umergec(S, TAcc), [TAcc#elixir_scope.clause_vars|CV] } }
|
||||
end,
|
||||
|
||||
{ TClauses, { TS, RawCV } } = lists:mapfoldl(Transformer, {S, []}, DecoupledClauses),
|
||||
{ TClauses, { TS, ReverseCV } } = lists:mapfoldl(Transformer, {S, []}, DecoupledClauses),
|
||||
|
||||
% Now get all the variables defined inside each clause
|
||||
CV = lists:reverse(RawCV),
|
||||
CV = lists:reverse(ReverseCV),
|
||||
AllVars = lists:foldl(fun(KV, Acc) ->
|
||||
elixir_scope:merge_clause_vars(Acc, KV)
|
||||
end, orddict:new(), CV),
|
||||
|
||||
AllVars = lists:umerge([orddict:fetch_keys(X) || X <- CV]),
|
||||
SharedVars = ordsets:intersection(CV),
|
||||
% Create a new scope that contains a list of all variables
|
||||
% defined inside all the clauses. It returns this new scope and
|
||||
% a list of tuples where the first element is the variable name,
|
||||
% the second one is the new pointer to the variable and the third
|
||||
% is the old pointer.
|
||||
{ FinalVars, FS } = lists:mapfoldl(fun({ Key, Ref }, Acc) ->
|
||||
normalize_vars(Key, Ref, Acc)
|
||||
end, TS, AllVars),
|
||||
|
||||
case AllVars of
|
||||
[] -> { TClauses, TS };
|
||||
_ ->
|
||||
% Create a new scope that contains a list of all variables
|
||||
% defined inside all the clauses. It returns this new scope and
|
||||
% a list of tuples where the first element is the variable name,
|
||||
% the second one is the new pointer to the variable and the third
|
||||
% is the old pointer.
|
||||
{ FinalVars, FS } = lists:mapfoldl(fun(X, Acc) -> normalize_vars(X, SharedVars, Acc) end, TS, AllVars),
|
||||
% Expand all clauses by adding a match operation at the end
|
||||
% that assigns variables missing in one clause to the others.
|
||||
expand_clauses(?line(Meta), TClauses, CV, FinalVars, [], FS).
|
||||
|
||||
% Defines a tuple that will be used as left side of the match operator
|
||||
Line = ?line(Meta),
|
||||
LeftVars = [{var, Line, NewValue} || { _, _, NewValue, _ } <- FinalVars],
|
||||
expand_clauses(Line, [Clause|T], [ClauseVars|V], FinalVars, Acc, S) ->
|
||||
case generate_match_vars(FinalVars, ClauseVars, [], []) of
|
||||
{ [], [] } ->
|
||||
expand_clauses(Line, T, V, FinalVars, [Clause|Acc], S);
|
||||
{ Left, Right } ->
|
||||
MatchExpr = generate_match(Line, Left, Right),
|
||||
ClauseExprs = element(5, Clause),
|
||||
[Final|RawClauseExprs] = lists:reverse(ClauseExprs),
|
||||
|
||||
% Expand all clauses by adding a match operation at the end
|
||||
% that assigns variables missing in one clause to the others.
|
||||
expand_clauses(Meta, TClauses, CV, LeftVars, FinalVars, [], FS)
|
||||
end.
|
||||
% If the last sentence has a match clause, we need to assign its value
|
||||
% in the variable list. If not, we insert the variable list before the
|
||||
% final clause in order to keep it tail call optimized.
|
||||
{ FinalClauseExprs, FS } = case has_match_tuple(Final) of
|
||||
true ->
|
||||
case Final of
|
||||
{ match, _, { var, _, UserVarName } = UserVar, _ } when UserVarName /= '_' ->
|
||||
{ [UserVar,MatchExpr,Final|RawClauseExprs], S };
|
||||
_ ->
|
||||
{ StorageVar, SS } = elixir_scope:build_erl_var(Line, S),
|
||||
StorageExpr = { match, Line, StorageVar, Final },
|
||||
{ [StorageVar,MatchExpr,StorageExpr|RawClauseExprs], SS }
|
||||
end;
|
||||
false ->
|
||||
{ [Final,MatchExpr|RawClauseExprs], S }
|
||||
end,
|
||||
|
||||
expand_clauses(Meta, [Clause|T], [ClauseVars|V], LeftVars, FinalVars, Acc, S) ->
|
||||
RightVars = [normalize_clause_var(Var, Kind, OldValue, ClauseVars) ||
|
||||
{ Var, Kind, _, OldValue } <- FinalVars],
|
||||
FinalClause = setelement(5, Clause, lists:reverse(FinalClauseExprs)),
|
||||
expand_clauses(Line, T, V, FinalVars, [FinalClause|Acc], FS)
|
||||
end;
|
||||
|
||||
AssignExpr = generate_match(Meta, LeftVars, RightVars),
|
||||
ClauseExprs = element(5, Clause),
|
||||
[Final|RawClauseExprs] = lists:reverse(ClauseExprs),
|
||||
|
||||
% If the last sentence has a match clause, we need to assign its value
|
||||
% in the variable list. If not, we insert the variable list before the
|
||||
% final clause in order to keep it tail call optimized.
|
||||
{ FinalClauseExprs, FS } = case has_match_tuple(Final) of
|
||||
true ->
|
||||
case Final of
|
||||
{ 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 }
|
||||
end;
|
||||
false ->
|
||||
{ [Final,AssignExpr|RawClauseExprs], S }
|
||||
end,
|
||||
|
||||
FinalClause = setelement(5, Clause, lists:reverse(FinalClauseExprs)),
|
||||
expand_clauses(Meta, T, V, LeftVars, FinalVars, [FinalClause|Acc], FS);
|
||||
|
||||
expand_clauses(_Meta, [], [], _LeftVars, _FinalVars, Acc, S) ->
|
||||
expand_clauses(_Line, [], [], _FinalVars, Acc, S) ->
|
||||
{ lists:reverse(Acc), S }.
|
||||
|
||||
% Handle each key/value clause pair and translate them accordingly.
|
||||
@@ -210,24 +205,14 @@ has_match_tuple(H) when is_list(H) ->
|
||||
|
||||
has_match_tuple(_) -> false.
|
||||
|
||||
% Normalize the given var checking its existence in the scope var dictionary.
|
||||
% Normalize the given var in between clauses
|
||||
% by picking one value as reference and retriving
|
||||
% its previous value.
|
||||
|
||||
normalize_vars({ Var, Kind } = Key, Shared, #elixir_scope{vars=Vars,clause_vars=ClauseVars} = S) ->
|
||||
{ NewValue, S1 } =
|
||||
case orddict:find(Key, Shared) of
|
||||
{ ok, SharedValue } ->
|
||||
{ SharedValue, S };
|
||||
error ->
|
||||
{ { _, _, ErlValue }, ErlS } = case (Kind /= nil) or (S#elixir_scope.noname) of
|
||||
true -> elixir_scope:build_erl_var(0, S);
|
||||
false -> elixir_scope:build_erl_var(0, Var, "_@" ++ atom_to_list(Var), S)
|
||||
end,
|
||||
{ ErlValue, ErlS }
|
||||
end,
|
||||
|
||||
S2 = S1#elixir_scope{
|
||||
vars=orddict:store(Key, NewValue, Vars),
|
||||
clause_vars=orddict:store(Key, NewValue, ClauseVars)
|
||||
normalize_vars(Key, Value, #elixir_scope{vars=Vars,clause_vars=ClauseVars} = S) ->
|
||||
FS = S#elixir_scope{
|
||||
vars=orddict:store(Key, Value, Vars),
|
||||
clause_vars=orddict:store(Key, Value, ClauseVars)
|
||||
},
|
||||
|
||||
Expr = case orddict:find(Key, Vars) of
|
||||
@@ -235,27 +220,29 @@ normalize_vars({ Var, Kind } = Key, Shared, #elixir_scope{vars=Vars,clause_vars=
|
||||
error -> { atom, 0, nil }
|
||||
end,
|
||||
|
||||
{ { Var, Kind, NewValue, Expr }, S2 }.
|
||||
{ { Key, Value, Expr }, FS }.
|
||||
|
||||
% Normalize a var by checking if it was defined in the clause.
|
||||
% If so, use it, otherwise use from main scope.
|
||||
% Generate match vars by checking if they were updated
|
||||
% or not and assigning the previous value.
|
||||
|
||||
normalize_clause_var(Var, Kind, OldValue, ClauseVars) ->
|
||||
case orddict:find({ Var, Kind }, ClauseVars) of
|
||||
{ ok, ClauseValue } -> { var, 0, ClauseValue };
|
||||
error -> OldValue
|
||||
end.
|
||||
generate_match_vars([{ Key, NewValue, OldValue }|T], ClauseVars, Left, Right) ->
|
||||
case orddict:find(Key, ClauseVars) of
|
||||
{ ok, NewValue } ->
|
||||
generate_match_vars(T, ClauseVars, Left, Right);
|
||||
{ ok, ClauseValue } ->
|
||||
generate_match_vars(T, ClauseVars, [{ var, 0, NewValue }|Left], [{ var, 0, ClauseValue }|Right]);
|
||||
error ->
|
||||
generate_match_vars(T, ClauseVars, [{ var, 0, NewValue }|Left], [OldValue|Right])
|
||||
end;
|
||||
|
||||
%% generate_match
|
||||
generate_match_vars([], _ClauseVars, Left, Right) ->
|
||||
{ Left, Right }.
|
||||
|
||||
generate_match(Meta, [Left], [Right]) ->
|
||||
{ match, ?line(Meta), Left, Right };
|
||||
generate_match(Line, [Left], [Right]) ->
|
||||
{ match, Line, Left, Right };
|
||||
|
||||
generate_match(Meta, LeftVars, RightVars) ->
|
||||
Line = ?line(Meta),
|
||||
generate_match(Line, LeftVars, RightVars) ->
|
||||
{ match, Line, { tuple, Line, LeftVars }, { tuple, Line, RightVars } }.
|
||||
|
||||
%% Listify
|
||||
|
||||
listify(Expr) when not is_list(Expr) -> [Expr];
|
||||
listify(Expr) -> Expr.
|
||||
@@ -30,13 +30,6 @@ start_link() ->
|
||||
init(_args) ->
|
||||
{ ok, #elixir_code_server{} }.
|
||||
|
||||
handle_call({ wait_until_finished, Pid }, _, Config) ->
|
||||
Waiting = Config#elixir_code_server.waiting,
|
||||
case is_list(Waiting) of
|
||||
true -> { reply, wait, Config#elixir_code_server{waiting=[Pid|Waiting]} };
|
||||
false -> { reply, ok, Config }
|
||||
end;
|
||||
|
||||
handle_call({ acquire, Path }, From, Config) ->
|
||||
Current = Config#elixir_code_server.loaded,
|
||||
case orddict:find(Path, Current) of
|
||||
@@ -87,11 +80,6 @@ handle_cast({ compiler_options, Options }, Config) ->
|
||||
Final = orddict:merge(fun(_,_,V) -> V end, Config#elixir_code_server.compiler_options, Options),
|
||||
{ noreply, Config#elixir_code_server{compiler_options=Final} };
|
||||
|
||||
handle_cast(finished, Config) ->
|
||||
Waiting = Config#elixir_code_server.waiting,
|
||||
[Pid ! { elixir_code_server, finished } || Pid <- lists:reverse(Waiting)],
|
||||
{ noreply, Config#elixir_code_server{waiting=done} };
|
||||
|
||||
handle_cast({ loaded, Path }, Config) ->
|
||||
Current = Config#elixir_code_server.loaded,
|
||||
case orddict:find(Path, Current) of
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
-module(elixir_compiler).
|
||||
-export([get_opts/0, get_opt/1, get_opt/2, string/2, file/1, file_to_path/2]).
|
||||
-export([core/0, module/3, eval_forms/4]).
|
||||
-export([core/0, module/3, eval_forms/4, format_error/1]).
|
||||
-include("elixir.hrl").
|
||||
-compile({parse_transform, elixir_transform}).
|
||||
|
||||
@@ -89,11 +89,17 @@ module(Forms, S, Callback) ->
|
||||
end,
|
||||
module(Forms, S#elixir_scope.file, Options, false, Callback).
|
||||
|
||||
module(Forms, File, Options, Bootstrap, Callback) when
|
||||
is_binary(File), is_list(Forms), is_list(Options), is_boolean(Bootstrap), is_function(Callback) ->
|
||||
module(Forms, File, RawOptions, Bootstrap, Callback) when
|
||||
is_binary(File), is_list(Forms), is_list(RawOptions), is_boolean(Bootstrap), is_function(Callback) ->
|
||||
{ Options, SkipNative } = compile_opts(Forms, RawOptions),
|
||||
Listname = binary_to_list(File),
|
||||
case compile:forms([no_auto_import()|Forms], [return,{source,Listname}|Options]) of
|
||||
{ok, ModuleName, Binary, Warnings} ->
|
||||
|
||||
case compile:noenv_forms([no_auto_import()|Forms], [return,{source,Listname}|Options]) of
|
||||
{ok, ModuleName, Binary, RawWarnings} ->
|
||||
Warnings = case SkipNative of
|
||||
true -> [{?MODULE,[{0,?MODULE,{skip_native,ModuleName}}]}|RawWarnings];
|
||||
false -> RawWarnings
|
||||
end,
|
||||
format_warnings(Bootstrap, File, Warnings),
|
||||
code:load_binary(ModuleName, Listname, Binary),
|
||||
Callback(ModuleName, Binary);
|
||||
@@ -112,6 +118,38 @@ core() ->
|
||||
|
||||
%% HELPERS
|
||||
|
||||
compile_opts(Forms, Options) ->
|
||||
EnvOptions = env_default_opts(),
|
||||
SkipNative = lists:member(native, EnvOptions) and contains_on_load(Forms),
|
||||
case SkipNative or lists:member([{native,false}], Options) of
|
||||
true -> { Options ++ lists:delete(native, EnvOptions), SkipNative };
|
||||
false -> { Options ++ EnvOptions, SkipNative }
|
||||
end.
|
||||
|
||||
env_default_opts() ->
|
||||
Key = "ERL_COMPILER_OPTIONS",
|
||||
case os:getenv(Key) of
|
||||
false -> [];
|
||||
Str when is_list(Str) ->
|
||||
case erl_scan:string(Str) of
|
||||
{ok,Tokens,_} ->
|
||||
case erl_parse:parse_term(Tokens ++ [{dot, 1}]) of
|
||||
{ok,List} when is_list(List) -> List;
|
||||
{ok,Term} -> [Term];
|
||||
{error,_Reason} ->
|
||||
io:format("Ignoring bad term in ~s\n", [Key]),
|
||||
[]
|
||||
end;
|
||||
{error, {_,_,_Reason}, _} ->
|
||||
io:format("Ignoring bad term in ~s\n", [Key]),
|
||||
[]
|
||||
end
|
||||
end.
|
||||
|
||||
contains_on_load([{ attribute, _, on_load, _ }|_]) -> true;
|
||||
contains_on_load([_|T]) -> contains_on_load(T);
|
||||
contains_on_load([]) -> false.
|
||||
|
||||
no_auto_import() ->
|
||||
Bifs = [{ Name, Arity } || { Name, Arity } <- erlang:module_info(exports), erl_internal:bif(Name, Arity)],
|
||||
{ attribute, 0, compile, { no_auto_import, Bifs } }.
|
||||
@@ -196,8 +234,12 @@ core_main() ->
|
||||
|
||||
%% ERROR HANDLING
|
||||
|
||||
format_error({ skip_native, Module }) ->
|
||||
io_lib:format("skipping native compilation for ~s because it contains on_load attribute",
|
||||
[elixir_errors:inspect(Module)]).
|
||||
|
||||
format_errors(_File, []) ->
|
||||
exit({nocompile, "compilation failed but no error was raised"});
|
||||
exit({ nocompile, "compilation failed but no error was raised" });
|
||||
|
||||
format_errors(File, Errors) ->
|
||||
lists:foreach(fun ({_, Each}) ->
|
||||
|
||||
@@ -110,8 +110,8 @@ run_on_definition_callbacks(Kind, Line, Module, Name, Args, Guards, Expr, S, CO)
|
||||
ok;
|
||||
_ ->
|
||||
Env = elixir_scope:to_ex_env({ Line, S }),
|
||||
Callbacks = 'Elixir.Module':get_attribute(Module, on_definition),
|
||||
[Mod:Fun(Env, Kind, Name, Args, Guards, Expr) || { Mod, Fun } <- Callbacks]
|
||||
elixir_module:eval_callbacks(Line, Module, on_definition,
|
||||
[Env, Kind, Name, Args, Guards, Expr], S)
|
||||
end.
|
||||
|
||||
%% Retrieve @file or fallback to default
|
||||
|
||||
@@ -155,7 +155,7 @@ expand_require(Meta, Receiver, { Name, Arity } = Tuple, Args, Module, Function,
|
||||
%% Expansion helpers
|
||||
|
||||
expand_macro_fun(Meta, Fun, Receiver, Name, Args, Module, Requires, SEnv) ->
|
||||
case (Receiver == Module) or is_element(Receiver, Requires) of
|
||||
case (Receiver == Module) or is_element(Receiver, Requires) or skip_requires(SEnv) of
|
||||
true -> ok;
|
||||
false ->
|
||||
Tuple = { unrequired_module, { Receiver, Name, length(Args), Requires } },
|
||||
@@ -182,12 +182,19 @@ expand_macro_named(Meta, Receiver, Name, Arity, Args, Module, Requires, SEnv) ->
|
||||
translate_expansion(Meta, Tree, S) ->
|
||||
{ TR, TS } = elixir_translator:translate_each(
|
||||
elixir_quote:linify(?line(Meta), Tree),
|
||||
S#elixir_scope{check_clauses=false}
|
||||
S#elixir_scope{
|
||||
check_clauses=false,
|
||||
macro_aliases=[] }
|
||||
),
|
||||
{ TR, TS#elixir_scope{check_clauses=S#elixir_scope.check_clauses} }.
|
||||
{ TR, TS#elixir_scope{
|
||||
check_clauses=S#elixir_scope.check_clauses,
|
||||
macro_aliases=S#elixir_scope.macro_aliases } }.
|
||||
|
||||
%% Helpers
|
||||
|
||||
skip_requires(#elixir_scope{check_requires=false}) -> true;
|
||||
skip_requires(_) -> false.
|
||||
|
||||
find_dispatch(Tuple, [{ Name, Values }|T]) ->
|
||||
case is_element(Tuple, Values) of
|
||||
true -> Name;
|
||||
|
||||
@@ -77,10 +77,10 @@ form_error(Meta, File, Module, Desc) ->
|
||||
|
||||
%% Shows a deprecation message
|
||||
|
||||
deprecation(Line, File, Message) -> deprecation(Line, File, Message, []).
|
||||
deprecation(Meta, File, Message) -> deprecation(Meta, File, Message, []).
|
||||
|
||||
deprecation(Line, File, Message, Args) ->
|
||||
io:format(file_format(Line, File, io_lib:format(Message, Args))).
|
||||
deprecation(Meta, File, Message, Args) ->
|
||||
io:format(file_format(?line(Meta), File, io_lib:format(Message, Args))).
|
||||
|
||||
%% Handle warnings and errors (called during module compilation)
|
||||
|
||||
@@ -109,6 +109,9 @@ handle_file_warning(_, File, {Line,erl_lint,{undefined_behaviour,Module}}) ->
|
||||
io:format(file_format(Line, File, Message))
|
||||
end;
|
||||
|
||||
handle_file_warning(_, _File, {Line,erl_lint,{unused_var,_Var}}) when Line =< 0 ->
|
||||
[];
|
||||
|
||||
handle_file_warning(_, File, {Line,erl_lint,{unused_var,Var}}) ->
|
||||
Message = format_error(erl_lint, { unused_var, format_var(Var) }),
|
||||
io:format(file_format(Line, File, Message));
|
||||
@@ -161,11 +164,17 @@ assert_function_scope(_Meta, _Kind, #elixir_scope{function=Function}) -> Functio
|
||||
raise(Meta, File, Kind, Message) when is_list(Meta) ->
|
||||
raise(?line(Meta), File, Kind, Message);
|
||||
|
||||
raise(none, File, Kind, Message) ->
|
||||
raise(0, 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).
|
||||
|
||||
file_format(0, File, Message) ->
|
||||
io_lib:format("~ts: ~ts~n", [File, Message]);
|
||||
|
||||
file_format(Line, File, Message) ->
|
||||
io_lib:format("~ts:~w: ~ts~n", [File, Line, Message]).
|
||||
|
||||
|
||||
@@ -196,11 +196,15 @@ translate({defmodule, Meta, [Ref, KV]}, S) ->
|
||||
|
||||
{ FRef, FS } = case TRef of
|
||||
{ atom, _, Module } ->
|
||||
FullModule = module_ref(Ref, Module, S#elixir_scope.module),
|
||||
FullModule = expand_module(Ref, Module, S),
|
||||
|
||||
RS = case elixir_aliases:nesting(S#elixir_scope.module, FullModule) of
|
||||
false -> S;
|
||||
Alias -> element(2, translate_each({ alias, Meta, [FullModule, [{ as, Alias }]] }, S))
|
||||
RS = case elixir_aliases:nesting_alias(S#elixir_scope.module, FullModule) of
|
||||
{ New, Old } ->
|
||||
S#elixir_scope{
|
||||
aliases=orddict:store(New, Old, S#elixir_scope.aliases),
|
||||
macro_aliases=orddict:store(New, Old, S#elixir_scope.macro_aliases)};
|
||||
false ->
|
||||
S
|
||||
end,
|
||||
|
||||
{
|
||||
@@ -327,14 +331,19 @@ rewrite_case_clauses([{do,[{in,_,[{'_',_,_},[false,nil]]}],False},{do,[{'_',_,_}
|
||||
rewrite_case_clauses(Clauses) ->
|
||||
Clauses.
|
||||
|
||||
module_ref(Raw, Module, Nesting) when is_atom(Raw); Nesting == nil ->
|
||||
expand_module(Raw, Module, #elixir_scope{module=Nesting}) when is_atom(Raw); Nesting == nil ->
|
||||
Module;
|
||||
|
||||
module_ref({ '__aliases__', _, ['Elixir'|_] }, Module, _Nesting) ->
|
||||
Module;
|
||||
expand_module({ '__aliases__', _, _ } = Alias, Module, S) ->
|
||||
case elixir_aliases:expand(Alias, S#elixir_scope.aliases, S#elixir_scope.macro_aliases) of
|
||||
Atom when is_atom(Atom) ->
|
||||
Module;
|
||||
Aliases when is_list(Aliases) ->
|
||||
elixir_aliases:concat([S#elixir_scope.module, Module])
|
||||
end;
|
||||
|
||||
module_ref(_Raw, Module, Nesting) ->
|
||||
elixir_aliases:concat([Nesting, Module]).
|
||||
expand_module(_Raw, Module, S) ->
|
||||
elixir_aliases:concat([S#elixir_scope.module, Module]).
|
||||
|
||||
is_reserved_data(moduledoc) -> true;
|
||||
is_reserved_data(doc) -> true;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
-module(elixir_module).
|
||||
-export([translate/4, compile/5, data_table/1, eval_quoted/4, format_error/1]).
|
||||
-export([translate/4, compile/5, data_table/1, eval_quoted/4,
|
||||
format_error/1, eval_callbacks/5]).
|
||||
-include("elixir.hrl").
|
||||
-compile({parse_transform, elixir_transform}).
|
||||
|
||||
@@ -133,7 +134,8 @@ eval_form(Line, Module, Block, Vars, RawS) ->
|
||||
S = scope_for_eval(Module, RawS),
|
||||
{ Value, NewS } = elixir_compiler:eval_forms([Block], Line, Vars, S),
|
||||
elixir_def_overridable:store_pending(Module),
|
||||
eval_callbacks(Line, Module, before_compile, [Module], NewS),
|
||||
Env = elixir_scope:to_ex_env({ Line, S }),
|
||||
eval_callbacks(Line, Module, before_compile, [Env], NewS),
|
||||
elixir_def_overridable:store_pending(Module),
|
||||
Value.
|
||||
|
||||
@@ -238,7 +240,8 @@ spec_for_macro(Else) -> Else.
|
||||
load_form(Line, Forms, S) ->
|
||||
elixir_compiler:module(Forms, S, fun(Module, Binary) ->
|
||||
EvalS = scope_for_eval(Module, S),
|
||||
eval_callbacks(Line, Module, after_compile, [Module, Binary], EvalS),
|
||||
Env = elixir_scope:to_ex_env({ Line, EvalS }),
|
||||
eval_callbacks(Line, Module, after_compile, [Env, Binary], EvalS),
|
||||
|
||||
case get(elixir_compiled) of
|
||||
Current when is_list(Current) ->
|
||||
@@ -321,20 +324,16 @@ else_clause() ->
|
||||
% HELPERS
|
||||
|
||||
eval_callbacks(Line, Module, Name, Args, RawS) ->
|
||||
S = RawS#elixir_scope{check_clauses=false},
|
||||
S = RawS#elixir_scope{check_clauses=false,check_requires=false},
|
||||
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 = { { '.', 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)}
|
||||
end,
|
||||
|
||||
{ Tree, _ } = elixir_translator:translate_each(Expr, Scope),
|
||||
{ Tree, _ } = elixir_dispatch:dispatch_require(Meta, M, F, Args, S, fun() ->
|
||||
apply(M, F, Args),
|
||||
{ { nil, 0 }, S }
|
||||
end),
|
||||
|
||||
try
|
||||
erl_eval:exprs([Tree], Binding)
|
||||
|
||||
@@ -553,10 +553,10 @@ build_sigil({ sigil, Line, Sigil, Parts, Modifiers }) ->
|
||||
build_bin_string({ bin_string, _Line, [H] }) when is_binary(H) -> H;
|
||||
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, [H] }) when is_binary(H) -> unicode:characters_to_list(H);
|
||||
build_list_string({ list_string, Line, Args }) ->
|
||||
Meta = [{line,Line}],
|
||||
{ { '.', Meta, [erlang, binary_to_list] }, Meta, [{ '<<>>', Meta, Args}] }.
|
||||
{ { '.', Meta, [unicode, characters_to_list] }, Meta, [{ '<<>>', Meta, Args}] }.
|
||||
|
||||
build_atom({ atom, _Line, Atom }) when is_atom(Atom) -> Atom;
|
||||
build_atom({ atom, Line, Args }) ->
|
||||
|
||||
@@ -52,15 +52,20 @@ do_quote({ unquote, _Meta, [Expr] }, #elixir_quote{unquote=true}, S) ->
|
||||
elixir_translator:translate_each(Expr, S);
|
||||
|
||||
do_quote({ 'alias!', _Meta, [Expr] }, Q, S) ->
|
||||
do_quote(Expr, Q#elixir_quote{expand_aliases=false}, S);
|
||||
do_quote(Expr, Q#elixir_quote{aliases_hygiene=false}, S);
|
||||
|
||||
do_quote({ '__aliases__', Meta, [H|T] } = Alias, #elixir_quote{aliases_hygiene=true} = Q, S) when is_atom(H) and (H /= 'Elixir') ->
|
||||
Annotation = case elixir_aliases:expand(Alias, S#elixir_scope.aliases, S#elixir_scope.macro_aliases) of
|
||||
Atom when is_atom(Atom) -> Atom;
|
||||
Aliases when is_list(Aliases) -> false
|
||||
end,
|
||||
|
||||
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),
|
||||
{ TAliases, SA } = do_quote([H|T], Q, S),
|
||||
|
||||
{ { tuple, Line, [
|
||||
{ atom, Line, '__aliases__' },
|
||||
meta(Meta, Q),
|
||||
meta([{alias,Annotation}|Meta], Q),
|
||||
TAliases
|
||||
] }, SA };
|
||||
|
||||
@@ -79,11 +84,11 @@ do_quote({ Left, Meta, nil }, Q, S) when is_atom(Left) ->
|
||||
Tuple = { tuple, Line, [
|
||||
{ atom, Line, Left },
|
||||
meta(Meta, Q),
|
||||
{ atom, Line, Q#elixir_quote.var_context }
|
||||
{ atom, Line, Q#elixir_quote.vars_hygiene }
|
||||
] },
|
||||
{ Tuple, S };
|
||||
|
||||
do_quote({ Name, Meta, ArgsOrAtom } = Tuple, #elixir_quote{expand_imports=true} = Q, S) when is_atom(Name) ->
|
||||
do_quote({ Name, Meta, ArgsOrAtom } = Tuple, #elixir_quote{imports_hygiene=true} = Q, S) when is_atom(Name) ->
|
||||
Arity = case is_atom(ArgsOrAtom) of
|
||||
true -> 0;
|
||||
false -> length(ArgsOrAtom)
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
-module(elixir_scope).
|
||||
-export([translate_var/5,
|
||||
build_erl_var/2, build_ex_var/2,
|
||||
build_erl_var/3, build_ex_var/3,
|
||||
build_erl_var/4, build_ex_var/4,
|
||||
serialize/1, deserialize/1,
|
||||
serialize_with_vars/2, deserialize_with_vars/2,
|
||||
to_erl_env/1, to_ex_env/1, filename/1,
|
||||
@@ -30,8 +28,8 @@ translate_var(Meta, Name, Kind, S, Callback) ->
|
||||
{ Else, _ } ->
|
||||
{ NewVar, NS } = if
|
||||
Kind /= nil -> build_erl_var(Line, S);
|
||||
Else -> build_erl_var(Line, Name, S);
|
||||
S#elixir_scope.noname -> build_erl_var(Line, Name, S);
|
||||
Else -> build_erl_var(Line, Name, Name, S);
|
||||
S#elixir_scope.noname -> build_erl_var(Line, Name, Name, S);
|
||||
true -> { { var, Line, Name }, S }
|
||||
end,
|
||||
RealName = element(3, NewVar),
|
||||
@@ -58,9 +56,6 @@ translate_var(Meta, Name, Kind, S, Callback) ->
|
||||
build_ex_var(Line, S) -> build_ex_var(Line, '', "_", S).
|
||||
build_erl_var(Line, S) -> build_erl_var(Line, '', "_", S).
|
||||
|
||||
build_ex_var(Line, Key, S) -> build_ex_var(Line, Key, Key, S).
|
||||
build_erl_var(Line, Key, S) -> build_erl_var(Line, Key, Key, S).
|
||||
|
||||
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} }.
|
||||
@@ -101,7 +96,8 @@ filename(Other) -> element(3, Other).
|
||||
serialize(S) ->
|
||||
elixir_tree_helpers:abstract_syntax(
|
||||
{ S#elixir_scope.file, S#elixir_scope.functions, S#elixir_scope.check_clauses,
|
||||
S#elixir_scope.requires, S#elixir_scope.macros, S#elixir_scope.aliases, S#elixir_scope.scheduled }
|
||||
S#elixir_scope.requires, S#elixir_scope.macros, S#elixir_scope.aliases,
|
||||
S#elixir_scope.macro_aliases, S#elixir_scope.scheduled }
|
||||
).
|
||||
|
||||
serialize_with_vars(Line, S) when is_integer(Line) ->
|
||||
@@ -119,7 +115,7 @@ serialize_with_vars(Line, S) when is_integer(Line) ->
|
||||
|
||||
deserialize(Tuple) -> deserialize_with_vars(Tuple, []).
|
||||
|
||||
deserialize_with_vars({ File, Functions, CheckClauses, Requires, Macros, Aliases, Scheduled }, Vars) ->
|
||||
deserialize_with_vars({ File, Functions, CheckClauses, Requires, Macros, Aliases, MacroAliases, Scheduled }, Vars) ->
|
||||
#elixir_scope{
|
||||
file=File,
|
||||
functions=Functions,
|
||||
@@ -127,6 +123,7 @@ deserialize_with_vars({ File, Functions, CheckClauses, Requires, Macros, Aliases
|
||||
requires=Requires,
|
||||
macros=Macros,
|
||||
aliases=Aliases,
|
||||
macro_aliases=MacroAliases,
|
||||
scheduled=Scheduled,
|
||||
vars=orddict:from_list(Vars),
|
||||
counter=[{'',length(Vars)}]
|
||||
|
||||
@@ -107,7 +107,8 @@ translate_each({ alias, Meta, [Ref, KV] }, S) ->
|
||||
{ as, false } ->
|
||||
{ Old, SR };
|
||||
{ as, Other } ->
|
||||
{ TOther, SA } = translate_each(Other, SR#elixir_scope{aliases=[]}),
|
||||
{ TOther, SA } = translate_each(no_alias_expansion(Other),
|
||||
SR#elixir_scope{aliases=[],macro_aliases=[]}),
|
||||
case TOther of
|
||||
{ atom, _, Atom } -> { Atom, SA };
|
||||
_ -> syntax_error(Meta, S#elixir_scope.file,
|
||||
@@ -127,7 +128,8 @@ translate_each({ alias, Meta, [Ref, KV] }, S) ->
|
||||
end,
|
||||
|
||||
{ { nil, ?line(Meta) }, SF#elixir_scope{
|
||||
aliases=orddict:store(New, Old, S#elixir_scope.aliases)
|
||||
aliases=orddict:store(New, Old, S#elixir_scope.aliases),
|
||||
macro_aliases=orddict:store(New, Old, S#elixir_scope.macro_aliases)
|
||||
} }
|
||||
end;
|
||||
_ ->
|
||||
@@ -213,8 +215,8 @@ translate_each({ '__CALLER__', Meta, Atom }, S) when is_atom(Atom) ->
|
||||
%% Aliases
|
||||
|
||||
translate_each({ '__aliases__', Meta, _ } = Alias, S) ->
|
||||
case elixir_aliases:expand(Alias, S#elixir_scope.aliases) of
|
||||
Atom when is_atom(Atom) -> { { atom, ?line(Meta), Atom}, S };
|
||||
case elixir_aliases:expand(Alias, S#elixir_scope.aliases, S#elixir_scope.macro_aliases) of
|
||||
Atom when is_atom(Atom) -> { { atom, ?line(Meta), Atom }, S };
|
||||
Aliases ->
|
||||
{ TAliases, SA } = translate_args(Aliases, S),
|
||||
|
||||
@@ -244,13 +246,25 @@ translate_each({ quote, Meta, [T] }, S) when is_list(T) ->
|
||||
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(Meta, VarContext,
|
||||
"invalid argument given for var_context in quote", S);
|
||||
Hygiene = case lists:keyfind(hygiene, 1, T) of
|
||||
{ hygiene, true } ->
|
||||
elixir_errors:deprecation(Meta, S#elixir_scope.file, "hygiene: true is deprecated, please set it to [] instead", []),
|
||||
[];
|
||||
{ hygiene, List } when is_list(List) ->
|
||||
List;
|
||||
{ hygiene, false } ->
|
||||
elixir_errors:deprecation(Meta, S#elixir_scope.file, "hygiene: false is deprecated, please set it to [vars: false] instead", []),
|
||||
[{vars,false}, {imports,false}, {aliases,false}];
|
||||
false ->
|
||||
case lists:keyfind(hygiene, 1, T) of
|
||||
{ hygiene, false } -> nil;
|
||||
[]
|
||||
end,
|
||||
|
||||
Vars = case lists:keyfind(var_context, 1, T) of
|
||||
{ var_context, VarContext } ->
|
||||
expand_var_context(Meta, VarContext, "invalid argument given for var_context in quote", S);
|
||||
false ->
|
||||
case lists:keyfind(vars, 1, Hygiene) of
|
||||
{ vars, false } -> nil;
|
||||
_ ->
|
||||
case S#elixir_scope.module of
|
||||
nil -> 'Elixir';
|
||||
@@ -259,20 +273,18 @@ translate_each({ quote, Meta, [T] }, S) when is_list(T) ->
|
||||
end
|
||||
end,
|
||||
|
||||
GetExpansion = fun(Key) ->
|
||||
GetExpansion = fun(Key, New) ->
|
||||
case lists:keyfind(Key, 1, T) of
|
||||
{ Key, Bool } when is_boolean(Bool) ->
|
||||
elixir_errors:deprecation(Meta, S#elixir_scope.file, "the ~s option for quote is deprecated, please use hygiene: [~s: ~s] instead", [Key, New, Bool]),
|
||||
Bool;
|
||||
false ->
|
||||
case lists:keyfind(hygiene, 1, T) of
|
||||
{ hygiene, Bool } when is_boolean(Bool) -> Bool;
|
||||
false -> true
|
||||
end
|
||||
not(lists:keyfind(New, 1, Hygiene) == { New, false })
|
||||
end
|
||||
end,
|
||||
|
||||
Aliases = GetExpansion(expand_aliases),
|
||||
Imports = GetExpansion(expand_imports),
|
||||
Aliases = GetExpansion(aliases_hygiene, aliases),
|
||||
Imports = GetExpansion(imports_hygiene, imports),
|
||||
|
||||
{ DefaultLine, DefaultFile } = case lists:keyfind(location, 1, T) of
|
||||
{ location, keep } -> { keep, keep };
|
||||
@@ -314,8 +326,8 @@ translate_each({ quote, Meta, [T] }, S) when is_list(T) ->
|
||||
_ -> true
|
||||
end,
|
||||
|
||||
Q = #elixir_quote{var_context=Context, line=TLine, unquote=Unquote,
|
||||
expand_aliases=Aliases, expand_imports=Imports},
|
||||
Q = #elixir_quote{vars_hygiene=Vars, line=TLine, unquote=Unquote,
|
||||
aliases_hygiene=Aliases, imports_hygiene=Imports},
|
||||
|
||||
elixir_quote:quote(TExprs, Q, SL);
|
||||
|
||||
@@ -472,6 +484,7 @@ translate_each({ { '.', _, [Expr] }, Meta, Args } = Original, S) ->
|
||||
{ TExpr, SE } = translate_each(Expr, S),
|
||||
case TExpr of
|
||||
{ atom, _, Atom } ->
|
||||
elixir_errors:deprecation(Meta, S#elixir_scope.file, "the :~s.() syntax is deprecated, please use ~s() instead", [Atom, Atom]),
|
||||
translate_each({ Atom, Meta, Args }, S);
|
||||
_ ->
|
||||
case elixir_partials:handle(Original, S) of
|
||||
@@ -544,6 +557,12 @@ expand_var_context(Meta, Alias, Msg, S) ->
|
||||
syntax_error(Meta, S#elixir_scope.file, "~ts, expected a compile time available alias or an atom", [Msg])
|
||||
end.
|
||||
|
||||
no_alias_expansion({ '__aliases__', Meta, [H|T] }) when (H /= 'Elixir') and is_atom(H) ->
|
||||
{ '__aliases__', Meta, ['Elixir',H|T] };
|
||||
|
||||
no_alias_expansion(Other) ->
|
||||
Other.
|
||||
|
||||
%% Translate args
|
||||
|
||||
%% Variables in arguments are not propagated from one
|
||||
|
||||
@@ -173,7 +173,7 @@ defmodule Binary.Inspect.ListTest do
|
||||
end
|
||||
|
||||
test :codepoints do
|
||||
assert inspect('é') == "[195,169]"
|
||||
assert inspect('é') == "'é'"
|
||||
end
|
||||
|
||||
test :empty do
|
||||
|
||||
@@ -18,7 +18,7 @@ bar '''
|
||||
end
|
||||
|
||||
test :utf8 do
|
||||
assert length(' ゆんゆん') == 13
|
||||
assert length(' ゆんゆん') == 5
|
||||
end
|
||||
|
||||
test :octals do
|
||||
@@ -37,10 +37,9 @@ bar '''
|
||||
assert '\xfF' == 'ÿ'
|
||||
assert '\x{A}' == '\n'
|
||||
assert '\x{e9}' == 'é'
|
||||
assert '\x{10F}' == [196,143]
|
||||
assert '\x{10FF}' == [225,131,191]
|
||||
assert '\x{10FFF}' == [240,144,191,191]
|
||||
assert '\x{10FFFF}' == [244,143,191,191]
|
||||
assert '\x{10F}' == [271]
|
||||
assert '\x{10FF}' == [4351]
|
||||
assert '\x{10FFF}' == [69631]
|
||||
assert '\x{10FFFF}' == [1114111]
|
||||
end
|
||||
|
||||
end
|
||||
@@ -61,8 +61,8 @@ defmodule CodeTest do
|
||||
end
|
||||
|
||||
test :string_to_ast do
|
||||
assert Code.string_to_ast("1 + 2") == { :ok, quote hygiene: false, line: 1, do: 1 + 2 }
|
||||
assert Code.string_to_ast("1 + 2; 3 + 4") == { :ok, quote hygiene: false, line: 1, do: (1 + 2; 3 + 4) }
|
||||
assert Code.string_to_ast("1 + 2") == { :ok, quote hygiene: [imports: false], line: 1, do: 1 + 2 }
|
||||
assert Code.string_to_ast("1 + 2; 3 + 4") == { :ok, quote hygiene: [imports: false], line: 1, do: (1 + 2; 3 + 4) }
|
||||
assert { :error, _ } = Code.string_to_ast("a.1")
|
||||
end
|
||||
|
||||
@@ -72,7 +72,7 @@ defmodule CodeTest do
|
||||
end
|
||||
|
||||
test :string_to_ast! do
|
||||
assert Code.string_to_ast!("1 + 2") == quote hygiene: false, line: 1, do: 1 + 2
|
||||
assert Code.string_to_ast!("1 + 2") == quote hygiene: [imports: false], line: 1, do: 1 + 2
|
||||
|
||||
assert_raise SyntaxError, fn ->
|
||||
Code.string_to_ast!("a.1")
|
||||
|
||||
@@ -10,14 +10,16 @@ defmodule FileTest do
|
||||
|
||||
# cp_r/c is managed in setup/teardown because if it is stored in
|
||||
# the repository, reltool can't build a release
|
||||
def setup do
|
||||
setup do
|
||||
src = fixture_path("cp_r")
|
||||
:file.make_symlink 'certainly/invalid', Path.join([src, "c"])
|
||||
:ok
|
||||
end
|
||||
|
||||
def teardown do
|
||||
teardown do
|
||||
src = fixture_path("cp_r")
|
||||
File.rm Path.join([src, "c"])
|
||||
:ok
|
||||
end
|
||||
|
||||
test :cp_with_src_file_and_dest_file do
|
||||
@@ -677,14 +679,16 @@ defmodule FileTest do
|
||||
|
||||
# cp_r/c is managed in setup/teardown because if it is stored in
|
||||
# the repository, reltool can't build a release
|
||||
def setup do
|
||||
setup do
|
||||
src = fixture_path("cp_r")
|
||||
:file.make_symlink 'certainly/invalid', Path.join([src, "c"])
|
||||
:ok
|
||||
end
|
||||
|
||||
def teardown do
|
||||
teardown do
|
||||
src = fixture_path("cp_r")
|
||||
File.rm Path.join([src, "c"])
|
||||
:ok
|
||||
end
|
||||
|
||||
test :rm_file do
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
Code.require_file "../../test_helper.exs", __FILE__
|
||||
|
||||
defmodule IO.ANSITest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
test :escape_single do
|
||||
assert IO.ANSI.escape("Hello, %{red}world!", true) ==
|
||||
"Hello, #{IO.ANSI.red}world!#{IO.ANSI.reset}"
|
||||
assert IO.ANSI.escape("Hello, %{ red }world!", true) ==
|
||||
"Hello, #{IO.ANSI.red}world!#{IO.ANSI.reset}"
|
||||
|
||||
end
|
||||
|
||||
test :escape_multiple do
|
||||
assert IO.ANSI.escape("Hello, %{red,bright}world!", true) ==
|
||||
"Hello, #{IO.ANSI.red}#{IO.ANSI.bright}world!#{IO.ANSI.reset}"
|
||||
assert IO.ANSI.escape("Hello, %{red, bright}world!", true) ==
|
||||
"Hello, #{IO.ANSI.red}#{IO.ANSI.bright}world!#{IO.ANSI.reset}"
|
||||
assert IO.ANSI.escape("Hello, %{red , bright}world!", true) ==
|
||||
"Hello, #{IO.ANSI.red}#{IO.ANSI.bright}world!#{IO.ANSI.reset}"
|
||||
end
|
||||
|
||||
test :no_emit do
|
||||
assert IO.ANSI.escape("Hello, %{red,bright}world!", false) ==
|
||||
"Hello, world!"
|
||||
end
|
||||
|
||||
test :fragment do
|
||||
assert IO.ANSI.escape("%{red}") == "#{IO.ANSI.red}#{IO.ANSI.reset}"
|
||||
assert IO.ANSI.escape_fragment("") == ""
|
||||
end
|
||||
|
||||
test :noop do
|
||||
assert IO.ANSI.escape("") == ""
|
||||
end
|
||||
|
||||
test :invalid do
|
||||
assert_raise ArgumentError, "invalid ANSI sequence specification: brigh", fn ->
|
||||
IO.ANSI.escape("%{brigh}, yes")
|
||||
end
|
||||
assert_raise ArgumentError, "invalid ANSI sequence specification: brigh", fn ->
|
||||
IO.ANSI.escape("%{brigh,red}, yes")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -26,4 +26,56 @@ defmodule Kernel.AliasTest do
|
||||
alias List, as: Nested
|
||||
assert Nested.flatten([[13]]) == [13]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Kernel.AliasNestingGenerator do
|
||||
defmacro create do
|
||||
quote do
|
||||
defmodule Parent do
|
||||
def a, do: :a
|
||||
end
|
||||
|
||||
defmodule Parent.Child do
|
||||
def b, do: Parent.a
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Kernel.AliasNestingTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
require Kernel.AliasNestingGenerator
|
||||
Kernel.AliasNestingGenerator.create
|
||||
|
||||
test :aliases_nesting do
|
||||
assert Parent.a == :a
|
||||
assert Parent.Child.b == :a
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Kernel.FullAliasNestingGenerator do
|
||||
defmacro create do
|
||||
quote do
|
||||
defmodule Parent do
|
||||
def a, do: :a
|
||||
end
|
||||
|
||||
defmodule Parent.Child do
|
||||
def b, do: Parent.a
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Kernel.FullAliasNestingTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
require Kernel.FullAliasNestingGenerator
|
||||
Kernel.FullAliasNestingGenerator.create
|
||||
|
||||
test :aliases_nesting do
|
||||
assert Parent.a == :a
|
||||
assert Parent.Child.b == :a
|
||||
end
|
||||
end
|
||||
|
||||
@@ -18,6 +18,18 @@ defmodule Kernel.CaseTest do
|
||||
assert vars_case(-1, -1) == { 0, 1 }
|
||||
end
|
||||
|
||||
test :nested_vars_match do
|
||||
x = { :error, { :ok, :done } }
|
||||
assert (case x do
|
||||
{ :ok, right } ->
|
||||
right
|
||||
{ _left, right } ->
|
||||
case right do
|
||||
{ :ok, right } -> right
|
||||
end
|
||||
end) == :done
|
||||
end
|
||||
|
||||
test :match_with_in do
|
||||
assert(case 3 do
|
||||
x in [1,2,3] ->
|
||||
|
||||
@@ -270,7 +270,7 @@ defmodule Kernel.ErrorsTest do
|
||||
end
|
||||
|
||||
test :macros_compiled_callback do
|
||||
assert [{Kernel.ErrorsTest,:__before_compile__,[Foo],_}|_] =
|
||||
assert [{Kernel.ErrorsTest,:__before_compile__,[Macro.Env[module: Foo]],_}|_] =
|
||||
rescue_stacktrace("defmodule Foo do\nModule.put_attribute(__MODULE__, :before_compile, Kernel.ErrorsTest)\nend")
|
||||
end
|
||||
|
||||
|
||||
@@ -15,12 +15,6 @@ defmodule Kernel.PartialApplicationTest do
|
||||
assert fun.(13, 6) == 7
|
||||
end
|
||||
|
||||
test :partial_with_atom_call_and_one_item do
|
||||
fun = :minus.(10, &1)
|
||||
assert fun.(5) == 5
|
||||
assert fun.(3) == 7
|
||||
end
|
||||
|
||||
test :partial_with_funcall_and_one_item do
|
||||
fun = minus(&1, &2)
|
||||
fun = fun.(10, &1)
|
||||
|
||||
@@ -90,7 +90,7 @@ defmodule Kernel.QuoteTest.VarHygiene do
|
||||
end
|
||||
|
||||
defmacro no_hygiene do
|
||||
quote [hygiene: false] do
|
||||
quote [hygiene: [vars: false]] do
|
||||
a = 1
|
||||
end
|
||||
end
|
||||
@@ -157,10 +157,43 @@ end
|
||||
defmodule Kernel.QuoteTest.AliasHygieneTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
alias Dict, as: SuperDict
|
||||
|
||||
defmacro dict do
|
||||
quote do: Dict.Bar
|
||||
end
|
||||
|
||||
defmacro super_dict do
|
||||
quote do: SuperDict.Bar
|
||||
end
|
||||
|
||||
test :annotate_aliases do
|
||||
assert quote(do: Foo.Bar) == { :__aliases__, [alias: false], [:Foo, :Bar] }
|
||||
assert quote(do: Dict.Bar) == { :__aliases__, [alias: false], [:Dict, :Bar] }
|
||||
assert quote(do: SuperDict.Bar) == { :__aliases__, [alias: Dict.Bar], [:SuperDict, :Bar] }
|
||||
assert quote(do: alias!(SuperDict.Bar)) == { :__aliases__, [], [:SuperDict, :Bar] }
|
||||
end
|
||||
|
||||
test :expand_aliases do
|
||||
assert Code.eval_quoted(quote do: Foo.Bar) == { Elixir.Foo.Bar, [] }
|
||||
assert Code.eval_quoted(quote do: alias!(Foo.Bar)) == { Foo.Bar, [] }
|
||||
assert Code.eval_quoted(quote expand_aliases: false, do: Foo.Bar) == { Foo.Bar, [] }
|
||||
assert Code.eval_quoted(quote do: SuperDict.Bar) == { Elixir.Dict.Bar, [] }
|
||||
assert Code.eval_quoted(quote do: alias!(SuperDict.Bar)) == { Elixir.SuperDict.Bar, [] }
|
||||
|
||||
assert Code.eval_quoted(quote do
|
||||
alias HashDict, as: SuperDict
|
||||
SuperDict.Bar
|
||||
end) == { Elixir.HashDict.Bar, [] }
|
||||
|
||||
assert Code.eval_quoted(quote do
|
||||
alias HashDict, as: Dict
|
||||
require Kernel.QuoteTest.AliasHygieneTest
|
||||
Kernel.QuoteTest.AliasHygieneTest.dict
|
||||
end) == { Elixir.Dict.Bar, [] }
|
||||
|
||||
assert Code.eval_quoted(quote do
|
||||
alias HashDict, as: SuperDict
|
||||
require Kernel.QuoteTest.AliasHygieneTest
|
||||
Kernel.QuoteTest.AliasHygieneTest.super_dict
|
||||
end) == { Elixir.Dict.Bar, [] }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ defmodule KernelTest do
|
||||
end
|
||||
|
||||
test :function_from___ENV__ do
|
||||
assert __ENV__.function == { :test_function_from___ENV__, 0 }
|
||||
assert __ENV__.function == { :test_function_from___ENV__, 1 }
|
||||
end
|
||||
|
||||
defp x(value) when value in [1,2,3], do: true
|
||||
@@ -347,7 +347,7 @@ defmodule KernelTest do
|
||||
test :invalid_match do
|
||||
a = 3
|
||||
assert_raise CaseClauseError, fn ->
|
||||
destructure [^a,b,c], a_list
|
||||
destructure [^a,_b,_c], a_list
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -3,13 +3,9 @@ Code.require_file "../test_helper.exs", __FILE__
|
||||
defmodule ListTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
test :brackets_function do
|
||||
assert :[].(1,2,3) == [1,2,3]
|
||||
end
|
||||
|
||||
test :optional_comma do
|
||||
assert :[].(1,) == [ 1, ]
|
||||
assert :[].(1,2,3,) == [1,2,3,]
|
||||
assert [1] == [ 1, ]
|
||||
assert [1,2,3] == [1,2,3,]
|
||||
end
|
||||
|
||||
test :partial_application do
|
||||
|
||||
@@ -82,6 +82,14 @@ defmodule MacroTest do
|
||||
assert Macro.expand(quote(do: :foo), __ENV__) == :foo
|
||||
end
|
||||
|
||||
defmacro local_macro do
|
||||
:local_macro
|
||||
end
|
||||
|
||||
test :expand_local_macro do
|
||||
assert Macro.expand(quote(do: local_macro), __ENV__) == :local_macro
|
||||
end
|
||||
|
||||
test :expand_with_imported_macro do
|
||||
assert Macro.expand(quote(do: 1 || false), __ENV__) == (quote var_context: Kernel do
|
||||
case 1 do
|
||||
@@ -288,7 +296,7 @@ defmodule MacroTest do
|
||||
|
||||
test :env_stacktrace do
|
||||
env = __ENV__.file("foo").line(12)
|
||||
assert env.stacktrace == [{ __MODULE__, :test_env_stacktrace, 0, [file: "foo", line: 12] }]
|
||||
assert env.stacktrace == [{ __MODULE__, :test_env_stacktrace, 1, [file: "foo", line: 12] }]
|
||||
env = env.function(nil)
|
||||
assert env.stacktrace == [{ __MODULE__, :__MODULE__, 0, [file: "foo", line: 12] }]
|
||||
env = env.module(nil)
|
||||
|
||||
@@ -16,12 +16,12 @@ defmodule ModuleTest.ToBeUsed do
|
||||
quote do: (def before_compile, do: true)
|
||||
end
|
||||
|
||||
defmacro __after_compile__(ModuleTest.ToUse, bin) when is_binary(bin) do
|
||||
defmacro __after_compile__(Macro.Env[module: ModuleTest.ToUse], bin) when is_binary(bin) do
|
||||
# IO.puts "HELLO"
|
||||
end
|
||||
|
||||
defmacro callback(target) do
|
||||
value = Module.get_attribute(target, :has_callback)
|
||||
defmacro callback(env) do
|
||||
value = Module.get_attribute(env.module, :has_callback)
|
||||
quote do
|
||||
name = :original_value
|
||||
args = [1]
|
||||
@@ -57,7 +57,7 @@ defmodule ModuleTest do
|
||||
Module.eval_quoted __MODULE__, contents, [], file: "sample.ex", line: 13
|
||||
|
||||
defmacrop in_module(block) do
|
||||
quote expand_aliases: false do
|
||||
quote do
|
||||
defmodule Temp, unquote(block)
|
||||
:code.purge(Temp)
|
||||
:code.delete(Temp)
|
||||
|
||||
@@ -71,11 +71,18 @@ defmodule OptionParserTest do
|
||||
end
|
||||
|
||||
test "parses more than one key/value options" do
|
||||
assert OptionParser.parse(["--source", "from_docs/", "--docs", "false"]) == { [source: "from_docs/", docs: false], [] }
|
||||
assert OptionParser.parse(["--source", "from_docs/", "--docs", "false"]) ==
|
||||
{ [source: "from_docs/", docs: false], [] }
|
||||
end
|
||||
|
||||
test "accumulates options accumulators" do
|
||||
assert OptionParser.parse(["--require", "foo", "--require", "bar", "baz"]) == { [require: "foo", require: "bar"], ["baz"] }
|
||||
test "overrides options by default" do
|
||||
assert OptionParser.parse(["--require", "foo", "--require", "bar", "baz"]) ==
|
||||
{ [require: "bar"], ["baz"] }
|
||||
end
|
||||
|
||||
test "keeps options on keep" do
|
||||
assert OptionParser.parse(["--require", "foo", "--require", "bar", "baz"], switches: [require: :keep]) ==
|
||||
{ [require: "foo", require: "bar"], ["baz"] }
|
||||
end
|
||||
|
||||
test "parses mixed options" do
|
||||
|
||||
@@ -116,7 +116,7 @@ defmodule ProtocolTest do
|
||||
assert_protocol_for(ProtocolTest.WithAll, Tuple, {Bar,2,3})
|
||||
assert_protocol_for(ProtocolTest.WithAll, BitString, "foo")
|
||||
assert_protocol_for(ProtocolTest.WithAll, BitString, <<1>>)
|
||||
assert_protocol_for(ProtocolTest.WithAll, PID, Process.self)
|
||||
assert_protocol_for(ProtocolTest.WithAll, PID, Kernel.self)
|
||||
assert_protocol_for(ProtocolTest.WithAll, Port, hd(:erlang.ports))
|
||||
assert_protocol_for(ProtocolTest.WithAll, Reference, make_ref)
|
||||
end
|
||||
|
||||
@@ -127,19 +127,6 @@ end
|
||||
defmodule Regex.ListTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
test :compile do
|
||||
assert is_record(Regex.compile!('foo'), Regex)
|
||||
assert is_regex(Regex.compile!('foo'))
|
||||
end
|
||||
|
||||
test :source do
|
||||
assert Regex.source(Regex.compile!('foo')) == "foo"
|
||||
end
|
||||
|
||||
test :opts do
|
||||
assert Regex.opts(Regex.compile!('foo', 'u')) == "u"
|
||||
end
|
||||
|
||||
test :match? do
|
||||
assert Regex.match?(%r(foo), 'foo')
|
||||
refute Regex.match?(%r(foo), 'FOO')
|
||||
|
||||
@@ -12,8 +12,8 @@ defmodule TupleTest do
|
||||
end
|
||||
|
||||
test :optional_comma do
|
||||
assert :{}.(1,) == { 1, }
|
||||
assert :{}.(1, 2, 3) == { 1, 2, 3, }
|
||||
assert { 1 } == { 1, }
|
||||
assert { 1, 2, 3 } == { 1, 2, 3, }
|
||||
end
|
||||
|
||||
test :partial_application do
|
||||
|
||||
@@ -7,7 +7,7 @@ defmodule Typespec.TypeTest do
|
||||
# definition and not on the hassles of handling test
|
||||
# module
|
||||
defmacrop test_module([{:do, block}]) do
|
||||
quote expand_aliases: false do
|
||||
quote do
|
||||
{ :module, T, _binary, result } = defmodule T do
|
||||
unquote(block)
|
||||
end
|
||||
@@ -17,6 +17,32 @@ defmodule Typespec.TypeTest do
|
||||
end
|
||||
end
|
||||
|
||||
test "invalid type specification" do
|
||||
assert_raise ArgumentError, "invalid type specification mytype = 1", fn ->
|
||||
test_module do
|
||||
@type mytype = 1
|
||||
end
|
||||
end
|
||||
assert_raise ArgumentError, "invalid type specification mytype = 1", fn ->
|
||||
test_module do
|
||||
@typep mytype = 1
|
||||
end
|
||||
end
|
||||
assert_raise ArgumentError, "invalid type specification mytype = 1", fn ->
|
||||
test_module do
|
||||
@opaque mytype = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "invalid function specification" do
|
||||
assert_raise ArgumentError, "invalid function type specification myfun = 1", fn ->
|
||||
test_module do
|
||||
@spec myfun = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "@type with no body (defaults to 'term')" do
|
||||
spec = test_module do
|
||||
@type mytype
|
||||
@@ -292,6 +318,18 @@ defmodule Typespec.TypeTest do
|
||||
] = Enum.sort(specs)
|
||||
end
|
||||
|
||||
test "block handling" do
|
||||
spec = test_module do
|
||||
spec = @spec foo((() -> [ integer ])) :: integer
|
||||
def foo(_), do: 1
|
||||
spec
|
||||
end
|
||||
assert {{:foo,1},
|
||||
{:type,_,:fun,[{:type,_,:product,[
|
||||
{:type,_,:fun,[{:type,_,:product,[]},{:type,_,:list,[{:type,_,:integer,[]}]}]}]},
|
||||
{:type,_,:integer,[]}]}} = spec
|
||||
end
|
||||
|
||||
# Conversion to AST
|
||||
|
||||
test "type_to_ast" do
|
||||
@@ -309,6 +347,7 @@ defmodule Typespec.TypeTest do
|
||||
(quote do: @type tuple_type() :: {integer()}),
|
||||
(quote do: @type ftype() :: (() -> any()) | (() -> integer()) | ((integer() -> integer()))),
|
||||
(quote do: @type cl() :: char_list()),
|
||||
(quote do: @type ab() :: as_boolean(term())),
|
||||
(quote do: @type vaf() :: (... -> any())),
|
||||
(quote do: @type rng() :: 1 .. 10),
|
||||
]
|
||||
@@ -405,14 +444,14 @@ defmodule Typespec.TypeTest do
|
||||
@typep b :: any
|
||||
@spec t(b) :: b
|
||||
def t(b), do: b
|
||||
@opaque c :: any
|
||||
@opaque c :: atom
|
||||
end
|
||||
|
||||
:code.delete(T)
|
||||
:code.purge(T)
|
||||
|
||||
assert [
|
||||
{:opaque, {:c,{:type,_,:any,[]},[]}},
|
||||
{:opaque, {:c,{:type,_,:atom,[]},[]}},
|
||||
{:type, {:a,{:type,_,:any,[]},[]}},
|
||||
{:typep, {:b,{:type,_,:any,[]},[]}},
|
||||
] = Kernel.Typespec.beam_types(binary)
|
||||
|
||||
@@ -69,7 +69,7 @@ vars_receive_test() ->
|
||||
|
||||
case_test() ->
|
||||
{true, _} = eval("case 1 do\n2 -> false\n1 -> true\nend"),
|
||||
{true, [{x,1}]} = eval("case 1 do\n{x,y} -> false\nx -> true\nend"),
|
||||
{true, [{x,1},{y,nil}]} = eval("case 1 do\n{x,y} -> false\nx -> true\nend"),
|
||||
{true, _} = eval("case {1,2} do;{3,4} -> false\n_ -> true\nend").
|
||||
|
||||
case_with_do_ambiguity_test() ->
|
||||
|
||||
@@ -13,11 +13,13 @@ defmodule ExUnit do
|
||||
|
||||
# 2) Next we create a new TestCase and use ExUnit.Case
|
||||
defmodule AssertionTest do
|
||||
# 3) Notice we pass async: true, this runs the test case in parallel
|
||||
# 3) Notice we pass async: true, this runs the test case
|
||||
# concurrently with other test cases
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
# 4) A test is a method which name finishes with _test
|
||||
def test_always_pass do
|
||||
# 4) A test is a function whose name starts with
|
||||
# test and receives a context
|
||||
def test_always_pass(_) do
|
||||
assert true
|
||||
end
|
||||
|
||||
@@ -33,9 +35,13 @@ defmodule ExUnit do
|
||||
|
||||
bin/elixir assertion_test.exs
|
||||
|
||||
## Assertions
|
||||
## Case, callbacks and assertions
|
||||
|
||||
Check ExUnit.Assertions for assertions documentation.
|
||||
Check `ExUnit.Case` and `ExUnit.Callbacks` for more information about
|
||||
defining test cases.
|
||||
|
||||
The `ExUnit.Assertions` module contains a set of macros to easily
|
||||
generate assertions with appropriate error messages.
|
||||
|
||||
## User config
|
||||
|
||||
@@ -123,11 +129,10 @@ defmodule ExUnit do
|
||||
ExUnit.Server.merge_options(options)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Registers a callback to be invoked every time a
|
||||
new ExUnit process is spawned.
|
||||
"""
|
||||
@doc false
|
||||
def after_spawn(callback) do
|
||||
IO.puts "ExUnit.after_spawn is deprecated, please use setup_all instead"
|
||||
Exception.print_stacktrace
|
||||
ExUnit.Server.add_after_spawn(callback)
|
||||
end
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ defmodule ExUnit.Assertions do
|
||||
quote do
|
||||
left = unquote(expected)
|
||||
right = unquote(actual)
|
||||
assert unquote(operator).(left, right), left, right, reason: unquote(text)
|
||||
assert unquote(operator)(left, right), left, right, reason: unquote(text)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
defmodule ExUnit.Callbacks do
|
||||
@moduledoc %B"""
|
||||
This module defines four callbacks: `setup_all`, `teardown_all`,
|
||||
`setup` and `teardown`. Those callbacks are defined via macros
|
||||
and receives a keyword list of metadata. The callback may
|
||||
optionally define extra data which will be available in the test
|
||||
cases.
|
||||
|
||||
## Examples
|
||||
|
||||
defmodule AssertionTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
setup do
|
||||
IO.puts "This is a setup callback"
|
||||
|
||||
# Returns extra metadata
|
||||
{ :ok, [hello: "world"] }
|
||||
end
|
||||
|
||||
setup context do
|
||||
# We can access the test name in the context
|
||||
IO.puts "Setting up: #{context[:test]}"
|
||||
|
||||
# The metadata returned by the previous setup as well
|
||||
assert context[:hello] == "world"
|
||||
|
||||
# No metadata
|
||||
:ok
|
||||
end
|
||||
|
||||
test "always pass" do
|
||||
assert true
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
|
||||
@doc false
|
||||
defmacro __using__(opts) do
|
||||
parent = opts[:parent]
|
||||
|
||||
quote do
|
||||
@exunit_setup []
|
||||
@exunit_teardown []
|
||||
@exunit_setup_all []
|
||||
@exunit_teardown_all []
|
||||
|
||||
@before_compile unquote(__MODULE__)
|
||||
@after_compile unquote(__MODULE__)
|
||||
import unquote(__MODULE__)
|
||||
|
||||
def __exunit__(:parent) do
|
||||
unquote(parent)
|
||||
end
|
||||
|
||||
def __exunit__(:setup, context) do
|
||||
context = __exunit_setup__ unquote(parent).__exunit__(:setup, context)
|
||||
ExUnit.Callbacks.__setup__(__MODULE__, context)
|
||||
end
|
||||
|
||||
def __exunit__(:teardown, context) do
|
||||
context = unquote(parent).__exunit__(:teardown, __exunit_teardown__ context)
|
||||
ExUnit.Callbacks.__teardown__(__MODULE__, context)
|
||||
end
|
||||
|
||||
def __exunit__(:setup_all, context) do
|
||||
context = __exunit_setup_all__ unquote(parent).__exunit__(:setup_all, context)
|
||||
ExUnit.Callbacks.__setup_all__(__MODULE__, context)
|
||||
end
|
||||
|
||||
def __exunit__(:teardown_all, context) do
|
||||
context = unquote(parent).__exunit__(:teardown_all, __exunit_teardown_all__ context)
|
||||
ExUnit.Callbacks.__teardown_all__(__MODULE__, context)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
defmacro __before_compile__(env) do
|
||||
[ compile_callbacks(env, :exunit_setup),
|
||||
compile_callbacks(env, :exunit_teardown),
|
||||
compile_callbacks(env, :exunit_setup_all),
|
||||
compile_callbacks(env, :exunit_teardown_all) ]
|
||||
end
|
||||
|
||||
defmacro setup(var // quote(do: _), block) do
|
||||
quote do
|
||||
name = :"__exunit_setup_#{length(@exunit_setup)}"
|
||||
defp name, [unquote(Macro.escape var)], [], unquote(Macro.escape block)
|
||||
@exunit_setup [name|@exunit_setup]
|
||||
end
|
||||
end
|
||||
|
||||
defmacro teardown(var // quote(do: _), block) do
|
||||
quote do
|
||||
name = :"__exunit_teardown_#{length(@exunit_teardown)}"
|
||||
defp name, [unquote(Macro.escape var)], [], unquote(Macro.escape block)
|
||||
@exunit_teardown [name|@exunit_teardown]
|
||||
end
|
||||
end
|
||||
|
||||
defmacro setup_all(var // quote(do: _), block) do
|
||||
quote do
|
||||
name = :"__exunit_setup_all_#{length(@exunit_setup_all)}"
|
||||
defp name, [unquote(Macro.escape var)], [], unquote(Macro.escape block)
|
||||
@exunit_setup_all [name|@exunit_setup_all]
|
||||
end
|
||||
end
|
||||
|
||||
defmacro teardown_all(var // quote(do: _), block) do
|
||||
quote do
|
||||
name = :"__exunit_teardown_all_#{length(@exunit_teardown_all)}"
|
||||
defp name, [unquote(Macro.escape var)], [], unquote(Macro.escape block)
|
||||
@exunit_teardown_all [name|@exunit_teardown_all]
|
||||
end
|
||||
end
|
||||
|
||||
## Helpers
|
||||
|
||||
def __merge__(_mod, other, :ok), do: other
|
||||
def __merge__(_mod, other, { :ok, data }), do: Keyword.merge(other, data)
|
||||
def __merge__(mod, _, failure) do
|
||||
raise "expected ExUnit callback in #{inspect mod} to return :ok " <>
|
||||
" or { :ok, data }, got #{inspect failure} instead"
|
||||
end
|
||||
|
||||
defp compile_callbacks(env, kind) do
|
||||
callbacks = Module.get_attribute(env.module, kind) |> Enum.reverse
|
||||
|
||||
acc =
|
||||
Enum.reduce callbacks, quote(do: context), fn(callback, acc) ->
|
||||
quote do
|
||||
context = unquote(acc)
|
||||
unquote(__MODULE__).__merge__(__MODULE__, context, unquote(callback)(context))
|
||||
end
|
||||
end
|
||||
|
||||
quote do
|
||||
defp unquote(:"__#{kind}__")(context), do: unquote(acc)
|
||||
end
|
||||
end
|
||||
|
||||
## Deprecated handling
|
||||
|
||||
@doc false
|
||||
def __after_compile__(env, _binary) do
|
||||
test_case = env.module
|
||||
|
||||
Enum.each [:setup, :teardown, :setup_all, :teardown_all], fn(kind) ->
|
||||
if Enum.any? 0..2, function_exported?(test_case, kind, &1) do
|
||||
IO.puts "[Warning] #{kind} callback in #{inspect test_case} is deprecated. Please use the #{kind} macro instead."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def __setup__(test_case, context) do
|
||||
cond do
|
||||
function_exported?(test_case, :setup, 2) ->
|
||||
test_case.setup(context, context[:test])
|
||||
function_exported?(test_case, :setup, 1) ->
|
||||
test_case.setup(context)
|
||||
function_exported?(test_case, :setup, 0) ->
|
||||
test_case.setup
|
||||
true ->
|
||||
context
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def __teardown__(test_case, context) do
|
||||
cond do
|
||||
function_exported?(test_case, :teardown, 2) ->
|
||||
test_case.teardown(context, context[:test])
|
||||
function_exported?(test_case, :teardown, 1) ->
|
||||
test_case.teardown(context)
|
||||
function_exported?(test_case, :teardown, 0) ->
|
||||
test_case.teardown
|
||||
true ->
|
||||
context
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def __teardown_all__(test_case, context) do
|
||||
cond do
|
||||
function_exported?(test_case, :teardown_all, 1) ->
|
||||
test_case.teardown_all(context)
|
||||
function_exported?(test_case, :teardown_all, 0) ->
|
||||
test_case.teardown_all
|
||||
true ->
|
||||
context
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def __setup_all__(test_case, context) do
|
||||
cond do
|
||||
function_exported?(test_case, :setup_all, 0) ->
|
||||
test_case.setup_all
|
||||
true ->
|
||||
context
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -9,49 +9,45 @@ defmodule ExUnit.Case do
|
||||
in parallel with others. Must be used for performance
|
||||
when your test cases do not change any global state;
|
||||
|
||||
## Callbacks
|
||||
This module automatically includes all callbacks defined
|
||||
in `ExUnit.Callbacks`. Read it for more information.
|
||||
|
||||
`ExUnit.Case` defines four callbacks:
|
||||
## Examples
|
||||
|
||||
* `setup_all()` and `teardown_all(context)` which are executed
|
||||
before and after all tests respectively;
|
||||
* `setup(context, test)` and `teardown(context, test)` which are
|
||||
executed before and after each test, receiving the test name
|
||||
as argument;
|
||||
defmodule AssertionTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
Such callbacks are useful to clean up any side-effect a test may cause,
|
||||
as for example, state in genservers, data on filesystem, or entries in
|
||||
a database. Data can be passed in between such callbacks as context,
|
||||
the context value returned by `setup_all` is passed down to all other
|
||||
callbacks. The value can then be updated in `setup` which is passed
|
||||
down to `teardown`.
|
||||
|
||||
## Examples
|
||||
|
||||
defmodule AssertionTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
def test_always_pass
|
||||
assert true
|
||||
end
|
||||
end
|
||||
def test_always_pass
|
||||
assert true
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
|
||||
@doc false
|
||||
defmacro __using__(opts // []) do
|
||||
if Keyword.get(opts, :async, false) do
|
||||
ExUnit.Server.add_async_case(__CALLER__.module)
|
||||
else
|
||||
ExUnit.Server.add_sync_case(__CALLER__.module)
|
||||
end
|
||||
async = Keyword.get(opts, :async, false)
|
||||
parent = Keyword.get(opts, :parent, __MODULE__)
|
||||
|
||||
quote do
|
||||
if unquote(async) do
|
||||
ExUnit.Server.add_async_case(__MODULE__)
|
||||
else
|
||||
ExUnit.Server.add_sync_case(__MODULE__)
|
||||
end
|
||||
|
||||
use ExUnit.Callbacks, parent: unquote(parent)
|
||||
|
||||
import ExUnit.Assertions
|
||||
import ExUnit.Case
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def __exunit__(kind, context) when kind in [:setup, :teardown, :setup_all, :teardown_all] do
|
||||
context
|
||||
end
|
||||
|
||||
@doc """
|
||||
Provides a convenient macro that allows a test to be
|
||||
defined with a string. This macro automatically inserts
|
||||
@@ -67,10 +63,10 @@ defmodule ExUnit.Case do
|
||||
end
|
||||
|
||||
"""
|
||||
defmacro test(message, contents) do
|
||||
defmacro test(message, var // quote(do: _), contents) do
|
||||
contents =
|
||||
case contents do
|
||||
[do: block] ->
|
||||
[do: _] ->
|
||||
quote do
|
||||
unquote(contents)
|
||||
:ok
|
||||
@@ -84,12 +80,15 @@ defmodule ExUnit.Case do
|
||||
|
||||
quote do
|
||||
message = unquote(message)
|
||||
|
||||
message = if is_binary(message) do
|
||||
:"test #{message}"
|
||||
else
|
||||
:"test_#{message}"
|
||||
end
|
||||
def message, [], [], do: unquote(Macro.escape contents)
|
||||
|
||||
def message, [unquote(Macro.escape var)], [], do:
|
||||
unquote(Macro.escape contents)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
defmodule ExUnit.CaseTemplate do
|
||||
@moduledoc """
|
||||
This module allows a developer to define a test case
|
||||
template to be used throughout his tests. This is useful
|
||||
when there are a set of functions that should be shared
|
||||
between tests or a set of setup/teardown callbacks.
|
||||
|
||||
By using this module, the callbacks and assertions
|
||||
available for regular test cases will also be available.
|
||||
|
||||
## Example
|
||||
|
||||
defmodule MyCase do
|
||||
use ExUnit.CaseTemplate
|
||||
|
||||
setup do
|
||||
IO.puts "This will run before each test that uses this case"
|
||||
end
|
||||
end
|
||||
|
||||
defmodule MyTest do
|
||||
use MyCase, async: true
|
||||
|
||||
test "truth" do
|
||||
assert true
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
|
||||
@doc false
|
||||
defmacro __using__(_) do
|
||||
quote do
|
||||
use ExUnit.Callbacks, parent: ExUnit.Case
|
||||
|
||||
import unquote(__MODULE__)
|
||||
import ExUnit.Assertions
|
||||
|
||||
defmacro __using__(opts) do
|
||||
unquote(__MODULE__).__parent__(__MODULE__, opts)
|
||||
end
|
||||
|
||||
defoverridable [__using__: 1]
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def __parent__(module, opts) do
|
||||
opts = Keyword.put(opts, :parent, module)
|
||||
|
||||
quote do
|
||||
use ExUnit.Case, unquote(opts)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Allows a developer to code to be invoked when
|
||||
this module is used.
|
||||
"""
|
||||
defmacro using(var // quote(do: _), do: block) do
|
||||
quote location: :keep do
|
||||
defmacro __using__(unquote(var) = opts) do
|
||||
parent = unquote(__MODULE__).__parent__(__MODULE__, opts)
|
||||
result = unquote(block)
|
||||
{ :__block__, [], [parent, result] }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -45,10 +45,10 @@ defmodule ExUnit.Runner do
|
||||
# cases counter and attempt to spawn new ones.
|
||||
defp wait_until_available(config) do
|
||||
receive do
|
||||
{ pid, :test_finished, { test_case, test, final } } ->
|
||||
{ _pid, :test_finished, { test_case, test, final } } ->
|
||||
config.formatter.test_finished(config.formatter_id, test_case, test, final)
|
||||
wait_until_available config
|
||||
{ pid, :case_finished, test_case } ->
|
||||
{ _pid, :case_finished, test_case } ->
|
||||
config.formatter.case_finished(config.formatter_id, test_case)
|
||||
loop config.update_taken_cases(&1-1)
|
||||
end
|
||||
@@ -72,9 +72,9 @@ defmodule ExUnit.Runner do
|
||||
|
||||
try do
|
||||
tests = tests_for(test_case)
|
||||
context = run_setup_all(test_case)
|
||||
context = test_case.__exunit__(:setup_all, [])
|
||||
Enum.each tests, run_test(config, pid, test_case, context, &1)
|
||||
run_teardown_all(test_case, context)
|
||||
test_case.__exunit__(:teardown_all, context)
|
||||
after
|
||||
pid <- { self, :case_finished, test_case }
|
||||
end
|
||||
@@ -84,10 +84,10 @@ defmodule ExUnit.Runner do
|
||||
config.formatter.test_started(config.formatter_id, test_case, test)
|
||||
|
||||
final = try do
|
||||
context = run_setup(test_case, setup_context, test)
|
||||
context = test_case.__exunit__(:setup, Keyword.put(setup_context, :test, test))
|
||||
|
||||
partial = try do
|
||||
apply test_case, test, []
|
||||
apply test_case, test, [context]
|
||||
nil
|
||||
rescue
|
||||
error1 ->
|
||||
@@ -97,7 +97,7 @@ defmodule ExUnit.Runner do
|
||||
{ kind1, error1, filtered_stacktrace }
|
||||
end
|
||||
|
||||
run_teardown(test_case, context, test)
|
||||
test_case.__exunit__(:teardown, context)
|
||||
partial
|
||||
rescue
|
||||
error2 ->
|
||||
@@ -110,52 +110,6 @@ defmodule ExUnit.Runner do
|
||||
pid <- { self, :test_finished, { test_case, test, final } }
|
||||
end
|
||||
|
||||
defp run_setup_all(test_case) do
|
||||
cond do
|
||||
function_exported?(test_case, :setup_all, 0) ->
|
||||
test_case.setup_all
|
||||
true ->
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
defp run_setup(test_case, context, test) do
|
||||
cond do
|
||||
function_exported?(test_case, :setup, 2) ->
|
||||
test_case.setup(context, test)
|
||||
function_exported?(test_case, :setup, 1) ->
|
||||
test_case.setup(context)
|
||||
function_exported?(test_case, :setup, 0) ->
|
||||
test_case.setup
|
||||
true ->
|
||||
context
|
||||
end
|
||||
end
|
||||
|
||||
defp run_teardown(test_case, context, test) do
|
||||
cond do
|
||||
function_exported?(test_case, :teardown, 2) ->
|
||||
test_case.teardown(context, test)
|
||||
function_exported?(test_case, :teardown, 1) ->
|
||||
test_case.teardown(context)
|
||||
function_exported?(test_case, :teardown, 0) ->
|
||||
test_case.teardown
|
||||
true ->
|
||||
context
|
||||
end
|
||||
end
|
||||
|
||||
defp run_teardown_all(test_case, context) do
|
||||
cond do
|
||||
function_exported?(test_case, :teardown_all, 1) ->
|
||||
test_case.teardown_all(context)
|
||||
function_exported?(test_case, :teardown_all, 0) ->
|
||||
test_case.teardown_all
|
||||
true ->
|
||||
context
|
||||
end
|
||||
end
|
||||
|
||||
## Helpers
|
||||
|
||||
defp take_async_cases(Config[] = config, count) do
|
||||
@@ -176,7 +130,14 @@ defmodule ExUnit.Runner do
|
||||
|
||||
defp tests_for(mod) do
|
||||
exports = mod.__info__(:functions)
|
||||
lc { function, 0 } inlist exports, is_test?(atom_to_list(function)), do: function
|
||||
|
||||
lc { function, 0 } inlist exports, is_test?(atom_to_list(function)) do
|
||||
IO.puts "Test function #{inspect mod}.#{function} with arity 0 is no longer supported. Use the test macro instead."
|
||||
end
|
||||
|
||||
lc { function, 1 } inlist exports, is_test?(atom_to_list(function)) do
|
||||
function
|
||||
end
|
||||
end
|
||||
|
||||
defp is_test?('test_' ++ _), do: true
|
||||
|
||||
@@ -1,40 +1,52 @@
|
||||
Code.require_file "../../test_helper.exs", __FILE__
|
||||
|
||||
defmodule ExUnit.CallbacksTest do
|
||||
use ExUnit.Case, sync: false
|
||||
use ExUnit.Case
|
||||
|
||||
def setup_all do
|
||||
[my_context: :setup_all]
|
||||
setup_all do
|
||||
{ :ok, [context: :setup_all] }
|
||||
end
|
||||
|
||||
def setup([my_context: context], test) do
|
||||
setup do
|
||||
{ :ok, [initial_setup: true] }
|
||||
end
|
||||
|
||||
setup context do
|
||||
assert context[:initial_setup]
|
||||
assert context[:context] == :setup_all
|
||||
{ :ok, [context: :setup] }
|
||||
end
|
||||
|
||||
setup context do
|
||||
if Process.get(:ex_unit_callback) do
|
||||
raise "ex_unit_callback was not cleaned"
|
||||
else
|
||||
Process.put(:ex_unit_callback, test)
|
||||
Process.put(:ex_unit_callback, context[:test])
|
||||
end
|
||||
|
||||
assert context == :setup_all
|
||||
|
||||
[my_context: :setup]
|
||||
:ok
|
||||
end
|
||||
|
||||
def teardown([my_context: context], test) do
|
||||
assert context == :setup
|
||||
assert Process.get(:ex_unit_callback) == test
|
||||
teardown context do
|
||||
assert context[:context] == :setup
|
||||
:ok
|
||||
end
|
||||
|
||||
teardown context do
|
||||
assert Process.get(:ex_unit_callback) == context[:test]
|
||||
Process.delete(:ex_unit_callback)
|
||||
:ok
|
||||
end
|
||||
|
||||
def teardown_all([my_context: context]) do
|
||||
assert context == :setup_all
|
||||
teardown_all context do
|
||||
assert context[:context] == :setup_all
|
||||
:ok
|
||||
end
|
||||
|
||||
test :callback do
|
||||
assert Process.get(:ex_unit_callback) == :test_callback
|
||||
test "callbacks can run custom code" do
|
||||
assert Process.get(:ex_unit_callback) == :"test callbacks can run custom code"
|
||||
end
|
||||
|
||||
# Ensure we run at least two tests, so setup is run twice
|
||||
test :ok do
|
||||
assert :ok == :ok
|
||||
test "receives context from callback", context do
|
||||
assert context[:context] == :setup
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
Code.require_file "../../test_helper.exs", __FILE__
|
||||
|
||||
defmodule ExUnit.SampleCase do
|
||||
use ExUnit.CaseTemplate
|
||||
|
||||
using _ do
|
||||
quote do
|
||||
import unquote(__MODULE__)
|
||||
end
|
||||
end
|
||||
|
||||
def hello do
|
||||
"world"
|
||||
end
|
||||
|
||||
setup_all do
|
||||
{ :ok, [context: :setup_all] }
|
||||
end
|
||||
|
||||
setup context do
|
||||
assert context[:context] == :setup_all
|
||||
{ :ok, [context: :setup] }
|
||||
end
|
||||
|
||||
teardown context do
|
||||
assert context[:context] == :setup
|
||||
:ok
|
||||
end
|
||||
|
||||
teardown_all context do
|
||||
assert context[:context] == :setup_all
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defmodule ExUnit.CaseTemplateTest do
|
||||
use ExUnit.SampleCase, async: true
|
||||
|
||||
test "receives context from parent case", context do
|
||||
assert context[:context] == :setup
|
||||
end
|
||||
|
||||
test "using code is executed" do
|
||||
assert hello == "world"
|
||||
end
|
||||
end
|
||||
@@ -31,10 +31,6 @@ end
|
||||
defmodule ExUnitTest do
|
||||
use ExUnit.Case, async: false
|
||||
|
||||
test "it runs after_spawn hooks" do
|
||||
assert Process.get(:after_spawn) == :ex_unit
|
||||
end
|
||||
|
||||
test "it can read user config" do
|
||||
File.write("ex_unit.test.config","[extra_option: true]")
|
||||
assert ExUnit.user_options("nosuchfile.config") == []
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
# Configure ExUnit, no options supported yet.
|
||||
ExUnit.start []
|
||||
ExUnit.after_spawn fn -> Process.put(:after_spawn, :ex_unit) end
|
||||
|
||||
+2
-2
@@ -119,8 +119,8 @@ defmodule IEx do
|
||||
|
||||
defp ensure_module_exists(node, mod) do
|
||||
unless :rpc.call node, :code, :is_loaded, [mod] do
|
||||
{m,b,f} = :code.get_object_code mod
|
||||
{:module, mod} = :rpc.call node, :code, :load_binary, [m,f,b]
|
||||
{ m, b, f } = :code.get_object_code mod
|
||||
{ :module, _ } = :rpc.call node, :code, :load_binary, [m,f,b]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
+7
-12
@@ -11,7 +11,7 @@ defmodule IEx.CLI do
|
||||
else
|
||||
:user.start
|
||||
IO.puts "Warning: could not run smart terminal, falling back to dumb one"
|
||||
IEx.start
|
||||
IEx.start([], fn -> :elixir.start_cli end)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -29,20 +29,15 @@ defmodule IEx.CLI do
|
||||
end
|
||||
|
||||
defp tty do
|
||||
remote =
|
||||
if remsh = get_remsh(:init.get_plain_arguments) do
|
||||
unless is_alive do
|
||||
raise ArgumentError, message: "In order to use --remsh, you need to name the current node"
|
||||
end
|
||||
if is_atom(remsh), do: remsh, else: binary_to_atom(remsh)
|
||||
end
|
||||
|
||||
function = fn ->
|
||||
IEx.start([], fn -> Kernel.CLI.wait_until_finished end)
|
||||
IEx.start([], fn -> :elixir.start_cli end)
|
||||
end
|
||||
|
||||
args =
|
||||
if remote do
|
||||
if remote = get_remsh(:init.get_plain_arguments) do
|
||||
unless is_alive do
|
||||
raise ArgumentError, message: "In order to use --remsh, you need to name the current node"
|
||||
end
|
||||
{ remote, :erlang, :apply, [function, []] }
|
||||
else
|
||||
{ :erlang, :apply, [function, []] }
|
||||
@@ -51,7 +46,7 @@ defmodule IEx.CLI do
|
||||
:user_drv.start([:"tty_sl -c -e", args])
|
||||
end
|
||||
|
||||
defp get_remsh(['--remsh',h|_]), do: list_to_binary(h)
|
||||
defp get_remsh(['--remsh',h|_]), do: list_to_atom(h)
|
||||
defp get_remsh([_|t]), do: get_remsh(t)
|
||||
defp get_remsh([]), do: nil
|
||||
end
|
||||
|
||||
@@ -190,7 +190,7 @@ defmodule IEx.Helpers do
|
||||
if doc do
|
||||
IO.write "\n" <> print_signature(doc)
|
||||
else
|
||||
IO.puts "No docs for #{inspect module}#.{function}/#{arity} have been found"
|
||||
IO.puts "No docs for #{inspect module}.#{function}/#{arity} have been found"
|
||||
end
|
||||
else
|
||||
IO.puts "#{inspect module} was not compiled with docs"
|
||||
|
||||
+27
-12
@@ -5,18 +5,11 @@ defmodule Mix.CLI do
|
||||
Runs Mix according to the command line arguments.
|
||||
"""
|
||||
def run(args // System.argv) do
|
||||
Mix.Local.append_tasks
|
||||
|
||||
args = load_mixfile(args)
|
||||
{ task, args } = get_task(args)
|
||||
|
||||
if Mix.Project.get do
|
||||
Mix.Task.run "loadpaths", ["--no-check"]
|
||||
Mix.Task.reenable "loadpaths"
|
||||
Mix.Task.reenable "deps.loadpaths"
|
||||
if help?(args) do
|
||||
display_banner()
|
||||
else
|
||||
proceed(args)
|
||||
end
|
||||
|
||||
run_task task, args
|
||||
end
|
||||
|
||||
defp load_mixfile(args) do
|
||||
@@ -34,7 +27,7 @@ defmodule Mix.CLI do
|
||||
end
|
||||
|
||||
defp get_task([]) do
|
||||
{ Mix.project[:default_task] || "compile", [] }
|
||||
{ Mix.project[:default_task] || "run", [] }
|
||||
end
|
||||
|
||||
defp run_task(name, args) do
|
||||
@@ -54,4 +47,26 @@ defmodule Mix.CLI do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp proceed(args) do
|
||||
Mix.Local.append_tasks
|
||||
|
||||
args = load_mixfile(args)
|
||||
{ task, args } = get_task(args)
|
||||
|
||||
if Mix.Project.get do
|
||||
Mix.Task.run "loadpaths", ["--no-check"]
|
||||
Mix.Task.reenable "loadpaths"
|
||||
Mix.Task.reenable "deps.loadpaths"
|
||||
end
|
||||
|
||||
run_task task, args
|
||||
end
|
||||
|
||||
defp display_banner() do
|
||||
run_task "help", []
|
||||
end
|
||||
|
||||
defp help?([first_arg|_]) when first_arg in ["--help", "-h", "-help"], do: true
|
||||
defp help?(_), do: false
|
||||
end
|
||||
@@ -33,7 +33,7 @@ defmodule Mix.Deps.Lock do
|
||||
%b("#{app}": #{inspect rev, raw: true})
|
||||
end
|
||||
|
||||
File.write! lockfile, "[ " <> lines <> " ]"
|
||||
File.write! lockfile, "[ " <> lines <> " ]\n"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ defmodule Mix.Generator do
|
||||
If the file already exists, asks for user confirmation.
|
||||
"""
|
||||
def create_file(path, contents) do
|
||||
Mix.shell.info "* creating #{path}"
|
||||
Mix.shell.info "%{green}* creating%{reset} #{path}"
|
||||
|
||||
if overwriting?(path) do
|
||||
File.write! path, contents
|
||||
@@ -19,7 +19,7 @@ defmodule Mix.Generator do
|
||||
Creates a directory if one does not exist yet.
|
||||
"""
|
||||
def create_directory(path) do
|
||||
Mix.shell.info "* creating #{path}"
|
||||
Mix.shell.info "%{green}* creating%{reset} #{path}"
|
||||
File.mkdir_p! path
|
||||
end
|
||||
|
||||
|
||||
@@ -40,8 +40,8 @@ defmodule Mix.Project do
|
||||
|
||||
# Invoked after each Mix.Project is compiled.
|
||||
@doc false
|
||||
def __after_compile__(module, _binary) do
|
||||
push module
|
||||
def __after_compile__(env, _binary) do
|
||||
push env.module
|
||||
end
|
||||
|
||||
# Push a project into the project stack. Only
|
||||
@@ -108,13 +108,6 @@ defmodule Mix.Project do
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def sources do
|
||||
IO.puts "Mix.Project.sources is deprecated, please use Mix.Project.config_files instead"
|
||||
Exception.print_stacktrace
|
||||
config_files
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a list of project config files (mix.exs and mix.lock).
|
||||
"""
|
||||
@@ -136,15 +129,17 @@ defmodule Mix.Project do
|
||||
|
||||
defp default_config do
|
||||
[ compile_path: "ebin",
|
||||
elixirc_exts: [:ex],
|
||||
default_env: [test: :test],
|
||||
default_task: "compile",
|
||||
default_task: "run",
|
||||
deps_path: "deps",
|
||||
erlc_paths: ["src"],
|
||||
elixirc_exts: [:ex],
|
||||
elixirc_paths: ["lib"],
|
||||
elixirc_watch_exts: [:ex, :eex, :exs],
|
||||
lockfile: "mix.lock",
|
||||
prepare_task: "app.start",
|
||||
elixirc_paths: ["lib"],
|
||||
elixirc_watch_exts: [:ex, :eex, :exs] ]
|
||||
erlc_paths: ["src"],
|
||||
erlc_include_path: "include",
|
||||
erlc_options: [:debug_info] ]
|
||||
end
|
||||
|
||||
defp get_project_config(nil), do: []
|
||||
|
||||
@@ -11,14 +11,8 @@ defmodule Mix.SCM.Path do
|
||||
end
|
||||
|
||||
def accepts_options(opts) do
|
||||
cond do
|
||||
raw = opts[:path] ->
|
||||
Keyword.put opts, :dest, Path.expand(raw)
|
||||
raw = opts[:raw] ->
|
||||
IO.puts "The option :raw is deprecated in favor of :path in mix deps"
|
||||
Keyword.put opts, :dest, Path.expand(raw)
|
||||
true ->
|
||||
nil
|
||||
if raw = opts[:path] do
|
||||
Keyword.put opts, :dest, Path.expand(raw)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -18,14 +18,14 @@ defmodule Mix.Shell.IO do
|
||||
Writes a message to the shell followed by new line.
|
||||
"""
|
||||
def info(message) do
|
||||
IO.puts message
|
||||
IO.puts IO.ANSI.escape(message)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Writes an error message to the shell followed by new line.
|
||||
"""
|
||||
def error(message) do
|
||||
IO.puts :stderr, message
|
||||
IO.puts :stderr, IO.ANSI.escape "%{red,bright}#{message}"
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -34,6 +34,6 @@ defmodule Mix.Shell.IO do
|
||||
regex `%r/^Y(es)?$/i`.
|
||||
"""
|
||||
def yes?(message) do
|
||||
IO.gets(message <> " [Yn] ") =~ %r/^(Y(es)?)?$/i
|
||||
IO.gets(message <> IO.ANSI.escape(" [Yn] ")) =~ %r/^(Y(es)?)?$/i
|
||||
end
|
||||
end
|
||||
@@ -50,14 +50,14 @@ defmodule Mix.Shell.Process do
|
||||
Simply forwards the message to the current process.
|
||||
"""
|
||||
def info(message) do
|
||||
self <- { :mix_shell, :info, [message] }
|
||||
self <- { :mix_shell, :info, [IO.ANSI.escape(message, false)] }
|
||||
end
|
||||
|
||||
@doc """
|
||||
Simply forwards the message to the current process.
|
||||
"""
|
||||
def error(message) do
|
||||
self <- { :mix_shell, :error, [message] }
|
||||
self <- { :mix_shell, :error, [IO.ANSI.escape(message, false)] }
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -70,7 +70,7 @@ defmodule Mix.Shell.Process do
|
||||
process input given. Value must be true or false.
|
||||
"""
|
||||
def yes?(message) do
|
||||
self <- { :mix_shell, :yes?, [message] }
|
||||
self <- { :mix_shell, :yes?, [IO.ANSI.escape(message, false)] }
|
||||
|
||||
receive do
|
||||
{ :mix_shell_input, :yes?, response } -> response
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
defmodule Mix.Tasks.Clean do
|
||||
use Mix.Task
|
||||
alias Mix.Tasks.Compile.Erlang
|
||||
|
||||
@shortdoc "Clean generated application files"
|
||||
|
||||
@@ -14,6 +15,12 @@ defmodule Mix.Tasks.Clean do
|
||||
def run(args) do
|
||||
{ opts, _ } = OptionParser.parse(args)
|
||||
File.rm_rf Mix.project[:compile_path] || "ebin"
|
||||
|
||||
Enum.each Mix.project[:erlc_paths], fn(source_path) ->
|
||||
pairs = Erlang.extract_stale_pairs(source_path, [:yrl, :xrl], source_path, :erl, true)
|
||||
Enum.each pairs, fn { _, output } -> File.rm_rf output end
|
||||
end
|
||||
|
||||
if opts[:all], do: Mix.Task.run("deps.clean")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
defmodule Mix.Tasks.Compile.Erlang do
|
||||
alias :epp, as: Epp
|
||||
alias :digraph, as: Graph
|
||||
alias :digraph_utils, as: GraphUtils
|
||||
alias Mix.Utils
|
||||
|
||||
use Mix.Task
|
||||
|
||||
@hidden true
|
||||
@@ -7,57 +12,200 @@ defmodule Mix.Tasks.Compile.Erlang do
|
||||
@moduledoc """
|
||||
A task to compile Erlang source files.
|
||||
|
||||
When this task runs, it will first check the mod times of
|
||||
all of the files. Every file will checked, if file or
|
||||
file dependencies, like include files, was changed.
|
||||
If file of his dependencies haven't been changed since the
|
||||
last compilation, it will not compile. If file or one of his
|
||||
dependency has changed, it will compile.
|
||||
|
||||
For this reason, this task touches your `:compile_path`
|
||||
directory and sets the modification time to the current
|
||||
time and date at the end of each compilation. You can
|
||||
force compilation regardless of mod times by passing
|
||||
the `--force` option.
|
||||
|
||||
## Command line options
|
||||
|
||||
* `--force` - forces compilation regardless of module times
|
||||
|
||||
## Configuration
|
||||
|
||||
* `ERL_COMPILER_OPTIONS` - can be used to give default compile options.
|
||||
It's value must be a valid Erlang term. If the value is a list, it will
|
||||
be used as is. If it is not a list, it will be put into a list.
|
||||
|
||||
## Configuration
|
||||
* `:erlc_paths` - directories to find source files.
|
||||
Defaults to `["src"]`, can be configured as:
|
||||
|
||||
* `:erlc_options` - compilation options that applies to Erlang compiler
|
||||
By default, the following options are on: `[:verbose, :report_errors, :report_warnings]`
|
||||
[erlc_paths: ["src", "other"]]
|
||||
|
||||
* `:erlc_include_path` - directory for adding include files.
|
||||
Defaults to `"include"`, can be configured as:
|
||||
|
||||
[`erlc_include_path`: "other"]
|
||||
|
||||
* `:erlc_options` - compilation options that applies to Erlang's
|
||||
compiler. `:debug_info` is enabled by default.
|
||||
|
||||
There are many available options here:
|
||||
http://www.erlang.org/doc/man/compile.html#file-2
|
||||
"""
|
||||
def run(_) do
|
||||
project = Mix.project
|
||||
|
||||
files = Mix.Utils.extract_files(project[:erlc_paths], [:erl])
|
||||
compile_path = project[:compile_path]
|
||||
defrecord Erl, file: nil, module: nil, behaviours: [], compile: [],
|
||||
includes: [], mtime: nil, invalid: false
|
||||
|
||||
def run(args) do
|
||||
{ opts, _ } = OptionParser.parse(args, switches: [force: :boolean])
|
||||
|
||||
project = Mix.project
|
||||
source_paths = project[:erlc_paths]
|
||||
files = Mix.Utils.extract_files(source_paths, [:erl])
|
||||
compile_path = to_erl_file project[:compile_path]
|
||||
include_path = to_erl_file project[:erlc_include_path]
|
||||
|
||||
erlc_options = project[:erlc_options] || []
|
||||
erlc_options = erlc_options ++ [{:outdir, compile_path}, {:i, include_path}, :report]
|
||||
erlc_options = Enum.map erlc_options, fn
|
||||
{ kind, dir } when kind in [:i, :outdit] ->
|
||||
{ kind, to_erl_file(dir) }
|
||||
opt ->
|
||||
opt
|
||||
end
|
||||
|
||||
files = files |> scan_sources(include_path, source_paths) |> sort_dependencies
|
||||
|
||||
unless opts[:force] do
|
||||
files = Enum.filter(files, requires_compilation?(compile_path, &1))
|
||||
end
|
||||
|
||||
if files == [] do
|
||||
:noop
|
||||
else
|
||||
Mix.Utils.preserving_mtime(compile_path, fn ->
|
||||
Utils.preserving_mtime(compile_path, fn ->
|
||||
File.mkdir_p! compile_path
|
||||
compile_files project, files, compile_path
|
||||
compile_files files, compile_path, erlc_options
|
||||
end)
|
||||
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp compile_files(project, files, compile_path) do
|
||||
erlc_options = project[:erlc_options] || []
|
||||
defp scan_sources(files, include_path, source_paths) do
|
||||
include_paths = [include_path | source_paths]
|
||||
Enum.reduce(files, [], scan_source(&2, &1, include_paths)) |> Enum.reverse
|
||||
end
|
||||
|
||||
erlc_options = Enum.map erlc_options, fn(opt) ->
|
||||
case opt do
|
||||
{ :i, dir } -> { :i, Path.expand(dir) |> binary_to_list }
|
||||
_ -> opt
|
||||
defp scan_source(acc, file, include_paths) do
|
||||
erl_file = Erl[file: file, module: Path.basename(file, ".erl")]
|
||||
|
||||
case Epp.parse_file(to_erl_file(file), include_paths, []) do
|
||||
{ :ok, forms } ->
|
||||
[List.foldl(tl(forms), erl_file, do_form(file, &1, &2)) | acc]
|
||||
{ :error, _error } ->
|
||||
acc
|
||||
end
|
||||
end
|
||||
|
||||
defp do_form(file, form, Erl[] = erl) do
|
||||
case form do
|
||||
{:attribute, _, :file, {include_file, _}} when file != include_file ->
|
||||
if File.regular?(include_file) do
|
||||
erl.update_includes [include_file|&1]
|
||||
else
|
||||
erl
|
||||
end
|
||||
{:attribute, _, :behaviour, behaviour} ->
|
||||
erl.update_behaviours [behaviour|&1]
|
||||
{:attribute, _, :compile, value} ->
|
||||
erl.update_compile [value|&1]
|
||||
_ ->
|
||||
erl
|
||||
end
|
||||
end
|
||||
|
||||
defp sort_dependencies(erls) do
|
||||
graph = Graph.new
|
||||
|
||||
lc erl inlist erls do
|
||||
Graph.add_vertex(graph, erl.module, erl)
|
||||
end
|
||||
|
||||
lc erl inlist erls do
|
||||
lc b inlist erl.behaviours, do: Graph.add_edge(graph, b, erl.module)
|
||||
lc c inlist erl.compile do
|
||||
case c do
|
||||
{:parse_transform, transform} -> Graph.add_edge(graph, transform, erl.module)
|
||||
_ -> :ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
compile_path = compile_path |> Path.expand |> binary_to_list
|
||||
erlc_options = [{:outdir, compile_path}] ++ erlc_options
|
||||
result =
|
||||
case GraphUtils.topsort(graph) do
|
||||
false -> erls
|
||||
mods ->
|
||||
lc m inlist mods, do: elem(Graph.vertex(graph, m), 1)
|
||||
end
|
||||
|
||||
Graph.delete(graph)
|
||||
result
|
||||
end
|
||||
|
||||
defp requires_compilation?(compile_path, erl) do
|
||||
beam = Path.join(compile_path, "#{erl.module}#{:code.objfile_extension}")
|
||||
Utils.stale?([erl.file|erl.includes], [beam])
|
||||
end
|
||||
|
||||
defp compile_files(files, compile_path, erlc_options) do
|
||||
File.mkdir_p!(compile_path)
|
||||
Enum.each files, compile_file(&1, erlc_options)
|
||||
end
|
||||
|
||||
Enum.each files, fn(file) ->
|
||||
file = Path.rootname(file, ".erl") |> Path.expand |> binary_to_list
|
||||
defp compile_file(erl, erlc_options) do
|
||||
file = to_erl_file Path.rootname(erl.file, ".erl")
|
||||
interpret_result file, :compile.file(file, erlc_options), ".erl"
|
||||
end
|
||||
|
||||
case :compile.file(file, erlc_options) do
|
||||
{ :ok, _ } -> Mix.shell.info "Compiled #{file}.erl"
|
||||
:error -> Mix.shell.error "== Compilation error on file #{file}.erl =="
|
||||
## Helpers shared accross erlang compilers
|
||||
|
||||
@doc """
|
||||
Extract stale pairs considering the set of directories
|
||||
and filename extensions. It first looks up the `dir1`
|
||||
for files with `ext1` extensions and then recursively
|
||||
try to find matching pairs in `dir2` with `ext2`
|
||||
extension.
|
||||
"""
|
||||
def extract_stale_pairs(dir1, ext1, dir2, ext2, force) do
|
||||
files = Mix.Utils.extract_files([dir1], List.wrap(ext1))
|
||||
Enum.reduce files, [], fn(file, acc) ->
|
||||
compiled_file = Path.rootname(file) |> Path.basename
|
||||
compiled_file = Path.join(dir2, compiled_file <> "." <> to_binary(ext2))
|
||||
if force or Mix.Utils.stale?([file], [compiled_file]) do
|
||||
[{file, compiled_file} | acc]
|
||||
else
|
||||
acc
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Interprets compilation results and prints them to the console.
|
||||
"""
|
||||
def interpret_result(file, result, ext // "") do
|
||||
case result do
|
||||
{ :ok, _ } ->
|
||||
Mix.shell.info "Compiled #{file}#{ext}"
|
||||
:error ->
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Converts the given file to a format accepted by
|
||||
Erlang compilation tools.
|
||||
"""
|
||||
def to_erl_file(file) do
|
||||
to_char_list(file)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -80,9 +80,9 @@ defmodule Mix.Tasks.Compile do
|
||||
|
||||
defp get_compilers do
|
||||
Mix.project[:compilers] || if Mix.Project.get do
|
||||
[:erlang, :elixir, :app]
|
||||
[:yecc, :leex, :erlang, :elixir, :app]
|
||||
else
|
||||
[:erlang, :elixir]
|
||||
[:yecc, :leex, :erlang, :elixir]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
defmodule Mix.Tasks.Compile.Leex do
|
||||
alias :leex, as: Leex
|
||||
alias Mix.Tasks.Compile.Erlang
|
||||
|
||||
use Mix.Task
|
||||
|
||||
@hidden true
|
||||
@shortdoc "Compile Leex source files"
|
||||
|
||||
@moduledoc """
|
||||
A task to compile Leex source files.
|
||||
|
||||
When this task runs, it will check the mod time of every file, and
|
||||
if it has changed, then file will be compiled. Files will be
|
||||
compiled in the same source directory with .erl extension.
|
||||
You can force compilation regardless of mod times by passing
|
||||
the `--force` option.
|
||||
|
||||
## Command line options
|
||||
|
||||
* `--force` - forces compilation regardless of module times;
|
||||
|
||||
## Configuration
|
||||
|
||||
* `:erlc_paths` - directories to find source files.
|
||||
Defaults to `["src"]`, can be configured as:
|
||||
|
||||
[erlc_paths: ["src", "other"]]
|
||||
|
||||
* `:leex_options` - compilation options that applies
|
||||
to Leex's compiler. There are many available options
|
||||
here: http://www.erlang.org/doc/man/leex.html#file-2
|
||||
|
||||
"""
|
||||
def run(args) do
|
||||
{ opts, _ } = OptionParser.parse(args, switches: [force: :boolean])
|
||||
|
||||
project = Mix.project
|
||||
source_paths = project[:erlc_paths]
|
||||
|
||||
files = lc source_path inlist source_paths do
|
||||
Erlang.extract_stale_pairs(source_path, :xrl, source_path, :erl, opts[:force])
|
||||
end |> List.flatten
|
||||
|
||||
if files == [] do
|
||||
:noop
|
||||
else
|
||||
compile_files(files, project[:leex_options] || [])
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp compile_files(files, options) do
|
||||
lc {input, output} inlist files do
|
||||
options = options ++ [scannerfile: Erlang.to_erl_file(output), report: true]
|
||||
Erlang.interpret_result(input,
|
||||
Leex.file(Erlang.to_erl_file(input), options))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
defmodule Mix.Tasks.Compile.Yecc do
|
||||
|
||||
alias :compile, as: Compiler
|
||||
alias :yecc, as: Yecc
|
||||
alias Mix.Utils
|
||||
alias Mix.Tasks.Compile.Erlang
|
||||
use Mix.Task
|
||||
|
||||
@hidden true
|
||||
@shortdoc "Compile Yecc source files"
|
||||
|
||||
@moduledoc """
|
||||
A task to compile Yecc source files.
|
||||
|
||||
When this task runs, it will check the mod time of every file, and
|
||||
if it has changed, then file will be compiled. Files will be
|
||||
compiled in the same source directory with .erl extension.
|
||||
You can force compilation regardless of mod times by passing
|
||||
the `--force` option.
|
||||
|
||||
## Command line options
|
||||
|
||||
* `--force` - forces compilation regardless of module times;
|
||||
|
||||
## Configuration
|
||||
|
||||
* `:erlc_paths` - directories to find source files.
|
||||
Defaults to `["src"]`, can be configured as:
|
||||
|
||||
[erlc_paths: ["src", "other"]]
|
||||
|
||||
* `:yecc_options` - compilation options that applies
|
||||
to Yecc's compiler. There are many other available
|
||||
options here: http://www.erlang.org/doc/man/yecc.html#file-1
|
||||
|
||||
"""
|
||||
def run(args) do
|
||||
{ opts, _ } = OptionParser.parse(args, switches: [force: :boolean])
|
||||
|
||||
project = Mix.project
|
||||
source_paths = project[:erlc_paths]
|
||||
|
||||
files = lc source_path inlist source_paths do
|
||||
Erlang.extract_stale_pairs(source_path, :yrl, source_path, :erl, opts[:force])
|
||||
end |> List.flatten
|
||||
|
||||
if files == [] do
|
||||
:noop
|
||||
else
|
||||
compile_files(files, project[:yecc_options] || [])
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp compile_files(files, options) do
|
||||
lc { input, output } inlist files do
|
||||
options = options ++ [parserfile: Erlang.to_erl_file(output), report: true]
|
||||
Erlang.interpret_result(input,
|
||||
Yecc.file(Erlang.to_erl_file(input), options))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -141,7 +141,7 @@ defmodule Mix.Tasks.Escriptize do
|
||||
:ok ->
|
||||
Kernel.CLI.run fn -> @module.main(args) end, true
|
||||
_ ->
|
||||
IO.puts :stderr, "Elixir is not in the code path, aborting."
|
||||
IO.puts :stderr, IO.ANSI.escape("%{red, bright} Elixir is not in the code path, aborting.")
|
||||
System.halt(1)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -34,21 +34,22 @@ defmodule Mix.Tasks.Help do
|
||||
sorted = Enum.sort(docs)
|
||||
|
||||
Enum.each sorted, fn({ task, doc }) ->
|
||||
shell.info format('mix ~-#{max}s # ~s', [task, doc])
|
||||
shell.info format("mix ~-#{max}s # ~s", [task, doc])
|
||||
end
|
||||
end
|
||||
|
||||
def run([task]) do
|
||||
module = Mix.Task.get(task)
|
||||
IO.puts "# mix help #{task}\n"
|
||||
shell = Mix.shell
|
||||
shell.info "%{bright}# mix help #{task}\n"
|
||||
|
||||
if doc = Mix.Task.moduledoc(module) do
|
||||
IO.write doc
|
||||
else
|
||||
IO.puts "There is no documentation for this task"
|
||||
shell.info "There is no documentation for this task"
|
||||
end
|
||||
|
||||
IO.puts "Location: #{where_is_file(module)}"
|
||||
shell.info "Location: #{where_is_file(module)}"
|
||||
end
|
||||
|
||||
defp where_is_file(module) do
|
||||
|
||||
@@ -73,7 +73,7 @@ defmodule Mix.Tasks.New do
|
||||
end
|
||||
|
||||
defp check_project_name!(name) do
|
||||
unless name =~ %r/^[a-z][\w_]+$/i do
|
||||
unless name =~ %r/^[a-z][\w_]*$/i do
|
||||
raise Mix.Error, message: "project path must start with a letter and have only letters, numbers and underscore"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -25,7 +25,8 @@ defmodule Mix.Tasks.Run do
|
||||
"""
|
||||
def run(args) do
|
||||
{ opts, head } = OptionParser.parse_head(args,
|
||||
aliases: [r: :require, pr: :parallel_require])
|
||||
aliases: [r: :require, pr: :parallel_require],
|
||||
switches: [parallel_require: :keep, require: :keep])
|
||||
|
||||
Enum.each opts, fn({ key, value }) ->
|
||||
case key do
|
||||
@@ -39,8 +40,7 @@ defmodule Mix.Tasks.Run do
|
||||
end
|
||||
|
||||
Mix.Task.run Mix.project[:prepare_task], args
|
||||
Code.eval Enum.join(head, " ")
|
||||
|
||||
if head != [], do: Code.eval Enum.join(head, " ")
|
||||
if opts[:no_halt], do: :timer.sleep(:infinity)
|
||||
end
|
||||
|
||||
|
||||
+10
-12
@@ -57,7 +57,8 @@ defmodule Mix.Utils do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns true if any of the sources are stale compared to the given target.
|
||||
Returns true if any of the sources are stale
|
||||
compared to the given target.
|
||||
"""
|
||||
def stale?(sources, targets) do
|
||||
extract_stale(sources, targets) != []
|
||||
@@ -91,8 +92,8 @@ defmodule Mix.Utils do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Executes a function but preserves the given path
|
||||
mtime properties.
|
||||
Executes a function but preserves the given path mtime
|
||||
properties.
|
||||
"""
|
||||
def preserving_mtime(path, fun) do
|
||||
previous = last_modified(path)
|
||||
@@ -105,16 +106,13 @@ defmodule Mix.Utils do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Extract files from a list of paths or from a wildcard.
|
||||
Extract files from a list of paths.
|
||||
|
||||
If the list of paths contains a directory, the directory
|
||||
is expanded according to the given pattern.
|
||||
|
||||
It ignores files which start with "."
|
||||
In case any of the paths is a directory, the directory is looped
|
||||
recursively searching for the given extensions or the given pattern.
|
||||
When looking up directories, files starting with "." are ignored.
|
||||
"""
|
||||
def extract_files(paths, _pattern) when is_binary(paths) do
|
||||
Path.wildcard(paths) |> exclude_files
|
||||
end
|
||||
def extract_files(paths, exts_or_pattern)
|
||||
|
||||
def extract_files(paths, exts) when is_list(exts) do
|
||||
extract_files(paths, "*.{#{Enum.join(exts, ",")}}")
|
||||
@@ -253,7 +251,7 @@ defmodule Mix.Utils do
|
||||
"""
|
||||
def relative_to_cwd(path) do
|
||||
case File.cwd do
|
||||
{ :ok, base } -> String.replace(path, base <> "/", "")
|
||||
{ :ok, base } -> Path.relative_to(path, base)
|
||||
_ -> path
|
||||
end
|
||||
end
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user