Compare commits

...
86 Commits
Author SHA1 Message Date
José Valim 90ff71b0cc Release v0.8.1 2013-02-17 21:28:17 -07:00
José Valim 7dd0aabbe2 Deprecate ExUnit.after_spawn callbacks 2013-02-17 20:46:58 -07:00
José Valim 658d7c7913 ExUnit callbacks must return :ok or { :ok, data } 2013-02-17 20:43:01 -07:00
José Valim 1f491dfbe2 Add support to case templates 2013-02-17 20:43:01 -07:00
José Valim 8ca4517563 Merge pull request #847 from jeremyjh/apply-doc-fix
correct apply/3 example
2013-02-17 18:38:51 -08:00
Jeremy Huffman 4e8690cd8e correct apply/3 example 2013-02-17 21:22:11 -05:00
José Valim 8f57b4c9af Add metadata support to ExUnit
Defining setup/teardown/test explicitly is now deprecated.
They should now be defined via their respective macros:

    def setup do
      ...
    end

Now becomes:

    setup do
      ...
    end

When defined using the new format, setup callbacks can
return metadata:

    setup do
      [do_this: :do_that]
    end

And now the test macro can access this metadata:

    test "check metadata", data do
      assert data[:do_this] == :do_that
    end
2013-02-17 18:23:31 -07:00
José Valim 19d402b7d5 Disable native compilation when on_load attributes is present
This is a work around an Erlang bug, see #845 for more information.
Closes #845.
2013-02-17 12:22:08 -07:00
José Valim cf6751a3c8 Merge pull request #844 from michaelklishin/issue843
Make Mix support --help, -h and -help
2013-02-17 09:20:54 -08:00
Michael Klishin 42a0719c8a Use fn clause matching
This does not perform downcasing and thus makes BASIC refugees
unhappy, but @yrashk suggests we can live with that.
2013-02-17 21:10:40 +04:00
Michael Klishin 4527c1e849 Make Mix CLI support --help, -h and -help, fixes #843 2013-02-17 21:05:38 +04:00
José Valim 5557e620ff Backwards incompatible change on before_compile and after_compile
Both callbacks now receive the environment as
first argument instead of the module.
2013-02-16 19:57:14 -07:00
José Valim 683153ef05 Switches are now overridden by default but can be kept in order if chosen, closes #782 2013-02-16 19:17:35 -07:00
José Valim 8a77e878ab Fix bug when running mix on directories containig space, closes #840 2013-02-16 18:38:29 -07:00
José Valim a19a4b893d Update CHANGELOG 2013-02-16 17:46:10 -07:00
José Valim b8689da1f5 Small refactoring on Mix tasks 2013-02-16 17:44:27 -07:00
José Valim a05592fdd9 Only accept the IO device on inspect when we have three args 2013-02-16 16:43:41 -07:00
José Valim 3147df88fa Merge pull request #776 from liveforeverx/compile-erl-new
WiP for compiling of erlang/leex/yecc source files
2013-02-16 15:43:32 -08:00
José Valim 1946feb4e1 Merge pull request #842 from happy4crazy/doc-fixes
Kernel doc fixes
2013-02-15 14:27:24 -08:00
Alan O'Donnell a0101bc056 Doc typos, misc. style cleanup 2013-02-15 17:08:22 -05:00
Yurii Rashkovskii 129725a168 Suppress minor warning (unused variable) in hash_dict.ex 2013-02-15 13:02:16 -08:00
José Valim e93613bc05 Merge pull request #841 from happy4crazy/iex-helpers
Tiny typo in IEx.Helpers.h/1
2013-02-15 12:57:20 -08:00
Alan O'Donnell 7b51500a60 Fix typo in IEx.Helpers.h/1 2013-02-15 15:01:04 -05:00
Alan O'Donnell f6058afb26 Misc. documentation cleanup 2013-02-15 14:59:25 -05:00
Alan O'Donnell 67b5236874 Fix invalid bit syntax in docs. 2013-02-15 13:07:03 -05:00
José Valim 1e18d8e08e Default mix task should be run, closes #834 2013-02-14 15:00:30 -07:00
José Valim 9ebce19f36 Push a test case related to the nested vars in clauses 2013-02-14 10:46:20 -07:00
José Valim fb82b0c679 Pick latest variable in between clauses instead of picking a random one 2013-02-14 09:43:54 -07:00
Yurii Rashkovskii 9dc7f44049 Drop HashDict.t type and spec HashDict.new with Dict.t 2013-02-12 12:39:23 -08:00
Yurii Rashkovskii 5e2c6e14be Add HashDict.t type 2013-02-12 11:36:55 -08:00
José Valim e896e311c1 Merge pull request #833 from kimshrier/process_self_deprecations
Change references or invocations of Process.self to Kernel.self
2013-02-09 14:58:47 -08:00
Kim Shrier ced13c159f Change references or invocations of Process.self to Kernel.self 2013-02-09 15:41:09 -07:00
José Valim 5fe1544ff3 Deprecate Process.self in favor of Kernel.self to make imports easier, closes #832 2013-02-09 12:10:15 -07:00
José Valim 96c7f68a62 Merge pull request #825 from khia/node_connect
Node.connect/1
2013-02-08 10:36:00 -08:00
Yurii Rashkovskii d0dc9f5c2c Minor indentation fix 2013-02-06 10:32:26 -08:00
Yurii Rashkovskii 8a130978b2 Remove some garbage 2013-02-05 11:20:04 -08:00
Yurii Rashkovskii 71e2252064 Unpack __block__ in type specs
Fixes #827
2013-02-05 11:18:49 -08:00
ILYA Khlopotov ddc9733cf2 Add Node.connect/1 method 2013-02-04 12:34:11 -08:00
ILYA Khlopotov 9bcc4be3f5 Fix typo in typespec of Node.disconnect 2013-02-04 12:32:29 -08:00
José Valim f66206b4f3 s/on/in 2013-02-04 08:38:52 -07:00
José Valim 28211b2714 Ensure aliases expansion also works for implicit aliases 2013-02-03 21:57:39 -07:00
Dima 93f1bb920b developing in progress for:
* check erlang files and dependencies before compiling
* error output for erlang compiling
* compiling of leex and yecc
2013-02-03 23:36:25 +01:00
José Valim 109c07e757 Improve aliases hygiene by using metadata 2013-02-03 15:12:38 -07:00
José Valim eb36c2b288 Improve docs for File 2013-02-03 14:03:59 -07:00
José Valim 3537bbbbf4 Refactor aliases handling 2013-02-03 13:07:20 -07:00
José Valim cd51c80362 Tidy up and clean up quote API, closes #814 2013-02-03 12:14:18 -07:00
Yurii Rashkovskii a1a576e9d1 Implement as_boolean/1 type
Closes #798
2013-02-03 10:40:35 -08:00
José Valim 1dc4bf0989 Use tag from VERSION on package.exs 2013-02-02 21:48:06 -07:00
Yurii Rashkovskii 1812e652db Minor IO.ANSI.terminal?/0 documentation improvement 2013-02-02 15:13:47 -08:00
Yurii Rashkovskii 7f73cb3128 Add missing specification for IO.ANSI.terminal?/1 2013-02-02 14:37:41 -08:00
Yurii Rashkovskii 0123edd8a3 Better error reporting for invalid ANSI escape sequences 2013-02-02 14:25:10 -08:00
Yurii Rashkovskii 87af330589 Don't emit ANSI sequence escape codes in shell in OTP prior to R16 2013-02-02 14:10:27 -08:00
José Valim 3ae634a13c Update CHANGELOG 2013-02-02 15:08:14 -07:00
José Valim de3bd8ec86 Expand current module macros with Macro.expand, closes #815 2013-02-02 15:04:30 -07:00
Yurii Rashkovskii 3fea472cc2 IO.ANSI.escape shouldn't emit a reset sequence if no ANSI sequence was emitted
Closes #807
2013-02-02 13:02:51 -08:00
Yurii Rashkovskii 7cb18091d0 Better error reporting for invalid type specifications
Closes #818
2013-02-02 12:47:17 -08:00
José Valim 8994feb0a3 Fix unused vars warnings 2013-02-02 13:33:00 -07:00
José Valim f4aed8e873 Reduce vars footprint in case/receive clauses 2013-02-02 13:20:59 -07:00
José Valim 008ee62f8b Take into account Erlang may pass line numbers as none 2013-02-02 09:00:43 -07:00
José Valim d543650f70 Merge pull request #813 from alexrp/master
Fix #812: Write a trailing newline character to the Mix lockfile.
2013-01-30 21:16:16 -08:00
alexrp febf5e46b6 Fix #812: Write a trailing newline character to the Mix lockfile. 2013-01-31 05:20:49 +01:00
José Valim 7196543060 Improve how aliases for defmodule are generated 2013-01-30 16:43:29 -07:00
José Valim fade5452e1 R16A fixes 2013-01-30 11:15:40 -07:00
Yurii Rashkovskii 9e46866851 Allow building Elixir on R16 2013-01-30 09:41:32 -08:00
José Valim 1dd1f2b213 Use hardcoded version on elixir.app.src for now
Related to #808
2013-01-30 10:04:23 -07:00
José Valim 3137bfc614 Move bootstrap code for IEx to IEx.CLI 2013-01-29 12:30:09 -07:00
José Valim 4d3d61e173 Deprecate :local.(args) syntax 2013-01-28 21:21:18 -07:00
Yurii Rashkovskii 2e66218f58 Minor IO.ANSI documentation fix 2013-01-28 12:22:05 -08:00
José Valim 3a1fbde95d Remove deprecated branches 2013-01-28 13:08:18 -07:00
José Valim 088978fa04 Update CHANGELOG 2013-01-28 13:04:09 -07:00
José Valim 249f83e82b IO should default to the group leader 2013-01-28 13:03:08 -07:00
José Valim 608f0a3dcb Allow mix projects with just one letter, closes #799 2013-01-28 12:47:09 -07:00
José Valim d8aa534e3a Change IO.ANSI escape from #[] to %{} 2013-01-28 12:45:14 -07:00
José Valim c2df5fa5ff Move ansi control to Mix.Shell 2013-01-28 12:32:01 -07:00
José Valim fee8836366 Merge pull request #800 from yrashk/mix-colors
Initial ANSIfication of mix
2013-01-28 11:18:20 -08:00
José Valim 35a43afb22 Define a sequence as a key, value pair 2013-01-28 12:13:47 -07:00
Yurii Rashkovskii c359f212f8 Initial ANSIfication of mix 2013-01-28 11:10:19 -08:00
José Valim 0f2dd3f7d4 Merge pull request #775 from yrashk/io-ansi
Initial IO.ANSI implementation (based on https://github.com/yrashk/ansiex)
2013-01-28 10:46:29 -08:00
José Valim 20942ee329 Better support for Unicode lists 2013-01-28 11:43:25 -07:00
José Valim d2f76453d6 Compile before release targets 2013-01-28 10:43:29 -07:00
Yurii Rashkovskii 50d375e33b IO.ANSI.escape implies terminating #[reset] now and IO.ANSI.escape_fragment has been introduced 2013-01-20 10:36:31 -08:00
Yurii Rashkovskii 119d48e093 Improve IO.ANSI test case so it can be run on non-terminals correctly 2013-01-19 20:22:16 -08:00
Yurii Rashkovskii 6960f856f4 Put proper IO.ANSI tests in 2013-01-19 20:17:59 -08:00
Yurii Rashkovskii eb11254369 Introduce IO.ANSI.escape/1,2 functions 2013-01-19 19:38:14 -08:00
Yurii Rashkovskii f686b08e5e Add a pre-R16 warning about ANSI escape sequences in iex/erl 2013-01-19 15:35:08 -08:00
Yurii Rashkovskii 80bd148852 Initial IO.ANSI implementation (based on https://github.com/yrashk/ansiex) 2013-01-19 15:28:35 -08:00
113 changed files with 2127 additions and 1118 deletions
+27
View File
@@ -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
+4 -4
View File
@@ -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
View File
@@ -23,4 +23,5 @@ This document simply outlines the release process:
## Places where version is mentioned
* VERSION
* CHANGELOG
* CHANGELOG
* src/elixir.app.src
+1 -1
View File
@@ -1 +1 @@
0.8.0
0.8.1
+3 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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 %*
+1 -1
View File
@@ -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
View File
@@ -1 +1,2 @@
call "%~dp0\elixir.bat" "%~dp0\mix" %*
@echo off
call "%~dp0\elixir.bat" "%~dp0\mix" %*
+1 -1
View File
@@ -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
+1 -1
View File
@@ -112,7 +112,7 @@ defmodule EEx.Tokenizer do
else
:middle_expr
end
error ->
_error ->
:middle_expr
end
end
+5 -4
View File
@@ -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
}).
+2 -15
View File
@@ -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
-274
View File
@@ -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
+9 -9
View File
@@ -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
-5
View File
@@ -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
View File
@@ -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
+6 -2
View File
@@ -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
View File
@@ -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
+233
View File
@@ -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
View File
@@ -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 """
+1 -15
View File
@@ -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)
+1 -1
View File
@@ -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)
+2 -2
View File
@@ -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
+41 -28
View File
@@ -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
+33 -5
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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.
+32 -21
View File
@@ -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
+3 -3
View File
@@ -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
+3 -4
View File
@@ -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
+2 -2
View File
@@ -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
+8 -8
View File
@@ -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
+2 -3
View File
@@ -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
+1 -1
View File
@@ -24,4 +24,4 @@
{verbose, false}
]}.
{require_otp_vsn,"R15"}.
{require_otp_vsn,"(R15|R16).*"}.
+2 -1
View File
@@ -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
+69 -28
View File
@@ -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
+68 -81
View File
@@ -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.
-12
View File
@@ -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
+48 -6
View File
@@ -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}) ->
+2 -2
View File
@@ -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
+10 -3
View File
@@ -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;
+12 -3
View File
@@ -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]).
+18 -9
View File
@@ -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;
+11 -12
View File
@@ -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)
+2 -2
View File
@@ -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 }) ->
+11 -6
View File
@@ -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)
+6 -9
View File
@@ -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)}]
+38 -19
View File
@@ -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
+5 -6
View File
@@ -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
+3 -3
View File
@@ -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")
+8 -4
View File
@@ -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
+45
View File
@@ -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
+53 -1
View File
@@ -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)
+37 -4
View File
@@ -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
+2 -2
View File
@@ -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
+2 -6
View File
@@ -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
+9 -1
View File
@@ -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)
+4 -4
View File
@@ -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)
+10 -3
View File
@@ -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
+1 -1
View File
@@ -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
-13
View File
@@ -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')
+2 -2
View File
@@ -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
+42 -3
View File
@@ -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)
+1 -1
View File
@@ -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() ->
+14 -9
View File
@@ -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
+1 -1
View File
@@ -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
+206
View File
@@ -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
+30 -31
View File
@@ -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
+69
View File
@@ -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
+15 -54
View File
@@ -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
+31 -19
View File
@@ -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
-4
View File
@@ -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") == []
-2
View File
@@ -1,3 +1 @@
# Configure ExUnit, no options supported yet.
ExUnit.start []
ExUnit.after_spawn fn -> Process.put(:after_spawn, :ex_unit) end
+2 -2
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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
+9 -14
View File
@@ -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: []
+2 -8
View File
@@ -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
+3 -3
View File
@@ -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
+3 -3
View File
@@ -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
+8 -1
View File
@@ -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
+170 -22
View File
@@ -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
+2 -2
View File
@@ -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
+61
View File
@@ -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
+62
View File
@@ -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
+1 -1
View File
@@ -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
+5 -4
View File
@@ -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
+1 -1
View File
@@ -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
+3 -3
View File
@@ -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
View File
@@ -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