Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32d4739dad | ||
|
|
650cfc0448 | ||
|
|
d4d824022f | ||
|
|
c6284557e7 | ||
|
|
a8a2f86a23 | ||
|
|
fc219e7dca | ||
|
|
96a103d222 | ||
|
|
f525f5b8cb | ||
|
|
5c30a687fa | ||
|
|
9ffe8b0e97 | ||
|
|
12c8564a26 | ||
|
|
903aa60fec | ||
|
|
969bc968ee | ||
|
|
e941d78daf | ||
|
|
8d3475825b | ||
|
|
68c890e179 | ||
|
|
6f73d7dbb9 | ||
|
|
c2ac967123 | ||
|
|
dd573c9806 | ||
|
|
58ee415e63 | ||
|
|
561397da7a | ||
|
|
3d2438c625 | ||
|
|
a08710bfda | ||
|
|
151695b43c | ||
|
|
2d8259b432 | ||
|
|
ae03e10db5 | ||
|
|
208fbaf5ef | ||
|
|
820ca0a57e | ||
|
|
cf309ede14 | ||
|
|
da0a95695d | ||
|
|
b2541a1b93 | ||
|
|
5df40f7c42 | ||
|
|
5e24dbaddb | ||
|
|
3f8e3d4d38 | ||
|
|
391fee3ea2 | ||
|
|
6cee8572e0 | ||
|
|
f2d9e802cd | ||
|
|
5247961185 | ||
|
|
53a2618cba | ||
|
|
d360addc65 | ||
|
|
08e7da305c | ||
|
|
afab1f8d73 | ||
|
|
749266b649 | ||
|
|
c4dcaf540d | ||
|
|
561d6ea0d1 | ||
|
|
1574f61a8b | ||
|
|
7dc941227f | ||
|
|
ca026039a3 | ||
|
|
4b06a2d316 | ||
|
|
00299fe7cb | ||
|
|
9e6b650efd | ||
|
|
c56d0760f6 | ||
|
|
aedc78536f | ||
|
|
0c17fa0ab2 | ||
|
|
752a09c988 | ||
|
|
d5a8821ec1 | ||
|
|
aa7400aaad | ||
|
|
d849fc5dca | ||
|
|
3d73d1a359 | ||
|
|
18bcef3ef4 | ||
|
|
115fd99bfb | ||
|
|
879499b409 | ||
|
|
e265771c6e | ||
|
|
f9f728cb66 | ||
|
|
e321caee24 | ||
|
|
5dd3a23752 | ||
|
|
c572cb80fc | ||
|
|
b761f1a605 | ||
|
|
c1f907b412 | ||
|
|
c45d0f4f2f | ||
|
|
406f0dcffe | ||
|
|
9dbc216c11 | ||
|
|
22b455fad4 | ||
|
|
9807b506eb | ||
|
|
624423686d | ||
|
|
e5cba5e4b5 | ||
|
|
1f15bf72e7 | ||
|
|
e6bc5a273c | ||
|
|
8fa6c4f96f | ||
|
|
b7e8f97de1 | ||
|
|
5fc68fe71b | ||
|
|
808260cd1b | ||
|
|
d9152c08d8 | ||
|
|
4048e542d9 | ||
|
|
b08273700f | ||
|
|
0d5512931e | ||
|
|
7fc37de058 | ||
|
|
c4dac6fd7a | ||
|
|
d3122ddcc5 | ||
|
|
74cfcda902 | ||
|
|
6fc6dd4352 | ||
|
|
7014986f65 | ||
|
|
031f4e942f | ||
|
|
9904bdce9d | ||
|
|
754418493f | ||
|
|
27000ea1b4 | ||
|
|
620ee4298c | ||
|
|
1d09da7e53 | ||
|
|
5115256853 | ||
|
|
0d715343de | ||
|
|
441547d8e4 | ||
|
|
5e123e6519 | ||
|
|
5b45ee5c8d | ||
|
|
939942bdb3 | ||
|
|
e64be43090 | ||
|
|
da898e1ac9 | ||
|
|
8dba46a0ac | ||
|
|
633eb871ee | ||
|
|
91c0fa71fa | ||
|
|
87da4e91bb | ||
|
|
7768828d9c | ||
|
|
8ece000489 | ||
|
|
1297fa5953 | ||
|
|
c7a2442bc7 | ||
|
|
f88ee1c23a | ||
|
|
64a22fc15d | ||
|
|
07cd772962 | ||
|
|
160a8255ef | ||
|
|
cca430522d | ||
|
|
db417110e7 | ||
|
|
67896b56c5 | ||
|
|
d21d603617 | ||
|
|
fdd108fc43 | ||
|
|
19ff7a03f9 | ||
|
|
7f5744bde7 | ||
|
|
b87797f788 | ||
|
|
48b19ec580 | ||
|
|
f2e200f832 | ||
|
|
a541f53dd1 | ||
|
|
6ce77b1b49 | ||
|
|
5222c7737f | ||
|
|
b0cf65b5cd | ||
|
|
7cdc25538e | ||
|
|
b82cd8cbd0 | ||
|
|
9365556ea4 | ||
|
|
de376663f0 | ||
|
|
f24ec671c2 | ||
|
|
c0f7a268c7 | ||
|
|
7b4e0915b1 | ||
|
|
2ea7de7361 | ||
|
|
82cc435fd7 | ||
|
|
8eab556f55 | ||
|
|
5a37594853 | ||
|
|
82f3fa00d0 | ||
|
|
c97dc3bd15 | ||
|
|
d6e99e2f5a | ||
|
|
46b6f82473 | ||
|
|
c80e43ca42 | ||
|
|
80e41f8b4b | ||
|
|
9e16cf36ed | ||
|
|
1d8e39a2df | ||
|
|
21db352039 | ||
|
|
45f033d15a | ||
|
|
8eba2fcba0 | ||
|
|
e476c06798 | ||
|
|
0b6b7ea0e4 | ||
|
|
e62ef92a4b | ||
|
|
7be271876e | ||
|
|
7c639f9252 | ||
|
|
f03e707e8e | ||
|
|
38e63451d6 | ||
|
|
5e1366c946 | ||
|
|
0470ebf5af | ||
|
|
b71642045d | ||
|
|
582e814422 | ||
|
|
d15ad4da80 | ||
|
|
bc0089ff42 | ||
|
|
bff8a6ae75 | ||
|
|
6d4ad4ade6 | ||
|
|
057691722e | ||
|
|
0e2752f7c2 | ||
|
|
76b2255d7b | ||
|
|
93d5128db3 | ||
|
|
6d98020923 | ||
|
|
77110f970b | ||
|
|
cf6ce9a3fd | ||
|
|
be0dea7c0a | ||
|
|
b0b444bbd3 | ||
|
|
3e45188e88 | ||
|
|
51171cb8e1 |
@@ -0,0 +1 @@
|
||||
lib/elixir/test/elixir/fixtures/*.txt text eol=lf
|
||||
@@ -1,3 +1,41 @@
|
||||
# v0.9.3 (2013-06-23)
|
||||
|
||||
* enhancements
|
||||
* [File] Add `File.chgrp`, `File.chmod` and `File.chown`
|
||||
* [Kernel] Add --warnings-as-errors to Elixir's compiler options
|
||||
* [Kernel] Print warnings to stderr
|
||||
* [Kernel] Warn on undefined module attributes
|
||||
* [Kernel] Emit warning for `x in []` in guards
|
||||
* [Kernel] Add `binding/0` and `binding/1` for retrieving bindings
|
||||
* [Kernel] `quote` now allows a binding as an option
|
||||
* [Macro] Add `Macro.expand_once/2` and `Macro.expand_all/2`
|
||||
* [Mix] Implement `Mix.Version` for basic versioniong semantics
|
||||
* [Mix] Support creation and installation of archives (.ez files)
|
||||
* [Mix] `github: ...` shortcut now uses the faster `git` schema instead of `https`
|
||||
* [Record] Allow types to be given to `defrecord` and `defrecordp`
|
||||
|
||||
* bug fix
|
||||
* [Kernel] The elixir executable on Windows now supports the same options as the UNIX one
|
||||
* [Kernel] Improve error messages on default clauses clash
|
||||
* [Kernel] `__MODULE__.Foo` now returns `Foo` when outside of a Module
|
||||
* [Kernel] Improve error messages when default clauses from different definitions collide
|
||||
* [Kernel] `^x` variables should always refer to the value before the expression
|
||||
* [Kernel] Allow `(x, y) when z` in function clauses and try expressions
|
||||
* [Mix] Mix now properly evaluates rebar scripts
|
||||
|
||||
* deprecations
|
||||
* [Code] `Code.string_to_ast/1` has been deprecated in favor of `Code.string_to_quoted/1`
|
||||
* [Macro] `Macro.to_binary/1` has been deprecated in favor of `Macro.to_string/1`
|
||||
* [Typespec] Deprecate `(fun(...) -> ...)` in favor of `(... -> ...)`
|
||||
|
||||
* backwards incompatible changes
|
||||
* [Bitwise] Precedence of operators used by the Bitwise module were changed. Check `elixir_parser.yrl` for more information.
|
||||
* [File] `rm_rf` and `cp_r` now returns a tuple with three elements on failures
|
||||
* [Kernel] The quoted representation for `->` clauses changed from a tuple with two elements to a tuple with three elements to support metadata
|
||||
* [Kernel] Sigils now dispatch to `sigil_$` instead of `__$__` where `$` is the sigil caracter
|
||||
* [Macro] `Macro.expand/2` now expands until final form. Although this is backwards incompatible, it is very likely you do not need to change your code, since expansion until its final form is recommended, particularly if you are expecting an atom out of it
|
||||
* [Mix] No longer support beam files on `mix local`
|
||||
|
||||
# v0.9.2 (2013-06-13)
|
||||
|
||||
* enhancements
|
||||
|
||||
+2
-2
@@ -95,8 +95,8 @@ Good pull requests - patches, improvements, new features - are a fantastic
|
||||
help. They should remain focused in scope and avoid containing unrelated
|
||||
commits.
|
||||
|
||||
**IMPORTANT**: By submitting a patch, you agree that your work will be made
|
||||
public forever and will be licensed under the license used by the project.
|
||||
**IMPORTANT**: By submitting a patch, you agree that your work will be
|
||||
licensed under the license used by the project.
|
||||
|
||||
If you have any significant pull request in mind (e.g. implementing features,
|
||||
refactoring code, porting to a different language), **please ask first**
|
||||
|
||||
@@ -92,6 +92,9 @@ clean:
|
||||
rm -rf lib/elixir/src/*_parser.erl
|
||||
rm -rf lib/elixir/test/ebin
|
||||
|
||||
clean_exbeam:
|
||||
@ rm -f lib/*/ebin/Elixir.*.beam
|
||||
|
||||
#==> Release tasks
|
||||
|
||||
SOURCE_REF = $(shell head="$$(git rev-parse HEAD)" tag="$$(git tag --points-at $$head | tail -1)" ; echo "$${tag:-$$head}\c")
|
||||
|
||||
@@ -26,7 +26,7 @@ Building the documentation requires [ex_doc](https://github.com/elixir-lang/ex_d
|
||||
|
||||
# After cloning and compiling Elixir
|
||||
$ git clone git://github.com/elixir-lang/ex_doc.git
|
||||
$ cd ../ex_doc && ../elixir/bin/mix compile
|
||||
$ cd ex_doc && ../elixir/bin/mix compile
|
||||
$ cd ../elixir && make docs
|
||||
|
||||
## Contributing
|
||||
@@ -52,4 +52,4 @@ We appreciate any contribution to Elixir, so check out our [CONTRIBUTING.md](CON
|
||||
|
||||
Elixir source code is released under Apache 2 License with some parts under Erlang's license (EPL).
|
||||
|
||||
Check LEGAL and LICENSE files for more information.
|
||||
Check LEGAL and LICENSE files for more information.
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
|
||||
This document simply outlines the release process:
|
||||
|
||||
1) Remove `.dev` extension from VERSION
|
||||
1) Remove `+dev` extension from VERSION
|
||||
|
||||
2) Run `make clean test` to ensure all tests pass from scratch and the CI is green
|
||||
|
||||
@@ -18,7 +18,7 @@ This document simply outlines the release process:
|
||||
|
||||
8) Push package to expm with `expm publish package.exs`
|
||||
|
||||
9) After release, bump versions and add `.dev` back
|
||||
9) After release, bump versions and add `+dev` back
|
||||
|
||||
## Places where version is mentioned
|
||||
|
||||
|
||||
+13
-4
@@ -2,7 +2,7 @@
|
||||
if [ $# -eq 0 ] || [ $1 = "--help" ] || [ $1 = "-h" ]; then
|
||||
echo "Usage: `basename $0` [options] [.exs file] [data]
|
||||
|
||||
-v Prints version
|
||||
-v Prints version and exit
|
||||
-e \"command\" Evaluates the given command (*)
|
||||
-r \"file\" Requires the given files/patterns (*)
|
||||
-S \"script\" Finds and executes the given script (*)
|
||||
@@ -43,7 +43,7 @@ while [ $I -le $# ]; do
|
||||
case "$PEEK" in
|
||||
-v|--compile|--no-halt|+iex|+compile)
|
||||
;;
|
||||
-e|-r|-pr|-pa|-pz|--remsh|-S)
|
||||
-e|-r|-pr|-pa|-pz|--remsh)
|
||||
S=2
|
||||
;;
|
||||
--detached|--hidden)
|
||||
@@ -74,12 +74,21 @@ done
|
||||
SELF=$(readlink_f "$0")
|
||||
SCRIPT_PATH=$(dirname "$SELF")
|
||||
|
||||
if [ "$ERL_LIBS" = "" ]
|
||||
then
|
||||
ERL_LIBS="$SCRIPT_PATH/../lib"
|
||||
else
|
||||
PATHSEP=":"
|
||||
if [ "$OSTYPE" = "msys" ]; then PATHSEP=";" ; fi
|
||||
ERL_LIBS="$ERL_LIBS$PATHSEP$SCRIPT_PATH/../lib"
|
||||
fi
|
||||
|
||||
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 -extra "$@"
|
||||
exec "$SCRIPT_PATH"/erl -env ERL_LIBS "$ERL_LIBS" -boot elixir -noshell $ELIXIR_ERL_OPTS $ERL -extra "$@"
|
||||
else
|
||||
exec erl -env ERL_LIBS $ERL_LIBS:"$SCRIPT_PATH/../lib" -noshell $ELIXIR_ERL_OPTS $ERL -extra "$@"
|
||||
exec erl -env ERL_LIBS "$ERL_LIBS" -noshell $ELIXIR_ERL_OPTS $ERL -extra "$@"
|
||||
fi
|
||||
|
||||
+102
-12
@@ -1,25 +1,115 @@
|
||||
@echo off
|
||||
SETLOCAL enabledelayedexpansion
|
||||
set argc=0
|
||||
for %%x in (%*) do set /A argc+=1
|
||||
if %argc% == 0 (
|
||||
if %argc%== 0 (
|
||||
goto documentation
|
||||
) else (
|
||||
goto run
|
||||
goto parseopts
|
||||
)
|
||||
:documentation
|
||||
echo Usage: %~nx0 [options] [.exs file] [data]
|
||||
echo.
|
||||
echo -v Prints version and exit
|
||||
echo -e command Evaluates the given command (*)
|
||||
echo -r command Requires the given file/pattern (*)
|
||||
echo -pr command Requires the given file/pattern in parallel (*)
|
||||
echo -pa path Prepend the given path to Erlang code path (*)
|
||||
echo -pz path Append the given path to Erlang code path (*)
|
||||
echo --app app Start the given app and its dependencies (*)
|
||||
echo --no-halt Do not halt the Erlang VM after execution
|
||||
echo -v Prints version and exit
|
||||
echo -e command Evaluates the given command (*)
|
||||
echo -r file Requires the given files/patterns (*)
|
||||
echo -S script Finds and executes the given script (*)
|
||||
echo -pr file Requires the given files/patterns in parallel (*)
|
||||
echo -pa path Prepends the given path to Erlang code path (*)
|
||||
echo -pz path Appends the given path to Erlang code path (*)
|
||||
echo --app app Start the given app and its dependencies (*)
|
||||
echo --erl switches Switches to be passed down to erlang (*)
|
||||
echo --name name Makes and assigns a name to the distributed node
|
||||
echo --sname name Makes and assigns a short name to the distributed node
|
||||
echo --cookie cookie Sets a cookie for this distributed node
|
||||
echo --hidden Makes a hidden node
|
||||
echo --detached Starts the Erlang VM detached from console
|
||||
echo --no-halt Does not halt the Erlang VM after execution
|
||||
echo.
|
||||
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
|
||||
echo ** Options can be passed to the erlang runtime using ELIXIR_ERL_OPTS or --erl
|
||||
goto :EOF
|
||||
|
||||
:parseopts
|
||||
|
||||
rem Parameters for Erlang
|
||||
set parsErlang=
|
||||
|
||||
rem Make sure we keep a copy of all parameters
|
||||
set allPars=%*
|
||||
|
||||
rem Get the original path name from the batch file
|
||||
set originPath=%~dp0
|
||||
|
||||
rem Optional parameters before the "-extra" parameter
|
||||
set beforeExtra=
|
||||
|
||||
rem Recursive loop called for each parameter that parses the cmd line parameters
|
||||
:startloop
|
||||
set par=%1
|
||||
shift
|
||||
if "%par%"=="" (
|
||||
rem if no parameters defined
|
||||
goto :run
|
||||
)
|
||||
rem ******* ERLANG PARAMETERS **********************
|
||||
for /f "usebackq" %%m in (`echo %par%^|findstr \--detached`) do (
|
||||
set parsErlang=%parsErlang% -detached
|
||||
goto:startloop
|
||||
)
|
||||
for /f "usebackq" %%m in (`echo %par%^|findstr \--hidden`) do (
|
||||
set parsErlang=%parsErlang% -hidden
|
||||
goto:startloop
|
||||
)
|
||||
for /f "usebackq" %%m in (`echo %par%^|findstr \--cookie`) do (
|
||||
set parsErlang=%parsErlang% -setcookie %1
|
||||
shift
|
||||
goto:startloop
|
||||
)
|
||||
for /f "usebackq" %%m in (`echo %par%^|findstr \--sname`) do (
|
||||
set parsErlang=%parsErlang% -sname %1
|
||||
shift
|
||||
goto:startloop
|
||||
)
|
||||
for /f "usebackq" %%m in (`echo %par%^|findstr \--name`) do (
|
||||
set parsErlang=%parsErlang% -name %1
|
||||
shift
|
||||
goto:startloop
|
||||
)
|
||||
for /f "usebackq" %%m in (`echo %par%^|findstr \--erl`) do (
|
||||
set beforeExtra=%beforeExtra% %~1
|
||||
shift
|
||||
goto:startloop
|
||||
)
|
||||
rem ******* elixir parameters **********************
|
||||
for /f "usebackq" %%m in (`echo %par%^|findstr \--v`) do (
|
||||
goto:startloop
|
||||
)
|
||||
for /f "usebackq" %%m in (`echo %par%^|findstr \--compile`) do (
|
||||
goto:startloop
|
||||
)
|
||||
for /f "usebackq" %%m in (`echo %par%^|findstr \--no-halt`) do (
|
||||
goto:startloop
|
||||
)
|
||||
for /f "usebackq" %%m in (`echo %par%^|findstr \+iex`) do (
|
||||
goto:startloop
|
||||
)
|
||||
for /f "usebackq" %%m in (`echo %par%^|findstr \+compile`) do (
|
||||
goto:startloop
|
||||
)
|
||||
for /f "usebackq" %%m in (`echo %par%^|findstr \-[er]`) do (
|
||||
shift
|
||||
goto:startloop
|
||||
)
|
||||
for /f "usebackq" %%m in (`echo %par%^|findstr \-p[raz]`) do (
|
||||
shift
|
||||
goto:startloop
|
||||
)
|
||||
for /f "usebackq" %%m in (`echo %par%^|findstr \--remsh`) do (
|
||||
shift
|
||||
goto:startloop
|
||||
)
|
||||
rem ******* assume all pre-params are parsed ********************
|
||||
:run
|
||||
erl -env ERL_LIBS %ERL_LIBS%;"%~dp0\..\lib" -noshell %ELIXIR_ERL_OPTS% -s elixir start_cli -extra %*
|
||||
erl -env ERL_LIBS %ERL_LIBS%;"%originPath%\..\lib" -noshell %ELIXIR_ERL_OPTS% %parsErlang% -s elixir start_cli %beforeExtra% -extra %*
|
||||
|
||||
@@ -6,6 +6,7 @@ if [ $# -eq 0 ]; then
|
||||
--no-docs Do not attach documentation to compiled modules
|
||||
--no-debug-info Do not attach debug info to compiled modules
|
||||
--ignore-module-conflict
|
||||
--warnings-as-errors Treat warnings as errors and return non-zero exit code
|
||||
|
||||
** Options given after -- are passed down to the executed code
|
||||
** Options can be passed to the erlang runtime using ELIXIR_ERL_OPTS" >&2
|
||||
|
||||
@@ -13,6 +13,7 @@ echo -o The directory to output compiled files
|
||||
echo --no-docs Do not attach documentation to compiled modules
|
||||
echo --no-debug-info Do not attach debug info to compiled modules
|
||||
echo --ignore-module-conflict
|
||||
echo --warnings-as-errors Treat warnings as errors and return non-zero exit code
|
||||
echo.
|
||||
echo ** Options marked with (*) can be given more than once
|
||||
echo ** Options given after -- are passed down to the executed code
|
||||
|
||||
@@ -13,6 +13,9 @@ if [ $# -gt 0 ] && ([ $1 = "--help" ] || [ $1 = "-h" ]); then
|
||||
--erl \"switches\" Switches to be passed down to erlang (*)
|
||||
--name \"name\" Makes and assigns a name to the distributed node
|
||||
--sname \"name\" Makes and assigns a short name to the distributed node
|
||||
--cookie \"cookie\" Sets a cookie for this distributed node
|
||||
--hidden Makes a hidden node
|
||||
--detached Starts the Erlang VM detached from console
|
||||
--remsh \"name\" Connects to a node using a remote shell
|
||||
--dot-iex \"path\" Overrides default .iex file and uses path instead;
|
||||
path can be empty, then no file will be loaded
|
||||
|
||||
@@ -75,7 +75,7 @@ defmodule EEx.Compiler do
|
||||
# Check if the syntax node represents an empty string
|
||||
|
||||
defp is_empty?(bin) when is_binary(bin) do
|
||||
bc(<<c>> inbits bin, not c in [?\s,?\t,?\r,?\n], do: <<c>>) == ""
|
||||
bc(<<c>> inbits bin, not c in [?\s, ?\t, ?\r, ?\n], do: <<c>>) == ""
|
||||
end
|
||||
|
||||
defp is_empty?({ :<>, _, [left, right] }) do
|
||||
|
||||
@@ -20,8 +20,8 @@ defmodule EEx.Tokenizer do
|
||||
end
|
||||
|
||||
defp tokenize('<%%' ++ t, current_line, line, buffer, acc) do
|
||||
{ buffer, new_line, rest } = tokenize_expr t, line, [?%,?<|buffer]
|
||||
tokenize rest, current_line, new_line, [?>,?%|buffer], acc
|
||||
{ buffer, new_line, rest } = tokenize_expr t, line, [?%, ?<|buffer]
|
||||
tokenize rest, current_line, new_line, [?>, ?%|buffer], acc
|
||||
end
|
||||
|
||||
defp tokenize('<%#' ++ t, current_line, line, buffer, acc) do
|
||||
@@ -63,7 +63,7 @@ defmodule EEx.Tokenizer do
|
||||
|
||||
# Tokenize an expression until we find %>
|
||||
|
||||
defp tokenize_expr([?%,?>|t], line, buffer) do
|
||||
defp tokenize_expr([?%, ?>|t], line, buffer) do
|
||||
{ buffer, line, t }
|
||||
end
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ defmodule EEx.SmartEngineTest do
|
||||
end
|
||||
|
||||
test "evaluates with loops" do
|
||||
assert_eval "1\n2\n3\n", "<%= lc x inlist [1,2,3] do %><%= x %>\n<% end %>"
|
||||
assert_eval "1\n2\n3\n", "<%= lc x inlist [1, 2, 3] do %><%= x %>\n<% end %>"
|
||||
end
|
||||
|
||||
defp assert_eval(expected, actual, binding // []) do
|
||||
|
||||
@@ -21,7 +21,7 @@ defmodule EEx.TokenizerTest do
|
||||
end
|
||||
|
||||
test "strings with more than one line" do
|
||||
assert T.tokenize('foo\n<%= bar %>', 1) == [ { :text, 1, "foo\n" },{ :expr, 2, '=', ' bar ' } ]
|
||||
assert T.tokenize('foo\n<%= bar %>', 1) == [ { :text, 1, "foo\n" }, { :expr, 2, '=', ' bar ' } ]
|
||||
end
|
||||
|
||||
test "strings with more than one line and expression with more than one line" do
|
||||
|
||||
@@ -101,7 +101,7 @@ defmodule EExTest do
|
||||
end
|
||||
|
||||
test "evaluates with require code" do
|
||||
assert_eval "foo 1,2,3", "foo <% require Enum, as: E %><%= E.join [1,2,3], \",\" %>"
|
||||
assert_eval "foo 1,2,3", "foo <% require Enum, as: E %><%= E.join [1, 2, 3], \",\" %>"
|
||||
end
|
||||
|
||||
test "evaluates with end of token" do
|
||||
@@ -242,7 +242,7 @@ Number 3
|
||||
"""
|
||||
|
||||
string = """
|
||||
<%= Enum.map [1,2,3], fn x -> %>
|
||||
<%= Enum.map [1, 2, 3], fn x -> %>
|
||||
Number <%= x %>
|
||||
<% end %>
|
||||
"""
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
noname=false, %% when true, don't add new names (used by try)
|
||||
super=false, %% when true, it means super was invoked
|
||||
caller=false, %% when true, it means caller was invoked
|
||||
name_args=false, %% when true, it means arguments should be named
|
||||
module=nil, %% the current module
|
||||
function=nil, %% the current function
|
||||
vars=[], %% a dict of defined variables and their alias
|
||||
temp_vars=[], %% a dict of all variables defined in a particular assign
|
||||
backup_vars=nil, %% a copy of vars to be used on ^var
|
||||
temp_vars=nil, %% a set of all variables defined in a particular assign
|
||||
clause_vars=nil, %% a dict of all variables defined in a particular clause
|
||||
extra_guards=nil, %% extra guards from args expansion
|
||||
counter=[], %% a counter for the variables defined
|
||||
|
||||
@@ -57,7 +57,7 @@ defmodule Behaviour do
|
||||
case Macro.extract_args(fun) do
|
||||
{ name, args } -> :ok
|
||||
:error ->
|
||||
raise ArgumentError, message: "invalid syntax in defcallback #{Macro.to_binary(fun)}"
|
||||
raise ArgumentError, message: "invalid syntax in defcallback #{Macro.to_string(fun)}"
|
||||
end
|
||||
|
||||
arity = length(args)
|
||||
@@ -113,4 +113,4 @@ defmodule Behaviour do
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -314,7 +314,7 @@ defimpl Binary.Inspect, for: Tuple do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> inspect({1,2,3})
|
||||
iex> inspect({1, 2, 3})
|
||||
"{1,2,3}"
|
||||
iex> inspect(ArgumentError.new)
|
||||
"ArgumentError[message: \\\"argument error\\\"]"
|
||||
@@ -335,7 +335,7 @@ defimpl Binary.Inspect, for: Tuple do
|
||||
list = tuple_to_list(record)
|
||||
[name|tail] = list
|
||||
|
||||
if (fields = record_fields(name)) && (length(fields) == size(record) - 1) do
|
||||
if is_atom(name) && (fields = record_fields(name)) && (length(fields) == size(record) - 1) do
|
||||
if Enum.first(tail) == :__exception__ do
|
||||
record_join(name, tl(fields), tl(tail), opts)
|
||||
else
|
||||
|
||||
+93
-28
@@ -72,11 +72,13 @@ defmodule Code do
|
||||
* `:aliases` - a list of tuples with the alias and its target
|
||||
* `:requires` - a list of modules required
|
||||
* `:functions` - a list of tuples where the first element is a module
|
||||
and the second a list of imported function names and arity
|
||||
and the second a list of imported function names and arity. The list
|
||||
of function names and arity must be sorted;
|
||||
* `:macros` - a list of tuples where the first element is a module
|
||||
and the second a list of imported macro names and arity
|
||||
and the second a list of imported macro names and arity. The list
|
||||
of function names and arity must be sorted;
|
||||
|
||||
Notice that setting any ov the values above overrides Elixir default
|
||||
Notice that setting any of the values above overrides Elixir default
|
||||
values. For example, setting `:requires` to `[]`, will no longer
|
||||
automatically required the `Kernel` module, in the same way setting
|
||||
`:macros` will no longer auto-import `Kernel` macros as `if`, `case`,
|
||||
@@ -98,10 +100,15 @@ defmodule Code do
|
||||
def eval_string(string, binding // [], opts // [])
|
||||
|
||||
def eval_string(string, binding, Macro.Env[] = env) do
|
||||
eval_string(string, binding, env.to_keywords)
|
||||
do_eval_string(string, binding, env.to_keywords)
|
||||
end
|
||||
|
||||
def eval_string(string, binding, opts) do
|
||||
def eval_string(string, binding, opts) when is_list(opts) do
|
||||
validate_eval_opts(opts)
|
||||
do_eval_string(string, binding, opts)
|
||||
end
|
||||
|
||||
defp do_eval_string(string, binding, opts) when is_list(binding) do
|
||||
{ value, binding, _scope } =
|
||||
:elixir.eval :unicode.characters_to_list(string), binding, opts
|
||||
{ value, binding }
|
||||
@@ -130,17 +137,65 @@ defmodule Code do
|
||||
def eval_quoted(quoted, binding // [], opts // [])
|
||||
|
||||
def eval_quoted(quoted, binding, Macro.Env[] = env) do
|
||||
eval_quoted(quoted, binding, env.to_keywords)
|
||||
do_eval_quoted(quoted, binding, env.to_keywords)
|
||||
end
|
||||
|
||||
def eval_quoted(quoted, binding, opts) do
|
||||
def eval_quoted(quoted, binding, opts) when is_list(opts) do
|
||||
validate_eval_opts(opts)
|
||||
do_eval_quoted(quoted, binding, opts)
|
||||
end
|
||||
|
||||
defp do_eval_quoted(quoted, binding, opts) when is_list(binding) do
|
||||
{ value, binding, _scope } =
|
||||
:elixir.eval_quoted [quoted], binding, opts
|
||||
{ value, binding }
|
||||
end
|
||||
|
||||
defp validate_eval_opts(opts) do
|
||||
if f = opts[:functions], do: validate_imports(:functions, f)
|
||||
if m = opts[:macros], do: validate_imports(:macros, m)
|
||||
if a = opts[:aliases], do: validate_aliases(:aliases, a)
|
||||
if r = opts[:requires], do: validate_requires(:requires, r)
|
||||
end
|
||||
|
||||
defp validate_requires(kind, requires) do
|
||||
valid = is_list(requires) and Enum.all?(requires, is_atom(&1))
|
||||
|
||||
unless valid do
|
||||
raise ArgumentError, "expected :#{kind} option given to eval in the format: [module]"
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_aliases(kind, aliases) do
|
||||
valid = is_list(aliases) and Enum.all?(aliases, fn { k, v } ->
|
||||
is_atom(k) and is_atom(v)
|
||||
end)
|
||||
|
||||
unless valid do
|
||||
raise ArgumentError, "expected :#{kind} option given to eval in the format: [{ module, module }]"
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_imports(kind, imports) do
|
||||
valid = is_list(imports) and Enum.all?(imports, fn { k, v } ->
|
||||
is_atom(k) and is_list(v) and Enum.all?(v, fn { name, arity } ->
|
||||
is_atom(name) and is_integer(arity)
|
||||
end)
|
||||
end)
|
||||
|
||||
unless valid do
|
||||
raise ArgumentError, "expected :#{kind} option given to eval in the format: [{ module, [{ name, arity }] }]"
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def string_to_ast(string, opts // []) do
|
||||
IO.write "[WARNING] Code.string_to_ast is deprecated, please use Code.string_to_quoted instead\n#{Exception.format_stacktrace}"
|
||||
string_to_quoted(string, opts)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Converts the given string to AST. It returns `{ :ok, ast }`
|
||||
Converts the given string to quoted form. It returns `{ :ok, quoted_form }`
|
||||
if it succeeds, `{ :error, { line, error, token } }` otherwise.
|
||||
|
||||
## Options
|
||||
@@ -153,41 +208,47 @@ defmodule Code do
|
||||
* `:existing_atoms_only` - When true, raises an error
|
||||
when non-existing atoms are found by the tokenizer.
|
||||
|
||||
## Macro.to_binary/1
|
||||
## Macro.to_string/1
|
||||
|
||||
The opposite of converting a string to its AST is
|
||||
`Macro.to_binary`, which converts a AST to a binary
|
||||
The opposite of converting a string to its quoted form is
|
||||
`Macro.to_string`, which converts a quoted form to a string/binary
|
||||
representation.
|
||||
"""
|
||||
def string_to_ast(string, opts // []) do
|
||||
def string_to_quoted(string, opts // []) do
|
||||
file = Keyword.get opts, :file, "nofile"
|
||||
line = Keyword.get opts, :line, 1
|
||||
res = :elixir_translator.forms(:unicode.characters_to_list(string), line, file, opts)
|
||||
|
||||
case res do
|
||||
{ :ok, ast } -> { :ok, unpack_ast(line, ast) }
|
||||
{ :ok, forms } -> { :ok, unpack_quote(line, forms) }
|
||||
_ -> res
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def string_to_ast!(string, opts // []) do
|
||||
IO.write "[WARNING] Code.string_to_ast! is deprecated, please use Code.string_to_quoted! instead\n#{Exception.format_stacktrace}"
|
||||
string_to_quoted!(string, opts)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Converts the given string to AST. It returns the ast if it succeeds,
|
||||
Converts the given string to quoted form. It returns the ast if it succeeds,
|
||||
raises an exception otherwise. The exception is a TokenMissingError
|
||||
in case a token is missing (usually because the expression is incomplete),
|
||||
SyntaxError otherwise.
|
||||
|
||||
Check `Code.string_to_ast/2` for options information.
|
||||
Check `Code.string_to_quoted/2` for options information.
|
||||
"""
|
||||
def string_to_ast!(string, opts // []) do
|
||||
def string_to_quoted!(string, opts // []) do
|
||||
file = Keyword.get opts, :file, "nofile"
|
||||
line = Keyword.get opts, :line, 1
|
||||
res = :elixir_translator.forms!(:unicode.characters_to_list(string), line, file, opts)
|
||||
unpack_ast(line, res)
|
||||
unpack_quote(line, res)
|
||||
end
|
||||
|
||||
defp unpack_ast(_line, []), do: nil
|
||||
defp unpack_ast(_line, [forms]) when not is_list(forms), do: forms
|
||||
defp unpack_ast(line, forms), do: { :__block__, [line: line], forms }
|
||||
defp unpack_quote(_line, []), do: nil
|
||||
defp unpack_quote(_line, [forms]) when not is_list(forms), do: forms
|
||||
defp unpack_quote(line, forms), do: { :__block__, [line: line], forms }
|
||||
|
||||
@doc """
|
||||
Loads the given `file`. Accepts `relative_to` as an argument to tell where
|
||||
@@ -239,7 +300,7 @@ defmodule Code do
|
||||
|
||||
@doc """
|
||||
Loads the compilation options from the code server.
|
||||
Check compiler_options/1 for more information.
|
||||
Check `compiler_options/1` for more information.
|
||||
"""
|
||||
def compiler_options do
|
||||
:elixir_code_server.call :compiler_options
|
||||
@@ -251,13 +312,17 @@ defmodule Code do
|
||||
|
||||
Available options are:
|
||||
|
||||
* docs - when true, retain documentation in the compiled module.
|
||||
True by default;
|
||||
* debug_info - when true, retain debug information in the compiled module.
|
||||
This allows a developer to reconstruct the original source
|
||||
code, for such reasons, false by default;
|
||||
* ignore_module_conflict - when true, override modules that were already defined
|
||||
without raising errors, false by default;
|
||||
* `:docs` - when true, retain documentation in the compiled module.
|
||||
True by default;
|
||||
|
||||
* `:debug_info` - when true, retain debug information in the compiled module.
|
||||
This allows a developer to reconstruct the original source
|
||||
code, for such reasons, false by default;
|
||||
|
||||
* `:ignore_module_conflict` - when true, override modules that were already defined
|
||||
without raising errors, false by default;
|
||||
|
||||
* `:warnings_as_errors` - cause compilation to fail when warnings are spewed;
|
||||
|
||||
"""
|
||||
def compiler_options(opts) do
|
||||
|
||||
@@ -387,7 +387,7 @@ defmodule Dict do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a new dict where the the given `keys` a removed from `dict`.
|
||||
Returns a new dict where the given `keys` are removed from `dict`.
|
||||
Any non-member keys are ignored.
|
||||
|
||||
## Examples
|
||||
|
||||
+60
-58
@@ -8,7 +8,7 @@ defprotocol Enumerable do
|
||||
|
||||
For example, in the expression
|
||||
|
||||
Enum.map([1,2,3], &1 * 2)
|
||||
Enum.map([1, 2, 3], &1 * 2)
|
||||
|
||||
`Enum.map` invokes `Enumerable.reduce` to perform the reducing operation
|
||||
that builds a mapped list by calling the mapping function `&1 * 2` on every
|
||||
@@ -58,13 +58,15 @@ defmodule Enum do
|
||||
flavours. If a given collection implements the mentioned protocol (like
|
||||
list, for instance), you can do:
|
||||
|
||||
Enum.map([1,2,3], fn(x) -> x * 2 end)
|
||||
Enum.map([1, 2, 3], fn(x) -> x * 2 end)
|
||||
|
||||
Depending on the type of the collection, the user-provided function will
|
||||
accept a certain type of argument. For dicts, the argument is always a
|
||||
`{ key, value }` tuple.
|
||||
"""
|
||||
|
||||
@compile :inline_list_funcs
|
||||
|
||||
@type t :: Enumerable.t
|
||||
@type element :: any
|
||||
@type index :: non_neg_integer
|
||||
@@ -91,7 +93,7 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.count([1,2,3])
|
||||
iex> Enum.count([1, 2, 3])
|
||||
3
|
||||
|
||||
"""
|
||||
@@ -117,18 +119,18 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.all?([2,4,6], fn(x) -> rem(x, 2) == 0 end)
|
||||
iex> Enum.all?([2, 4, 6], fn(x) -> rem(x, 2) == 0 end)
|
||||
true
|
||||
|
||||
iex> Enum.all?([2,3,4], fn(x) -> rem(x, 2) == 0 end)
|
||||
iex> Enum.all?([2, 3, 4], fn(x) -> rem(x, 2) == 0 end)
|
||||
false
|
||||
|
||||
If no function is given, it defaults to checking if
|
||||
all items in the collection evaluate to true.
|
||||
|
||||
iex> Enum.all?([1,2,3])
|
||||
iex> Enum.all?([1, 2, 3])
|
||||
true
|
||||
iex> Enum.all?([1,nil,3])
|
||||
iex> Enum.all?([1, nil, 3])
|
||||
false
|
||||
|
||||
"""
|
||||
@@ -155,18 +157,18 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.any?([2,4,6], fn(x) -> rem(x, 2) == 1 end)
|
||||
iex> Enum.any?([2, 4, 6], fn(x) -> rem(x, 2) == 1 end)
|
||||
false
|
||||
|
||||
iex> Enum.any?([2,3,4], fn(x) -> rem(x, 2) == 1 end)
|
||||
iex> Enum.any?([2, 3, 4], fn(x) -> rem(x, 2) == 1 end)
|
||||
true
|
||||
|
||||
If no function is given, it defaults to checking if
|
||||
at least one item in the collection evaluates to true.
|
||||
|
||||
iex> Enum.any?([false,false,false])
|
||||
iex> Enum.any?([false, false, false])
|
||||
false
|
||||
iex> Enum.any?([false,true,false])
|
||||
iex> Enum.any?([false, true, false])
|
||||
true
|
||||
|
||||
"""
|
||||
@@ -196,13 +198,13 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.at([2,4,6], 0)
|
||||
iex> Enum.at([2, 4, 6], 0)
|
||||
2
|
||||
iex> Enum.at([2,4,6], 2)
|
||||
iex> Enum.at([2, 4, 6], 2)
|
||||
6
|
||||
iex> Enum.at([2,4,6], 4)
|
||||
iex> Enum.at([2, 4, 6], 4)
|
||||
nil
|
||||
iex> Enum.at([2,4,6], 4, :none)
|
||||
iex> Enum.at([2, 4, 6], 4, :none)
|
||||
:none
|
||||
|
||||
"""
|
||||
@@ -221,11 +223,11 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.drop([1,2,3], 2)
|
||||
iex> Enum.drop([1, 2, 3], 2)
|
||||
[3]
|
||||
iex> Enum.drop([1,2,3], 10)
|
||||
iex> Enum.drop([1, 2, 3], 10)
|
||||
[]
|
||||
iex> Enum.drop([1,2,3], 0)
|
||||
iex> Enum.drop([1, 2, 3], 0)
|
||||
[1,2,3]
|
||||
|
||||
"""
|
||||
@@ -253,7 +255,7 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.drop_while([1,2,3,4,5], fn(x) -> x < 3 end)
|
||||
iex> Enum.drop_while([1, 2, 3, 4, 5], fn(x) -> x < 3 end)
|
||||
[3,4,5]
|
||||
"""
|
||||
@spec drop_while(t, (element -> as_boolean(term))) :: list
|
||||
@@ -313,7 +315,7 @@ defmodule Enum do
|
||||
|
||||
iex> Enum.empty?([])
|
||||
true
|
||||
iex> Enum.empty?([1,2,3])
|
||||
iex> Enum.empty?([1, 2, 3])
|
||||
false
|
||||
|
||||
"""
|
||||
@@ -336,11 +338,11 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.fetch([2,4,6], 0)
|
||||
iex> Enum.fetch([2, 4, 6], 0)
|
||||
{ :ok, 2 }
|
||||
iex> Enum.fetch([2,4,6], 2)
|
||||
iex> Enum.fetch([2, 4, 6], 2)
|
||||
{ :ok, 6 }
|
||||
iex> Enum.fetch([2,4,6], 4)
|
||||
iex> Enum.fetch([2, 4, 6], 4)
|
||||
:error
|
||||
|
||||
"""
|
||||
@@ -370,13 +372,13 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.fetch!([2,4,6], 0)
|
||||
iex> Enum.fetch!([2, 4, 6], 0)
|
||||
2
|
||||
|
||||
iex> Enum.fetch!([2,4,6], 2)
|
||||
iex> Enum.fetch!([2, 4, 6], 2)
|
||||
6
|
||||
|
||||
iex> Enum.fetch!([2,4,6], 4)
|
||||
iex> Enum.fetch!([2, 4, 6], 4)
|
||||
** (Enum.OutOfBoundsError) out of bounds error
|
||||
|
||||
"""
|
||||
@@ -435,13 +437,13 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.find([2,4,6], fn(x) -> rem(x, 2) == 1 end)
|
||||
iex> Enum.find([2, 4, 6], fn(x) -> rem(x, 2) == 1 end)
|
||||
nil
|
||||
|
||||
iex> Enum.find([2,4,6], 0, fn(x) -> rem(x, 2) == 1 end)
|
||||
iex> Enum.find([2, 4, 6], 0, fn(x) -> rem(x, 2) == 1 end)
|
||||
0
|
||||
|
||||
iex> Enum.find([2,3,4], fn(x) -> rem(x, 2) == 1 end)
|
||||
iex> Enum.find([2, 3, 4], fn(x) -> rem(x, 2) == 1 end)
|
||||
3
|
||||
|
||||
"""
|
||||
@@ -468,10 +470,10 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.find_value([2,4,6], fn(x) -> rem(x, 2) == 1 end)
|
||||
iex> Enum.find_value([2, 4, 6], fn(x) -> rem(x, 2) == 1 end)
|
||||
nil
|
||||
|
||||
iex> Enum.find_value([2,3,4], fn(x) -> rem(x, 2) == 1 end)
|
||||
iex> Enum.find_value([2, 3, 4], fn(x) -> rem(x, 2) == 1 end)
|
||||
true
|
||||
|
||||
"""
|
||||
@@ -501,10 +503,10 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.find_index([2,4,6], fn(x) -> rem(x, 2) == 1 end)
|
||||
iex> Enum.find_index([2, 4, 6], fn(x) -> rem(x, 2) == 1 end)
|
||||
nil
|
||||
|
||||
iex> Enum.find_index([2,3,4], fn(x) -> rem(x, 2) == 1 end)
|
||||
iex> Enum.find_index([2, 3, 4], fn(x) -> rem(x, 2) == 1 end)
|
||||
1
|
||||
|
||||
"""
|
||||
@@ -529,7 +531,7 @@ defmodule Enum do
|
||||
|
||||
iex> Enum.first([])
|
||||
nil
|
||||
iex> Enum.first([1,2,3])
|
||||
iex> Enum.first([1, 2, 3])
|
||||
1
|
||||
|
||||
"""
|
||||
@@ -557,11 +559,11 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.join([1,2,3])
|
||||
iex> Enum.join([1, 2, 3])
|
||||
"123"
|
||||
iex> Enum.join([1,2,3], " = ")
|
||||
iex> Enum.join([1, 2, 3], " = ")
|
||||
"1 = 2 = 3"
|
||||
iex> Enum.join([1,2,3], ' = ')
|
||||
iex> Enum.join([1, 2, 3], ' = ')
|
||||
'1 = 2 = 3'
|
||||
|
||||
"""
|
||||
@@ -627,11 +629,11 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.map_join([1,2,3], &1 * 2)
|
||||
iex> Enum.map_join([1, 2, 3], &1 * 2)
|
||||
"246"
|
||||
iex> Enum.map_join([1,2,3], " = ", &1 * 2)
|
||||
iex> Enum.map_join([1, 2, 3], " = ", &1 * 2)
|
||||
"2 = 4 = 6"
|
||||
iex> Enum.map_join([1,2,3], ' = ', &1 * 2)
|
||||
iex> Enum.map_join([1, 2, 3], ' = ', &1 * 2)
|
||||
'2 = 4 = 6'
|
||||
|
||||
"""
|
||||
@@ -739,7 +741,7 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.sort([3,2,1])
|
||||
iex> Enum.sort([3, 2, 1])
|
||||
[1,2,3]
|
||||
|
||||
"""
|
||||
@@ -759,7 +761,7 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.sort([1,2,3], &1 > &2)
|
||||
iex> Enum.sort([1, 2, 3], &1 > &2)
|
||||
[3,2,1]
|
||||
|
||||
"""
|
||||
@@ -785,15 +787,15 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.split([1,2,3], 2)
|
||||
iex> Enum.split([1, 2, 3], 2)
|
||||
{ [1,2], [3] }
|
||||
iex> Enum.split([1,2,3], 10)
|
||||
iex> Enum.split([1, 2, 3], 10)
|
||||
{ [1,2,3], [] }
|
||||
iex> Enum.split([1,2,3], 0)
|
||||
iex> Enum.split([1, 2, 3], 0)
|
||||
{ [], [1,2,3] }
|
||||
iex> Enum.split([1,2,3], -1)
|
||||
iex> Enum.split([1, 2, 3], -1)
|
||||
{ [1,2], [3] }
|
||||
iex> Enum.split([1,2,3], -5)
|
||||
iex> Enum.split([1, 2, 3], -5)
|
||||
{ [], [1,2,3] }
|
||||
|
||||
"""
|
||||
@@ -825,7 +827,7 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.split_while([1,2,3,4], fn(x) -> x < 3 end)
|
||||
iex> Enum.split_while([1, 2, 3, 4], fn(x) -> x < 3 end)
|
||||
{ [1, 2], [3, 4] }
|
||||
"""
|
||||
@spec split_while(t, (element -> as_boolean(term))) :: {list, list}
|
||||
@@ -850,11 +852,11 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.take([1,2,3], 2)
|
||||
iex> Enum.take([1, 2, 3], 2)
|
||||
[1,2]
|
||||
iex> Enum.take([1,2,3], 10)
|
||||
iex> Enum.take([1, 2, 3], 10)
|
||||
[1,2,3]
|
||||
iex> Enum.take([1,2,3], 0)
|
||||
iex> Enum.take([1, 2, 3], 0)
|
||||
[]
|
||||
|
||||
"""
|
||||
@@ -870,10 +872,10 @@ defmodule Enum do
|
||||
|
||||
def take(collection, count) when count > 0 do
|
||||
{ list, _ } = Enumerable.reduce(collection, { [], count }, fn(entry, { list, count }) ->
|
||||
if count > 0 do
|
||||
if count > 1 do
|
||||
{ [entry|list], count - 1 }
|
||||
else
|
||||
throw({ :enum_take, list })
|
||||
throw({ :enum_take, [entry|list] })
|
||||
end
|
||||
end)
|
||||
:lists.reverse(list)
|
||||
@@ -892,7 +894,7 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.take_while([1,2,3], fn(x) -> x < 3 end)
|
||||
iex> Enum.take_while([1, 2, 3], fn(x) -> x < 3 end)
|
||||
[1, 2]
|
||||
|
||||
"""
|
||||
@@ -938,10 +940,10 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.uniq([1,2,3,2,1])
|
||||
iex> Enum.uniq([1, 2, 3, 2, 1])
|
||||
[1, 2, 3]
|
||||
|
||||
iex> Enum.uniq([{1,:x}, {2,:y}, {1,:z}], fn {x,_} -> x end)
|
||||
iex> Enum.uniq([{1, :x}, {2, :y}, {1, :z}], fn {x, _} -> x end)
|
||||
[{1,:x}, {2,:y}]
|
||||
|
||||
"""
|
||||
@@ -996,7 +998,7 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.max([1,2,3])
|
||||
iex> Enum.max([1, 2, 3])
|
||||
3
|
||||
|
||||
"""
|
||||
@@ -1071,7 +1073,7 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.min([1,2,3])
|
||||
iex> Enum.min([1, 2, 3])
|
||||
1
|
||||
|
||||
"""
|
||||
|
||||
+16
-10
@@ -192,9 +192,7 @@ defmodule Exception do
|
||||
calculates the current stacktrace and formats it. As consequence,
|
||||
the value of `System.stacktrace` is changed.
|
||||
"""
|
||||
def format_stacktrace(trace // nil)
|
||||
|
||||
def format_stacktrace(trace) do
|
||||
def format_stacktrace(trace // nil) do
|
||||
trace = trace || try do
|
||||
throw(:stacktrace)
|
||||
catch
|
||||
@@ -208,14 +206,22 @@ defmodule Exception do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Prints the current stacktrace to standard output.
|
||||
|
||||
A stacktrace must be given as argument. If not, this function
|
||||
calculates the current stacktrace and formats it. As consequence,
|
||||
the value of `System.stacktrace` is changed.
|
||||
Formats the caller, i.e. the first entry in the stacktrace.
|
||||
Notice that doing to tail call optimization, the stacktrace
|
||||
may not report the direct caller of the function.
|
||||
"""
|
||||
def print_stacktrace(trace // nil) do
|
||||
IO.write format_stacktrace(trace)
|
||||
def format_caller(trace // nil) do
|
||||
trace = trace || try do
|
||||
throw(:stacktrace)
|
||||
catch
|
||||
:stacktrace -> Enum.drop(:erlang.get_stacktrace, 1)
|
||||
end
|
||||
|
||||
if entry = Enum.at(trace, 1) do
|
||||
format_stacktrace_entry(entry)
|
||||
else
|
||||
"nofile:0: "
|
||||
end
|
||||
end
|
||||
|
||||
## Helpers
|
||||
|
||||
+169
-98
@@ -38,10 +38,12 @@ defexception File.Error, [reason: nil, action: "", path: nil] do
|
||||
end
|
||||
end
|
||||
|
||||
defexception File.CopyError, [reason: nil, action: "", source: nil, destination: nil] do
|
||||
defexception File.CopyError, [reason: nil, action: "", source: nil, destination: nil, on: nil] do
|
||||
def message(exception) do
|
||||
formatted = list_to_binary(:file.format_error(reason exception))
|
||||
"could not #{action exception} from #{source exception} to #{destination exception}: #{formatted}"
|
||||
location = if on = on(exception), do: ". #{on}", else: ""
|
||||
"could not #{action exception} from #{source exception} to " <>
|
||||
"#{destination exception}#{location}: #{formatted}"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -54,28 +56,30 @@ end
|
||||
|
||||
defmodule File do
|
||||
@moduledoc """
|
||||
This module contains function to manipulate files.
|
||||
Many of the functions that interact with the filesystem
|
||||
have their naming based on its UNIX variants. For
|
||||
example, deleting a file is done with `File.rm`.
|
||||
Getting its stats with `File.stat`.
|
||||
This module contains functions to manipulate files.
|
||||
|
||||
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
|
||||
Some of those functions are low-level, allowing the user
|
||||
to interact with the file or IO devices, like `File.open/2`,
|
||||
`File.copy/3` and others. This module also provides higher
|
||||
level functions that works with filenames and have their naming
|
||||
based on UNIX variants. For example, one can copy a file
|
||||
via `File.cp/3` and remove files and directories recursively
|
||||
via `File.rm_rf/2`
|
||||
|
||||
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.
|
||||
|
||||
Most of the functions in this module return `:ok`
|
||||
or `{ :ok, result }` in case of success, `{ :error, reason }`
|
||||
otherwise. Those function are also followed by
|
||||
a variant that ends with `!` which returns the
|
||||
result (without the `{ :ok, result }` tuple) in
|
||||
case of success or raises an exception in case it
|
||||
fails. For example:
|
||||
Most of the functions in this module return `:ok` or
|
||||
`{ :ok, result }` in case of success, `{ :error, reason }`
|
||||
otherwise. Those function are also followed by a variant
|
||||
that ends with `!` which returns the result (without the
|
||||
`{ :ok, result }` tuple) in case of success or raises an
|
||||
exception in case it fails. For example:
|
||||
|
||||
File.read("hello.txt")
|
||||
#=> { :ok, "World" }
|
||||
@@ -89,16 +93,10 @@ defmodule File do
|
||||
File.read!("invalid.txt")
|
||||
#=> raises File.Error
|
||||
|
||||
In general, a developer should use the former in case
|
||||
he wants to react in the fie does not exist. The latter
|
||||
should be used when the developer expects his software
|
||||
to fail in case the file cannot be read (i.e. it is
|
||||
literally an exception).
|
||||
|
||||
Finally, the functions in this module accept either
|
||||
a char lists or a binary. When manipulating paths, a char
|
||||
list is returned if one is given as argument. However,
|
||||
when reading files, binaries are always returned.
|
||||
In general, a developer should use the former in case he wants
|
||||
to react if the file does not exist. The latter should be used
|
||||
when the developer expects his software to fail in case the
|
||||
file cannot be read (i.e. it is literally an exception).
|
||||
"""
|
||||
|
||||
alias :file, as: F
|
||||
@@ -125,7 +123,7 @@ defmodule File do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns true if the given argument exists.
|
||||
Returns true if the given path exists.
|
||||
It can be regular file, directory, socket,
|
||||
symbolic link, named pipe or device file.
|
||||
|
||||
@@ -233,7 +231,7 @@ defmodule File do
|
||||
|
||||
@doc """
|
||||
Returns information about the `path`. If it exists, it
|
||||
returns a `{ :ok, info }` tuple, where info is as a
|
||||
returns a `{ :ok, info }` tuple, where info is a
|
||||
`File.Info` record. Retuns `{ :error, reason }` with
|
||||
the same reasons as `File.read` if a failure occurs.
|
||||
|
||||
@@ -310,17 +308,24 @@ defmodule File do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Copies the contents of `source` to `destination`. Both
|
||||
parameters can be a filename or an io device opened with `File.open`.
|
||||
`bytes_count` specifies the number of bytes to count, the default
|
||||
being `:infinity`.
|
||||
Copies the contents of `source` to `destination`.
|
||||
|
||||
If file `destination` already exists, it is overriden
|
||||
Both parameters can be a filename or an io device opened
|
||||
with `File.open/2`. `bytes_count` specifies the number of
|
||||
bytes to copy, the default being `:infinity`.
|
||||
|
||||
If file `destination` already exists, it is overwritten
|
||||
by the contents in `source`.
|
||||
|
||||
Returns `{ :ok, bytes_copied }` if successful,
|
||||
`{ :error, reason }` otherwise.
|
||||
|
||||
Compared to the `File.cp/3`, this function is more low-level,
|
||||
allowing a copy from device to device limited by a number of
|
||||
bytes. On the other hand, `File.cp/3` performs more extensive
|
||||
checks on both source and destination and it also preserves
|
||||
the file mode after copy.
|
||||
|
||||
Typical error reasons are the same as in `open/2`,
|
||||
`read/1` and `write/2`.
|
||||
"""
|
||||
@@ -329,7 +334,7 @@ defmodule File do
|
||||
end
|
||||
|
||||
@doc """
|
||||
The same as `copy/3` but raises an File.CopyError if it fails.
|
||||
The same as `copy/3` but raises an `File.CopyError` if it fails.
|
||||
Returns the `bytes_copied` otherwise.
|
||||
"""
|
||||
def copy!(source, destination, bytes_count // :infinity) do
|
||||
@@ -342,40 +347,36 @@ defmodule File do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Copies the contents in `source` to `destination`.
|
||||
Similar to the command `cp -r` in Unix systems,
|
||||
this function behaves differently depending
|
||||
if `source` and `destination` are a file or a directory.
|
||||
Copies the contents in `source` to `destination` preserving its mode.
|
||||
|
||||
If both are files, it simply copies `source` to
|
||||
`destination`. However, if `destination` is a directory,
|
||||
it copies the contents of `source` to `destination/source`
|
||||
recursively.
|
||||
Similar to the command `cp` in Unix systems, this function
|
||||
behaves differently depending if `destination` is a directory
|
||||
or not. In particular, if `destination` is a directory, it
|
||||
copies the contents of `source` to `destination/source`.
|
||||
|
||||
If a file already exists in the destination,
|
||||
it invokes a callback which should return
|
||||
true if the existing file should be overriden,
|
||||
false otherwise. It defaults to return true.
|
||||
If a file already exists in the destination, it invokes a
|
||||
callback which should return true if the existing file
|
||||
should be overwritten, false otherwise. It defaults to return true.
|
||||
|
||||
It returns `:ok` in case of success, returns
|
||||
`{ :error, reason }` otherwise.
|
||||
|
||||
If you want to copy contents from an io device to another device
|
||||
or do a straight copy from a source to a destination without
|
||||
preserving modes, check `File.copy/3` instead.
|
||||
"""
|
||||
def cp(source, destination, callback // fn(_, _) -> true end) do
|
||||
if dir?(source) do
|
||||
{ :error, :eisdir }
|
||||
else
|
||||
output =
|
||||
if dir?(destination) do
|
||||
mkdir(destination)
|
||||
FN.join(destination, FN.basename(source))
|
||||
else
|
||||
destination
|
||||
end
|
||||
|
||||
case do_cp_file(source, output, callback, []) do
|
||||
{ :error, _ } = error -> error
|
||||
_ -> :ok
|
||||
output =
|
||||
if dir?(destination) do
|
||||
mkdir(destination)
|
||||
FN.join(destination, FN.basename(source))
|
||||
else
|
||||
destination
|
||||
end
|
||||
|
||||
case do_cp_file(source, output, callback, []) do
|
||||
{ :error, reason, _ } -> { :error, reason }
|
||||
_ -> :ok
|
||||
end
|
||||
end
|
||||
|
||||
@@ -388,7 +389,8 @@ defmodule File do
|
||||
:ok -> :ok
|
||||
{ :error, reason } ->
|
||||
raise File.CopyError, reason: reason, action: "copy recursively",
|
||||
source: :unicode.characters_to_binary(source), destination: :unicode.characters_to_binary(destination)
|
||||
source: :unicode.characters_to_binary(source),
|
||||
destination: :unicode.characters_to_binary(destination)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -405,7 +407,7 @@ defmodule File do
|
||||
|
||||
If a file already exists in the destination,
|
||||
it invokes a callback which should return
|
||||
true if the existing file should be overriden,
|
||||
true if the existing file should be overwritten,
|
||||
false otherwise. It defaults to return true.
|
||||
|
||||
If a directory already exists in the destination
|
||||
@@ -434,7 +436,7 @@ defmodule File do
|
||||
|
||||
# Same as before, but asks the user how to proceed in case of conflicts
|
||||
File.cp_r "samples/.", "tmp", fn(source, destination) ->
|
||||
IO.gets("Overriding #{destination} by #{source}. Type y to confirm.") == "y"
|
||||
IO.gets("Overwriting #{destination} by #{source}. Type y to confirm.") == "y"
|
||||
end
|
||||
|
||||
"""
|
||||
@@ -448,7 +450,7 @@ defmodule File do
|
||||
end
|
||||
|
||||
case do_cp_r(source, output, callback, []) do
|
||||
{ :error, _ } = error -> error
|
||||
{ :error, _, _ } = error -> error
|
||||
res -> { :ok, res }
|
||||
end
|
||||
end
|
||||
@@ -460,19 +462,26 @@ defmodule File do
|
||||
def cp_r!(source, destination, callback // fn(_, _) -> true end) do
|
||||
case cp_r(source, destination, callback) do
|
||||
{ :ok, files } -> files
|
||||
{ :error, reason } ->
|
||||
{ :error, reason, file } ->
|
||||
raise File.CopyError, reason: reason, action: "copy recursively",
|
||||
source: :unicode.characters_to_binary(source), destination: :unicode.characters_to_binary(destination)
|
||||
source: :unicode.characters_to_binary(source),
|
||||
destination: :unicode.characters_to_binary(destination),
|
||||
on: file
|
||||
end
|
||||
end
|
||||
|
||||
# src may be a file or a directory, dest is definitely
|
||||
# a directory. Returns nil unless an error is found.
|
||||
defp do_cp_r(src, dest, callback, acc) when is_list(acc) do
|
||||
case F.read_link(src) do
|
||||
{ :ok, link } ->
|
||||
do_cp_link(link, src, dest, callback, acc)
|
||||
_ ->
|
||||
case :elixir.file_type(src) do
|
||||
{ :ok, :regular } ->
|
||||
do_cp_file(src, dest, callback, acc)
|
||||
{ :ok, :symlink } ->
|
||||
case F.read_link(src) do
|
||||
{ :ok, link } -> do_cp_link(link, src, dest, callback, acc)
|
||||
{ :error, reason } -> { :error, reason, :unicode.characters_to_binary(src) }
|
||||
end
|
||||
{ :ok, :directory } ->
|
||||
case F.list_dir(src) do
|
||||
{ :ok, files } ->
|
||||
case mkdir(dest) do
|
||||
@@ -480,12 +489,12 @@ defmodule File do
|
||||
Enum.reduce(files, [dest|acc], fn(x, acc) ->
|
||||
do_cp_r(FN.join(src, x), FN.join(dest, x), callback, acc)
|
||||
end)
|
||||
reason -> reason
|
||||
{ :error, reason } -> { :error, reason, :unicode.characters_to_binary(dest) }
|
||||
end
|
||||
{ :error, :enotdir } ->
|
||||
do_cp_file(src, dest, callback, acc)
|
||||
reason -> reason
|
||||
{ :error, reason } -> { :error, reason, :unicode.characters_to_binary(src) }
|
||||
end
|
||||
{ :ok, _ } -> { :error, :eio, src }
|
||||
{ :error, reason } -> { :error, reason, :unicode.characters_to_binary(src) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -514,12 +523,12 @@ defmodule File do
|
||||
{ :ok, _ } ->
|
||||
copy_file_mode!(src, dest)
|
||||
[dest|acc]
|
||||
reason -> reason
|
||||
{ :error, reason } -> { :error, reason, :unicode.characters_to_binary(src) }
|
||||
end
|
||||
else
|
||||
acc
|
||||
end
|
||||
reason -> reason
|
||||
{ :error, reason } -> { :error, reason, :unicode.characters_to_binary(src) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -533,12 +542,12 @@ defmodule File do
|
||||
rm(dest)
|
||||
case F.make_symlink(link, dest) do
|
||||
:ok -> [dest|acc]
|
||||
reason -> reason
|
||||
{ :error, reason } -> { :error, reason, :unicode.characters_to_binary(src) }
|
||||
end
|
||||
else
|
||||
acc
|
||||
end
|
||||
reason -> reason
|
||||
{ :error, reason } -> { :error, reason, :unicode.characters_to_binary(src) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -638,7 +647,7 @@ defmodule File do
|
||||
|
||||
@doc """
|
||||
Remove files and directories recursively at the given `path`.
|
||||
Symlinks are not followed but simply removed, non existing
|
||||
Symlinks are not followed but simply removed, non-existing
|
||||
files are simply ignored (i.e. doesn't make this function fail).
|
||||
|
||||
Returns `{ :ok, files_and_directories }` with all files and
|
||||
@@ -671,18 +680,18 @@ defmodule File do
|
||||
case rmdir(path) do
|
||||
:ok -> { :ok, [path|acc] }
|
||||
{ :error, :enoent } -> res
|
||||
reason -> reason
|
||||
reason -> { :error, reason, :unicode.characters_to_binary(path) }
|
||||
end
|
||||
reason -> reason
|
||||
reason -> { :error, reason, :unicode.characters_to_binary(path) }
|
||||
end
|
||||
{ :error, :enotdir } ->
|
||||
{ :error, reason } when reason in [:enotdir, :eio] ->
|
||||
case rm(path) do
|
||||
:ok -> { :ok, [path|acc] }
|
||||
{ :error, :enoent } -> entry
|
||||
reason -> reason
|
||||
{ :error, reason } when reason in [:enoent, :enotdir] -> entry
|
||||
reason -> { :error, reason, :unicode.characters_to_binary(path) }
|
||||
end
|
||||
{ :error, :enoent } -> entry
|
||||
reason -> reason
|
||||
{ :error, reason } -> { :error, reason, :unicode.characters_to_binary(path) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -704,8 +713,10 @@ defmodule File do
|
||||
def rm_rf!(path) do
|
||||
case rm_rf(path) do
|
||||
{ :ok, files } -> files
|
||||
{ :error, reason } ->
|
||||
raise File.Error, reason: reason, action: "remove files and directories recursively from", path: :unicode.characters_to_binary(path)
|
||||
{ :error, reason, _ } ->
|
||||
raise File.Error, reason: reason,
|
||||
action: "remove files and directories recursively from",
|
||||
path: :unicode.characters_to_binary(path)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -753,14 +764,14 @@ defmodule File do
|
||||
|
||||
This function returns:
|
||||
|
||||
* { :ok, io_device } - The file has been opened in the requested mode.
|
||||
* `{ :ok, io_device }` - The file has been opened in the requested mode.
|
||||
`io_device` is actually the pid of the process which handles the file.
|
||||
This process is linked to the process which originally opened the file.
|
||||
If any process to which the io_device is linked terminates, the file will
|
||||
be closed and the process itself will be terminated. An io_device returned
|
||||
If any process to which the `io_device` is linked terminates, the file will
|
||||
be closed and the process itself will be terminated. An `io_device` returned
|
||||
from this call can be used as an argument to the `IO` module functions.
|
||||
|
||||
* { :error, reason } - The file could not be opened.
|
||||
* `{ :error, reason }` - The file could not be opened.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -939,13 +950,13 @@ defmodule File do
|
||||
passed into `Enum`. The device is iterated line
|
||||
by line, at the end of iteration the file is closed.
|
||||
|
||||
This reads the file as utf-8. CHeck out `File.biniterator`
|
||||
This reads the file as utf-8. Check out `File.biniterator`
|
||||
to handle the file as a raw binary.
|
||||
|
||||
## Examples
|
||||
|
||||
An example that lazily iterates a file replacing all double
|
||||
quotes per single quotes and write each line to a target file
|
||||
quotes per single quotes and writes each line to a target file
|
||||
is shown below:
|
||||
|
||||
{ :ok, device } = File.open("README.md")
|
||||
@@ -1008,6 +1019,66 @@ defmodule File do
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Changes the unix file `mode` for a given `file`.
|
||||
Returns `:ok` on success, or `{:error, reason}`
|
||||
on failure.
|
||||
"""
|
||||
def chmod(file, mode) do
|
||||
F.change_mode(file, mode)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Same as `chmod/2`, but raises an exception in case of failure. Otherwise `:ok`.
|
||||
"""
|
||||
def chmod!(file, mode) do
|
||||
case chmod(file, mode) do
|
||||
:ok -> :ok
|
||||
{ :error, reason } ->
|
||||
raise File.Error, reason: reason, action: "change mode for", path: :unicode.characters_to_binary(file)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Changes the user group given by the group id `gid`
|
||||
for a given `file`. Returns `:ok` on success, or
|
||||
`{:error, reason}` on failure.
|
||||
"""
|
||||
def chgrp(file, gid) do
|
||||
F.change_group(file, gid)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Same as `chgrp/2`, but raises an exception in case of failure. Otherwise `:ok`.
|
||||
"""
|
||||
def chgrp!(file, gid) do
|
||||
case chgrp(file, gid) do
|
||||
:ok -> :ok
|
||||
{ :error, reason } ->
|
||||
raise File.Error, reason: reason, action: "change group for", path: :unicode.characters_to_binary(file)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Changes the owner given by the user id `gid`
|
||||
for a given `file`. Returns `:ok` on success,
|
||||
or `{:error, reason}` on failure.
|
||||
"""
|
||||
def chown(file, uid) do
|
||||
F.change_owner(file, uid)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Same as `chown/2`, but raises an exception in case of failure. Otherwise `:ok`.
|
||||
"""
|
||||
def chown!(file, gid) do
|
||||
case chown(file, gid) do
|
||||
:ok -> :ok
|
||||
{ :error, reason } ->
|
||||
raise File.Error, reason: reason, action: "change owner for", path: :unicode.characters_to_binary(file)
|
||||
end
|
||||
end
|
||||
|
||||
## Helpers
|
||||
|
||||
defp open_defaults([:charlist|t], _add_binary) do
|
||||
|
||||
+33
-45
@@ -72,7 +72,7 @@ defmodule HashDict do
|
||||
|
||||
## Examples
|
||||
|
||||
HashDict.new [{:b,1},{:a,2}]
|
||||
HashDict.new [{:b, 1}, {:a, 2}]
|
||||
#=> #HashDict<[a: 2, b: 1]>
|
||||
|
||||
"""
|
||||
@@ -209,11 +209,14 @@ defmodule HashDict do
|
||||
ordered()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if two dicts are equal
|
||||
"""
|
||||
def equal?(dict1, dict2) do
|
||||
size = elem(dict1, 1)
|
||||
case elem(dict2, 1) do
|
||||
^size ->
|
||||
dict_equal?(dict1, dict1)
|
||||
dict_equal?(dict1, dict2)
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
@@ -267,12 +270,6 @@ defmodule HashDict do
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Splits a dict into two dicts,
|
||||
one containing entries with key in the keys list,
|
||||
and another containing entries with key not in keys.
|
||||
Returns a 2-tuple of the new dicts.
|
||||
"""
|
||||
def split(dict, keys) do
|
||||
split(keys, new, dict)
|
||||
end
|
||||
@@ -288,10 +285,6 @@ defmodule HashDict do
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a new dict with only the entries
|
||||
which key is in keys.
|
||||
"""
|
||||
def take(dict, keys) do
|
||||
take(dict, keys, new)
|
||||
end
|
||||
@@ -304,10 +297,6 @@ defmodule HashDict do
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a new dict with only the entries
|
||||
which key is not in keys
|
||||
"""
|
||||
def drop(dict, []), do: dict
|
||||
|
||||
def drop(dict, [key|keys]) do
|
||||
@@ -379,7 +368,7 @@ defmodule HashDict do
|
||||
{ dict, value, 0 }
|
||||
{ root, value, -1 } ->
|
||||
{ if depth > 0 and trie(dict, :contract_on) == size do
|
||||
root = node_contract(root, depth, depth - 1)
|
||||
root = node_contract(root, depth)
|
||||
trie(dict,
|
||||
root: root,
|
||||
size: size - 1,
|
||||
@@ -410,11 +399,11 @@ defmodule HashDict do
|
||||
## Bucket helpers
|
||||
|
||||
# Get value from the bucket
|
||||
defp bucket_get([{k,_}|_bucket], key) when k > key do
|
||||
defp bucket_get([{k, _}|_bucket], key) when k > key do
|
||||
false
|
||||
end
|
||||
|
||||
defp bucket_get([{key,_}=e|_bucket], key) do
|
||||
defp bucket_get([{key, _}=e|_bucket], key) do
|
||||
e
|
||||
end
|
||||
|
||||
@@ -427,19 +416,19 @@ defmodule HashDict do
|
||||
end
|
||||
|
||||
# Puts a value in the bucket
|
||||
defp bucket_put([{k,_}|_]=bucket, key, { :put, value }) when k > key do
|
||||
defp bucket_put([{k, _}|_]=bucket, key, { :put, value }) when k > key do
|
||||
{ [{key, value}|bucket], 1 }
|
||||
end
|
||||
|
||||
defp bucket_put([{k,_}|_]=bucket, key, { :update, initial, _fun }) when k > key do
|
||||
defp bucket_put([{k, _}|_]=bucket, key, { :update, initial, _fun }) when k > key do
|
||||
{ [{key, initial}|bucket], 1 }
|
||||
end
|
||||
|
||||
defp bucket_put([{key,_}|bucket], key, { :put, value }) do
|
||||
{ [{key,value}|bucket], 0 }
|
||||
defp bucket_put([{key, _}|bucket], key, { :put, value }) do
|
||||
{ [{key, value}|bucket], 0 }
|
||||
end
|
||||
|
||||
defp bucket_put([{key,value}|bucket], key, { :update, _initial, fun }) do
|
||||
defp bucket_put([{key, value}|bucket], key, { :update, _initial, fun }) do
|
||||
{ [{key, fun.(value)}|bucket], 0 }
|
||||
end
|
||||
|
||||
@@ -449,26 +438,26 @@ defmodule HashDict do
|
||||
end
|
||||
|
||||
defp bucket_put([], key, { :put, value }) do
|
||||
{ [{key,value}], 1 }
|
||||
{ [{key, value}], 1 }
|
||||
end
|
||||
|
||||
defp bucket_put([], key, { :update, initial, _fun }) do
|
||||
{ [{key,initial}], 1 }
|
||||
{ [{key, initial}], 1 }
|
||||
end
|
||||
|
||||
# Puts a value in the bucket without returning
|
||||
# the operation value
|
||||
defp bucket_put!([{k,_}|_]=bucket, key, value) when k > key, do: [{key,value}|bucket]
|
||||
defp bucket_put!([{key,_}|bucket], key, value), do: [{key,value}|bucket]
|
||||
defp bucket_put!([{_,_}=e|bucket], key, value), do: [e|bucket_put!(bucket, key, value)]
|
||||
defp bucket_put!([], key, value), do: [{key,value}]
|
||||
defp bucket_put!([{k, _}|_]=bucket, key, value) when k > key, do: [{key, value}|bucket]
|
||||
defp bucket_put!([{key, _}|bucket], key, value), do: [{key, value}|bucket]
|
||||
defp bucket_put!([{_, _}=e|bucket], key, value), do: [e|bucket_put!(bucket, key, value)]
|
||||
defp bucket_put!([], key, value), do: [{key, value}]
|
||||
|
||||
# Deletes a key from the bucket
|
||||
defp bucket_delete([{k,_}|_]=bucket, key) when k > key do
|
||||
defp bucket_delete([{k, _}|_]=bucket, key) when k > key do
|
||||
{ bucket, nil, 0 }
|
||||
end
|
||||
|
||||
defp bucket_delete([{key,value}|bucket], key) do
|
||||
defp bucket_delete([{key, value}|bucket], key) do
|
||||
{ bucket, value, -1 }
|
||||
end
|
||||
|
||||
@@ -572,24 +561,23 @@ defmodule HashDict do
|
||||
node_expand(b7, depth, n), node_expand(b8, depth, n) }
|
||||
end
|
||||
|
||||
defp node_contract({ b1, b2, b3, b4, b5, b6, b7, b8 }, depth, n) when depth > 1 do
|
||||
defp node_contract({ b1, b2, b3, b4, b5, b6, b7, b8 }, depth) when depth > 0 do
|
||||
depth = depth - 1
|
||||
{ node_contract(b1, depth, n), node_contract(b2, depth, n), node_contract(b3, depth, n),
|
||||
node_contract(b4, depth, n), node_contract(b5, depth, n), node_contract(b6, depth, n),
|
||||
node_contract(b7, depth, n), node_contract(b8, depth, n) }
|
||||
{ node_contract(b1, depth), node_contract(b2, depth), node_contract(b3, depth),
|
||||
node_contract(b4, depth), node_contract(b5, depth), node_contract(b6, depth),
|
||||
node_contract(b7, depth), node_contract(b8, depth) }
|
||||
end
|
||||
|
||||
defp node_contract({ b1, b2, b3, b4, b5, b6, b7, b8 }, 1, n) do
|
||||
@node_template |> each_contract(b1, n) |> each_contract(b2, n) |> each_contract(b3, n)
|
||||
|> each_contract(b4, n) |> each_contract(b5, n) |> each_contract(b6, n)
|
||||
|> each_contract(b7, n) |> each_contract(b8, n)
|
||||
defp node_contract({ b1, b2, b3, b4, b5, b6, b7, b8 }, 0) do
|
||||
b1 |> each_contract(b2) |> each_contract(b3) |> each_contract(b4)
|
||||
|> each_contract(b5) |> each_contract(b6) |> each_contract(b7)
|
||||
|> each_contract(b8)
|
||||
end
|
||||
|
||||
defp each_contract(acc, { b1, b2, b3, b4, b5, b6, b7, b8 }, n) do
|
||||
acc |> node_relocate(b1, n) |> node_relocate(b2, n) |> node_relocate(b3, n)
|
||||
|> node_relocate(b4, n) |> node_relocate(b5, n) |> node_relocate(b6, n)
|
||||
|> node_relocate(b7, n) |> node_relocate(b8, n)
|
||||
end
|
||||
defp each_contract([{k, _v}=e|acc], [{key, _value}|_]=bucket) when k < key, do: [e|each_contract(acc, bucket)]
|
||||
defp each_contract(acc, [e|bucket]), do: [e|each_contract(acc, bucket)]
|
||||
defp each_contract([], bucket), do: bucket
|
||||
defp each_contract(acc, []), do: acc
|
||||
|
||||
defp node_relocate(node // @node_template, bucket, n) do
|
||||
:lists.foldl fn { key, value }, acc ->
|
||||
|
||||
@@ -125,6 +125,17 @@ defmodule IO do
|
||||
:io.put_chars erl_dev, [to_iodata(item), ?\n]
|
||||
end
|
||||
|
||||
@doc """
|
||||
Writes the given arguments to stderr as a warning.
|
||||
|
||||
If `--warnings-as-errors` is true and this function is
|
||||
called during compilation, compilation will fail.
|
||||
"""
|
||||
def warn(item) do
|
||||
:elixir_code_server.cast(:register_warning)
|
||||
:io.put_chars :standard_error, [to_iodata(item), ?\n]
|
||||
end
|
||||
|
||||
@doc """
|
||||
Inspects and writes the given argument to the device
|
||||
followed by a new line. A set of options can be given.
|
||||
|
||||
+11
-15
@@ -2,18 +2,14 @@ defmodule IO.ANSI.Sequence do
|
||||
@moduledoc false
|
||||
|
||||
defmacro defsequence(name, code) do
|
||||
quote do
|
||||
name = unquote(name)
|
||||
code = unquote(code)
|
||||
quote binding: [name: name, code: code] do
|
||||
def unquote(name)() do
|
||||
"\e[#{unquote(code)}m"
|
||||
end
|
||||
|
||||
def name, [], [], do:
|
||||
quote do: "\e[#{unquote(code)}m"
|
||||
|
||||
args =
|
||||
quote do: [<< unquote(atom_to_binary(name)), rest :: binary >> ]
|
||||
|
||||
defp :escape_sequence, args, [], do:
|
||||
quote do: { "\e[#{unquote(code)}m", rest }
|
||||
defp escape_sequence(<< unquote(atom_to_binary(name)), rest :: binary >>) do
|
||||
{ "\e[#{unquote(code)}m", rest }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -76,7 +72,7 @@ defmodule IO.ANSI do
|
||||
@doc "Sets primary (default) font"
|
||||
defsequence :primary_font, 10
|
||||
|
||||
lc font_n inlist [1,2,3,4,5,6,7,8,9] do
|
||||
lc font_n inlist [1, 2, 3, 4, 5, 6, 7, 8, 9] do
|
||||
@doc "Sets alternative font #{font_n}"
|
||||
defsequence :"font_#{font_n}", font_n + 10
|
||||
end
|
||||
@@ -151,7 +147,7 @@ defmodule IO.ANSI do
|
||||
|
||||
## Example
|
||||
|
||||
iex> IO.ANSI.escape("Hello %{red,bright,green}yes")
|
||||
iex> IO.ANSI.escape("Hello %{red,bright,green}yes", true)
|
||||
"Hello \e[31m\e[1m\e[32myes\e[0m"
|
||||
"""
|
||||
@spec escape(String.t, emit :: boolean) :: String.t
|
||||
@@ -177,9 +173,9 @@ defmodule IO.ANSI do
|
||||
|
||||
## Example
|
||||
|
||||
iex> IO.ANSI.escape_fragment("Hello %{red,bright,green}yes")
|
||||
iex> IO.ANSI.escape_fragment("Hello %{red,bright,green}yes", true)
|
||||
"Hello \e[31m\e[1m\e[32myes"
|
||||
iex> IO.ANSI.escape_fragment("%{reset}bye")
|
||||
iex> IO.ANSI.escape_fragment("%{reset}bye", true)
|
||||
"\e[0mbye"
|
||||
|
||||
"""
|
||||
|
||||
+270
-154
@@ -28,7 +28,9 @@ defmodule Kernel do
|
||||
3
|
||||
|
||||
"""
|
||||
defmacro left + right
|
||||
def left + right do
|
||||
:erlang.+(left, right)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Arithmetic minus. Allowed in guard clauses.
|
||||
@@ -39,7 +41,35 @@ defmodule Kernel do
|
||||
-1
|
||||
|
||||
"""
|
||||
defmacro left - right
|
||||
def left - right do
|
||||
:erlang.-(left, right)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Arithmetic unary plus. Allowed in guard clauses.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> +1
|
||||
1
|
||||
|
||||
"""
|
||||
def (+value) do
|
||||
:erlang.+(value)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Arithmetic unary minus. Allowed in guard clauses.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> -2
|
||||
-2
|
||||
|
||||
"""
|
||||
def (-value) do
|
||||
:erlang.-(value)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Arithmetic multiplication. Allowed in guard clauses.
|
||||
@@ -50,7 +80,9 @@ defmodule Kernel do
|
||||
2
|
||||
|
||||
"""
|
||||
defmacro left * right
|
||||
def left * right do
|
||||
:erlang.*(left, right)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Arithmetic division. Differently from other languages,
|
||||
@@ -65,7 +97,9 @@ defmodule Kernel do
|
||||
2.0
|
||||
|
||||
"""
|
||||
defmacro left / right
|
||||
def left / right do
|
||||
:erlang./(left, right)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Sends a message to the process identified on the left.
|
||||
@@ -78,21 +112,25 @@ defmodule Kernel do
|
||||
process <- { :ok, "Sending myself a message" }
|
||||
|
||||
"""
|
||||
defmacro pid <- msg
|
||||
def pid <- msg do
|
||||
:erlang.!(pid, msg)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Concatenates two lists. Allowed in guard clauses.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> [1] ++ [2,3]
|
||||
iex> [1] ++ [2, 3]
|
||||
[1,2,3]
|
||||
|
||||
iex> 'foo' ++ 'bar'
|
||||
'foobar'
|
||||
|
||||
"""
|
||||
defmacro left ++ right
|
||||
def left ++ right do
|
||||
:erlang.++(left, right)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Removes the first occorrence of an item on the left
|
||||
@@ -100,14 +138,16 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> [1,2,3] -- [1,2]
|
||||
iex> [1, 2, 3] -- [1, 2]
|
||||
[3]
|
||||
|
||||
iex> [1,2,3,2,1] -- [1,2,2]
|
||||
iex> [1, 2, 3, 2, 1] -- [1, 2, 2]
|
||||
[3,1]
|
||||
|
||||
"""
|
||||
defmacro left -- right
|
||||
def left -- right do
|
||||
:erlang.--(left, right)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Boolean or. Arguments must be booleans.
|
||||
@@ -158,7 +198,9 @@ defmodule Kernel do
|
||||
true
|
||||
|
||||
"""
|
||||
defmacro not arg
|
||||
def not(arg) do
|
||||
:erlang.not(arg)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Receives any argument and returns true if it is false
|
||||
@@ -169,7 +211,7 @@ defmodule Kernel do
|
||||
|
||||
iex> !1
|
||||
false
|
||||
iex> ![1,2,3]
|
||||
iex> ![1, 2, 3]
|
||||
false
|
||||
iex> !false
|
||||
true
|
||||
@@ -177,7 +219,9 @@ defmodule Kernel do
|
||||
true
|
||||
|
||||
"""
|
||||
defmacro !arg
|
||||
def !(arg) do
|
||||
arg == nil or arg == false
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns true if left is less than right.
|
||||
@@ -189,7 +233,9 @@ defmodule Kernel do
|
||||
true
|
||||
|
||||
"""
|
||||
defmacro left < right
|
||||
def left < right do
|
||||
:erlang.<(left, right)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns true if left is more than right.
|
||||
@@ -201,7 +247,9 @@ defmodule Kernel do
|
||||
false
|
||||
|
||||
"""
|
||||
defmacro left > right
|
||||
def left > right do
|
||||
:erlang.>(left, right)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns true if left is less than or equal to right.
|
||||
@@ -213,7 +261,9 @@ defmodule Kernel do
|
||||
true
|
||||
|
||||
"""
|
||||
defmacro left <= right
|
||||
def left <= right do
|
||||
:erlang."=<"(left, right)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns true if left is more than or equal to right.
|
||||
@@ -225,7 +275,9 @@ defmodule Kernel do
|
||||
false
|
||||
|
||||
"""
|
||||
defmacro left >= right
|
||||
def left >= right do
|
||||
:erlang.>=(left, right)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns true if the two items are equal.
|
||||
@@ -244,7 +296,9 @@ defmodule Kernel do
|
||||
true
|
||||
|
||||
"""
|
||||
defmacro left == right
|
||||
def left == right do
|
||||
:erlang.==(left, right)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns true if the two items are not equal.
|
||||
@@ -262,7 +316,9 @@ defmodule Kernel do
|
||||
false
|
||||
|
||||
"""
|
||||
defmacro left != right
|
||||
def left != right do
|
||||
:erlang."/="(left, right)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns true if the two items are strictly equal.
|
||||
@@ -277,7 +333,9 @@ defmodule Kernel do
|
||||
false
|
||||
|
||||
"""
|
||||
defmacro left === right
|
||||
def left === right do
|
||||
:erlang."=:="(left, right)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns true if the two items are strictly not equal.
|
||||
@@ -292,7 +350,9 @@ defmodule Kernel do
|
||||
true
|
||||
|
||||
"""
|
||||
defmacro left !== right
|
||||
def left !== right do
|
||||
:erlang."=/="(left, right)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Invokes the given `fun` with the array of arguments `args`.
|
||||
@@ -303,18 +363,22 @@ defmodule Kernel do
|
||||
4
|
||||
|
||||
"""
|
||||
defmacro apply(fun, args)
|
||||
def apply(fun, args) do
|
||||
:erlang.apply(fun, args)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Invokes the given `fun` from `module` with the array of arguments `args`.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> apply(Enum, :reverse, [[1,2,3]])
|
||||
iex> apply(Enum, :reverse, [[1, 2, 3]])
|
||||
[3,2,1]
|
||||
|
||||
"""
|
||||
defmacro apply(module, fun, args)
|
||||
def apply(module, fun, args) do
|
||||
:erlang.apply(module, fun, args)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns an integer or float which is the arithmetical absolute value of `number`.
|
||||
@@ -424,7 +488,7 @@ defmodule Kernel do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Like 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.
|
||||
"""
|
||||
@@ -481,9 +545,9 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> bit_size(<<433::16,3::3>>)
|
||||
iex> bit_size(<<433::16, 3::3>>)
|
||||
19
|
||||
iex> bit_size(<<1,2,3>>)
|
||||
iex> bit_size(<<1, 2, 3>>)
|
||||
24
|
||||
|
||||
"""
|
||||
@@ -511,9 +575,9 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> byte_size(<<433::16,3::3>>)
|
||||
iex> byte_size(<<433::16, 3::3>>)
|
||||
3
|
||||
iex> byte_size(<<1,2,3>>)
|
||||
iex> byte_size(<<1, 2, 3>>)
|
||||
3
|
||||
|
||||
"""
|
||||
@@ -602,7 +666,7 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> iolist_size([1,2|<<3,4>>])
|
||||
iex> iolist_size([1, 2|<<3, 4>>])
|
||||
4
|
||||
|
||||
"""
|
||||
@@ -616,10 +680,10 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> bin1 = <<1,2,3>>
|
||||
...> bin2 = <<4,5>>
|
||||
iex> bin1 = <<1, 2, 3>>
|
||||
...> bin2 = <<4, 5>>
|
||||
...> bin3 = <<6>>
|
||||
...> iolist_to_binary([bin1,1,[2,3,bin2],4|bin3])
|
||||
...> iolist_to_binary([bin1, 1, [2, 3, bin2], 4|bin3])
|
||||
<<1,2,3,1,2,3,4,5,4,6>>
|
||||
|
||||
"""
|
||||
@@ -789,7 +853,7 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> length([1,2,3,4,5,6,7,8,9])
|
||||
iex> length([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||
9
|
||||
"""
|
||||
@spec length(list) :: non_neg_integer
|
||||
@@ -829,10 +893,10 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> bin1 = <<1,2,3>>
|
||||
...> bin2 = <<4,5>>
|
||||
...> bin3 = <<6,7::4>>
|
||||
...> list_to_bitstring([bin1,1,[2,3,bin2],4|bin3])
|
||||
iex> bin1 = <<1, 2, 3>>
|
||||
...> bin2 = <<4, 5>>
|
||||
...> 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::size(4)>>
|
||||
|
||||
"""
|
||||
@@ -1072,7 +1136,7 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
spawn(SomeModule, :function, [1,2,3])
|
||||
spawn(SomeModule, :function, [1, 2, 3])
|
||||
|
||||
"""
|
||||
@spec spawn(module, atom, list) :: pid
|
||||
@@ -1110,7 +1174,7 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
spawn_link(SomeModule, :function, [1,2,3])
|
||||
spawn_link(SomeModule, :function, [1, 2, 3])
|
||||
|
||||
"""
|
||||
@spec spawn_link(module, atom, list) :: pid
|
||||
@@ -1425,6 +1489,20 @@ defmodule Kernel do
|
||||
user(name: name) = record
|
||||
name #=> "José"
|
||||
|
||||
## Types
|
||||
|
||||
`defrecordp` allows a developer to generate a type
|
||||
automatically by simply providing a type to its fields.
|
||||
The following definition:
|
||||
|
||||
defrecordp :user,
|
||||
name: "José" :: binary,
|
||||
age: 25 :: integer
|
||||
|
||||
Will generate the following type:
|
||||
|
||||
@typep user_t :: { :user, binary, integer }
|
||||
|
||||
"""
|
||||
defmacro defrecordp(name, fields) when is_atom(name) do
|
||||
Record.defrecordp(name, fields)
|
||||
@@ -1563,6 +1641,34 @@ defmodule Kernel do
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Matches the term on the left against the regular expression or string on the
|
||||
right. Returns true if `left` matches `right` (if it's a regular expression)
|
||||
or contains `right` (if it's a string).
|
||||
|
||||
## Examples
|
||||
|
||||
iex> "abcd" =~ %r/c(d)/
|
||||
true
|
||||
|
||||
iex> "abcd" =~ %r/e/
|
||||
false
|
||||
|
||||
iex> "abcd" =~ "bc"
|
||||
true
|
||||
|
||||
iex> "abcd" =~ "ad"
|
||||
false
|
||||
|
||||
"""
|
||||
def left =~ right when is_binary(left) and is_binary(right) do
|
||||
:binary.match(left, right) != :nomatch
|
||||
end
|
||||
|
||||
def left =~ right when is_binary(left) and is_tuple(right) and :erlang.element(1, right) == Regex do
|
||||
Regex.match?(right, left)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Defines the current module as a protocol and specifies the API
|
||||
that should be implemented.
|
||||
@@ -1754,7 +1860,7 @@ defmodule Kernel do
|
||||
|
||||
defmodule AssertionTest do
|
||||
require ExUnit.Case
|
||||
ExUnit.Case.__using__([sync: true])
|
||||
ExUnit.Case.__using__([async: true])
|
||||
|
||||
def test_always_pass do
|
||||
true = true
|
||||
@@ -1795,7 +1901,7 @@ defmodule Kernel do
|
||||
iex> inspect(:foo)
|
||||
":foo"
|
||||
|
||||
iex> inspect [1,2,3,4,5], limit: 3
|
||||
iex> inspect [1, 2, 3, 4, 5], limit: 3
|
||||
"[1,2,3,...]"
|
||||
|
||||
inspect(ArgumentError[])
|
||||
@@ -1812,14 +1918,10 @@ defmodule Kernel do
|
||||
#=> #Function<...>
|
||||
|
||||
"""
|
||||
defmacro inspect(arg, opts // []) do
|
||||
quote do
|
||||
arg = unquote(arg)
|
||||
opts = unquote(opts)
|
||||
case is_tuple(arg) && Keyword.get(opts, :raw, false) do
|
||||
true -> Binary.Inspect.Tuple.inspect(arg, opts)
|
||||
false -> Binary.Inspect.inspect(arg, opts)
|
||||
end
|
||||
def inspect(arg, opts // []) do
|
||||
case is_tuple(arg) and Keyword.get(opts, :raw, false) do
|
||||
true -> Binary.Inspect.Tuple.inspect(arg, opts)
|
||||
false -> Binary.Inspect.inspect(arg, opts)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1988,17 +2090,17 @@ defmodule Kernel do
|
||||
true
|
||||
iex> match?(1, 2)
|
||||
false
|
||||
iex> match?({1,_}, {1,2})
|
||||
iex> match?({1, _}, {1, 2})
|
||||
true
|
||||
|
||||
Match can also be used to filter or find a value in an enumerable:
|
||||
|
||||
list = [{:a,1},{:b,2},{:a,3}]
|
||||
list = [{:a, 1}, {:b, 2}, {:a, 3}]
|
||||
Enum.filter list, match?({:a, _}, _)
|
||||
|
||||
Guard clauses can also be given to the match:
|
||||
|
||||
list = [{:a,1},{:b,2},{:a,3}]
|
||||
list = [{:a, 1}, {:b, 2}, {:a, 3}]
|
||||
Enum.filter list, match?({:a, x } when x < 2, &1)
|
||||
|
||||
"""
|
||||
@@ -2068,7 +2170,7 @@ defmodule Kernel do
|
||||
In order to reduce verbosity, functions in Elixir can be written
|
||||
using a shortcut syntax via `fn`:
|
||||
|
||||
Enum.map [1,2,3], fn x ->
|
||||
Enum.map [1, 2, 3], fn x ->
|
||||
x * 2
|
||||
end
|
||||
|
||||
@@ -2076,25 +2178,25 @@ defmodule Kernel do
|
||||
`do/end` always matches the furthest call, if we used the `function`
|
||||
macro as below:
|
||||
|
||||
Enum.map [1,2,3], function do
|
||||
Enum.map [1, 2, 3], function do
|
||||
x -> x * 2
|
||||
end
|
||||
|
||||
It would be parsed as:
|
||||
|
||||
Enum.map([1,2,3], function) do
|
||||
Enum.map([1, 2, 3], function) do
|
||||
x -> x * 2
|
||||
end
|
||||
|
||||
The stab shortcut syntax has the proper precedence:
|
||||
|
||||
Enum.map [1,2,3], fn
|
||||
Enum.map [1, 2, 3], fn
|
||||
x -> x * 2
|
||||
end
|
||||
|
||||
Which is handled as:
|
||||
|
||||
Enum.map([1,2,3], fn
|
||||
Enum.map([1, 2, 3], fn
|
||||
x -> x * 2
|
||||
end)
|
||||
|
||||
@@ -2347,6 +2449,58 @@ defmodule Kernel do
|
||||
"""
|
||||
defmacro @(expr)
|
||||
|
||||
@doc """
|
||||
Returns the binding as a keyword list where the variable name
|
||||
is the key and the variable value is the value.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> x = 1
|
||||
iex> binding()
|
||||
[x: 1]
|
||||
iex> x = 2
|
||||
iex> binding()
|
||||
[x: 2]
|
||||
|
||||
"""
|
||||
defmacro binding
|
||||
|
||||
@doc """
|
||||
Receives a list of atoms at compilation time and returns the
|
||||
binding of the given variables as a keyword list where the
|
||||
variable name is the key and the variable value is the value.
|
||||
In case a variable in the list does not exist in the binding,
|
||||
it is not included in the returned result.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> x = 1
|
||||
iex> binding([:x, :y])
|
||||
[x: 1]
|
||||
|
||||
"""
|
||||
defmacro binding(list)
|
||||
|
||||
@doc """
|
||||
Receives a list of tuples at compilation time containing the
|
||||
variable name and its scope and returns the binding of the given
|
||||
variables as a keyword list with the variable name and scope pair
|
||||
as key and the variable value as value.
|
||||
|
||||
In case a variable in the list does not exist in the binding,
|
||||
it is not included in the returned result.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> var!(x, :foo) = 1
|
||||
iex> binding([x: nil], true)
|
||||
[]
|
||||
iex> binding([x: :foo], true)
|
||||
[{ { :x, :foo }, 1 }]
|
||||
|
||||
"""
|
||||
defmacro binding(list, true)
|
||||
|
||||
@doc """
|
||||
Provides an `if` macro. This macro expects the first argument to
|
||||
be a condition and the rest are keyword arguments.
|
||||
@@ -2412,7 +2566,7 @@ defmodule Kernel do
|
||||
end
|
||||
"""
|
||||
defmacro cond([do: { :->, _, pairs }]) do
|
||||
[{ [condition], clause }|t] = :lists.reverse pairs
|
||||
[{ [condition], meta, clause }|t] = :lists.reverse pairs
|
||||
|
||||
new_acc =
|
||||
case condition do
|
||||
@@ -2422,14 +2576,14 @@ defmodule Kernel do
|
||||
x when is_atom(x) and not x in [false, nil] ->
|
||||
clause
|
||||
_ ->
|
||||
quote do
|
||||
quote line: get_line(meta) do
|
||||
case !unquote(condition) do
|
||||
false -> unquote(clause)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
build_cond_clauses(t, new_acc)
|
||||
build_cond_clauses(t, new_acc, meta)
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -2462,7 +2616,7 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> destructure([x,y,z], [1,2,3,4,5])
|
||||
iex> destructure([x, y, z], [1, 2, 3, 4, 5])
|
||||
...> {x, y, z}
|
||||
{1, 2, 3}
|
||||
|
||||
@@ -2471,7 +2625,7 @@ defmodule Kernel do
|
||||
fine. If the right size is smaller, the remaining items
|
||||
are simply assigned to nil:
|
||||
|
||||
iex> destructure([x,y,z], [1])
|
||||
iex> destructure([x, y, z], [1])
|
||||
...> {x, y, z}
|
||||
{1, nil, nil}
|
||||
|
||||
@@ -2479,7 +2633,7 @@ defmodule Kernel do
|
||||
on the left side of a match:
|
||||
|
||||
x = 1
|
||||
destructure([^x,y,z], [1,2,3])
|
||||
destructure([^x, y, z], [1, 2, 3])
|
||||
|
||||
The example above will only work if x matches
|
||||
the first value from the right side. Otherwise,
|
||||
@@ -2734,8 +2888,8 @@ defmodule Kernel do
|
||||
defmacro left && right do
|
||||
quote do
|
||||
case unquote(left) do
|
||||
andand in [false, nil] ->
|
||||
andand
|
||||
var!(andand, false) in [false, nil] ->
|
||||
var!(andand, false)
|
||||
_ ->
|
||||
unquote(right)
|
||||
end
|
||||
@@ -2765,10 +2919,10 @@ defmodule Kernel do
|
||||
defmacro left || right do
|
||||
quote do
|
||||
case unquote(left) do
|
||||
oror in [false, nil] ->
|
||||
var!(oror, false) in [false, nil] ->
|
||||
unquote(right)
|
||||
oror ->
|
||||
oror
|
||||
var!(oror, false) ->
|
||||
var!(oror, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2780,7 +2934,7 @@ defmodule Kernel do
|
||||
## Examples
|
||||
|
||||
iex> x = 1
|
||||
...> x in [1,2,3]
|
||||
...> x in [1, 2, 3]
|
||||
true
|
||||
|
||||
This macro simply translates the expression above to:
|
||||
@@ -2796,14 +2950,14 @@ defmodule Kernel do
|
||||
optionally omit the variable declaration, for example:
|
||||
|
||||
case 3 do
|
||||
x when x in [1,2] -> x * 2
|
||||
x when x in [1, 2] -> x * 2
|
||||
_ -> 0
|
||||
end
|
||||
|
||||
Could be rewritten as:
|
||||
|
||||
case 3 do
|
||||
x in [1,2] -> x * 2
|
||||
x in [1, 2] -> x * 2
|
||||
_ -> 0
|
||||
end
|
||||
|
||||
@@ -2812,55 +2966,6 @@ defmodule Kernel do
|
||||
"""
|
||||
defmacro left in right
|
||||
|
||||
@doc """
|
||||
Matches the term on the left against the regular expression or string on the
|
||||
right. Returns true if `left` matches `right` (if it's a regular expression)
|
||||
or contains `right` (if it's a string).
|
||||
|
||||
## Examples
|
||||
|
||||
iex> "abcd" =~ %r/c(d)/
|
||||
true
|
||||
|
||||
iex> "abcd" =~ %r/e/
|
||||
false
|
||||
|
||||
iex> "abcd" =~ "bc"
|
||||
true
|
||||
|
||||
iex> "abcd" =~ "ad"
|
||||
false
|
||||
|
||||
"""
|
||||
# fast path for literal binaries
|
||||
defmacro left =~ right when is_binary(right) do
|
||||
quote do
|
||||
String.contains?(unquote(left), unquote(right))
|
||||
end
|
||||
end
|
||||
|
||||
# fast path for literal binaries
|
||||
defmacro left =~ ({:<<>>, _, [_bin]} = right) do
|
||||
quote do
|
||||
String.contains?(unquote(left), unquote(right))
|
||||
end
|
||||
end
|
||||
|
||||
# slow path for everything else
|
||||
defmacro left =~ right do
|
||||
quote do
|
||||
str = unquote(left)
|
||||
case unquote(right) do
|
||||
bin when is_binary(bin) ->
|
||||
String.contains?(str, bin)
|
||||
re when is_regex(re) ->
|
||||
Regex.match?(re, str)
|
||||
other ->
|
||||
raise ArgumentError, message: "bad argument on the right side of =~: #{inspect other}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
`|>` is called the pipeline operator as it is useful
|
||||
to write pipeline style expressions. This operator
|
||||
@@ -2869,12 +2974,12 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> [1,[2],3] |> List.flatten |> Enum.map(&1 * 2)
|
||||
iex> [1, [2], 3] |> List.flatten |> Enum.map(&1 * 2)
|
||||
[2,4,6]
|
||||
|
||||
The expression above is simply translated to:
|
||||
|
||||
Enum.map(List.flatten([1,[2],3]), &1 * 2)
|
||||
Enum.map(List.flatten([1, [2], 3]), &1 * 2)
|
||||
|
||||
Be aware of operator precendence when using this operator.
|
||||
For example, the following expression:
|
||||
@@ -2921,13 +3026,13 @@ defmodule Kernel do
|
||||
end
|
||||
|
||||
defp validate_pipeline_args([]), do: nil
|
||||
defp validate_pipeline_args([ {:&,_,_ } | _ ]), do: :error
|
||||
defp validate_pipeline_args([ {:&, _, _ } | _ ]), do: :error
|
||||
defp validate_pipeline_args([_|t]) do
|
||||
validate_pipeline_args(t)
|
||||
end
|
||||
|
||||
defp pipeline_error(arg) do
|
||||
raise ArgumentError, message: "Unsupported expression in pipeline |> operator: #{Macro.to_binary arg}"
|
||||
raise ArgumentError, message: "Unsupported expression in pipeline |> operator: #{Macro.to_string arg}"
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -3121,14 +3226,14 @@ defmodule Kernel do
|
||||
{ :error, _ } ->
|
||||
:elixir_aliases.ensure_loaded(caller.line, caller.file, atom, caller.context_modules)
|
||||
_ ->
|
||||
raise "cannot use module #{inspect atom} in access protocol because it does not export __record__/1"
|
||||
raise ArgumentError, message: "cannot use module #{inspect atom} in access protocol because it does not export __record__/1"
|
||||
end
|
||||
end
|
||||
|
||||
Record.access(atom, fields, args, caller)
|
||||
false ->
|
||||
case caller.in_match? do
|
||||
true -> raise << "the access protocol cannot be used inside match clauses ",
|
||||
true -> raise ArgumentError, message: << "the access protocol cannot be used inside match clauses ",
|
||||
"(for example, on the left hand side of a match or in function signatures)" >>
|
||||
false -> quote do: Access.access(unquote(element), unquote(args))
|
||||
end
|
||||
@@ -3158,9 +3263,8 @@ defmodule Kernel do
|
||||
* `:append_first` - If true, when delegated, first argument
|
||||
passed to the delegate will be relocated to the end of the
|
||||
arguments when dispatched to the target. The motivation behind
|
||||
this is a disparity between conventions used in Elixir and :
|
||||
Elixir's convention is to pass the "handle" as a first argument,
|
||||
while in Erlang the convention is to pass it as the last argument
|
||||
this is because Elixir normalizes the "handle" as a first
|
||||
argument and some Erlang modules expect it as last argument.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -3170,16 +3274,16 @@ defmodule Kernel do
|
||||
defdelegate other_reverse(list), to: :lists, as: :reverse
|
||||
end
|
||||
|
||||
MyList.reverse([1,2,3])
|
||||
MyList.reverse([1, 2, 3])
|
||||
#=> [3,2,1]
|
||||
|
||||
MyList.other_reverse([1,2,3])
|
||||
MyList.other_reverse([1, 2, 3])
|
||||
#=> [3,2,1]
|
||||
|
||||
"""
|
||||
defmacro defdelegate(funs, opts) do
|
||||
quote do
|
||||
funs = unquote(Macro.escape_quoted(funs))
|
||||
funs = unquote(Macro.escape(funs, unquote: true))
|
||||
opts = unquote(opts)
|
||||
|
||||
target = Keyword.get(opts, :to) ||
|
||||
@@ -3190,7 +3294,7 @@ defmodule Kernel do
|
||||
lc fun inlist List.wrap(funs) do
|
||||
case Macro.extract_args(fun) do
|
||||
{ name, args } -> :ok
|
||||
:error -> raise ArgumentError, message: "invalid syntax in defdelegate #{Macro.to_binary(fun)}"
|
||||
:error -> raise ArgumentError, message: "invalid syntax in defdelegate #{Macro.to_string(fun)}"
|
||||
end
|
||||
|
||||
actual_args =
|
||||
@@ -3219,7 +3323,7 @@ defmodule Kernel do
|
||||
"f\\\#{o}o"
|
||||
|
||||
"""
|
||||
defmacro __B__(string, []) do
|
||||
defmacro sigil_B(string, []) do
|
||||
string
|
||||
end
|
||||
|
||||
@@ -3235,7 +3339,7 @@ defmodule Kernel do
|
||||
"foo"
|
||||
|
||||
"""
|
||||
defmacro __b__({ :<<>>, line, pieces }, []) do
|
||||
defmacro sigil_b({ :<<>>, line, pieces }, []) do
|
||||
{ :<<>>, line, Macro.unescape_tokens(pieces) }
|
||||
end
|
||||
|
||||
@@ -3251,7 +3355,7 @@ defmodule Kernel do
|
||||
'f\\\#{o}o'
|
||||
|
||||
"""
|
||||
defmacro __C__({ :<<>>, _line, [string] }, []) when is_binary(string) do
|
||||
defmacro sigil_C({ :<<>>, _line, [string] }, []) when is_binary(string) do
|
||||
binary_to_list(string)
|
||||
end
|
||||
|
||||
@@ -3270,11 +3374,11 @@ 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
|
||||
defmacro sigil_c({ :<<>>, _line, [string] }, []) when is_binary(string) do
|
||||
:unicode.characters_to_list(Macro.unescape_binary(string))
|
||||
end
|
||||
|
||||
defmacro __c__({ :<<>>, line, pieces }, []) do
|
||||
defmacro sigil_c({ :<<>>, line, pieces }, []) do
|
||||
binary = { :<<>>, line, Macro.unescape_tokens(pieces) }
|
||||
quote do: :unicode.characters_to_list(unquote(binary))
|
||||
end
|
||||
@@ -3288,13 +3392,13 @@ defmodule Kernel do
|
||||
true
|
||||
|
||||
"""
|
||||
defmacro __r__({ :<<>>, _line, [string] }, options) when is_binary(string) do
|
||||
defmacro sigil_r({ :<<>>, _line, [string] }, options) when is_binary(string) do
|
||||
binary = Macro.unescape_binary(string, Regex.unescape_map(&1))
|
||||
regex = Regex.compile!(binary, options)
|
||||
Macro.escape(regex)
|
||||
end
|
||||
|
||||
defmacro __r__({ :<<>>, line, pieces }, options) do
|
||||
defmacro sigil_r({ :<<>>, line, pieces }, options) do
|
||||
binary = { :<<>>, line, Macro.unescape_tokens(pieces, Regex.unescape_map(&1)) }
|
||||
quote do: Regex.compile!(unquote(binary), unquote(options))
|
||||
end
|
||||
@@ -3309,7 +3413,7 @@ defmodule Kernel do
|
||||
true
|
||||
|
||||
"""
|
||||
defmacro __R__({ :<<>>, _line, [string] }, options) when is_binary(string) do
|
||||
defmacro sigil_R({ :<<>>, _line, [string] }, options) when is_binary(string) do
|
||||
regex = Regex.compile!(string, options)
|
||||
Macro.escape(regex)
|
||||
end
|
||||
@@ -3334,11 +3438,11 @@ defmodule Kernel do
|
||||
|
||||
"""
|
||||
|
||||
defmacro __w__({ :<<>>, _line, [string] }, modifiers) when is_binary(string) do
|
||||
defmacro sigil_w({ :<<>>, _line, [string] }, modifiers) when is_binary(string) do
|
||||
split_words(Macro.unescape_binary(string), modifiers)
|
||||
end
|
||||
|
||||
defmacro __w__({ :<<>>, line, pieces }, modifiers) do
|
||||
defmacro sigil_w({ :<<>>, line, pieces }, modifiers) do
|
||||
binary = { :<<>>, line, Macro.unescape_tokens(pieces) }
|
||||
split_words(binary, modifiers)
|
||||
end
|
||||
@@ -3359,7 +3463,7 @@ defmodule Kernel do
|
||||
["foo", "\\\#{bar}", "baz"]
|
||||
|
||||
"""
|
||||
defmacro __W__({ :<<>>, _line, [string] }, modifiers) when is_binary(string) do
|
||||
defmacro sigil_W({ :<<>>, _line, [string] }, modifiers) when is_binary(string) do
|
||||
split_words(string, modifiers)
|
||||
end
|
||||
|
||||
@@ -3396,24 +3500,36 @@ defmodule Kernel do
|
||||
# end
|
||||
# end
|
||||
#
|
||||
defp build_cond_clauses([{ [condition], clause }|t], acc) do
|
||||
new_acc = quote do
|
||||
case unquote(condition) do
|
||||
_ in [false, nil] -> unquote(acc)
|
||||
_ -> unquote(clause)
|
||||
end
|
||||
defp build_cond_clauses([{ [condition], new, clause }|t], acc, old) do
|
||||
stab = { :->, [], [falsy_clause(old, acc), truthy_clause(new, clause)] }
|
||||
acc = quote do
|
||||
case unquote(condition), do: unquote(stab)
|
||||
end
|
||||
|
||||
build_cond_clauses(t, new_acc)
|
||||
build_cond_clauses(t, acc, new)
|
||||
end
|
||||
|
||||
defp build_cond_clauses([], acc), do: acc
|
||||
defp build_cond_clauses([], acc, _), do: acc
|
||||
|
||||
defp expand_compact([{ :compact, false }|t]), do: expand_compact(t)
|
||||
defp expand_compact([{ :compact, true }|t]), do: [:compact|expand_compact(t)]
|
||||
defp expand_compact([h|t]), do: [h|expand_compact(t)]
|
||||
defp expand_compact([]), do: []
|
||||
|
||||
defp falsy_clause(meta, acc) do
|
||||
{ [quote(do: _ in [false, nil])], meta, acc }
|
||||
end
|
||||
|
||||
defp truthy_clause(meta, clause) do
|
||||
{ [quote(do: _)], meta, clause }
|
||||
end
|
||||
|
||||
defp get_line(meta) do
|
||||
case :lists.keyfind(:line, 1, meta) do
|
||||
{ :line, line } -> line
|
||||
false -> 0
|
||||
end
|
||||
end
|
||||
|
||||
defp split_words(string, modifiers) do
|
||||
mod = case modifiers do
|
||||
[] -> ?b
|
||||
|
||||
@@ -107,37 +107,37 @@ defmodule Kernel.CLI do
|
||||
System.halt 0
|
||||
end
|
||||
|
||||
defp process_shared(["-pa",h|t], config) do
|
||||
defp process_shared(["-pa", h|t], config) do
|
||||
Enum.each Path.wildcard(Path.expand(h)), Code.prepend_path(&1)
|
||||
process_shared t, config
|
||||
end
|
||||
|
||||
defp process_shared(["-pz",h|t], config) do
|
||||
defp process_shared(["-pz", h|t], config) do
|
||||
Enum.each Path.wildcard(Path.expand(h)), Code.append_path(&1)
|
||||
process_shared t, config
|
||||
end
|
||||
|
||||
defp process_shared(["--app",h|t], config) do
|
||||
process_shared t, config.update_commands [{:app,h}|&1]
|
||||
defp process_shared(["--app", h|t], config) do
|
||||
process_shared t, config.update_commands [{:app, h}|&1]
|
||||
end
|
||||
|
||||
defp process_shared(["--no-halt"|t], config) do
|
||||
process_shared t, config.halt(false)
|
||||
end
|
||||
|
||||
defp process_shared(["-e",h|t], config) do
|
||||
process_shared t, config.update_commands [{:eval,h}|&1]
|
||||
defp process_shared(["-e", h|t], config) do
|
||||
process_shared t, config.update_commands [{:eval, h}|&1]
|
||||
end
|
||||
|
||||
defp process_shared(["-r",h|t], config) do
|
||||
process_shared t, config.update_commands [{:require,h}|&1]
|
||||
defp process_shared(["-r", h|t], config) do
|
||||
process_shared t, config.update_commands [{:require, h}|&1]
|
||||
end
|
||||
|
||||
defp process_shared(["-pr",h|t], config) do
|
||||
process_shared t, config.update_commands [{:parallel_require,h}|&1]
|
||||
defp process_shared(["-pr", h|t], config) do
|
||||
process_shared t, config.update_commands [{:parallel_require, h}|&1]
|
||||
end
|
||||
|
||||
defp process_shared([erl,_|t], config) when erl in ["--erl", "--sname", "--name", "--cookie"] do
|
||||
defp process_shared([erl, _|t], config) when erl in ["--erl", "--sname", "--name", "--cookie"] do
|
||||
process_shared t, config
|
||||
end
|
||||
|
||||
@@ -159,8 +159,8 @@ defmodule Kernel.CLI do
|
||||
process_iex t, config
|
||||
end
|
||||
|
||||
defp process_argv(["-S",h|t], config) do
|
||||
{ config.update_commands([{:script,h}|&1]), t }
|
||||
defp process_argv(["-S", h|t], config) do
|
||||
{ config.update_commands([{:script, h}|&1]), t }
|
||||
end
|
||||
|
||||
defp process_argv([h|t] = list, config) do
|
||||
@@ -168,7 +168,7 @@ defmodule Kernel.CLI do
|
||||
"-" <> _ ->
|
||||
shared_option? list, config, process_argv(&1, &2)
|
||||
_ ->
|
||||
{ config.update_commands([{:file,h}|&1]), t }
|
||||
{ config.update_commands([{:file, h}|&1]), t }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -182,20 +182,24 @@ defmodule Kernel.CLI do
|
||||
{ config, t }
|
||||
end
|
||||
|
||||
defp process_compiler(["-o",h|t], config) do
|
||||
defp process_compiler(["-o", h|t], config) do
|
||||
process_compiler t, config.output(h)
|
||||
end
|
||||
|
||||
defp process_compiler(["--no-docs"|t], config) do
|
||||
process_compiler t, config.update_compiler_options([{:docs,false}|&1])
|
||||
process_compiler t, config.update_compiler_options([{:docs, false}|&1])
|
||||
end
|
||||
|
||||
defp process_compiler(["--no-debug-info"|t], config) do
|
||||
process_compiler t, config.update_compiler_options([{:debug_info,false}|&1])
|
||||
process_compiler t, config.update_compiler_options([{:debug_info, false}|&1])
|
||||
end
|
||||
|
||||
defp process_compiler(["--ignore-module-conflict"|t], config) do
|
||||
process_compiler t, config.update_compiler_options([{:ignore_module_conflict,true}|&1])
|
||||
process_compiler t, config.update_compiler_options([{:ignore_module_conflict, true}|&1])
|
||||
end
|
||||
|
||||
defp process_compiler(["--warnings-as-errors"|t], config) do
|
||||
process_compiler t, config.update_compiler_options([{:warnings_as_errors, true}|&1])
|
||||
end
|
||||
|
||||
defp process_compiler([h|t] = list, config) do
|
||||
@@ -209,7 +213,7 @@ defmodule Kernel.CLI do
|
||||
end
|
||||
|
||||
defp process_compiler([], config) do
|
||||
{ config.update_commands([{:compile,config.compile}|&1]), [] }
|
||||
{ config.update_commands([{:compile, config.compile}|&1]), [] }
|
||||
end
|
||||
|
||||
# Process iex options
|
||||
@@ -220,16 +224,16 @@ defmodule Kernel.CLI do
|
||||
|
||||
# This clause is here so that Kernel.CLI does not error out with "unknown
|
||||
# option"
|
||||
defp process_iex(["--dot-iex",_|t], config) do
|
||||
defp process_iex(["--dot-iex", _|t], config) do
|
||||
process_iex t, config
|
||||
end
|
||||
|
||||
defp process_iex([opt,_|t], config) when opt in ["--remsh"] do
|
||||
defp process_iex([opt, _|t], config) when opt in ["--remsh"] do
|
||||
process_iex t, config
|
||||
end
|
||||
|
||||
defp process_iex(["-S",h|t], config) do
|
||||
{ config.update_commands([{:script,h}|&1]), t }
|
||||
defp process_iex(["-S", h|t], config) do
|
||||
{ config.update_commands([{:script, h}|&1]), t }
|
||||
end
|
||||
|
||||
defp process_iex([h|t] = list, config) do
|
||||
@@ -237,7 +241,7 @@ defmodule Kernel.CLI do
|
||||
"-" <> _ ->
|
||||
shared_option? list, config, process_iex(&1, &2)
|
||||
_ ->
|
||||
{ config.update_commands([{:file,h}|&1]), t }
|
||||
{ config.update_commands([{:file, h}|&1]), t }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -247,7 +251,7 @@ defmodule Kernel.CLI do
|
||||
|
||||
# Process commands
|
||||
|
||||
defp process_command({:cookie,h}, _config) do
|
||||
defp process_command({:cookie, h}, _config) do
|
||||
if Node.alive? do
|
||||
Node.set_cookie(binary_to_atom(h))
|
||||
:ok
|
||||
@@ -325,7 +329,6 @@ defmodule Kernel.CLI do
|
||||
Code.compiler_options(config.compiler_options)
|
||||
Kernel.ParallelCompiler.files_to_path(files, config.output,
|
||||
fn file -> IO.puts "Compiled #{file}" end)
|
||||
:ok
|
||||
else
|
||||
{ :error, "--compile : No files matched patterns #{Enum.join(patterns, ",")}" }
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
defexception Kernel.CompilationError, message: "compilation failed"
|
||||
|
||||
defmodule Kernel.ParallelCompiler do
|
||||
alias :orddict, as: OrdDict
|
||||
|
||||
@@ -15,6 +17,10 @@ defmodule Kernel.ParallelCompiler do
|
||||
the current file stops being compiled until the dependency is
|
||||
resolved.
|
||||
|
||||
If there is any error during compilation or if warnings_as_errors
|
||||
is set to true and there is a warning, this function will fail
|
||||
with an exception.
|
||||
|
||||
A callback that is invoked every time a file is compiled
|
||||
with its name can be optionally given as argument.
|
||||
"""
|
||||
@@ -32,8 +38,14 @@ defmodule Kernel.ParallelCompiler do
|
||||
|
||||
defp spawn_compilers(files, path, callback) do
|
||||
Code.ensure_loaded(Kernel.ErrorHandler)
|
||||
:elixir_code_server.cast(:reset_warnings)
|
||||
schedulers = max(:erlang.system_info(:schedulers_online), 2)
|
||||
spawn_compilers(files, path, callback, [], [], schedulers, [])
|
||||
|
||||
result = spawn_compilers(files, path, callback, [], [], schedulers, [])
|
||||
case :elixir_code_server.call(:compilation_status) do
|
||||
:ok -> result
|
||||
:error -> raise Kernel.CompilationError, [], []
|
||||
end
|
||||
end
|
||||
|
||||
# We already have 4 currently running, don't spawn new ones
|
||||
@@ -64,7 +76,7 @@ defmodule Kernel.ParallelCompiler do
|
||||
end
|
||||
end
|
||||
|
||||
spawn_compilers(t, output, callback, waiting, [{child,h}|queued], schedulers, result)
|
||||
spawn_compilers(t, output, callback, waiting, [{child, h}|queued], schedulers, result)
|
||||
end
|
||||
|
||||
# No more files, nothing waiting, queue is empty, we are done
|
||||
@@ -138,4 +150,4 @@ defmodule Kernel.ParallelCompiler do
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -247,7 +247,7 @@ defmodule Kernel.SpecialForms do
|
||||
for example:
|
||||
|
||||
iex> import List
|
||||
...> flatten([1,[2],3])
|
||||
...> flatten([1, [2], 3])
|
||||
[1,2,3]
|
||||
|
||||
## Selector
|
||||
@@ -388,6 +388,8 @@ defmodule Kernel.SpecialForms do
|
||||
Read the Stacktrace information section below for more information;
|
||||
* `:hygiene` - Allows a developer to disable hygiene selectively;
|
||||
* `:context` - Sets the context resolution happens at;
|
||||
* `:binding` - Passes a binding to the macro. Whenever a binding is given,
|
||||
unquote is automatically disabled;
|
||||
|
||||
## Macro literals
|
||||
|
||||
@@ -397,7 +399,7 @@ defmodule Kernel.SpecialForms do
|
||||
:sum #=> Atoms
|
||||
1 #=> Integers
|
||||
2.0 #=> Floats
|
||||
[1,2] #=> Lists
|
||||
[1, 2] #=> Lists
|
||||
"binaries" #=> Binaries
|
||||
{key, value} #=> Tuple with two elements
|
||||
|
||||
@@ -629,6 +631,79 @@ defmodule Kernel.SpecialForms do
|
||||
code as if it was defined inside `GenServer.Behaviour` file, in
|
||||
particular, the macro `__FILE__` and exceptions happening inside
|
||||
the quote will always point to `GenServer.Behaviour` file.
|
||||
|
||||
## Binding and unquote fragments
|
||||
|
||||
Elixir quote/unquote mechanisms provides a functionality called
|
||||
unquote fragments. Unquote fragments provide an easy to generate
|
||||
functions on the fly. Consider this example:
|
||||
|
||||
kv = [foo: 1, bar: 2]
|
||||
Enum.each kv, fn { k, v } ->
|
||||
def unquote(k)(), do: unquote(v)
|
||||
end
|
||||
|
||||
In the example above, we have generated the function `foo/0` and
|
||||
`bar/0` dynamically. Now, imagine that, we want to convert this
|
||||
functionality into a macro:
|
||||
|
||||
defmacro defkv(kv) do
|
||||
Enum.each kv, fn { k, v } ->
|
||||
quote do
|
||||
def unquote(k)(), do: unquote(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
We can invoke this macro as:
|
||||
|
||||
defkv [foo: 1, bar: 2]
|
||||
|
||||
However, we can't invoke it as follows:
|
||||
|
||||
kv = [foo: 1, bar: 2]
|
||||
defkv kv
|
||||
|
||||
This is because the macro is expecting its arguments to be a
|
||||
key-value at **compilation** time. Since in the example above
|
||||
we are passing the representation of the variable `kv`, our
|
||||
code fails.
|
||||
|
||||
This is actually a common pitfall when developing macros. In
|
||||
practive, we want to avoid doing work at compilation time as
|
||||
much as we can. That said, let's attempt to improve our macro:
|
||||
|
||||
defmacro defkv(kv) do
|
||||
quote do
|
||||
Enum.each unquote(kv), fn { k, v } ->
|
||||
def unquote(k)(), do: unquote(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
If you try to run our new macro, you will notice it won't
|
||||
even compile, complaining that the variables `k` and `v`
|
||||
do not exist. This is because of the ambiguity: `unquote(k)`
|
||||
can either be an unquote fragment, as previously, or a regular
|
||||
unquote as in `unquote(kv)`.
|
||||
|
||||
One solution for this problem is to disable unquoting in the
|
||||
macro, however, doing that would make it impossible to inject
|
||||
`kv` representation into the tree. That's when the `:binding`
|
||||
option comes to the rescue. By using `:binding`, we can
|
||||
automatically disable unquoting while still injecting the
|
||||
desired variables into the tree:
|
||||
|
||||
defmacro defkv(kv) do
|
||||
quote binding: [kv: kv] do
|
||||
Enum.each kv, fn { k, v } ->
|
||||
def unquote(k)(), do: unquote(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
In fact, the `:binding` option is recommended every time one
|
||||
desires to inject a value into the quote.
|
||||
"""
|
||||
defmacro quote(opts, block)
|
||||
|
||||
@@ -643,6 +718,10 @@ defmodule Kernel.SpecialForms do
|
||||
|
||||
@doc """
|
||||
Defines a variable in the given context.
|
||||
|
||||
If the context is false, it is not stored in any particular
|
||||
context and the variable is not shared in between clauses.
|
||||
|
||||
Check `quote/2` for more information.
|
||||
"""
|
||||
defmacro var!(var, context)
|
||||
@@ -686,7 +765,7 @@ defmodule Kernel.SpecialForms do
|
||||
|
||||
## Examples
|
||||
|
||||
values = [2,3,4]
|
||||
values = [2, 3, 4]
|
||||
quote do: sum(1, unquote_splicing(values), 5)
|
||||
#=> { :sum, [], [1, 2, 3, 4, 5] }
|
||||
|
||||
@@ -697,7 +776,7 @@ defmodule Kernel.SpecialForms do
|
||||
@doc """
|
||||
List comprehensions allow you to quickly build a list from another list:
|
||||
|
||||
iex> lc n inlist [1,2,3,4], do: n * 2
|
||||
iex> lc n inlist [1, 2, 3, 4], do: n * 2
|
||||
[2,4,6,8]
|
||||
|
||||
A comprehension accepts many generators and also filters. Generators
|
||||
@@ -705,33 +784,33 @@ defmodule Kernel.SpecialForms do
|
||||
to loop lists and bitstrings:
|
||||
|
||||
# A list generator:
|
||||
iex> lc n inlist [1,2,3,4], do: n * 2
|
||||
iex> lc n inlist [1, 2, 3, 4], do: n * 2
|
||||
[2,4,6,8]
|
||||
|
||||
# A bit string generator:
|
||||
iex> lc <<n>> inbits <<1,2,3,4>>, do: n * 2
|
||||
iex> lc <<n>> inbits <<1, 2, 3, 4>>, do: n * 2
|
||||
[2,4,6,8]
|
||||
|
||||
# A generator from a variable:
|
||||
iex> list = [1,2,3,4]
|
||||
iex> list = [1, 2, 3, 4]
|
||||
...> lc n inlist list, do: n * 2
|
||||
[2,4,6,8]
|
||||
|
||||
# A comprehension with two generators
|
||||
iex> lc x inlist [1,2], y inlist [2,3], do: x*y
|
||||
iex> lc x inlist [1, 2], y inlist [2, 3], do: x*y
|
||||
[2,3,4,6]
|
||||
|
||||
Filters can also be given:
|
||||
|
||||
# A comprehension with a generator and a filter
|
||||
iex> lc n inlist [1,2,3,4,5,6], rem(n, 2) == 0, do: n
|
||||
iex> lc n inlist [1, 2, 3, 4, 5, 6], rem(n, 2) == 0, do: n
|
||||
[2,4,6]
|
||||
|
||||
Bit string generators are quite useful when you need to
|
||||
organize bit string streams:
|
||||
|
||||
iex> pixels = <<213,45,132,64,76,32,76,0,0,234,32,15>>
|
||||
iex> lc <<r::8,g::8,b::8>> inbits pixels, do: {r,g,b}
|
||||
iex> pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
|
||||
iex> lc <<r::8, g::8, b::8>> inbits pixels, do: {r, g, b}
|
||||
[{213,45,132},{64,76,32},{76,0,0},{234,32,15}]
|
||||
|
||||
"""
|
||||
@@ -755,7 +834,7 @@ defmodule Kernel.SpecialForms do
|
||||
and should not be invoked directly:
|
||||
|
||||
iex> quote do: (1; 2; 3)
|
||||
{ :__block__, [], [1,2,3] }
|
||||
{ :__block__, [], [1, 2, 3] }
|
||||
|
||||
"""
|
||||
defmacro __block__(args)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
defmodule Kernel.Typespec do
|
||||
@moduledoc """
|
||||
Holds macros and functions for working with typespecs.
|
||||
Provides macros and functions for working with typespecs.
|
||||
|
||||
The attributes `@type`, `@opaque`, `@typep`, `@spec` and
|
||||
`@callback` available in modules are handled by the equivalent
|
||||
@@ -8,20 +8,19 @@ defmodule Kernel.Typespec do
|
||||
|
||||
## Defining a type
|
||||
|
||||
@type type_name :: type
|
||||
@typep type_name :: type
|
||||
@opaque type_name :: type
|
||||
@type type_name :: type
|
||||
@typep type_name :: type
|
||||
@opaque type_name :: type
|
||||
|
||||
For more details, see documentation for deftype, deftypep and defopaque in
|
||||
Kernel.Typespec
|
||||
For more details, see documentation for `deftype`, `deftypep` and `defopaque`
|
||||
below.
|
||||
|
||||
## Defining a specification
|
||||
|
||||
@spec function_name(type, type) :: type
|
||||
@callback function_name(type, type) :: type
|
||||
@spec function_name(type, type) :: type
|
||||
@callback function_name(type, type) :: type
|
||||
|
||||
For more details, see documentation for defspec and defcallback in
|
||||
Kernel.Typespec
|
||||
For more details, see documentation for `defspec` and `defcallback` below.
|
||||
|
||||
## Types
|
||||
|
||||
@@ -30,9 +29,10 @@ defmodule Kernel.Typespec do
|
||||
|
||||
Most of the built-in types provided in Erlang (for example, `pid()`)
|
||||
are expressed the same way: `pid()` or simply `pid`. Parametrized types
|
||||
are also supported: `list(integer())` and so are remote types: `Enum.t`.
|
||||
are also supported (`list(integer())`) and so are remote types (`Enum.t`).
|
||||
|
||||
Certain data type shortcuts ([...], <<>> and {...}) are supported as well.
|
||||
Certain data type shortcuts (`[...]`, `<<>>` and `{...}`) are supported as
|
||||
well.
|
||||
|
||||
Main differences lie in how bit strings and functions are defined:
|
||||
|
||||
@@ -50,22 +50,16 @@ defmodule Kernel.Typespec do
|
||||
|
||||
Any anonymous function:
|
||||
|
||||
(fun(...) -> any)
|
||||
or
|
||||
((...) -> any)
|
||||
or
|
||||
(... -> any)
|
||||
|
||||
Anonymous function with arity of zero:
|
||||
|
||||
(fun() -> type)
|
||||
or
|
||||
(() -> type)
|
||||
|
||||
Anonymous function with some arity:
|
||||
|
||||
(fun(type, type) -> type)
|
||||
or
|
||||
((type, type) -> type)
|
||||
or
|
||||
(type, type -> type)
|
||||
@@ -87,7 +81,7 @@ defmodule Kernel.Typespec do
|
||||
|
||||
@doc """
|
||||
Defines a type.
|
||||
This macro is the one responsible to handle the attribute @type.
|
||||
This macro is the one responsible for handling the attribute `@type`.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -102,7 +96,7 @@ defmodule Kernel.Typespec do
|
||||
|
||||
@doc """
|
||||
Defines an opaque type.
|
||||
This macro is the one responsible to handle the attribute @opaque.
|
||||
This macro is the one responsible for handling the attribute `@opaque`.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -117,7 +111,7 @@ defmodule Kernel.Typespec do
|
||||
|
||||
@doc """
|
||||
Defines a private type.
|
||||
This macro is the one responsible to handle the attribute @typep.
|
||||
This macro is the one responsible for handling the attribute `@typep`.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -132,7 +126,7 @@ defmodule Kernel.Typespec do
|
||||
|
||||
@doc """
|
||||
Defines a spec.
|
||||
This macro is the one responsible to handle the attribute @spec.
|
||||
This macro is the one responsible for handling the attribute `@spec`.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -147,7 +141,7 @@ defmodule Kernel.Typespec do
|
||||
|
||||
@doc """
|
||||
Defines a callback.
|
||||
This macro is the one responsible to handle the attribute @callback.
|
||||
This macro is the one responsible for handling the attribute `@callback`.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -194,7 +188,7 @@ defmodule Kernel.Typespec do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns true if the current module defines a given type
|
||||
Returns `true` if the current module defines a given type
|
||||
(private, opaque or not). This function is only available
|
||||
for modules being compiled.
|
||||
"""
|
||||
@@ -205,7 +199,7 @@ defmodule Kernel.Typespec do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns true if the current module defines a given spec.
|
||||
Returns `true` if the current module defines a given spec.
|
||||
This function is only available for modules being compiled.
|
||||
"""
|
||||
def defines_spec?(module, name, arity) do
|
||||
@@ -214,7 +208,7 @@ defmodule Kernel.Typespec do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns true if the current module defines a callback.
|
||||
Returns `true` if the current module defines a callback.
|
||||
This function is only available for modules being compiled.
|
||||
"""
|
||||
def defines_callback?(module, name, arity) do
|
||||
@@ -225,7 +219,7 @@ defmodule Kernel.Typespec do
|
||||
@doc """
|
||||
Converts a spec clause back to Elixir AST.
|
||||
"""
|
||||
def spec_to_ast(name, { :type, line, :fun, [{:type, _, :product, args},result] }) do
|
||||
def spec_to_ast(name, { :type, line, :fun, [{:type, _, :product, args}, result] }) do
|
||||
args = lc arg inlist args, do: typespec_to_ast(arg)
|
||||
{ :::, [line: line], [{ name, [line: line], args }, typespec_to_ast(result)] }
|
||||
end
|
||||
@@ -262,13 +256,13 @@ defmodule Kernel.Typespec do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns all types available from the beam.
|
||||
Returns all types available from the module's beam code.
|
||||
|
||||
It is returned as a list of tuples where the first
|
||||
element is the type (`:typep`, `:type` and `:opaque`).
|
||||
|
||||
The module has to have a corresponding beam file
|
||||
on the file system.
|
||||
The module has to have a corresponding beam file on the disk which can be
|
||||
located by the runtime system.
|
||||
"""
|
||||
def beam_types(module) do
|
||||
case abstract_code(module) do
|
||||
@@ -289,26 +283,26 @@ defmodule Kernel.Typespec do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns all specs available from the beam.
|
||||
Returns all specs available from the module's beam code.
|
||||
|
||||
It is returned as a list of tuples where the first
|
||||
element is spec name and arity and the second is the spec.
|
||||
|
||||
The module has to have a corresponding beam file
|
||||
on the file system.
|
||||
The module has to have a corresponding beam file on the disk which can be
|
||||
located by the runtime system.
|
||||
"""
|
||||
def beam_specs(module) do
|
||||
from_abstract_code(module, :spec)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns all callbacks available from the beam.
|
||||
Returns all callbacks available from the module's beam code.
|
||||
|
||||
It is returned as a list of tuples where the first
|
||||
element is spec name and arity and the second is the spec.
|
||||
|
||||
The module has to have a corresponding beam file
|
||||
on the file system.
|
||||
The module has to have a corresponding beam file on the disk which can be
|
||||
located by the runtime system.
|
||||
"""
|
||||
def beam_callbacks(module) do
|
||||
from_abstract_code(module, :callback)
|
||||
@@ -356,7 +350,7 @@ defmodule Kernel.Typespec do
|
||||
end
|
||||
|
||||
def deftype(_kind, other, _caller) do
|
||||
type_spec = Macro.to_binary(other)
|
||||
type_spec = Macro.to_string(other)
|
||||
raise ArgumentError, message: "invalid type specification #{type_spec}"
|
||||
end
|
||||
|
||||
@@ -397,7 +391,7 @@ defmodule Kernel.Typespec do
|
||||
end
|
||||
|
||||
def defspec(_type, other, _caller) do
|
||||
spec = Macro.to_binary(other)
|
||||
spec = Macro.to_string(other)
|
||||
raise ArgumentError, message: "invalid function type specification #{spec}"
|
||||
end
|
||||
|
||||
@@ -451,17 +445,17 @@ defmodule Kernel.Typespec do
|
||||
fn(arg, expr) -> { :|, [line: line], [expr, arg] } end
|
||||
end
|
||||
|
||||
defp typespec_to_ast({ :type, line, :fun, [{:type, _, :product, args},result] }) do
|
||||
defp typespec_to_ast({ :type, line, :fun, [{:type, _, :product, args}, result] }) do
|
||||
args = lc arg inlist args, do: typespec_to_ast(arg)
|
||||
{ :"->", [line: line], [{args, typespec_to_ast(result)}] }
|
||||
{ :->, [line: line], [{args, [line: line], typespec_to_ast(result)}] }
|
||||
end
|
||||
|
||||
defp typespec_to_ast({ :type, line, :fun, [args, result] }) do
|
||||
{ :"->", [line: line], [{[typespec_to_ast(args)], typespec_to_ast(result)}] }
|
||||
{ :->, [line: line], [{[typespec_to_ast(args)], [line: line], typespec_to_ast(result)}] }
|
||||
end
|
||||
|
||||
defp typespec_to_ast({ :type, line, :fun, [] }) do
|
||||
typespec_to_ast({ :type, line, :fun, [{:type, line, :any}, {:type,line,:any, []} ] })
|
||||
typespec_to_ast({ :type, line, :fun, [{:type, line, :any}, {:type, line, :any, []} ] })
|
||||
end
|
||||
|
||||
defp typespec_to_ast({ :type, line, :range, [left, right] }) do
|
||||
@@ -475,12 +469,12 @@ defmodule Kernel.Typespec do
|
||||
|
||||
defp typespec_to_ast({ :var, line, var }) do
|
||||
var =
|
||||
case atom_to_binary(var) do
|
||||
<<"_", c :: [binary, size(1)], rest :: binary>> ->
|
||||
binary_to_atom("_#{String.downcase(c)}#{rest}")
|
||||
<<c :: [binary, size(1)], rest :: binary>> ->
|
||||
binary_to_atom("#{String.downcase(c)}#{rest}")
|
||||
end
|
||||
case atom_to_binary(var) do
|
||||
<<"_", c :: [binary, size(1)], rest :: binary>> ->
|
||||
binary_to_atom("_#{String.downcase(c)}#{rest}")
|
||||
<<c :: [binary, size(1)], rest :: binary>> ->
|
||||
binary_to_atom("#{String.downcase(c)}#{rest}")
|
||||
end
|
||||
{ var, line, nil }
|
||||
end
|
||||
|
||||
@@ -533,14 +527,14 @@ defmodule Kernel.Typespec do
|
||||
end
|
||||
|
||||
# Handle unions
|
||||
defp typespec({ :|, meta, [_,_] } = exprs, vars, caller) do
|
||||
defp typespec({ :|, meta, [_, _] } = exprs, vars, caller) do
|
||||
exprs = Enum.reverse(collect_union(exprs))
|
||||
union = lc e inlist exprs, do: typespec(e, vars, caller)
|
||||
{ :type, line(meta), :union, union }
|
||||
end
|
||||
|
||||
# Handle binaries
|
||||
defp typespec({:<<>>, meta, []}, _,_) do
|
||||
defp typespec({:<<>>, meta, []}, _, _) do
|
||||
{:type, line(meta), :binary, [{:integer, line(meta), 0}, {:integer, line(meta), 0}]}
|
||||
end
|
||||
|
||||
@@ -568,11 +562,13 @@ 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)
|
||||
defp typespec({:->, meta, [{[{:fun, _, arguments}], cmeta, return}]}, vars, caller) when is_list(arguments) do
|
||||
IO.warn "(fun(...) -> ...) in typespecs is deprecated, please drop fun() and " <>
|
||||
"write (... -> ...) instead. #{Exception.format_stacktrace(caller.stacktrace)}"
|
||||
typespec({:->, meta, [{arguments, cmeta, return}]}, vars, caller)
|
||||
end
|
||||
|
||||
defp typespec({:->, meta, [{arguments, return}]}, vars, caller) when is_list(arguments) do
|
||||
defp typespec({:->, meta, [{arguments, _, return}]}, vars, caller) when is_list(arguments) do
|
||||
args = fn_args(meta, arguments, return, vars, caller)
|
||||
{ :type, line(meta), :fun, args }
|
||||
end
|
||||
@@ -702,7 +698,7 @@ defmodule Kernel.Typespec do
|
||||
|
||||
defp fn_args(meta, args, return, vars, caller) do
|
||||
case [fn_args(meta, args, vars, caller), typespec(return, vars, caller)] do
|
||||
[{:type,_,:any},{:type,_,:any,[]}] -> []
|
||||
[{:type, _, :any}, {:type, _, :any, []}] -> []
|
||||
x -> x
|
||||
end
|
||||
end
|
||||
@@ -724,11 +720,11 @@ defmodule Kernel.Typespec do
|
||||
next,
|
||||
{ :type, _, :tuple, [{ :atom, _, atom }, type] }
|
||||
] }, acc) do
|
||||
unpack_typespec_kw(next, [{atom,typespec_to_ast(type)}|acc])
|
||||
unpack_typespec_kw(next, [{atom, typespec_to_ast(type)}|acc])
|
||||
end
|
||||
|
||||
defp unpack_typespec_kw({ :type, _, :tuple, [{ :atom, _, atom }, type] }, acc) do
|
||||
{ :ok, [{atom,typespec_to_ast(type)}|acc] }
|
||||
{ :ok, [{atom, typespec_to_ast(type)}|acc] }
|
||||
end
|
||||
|
||||
defp unpack_typespec_kw(_, _acc) do
|
||||
|
||||
@@ -56,7 +56,7 @@ defmodule Keyword do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Keyword.new([{:b,1},{:a,2}])
|
||||
iex> Keyword.new([{:b, 1}, {:a, 2}])
|
||||
[a: 2, b: 1]
|
||||
|
||||
"""
|
||||
@@ -74,7 +74,7 @@ defmodule Keyword do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Keyword.new([:a, :b], fn (x) -> {x,x} end) |> Enum.sort
|
||||
iex> Keyword.new([:a, :b], fn (x) -> {x, x} end) |> Enum.sort
|
||||
[a: :a, b: :b]
|
||||
|
||||
"""
|
||||
@@ -266,7 +266,7 @@ defmodule Keyword do
|
||||
def put_new(keywords, key, value) when is_atom(key) do
|
||||
case :lists.keyfind(key, 1, keywords) do
|
||||
{ ^key, _ } -> keywords
|
||||
false -> [{key,value}|keywords]
|
||||
false -> [{key, value}|keywords]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
+12
-10
@@ -10,12 +10,14 @@ defmodule List do
|
||||
first argument.
|
||||
"""
|
||||
|
||||
@compile :inline_list_funcs
|
||||
|
||||
@doc """
|
||||
Given a list of lists, concatenates the sublists into a single list.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> List.concat([[1,[2],3], [4], [5,6]])
|
||||
iex> List.concat([[1, [2], 3], [4], [5, 6]])
|
||||
[1,[2],3,4,5,6]
|
||||
|
||||
"""
|
||||
@@ -32,7 +34,7 @@ defmodule List do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> List.concat([1,2,3], [4,5,6])
|
||||
iex> List.concat([1, 2, 3], [4, 5, 6])
|
||||
[1,2,3,4,5,6]
|
||||
|
||||
"""
|
||||
@@ -47,7 +49,7 @@ defmodule List do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> List.delete([1,2,3], 1)
|
||||
iex> List.delete([1, 2, 3], 1)
|
||||
[2,3]
|
||||
|
||||
"""
|
||||
@@ -63,7 +65,7 @@ defmodule List do
|
||||
iex> List.duplicate("hello", 3)
|
||||
["hello","hello","hello"]
|
||||
|
||||
iex> List.duplicate([1,2], 2)
|
||||
iex> List.duplicate([1, 2], 2)
|
||||
[[1,2],[1,2]]
|
||||
"""
|
||||
def duplicate(elem, n) do
|
||||
@@ -77,10 +79,10 @@ defmodule List do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> List.flatten([1,[[2],3]])
|
||||
iex> List.flatten([1, [[2], 3]])
|
||||
[1,2,3]
|
||||
|
||||
iex> List.flatten([1,[[2],3]], [4,5])
|
||||
iex> List.flatten([1, [[2], 3]], [4, 5])
|
||||
[1,2,3,4,5]
|
||||
|
||||
"""
|
||||
@@ -98,10 +100,10 @@ defmodule List do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> List.foldl([5,5], 10, fn (x, acc) -> x + acc end)
|
||||
iex> List.foldl([5, 5], 10, fn (x, acc) -> x + acc end)
|
||||
20
|
||||
|
||||
iex> List.foldl([1,2,3,4], 0, fn (x, acc) -> x - acc end)
|
||||
iex> List.foldl([1, 2, 3, 4], 0, fn (x, acc) -> x - acc end)
|
||||
2
|
||||
|
||||
"""
|
||||
@@ -115,7 +117,7 @@ defmodule List do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> List.foldr([1,2,3,4], 0, fn (x, acc) -> x - acc end)
|
||||
iex> List.foldr([1, 2, 3, 4], 0, fn (x, acc) -> x - acc end)
|
||||
-2
|
||||
|
||||
"""
|
||||
@@ -241,7 +243,7 @@ defmodule List do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> List.wrap([1,2,3])
|
||||
iex> List.wrap([1, 2, 3])
|
||||
[1,2,3]
|
||||
|
||||
"""
|
||||
|
||||
@@ -109,7 +109,7 @@ defmodule ListDict do
|
||||
def put_new(dict, key, val) do
|
||||
case :lists.keyfind(key, 1, dict) do
|
||||
{ ^key, _ } -> dict
|
||||
false -> [{key,val}|dict]
|
||||
false -> [{key, val}|dict]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -131,35 +131,21 @@ defmodule ListDict do
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Splits a dict into two dicts,
|
||||
one containing entries with key in the keys list,
|
||||
and another containing entries with key not in keys.
|
||||
Returns a 2-tuple of the new dicts.
|
||||
"""
|
||||
def split(dict, keys) do
|
||||
acc = { new(), new() }
|
||||
Enum.reduce dict, acc, fn({ k, v }, { take, drop }) ->
|
||||
if :lists.member(k, keys) do
|
||||
{ [{k,v}|take], drop }
|
||||
{ [{k, v}|take], drop }
|
||||
else
|
||||
{ take, [{k,v}|drop] }
|
||||
{ take, [{k, v}|drop] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a new dict with only the entries
|
||||
which key is in keys
|
||||
"""
|
||||
def take(dict, keys) do
|
||||
lc { k, _ } = tuple inlist dict, :lists.member(k, keys), do: tuple
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a new dict with only the entries
|
||||
which key is not in keys
|
||||
"""
|
||||
def drop(dict, keys) do
|
||||
lc { k, _ } = tuple inlist dict, not :lists.member(k, keys), do: tuple
|
||||
end
|
||||
|
||||
+190
-126
@@ -1,4 +1,4 @@
|
||||
import Kernel, except: [to_binary: 1]
|
||||
import Kernel, except: [to_string: 1]
|
||||
|
||||
defmodule Macro do
|
||||
@moduledoc """
|
||||
@@ -14,10 +14,10 @@ defmodule Macro do
|
||||
:===, :!==,
|
||||
:==, :!=, :<=, :>=,
|
||||
:&&, :||, :<>, :++, :--, :**, ://, :::, :<-, :.., :|>, :=~,
|
||||
:<, :>,
|
||||
:<, :>, :->,
|
||||
:+, :-, :*, :/, :=, :|, :.,
|
||||
:and, :or, :xor, :when, :in, :inlist, :inbits,
|
||||
:<<<, :>>>, :|||, :&&&, :^^^, :~~~
|
||||
:<<<, :>>>, :|||, :&&&, :^^^
|
||||
]
|
||||
end
|
||||
|
||||
@@ -26,7 +26,7 @@ defmodule Macro do
|
||||
as a macro so it can be used in guard clauses.
|
||||
"""
|
||||
defmacro unary_ops do
|
||||
[:!, :@, :^, :not, :+, :-]
|
||||
[:!, :@, :^, :not, :+, :-, :~~~, :&]
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -42,8 +42,8 @@ defmodule Macro do
|
||||
|
||||
extract_args(quote do: foo) == { :foo, [] }
|
||||
extract_args(quote do: foo()) == { :foo, [] }
|
||||
extract_args(quote do: foo(1,2,3)) == { :foo, [1,2,3] }
|
||||
extract_args(quote do: 1.(1,2,3)) == :error
|
||||
extract_args(quote do: foo(1, 2, 3)) == { :foo, [1, 2, 3] }
|
||||
extract_args(quote do: 1.(1, 2, 3)) == :error
|
||||
|
||||
"""
|
||||
def extract_args(expr) do
|
||||
@@ -54,6 +54,10 @@ defmodule Macro do
|
||||
Recursively escapes a value so it can be inserted
|
||||
into a syntax tree.
|
||||
|
||||
One may pass `unquote: true` to `Macro.escape/2`
|
||||
which leaves unquote statements unescaped, effectively
|
||||
unquoting the contents on escape.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Macro.escape(:foo)
|
||||
@@ -62,14 +66,12 @@ defmodule Macro do
|
||||
iex> Macro.escape({ :a, :b, :c })
|
||||
{ :{}, [], [:a, :b, :c] }
|
||||
|
||||
"""
|
||||
def escape(expr) do
|
||||
:elixir_quote.escape(expr, false) |> elem(0)
|
||||
end
|
||||
iex> Macro.escape({ :unquote, [], [1] }, unquote: true)
|
||||
1
|
||||
|
||||
@doc false
|
||||
def escape_quoted(expr) do
|
||||
:elixir_quote.escape(expr, true) |> elem(0)
|
||||
"""
|
||||
def escape(expr, opts // []) do
|
||||
:elixir_quote.escape(expr, Keyword.get(opts, :unquote, false)) |> elem(0)
|
||||
end
|
||||
|
||||
@doc %B"""
|
||||
@@ -151,7 +153,7 @@ defmodule Macro do
|
||||
|
||||
Only tokens that are binaries are unescaped, all others are
|
||||
ignored. This function is useful when implementing your own
|
||||
sigils. Check the implementation of `Kernel.__b__`
|
||||
sigils. Check the implementation of `Kernel.sigil_b`
|
||||
for examples.
|
||||
"""
|
||||
def unescape_tokens(tokens) do
|
||||
@@ -166,112 +168,124 @@ defmodule Macro do
|
||||
:elixir_interpolation.unescape_tokens(tokens, map)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def to_binary(tree) do
|
||||
IO.write "[WARNING] Macro.to_binary is deprecated, please use Macro.to_string instead\n#{Exception.format_stacktrace}"
|
||||
to_string(tree)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Converts the given expression to a binary.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Macro.to_binary(quote do: foo.bar(1, 2, 3))
|
||||
iex> Macro.to_string(quote do: foo.bar(1, 2, 3))
|
||||
"foo.bar(1, 2, 3)"
|
||||
|
||||
"""
|
||||
def to_binary(tree)
|
||||
def to_string(tree)
|
||||
|
||||
# Variables
|
||||
def to_binary({ var, _, atom }) when is_atom(atom) do
|
||||
def to_string({ var, _, atom }) when is_atom(atom) do
|
||||
atom_to_binary(var, :utf8)
|
||||
end
|
||||
|
||||
# Aliases
|
||||
def to_binary({ :__aliases__, _, refs }) do
|
||||
Enum.map_join(refs, ".", call_to_binary(&1))
|
||||
def to_string({ :__aliases__, _, refs }) do
|
||||
Enum.map_join(refs, ".", call_to_string(&1))
|
||||
end
|
||||
|
||||
# Blocks
|
||||
def to_binary({ :__block__, _, [expr] }) do
|
||||
to_binary(expr)
|
||||
def to_string({ :__block__, _, [expr] }) do
|
||||
to_string(expr)
|
||||
end
|
||||
|
||||
def to_binary({ :__block__, _, _ } = expr) do
|
||||
block = adjust_new_lines block_to_binary(expr), "\n "
|
||||
def to_string({ :__block__, _, _ } = expr) do
|
||||
block = adjust_new_lines block_to_string(expr), "\n "
|
||||
"(\n " <> block <> "\n)"
|
||||
end
|
||||
|
||||
# Bits containers
|
||||
def to_binary({ :<<>>, _, args }) do
|
||||
"<<" <> Enum.map_join(args, ", ", to_binary(&1)) <> ">>"
|
||||
def to_string({ :<<>>, _, args }) do
|
||||
"<<" <> Enum.map_join(args, ", ", to_string(&1)) <> ">>"
|
||||
end
|
||||
|
||||
# Tuple containers
|
||||
def to_binary({ :{}, _, args }) do
|
||||
"{" <> Enum.map_join(args, ", ", to_binary(&1)) <> "}"
|
||||
def to_string({ :{}, _, args }) do
|
||||
"{" <> Enum.map_join(args, ", ", to_string(&1)) <> "}"
|
||||
end
|
||||
|
||||
# List containers
|
||||
def to_binary({ :[], _, args }) do
|
||||
"[" <> Enum.map_join(args, ", ", to_binary(&1)) <> "]"
|
||||
def to_string({ :[], _, args }) do
|
||||
"[" <> Enum.map_join(args, ", ", to_string(&1)) <> "]"
|
||||
end
|
||||
|
||||
# Fn keyword
|
||||
def to_binary({ :fn, _, [[do: { :->, _, [{_,tuple}] } = arrow]] })
|
||||
def to_string({ :fn, _, [[do: { :->, _, [{_, _, tuple}] } = arrow]] })
|
||||
when not is_tuple(tuple) or elem(tuple, 0) != :__block__ do
|
||||
"fn " <> arrow_to_binary(arrow) <> " end"
|
||||
"fn " <> arrow_to_string(arrow) <> " end"
|
||||
end
|
||||
|
||||
def to_binary({ :fn, _, [[do: { :->, _, [_] } = block]] }) do
|
||||
"fn " <> block_to_binary(block) <> "\nend"
|
||||
def to_string({ :fn, _, [[do: { :->, _, [_] } = block]] }) do
|
||||
"fn " <> block_to_string(block) <> "\nend"
|
||||
end
|
||||
|
||||
def to_binary({ :fn, _, [[do: block]] }) do
|
||||
block = adjust_new_lines block_to_binary(block), "\n "
|
||||
def to_string({ :fn, _, [[do: block]] }) do
|
||||
block = adjust_new_lines block_to_string(block), "\n "
|
||||
"fn\n " <> block <> "\nend"
|
||||
end
|
||||
|
||||
# Partial call
|
||||
def to_binary({ :&, _, [num] }) do
|
||||
def to_string({ :&, _, [num] }) do
|
||||
"&#{num}"
|
||||
end
|
||||
|
||||
# left -> right
|
||||
def to_binary({ :->, _, _ } = arrow) do
|
||||
"(" <> arrow_to_binary(arrow, true) <> ")"
|
||||
def to_string({ :->, _, _ } = arrow) do
|
||||
"(" <> arrow_to_string(arrow, true) <> ")"
|
||||
end
|
||||
|
||||
# Binary ops
|
||||
def to_binary({ op, _, [left, right] }) when op in binary_ops do
|
||||
op_to_binary(left) <> " #{op} " <> op_to_binary(right)
|
||||
def to_string({ op, _, [left, right] }) when op in binary_ops do
|
||||
op_to_string(left) <> " #{op} " <> op_to_string(right)
|
||||
end
|
||||
|
||||
# Splat when
|
||||
def to_string({ :when, _, args }) do
|
||||
{ left, right } = :elixir_tree_helpers.split_last(args)
|
||||
"(" <> Enum.map_join(left, ", ", to_string(&1)) <> ") when " <> to_string(right)
|
||||
end
|
||||
|
||||
# Unary ops
|
||||
def to_binary({ :not, _, [arg] }) do
|
||||
"not " <> to_binary(arg)
|
||||
def to_string({ :not, _, [arg] }) do
|
||||
"not " <> to_string(arg)
|
||||
end
|
||||
|
||||
def to_binary({ op, _, [arg] }) when op in unary_ops do
|
||||
atom_to_binary(op, :utf8) <> to_binary(arg)
|
||||
def to_string({ op, _, [arg] }) when op in unary_ops do
|
||||
atom_to_binary(op, :utf8) <> to_string(arg)
|
||||
end
|
||||
|
||||
# All other calls
|
||||
def to_binary({ target, _, args }) when is_list(args) do
|
||||
def to_string({ target, _, args }) when is_list(args) do
|
||||
{ list, last } = :elixir_tree_helpers.split_last(args)
|
||||
case is_kw_blocks?(last) do
|
||||
true -> call_to_binary_with_args(target, list) <> kw_blocks_to_binary(last)
|
||||
false -> call_to_binary_with_args(target, args)
|
||||
true -> call_to_string_with_args(target, list) <> kw_blocks_to_string(last)
|
||||
false -> call_to_string_with_args(target, args)
|
||||
end
|
||||
end
|
||||
|
||||
# Two-item tuples
|
||||
def to_binary({ left, right }) do
|
||||
to_binary({ :{}, [], [left, right] })
|
||||
def to_string({ left, right }) do
|
||||
to_string({ :{}, [], [left, right] })
|
||||
end
|
||||
|
||||
# Lists
|
||||
def to_binary(list) when is_list(list) do
|
||||
to_binary({ :[], [], list })
|
||||
def to_string(list) when is_list(list) do
|
||||
to_string({ :[], [], list })
|
||||
end
|
||||
|
||||
# All other structures
|
||||
def to_binary(other), do: inspect(other, raw: true)
|
||||
def to_string(other), do: inspect(other, raw: true)
|
||||
|
||||
# Block keywords
|
||||
defmacrop kw_keywords, do: [:do, :catch, :rescue, :after, :else]
|
||||
@@ -281,56 +295,56 @@ defmodule Macro do
|
||||
end
|
||||
defp is_kw_blocks?(_), do: false
|
||||
|
||||
defp module_to_binary(atom) when is_atom(atom), do: inspect(atom, raw: true)
|
||||
defp module_to_binary(other), do: call_to_binary(other)
|
||||
defp module_to_string(atom) when is_atom(atom), do: inspect(atom, raw: true)
|
||||
defp module_to_string(other), do: call_to_string(other)
|
||||
|
||||
defp call_to_binary(atom) when is_atom(atom), do: atom_to_binary(atom, :utf8)
|
||||
defp call_to_binary({ :., _, [arg] }), do: module_to_binary(arg) <> "."
|
||||
defp call_to_binary({ :., _, [left, right] }), do: module_to_binary(left) <> "." <> call_to_binary(right)
|
||||
defp call_to_binary(other), do: to_binary(other)
|
||||
defp call_to_string(atom) when is_atom(atom), do: atom_to_binary(atom, :utf8)
|
||||
defp call_to_string({ :., _, [arg] }), do: module_to_string(arg) <> "."
|
||||
defp call_to_string({ :., _, [left, right] }), do: module_to_string(left) <> "." <> call_to_string(right)
|
||||
defp call_to_string(other), do: to_string(other)
|
||||
|
||||
defp call_to_binary_with_args(target, args) do
|
||||
args = Enum.map_join(args, ", ", to_binary(&1))
|
||||
call_to_binary(target) <> "(" <> args <> ")"
|
||||
defp call_to_string_with_args(target, args) do
|
||||
args = Enum.map_join(args, ", ", to_string(&1))
|
||||
call_to_string(target) <> "(" <> args <> ")"
|
||||
end
|
||||
|
||||
defp kw_blocks_to_binary(kw) do
|
||||
defp kw_blocks_to_string(kw) do
|
||||
Enum.reduce(kw_keywords, " ", fn(x, acc) ->
|
||||
case Keyword.has_key?(kw, x) do
|
||||
true -> acc <> kw_block_to_binary(x, Keyword.get(kw, x))
|
||||
true -> acc <> kw_block_to_string(x, Keyword.get(kw, x))
|
||||
false -> acc
|
||||
end
|
||||
end) <> "end"
|
||||
end
|
||||
|
||||
defp kw_block_to_binary(key, value) do
|
||||
block = adjust_new_lines block_to_binary(value), "\n "
|
||||
defp kw_block_to_string(key, value) do
|
||||
block = adjust_new_lines block_to_string(value), "\n "
|
||||
atom_to_binary(key, :utf8) <> "\n " <> block <> "\n"
|
||||
end
|
||||
|
||||
defp block_to_binary({ :->, _, exprs }) do
|
||||
Enum.map_join(exprs, "\n", fn({ left, right }) ->
|
||||
defp block_to_string({ :->, _, exprs }) do
|
||||
Enum.map_join(exprs, "\n", fn({ left, _, right }) ->
|
||||
left = comma_join_or_empty_paren(left, false)
|
||||
left <> "->\n " <> adjust_new_lines block_to_binary(right), "\n "
|
||||
left <> "->\n " <> adjust_new_lines block_to_string(right), "\n "
|
||||
end)
|
||||
end
|
||||
|
||||
defp block_to_binary({ :__block__, _, exprs }) do
|
||||
Enum.map_join(exprs, "\n", to_binary(&1))
|
||||
defp block_to_string({ :__block__, _, exprs }) do
|
||||
Enum.map_join(exprs, "\n", to_string(&1))
|
||||
end
|
||||
|
||||
defp block_to_binary(other), do: to_binary(other)
|
||||
defp block_to_string(other), do: to_string(other)
|
||||
|
||||
defp op_to_binary({ op, _, [_, _] } = expr) when op in binary_ops do
|
||||
"(" <> to_binary(expr) <> ")"
|
||||
defp op_to_string({ op, _, [_, _] } = expr) when op in binary_ops do
|
||||
"(" <> to_string(expr) <> ")"
|
||||
end
|
||||
|
||||
defp op_to_binary(expr), do: to_binary(expr)
|
||||
defp op_to_string(expr), do: to_string(expr)
|
||||
|
||||
defp arrow_to_binary({ :->, _, pairs }, paren // false) do
|
||||
Enum.map_join(pairs, "; ", fn({ left, right }) ->
|
||||
defp arrow_to_string({ :->, _, pairs }, paren // false) do
|
||||
Enum.map_join(pairs, "; ", fn({ left, _, right }) ->
|
||||
left = comma_join_or_empty_paren(left, paren)
|
||||
left <> "-> " <> to_binary(right)
|
||||
left <> "-> " <> to_string(right)
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -338,7 +352,7 @@ defmodule Macro do
|
||||
defp comma_join_or_empty_paren([], false), do: ""
|
||||
|
||||
defp comma_join_or_empty_paren(left, _) do
|
||||
Enum.map_join(left, ", ", to_binary(&1)) <> " "
|
||||
Enum.map_join(left, ", ", to_string(&1)) <> " "
|
||||
end
|
||||
|
||||
defp adjust_new_lines(block, replacement) do
|
||||
@@ -351,32 +365,18 @@ defmodule Macro do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Receives an expression representation and expands it. The following
|
||||
contents are expanded:
|
||||
Receives a AST node and expands it once. The following contents are expanded:
|
||||
|
||||
* Macros (local or remote);
|
||||
* Aliases are expanded (if possible) and return atoms;
|
||||
* All pseudo-variables (__FILE__, __MODULE__, etc);
|
||||
* Module attributes reader (@foo);
|
||||
|
||||
In case the expression cannot be expanded, it returns the expression itself.
|
||||
|
||||
Notice that `Macro.expand` is not recursive and it does not
|
||||
expand child expressions. In this example
|
||||
|
||||
Macro.expand(quote(do: !some_macro), __ENV__)
|
||||
|
||||
`!some_macro` will expand to something like:
|
||||
|
||||
case some_macro do
|
||||
false -> true
|
||||
nil -> true
|
||||
_ -> false
|
||||
end
|
||||
|
||||
Notice that the `!` operator is a macro that expands to a case.
|
||||
Even though `some_macro` is also a macro, it is not expanded
|
||||
because it is a child expression given to `!` as argument.
|
||||
In case the expression cannot be expanded, it returns the expression
|
||||
itself. Notice that `Macro.expand_once/2` performs the expansion just
|
||||
once and it is not recursive. Check `Macro.expand/2` for expansion
|
||||
until the node no longer represents a macro and `Macro.expand_all/2`
|
||||
for recursive expansion.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -439,46 +439,50 @@ defmodule Macro do
|
||||
end
|
||||
|
||||
"""
|
||||
def expand(aliases, env) do
|
||||
expand(aliases, env, nil)
|
||||
def expand_once(aliases, env) do
|
||||
expand_once(aliases, env, nil) |> elem(0)
|
||||
end
|
||||
|
||||
defp expand({ :__aliases__, _, _ } = original, env, cache) do
|
||||
defp expand_once({ :__aliases__, _, _ } = original, env, cache) do
|
||||
case :elixir_aliases.expand(original, env.aliases, env.macro_aliases) do
|
||||
atom when is_atom(atom) -> atom
|
||||
atom when is_atom(atom) -> { atom, true, cache }
|
||||
aliases ->
|
||||
aliases = lc alias inlist aliases, do: expand(alias, env, cache)
|
||||
aliases = lc alias inlist aliases, do: (expand_once(alias, env, cache) |> elem(0))
|
||||
|
||||
case :lists.all(is_atom(&1), aliases) do
|
||||
true -> :elixir_aliases.concat(aliases)
|
||||
false -> original
|
||||
true -> { :elixir_aliases.concat(aliases), true, cache }
|
||||
false -> { original, false, cache }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Expand @ calls
|
||||
defp expand({ :@, _, [{ name, _, args }] } = original, env, _cache) when is_atom(args) or args == [] do
|
||||
defp expand_once({ :@, _, [{ name, _, args }] } = original, env, cache) when is_atom(args) or args == [] do
|
||||
case (module = env.module) && Module.open?(module) do
|
||||
true -> Module.get_attribute(module, name)
|
||||
false -> original
|
||||
true -> { Module.get_attribute(module, name), true, cache }
|
||||
false -> { original, false, cache }
|
||||
end
|
||||
end
|
||||
|
||||
# Expand pseudo-variables
|
||||
defp expand({ :__MODULE__, _, atom }, env, _cache) when is_atom(atom), do: env.module
|
||||
defp expand({ :__FILE__, _, atom }, env, _cache) when is_atom(atom), do: env.file
|
||||
defp expand({ :__DIR__, _, atom }, env, _cache) when is_atom(atom), do: :filename.dirname(env.file)
|
||||
defp expand({ :__ENV__, _, atom }, env, _cache) when is_atom(atom), do: env
|
||||
defp expand_once({ :__MODULE__, _, atom }, env, cache) when is_atom(atom),
|
||||
do: { env.module, true, cache }
|
||||
defp expand_once({ :__FILE__, _, atom }, env, cache) when is_atom(atom),
|
||||
do: { env.file, true, cache }
|
||||
defp expand_once({ :__DIR__, _, atom }, env, cache) when is_atom(atom),
|
||||
do: { :filename.dirname(env.file), true, cache }
|
||||
defp expand_once({ :__ENV__, _, atom }, env, cache) when is_atom(atom),
|
||||
do: { env, true, cache }
|
||||
|
||||
# Expand possible macro import invocation
|
||||
defp expand({ atom, line, args } = original, env, cache) when is_atom(atom) do
|
||||
defp expand_once({ atom, line, args } = original, env, cache) when is_atom(atom) do
|
||||
args = case is_atom(args) do
|
||||
true -> []
|
||||
false -> args
|
||||
end
|
||||
|
||||
case not is_partial?(args) do
|
||||
false -> original
|
||||
false -> { original, false, cache }
|
||||
true ->
|
||||
module = env.module
|
||||
|
||||
@@ -488,43 +492,103 @@ defmodule Macro do
|
||||
[]
|
||||
end
|
||||
|
||||
cache = to_erl_env(env, cache)
|
||||
expand = :elixir_dispatch.expand_import(line, { atom, length(args) }, args,
|
||||
env.module, extra, to_erl_env(env, cache))
|
||||
env.module, extra, cache)
|
||||
|
||||
case expand do
|
||||
{ :ok, _, expanded } -> expanded
|
||||
{ :error, _ } -> original
|
||||
{ :ok, _, expanded } -> { expanded, true, cache }
|
||||
{ :error, _ } -> { original, false, cache }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Expand possible macro require invocation
|
||||
defp expand({ { :., _, [left, right] }, line, args } = original, env, cache) when is_atom(right) do
|
||||
receiver = expand(left, env)
|
||||
defp expand_once({ { :., _, [left, right] }, line, args } = original, env, cache) when is_atom(right) do
|
||||
{ receiver, _, _ } = expand_once(left, env, cache)
|
||||
|
||||
case is_atom(receiver) and not is_partial?(args) do
|
||||
false -> original
|
||||
false -> { original, false, cache }
|
||||
true ->
|
||||
cache = to_erl_env(env, cache)
|
||||
expand = :elixir_dispatch.expand_require(line, receiver, { right, length(args) },
|
||||
args, env.module, to_erl_env(env, cache))
|
||||
args, env.module, cache)
|
||||
|
||||
case expand do
|
||||
{ :ok, _receiver, expanded } -> expanded
|
||||
{ :error, _ } -> original
|
||||
{ :ok, _receiver, expanded } -> { expanded, true, cache }
|
||||
{ :error, _ } -> { original, false, cache }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Anything else is just returned
|
||||
defp expand(other, _env, _cache), do: other
|
||||
defp expand_once(other, _env, cache), do: { other, false, cache }
|
||||
|
||||
defp to_erl_env(env, nil), do: :elixir_scope.to_erl_env(env)
|
||||
defp to_erl_env(_env, cache), do: cache
|
||||
|
||||
## Helpers
|
||||
|
||||
defp is_partial?(args) do
|
||||
:lists.any(match?({ :&, _, [_] }, &1), args)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Receives a AST node and expands it until it no longer represents
|
||||
a macro. Check `Macro.expand_once/2` for more information on how
|
||||
expansion works and `Macro.expand_all/2` for recursive expansion.
|
||||
"""
|
||||
def expand(tree, env) do
|
||||
expand(tree, env, nil) |> elem(0)
|
||||
end
|
||||
|
||||
@doc false # Used internally by Elixir
|
||||
def expand(tree, env, cache) do
|
||||
expand_until({ tree, true, cache }, env)
|
||||
end
|
||||
|
||||
defp expand_until({ tree, true, cache }, env) do
|
||||
expand_until(expand_once(tree, env, cache), env)
|
||||
end
|
||||
|
||||
defp expand_until({ tree, false, cache }, _env) do
|
||||
{ tree, cache }
|
||||
end
|
||||
|
||||
@doc """
|
||||
Receives a AST node and expands it until it no longer represents
|
||||
a macro. Then it expands all of its children recursively.
|
||||
|
||||
Check `Macro.expand_once/2` for more information on how expansion
|
||||
works.
|
||||
"""
|
||||
def expand_all(tree, env) do
|
||||
expand_all(tree, env, nil) |> elem(0)
|
||||
end
|
||||
|
||||
@doc false # Used internally by Elixir
|
||||
def expand_all(tree, env, cache) do
|
||||
expand_all_until(expand(tree, env, cache), env)
|
||||
end
|
||||
|
||||
defp expand_all_until({ { left, meta, right }, cache }, env) do
|
||||
{ left, cache } = expand_all(left, env, cache)
|
||||
{ right, cache } = expand_all(right, env, cache)
|
||||
{ { left, meta, right }, cache }
|
||||
end
|
||||
|
||||
defp expand_all_until({ { left, right }, cache }, env) do
|
||||
{ left, cache } = expand_all(left, env, cache)
|
||||
{ right, cache } = expand_all(right, env, cache)
|
||||
{ { left, right }, cache }
|
||||
end
|
||||
|
||||
defp expand_all_until({ list, cache }, env) when is_list(list) do
|
||||
:lists.mapfoldl(expand_all(&1, env, &2), cache, list)
|
||||
end
|
||||
|
||||
defp expand_all_until({ other, cache }, _env) do
|
||||
{ other, cache }
|
||||
end
|
||||
|
||||
@doc """
|
||||
Recurs the quoted expression checking if all sub terms are
|
||||
safe (i.e. they represented data structured and don't actually
|
||||
|
||||
+48
-30
@@ -1,6 +1,4 @@
|
||||
defmodule Module do
|
||||
require :ets, as: ETS
|
||||
|
||||
defmacrop is_env(env) do
|
||||
quote do
|
||||
is_tuple(unquote(env)) and size(unquote(env)) > 1 and elem(unquote(env), 0) == Macro.Env
|
||||
@@ -192,7 +190,7 @@ defmodule Module do
|
||||
IO.puts "and guards"
|
||||
IO.inspect guards
|
||||
IO.puts "and body"
|
||||
IO.puts Macro.to_binary(body)
|
||||
IO.puts Macro.to_string(body)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -304,7 +302,7 @@ defmodule Module do
|
||||
"""
|
||||
def open?(module) do
|
||||
table = data_table_for(module)
|
||||
table == ETS.info(table, :name)
|
||||
table == :ets.info(table, :name)
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -492,12 +490,12 @@ defmodule Module do
|
||||
{ simplify_signature(x, line, acc), acc + 1 }
|
||||
end
|
||||
|
||||
case ETS.lookup(table, tuple) do
|
||||
case :ets.lookup(table, tuple) do
|
||||
[] ->
|
||||
ETS.insert(table, { tuple, line, kind, signature, doc })
|
||||
:ets.insert(table, { tuple, line, kind, signature, doc })
|
||||
:ok
|
||||
[{ tuple, line, _old_kind, old_sign, old_doc }] ->
|
||||
ETS.insert(table, {
|
||||
:ets.insert(table, {
|
||||
tuple,
|
||||
line,
|
||||
kind,
|
||||
@@ -577,7 +575,7 @@ defmodule Module do
|
||||
def defines?(module, tuple) when is_tuple(tuple) do
|
||||
assert_not_compiled!(:defines?, module)
|
||||
table = function_table_for(module)
|
||||
ETS.lookup(table, tuple) != []
|
||||
:ets.lookup(table, tuple) != []
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -597,7 +595,7 @@ defmodule Module do
|
||||
def defines?(module, tuple, kind) do
|
||||
assert_not_compiled!(:defines?, module)
|
||||
table = function_table_for(module)
|
||||
case ETS.lookup(table, tuple) do
|
||||
case :ets.lookup(table, tuple) do
|
||||
[{ _, ^kind, _, _, _, _, _ }] -> true
|
||||
_ -> false
|
||||
end
|
||||
@@ -617,7 +615,7 @@ defmodule Module do
|
||||
def definitions_in(module) do
|
||||
assert_not_compiled!(:definitions_in, module)
|
||||
table = function_table_for(module)
|
||||
lc { tuple, _, _, _, _, _, _ } inlist ETS.tab2list(table), do: tuple
|
||||
lc { tuple, _, _, _, _, _, _ } inlist :ets.tab2list(table), do: tuple
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -636,7 +634,7 @@ defmodule Module do
|
||||
def definitions_in(module, kind) do
|
||||
assert_not_compiled!(:definitions_in, module)
|
||||
table = function_table_for(module)
|
||||
lc { tuple, stored_kind, _, _, _, _, _ } inlist ETS.tab2list(table), stored_kind == kind, do: tuple
|
||||
lc { tuple, stored_kind, _, _, _, _, _ } inlist :ets.tab2list(table), stored_kind == kind, do: tuple
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -690,26 +688,39 @@ defmodule Module do
|
||||
assert_not_compiled!(:put_attribute, module)
|
||||
table = data_table_for(module)
|
||||
value = normalize_attribute(key, value)
|
||||
acc = ETS.lookup_element(table, :__acc_attributes, 2)
|
||||
acc = :ets.lookup_element(table, :__acc_attributes, 2)
|
||||
|
||||
new =
|
||||
if :lists.member(key, acc) do
|
||||
case ETS.lookup(table, key) do
|
||||
[{^key,old}] -> [value|old]
|
||||
case :ets.lookup(table, key) do
|
||||
[{^key, old}] -> [value|old]
|
||||
[] -> [value]
|
||||
end
|
||||
else
|
||||
value
|
||||
end
|
||||
|
||||
ETS.insert(table, { key, new })
|
||||
:ets.insert(table, { key, new })
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the given attribute from a module. If the attribute
|
||||
was marked as accumulate with `Module.register_attribute`,
|
||||
was marked with `accumulate` with `Module.register_attribute`,
|
||||
a list is always returned.
|
||||
|
||||
The `@` macro compiles to a call to this function. For example,
|
||||
the following code:
|
||||
|
||||
@foo
|
||||
|
||||
Expands to:
|
||||
|
||||
Module.get_attribute(__MODULE__, @foo, true)
|
||||
|
||||
Notice the third argument is used to indicate if a warning
|
||||
should be emitted when the attribute was not previously defined.
|
||||
This is true for `@foo` attributes but false for direct calls.
|
||||
|
||||
## Examples
|
||||
|
||||
defmodule Foo do
|
||||
@@ -722,15 +733,22 @@ defmodule Module do
|
||||
end
|
||||
|
||||
"""
|
||||
def get_attribute(module, key) when is_atom(key) do
|
||||
def get_attribute(module, key, warn // false) when is_atom(key) do
|
||||
assert_not_compiled!(:get_attribute, module)
|
||||
table = data_table_for(module)
|
||||
|
||||
case ETS.lookup(table, key) do
|
||||
[{^key,old}] -> old
|
||||
case :ets.lookup(table, key) do
|
||||
[{^key, val}] -> val
|
||||
[] ->
|
||||
acc = ETS.lookup_element(table, :__acc_attributes, 2)
|
||||
if :lists.member(key, acc), do: [], else: nil
|
||||
acc = :ets.lookup_element(table, :__acc_attributes, 2)
|
||||
|
||||
if :lists.member(key, acc) do
|
||||
[]
|
||||
else
|
||||
warn && IO.warn "#{Exception.format_caller} undefined module attribute @#{key}, " <>
|
||||
"please remove access to @#{key} or explicitly set it to nil before access"
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -748,7 +766,7 @@ defmodule Module do
|
||||
def delete_attribute(module, key) when is_atom(key) do
|
||||
assert_not_compiled!(:delete_attribute, module)
|
||||
table = data_table_for(module)
|
||||
ETS.delete(table, key)
|
||||
:ets.delete(table, key)
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -789,13 +807,13 @@ defmodule Module do
|
||||
table = data_table_for(module)
|
||||
|
||||
if Keyword.get(opts, :persist, true) do
|
||||
old = ETS.lookup_element(table, :__persisted_attributes, 2)
|
||||
ETS.insert(table, { :__persisted_attributes, [new|old] })
|
||||
old = :ets.lookup_element(table, :__persisted_attributes, 2)
|
||||
:ets.insert(table, { :__persisted_attributes, [new|old] })
|
||||
end
|
||||
|
||||
if Keyword.get(opts, :accumulate, true) do
|
||||
old = ETS.lookup_element(table, :__acc_attributes, 2)
|
||||
ETS.insert(table, { :__acc_attributes, [new|old] })
|
||||
old = :ets.lookup_element(table, :__acc_attributes, 2)
|
||||
:ets.insert(table, { :__acc_attributes, [new|old] })
|
||||
end
|
||||
end
|
||||
|
||||
@@ -836,7 +854,7 @@ defmodule Module do
|
||||
:ok ->
|
||||
:ok
|
||||
{ :error, :private_doc } ->
|
||||
IO.puts "#{env.file}:#{line} function #{name}/#{arity} is private, @doc's are always discarded for private functions"
|
||||
IO.warn "#{env.file}:#{line} function #{name}/#{arity} is private, @doc's are always discarded for private functions"
|
||||
end
|
||||
|
||||
delete_attribute(module, :doc)
|
||||
@@ -850,12 +868,12 @@ defmodule Module do
|
||||
table = data_table_for(module)
|
||||
|
||||
new =
|
||||
case ETS.lookup(table, key) do
|
||||
[{^key,old}] -> [value|old]
|
||||
case :ets.lookup(table, key) do
|
||||
[{^key, old}] -> [value|old]
|
||||
[] -> [value]
|
||||
end
|
||||
|
||||
ETS.insert(table, { key, new })
|
||||
:ets.insert(table, { key, new })
|
||||
end
|
||||
|
||||
## Helpers
|
||||
|
||||
@@ -390,11 +390,11 @@ defmodule Path do
|
||||
|
||||
defp do_join(<<uc_letter, ?:, rest :: binary>>, relativename, [], :win32) when uc_letter in ?A..?Z, do:
|
||||
do_join(rest, relativename, [?:, uc_letter+?a-?A], :win32)
|
||||
defp do_join(<<?\\,rest :: binary>>, relativename, result, :win32), do:
|
||||
do_join(<<?/,rest :: binary>>, relativename, result, :win32)
|
||||
defp do_join(<<?/,rest :: binary>>, relativename, [?., ?/|result], os_type), do:
|
||||
defp do_join(<<?\\, rest :: binary>>, relativename, result, :win32), do:
|
||||
do_join(<<?/, rest :: binary>>, relativename, result, :win32)
|
||||
defp do_join(<<?/, rest :: binary>>, relativename, [?., ?/|result], os_type), do:
|
||||
do_join(rest, relativename, [?/|result], os_type)
|
||||
defp do_join(<<?/,rest :: binary>>, relativename, [?/|result], os_type), do:
|
||||
defp do_join(<<?/, rest :: binary>>, relativename, [?/|result], os_type), do:
|
||||
do_join(rest, relativename, [?/|result], os_type)
|
||||
defp do_join(<<>>, <<>>, result, os_type), do:
|
||||
list_to_binary(maybe_remove_dirsep(result, os_type))
|
||||
@@ -404,7 +404,7 @@ defmodule Path do
|
||||
do_join(relativename, <<>>, [?/|result], os_type)
|
||||
defp do_join(<<>>, relativename, result, os_type), do:
|
||||
do_join(relativename, <<>>, [?/|result], os_type)
|
||||
defp do_join(<<char,rest :: binary>>, relativename, result, os_type) when is_integer(char), do:
|
||||
defp do_join(<<char, rest :: binary>>, relativename, result, os_type) when is_integer(char), do:
|
||||
do_join(rest, relativename, [char|result], os_type)
|
||||
|
||||
defp maybe_remove_dirsep([?/, ?:, letter], :win32), do:
|
||||
|
||||
@@ -76,9 +76,9 @@ defmodule Process do
|
||||
@doc """
|
||||
Sends an exit signal with the given reason to the pid.
|
||||
|
||||
The following behavior apply if reason is any term except `:normal` or `:kill`:
|
||||
The following behavior applies if reason is any term except `:normal` or `:kill`:
|
||||
|
||||
1) If pid is not trapping exits, pid itself will exist with the given reason;
|
||||
1) If pid is not trapping exits, pid will exit with the given reason;
|
||||
|
||||
2) If pid is trapping exits, the exit signal is transformed into a message
|
||||
{'EXIT', from, reason} and delivered to the message queue of pid;
|
||||
@@ -101,6 +101,14 @@ defmodule Process do
|
||||
:erlang.exit(pid, reason)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the pid (process identifier) of the calling process.
|
||||
"""
|
||||
@spec self() :: pid
|
||||
def self() do
|
||||
:erlang.self()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the pid of a new process started by the application of `fun`.
|
||||
It behaves exactly the same as `Kernel.spawn/1`.
|
||||
|
||||
+21
-17
@@ -34,6 +34,8 @@ defmodule Protocol do
|
||||
|
||||
# Set up a clear slate to store defined functions
|
||||
@functions []
|
||||
@only nil
|
||||
@except nil
|
||||
|
||||
# Invoke the user given block
|
||||
unquote(block)
|
||||
@@ -81,14 +83,14 @@ defmodule Protocol do
|
||||
def assert_protocol(module) do
|
||||
case Code.ensure_compiled(module) do
|
||||
{ :module, ^module } -> nil
|
||||
_ -> raise ArgumentError, message: "#{module} is not loaded"
|
||||
_ -> raise ArgumentError, message: "#{inspect module} is not loaded"
|
||||
end
|
||||
|
||||
try do
|
||||
module.__protocol__(:name)
|
||||
rescue
|
||||
UndefinedFunctionError ->
|
||||
raise ArgumentError, message: "#{module} is not a protocol"
|
||||
raise ArgumentError, message: "#{inspect module} is not a protocol"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -236,13 +238,13 @@ defmodule Protocol do
|
||||
|
||||
# Returns a quoted expression that allows to check
|
||||
# if the first item in the tuple is a built-in or not.
|
||||
defp is_builtin?([{h,_,_}]) do
|
||||
defp is_builtin?([{h, _, _}]) do
|
||||
quote do
|
||||
first == unquote(h)
|
||||
end
|
||||
end
|
||||
|
||||
defp is_builtin?([{h,_,_}|t]) do
|
||||
defp is_builtin?([{h, _, _}|t]) do
|
||||
quote do
|
||||
first == unquote(h) or unquote(is_builtin?(t))
|
||||
end
|
||||
@@ -349,36 +351,38 @@ defmodule Protocol.DSL do
|
||||
end
|
||||
|
||||
defp default_clause(name, args) do
|
||||
quote do: { [other], apply(other, unquote(name), [unquote_splicing(args)]) }
|
||||
{ [quote do: other],
|
||||
[],
|
||||
quote(do: apply(other, unquote(name), [unquote_splicing(args)])) }
|
||||
end
|
||||
|
||||
defp nil_clause() do
|
||||
quote do
|
||||
{ [nil], raise(Protocol.UndefinedError, protocol: __MODULE__, structure: xA) }
|
||||
end
|
||||
{ [nil],
|
||||
[],
|
||||
quote(do: raise(Protocol.UndefinedError, protocol: __MODULE__, structure: xA)) }
|
||||
end
|
||||
|
||||
defp record_clause(name, args, nil) do
|
||||
quote do
|
||||
{ [__MODULE__.Record],
|
||||
Module.concat(__MODULE__, :erlang.element(1, xA)).unquote(name)(unquote_splicing(args)) }
|
||||
end
|
||||
{ [quote(do: __MODULE__.Record)],
|
||||
[],
|
||||
quote(do: Module.concat(__MODULE__, :erlang.element(1, xA)).unquote(name)(unquote_splicing(args))) }
|
||||
end
|
||||
|
||||
defp record_clause(name, args, fallback) do
|
||||
arity = length(args)
|
||||
|
||||
quote do
|
||||
{ [__MODULE__.Record],
|
||||
(target = Module.concat(__MODULE__, :erlang.element(1, xA))
|
||||
{ [quote(do: __MODULE__.Record)],
|
||||
[],
|
||||
quote do
|
||||
target = Module.concat(__MODULE__, :erlang.element(1, xA))
|
||||
try do
|
||||
target.unquote(name)(unquote_splicing(args))
|
||||
catch
|
||||
:error, :undef, [[{ ^target, name, args, _ }|_]|_] when
|
||||
name == unquote(name) and length(args) == unquote(arity) ->
|
||||
apply unquote(fallback), name, [unquote_splicing(args)]
|
||||
end) }
|
||||
end
|
||||
end
|
||||
end }
|
||||
end
|
||||
|
||||
defmacro def(expression) do
|
||||
|
||||
+75
-28
@@ -127,21 +127,28 @@ defmodule Record do
|
||||
## Types
|
||||
|
||||
Every record defines a type named `t` that can be accessed in typespecs.
|
||||
For example, assuming the `Config` record defined above, it could be used
|
||||
in typespecs as follow:
|
||||
Those types can be passed at the moment the record is defined:
|
||||
|
||||
@spec handle_config(Config.t) :: boolean()
|
||||
defrecord User,
|
||||
name: "" :: string,
|
||||
age: 0 :: integer
|
||||
|
||||
Inside the record definition, a developer can define his own types too:
|
||||
All the fields without a specified type are assumed to have type `term`.
|
||||
|
||||
Assuming the `User` record defined above, it could be used in typespecs
|
||||
as follow:
|
||||
|
||||
@spec handle_user(User.t) :: boolean()
|
||||
|
||||
If the developer wants to define their own types to be used with the
|
||||
record, Elixir allows a more lengthy definition with the help of the
|
||||
`record_type` macro:
|
||||
|
||||
defrecord Config, counter: 0, failures: [] do
|
||||
@type kind :: term
|
||||
record_type counter: integer, failures: [kind]
|
||||
end
|
||||
|
||||
When defining a type, all the fields not mentioned in the type are
|
||||
assumed to have type `term`.
|
||||
|
||||
## Importing records
|
||||
|
||||
It is also possible to import a public record (a record, defined using
|
||||
@@ -173,35 +180,55 @@ defmodule Record do
|
||||
"""
|
||||
def defrecord(name, values, opts) do
|
||||
block = Keyword.get(opts, :do, nil)
|
||||
{ fields, types } = record_split(values)
|
||||
|
||||
quote do
|
||||
unquoted_values = unquote(values)
|
||||
unquoted_fields = unquote(fields)
|
||||
|
||||
defmodule unquote(name) do
|
||||
@moduledoc false
|
||||
import Elixir.Record.DSL
|
||||
|
||||
@record_fields []
|
||||
@record_types []
|
||||
@record_types unquote(types)
|
||||
|
||||
# Reassign values to inner scope to
|
||||
# Reassign fields to inner scope to
|
||||
# avoid conflicts in nested records
|
||||
values = unquoted_values
|
||||
fields = unquoted_fields
|
||||
|
||||
Elixir.Record.deffunctions(values, __ENV__)
|
||||
Elixir.Record.deffunctions(fields, __ENV__)
|
||||
value = unquote(block)
|
||||
Elixir.Record.deftypes(values, @record_types, __ENV__)
|
||||
Elixir.Record.deftypes(fields, @record_types, __ENV__)
|
||||
value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp record_split(fields) when is_list(fields) do
|
||||
record_split(fields, [], [])
|
||||
end
|
||||
|
||||
defp record_split(other) do
|
||||
{ other, [] }
|
||||
end
|
||||
|
||||
defp record_split([{ field, { :::, _, [default, type] }}|t], defaults, types) do
|
||||
record_split t, [{ field, default }|defaults], [{ field, Macro.escape(type) }|types]
|
||||
end
|
||||
|
||||
defp record_split([other|t], defaults, types) do
|
||||
record_split t, [other|defaults], types
|
||||
end
|
||||
|
||||
defp record_split([], defaults, types) do
|
||||
{ :lists.reverse(defaults), types }
|
||||
end
|
||||
|
||||
@doc """
|
||||
Import public record definition as a set of private macros (as defined by defrecordp/2)
|
||||
|
||||
## Usage
|
||||
|
||||
Record.import Record.Module, as: macro_name
|
||||
Import public record definition as a set of private macros,
|
||||
as defined by `Kernel.defrecordp/2`. This is useful when one
|
||||
desires to manipulate a record via a set of macros instead
|
||||
of the regular access syntax.
|
||||
|
||||
## Example
|
||||
|
||||
@@ -214,7 +241,8 @@ defmodule Record do
|
||||
"""
|
||||
defmacro import(module, as: name) do
|
||||
quote do
|
||||
Record.defmacros(unquote(name), unquote(module).__record__(:fields), __ENV__, unquote(module))
|
||||
Record.defmacros(unquote(name),
|
||||
unquote(module).__record__(:fields), __ENV__, unquote(module))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -224,12 +252,31 @@ defmodule Record do
|
||||
in `values`. This is invoked directly by `Kernel.defrecordp`,
|
||||
so check it for more information and documentation.
|
||||
"""
|
||||
def defrecordp(name, fields) do
|
||||
def defrecordp(name, fields) when is_atom(name) and is_list(fields) do
|
||||
{ fields, types, def_type } = recordp_split(fields, [], [], false)
|
||||
type = :"#{name}_t"
|
||||
|
||||
quote do
|
||||
Record.defmacros(unquote(name), unquote(fields), __ENV__)
|
||||
|
||||
if unquote(def_type) do
|
||||
@typep unquote(type)() :: { unquote(name), unquote_splicing(types) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp recordp_split([{ field, { :::, _, [default, type] }}|t], defaults, types, _) do
|
||||
recordp_split t, [{ field, default }|defaults], [type|types], true
|
||||
end
|
||||
|
||||
defp recordp_split([other|t], defaults, types, def_type) do
|
||||
recordp_split t, [other|defaults], [quote(do: term)|types], def_type
|
||||
end
|
||||
|
||||
defp recordp_split([], defaults, types, def_type) do
|
||||
{ :lists.reverse(defaults), :lists.reverse(types), def_type }
|
||||
end
|
||||
|
||||
@doc """
|
||||
Defines record functions skipping the module definition.
|
||||
This is called directly by `defrecord`. It expects the record
|
||||
@@ -368,7 +415,7 @@ defmodule Record do
|
||||
@doc false
|
||||
def access(atom, fields, keyword, caller) do
|
||||
unless is_keyword(keyword) do
|
||||
raise "expected contents inside brackets to be a Keyword"
|
||||
raise ArgumentError, message: "expected contents inside brackets to be a Keyword"
|
||||
end
|
||||
|
||||
in_match = caller.in_match?
|
||||
@@ -398,7 +445,7 @@ defmodule Record do
|
||||
quote do: { unquote_splicing([atom|match]) }
|
||||
_ ->
|
||||
keys = lc { key, _ } inlist remaining, do: key
|
||||
raise "record #{inspect atom} does not have the keys: #{inspect keys}"
|
||||
raise ArgumentError, message: "record #{inspect atom} does not have the keys: #{inspect keys}"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -418,11 +465,11 @@ defmodule Record do
|
||||
@doc false
|
||||
defp update(atom, fields, var, keyword, caller) do
|
||||
unless is_keyword(keyword) do
|
||||
raise "expected contents inside brackets to be a Keyword"
|
||||
raise ArgumentError, message: "expected contents inside brackets to be a Keyword"
|
||||
end
|
||||
|
||||
if caller.in_match? do
|
||||
raise "cannot invoke update style macro inside match context"
|
||||
raise ArgumentError, message: "cannot invoke update style macro inside match context"
|
||||
end
|
||||
|
||||
Enum.reduce keyword, var, fn({ key, value }, acc) ->
|
||||
@@ -432,7 +479,7 @@ defmodule Record do
|
||||
:erlang.setelement(unquote(index + 2), unquote(acc), unquote(value))
|
||||
end
|
||||
else
|
||||
raise "record #{inspect atom} does not have the key: #{inspect key}"
|
||||
raise ArgumentError, message: "record #{inspect atom} does not have the key: #{inspect key}"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -448,7 +495,7 @@ defmodule Record do
|
||||
:erlang.element(unquote(index + 2), unquote(var))
|
||||
end
|
||||
else
|
||||
raise "record #{inspect atom} does not have the key: #{inspect key}"
|
||||
raise ArgumentError, message: "record #{inspect atom} does not have the key: #{inspect key}"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -477,7 +524,7 @@ defmodule Record do
|
||||
if index do
|
||||
quote do: :erlang.element(unquote(index + 2), unquote(record))
|
||||
else
|
||||
raise "record #{inspect atom} does not have the key: #{inspect key}"
|
||||
raise ArgumentError, message: "record #{inspect atom} does not have the key: #{inspect key}"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -726,7 +773,7 @@ defmodule Record do
|
||||
@spec to_keywords(t) :: options
|
||||
@spec update(options, t) :: t
|
||||
@spec __record__(:name) :: atom
|
||||
@spec __record__(:fields) :: [{atom,any}]
|
||||
@spec __record__(:fields) :: [{atom, any}]
|
||||
@spec __index__(atom) :: non_neg_integer | nil
|
||||
end
|
||||
end
|
||||
|
||||
+1
-13
@@ -79,18 +79,6 @@ defmodule Regex do
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def index(regex(re_pattern: compiled), string) do
|
||||
IO.puts "Regex.index(re, string) is deprecated. Please use " <>
|
||||
"Regex.run(re, string, return: :index) instead."
|
||||
Exception.print_stacktrace
|
||||
|
||||
case :re.run(string, compiled, [{ :capture, :first, :index }]) do
|
||||
:nomatch -> nil
|
||||
{ :match, [{index,_}] } -> index
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a boolean if there was a match or not.
|
||||
|
||||
@@ -326,7 +314,7 @@ defmodule Regex do
|
||||
defp translate_options(<<?x, t :: binary>>), do: [:extended|translate_options(t)]
|
||||
defp translate_options(<<?f, t :: binary>>), do: [:firstline|translate_options(t)]
|
||||
defp translate_options(<<?r, t :: binary>>), do: [:ungreedy|translate_options(t)]
|
||||
defp translate_options(<<?s, t :: binary>>), do: [:dotall,{:newline,:anycrlf}|translate_options(t)]
|
||||
defp translate_options(<<?s, t :: binary>>), do: [:dotall, {:newline, :anycrlf}|translate_options(t)]
|
||||
defp translate_options(<<?m, t :: binary>>), do: [:multiline|translate_options(t)]
|
||||
defp translate_options(<<?g, t :: binary>>), do: [:groups|translate_options(t)]
|
||||
defp translate_options(<<>>), do: []
|
||||
|
||||
@@ -146,7 +146,7 @@ defmodule String do
|
||||
|
||||
iex> String.split("foo bar")
|
||||
["foo", "bar"]
|
||||
iex> String.split("foo" <> <<194,133>> <> "bar")
|
||||
iex> String.split("foo" <> <<194, 133>> <> "bar")
|
||||
["foo", "bar"]
|
||||
iex> String.split(" foo bar ")
|
||||
["foo", "bar"]
|
||||
@@ -383,7 +383,7 @@ defmodule String do
|
||||
"a,[b],c"
|
||||
iex> String.replace("a,b,c", ",", "[]", insert_replaced: 2)
|
||||
"a[],b[],c"
|
||||
iex> String.replace("a,b,c", ",", "[]", insert_replaced: [1,1])
|
||||
iex> String.replace("a,b,c", ",", "[]", insert_replaced: [1, 1])
|
||||
"a[,,]b[,,]c"
|
||||
|
||||
"""
|
||||
@@ -399,7 +399,7 @@ defmodule String do
|
||||
opts = if options[:global] != false, do: [:global], else: []
|
||||
|
||||
if insert = options[:insert_replaced] do
|
||||
opts = [{:insert_replaced,insert}|opts]
|
||||
opts = [{:insert_replaced, insert}|opts]
|
||||
end
|
||||
|
||||
opts
|
||||
@@ -773,8 +773,8 @@ defmodule String do
|
||||
{34.0,""}
|
||||
iex> String.to_float("34.25")
|
||||
{34.25,""}
|
||||
iex> String.to_float("56.5xyz")
|
||||
{56.5,"xyz"}
|
||||
iex> String.to_float("56.5xyz")
|
||||
{56.5,"xyz"}
|
||||
iex> String.to_float("pi")
|
||||
:error
|
||||
|
||||
@@ -824,7 +824,7 @@ defmodule String do
|
||||
end
|
||||
|
||||
defp do_starts_with(string, prefix) when is_binary(prefix) do
|
||||
match?({0,_}, :binary.match(string, prefix))
|
||||
match?({0, _}, :binary.match(string, prefix))
|
||||
end
|
||||
|
||||
defp do_starts_with(_, _) do
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
-behaviour(application).
|
||||
-export([main/1, start_cli/0,
|
||||
scope_for_eval/1, scope_for_eval/2,
|
||||
eval/2, eval/3, eval/4,
|
||||
eval/2, eval/3, eval/4, file_type/1,
|
||||
eval_quoted/2, eval_quoted/3, eval_quoted/4,
|
||||
eval_forms/3, translate_forms/3]).
|
||||
-include("elixir.hrl").
|
||||
-include_lib("kernel/include/file.hrl").
|
||||
|
||||
%% Top level types
|
||||
-export_type([char_list/0, as_boolean/1]).
|
||||
@@ -43,6 +44,14 @@ start_cli() ->
|
||||
application:start(?MODULE),
|
||||
'Elixir.Kernel.CLI':main(init:get_plain_arguments()).
|
||||
|
||||
%% Helpers
|
||||
|
||||
file_type(File) ->
|
||||
case file:read_file_info(File) of
|
||||
{ ok, #file_info{type=Type} } -> { ok, Type };
|
||||
{ error, _ } = Error -> Error
|
||||
end.
|
||||
|
||||
%% EVAL HOOKS
|
||||
|
||||
scope_for_eval(Opts) ->
|
||||
|
||||
@@ -134,6 +134,7 @@ concat(Args) -> list_to_atom(raw_concat(Args)).
|
||||
safe_concat(Args) -> list_to_existing_atom(raw_concat(Args)).
|
||||
|
||||
raw_concat(['Elixir'|Args]) -> do_concat(Args);
|
||||
raw_concat([nil|Args]) -> do_concat(Args);
|
||||
raw_concat(Args) -> do_concat(Args).
|
||||
|
||||
do_concat(Args) ->
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
%% fn, receive and friends. try is handled in elixir_try.
|
||||
-module(elixir_clauses).
|
||||
-export([
|
||||
assigns/3, assigns_block/5, assigns_block/6, extract_last_guards/1,
|
||||
assigns/3, assigns_block/5, assigns_block/6, extract_splat_guards/1,
|
||||
get_pairs/4, get_pairs/5, match/3, extract_args/1, extract_guards/1]).
|
||||
-include("elixir.hrl").
|
||||
|
||||
@@ -14,7 +14,7 @@ get_pairs(Meta, Key, Clauses, S) ->
|
||||
get_pairs(Meta, Key, Clauses, S, AllowNil) ->
|
||||
case lists:keyfind(Key, 1, Clauses) of
|
||||
{ Key, { '->', _, Pairs } } ->
|
||||
[{ Key, Left, Right } || { Left, Right } <- Pairs];
|
||||
[{ Key, PMeta, Left, Right } || { Left, PMeta, Right } <- Pairs];
|
||||
{ Key, nil } when AllowNil ->
|
||||
[];
|
||||
{ Key, _ } ->
|
||||
@@ -25,9 +25,12 @@ get_pairs(Meta, Key, Clauses, S, AllowNil) ->
|
||||
|
||||
% Function for translating assigns.
|
||||
|
||||
assigns(Fun, Args, #elixir_scope{context=Context} = S) when Context /= match ->
|
||||
{ Result, NewS } = assigns(Fun, Args, S#elixir_scope{context=match, temp_vars=orddict:new()}),
|
||||
{ Result, NewS#elixir_scope{context=Context} };
|
||||
assigns(Fun, Args, #elixir_scope{context=Context, temp_vars=TempVars,
|
||||
backup_vars=BackupVars, vars=Vars} = S) when Context /= match ->
|
||||
{ Result, NewS } = assigns(Fun, Args, S#elixir_scope{context=match,
|
||||
temp_vars=ordsets:new(), backup_vars=Vars}),
|
||||
{ Result, NewS#elixir_scope{context=Context,
|
||||
temp_vars=TempVars, backup_vars=BackupVars} };
|
||||
|
||||
assigns(Fun, Args, S) -> Fun(Args, S).
|
||||
|
||||
@@ -77,13 +80,13 @@ extract_args({ Name, _, Args }) when is_atom(Name), is_atom(Args) -> { Name, []
|
||||
extract_args({ Name, _, Args }) when is_atom(Name), is_list(Args) -> { Name, Args };
|
||||
extract_args(_) -> error.
|
||||
|
||||
% Extract guards when it is in the last element of the args
|
||||
% Extract guards when multiple left side args are allowed.
|
||||
|
||||
extract_last_guards([]) -> { [], [] };
|
||||
extract_last_guards(Args) ->
|
||||
{ Left, Right } = elixir_tree_helpers:split_last(Args),
|
||||
{ Bare, Guards } = extract_guards(Right),
|
||||
{ Left ++ [Bare], Guards }.
|
||||
extract_splat_guards([{ 'when', _, [_,_|_] = Args }]) ->
|
||||
{ Left, Right } = elixir_tree_helpers:split_last(Args),
|
||||
{ Left, extract_or_clauses(Right, []) };
|
||||
extract_splat_guards(Else) ->
|
||||
{ Else, [] }.
|
||||
|
||||
% Function for translating macros with match style like case and receive.
|
||||
|
||||
@@ -95,15 +98,15 @@ match(Meta, Clauses, #elixir_scope{clause_vars=C1} = S) ->
|
||||
do_match(_Meta, [], S) ->
|
||||
{ [], S };
|
||||
|
||||
do_match(Meta, [DecoupledClause], S) ->
|
||||
{ TDecoupledClause, TS } = each_clause(Meta, DecoupledClause, S),
|
||||
do_match(_Meta, [DecoupledClause], S) ->
|
||||
{ TDecoupledClause, TS } = each_clause(DecoupledClause, S),
|
||||
{ [TDecoupledClause], TS };
|
||||
|
||||
do_match(Meta, DecoupledClauses, S) ->
|
||||
% Transform tree just passing the variables counter forward
|
||||
% and storing variables defined inside each clause.
|
||||
Transformer = fun(X, {Acc, CV}) ->
|
||||
{ TX, TAcc } = each_clause(Meta, X, Acc),
|
||||
{ TX, TAcc } = each_clause(X, Acc),
|
||||
{ TX, { merge_clauses_scope(S, TAcc), [TAcc#elixir_scope.clause_vars|CV] } }
|
||||
end,
|
||||
|
||||
@@ -163,21 +166,21 @@ expand_clauses(_Line, [], [], _FinalVars, Acc, S) ->
|
||||
|
||||
% Handle each key/value clause pair and translate them accordingly.
|
||||
|
||||
each_clause(Meta, { do, [Condition], Expr }, S) ->
|
||||
each_clause({ do, Meta, [Condition], Expr }, S) ->
|
||||
assigns_block(?line(Meta), fun elixir_translator:translate_each/2, Condition, [Expr], S);
|
||||
|
||||
each_clause(Meta, { else, [Condition], Expr }, S) ->
|
||||
each_clause({ else, Meta, [Condition], Expr }, S) ->
|
||||
assigns_block(?line(Meta), fun elixir_translator:translate_each/2, Condition, [Expr], S);
|
||||
|
||||
each_clause(Meta, { 'after', [Condition], Expr }, S) ->
|
||||
each_clause({ 'after', Meta, [Condition], Expr }, S) ->
|
||||
{ TCondition, SC } = elixir_translator:translate_each(Condition, S),
|
||||
{ TBody, SB } = elixir_translator:translate([Expr], SC),
|
||||
{ { clause, ?line(Meta), [TCondition], [], TBody }, SB };
|
||||
|
||||
each_clause(Meta, { Key, [_|_], _ }, S) when Key == do; Key == 'after' ->
|
||||
each_clause({ Key, Meta, [_|_], _ }, S) when Key == do; Key == 'after' ->
|
||||
elixir_errors:syntax_error(Meta, S#elixir_scope.file, "too many arguments given for ~ts", [Key]);
|
||||
|
||||
each_clause(Meta, { Key, _, _ }, S) ->
|
||||
each_clause({ Key, Meta, _, _ }, S) ->
|
||||
elixir_errors:syntax_error(Meta, S#elixir_scope.file, "invalid key ~ts", [Key]).
|
||||
|
||||
% Check if the given expression is a match tuple.
|
||||
@@ -253,6 +256,5 @@ merge_clauses_scope(S1, S2) ->
|
||||
counter=S2#elixir_scope.counter,
|
||||
extra_guards=S2#elixir_scope.extra_guards,
|
||||
super=S1#elixir_scope.super orelse S2#elixir_scope.super,
|
||||
caller=S1#elixir_scope.caller orelse S2#elixir_scope.caller,
|
||||
name_args=S1#elixir_scope.name_args orelse S2#elixir_scope.name_args
|
||||
caller=S1#elixir_scope.caller orelse S2#elixir_scope.caller
|
||||
}.
|
||||
@@ -5,11 +5,12 @@
|
||||
-behavior(gen_server).
|
||||
-record(elixir_code_server, {
|
||||
argv=[],
|
||||
compilation_status=ok,
|
||||
loaded=[],
|
||||
at_exit=[],
|
||||
pool=[],
|
||||
counter=0,
|
||||
compiler_options=[{docs,true},{debug_info,true}],
|
||||
compiler_options=[{docs,true},{debug_info,true},{warnings_as_errors,false}],
|
||||
waiting=[]
|
||||
}).
|
||||
|
||||
@@ -58,6 +59,9 @@ handle_call(argv, _From, Config) ->
|
||||
handle_call(compiler_options, _From, Config) ->
|
||||
{ reply, Config#elixir_code_server.compiler_options, Config };
|
||||
|
||||
handle_call(compilation_status, _From, Config) ->
|
||||
{ reply, Config#elixir_code_server.compilation_status, Config };
|
||||
|
||||
handle_call(retrieve_module_name, _From, Config) ->
|
||||
case Config#elixir_code_server.pool of
|
||||
[H|T] ->
|
||||
@@ -80,6 +84,15 @@ 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(register_warning, Config) ->
|
||||
case orddict:find(warnings_as_errors, Config#elixir_code_server.compiler_options) of
|
||||
{ ok, true } -> { noreply, Config#elixir_code_server{compilation_status=error} };
|
||||
_ -> { noreply, Config }
|
||||
end;
|
||||
|
||||
handle_cast(reset_warnings, Config) ->
|
||||
{ noreply, Config#elixir_code_server{compilation_status=ok} };
|
||||
|
||||
handle_cast({ loaded, Path }, Config) ->
|
||||
Current = Config#elixir_code_server.loaded,
|
||||
case orddict:find(Path, Current) of
|
||||
|
||||
@@ -208,9 +208,9 @@ core_main() ->
|
||||
[
|
||||
"lib/elixir/lib/kernel.ex",
|
||||
"lib/elixir/lib/keyword.ex",
|
||||
"lib/elixir/lib/module.ex",
|
||||
"lib/elixir/lib/list.ex",
|
||||
"lib/elixir/lib/kernel/typespec.ex",
|
||||
"lib/elixir/lib/module.ex",
|
||||
"lib/elixir/lib/record.ex",
|
||||
"lib/elixir/lib/macro.ex",
|
||||
"lib/elixir/lib/macro/env.ex",
|
||||
|
||||
@@ -100,7 +100,7 @@ store_definition(Kind, Line, CheckClauses, Module, Call, Body, RawS) ->
|
||||
|
||||
{ Name, Args } = case elixir_clauses:extract_args(NameAndArgs) of
|
||||
error ->
|
||||
Format = [Kind, 'Elixir.Macro':to_binary(NameAndArgs)],
|
||||
Format = [Kind, 'Elixir.Macro':to_string(NameAndArgs)],
|
||||
elixir_errors:syntax_error(Line, S#elixir_scope.file, "invalid syntax in ~ts ~ts", Format);
|
||||
Tuple ->
|
||||
Tuple
|
||||
@@ -132,9 +132,10 @@ store_definition(Kind, Line, CheckClauses, Module, Name, Args, Guards, Body, #el
|
||||
CTable = clauses_table(Module),
|
||||
|
||||
compile_super(Module, TS),
|
||||
check_previous_defaults(Table, Line, Name, Arity, Kind, DefaultsLength, S),
|
||||
|
||||
store_each(CheckClauses, Kind, File, Location,
|
||||
Table, CTable, DefaultsLength, Function),
|
||||
|
||||
[store_each(false, Kind, File, Location, Table, CTable, 0,
|
||||
default_function_for(Kind, Name, Default)) || Default <- Defaults],
|
||||
|
||||
@@ -211,14 +212,6 @@ translate_clause(Line, Kind, Unpacked, Guards, Body, S) ->
|
||||
{ TClause, TS } = elixir_clauses:assigns_block(Line,
|
||||
fun elixir_translator:translate/2, Unpacked, [Expr], Guards, S),
|
||||
|
||||
%% Add names to args
|
||||
NClause = case TS#elixir_scope.name_args of
|
||||
true ->
|
||||
NArgs = elixir_def_overridable:assign_args(Line, element(3, TClause), TS),
|
||||
setelement(3, TClause, NArgs);
|
||||
false -> TClause
|
||||
end,
|
||||
|
||||
%% Set __CALLER__ if used
|
||||
FClause = case is_macro(Kind) andalso TS#elixir_scope.caller of
|
||||
true ->
|
||||
@@ -226,8 +219,8 @@ translate_clause(Line, Kind, Unpacked, Guards, Body, S) ->
|
||||
{ 'var', Line, '__CALLER__' },
|
||||
?wrap_call(Line, elixir_scope, to_ex_env, [{ var, Line, '_@CALLER' }])
|
||||
},
|
||||
setelement(5, NClause, [FBody|element(5, NClause)]);
|
||||
false -> NClause
|
||||
setelement(5, TClause, [FBody|element(5, TClause)]);
|
||||
false -> TClause
|
||||
end,
|
||||
|
||||
{ [FClause], TS }.
|
||||
@@ -317,8 +310,8 @@ store_each(Check, Kind, File, Location, Table, CTable, Defaults, {function, Line
|
||||
FinalLocation = StoredLocation,
|
||||
FinalDefaults = max(Defaults, StoredDefaults),
|
||||
check_valid_kind(Line, File, Name, Arity, Kind, StoredKind),
|
||||
check_valid_defaults(Line, File, Name, Arity, Kind, Defaults, StoredDefaults),
|
||||
(Check and StoredCheck) andalso check_valid_clause(Line, File, Name, Arity, Kind, Table);
|
||||
(Check and StoredCheck) andalso check_valid_clause(Line, File, Name, Arity, Kind, Table),
|
||||
check_valid_defaults(Line, File, Name, Arity, Kind, Defaults, StoredDefaults);
|
||||
[] ->
|
||||
FinalLine = Line,
|
||||
FinalLocation = Location,
|
||||
@@ -348,7 +341,18 @@ check_valid_defaults(_Line, _File, _Name, _Arity, _Kind, 0, _) -> [];
|
||||
check_valid_defaults(Line, File, Name, Arity, Kind, _, 0) ->
|
||||
elixir_errors:handle_file_warning(File, { Line, ?MODULE, { out_of_order_defaults, { Kind, Name, Arity } } });
|
||||
check_valid_defaults(Line, File, Name, Arity, Kind, _, _) ->
|
||||
elixir_errors:handle_file_warning(File, { Line, ?MODULE, { clauses_with_defaults, { Kind, Name, Arity } } }).
|
||||
elixir_errors:form_error(Line, File, ?MODULE, { clauses_with_defaults, { Kind, Name, Arity } }).
|
||||
|
||||
check_previous_defaults(Table, Line, Name, Arity, Kind, Defaults, S) ->
|
||||
Matches = ets:match(Table, { { Name, '$2' }, '$1', '_', '_', '_', '_', '$3' }),
|
||||
[ begin
|
||||
elixir_errors:form_error(Line, S#elixir_scope.file, ?MODULE,
|
||||
{ defs_with_defaults, Name, { Kind, Arity }, { K, A } })
|
||||
end || [K, A, D] <- Matches, A /= Arity, D /= 0, defaults_conflict(A, D, Arity, Defaults)].
|
||||
|
||||
defaults_conflict(A, D, Arity, Defaults) ->
|
||||
((Arity >= (A - D)) andalso (Arity < A)) orelse
|
||||
((A >= (Arity - Defaults)) andalso (A < Arity)).
|
||||
|
||||
assert_no_aliases_name(Line, '__aliases__', [Atom], #elixir_scope{file=File}) when is_atom(Atom) ->
|
||||
Message = "function names should start with lowercase characters or underscore, invalid name ~ts",
|
||||
@@ -362,8 +366,17 @@ assert_no_aliases_name(_Meta, _Aliases, _Args, _S) ->
|
||||
format_error({no_module,{Kind,Name,Arity}}) ->
|
||||
io_lib:format("cannot define function outside module, invalid scope for ~ts ~ts/~B", [Kind, Name, Arity]);
|
||||
|
||||
format_error({defs_with_defaults, Name, { Kind, Arity }, { K, A } }) when Arity > A ->
|
||||
io_lib:format("~ts ~ts/~B defaults conflicts with ~ts ~ts/~B",
|
||||
[Kind, Name, Arity, K, Name, A]);
|
||||
|
||||
format_error({defs_with_defaults, Name, { Kind, Arity }, { K, A } }) when Arity < A ->
|
||||
io_lib:format("~ts ~ts/~B conflicts with defaults from ~ts ~ts/~B",
|
||||
[Kind, Name, Arity, K, Name, A]);
|
||||
|
||||
format_error({clauses_with_defaults,{Kind,Name,Arity}}) ->
|
||||
io_lib:format("~ts ~ts/~B has default values and multiple clauses, use a separate clause for declaring defaults", [Kind, Name, Arity]);
|
||||
io_lib:format("~ts ~ts/~B has default values and multiple clauses, "
|
||||
"use a separate clause for declaring defaults", [Kind, Name, Arity]);
|
||||
|
||||
format_error({out_of_order_defaults,{Kind,Name,Arity}}) ->
|
||||
io_lib:format("clause with defaults should be the first clause in ~ts ~ts/~B", [Kind, Name, Arity]);
|
||||
@@ -372,4 +385,4 @@ format_error({override_function,{Kind,Name,Arity}}) ->
|
||||
io_lib:format("trying to override previously defined ~ts ~ts/~B", [Kind, Name, Arity]);
|
||||
|
||||
format_error({changed_kind,{Name,Arity,Previous,Current}}) ->
|
||||
io_lib:format("~ts ~ts/~B already defined as ~ts", [Current, Name, Arity, Previous]).
|
||||
io_lib:format("~ts ~ts/~B already defined as ~ts", [Current, Name, Arity, Previous]).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
% Holds the logic responsible for defining overridable functions and handling super.
|
||||
-module(elixir_def_overridable).
|
||||
-export([store_pending/1, is_defined/2, ensure_defined/4,
|
||||
assign_args/3, retrieve_args/3, name/2, store/3, format_error/1]).
|
||||
assign_args/3, name/2, store/3, format_error/1]).
|
||||
-include("elixir.hrl").
|
||||
|
||||
overridable(Module) ->
|
||||
@@ -25,15 +25,6 @@ ensure_defined(Meta, Module, Tuple, S) ->
|
||||
_ -> elixir_errors:form_error(Meta, S#elixir_scope.file, ?MODULE, { no_super, Module, Tuple })
|
||||
end.
|
||||
|
||||
%% Retrieve args defined for the given arity.
|
||||
|
||||
retrieve_args(Meta, Arity, S) ->
|
||||
Line = ?line(Meta),
|
||||
{
|
||||
[ { var, Line, super_arg(X) } || X <- lists:seq(1, Arity) ],
|
||||
S#elixir_scope{name_args=true}
|
||||
}.
|
||||
|
||||
%% Assign pseudo variables to the given vars.
|
||||
|
||||
assign_args(Line, Args, S) when is_integer(Line) ->
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
-define(builtin, 'Elixir.Kernel').
|
||||
|
||||
default_functions() ->
|
||||
[ { ?builtin, ordsets:union(in_elixir_functions(), in_erlang_functions()) } ].
|
||||
[ { ?builtin, elixir_imported_functions() } ].
|
||||
default_macros() ->
|
||||
[ { ?builtin, ordsets:union(in_elixir_macros(), in_erlang_macros()) } ].
|
||||
[ { ?builtin, elixir_imported_macros() } ].
|
||||
default_requires() ->
|
||||
[ ?builtin, 'Elixir.Kernel.Typespec', 'Elixir.Record' ].
|
||||
|
||||
@@ -23,9 +23,14 @@ find_import(Meta, Name, Arity, S) ->
|
||||
Tuple = { Name, Arity },
|
||||
|
||||
case find_dispatch(Meta, Tuple, S) of
|
||||
{ function, Receiver } -> Receiver;
|
||||
{ macro, Receiver } -> Receiver;
|
||||
_ -> false
|
||||
{ function, Receiver } ->
|
||||
elixir_tracker:record_import(Tuple, Receiver, S#elixir_scope.module, S#elixir_scope.function),
|
||||
Receiver;
|
||||
{ macro, Receiver } ->
|
||||
elixir_tracker:record_import(Tuple, Receiver, S#elixir_scope.module, S#elixir_scope.function),
|
||||
Receiver;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
%% Function retrieval
|
||||
@@ -312,13 +317,18 @@ get_optional_macros(Receiver) ->
|
||||
{ error, _ } -> []
|
||||
end.
|
||||
|
||||
%% Functions imported from Kernel module. Sorted on compilation.
|
||||
|
||||
in_elixir_functions() ->
|
||||
elixir_imported_functions() ->
|
||||
try
|
||||
?builtin:'__info__'(functions)
|
||||
catch
|
||||
error:undef -> []
|
||||
error:undef -> in_erlang_functions()
|
||||
end.
|
||||
|
||||
elixir_imported_macros() ->
|
||||
try
|
||||
?builtin:'__info__'(macros)
|
||||
catch
|
||||
error:undef -> in_erlang_macros()
|
||||
end.
|
||||
|
||||
%% Macros imported from Kernel module. Sorted on compilation.
|
||||
@@ -437,6 +447,9 @@ in_erlang_macros() ->
|
||||
{'and',2},
|
||||
{apply,2},
|
||||
{apply,3},
|
||||
{binding,0},
|
||||
{binding,1},
|
||||
{binding,2},
|
||||
{'case',2},
|
||||
{def,1},
|
||||
{def,2},
|
||||
|
||||
@@ -75,6 +75,11 @@ deprecation(Meta, File, Message, Args) ->
|
||||
|
||||
%% Handle warnings and errors (called during module compilation)
|
||||
|
||||
%% output warning based on warnings going to stdout or stderr.
|
||||
output_warning(Warning) ->
|
||||
elixir_code_server:cast(register_warning),
|
||||
io:format(standard_error, Warning, []).
|
||||
|
||||
%% Ignore on bootstrap
|
||||
handle_file_warning(true, _File, { _Line, sys_core_fold, nomatch_guard }) -> [];
|
||||
handle_file_warning(true, _File, { _Line, sys_core_fold, { nomatch_shadow, _ } }) -> [];
|
||||
@@ -93,14 +98,14 @@ handle_file_warning(_, File, {Line,erl_lint,{undefined_behaviour_func,{Fun,Arity
|
||||
Kind = protocol_or_behaviour(Module),
|
||||
Raw = "undefined ~ts function ~ts/~B (for ~ts ~ts)",
|
||||
Message = io_lib:format(Raw, [Kind, Fun, Arity, Kind, inspect(Module)]),
|
||||
io:format(file_format(Line, File, Message));
|
||||
output_warning(file_format(Line, File, Message));
|
||||
|
||||
handle_file_warning(_, File, {Line,erl_lint,{undefined_behaviour,Module}}) ->
|
||||
case elixir_compiler:get_opt(internal) of
|
||||
true -> [];
|
||||
false ->
|
||||
Message = io_lib:format("behaviour ~ts undefined", [inspect(Module)]),
|
||||
io:format(file_format(Line, File, Message))
|
||||
output_warning(file_format(Line, File, Message))
|
||||
end;
|
||||
|
||||
handle_file_warning(_, _File, {Line,erl_lint,{unused_var,_Var}}) when Line =< 0 ->
|
||||
@@ -108,16 +113,16 @@ 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));
|
||||
output_warning(file_format(Line, File, Message));
|
||||
|
||||
handle_file_warning(_, File, {Line,erl_lint,{shadowed_var,Var,Where}}) ->
|
||||
Message = format_error(erl_lint, { shadowed_var, format_var(Var), Where }),
|
||||
io:format(file_format(Line, File, Message));
|
||||
output_warning(file_format(Line, File, Message));
|
||||
|
||||
%% Default behavior
|
||||
handle_file_warning(_, File, {Line,Module,Desc}) ->
|
||||
Message = format_error(Module, Desc),
|
||||
io:format(file_format(Line, File, Message)).
|
||||
output_warning(file_format(Line, File, Message)).
|
||||
|
||||
handle_file_warning(File, Desc) ->
|
||||
handle_file_warning(false, File, Desc).
|
||||
|
||||
@@ -196,18 +196,15 @@ intersection([], _All) -> [].
|
||||
|
||||
%% Internal funs that are never imported etc.
|
||||
|
||||
remove_underscored(default, List) -> remove_underscored(List);
|
||||
remove_underscored(_, List) -> List.
|
||||
remove_underscored(default, List) ->
|
||||
lists:filter(fun({ Name, _ }) ->
|
||||
case atom_to_list(Name) of
|
||||
"_" ++ _ -> false;
|
||||
_ -> true
|
||||
end
|
||||
end, List);
|
||||
|
||||
remove_underscored([{ Name, _ } = H|T]) when Name < a ->
|
||||
case atom_to_list(Name) of
|
||||
[$_, $_, _, $_, $_] -> [H|remove_underscored(T)];
|
||||
"_" ++ _ -> remove_underscored(T);
|
||||
_ -> [H|remove_underscored(T)]
|
||||
end;
|
||||
|
||||
remove_underscored(T) ->
|
||||
T.
|
||||
remove_underscored(_, List) -> List.
|
||||
|
||||
remove_internals(Set) ->
|
||||
ordsets:del_element({ module_info, 1 },
|
||||
@@ -224,7 +221,7 @@ special_form() ->
|
||||
{'__ambiguousop__','*'},
|
||||
{'__scope__',2},
|
||||
{'__block__','*'},
|
||||
{'->','2'},
|
||||
{'->','*'},
|
||||
{'<<>>','*'},
|
||||
{'{}','*'},
|
||||
{'[]','*'},
|
||||
|
||||
@@ -156,8 +156,8 @@ extract_bit_values(Meta, [Size|T], default, Types, S) when is_integer(Size) anda
|
||||
extract_bit_values(Meta, T, {integer, Meta, Size}, Types, S);
|
||||
|
||||
extract_bit_values(Meta, [{ '|', _, [Left, Right] }], Size, Types, S) ->
|
||||
Expanded = 'Elixir.Macro':expand(Right, elixir_scope:to_ex_env({ ?line(Meta), S })),
|
||||
extract_bit_values(Meta, [Left|join_expansion(Expanded,[])], Size, Types, S);
|
||||
{ Expanded, ES } = 'Elixir.Macro':expand_all(Right, elixir_scope:to_ex_env({ ?line(Meta), S }), S),
|
||||
extract_bit_values(Meta, [Left|join_expansion(Expanded,[])], Size, Types, ES);
|
||||
|
||||
extract_bit_values(Meta, [_|_] = All, Size, Types, S) ->
|
||||
handle_unknown_specifier(Meta, All, Size, Types, S);
|
||||
@@ -198,11 +198,11 @@ extract_bit_type_or_size(_Value, _Args, _Other, _Types) ->
|
||||
{ error, unknown }.
|
||||
|
||||
handle_unknown_specifier(Meta, [H|T], Size, Types, S) ->
|
||||
case 'Elixir.Macro':expand(H, elixir_scope:to_ex_env({ ?line(Meta), S })) of
|
||||
H ->
|
||||
elixir_errors:syntax_error(Meta, S#elixir_scope.file, "unknown bitstring specifier ~ts", ['Elixir.Macro':to_binary(H)]);
|
||||
E ->
|
||||
extract_bit_values(Meta, join_expansion(E,T), Size, Types, S)
|
||||
case 'Elixir.Macro':expand_all(H, elixir_scope:to_ex_env({ ?line(Meta), S }), S) of
|
||||
{ H, ES } ->
|
||||
elixir_errors:syntax_error(Meta, ES#elixir_scope.file, "unknown bitstring specifier ~ts", ['Elixir.Macro':to_string(H)]);
|
||||
{ E, ES } ->
|
||||
extract_bit_values(Meta, join_expansion(E,T), Size, Types, ES)
|
||||
end.
|
||||
|
||||
join_expansion({ '__block__', _, [Expanded] }, Tail) -> join_expansion(Expanded, Tail);
|
||||
|
||||
@@ -119,7 +119,7 @@ translate({'@', Meta, [{ Name, _, Args }]}, S) ->
|
||||
translate_each({
|
||||
{ '.', Meta, ['Elixir.Module', get_attribute] },
|
||||
Meta,
|
||||
[ { '__MODULE__', Meta, false }, Name ]
|
||||
[ { '__MODULE__', Meta, false }, Name, true ]
|
||||
}, S);
|
||||
_ ->
|
||||
Contents = 'Elixir.Module':get_attribute(S#elixir_scope.module, Name),
|
||||
@@ -130,6 +130,55 @@ translate({'@', Meta, [{ Name, _, Args }]}, S) ->
|
||||
end
|
||||
end;
|
||||
|
||||
%% Binding
|
||||
|
||||
translate({ 'binding', Meta, [] }, S) ->
|
||||
translate({ 'binding', Meta, [false] }, S);
|
||||
|
||||
translate({ 'binding', Meta, [false] }, S) ->
|
||||
Line = ?line(Meta),
|
||||
{ elixir_tree_helpers:list_to_cons(Line,
|
||||
[ to_var_value_tuple(Line, Name, Var) || { { Name, nil }, Var } <- S#elixir_scope.vars]), S };
|
||||
|
||||
translate({ 'binding', Meta, [true] }, S) ->
|
||||
Line = ?line(Meta),
|
||||
{ elixir_tree_helpers:list_to_cons(Line,
|
||||
[ to_var_value_tuple(Line, Name, Kind, Var) || { { Name, Kind }, Var } <- S#elixir_scope.vars]), S };
|
||||
|
||||
translate({ 'binding', Meta, [List] }, #elixir_scope{vars=Vars} = S) when is_list(List) ->
|
||||
Line = ?line(Meta),
|
||||
Dict = lists:foldl(fun
|
||||
(Name, Acc) when is_atom(Name) ->
|
||||
case orddict:find({ Name, nil }, Vars) of
|
||||
{ ok, Var } ->
|
||||
orddict:store(Name, Var, Acc);
|
||||
error ->
|
||||
Acc
|
||||
end;
|
||||
(Name, _Acc) ->
|
||||
elixir_errors:syntax_error(Line, S#elixir_scope.file, "binding/1 expects a list of atoms "
|
||||
"at compilation time, got: ~ts", ['Elixir.Macro':to_string(Name)])
|
||||
end, [], List),
|
||||
{ elixir_tree_helpers:list_to_cons(Line,
|
||||
[ to_var_value_tuple(Line, Name, Var) || { Name, Var } <- Dict]), S };
|
||||
|
||||
translate({ 'binding', Meta, [List, true] }, #elixir_scope{vars=Vars} = S) when is_list(List) ->
|
||||
Line = ?line(Meta),
|
||||
Dict = lists:foldl(fun
|
||||
(Tuple, Acc) when is_tuple(Tuple) ->
|
||||
case orddict:find(Tuple, Vars) of
|
||||
{ ok, Var } ->
|
||||
orddict:store(Tuple, Var, Acc);
|
||||
error ->
|
||||
Acc
|
||||
end;
|
||||
(Tuple, _Acc) ->
|
||||
elixir_errors:syntax_error(Line, S#elixir_scope.file, "binding/2 expects a list of tuples "
|
||||
"at compilation time, got: ~ts", ['Elixir.Macro':to_string(Tuple)])
|
||||
end, [], List),
|
||||
{ elixir_tree_helpers:list_to_cons(Line,
|
||||
[ to_var_value_tuple(Line, Name, Kind, Var) || { { Name, Kind }, Var } <- Dict]), S };
|
||||
|
||||
%% Case
|
||||
|
||||
translate({'case', Meta, [Expr, KV]}, S) ->
|
||||
@@ -267,6 +316,9 @@ translate_in(Meta, Left, Right, S) ->
|
||||
end,
|
||||
|
||||
{ InGuard, TExpr } = case TRight of
|
||||
{ nil, _ } ->
|
||||
Expr = { atom, Line, false },
|
||||
{ Cache, Expr };
|
||||
{ cons, _, _, _ } ->
|
||||
[H|T] = elixir_tree_helpers:cons_to_list(TRight),
|
||||
Expr = lists:foldr(fun(X, Acc) ->
|
||||
@@ -318,15 +370,15 @@ decreasing_compare(Line, Var, Start, End) ->
|
||||
{ op, Line, '=<', Var, Start },
|
||||
{ op, Line, '>=', Var, End } }.
|
||||
|
||||
rewrite_case_clauses([{do,[{in,_,[{'_',_,_},[false,nil]]}],False},{do,[{'_',_,_}],True}]) ->
|
||||
[{do,[false],False},{do,[true],True}];
|
||||
rewrite_case_clauses([{do,Meta1,[{in,_,[{'_',_,_},[false,nil]]}],False},{do,Meta2,[{'_',_,_}],True}]) ->
|
||||
[{do,Meta1,[false],False},{do,Meta2,[true],True}];
|
||||
|
||||
rewrite_case_clauses(Clauses) ->
|
||||
Clauses.
|
||||
|
||||
%% defmodule :foo
|
||||
expand_module(Raw, Module, #elixir_scope{module=Nesting}) when is_atom(Raw); Nesting == nil ->
|
||||
Module;
|
||||
expand_module(Raw, _Module, _S) when is_atom(Raw) ->
|
||||
Raw;
|
||||
|
||||
%% defmodule Hello
|
||||
expand_module({ '__aliases__', _, [H] }, _Module, S) ->
|
||||
@@ -349,6 +401,19 @@ is_reserved_data(moduledoc) -> true;
|
||||
is_reserved_data(doc) -> true;
|
||||
is_reserved_data(_) -> false.
|
||||
|
||||
to_var_value_tuple(Line, Name, Var) ->
|
||||
{ tuple, Line,
|
||||
[ { atom, Line, Name },
|
||||
{ var, Line, Var } ] }.
|
||||
|
||||
to_var_value_tuple(Line, Name, Kind, Var) ->
|
||||
{ tuple, Line,
|
||||
[ { tuple, Line, [
|
||||
{ atom, Line, Name },
|
||||
{ atom, Line, Kind }
|
||||
] },
|
||||
{ var, Line, Var } ] }.
|
||||
|
||||
spec_to_macro(type) -> deftype;
|
||||
spec_to_macro(typep) -> deftypep;
|
||||
spec_to_macro(opaque) -> defopaque;
|
||||
|
||||
+161
-200
@@ -2,9 +2,10 @@ Nonterminals
|
||||
grammar expr_list
|
||||
expr paren_expr block_expr fn_expr bracket_expr call_expr bracket_at_expr max_expr
|
||||
base_expr matched_expr matched_op_expr unmatched_expr op_expr
|
||||
add_op mult_op unary_op two_op regex_op right_op bin_concat_op
|
||||
match_op send_op default_op when_op pipe_op in_op inc_op range_op
|
||||
andand_op oror_op and_op or_op comp_expr_op colon_colon_op three_op at_op
|
||||
comp_op_eol at_op_eol unary_op_eol and_op_eol or_op_eol tail_op_eol
|
||||
add_op_eol mult_op_eol exp_op_eol two_op_eol type_op_eol stab_op_eol
|
||||
arrow_op_eol range_op_eol than_op_eol default_op_eol match_op_eol
|
||||
when_op_eol in_op_eol inc_op_eol
|
||||
open_paren close_paren empty_paren
|
||||
open_bracket close_bracket
|
||||
open_curly close_curly
|
||||
@@ -12,7 +13,7 @@ Nonterminals
|
||||
base_comma_expr comma_expr optional_comma_expr matched_comma_expr
|
||||
call_args call_args_parens parens_call
|
||||
call_args_no_parens call_args_no_parens_strict call_args_parens_not_one
|
||||
stab stab_eol stab_op stab_expr stab_maybe_expr
|
||||
stab stab_eol stab_expr stab_maybe_expr
|
||||
kw_eol kw_expr kw_comma kw_base
|
||||
matched_kw_expr matched_kw_comma matched_kw_base
|
||||
dot_op dot_ref dot_identifier dot_op_identifier dot_do_identifier
|
||||
@@ -26,46 +27,40 @@ Terminals
|
||||
bracket_identifier paren_identifier do_identifier block_identifier
|
||||
fn 'end' aliases
|
||||
number signed_number atom bin_string list_string sigil
|
||||
dot_call_op comp_op op_identifier
|
||||
'not' 'and' 'or' 'xor' 'when' 'in' 'inlist' 'inbits' 'do'
|
||||
'true' 'false' 'nil'
|
||||
'=' '+' '-' '*' '/' '++' '--' '**' '//'
|
||||
'(' ')' '[' ']' '{' '}' '<<' '>>' '::'
|
||||
eol ',' '&' '|' '.' '^' '@' '<-' '<>' '->' '|>' '=~'
|
||||
'&&' '||' '!' '...' '..'
|
||||
'<<<' '>>>' '&&&' '|||' '^^^' '~~~'
|
||||
dot_call_op op_identifier
|
||||
comp_op at_op unary_op and_op or_op arrow_op match_op
|
||||
range_op in_op inc_op when_op than_op default_op tail_op
|
||||
dual_op add_op mult_op exp_op two_op type_op stab_op
|
||||
'true' 'false' 'nil' 'do' eol ',' '.' '&' '...'
|
||||
'(' ')' '[' ']' '{' '}' '<<' '>>'
|
||||
.
|
||||
|
||||
Rootsymbol grammar.
|
||||
|
||||
Left 5 do.
|
||||
Right 10 stab_op.
|
||||
Left 20 ','. % Solve nested call_args conflicts
|
||||
Right 30 colon_colon_op.
|
||||
Right 40 when_op.
|
||||
Right 50 default_op.
|
||||
Left 60 pipe_op.
|
||||
Left 70 inc_op.
|
||||
Right 80 match_op.
|
||||
Right 110 send_op.
|
||||
Left 120 oror_op.
|
||||
Left 130 andand_op.
|
||||
Left 140 or_op.
|
||||
Left 150 and_op.
|
||||
Left 160 comp_expr_op.
|
||||
Left 170 in_op.
|
||||
Right 180 regex_op.
|
||||
Right 190 right_op.
|
||||
Left 200 range_op.
|
||||
Left 210 three_op.
|
||||
Left 220 add_op.
|
||||
Left 230 mult_op.
|
||||
Right 240 bin_concat_op.
|
||||
Right 250 two_op.
|
||||
Nonassoc 300 unary_op.
|
||||
Right 10 stab_op_eol. %% ->
|
||||
Left 20 ','.
|
||||
Right 30 type_op_eol. %% ::
|
||||
Right 40 when_op_eol. %% when
|
||||
Left 50 inc_op_eol. %% inlist, inbits
|
||||
Right 60 default_op_eol. %% //
|
||||
Left 70 tail_op_eol. %% |
|
||||
Right 80 match_op_eol. %% =
|
||||
Left 130 or_op_eol. %% ||, |||, or, xor
|
||||
Left 140 and_op_eol. %% &&, &&&, and
|
||||
Left 150 comp_op_eol. %% <, >, <=, >=, ==, !=, =~, ===, !==
|
||||
Right 160 arrow_op_eol. %% < (op), (op) > (e.g <-, |>, <<<, >>>)
|
||||
Left 170 in_op_eol. %% in
|
||||
Left 200 range_op_eol. %% ..
|
||||
Left 210 add_op_eol. %% + (op), - (op)
|
||||
Left 220 mult_op_eol. %% * (op), / (op)
|
||||
Right 230 than_op_eol. %% < (op) > (e.g <>)
|
||||
Right 240 two_op_eol. %% ++, --, **
|
||||
Left 250 exp_op_eol. %% ^ (op) (e.g ^^^)
|
||||
Nonassoc 300 unary_op_eol. %% +, -, !, ^, not, ~~~
|
||||
Left 310 dot_call_op.
|
||||
Left 310 dot_op.
|
||||
Nonassoc 320 at_op.
|
||||
Left 310 dot_op. %% .
|
||||
Nonassoc 320 at_op_eol. %% @ (op)
|
||||
Nonassoc 330 var.
|
||||
|
||||
%%% MAIN FLOW OF EXPRESSIONS
|
||||
@@ -89,60 +84,52 @@ expr -> matched_expr : '$1'.
|
||||
expr -> unmatched_expr : '$1'.
|
||||
|
||||
matched_expr -> matched_expr matched_op_expr : build_op(element(1, '$2'), '$1', element(2, '$2')).
|
||||
matched_expr -> unary_op matched_expr : build_unary_op('$1', '$2').
|
||||
matched_expr -> at_op matched_expr : build_unary_op('$1', '$2').
|
||||
matched_expr -> unary_op_eol matched_expr : build_unary_op('$1', '$2').
|
||||
matched_expr -> at_op_eol matched_expr : build_unary_op('$1', '$2').
|
||||
matched_expr -> bracket_at_expr : '$1'.
|
||||
matched_expr -> fn_expr : '$1'.
|
||||
|
||||
unmatched_expr -> matched_expr op_expr : build_op(element(1, '$2'), '$1', element(2, '$2')).
|
||||
unmatched_expr -> unmatched_expr op_expr : build_op(element(1, '$2'), '$1', element(2, '$2')).
|
||||
unmatched_expr -> unary_op expr : build_unary_op('$1', '$2').
|
||||
unmatched_expr -> at_op expr : build_unary_op('$1', '$2').
|
||||
unmatched_expr -> unary_op_eol expr : build_unary_op('$1', '$2').
|
||||
unmatched_expr -> at_op_eol expr : build_unary_op('$1', '$2').
|
||||
unmatched_expr -> block_expr : '$1'.
|
||||
|
||||
op_expr -> match_op expr : { '$1', '$2' }.
|
||||
op_expr -> add_op expr : { '$1', '$2' }.
|
||||
op_expr -> mult_op expr : { '$1', '$2' }.
|
||||
op_expr -> two_op expr : { '$1', '$2' }.
|
||||
op_expr -> regex_op expr : { '$1', '$2' }.
|
||||
op_expr -> right_op expr : { '$1', '$2' }.
|
||||
op_expr -> andand_op expr : { '$1', '$2' }.
|
||||
op_expr -> three_op expr : { '$1', '$2' }.
|
||||
op_expr -> oror_op expr : { '$1', '$2' }.
|
||||
op_expr -> and_op expr : { '$1', '$2' }.
|
||||
op_expr -> or_op expr : { '$1', '$2' }.
|
||||
op_expr -> pipe_op expr : { '$1', '$2' }.
|
||||
op_expr -> bin_concat_op expr : { '$1', '$2' }.
|
||||
op_expr -> in_op expr : { '$1', '$2' }.
|
||||
op_expr -> inc_op expr : { '$1', '$2' }.
|
||||
op_expr -> when_op expr : { '$1', '$2' }.
|
||||
op_expr -> send_op expr : { '$1', '$2' }.
|
||||
op_expr -> range_op expr : { '$1', '$2' }.
|
||||
op_expr -> default_op expr : { '$1', '$2' }.
|
||||
op_expr -> colon_colon_op expr : { '$1', '$2' }.
|
||||
op_expr -> comp_expr_op expr : { '$1', '$2' }.
|
||||
op_expr -> match_op_eol expr : { '$1', '$2' }.
|
||||
op_expr -> add_op_eol expr : { '$1', '$2' }.
|
||||
op_expr -> mult_op_eol expr : { '$1', '$2' }.
|
||||
op_expr -> exp_op_eol expr : { '$1', '$2' }.
|
||||
op_expr -> two_op_eol expr : { '$1', '$2' }.
|
||||
op_expr -> and_op_eol expr : { '$1', '$2' }.
|
||||
op_expr -> or_op_eol expr : { '$1', '$2' }.
|
||||
op_expr -> tail_op_eol expr : { '$1', '$2' }.
|
||||
op_expr -> than_op_eol expr : { '$1', '$2' }.
|
||||
op_expr -> in_op_eol expr : { '$1', '$2' }.
|
||||
op_expr -> inc_op_eol expr : { '$1', '$2' }.
|
||||
op_expr -> when_op_eol expr : { '$1', '$2' }.
|
||||
op_expr -> range_op_eol expr : { '$1', '$2' }.
|
||||
op_expr -> default_op_eol expr : { '$1', '$2' }.
|
||||
op_expr -> type_op_eol expr : { '$1', '$2' }.
|
||||
op_expr -> comp_op_eol expr : { '$1', '$2' }.
|
||||
op_expr -> arrow_op_eol expr : { '$1', '$2' }.
|
||||
|
||||
matched_op_expr -> match_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> add_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> mult_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> two_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> regex_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> right_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> andand_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> three_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> oror_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> and_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> or_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> pipe_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> bin_concat_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> in_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> inc_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> when_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> send_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> range_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> default_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> colon_colon_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> comp_expr_op matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> match_op_eol matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> add_op_eol matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> mult_op_eol matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> exp_op_eol matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> two_op_eol matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> and_op_eol matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> or_op_eol matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> tail_op_eol matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> than_op_eol matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> in_op_eol matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> inc_op_eol matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> when_op_eol matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> range_op_eol matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> default_op_eol matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> type_op_eol matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> comp_op_eol matched_expr : { '$1', '$2' }.
|
||||
matched_op_expr -> arrow_op_eol matched_expr : { '$1', '$2' }.
|
||||
|
||||
block_expr -> parens_call call_args_parens do_block : build_identifier('$1', '$2' ++ '$3').
|
||||
block_expr -> parens_call call_args_parens call_args_parens do_block : build_nested_parens('$1', '$2', '$3' ++ '$4').
|
||||
@@ -171,24 +158,27 @@ max_expr -> open_paren stab close_paren : build_stab(lists:reverse('$2')).
|
||||
bracket_expr -> dot_bracket_identifier bracket_access : build_access(build_identifier('$1', nil), '$2').
|
||||
bracket_expr -> max_expr bracket_access : build_access('$1', '$2').
|
||||
|
||||
bracket_at_expr -> at_op dot_bracket_identifier bracket_access : build_access(build_unary_op('$1', build_identifier('$2', nil)), '$3').
|
||||
bracket_at_expr -> at_op max_expr bracket_access : build_access(build_unary_op('$1', '$2'), '$3').
|
||||
bracket_at_expr -> bracket_at_expr bracket_access : build_access('$1', '$2').
|
||||
bracket_at_expr -> at_op_eol dot_bracket_identifier bracket_access :
|
||||
build_access(build_unary_op('$1', build_identifier('$2', nil)), '$3').
|
||||
bracket_at_expr -> at_op_eol max_expr bracket_access :
|
||||
build_access(build_unary_op('$1', '$2'), '$3').
|
||||
bracket_at_expr -> bracket_at_expr bracket_access :
|
||||
build_access('$1', '$2').
|
||||
|
||||
base_expr -> number : ?exprs('$1').
|
||||
base_expr -> signed_number : { element(4, '$1'), [{line,?line('$1')}], ?exprs('$1') }.
|
||||
base_expr -> atom : build_atom('$1').
|
||||
base_expr -> list : '$1'.
|
||||
base_expr -> tuple : '$1'.
|
||||
base_expr -> 'true' : ?op('$1').
|
||||
base_expr -> 'false' : ?op('$1').
|
||||
base_expr -> 'nil' : ?op('$1').
|
||||
base_expr -> 'true' : ?id('$1').
|
||||
base_expr -> 'false' : ?id('$1').
|
||||
base_expr -> 'nil' : ?id('$1').
|
||||
base_expr -> aliases : { '__aliases__', [{line,?line('$1')}], ?exprs('$1') }.
|
||||
base_expr -> bin_string : build_bin_string('$1').
|
||||
base_expr -> list_string : build_list_string('$1').
|
||||
base_expr -> bit_string : '$1'.
|
||||
base_expr -> '&' : { '&', [{line,?line('$1')}], ?exprs('$1') }.
|
||||
base_expr -> '...' : { ?op('$1'), [{line,?line('$1')}], [] }.
|
||||
base_expr -> '...' : { ?id('$1'), [{line,?line('$1')}], [] }.
|
||||
base_expr -> sigil : build_sigil('$1').
|
||||
|
||||
%% Blocks
|
||||
@@ -217,9 +207,13 @@ stab_eol -> stab : '$1'.
|
||||
stab_eol -> stab eol : '$1'.
|
||||
|
||||
stab_expr -> expr : '$1'.
|
||||
stab_expr -> stab_op stab_maybe_expr : build_op('$1', [], '$2').
|
||||
stab_expr -> call_args_no_parens stab_op stab_maybe_expr : build_op('$2', unwrap_splice('$1'), '$3').
|
||||
stab_expr -> call_args_parens_not_one stab_op stab_maybe_expr : build_op('$2', unwrap_splice('$1'), '$3').
|
||||
stab_expr -> stab_op_eol stab_maybe_expr : build_op('$1', [], '$2').
|
||||
stab_expr -> call_args_no_parens stab_op_eol stab_maybe_expr :
|
||||
build_op('$2', unwrap_when(unwrap_splice('$1')), '$3').
|
||||
stab_expr -> call_args_parens_not_one stab_op_eol stab_maybe_expr :
|
||||
build_op('$2', unwrap_splice('$1'), '$3').
|
||||
stab_expr -> call_args_parens_not_one when_op matched_expr stab_op_eol stab_maybe_expr :
|
||||
build_op('$4', [{ 'when', [{line,?line('$2')}], unwrap_splice('$1') ++ ['$3'] }], '$5').
|
||||
|
||||
stab_maybe_expr -> 'expr' : '$1'.
|
||||
stab_maybe_expr -> '$empty' : nil.
|
||||
@@ -239,7 +233,7 @@ open_paren -> '(' eol : '$1'.
|
||||
close_paren -> ')' : '$1'.
|
||||
close_paren -> eol ')' : '$2'.
|
||||
|
||||
empty_paren -> open_paren ')' : nil.
|
||||
empty_paren -> open_paren ')' : '$1'.
|
||||
|
||||
open_bracket -> '[' : '$1'.
|
||||
open_bracket -> '[' eol : '$1'.
|
||||
@@ -258,107 +252,69 @@ close_curly -> eol '}' : '$2'.
|
||||
|
||||
% Operators
|
||||
|
||||
add_op -> '+' : '$1'.
|
||||
add_op -> '-' : '$1'.
|
||||
add_op -> '+' eol : '$1'.
|
||||
add_op -> '-' eol : '$1'.
|
||||
add_op_eol -> add_op : '$1'.
|
||||
add_op_eol -> add_op eol : '$1'.
|
||||
add_op_eol -> dual_op : '$1'.
|
||||
add_op_eol -> dual_op eol : '$1'.
|
||||
|
||||
mult_op -> '*' : '$1'.
|
||||
mult_op -> '/' : '$1'.
|
||||
mult_op -> '*' eol : '$1'.
|
||||
mult_op -> '/' eol : '$1'.
|
||||
mult_op_eol -> mult_op : '$1'.
|
||||
mult_op_eol -> mult_op eol : '$1'.
|
||||
|
||||
two_op -> '++' : '$1'.
|
||||
two_op -> '--' : '$1'.
|
||||
two_op -> '++' eol : '$1'.
|
||||
two_op -> '--' eol : '$1'.
|
||||
two_op -> '**' : '$1'.
|
||||
two_op -> '**' eol : '$1'.
|
||||
exp_op_eol -> exp_op : '$1'.
|
||||
exp_op_eol -> exp_op eol : '$1'.
|
||||
|
||||
regex_op -> '=~' : '$1'.
|
||||
regex_op -> '=~' eol : '$1'.
|
||||
two_op_eol -> two_op : '$1'.
|
||||
two_op_eol -> two_op eol : '$1'.
|
||||
|
||||
right_op -> '|>' : '$1'.
|
||||
right_op -> '|>' eol : '$1'.
|
||||
default_op_eol -> default_op : '$1'.
|
||||
default_op_eol -> default_op eol : '$1'.
|
||||
|
||||
three_op -> '&&&' : '$1'.
|
||||
three_op -> '&&&' eol : '$1'.
|
||||
three_op -> '|||' : '$1'.
|
||||
three_op -> '|||' eol : '$1'.
|
||||
three_op -> '<<<' : '$1'.
|
||||
three_op -> '<<<' eol : '$1'.
|
||||
three_op -> '>>>' : '$1'.
|
||||
three_op -> '>>>' eol : '$1'.
|
||||
three_op -> '^^^' : '$1'.
|
||||
three_op -> '^^^' eol : '$1'.
|
||||
type_op_eol -> type_op : '$1'.
|
||||
type_op_eol -> type_op eol : '$1'.
|
||||
|
||||
default_op -> '//' : '$1'.
|
||||
default_op -> '//' eol : '$1'.
|
||||
unary_op_eol -> unary_op : '$1'.
|
||||
unary_op_eol -> unary_op eol : '$1'.
|
||||
unary_op_eol -> dual_op : '$1'.
|
||||
unary_op_eol -> dual_op eol : '$1'.
|
||||
|
||||
colon_colon_op -> '::' : '$1'.
|
||||
colon_colon_op -> '::' eol : '$1'.
|
||||
match_op_eol -> match_op : '$1'.
|
||||
match_op_eol -> match_op eol : '$1'.
|
||||
|
||||
unary_op -> '+' : '$1'.
|
||||
unary_op -> '+' eol : '$1'.
|
||||
unary_op -> '-' : '$1'.
|
||||
unary_op -> '-' eol : '$1'.
|
||||
unary_op -> '!' : '$1'.
|
||||
unary_op -> '!' eol : '$1'.
|
||||
unary_op -> '^' : '$1'.
|
||||
unary_op -> '^' eol : '$1'.
|
||||
unary_op -> 'not' : '$1'.
|
||||
unary_op -> 'not' eol : '$1'.
|
||||
unary_op -> '~~~' : '$1'.
|
||||
unary_op -> '~~~' eol : '$1'.
|
||||
and_op_eol -> and_op : '$1'.
|
||||
and_op_eol -> and_op eol : '$1'.
|
||||
|
||||
at_op -> '@' : '$1'.
|
||||
at_op -> '@' eol : '$1'.
|
||||
or_op_eol -> or_op : '$1'.
|
||||
or_op_eol -> or_op eol : '$1'.
|
||||
|
||||
match_op -> '=' : '$1'.
|
||||
match_op -> '=' eol : '$1'.
|
||||
tail_op_eol -> tail_op : '$1'.
|
||||
tail_op_eol -> tail_op eol : '$1'.
|
||||
|
||||
andand_op -> '&&' : '$1'.
|
||||
andand_op -> '&&' eol : '$1'.
|
||||
than_op_eol -> than_op : '$1'.
|
||||
than_op_eol -> than_op eol : '$1'.
|
||||
|
||||
oror_op -> '||' : '$1'.
|
||||
oror_op -> '||' eol : '$1'.
|
||||
in_op_eol -> in_op : '$1'.
|
||||
in_op_eol -> in_op eol : '$1'.
|
||||
|
||||
and_op -> 'and' : '$1'.
|
||||
and_op -> 'and' eol : '$1'.
|
||||
inc_op_eol -> inc_op : '$1'.
|
||||
inc_op_eol -> inc_op eol : '$1'.
|
||||
|
||||
or_op -> 'or' : '$1'.
|
||||
or_op -> 'or' eol : '$1'.
|
||||
or_op -> 'xor' : '$1'.
|
||||
or_op -> 'xor' eol : '$1'.
|
||||
when_op_eol -> when_op : '$1'.
|
||||
when_op_eol -> when_op eol : '$1'.
|
||||
|
||||
pipe_op -> '|' : '$1'.
|
||||
pipe_op -> '|' eol : '$1'.
|
||||
stab_op_eol -> stab_op : '$1'.
|
||||
stab_op_eol -> stab_op eol : '$1'.
|
||||
|
||||
bin_concat_op -> '<>' : '$1'.
|
||||
bin_concat_op -> '<>' eol : '$1'.
|
||||
range_op_eol -> range_op : '$1'.
|
||||
range_op_eol -> range_op eol : '$1'.
|
||||
|
||||
in_op -> 'in' : '$1'.
|
||||
in_op -> 'in' eol : '$1'.
|
||||
at_op_eol -> at_op : '$1'.
|
||||
at_op_eol -> at_op eol : '$1'.
|
||||
|
||||
inc_op -> 'inlist' : '$1'.
|
||||
inc_op -> 'inlist' eol : '$1'.
|
||||
inc_op -> 'inbits' : '$1'.
|
||||
inc_op -> 'inbits' eol : '$1'.
|
||||
comp_op_eol -> comp_op : '$1'.
|
||||
comp_op_eol -> comp_op eol : '$1'.
|
||||
|
||||
when_op -> 'when' : '$1'.
|
||||
when_op -> 'when' eol : '$1'.
|
||||
|
||||
stab_op -> '->' : '$1'.
|
||||
stab_op -> '->' eol : '$1'.
|
||||
|
||||
send_op -> '<-' : '$1'.
|
||||
send_op -> '<-' eol : '$1'.
|
||||
|
||||
range_op -> '..' : '$1'.
|
||||
range_op -> '..' eol : '$1'.
|
||||
|
||||
comp_expr_op -> comp_op : '$1'.
|
||||
comp_expr_op -> comp_op eol : '$1'.
|
||||
arrow_op_eol -> arrow_op : '$1'.
|
||||
arrow_op_eol -> arrow_op eol : '$1'.
|
||||
|
||||
% Dot operator
|
||||
|
||||
@@ -394,7 +350,7 @@ matched_comma_expr -> matched_expr : ['$1'].
|
||||
matched_comma_expr -> matched_comma_expr ',' matched_expr : ['$3'|'$1'].
|
||||
|
||||
call_args_no_parens_strict -> call_args_no_parens : '$1'.
|
||||
call_args_no_parens_strict -> open_paren ')' : throw_no_parens_strict('$1').
|
||||
call_args_no_parens_strict -> empty_paren : throw_no_parens_strict('$1').
|
||||
call_args_no_parens_strict -> open_paren matched_kw_base close_paren : throw_no_parens_strict('$1').
|
||||
call_args_no_parens_strict -> open_paren matched_expr ',' call_args_no_parens close_paren : throw_no_parens_strict('$1').
|
||||
|
||||
@@ -402,7 +358,7 @@ call_args_no_parens -> matched_comma_expr : lists:reverse('$1').
|
||||
call_args_no_parens -> matched_kw_base : ['$1'].
|
||||
call_args_no_parens -> matched_comma_expr ',' matched_kw_base : lists:reverse(['$3'|'$1']).
|
||||
|
||||
call_args_parens_not_one -> open_paren ')' : [].
|
||||
call_args_parens_not_one -> empty_paren : [].
|
||||
call_args_parens_not_one -> open_paren matched_kw_base close_paren : ['$2'].
|
||||
call_args_parens_not_one -> open_paren matched_expr ',' call_args_no_parens close_paren : ['$2'|'$4'].
|
||||
|
||||
@@ -420,7 +376,7 @@ optional_comma_expr -> paren_expr ',' : '$1'.
|
||||
|
||||
call_args -> comma_expr : lists:reverse('$1').
|
||||
|
||||
call_args_parens -> open_paren ')' : [].
|
||||
call_args_parens -> empty_paren : [].
|
||||
call_args_parens -> open_paren call_args close_paren : '$2'.
|
||||
|
||||
% KV
|
||||
@@ -466,7 +422,7 @@ bit_string -> open_bit call_args close_bit : { '<<>>', [{line,?line('$1')}], '$2
|
||||
|
||||
Erlang code.
|
||||
|
||||
-define(op(Node), element(1, Node)).
|
||||
-define(id(Node), element(1, Node)).
|
||||
-define(line(Node), element(2, Node)).
|
||||
-define(exprs(Node), element(3, Node)).
|
||||
|
||||
@@ -479,17 +435,14 @@ Erlang code.
|
||||
|
||||
%% Operators
|
||||
|
||||
build_op({ _, _, _ } = Op, Left, Right) ->
|
||||
{ ?exprs(Op), [{line,?line(Op)}], [Left, Right] };
|
||||
|
||||
build_op({ BOp, Line }, { UOp, _, [Left] }, Right) when ?rearrange_bop(BOp), ?rearrange_uop(UOp) ->
|
||||
build_op({ _Kind, Line, BOp }, { UOp, _, [Left] }, Right) when ?rearrange_bop(BOp), ?rearrange_uop(UOp) ->
|
||||
{ UOp, [{line,Line}], [{ BOp, [{line,Line}], [Left, Right] }] };
|
||||
|
||||
build_op(Op, Left, Right) ->
|
||||
{ ?op(Op), [{line,?line(Op)}], [Left, Right] }.
|
||||
build_op({ _Kind, Line, Op }, Left, Right) ->
|
||||
{ Op, [{line,Line}], [Left, Right] }.
|
||||
|
||||
build_unary_op(Op, Expr) ->
|
||||
{ ?op(Op), [{line,?line(Op)}], [Expr] }.
|
||||
build_unary_op({ _Kind, Line, Op }, Expr) ->
|
||||
{ Op, [{line,Line}], [Expr] }.
|
||||
|
||||
build_tuple(_Marker, [Left, Right]) ->
|
||||
{ Left, Right };
|
||||
@@ -555,13 +508,13 @@ build_fn(Op, Stab) ->
|
||||
|
||||
build_access(Expr, Access) ->
|
||||
Meta = [{line,?line(Access)}],
|
||||
{ { '.', Meta, ['Elixir.Kernel', access] }, Meta, [ Expr, ?op(Access) ] }.
|
||||
{ { '.', Meta, ['Elixir.Kernel', access] }, Meta, [ Expr, ?id(Access) ] }.
|
||||
|
||||
%% Interpolation aware
|
||||
|
||||
build_sigil({ sigil, Line, Sigil, Parts, Modifiers }) ->
|
||||
Meta = [{line,Line}],
|
||||
{ list_to_atom([$_,$_,Sigil,$_,$_]), Meta, [ { '<<>>', Meta, Parts }, Modifiers ] }.
|
||||
{ list_to_atom("sigil_" ++ [Sigil]), Meta, [ { '<<>>', Meta, Parts }, Modifiers ] }.
|
||||
|
||||
build_bin_string({ bin_string, _Line, [H] }) when is_binary(H) -> H;
|
||||
build_bin_string({ bin_string, Line, Args }) -> { '<<>>', [{line,Line}], Args }.
|
||||
@@ -579,25 +532,25 @@ build_atom({ atom, Line, Args }) ->
|
||||
%% Keywords
|
||||
|
||||
build_stab([{ '->', Meta, [Left, Right] }|T]) ->
|
||||
{ '->', Meta, build_stab(T, Left, [Right], []) };
|
||||
{ '->', Meta, build_stab(Meta, T, Left, [Right], []) };
|
||||
|
||||
build_stab(Else) ->
|
||||
build_block(Else).
|
||||
|
||||
build_stab([{ '->', _, [Left, Right] }|T], Marker, Temp, Acc) ->
|
||||
H = { Marker, build_block(lists:reverse(Temp)) },
|
||||
build_stab(T, Left, [Right], [H|Acc]);
|
||||
build_stab(Old, [{ '->', New, [Left, Right] }|T], Marker, Temp, Acc) ->
|
||||
H = { Marker, Old, build_block(lists:reverse(Temp)) },
|
||||
build_stab(New, T, Left, [Right], [H|Acc]);
|
||||
|
||||
build_stab([H|T], Marker, Temp, Acc) ->
|
||||
build_stab(T, Marker, [H|Temp], Acc);
|
||||
build_stab(Meta, [H|T], Marker, Temp, Acc) ->
|
||||
build_stab(Meta, T, Marker, [H|Temp], Acc);
|
||||
|
||||
build_stab([], Marker, Temp, Acc) ->
|
||||
H = { Marker, build_block(lists:reverse(Temp)) },
|
||||
build_stab(Meta, [], Marker, Temp, Acc) ->
|
||||
H = { Marker, Meta, build_block(lists:reverse(Temp)) },
|
||||
lists:reverse([H|Acc]).
|
||||
|
||||
%% Every time the parser sees a (unquote_splicing())
|
||||
%% it assumes that a block is being spliced, wrapping
|
||||
%% the splicing in a __block__. But in the stab cause,
|
||||
%% the splicing in a __block__. But in the stab clause,
|
||||
%% we can have (unquote_splicing(1,2,3)) -> :ok, in such
|
||||
%% case, we don't actually want the block, since it is
|
||||
%% an arg style call. unwrap_splice unwraps the splice
|
||||
@@ -607,6 +560,14 @@ unwrap_splice([{ '__block__', [], [{ unquote_splicing, _, _ }] = Splice }]) ->
|
||||
|
||||
unwrap_splice(Other) -> Other.
|
||||
|
||||
unwrap_when(Args) ->
|
||||
case elixir_tree_helpers:split_last(Args) of
|
||||
{ Start, { 'when', Meta, [_, _] = End } } ->
|
||||
[{ 'when', Meta, Start ++ End }];
|
||||
{ _, _ } ->
|
||||
Args
|
||||
end.
|
||||
|
||||
%% Errors
|
||||
|
||||
throw(Line, Error, Token) ->
|
||||
|
||||
@@ -11,7 +11,7 @@ handle({ _, Meta, Args } = Original, S, Opt) when is_list(Args), S#elixir_scope.
|
||||
{ Call, Def, SC } when Def /= [] ->
|
||||
Final = validate(Meta, Def, SC),
|
||||
Block = setelement(3, Original, Call),
|
||||
elixir_translator:translate_fn(Meta, [{ Final, Block }], SC);
|
||||
elixir_translator:translate_fn(Meta, [{ Final, Meta, Block }], SC);
|
||||
_ -> error
|
||||
end;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%% Implements Elixir quote.
|
||||
-module(elixir_quote).
|
||||
-export([escape/2, erl_escape/3, erl_quote/3, linify/2, unquote/5]).
|
||||
-export([escape/2, erl_escape/3, erl_quote/4, linify/2, unquote/5]).
|
||||
-include("elixir.hrl").
|
||||
|
||||
%% Apply the line from site call on quoted contents.
|
||||
@@ -44,7 +44,7 @@ unquote(File, Meta, _Left, _Right, _Args) ->
|
||||
%% Escapes the given expression. It is similar to quote, but
|
||||
%% lines are kept and hygiene mechanisms are disabled.
|
||||
escape(Expr, Unquote) ->
|
||||
quote(Expr, #elixir_quote{
|
||||
quote(Expr, nil, #elixir_quote{
|
||||
line=keep,
|
||||
vars_hygiene=false,
|
||||
aliases_hygiene=false,
|
||||
@@ -60,11 +60,24 @@ erl_escape(Expr, Unquote, S) ->
|
||||
|
||||
%% Quotes an expression and return its quoted Elixir AST.
|
||||
|
||||
quote(Expr, Q, S) ->
|
||||
do_quote(Expr, Q, S).
|
||||
quote(Expr, nil, Q, S) ->
|
||||
do_quote(Expr, Q, S);
|
||||
|
||||
erl_quote(Expr, Q, S) ->
|
||||
{ QExpr, TQ } = quote(Expr, Q, S),
|
||||
quote(Expr, Binding, Q, S) ->
|
||||
Context = Q#elixir_quote.context,
|
||||
|
||||
Vars = [ { '{}', [],
|
||||
[ '=', [], [
|
||||
{ '{}', [], [K, [], Context] },
|
||||
V
|
||||
] ]
|
||||
} || { K, V } <- Binding],
|
||||
|
||||
{ TExprs, TQ } = do_quote(Expr, Q, S),
|
||||
{ { '{}',[], ['__block__',[], Vars ++ [TExprs] ] }, TQ }.
|
||||
|
||||
erl_quote(Expr, Binding, Q, S) ->
|
||||
{ QExpr, TQ } = quote(Expr, Binding, Q, S),
|
||||
{ TExpr, TS } = elixir_translator:translate_each(QExpr, S),
|
||||
{ TExpr, TQ, TS }.
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
translate_var(Meta, Name, Kind, S, Callback) ->
|
||||
Line = ?line(Meta),
|
||||
Vars = S#elixir_scope.vars,
|
||||
Tuple = { Name, Kind },
|
||||
|
||||
case Name of
|
||||
'_' ->
|
||||
@@ -20,9 +21,9 @@ translate_var(Meta, Name, Kind, S, Callback) ->
|
||||
case S#elixir_scope.context of
|
||||
match ->
|
||||
TempVars = S#elixir_scope.temp_vars,
|
||||
case { orddict:is_key({ Name, Kind }, Vars), orddict:find(Name, TempVars) } of
|
||||
{ true, { ok, Kind } } ->
|
||||
{ { var, Line, orddict:fetch({ Name, Kind }, Vars) }, S };
|
||||
case { orddict:is_key(Tuple, Vars), is_list(TempVars) andalso ordsets:is_element(Tuple, TempVars) } of
|
||||
{ true, true } ->
|
||||
{ { var, Line, orddict:fetch(Tuple, Vars) }, S };
|
||||
{ Else, _ } ->
|
||||
{ NewVar, NS } = if
|
||||
Kind /= nil -> build_erl_var(Line, S);
|
||||
@@ -33,11 +34,14 @@ translate_var(Meta, Name, Kind, S, Callback) ->
|
||||
RealName = element(3, NewVar),
|
||||
ClauseVars = S#elixir_scope.clause_vars,
|
||||
{ NewVar, NS#elixir_scope{
|
||||
vars=orddict:store({ Name, Kind }, RealName, Vars),
|
||||
temp_vars=orddict:store(Name, Kind, TempVars),
|
||||
vars=orddict:store(Tuple, RealName, Vars),
|
||||
temp_vars=if
|
||||
TempVars == nil -> TempVars;
|
||||
true -> ordsets:add_element(Tuple, TempVars)
|
||||
end,
|
||||
clause_vars=if
|
||||
ClauseVars == nil -> nil;
|
||||
true -> orddict:store({ Name, Kind }, RealName, ClauseVars)
|
||||
ClauseVars == nil; Kind == false -> ClauseVars;
|
||||
true -> orddict:store(Tuple, RealName, ClauseVars)
|
||||
end
|
||||
} }
|
||||
end;
|
||||
|
||||
+195
-117
@@ -10,73 +10,93 @@
|
||||
existing_atoms_only=false
|
||||
}).
|
||||
|
||||
-define(container2(T1, T2),
|
||||
-define(container(T1, T2),
|
||||
T1 == ${, T2 == $};
|
||||
T1 == $[, T2 == $]
|
||||
).
|
||||
|
||||
-define(comp3(T1, T2, T3),
|
||||
T1 == $=, T2 == $=, T3 == $=;
|
||||
T1 == $!, T2 == $=, T3 == $=
|
||||
).
|
||||
%% New ops table
|
||||
|
||||
-define(op3(T1, T2, T3),
|
||||
T1 == $<, T2 == $<, T3 == $<;
|
||||
T1 == $>, T2 == $>, T3 == $>;
|
||||
T1 == $^, T2 == $^, T3 == $^;
|
||||
T1 == $~, T2 == $~, T3 == $~;
|
||||
T1 == $&, T2 == $&, T3 == $&;
|
||||
T1 == $|, T2 == $|, T3 == $|
|
||||
).
|
||||
|
||||
-define(comp2(T1, T2),
|
||||
T1 == $=, T2 == $=;
|
||||
T1 == $!, T2 == $=;
|
||||
T1 == $<, T2 == $=;
|
||||
T1 == $>, T2 == $=
|
||||
).
|
||||
|
||||
-define(op2(T1, T2),
|
||||
T1 == $&, T2 == $&;
|
||||
T1 == $|, T2 == $|;
|
||||
T1 == $<, T2 == $>;
|
||||
T1 == $+, T2 == $+;
|
||||
T1 == $-, T2 == $-;
|
||||
T1 == $*, T2 == $*;
|
||||
T1 == $/, T2 == $/;
|
||||
T1 == $:, T2 == $:;
|
||||
T1 == $<, T2 == $-;
|
||||
T1 == $-, T2 == $>;
|
||||
T1 == $., T2 == $.;
|
||||
T1 == $|, T2 == $>;
|
||||
T1 == $/, T2 == $>;
|
||||
T1 == $=, T2 == $~
|
||||
).
|
||||
|
||||
-define(comp1(T),
|
||||
T == $<;
|
||||
T == $>
|
||||
).
|
||||
|
||||
-define(op1(T),
|
||||
T == $+;
|
||||
T == $-;
|
||||
T == $*;
|
||||
T == $/;
|
||||
T == $=;
|
||||
T == $|;
|
||||
T == $!;
|
||||
T == $^;
|
||||
T == $@
|
||||
).
|
||||
-define(at_op(T),
|
||||
T == $@).
|
||||
|
||||
-define(unary_op(T),
|
||||
T == '+';
|
||||
T == '-';
|
||||
T == '@';
|
||||
T == '!';
|
||||
T == '^'
|
||||
).
|
||||
% T == $&;
|
||||
T == $!;
|
||||
T == $^).
|
||||
|
||||
-define(unary_op3(T1, T2, T3),
|
||||
T1 == $~, T2 == $~, T3 == $~).
|
||||
|
||||
-define(exp_op3(T1, T2, T3),
|
||||
T1 == $^, T2 == $^, T3 == $^).
|
||||
|
||||
-define(two_op(T1, T2),
|
||||
T1 == $+, T2 == $+;
|
||||
T1 == $-, T2 == $-;
|
||||
T1 == $*, T2 == $*).
|
||||
|
||||
-define(than_op(T1, T2),
|
||||
T1 == $<, T2 == $>).
|
||||
|
||||
-define(mult_op(T),
|
||||
T == $* orelse T == $/).
|
||||
|
||||
-define(dual_op(T),
|
||||
T == $+ orelse T == $-).
|
||||
|
||||
-define(range_op(T1, T2),
|
||||
T1 == $., T2 == $.).
|
||||
|
||||
-define(arrow_op3(T1, T2, T3),
|
||||
T1 == $<, T2 == $<, T3 == $<;
|
||||
T1 == $>, T2 == $>, T3 == $>).
|
||||
|
||||
-define(arrow_op(T1, T2),
|
||||
T1 == $<, T2 == $-;
|
||||
T1 == $|, T2 == $>).
|
||||
|
||||
-define(comp_op(T),
|
||||
T == $<;
|
||||
T == $>).
|
||||
|
||||
-define(comp_op2(T1, T2),
|
||||
T1 == $=, T2 == $=;
|
||||
T1 == $=, T2 == $~;
|
||||
T1 == $!, T2 == $=;
|
||||
T1 == $<, T2 == $=;
|
||||
T1 == $>, T2 == $=).
|
||||
|
||||
-define(comp_op3(T1, T2, T3),
|
||||
T1 == $=, T2 == $=, T3 == $=;
|
||||
T1 == $!, T2 == $=, T3 == $=).
|
||||
|
||||
-define(and_op(T1, T2),
|
||||
T1 == $&, T2 == $&).
|
||||
|
||||
-define(or_op(T1, T2),
|
||||
T1 == $|, T2 == $|).
|
||||
|
||||
-define(and_op3(T1, T2, T3),
|
||||
T1 == $&, T2 == $&, T3 == $&).
|
||||
|
||||
-define(or_op3(T1, T2, T3),
|
||||
T1 == $|, T2 == $|, T3 == $|).
|
||||
|
||||
-define(match_op(T),
|
||||
T == $=).
|
||||
|
||||
-define(tail_op(T),
|
||||
T == $|).
|
||||
|
||||
-define(default_op(T1, T2),
|
||||
T1 == $/, T2 == $/).
|
||||
|
||||
-define(stab_op(T1, T2),
|
||||
T1 == $-, T2 == $>).
|
||||
|
||||
-define(type_op(T1, T2),
|
||||
T1 == $:, T2 == $:).
|
||||
|
||||
tokenize(String, Line, Opts) ->
|
||||
File = case lists:keyfind(file, 1, Opts) of
|
||||
@@ -205,13 +225,10 @@ tokenize([$?,Char|T], Line, Scope, Tokens) ->
|
||||
tokenize(T, Line, Scope, [{ number, Line, Char }|Tokens]);
|
||||
|
||||
% Dot identifier/operators
|
||||
|
||||
tokenize("..." ++ Rest, Line, Scope, Tokens) ->
|
||||
Token = check_call_identifier(identifier, Line, '...', Rest),
|
||||
tokenize(Rest, Line, Scope, [Token|Tokens]);
|
||||
|
||||
% ## Containers
|
||||
|
||||
tokenize([$.,T|Tail], Line, Scope, Tokens) when ?is_space(T) ->
|
||||
case [T|Tail] of
|
||||
[$\r,$\n|Rest] -> tokenize([$.|Rest], Line + 1, Scope, Tokens);
|
||||
@@ -219,22 +236,30 @@ tokenize([$.,T|Tail], Line, Scope, Tokens) when ?is_space(T) ->
|
||||
[_|Rest] -> tokenize([$.|Rest], Line, Scope, Tokens)
|
||||
end;
|
||||
|
||||
% ## Containers
|
||||
tokenize(".<<>>" ++ Rest, Line, Scope, Tokens) ->
|
||||
handle_call_identifier(Rest, Line, '<<>>', Scope, Tokens);
|
||||
|
||||
tokenize([$.,T1,T2|Rest], Line, Scope, Tokens) when ?container2(T1, T2) ->
|
||||
tokenize([$.,T1,T2|Rest], Line, Scope, Tokens) when ?container(T1, T2) ->
|
||||
handle_call_identifier(Rest, Line, list_to_atom([T1, T2]), Scope, Tokens);
|
||||
|
||||
% ## Three Token Operators
|
||||
tokenize([$.,T1,T2,T3|Rest], Line, Scope, Tokens) when ?comp3(T1, T2, T3); ?op3(T1, T2, T3) ->
|
||||
tokenize([$.,T1,T2,T3|Rest], Line, Scope, Tokens) when
|
||||
?unary_op3(T1, T2, T3); ?comp_op3(T1, T2, T3); ?and_op3(T1, T2, T3); ?or_op3(T1, T2, T3);
|
||||
?arrow_op3(T1, T2, T3); ?exp_op3(T1, T2, T3) ->
|
||||
handle_call_identifier(Rest, Line, list_to_atom([T1, T2, T3]), Scope, Tokens);
|
||||
|
||||
% ## Two Token Operators
|
||||
tokenize([$.,T1,T2|Rest], Line, Scope, Tokens) when ?comp2(T1, T2); ?op2(T1, T2) ->
|
||||
tokenize([$.,T1,T2|Rest], Line, Scope, Tokens) when
|
||||
?comp_op2(T1, T2); ?and_op(T1, T2); ?or_op(T1, T2); ?arrow_op(T1, T2);
|
||||
?range_op(T1, T2); ?than_op(T1, T2); ?default_op(T1, T2); ?two_op(T1, T2);
|
||||
?stab_op(T1, T2); ?type_op(T1, T2) ->
|
||||
handle_call_identifier(Rest, Line, list_to_atom([T1, T2]), Scope, Tokens);
|
||||
|
||||
% ## Single Token Operators
|
||||
tokenize([$.,T|Rest], Line, Scope, Tokens) when ?comp1(T); ?op1(T); T == $& ->
|
||||
tokenize([$.,T|Rest], Line, Scope, Tokens) when
|
||||
?at_op(T); ?unary_op(T); ?dual_op(T); ?mult_op(T); ?comp_op(T);
|
||||
?match_op(T); ?tail_op(T); T == $& ->
|
||||
handle_call_identifier(Rest, Line, list_to_atom([T]), Scope, Tokens);
|
||||
|
||||
% Dot call
|
||||
@@ -295,19 +320,26 @@ tokenize(":..." ++ Rest, Line, Scope, Tokens) ->
|
||||
tokenize(":<<>>" ++ Rest, Line, Scope, Tokens) ->
|
||||
tokenize(Rest, Line, Scope, [{ atom, Line, '<<>>' }|Tokens]);
|
||||
|
||||
tokenize([$:,T1,T2|Rest], Line, Scope, Tokens) when ?container2(T1, T2) ->
|
||||
tokenize([$:,T1,T2|Rest], Line, Scope, Tokens) when ?container(T1, T2) ->
|
||||
tokenize(Rest, Line, Scope, [{ atom, Line, list_to_atom([T1,T2]) }|Tokens]);
|
||||
|
||||
% ## Three Token Operators
|
||||
tokenize([$:,T1,T2,T3|Rest], Line, Scope, Tokens) when ?comp3(T1, T2, T3); ?op3(T1, T2, T3) ->
|
||||
tokenize([$:,T1,T2,T3|Rest], Line, Scope, Tokens) when
|
||||
?unary_op3(T1, T2, T3); ?comp_op3(T1, T2, T3); ?and_op3(T1, T2, T3); ?or_op3(T1, T2, T3);
|
||||
?arrow_op3(T1, T2, T3); ?exp_op3(T1, T2, T3) ->
|
||||
tokenize(Rest, Line, Scope, [{ atom, Line, list_to_atom([T1,T2,T3]) }|Tokens]);
|
||||
|
||||
% ## Two Token Operators
|
||||
tokenize([$:,T1,T2|Rest], Line, Scope, Tokens) when ?comp2(T1, T2); ?op2(T1, T2) ->
|
||||
tokenize([$:,T1,T2|Rest], Line, Scope, Tokens) when
|
||||
?comp_op2(T1, T2); ?and_op(T1, T2); ?or_op(T1, T2); ?arrow_op(T1, T2);
|
||||
?range_op(T1, T2); ?than_op(T1, T2); ?default_op(T1, T2); ?two_op(T1, T2);
|
||||
?stab_op(T1, T2); ?type_op(T1, T2) ->
|
||||
tokenize(Rest, Line, Scope, [{ atom, Line, list_to_atom([T1,T2]) }|Tokens]);
|
||||
|
||||
% ## Single Token Operators
|
||||
tokenize([$:,T|Rest], Line, Scope, Tokens) when ?comp1(T); ?op1(T); T == $&; T == $. ->
|
||||
tokenize([$:,T|Rest], Line, Scope, Tokens) when
|
||||
?at_op(T); ?unary_op(T); ?dual_op(T); ?mult_op(T); ?comp_op(T);
|
||||
?match_op(T); ?tail_op(T); T == $&; T == $. ->
|
||||
tokenize(Rest, Line, Scope, [{ atom, Line, list_to_atom([T]) }|Tokens]);
|
||||
|
||||
% End of line
|
||||
@@ -336,13 +368,24 @@ tokenize("\r\n" ++ Rest, Line, Scope, Tokens) ->
|
||||
tokenize([$&,H|Rest], Line, Scope, Tokens) when ?is_digit(H) ->
|
||||
tokenize(Rest, Line, Scope, [{ '&', Line, [list_to_integer([H])] }|Tokens]);
|
||||
|
||||
% ## Comparison three token operators
|
||||
tokenize([T1,T2,T3|Rest], Line, Scope, Tokens) when ?comp3(T1, T2, T3) ->
|
||||
handle_comp_op(Rest, Line, list_to_atom([T1,T2,T3]), Scope, Tokens);
|
||||
|
||||
% ## Three token operators
|
||||
tokenize([T1,T2,T3|Rest], Line, Scope, Tokens) when ?op3(T1, T2, T3) ->
|
||||
handle_op(Rest, Line, list_to_atom([T1,T2,T3]), Scope, Tokens);
|
||||
tokenize([T1,T2,T3|Rest], Line, Scope, Tokens) when ?unary_op3(T1, T2, T3) ->
|
||||
handle_nonl_op(Rest, Line, unary_op, list_to_atom([T1,T2,T3]), Scope, Tokens);
|
||||
|
||||
tokenize([T1,T2,T3|Rest], Line, Scope, Tokens) when ?comp_op3(T1, T2, T3) ->
|
||||
handle_op(Rest, Line, comp_op, list_to_atom([T1,T2,T3]), Scope, Tokens);
|
||||
|
||||
tokenize([T1,T2,T3|Rest], Line, Scope, Tokens) when ?and_op3(T1, T2, T3) ->
|
||||
handle_op(Rest, Line, and_op, list_to_atom([T1,T2,T3]), Scope, Tokens);
|
||||
|
||||
tokenize([T1,T2,T3|Rest], Line, Scope, Tokens) when ?or_op3(T1, T2, T3) ->
|
||||
handle_op(Rest, Line, or_op, list_to_atom([T1,T2,T3]), Scope, Tokens);
|
||||
|
||||
tokenize([T1,T2,T3|Rest], Line, Scope, Tokens) when ?arrow_op3(T1, T2, T3) ->
|
||||
handle_op(Rest, Line, arrow_op, list_to_atom([T1,T2,T3]), Scope, Tokens);
|
||||
|
||||
tokenize([T1,T2,T3|Rest], Line, Scope, Tokens) when ?exp_op3(T1, T2, T3) ->
|
||||
handle_op(Rest, Line, exp_op, list_to_atom([T1,T2,T3]), Scope, Tokens);
|
||||
|
||||
% ## Containers + punctuation tokens
|
||||
tokenize([T,T|Rest], Line, Scope, Tokens) when T == $<; T == $> ->
|
||||
@@ -354,21 +397,58 @@ tokenize([T|Rest], Line, Scope, Tokens) when T == $(;
|
||||
Token = { list_to_atom([T]), Line },
|
||||
handle_terminator(Rest, Line, Scope, Token, Tokens);
|
||||
|
||||
% ## Comparison two token operators
|
||||
tokenize([T1,T2|Rest], Line, Scope, Tokens) when ?comp2(T1, T2) ->
|
||||
handle_comp_op(Rest, Line, list_to_atom([T1, T2]), Scope, Tokens);
|
||||
|
||||
% ## Two Token Operators
|
||||
tokenize([T1,T2|Rest], Line, Scope, Tokens) when ?op2(T1, T2) ->
|
||||
handle_op(Rest, Line, list_to_atom([T1, T2]), Scope, Tokens);
|
||||
tokenize([T1,T2|Rest], Line, Scope, Tokens) when ?two_op(T1, T2) ->
|
||||
handle_op(Rest, Line, two_op, list_to_atom([T1, T2]), Scope, Tokens);
|
||||
|
||||
% ## Comparison single token operators
|
||||
tokenize([T|Rest], Line, Scope, Tokens) when ?comp1(T) ->
|
||||
handle_comp_op(Rest, Line, list_to_atom([T]), Scope, Tokens);
|
||||
tokenize([T1,T2|Rest], Line, Scope, Tokens) when ?than_op(T1, T2) ->
|
||||
handle_op(Rest, Line, than_op, list_to_atom([T1, T2]), Scope, Tokens);
|
||||
|
||||
tokenize([T1,T2|Rest], Line, Scope, Tokens) when ?range_op(T1, T2) ->
|
||||
handle_op(Rest, Line, range_op, list_to_atom([T1, T2]), Scope, Tokens);
|
||||
|
||||
tokenize([T1,T2|Rest], Line, Scope, Tokens) when ?arrow_op(T1, T2) ->
|
||||
handle_op(Rest, Line, arrow_op, list_to_atom([T1, T2]), Scope, Tokens);
|
||||
|
||||
tokenize([T1,T2|Rest], Line, Scope, Tokens) when ?comp_op2(T1, T2) ->
|
||||
handle_op(Rest, Line, comp_op, list_to_atom([T1, T2]), Scope, Tokens);
|
||||
|
||||
tokenize([T1,T2|Rest], Line, Scope, Tokens) when ?and_op(T1, T2) ->
|
||||
handle_op(Rest, Line, and_op, list_to_atom([T1, T2]), Scope, Tokens);
|
||||
|
||||
tokenize([T1,T2|Rest], Line, Scope, Tokens) when ?or_op(T1, T2) ->
|
||||
handle_op(Rest, Line, or_op, list_to_atom([T1, T2]), Scope, Tokens);
|
||||
|
||||
tokenize([T1,T2|Rest], Line, Scope, Tokens) when ?default_op(T1, T2) ->
|
||||
handle_op(Rest, Line, default_op, list_to_atom([T1, T2]), Scope, Tokens);
|
||||
|
||||
tokenize([T1,T2|Rest], Line, Scope, Tokens) when ?stab_op(T1, T2) ->
|
||||
handle_op(Rest, Line, stab_op, list_to_atom([T1, T2]), Scope, Tokens);
|
||||
|
||||
tokenize([T1,T2|Rest], Line, Scope, Tokens) when ?type_op(T1, T2) ->
|
||||
handle_op(Rest, Line, type_op, list_to_atom([T1, T2]), Scope, Tokens);
|
||||
|
||||
% ## Single Token Operators
|
||||
tokenize([T|Rest], Line, Scope, Tokens) when ?op1(T) ->
|
||||
handle_op(Rest, Line, list_to_atom([T]), Scope, Tokens);
|
||||
tokenize([T|Rest], Line, Scope, Tokens) when ?at_op(T) ->
|
||||
handle_nonl_op(Rest, Line, at_op, list_to_atom([T]), Scope, Tokens);
|
||||
|
||||
tokenize([T|Rest], Line, Scope, Tokens) when ?unary_op(T) ->
|
||||
handle_nonl_op(Rest, Line, unary_op, list_to_atom([T]), Scope, Tokens);
|
||||
|
||||
tokenize([T|Rest], Line, Scope, Tokens) when ?comp_op(T) ->
|
||||
handle_op(Rest, Line, comp_op, list_to_atom([T]), Scope, Tokens);
|
||||
|
||||
tokenize([T|Rest], Line, Scope, Tokens) when ?dual_op(T) ->
|
||||
handle_nonl_op(Rest, Line, dual_op, list_to_atom([T]), Scope, Tokens);
|
||||
|
||||
tokenize([T|Rest], Line, Scope, Tokens) when ?mult_op(T) ->
|
||||
handle_op(Rest, Line, mult_op, list_to_atom([T]), Scope, Tokens);
|
||||
|
||||
tokenize([T|Rest], Line, Scope, Tokens) when ?match_op(T) ->
|
||||
handle_op(Rest, Line, match_op, list_to_atom([T]), Scope, Tokens);
|
||||
|
||||
tokenize([T|Rest], Line, Scope, Tokens) when ?tail_op(T) ->
|
||||
handle_op(Rest, Line, tail_op, list_to_atom([T]), Scope, Tokens);
|
||||
|
||||
tokenize([$.|Rest], Line, Scope, Tokens) ->
|
||||
tokenize(Rest, Line, Scope, add_token_with_nl({ '.', Line }, Tokens));
|
||||
@@ -410,13 +490,13 @@ tokenize([H|_] = String, Line, Scope, Tokens) when ?is_downcase(H); H == $_ ->
|
||||
% Ambiguous unary/binary operators tokens
|
||||
|
||||
tokenize([Space, Sign, NotMarker|T], Line, Scope, [{ Identifier, _, _ } = H|Tokens]) when
|
||||
Sign == $+ orelse Sign == $-,
|
||||
?is_horizontal_space(Space),
|
||||
not(?is_space(NotMarker)),
|
||||
NotMarker /= $(, NotMarker /= $+, NotMarker /= $-, NotMarker /= $>,
|
||||
Identifier == identifier orelse Identifier == punctuated_identifier ->
|
||||
?dual_op(Sign),
|
||||
?is_horizontal_space(Space),
|
||||
not(?is_space(NotMarker)),
|
||||
NotMarker /= $(, NotMarker /= $+, NotMarker /= $-, NotMarker /= $>,
|
||||
Identifier == identifier orelse Identifier == punctuated_identifier ->
|
||||
Rest = [NotMarker|T],
|
||||
tokenize(Rest, Line, Scope, [{ list_to_atom([Sign]), Line }, setelement(1, H, op_identifier)|Tokens]);
|
||||
tokenize(Rest, Line, Scope, [{ dual_op, Line, list_to_atom([Sign]) }, setelement(1, H, op_identifier)|Tokens]);
|
||||
|
||||
% Spaces
|
||||
|
||||
@@ -462,20 +542,17 @@ handle_strings(T, Line, H, #scope{file=File} = Scope, Tokens) ->
|
||||
interpolation_error(Error, " (for string starting at line ~B)", [Line])
|
||||
end.
|
||||
|
||||
handle_comp_op([$:|Rest], Line, Op, Scope, Tokens) when ?is_space(hd(Rest)) ->
|
||||
handle_nonl_op([$:|Rest], Line, _Kind, Op, Scope, Tokens) when ?is_space(hd(Rest)) ->
|
||||
tokenize(Rest, Line, Scope, [{ kw_identifier, Line, Op }|Tokens]);
|
||||
|
||||
handle_comp_op(Rest, Line, Op, Scope, Tokens) ->
|
||||
tokenize(Rest, Line, Scope, add_token_with_nl({ comp_op, Line, Op }, Tokens)).
|
||||
handle_nonl_op(Rest, Line, Kind, Op, Scope, Tokens) ->
|
||||
tokenize(Rest, Line, Scope, [{ Kind, Line, Op }|Tokens]).
|
||||
|
||||
handle_op([$:|Rest], Line, Op, Scope, Tokens) when ?is_space(hd(Rest)) ->
|
||||
handle_op([$:|Rest], Line, _Kind, Op, Scope, Tokens) when ?is_space(hd(Rest)) ->
|
||||
tokenize(Rest, Line, Scope, [{ kw_identifier, Line, Op }|Tokens]);
|
||||
|
||||
handle_op(Rest, Line, Op, Scope, Tokens) when ?unary_op(Op) ->
|
||||
tokenize(Rest, Line, Scope, [{ Op, Line }|Tokens]);
|
||||
|
||||
handle_op(Rest, Line, Op, Scope, Tokens) ->
|
||||
tokenize(Rest, Line, Scope, add_token_with_nl({ Op, Line }, Tokens)).
|
||||
handle_op(Rest, Line, Kind, Op, Scope, Tokens) ->
|
||||
tokenize(Rest, Line, Scope, add_token_with_nl({ Kind, Line, Op }, Tokens)).
|
||||
|
||||
handle_call_identifier(Rest, Line, Op, Scope, Tokens) ->
|
||||
Token = check_call_identifier(identifier, Line, Op, Rest),
|
||||
@@ -813,15 +890,16 @@ check_keyword(Line, Identifier, Atom, Tokens) when
|
||||
Identifier == identifier; Identifier == do_identifier;
|
||||
Identifier == bracket_identifier; Identifier == paren_identifier ->
|
||||
case keyword(Atom) of
|
||||
true -> { ok, [{ Atom, Line }|Tokens] };
|
||||
op -> { ok, add_token_with_nl({ Atom, Line }, Tokens) };
|
||||
block -> { ok, [{ block_identifier, Line, Atom }|Tokens] };
|
||||
do ->
|
||||
do ->
|
||||
case do_keyword_valid(Tokens) of
|
||||
true -> { ok, [{ Atom, Line }|Tokens] };
|
||||
false -> { error, "do" }
|
||||
end;
|
||||
false -> nomatch
|
||||
false -> nomatch;
|
||||
true -> { ok, [{ Atom, Line }|Tokens] };
|
||||
block -> { ok, [{ block_identifier, Line, Atom }|Tokens] };
|
||||
unary_op -> { ok, [{ unary_op, Line, Atom }|Tokens] };
|
||||
Kind -> { ok, add_token_with_nl({ Kind, Line, Atom }, Tokens) }
|
||||
end;
|
||||
|
||||
check_keyword(_, _, _, _) -> nomatch.
|
||||
@@ -836,19 +914,19 @@ keyword('end') -> true;
|
||||
keyword('true') -> true;
|
||||
keyword('false') -> true;
|
||||
keyword('nil') -> true;
|
||||
keyword('not') -> true;
|
||||
|
||||
% Special handling for do
|
||||
keyword('do') -> do;
|
||||
|
||||
% Bin operator keywords
|
||||
keyword('and') -> op;
|
||||
keyword('or') -> op;
|
||||
keyword('xor') -> op;
|
||||
keyword('when') -> op;
|
||||
keyword('in') -> op;
|
||||
keyword('inlist') -> op;
|
||||
keyword('inbits') -> op;
|
||||
% Operators keywords
|
||||
keyword('not') -> unary_op;
|
||||
keyword('and') -> and_op;
|
||||
keyword('or') -> or_op;
|
||||
keyword('xor') -> or_op;
|
||||
keyword('when') -> when_op;
|
||||
keyword('in') -> in_op;
|
||||
keyword('inlist') -> inc_op;
|
||||
keyword('inbits') -> inc_op;
|
||||
|
||||
% Block keywords
|
||||
keyword('after') -> block;
|
||||
|
||||
@@ -24,7 +24,7 @@ setup(Module) ->
|
||||
end.
|
||||
|
||||
cleanup(Module) ->
|
||||
if_tracker(Module, fun(Pid) -> ?tracker:stop(Pid) end).
|
||||
if_tracker(Module, fun(Pid) -> unlink(Pid), ?tracker:stop(Pid) end).
|
||||
|
||||
record_local(Tuple, Module) when is_atom(Module) ->
|
||||
if_tracker(Module, fun(Pid) ->
|
||||
|
||||
@@ -62,9 +62,6 @@ translate_each({ '__block__', Meta, Args }, S) when is_list(Args) ->
|
||||
{ TArgs, NS } = translate(Args, S),
|
||||
{ { block, ?line(Meta), TArgs }, NS };
|
||||
|
||||
translate_each({ '__scope__', _Meta, [[{erl,true}],[{do,Expr}]] }, S) ->
|
||||
{ Expr, S };
|
||||
|
||||
translate_each({ '__scope__', _Meta, [[{file,File}],[{do,Expr}]] }, S) ->
|
||||
Old = S#elixir_scope.file,
|
||||
{ TExpr, TS } = translate_each(Expr, S#elixir_scope{file=File}),
|
||||
@@ -220,7 +217,7 @@ translate_each({ quote, Meta, [KV, Do] }, S) when is_list(Do) ->
|
||||
false -> syntax_error(Meta, S#elixir_scope.file, "invalid args for quote")
|
||||
end,
|
||||
|
||||
ValidOpts = [hygiene, context, var_context, location, line, file, unquote],
|
||||
ValidOpts = [hygiene, context, var_context, location, line, file, unquote, binding],
|
||||
{ TKV, ST } = translate_opts(Meta, quote, ValidOpts, KV, S),
|
||||
|
||||
Hygiene = case lists:keyfind(hygiene, 1, TKV) of
|
||||
@@ -261,29 +258,34 @@ translate_each({ quote, Meta, [KV, Do] }, S) when is_list(Do) ->
|
||||
false -> DefaultFile
|
||||
end,
|
||||
|
||||
TFile = case File of
|
||||
Scope = case File of
|
||||
keep -> S#elixir_scope.file;
|
||||
_ -> File
|
||||
end,
|
||||
|
||||
WExprs = if
|
||||
is_binary(TFile) ->
|
||||
{ '__scope__', Meta, [[{file,TFile}],[{do,Exprs}]] };
|
||||
QExprs = if
|
||||
is_binary(Scope) ->
|
||||
{ '__scope__', Meta, [[{file,Scope}],[{do,Exprs}]] };
|
||||
File == nil ->
|
||||
Exprs;
|
||||
true ->
|
||||
syntax_error(Meta, S#elixir_scope.file, "invalid args for quote, expected :file to be a binary")
|
||||
end,
|
||||
|
||||
{ Binding, DefaultUnquote } = case lists:keyfind(binding, 1, TKV) of
|
||||
{ binding, B } when is_list(B) -> { B, false };
|
||||
false -> { nil, true }
|
||||
end,
|
||||
|
||||
Unquote = case lists:keyfind(unquote, 1, TKV) of
|
||||
{ unquote, false } -> false;
|
||||
_ -> true
|
||||
{ unquote, Bool } when is_boolean(Bool) -> Bool;
|
||||
false -> DefaultUnquote
|
||||
end,
|
||||
|
||||
Q = #elixir_quote{vars_hygiene=Vars, line=Line, unquote=Unquote,
|
||||
aliases_hygiene=Aliases, imports_hygiene=Imports, context=Context},
|
||||
|
||||
{ TExprs, _TQ, TS } = elixir_quote:erl_quote(WExprs, Q, ST),
|
||||
{ TExprs, _TQ, TS } = elixir_quote:erl_quote(QExprs, Binding, Q, ST),
|
||||
{ TExprs, TS };
|
||||
|
||||
translate_each({ quote, Meta, [_, _] }, S) ->
|
||||
@@ -320,7 +322,7 @@ translate_each({ Kind, Meta, Args }, S) when is_list(Args), (Kind == lc) orelse
|
||||
|
||||
%% Super (exceptionally supports partial application)
|
||||
|
||||
translate_each({ super, Meta, Args } = Original, S) ->
|
||||
translate_each({ super, Meta, Args } = Original, S) when is_list(Args) ->
|
||||
case elixir_partials:handle(Original, S) of
|
||||
error ->
|
||||
assert_no_match_or_guard_scope(Meta, super, S),
|
||||
@@ -331,12 +333,7 @@ translate_each({ super, Meta, Args } = Original, S) ->
|
||||
{ _, Arity } = Function,
|
||||
|
||||
{ TArgs, TS } = if
|
||||
is_atom(Args) andalso Arity == 0 ->
|
||||
elixir_def_overridable:retrieve_args(Meta, Arity, S);
|
||||
is_atom(Args) ->
|
||||
elixir_errors:deprecation(Meta, S#elixir_scope.file, "omitting arguments of super is deprecated, please call super with arguments instead"),
|
||||
elixir_def_overridable:retrieve_args(Meta, Arity, S);
|
||||
not is_atom(Args) andalso length(Args) == Arity ->
|
||||
length(Args) == Arity ->
|
||||
translate_args(Args, S);
|
||||
true ->
|
||||
syntax_error(Meta, S#elixir_scope.file, "super must be called with the same number of arguments as the current function")
|
||||
@@ -356,7 +353,7 @@ translate_each({ 'super?', Meta, [] }, S) ->
|
||||
%% Variables
|
||||
|
||||
translate_each({ '^', Meta, [ { Name, _, Kind } ] }, #elixir_scope{context=match} = S) when is_atom(Name), is_atom(Kind) ->
|
||||
case orddict:find({ Name, Kind }, S#elixir_scope.vars) of
|
||||
case orddict:find({ Name, Kind }, S#elixir_scope.backup_vars) of
|
||||
{ ok, Value } ->
|
||||
{ { var, Meta, Value }, S };
|
||||
error ->
|
||||
@@ -369,7 +366,7 @@ translate_each({ '^', Meta, [ { Name, _, Kind } ] }, S) when is_atom(Name), is_a
|
||||
|
||||
translate_each({ '^', Meta, [ Expr ] }, S) ->
|
||||
syntax_error(Meta, S#elixir_scope.file,
|
||||
"the unary operator ^ can only be used with variables, invalid expression ^~ts", ['Elixir.Macro':to_binary(Expr)]);
|
||||
"the unary operator ^ can only be used with variables, invalid expression ^~ts", ['Elixir.Macro':to_string(Expr)]);
|
||||
|
||||
translate_each({ Name, Meta, Kind }, S) when is_atom(Name), is_atom(Kind) ->
|
||||
elixir_scope:translate_var(Meta, Name, Kind, S, fun() ->
|
||||
@@ -466,7 +463,7 @@ translate_each({ { '.', _, [Expr] }, Meta, Args } = Original, S) ->
|
||||
|
||||
translate_each({ Invalid, Meta, _Args }, S) ->
|
||||
syntax_error(Meta, S#elixir_scope.file, "unexpected parenthesis after ~ts",
|
||||
['Elixir.Macro':to_binary(Invalid)]);
|
||||
['Elixir.Macro':to_string(Invalid)]);
|
||||
|
||||
%% Literals
|
||||
|
||||
@@ -478,10 +475,21 @@ translate_each(Literal, S) ->
|
||||
%% Opts
|
||||
|
||||
translate_opts(Meta, Kind, Allowed, Opts, S) ->
|
||||
{ TOpts, TS } = translate_each(Opts, S),
|
||||
FOpts = elixir_tree_helpers:erl_to_elixir(TOpts),
|
||||
validate_opts(Meta, Kind, Allowed, FOpts, TS),
|
||||
{ FOpts, TS }.
|
||||
{ Expanded, TS } = case literal_opts(Opts) orelse skip_expansion(S#elixir_scope.module) of
|
||||
true -> { Opts, S };
|
||||
false -> 'Elixir.Macro':expand_all(Opts, elixir_scope:to_ex_env({ ?line(Meta), S }), S)
|
||||
end,
|
||||
validate_opts(Meta, Kind, Allowed, Expanded, TS),
|
||||
{ Expanded, TS }.
|
||||
|
||||
skip_expansion('Elixir.Kernel') -> true;
|
||||
skip_expansion('Elixir.Kernel.Typespec') -> true;
|
||||
skip_expansion(_) -> false.
|
||||
|
||||
literal_opts({ X, Y }) -> literal_opts(X) andalso literal_opts(Y);
|
||||
literal_opts(Opts) when is_list(Opts) -> lists:all(fun literal_opts/1, Opts);
|
||||
literal_opts(Opts) when is_atom(Opts); is_number(Opts); is_bitstring(Opts) -> true;
|
||||
literal_opts(_) -> false.
|
||||
|
||||
validate_opts(Meta, Kind, Allowed, Opts, S) when is_list(Opts) ->
|
||||
[begin
|
||||
@@ -555,11 +563,9 @@ no_alias_expansion(Other) ->
|
||||
%% Function
|
||||
|
||||
translate_fn(Meta, Clauses, S) ->
|
||||
Line = ?line(Meta),
|
||||
|
||||
Transformer = fun({ ArgsWithGuards, Expr }, Acc) ->
|
||||
{ Args, Guards } = elixir_clauses:extract_last_guards(ArgsWithGuards),
|
||||
elixir_clauses:assigns_block(Line, fun elixir_translator:translate/2, Args, [Expr], Guards, umergec(S, Acc))
|
||||
Transformer = fun({ ArgsWithGuards, CMeta, Expr }, Acc) ->
|
||||
{ Args, Guards } = elixir_clauses:extract_splat_guards(ArgsWithGuards),
|
||||
elixir_clauses:assigns_block(?line(CMeta), fun elixir_translator:translate/2, Args, [Expr], Guards, umergec(S, Acc))
|
||||
end,
|
||||
|
||||
{ TClauses, NS } = lists:mapfoldl(Transformer, S, Clauses),
|
||||
@@ -567,7 +573,7 @@ translate_fn(Meta, Clauses, S) ->
|
||||
|
||||
case length(lists:usort(Arities)) of
|
||||
1 ->
|
||||
{ { 'fun', Line, { clauses, TClauses } }, umergec(S, NS) };
|
||||
{ { 'fun', ?line(Meta), { clauses, TClauses } }, umergec(S, NS) };
|
||||
_ ->
|
||||
syntax_error(Meta, S#elixir_scope.file, "cannot mix clauses with different arities in function definition")
|
||||
end.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
%% to generate erlang abstract format for basic structures
|
||||
%% as lists, condition clauses, etc.
|
||||
-module(elixir_tree_helpers).
|
||||
-export([elixir_to_erl/1, erl_to_elixir/1, split_last/1,
|
||||
-export([elixir_to_erl/1, split_last/1,
|
||||
cons_to_list/1, list_to_cons/2, list_to_cons/3,
|
||||
convert_to_boolean/5, returns_boolean/1, get_line/1]).
|
||||
-include("elixir.hrl").
|
||||
@@ -54,22 +54,6 @@ elixir_to_erl_cons([H|T], Acc) ->
|
||||
|
||||
elixir_to_erl_cons([], Acc) -> Acc.
|
||||
|
||||
erl_to_elixir({ tuple, _, Args }) ->
|
||||
list_to_tuple([erl_to_elixir(X) || X <- Args]);
|
||||
|
||||
erl_to_elixir({ Kind, _, Value }) when Kind == atom; Kind == integer; Kind == float ->
|
||||
Value;
|
||||
|
||||
erl_to_elixir({ bin, _, [{ bin_element, 0, { string, 0, String }, default, default }] }) ->
|
||||
list_to_binary(String);
|
||||
|
||||
erl_to_elixir({ nil, _ }) -> [];
|
||||
|
||||
erl_to_elixir({ cons, _, Left, Right }) -> [erl_to_elixir(Left)|erl_to_elixir(Right)];
|
||||
|
||||
erl_to_elixir(Other) ->
|
||||
{ '__scope__', [], [[{erl,true}],[{do,Other}]] }.
|
||||
|
||||
%% Others
|
||||
|
||||
returns_boolean({ op, _, Op, _ }) when Op == 'not' -> true;
|
||||
@@ -94,7 +78,7 @@ returns_boolean({ call, _, { remote, _, { atom, _, erlang }, { atom, _, Fun } },
|
||||
returns_boolean({ call, _, { remote, _, { atom, _, erlang }, { atom, _, Fun } }, [_,_,_] }) when
|
||||
Fun == function_exported -> true;
|
||||
|
||||
returns_boolean({ atom, _, Bool }) when Bool == true; Bool == false -> true;
|
||||
returns_boolean({ atom, _, Bool }) when is_boolean(Bool) -> true;
|
||||
|
||||
returns_boolean({ 'case', _, _, Clauses }) ->
|
||||
lists:all(fun
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
clauses(Meta, Clauses, S) ->
|
||||
Catch = elixir_clauses:get_pairs(Meta, 'catch', Clauses, S),
|
||||
Rescue = elixir_clauses:get_pairs(Meta, rescue, Clauses, S),
|
||||
Transformer = fun(X, Acc) -> each_clause(Meta, X, umergec(S, Acc)) end,
|
||||
Transformer = fun(X, Acc) -> each_clause(X, umergec(S, Acc)) end,
|
||||
lists:mapfoldl(Transformer, S, Rescue ++ Catch).
|
||||
|
||||
each_clause(Meta, { 'catch', Raw, Expr }, S) ->
|
||||
{ Args, Guards } = elixir_clauses:extract_last_guards(Raw),
|
||||
each_clause({ 'catch', Meta, Raw, Expr }, S) ->
|
||||
{ Args, Guards } = elixir_clauses:extract_splat_guards(Raw),
|
||||
|
||||
Final = case Args of
|
||||
[X] -> [throw, X, { '_', Meta, nil }];
|
||||
@@ -23,19 +23,19 @@ each_clause(Meta, { 'catch', Raw, Expr }, S) ->
|
||||
Condition = { '{}', Meta, Final },
|
||||
elixir_clauses:assigns_block(?line(Meta), fun elixir_translator:translate_each/2, Condition, [Expr], Guards, S);
|
||||
|
||||
each_clause(Meta, { rescue, [Condition|T], Expr }, S) ->
|
||||
each_clause({ rescue, Meta, [Condition], Expr }, S) ->
|
||||
case normalize_rescue(Meta, Condition, S) of
|
||||
{ Left, Right } ->
|
||||
case Left of
|
||||
{ '_', _, _ } ->
|
||||
{ ClauseVar, CS } = elixir_scope:build_ex_var(?line(Meta), S),
|
||||
{ Clause, _ } = rescue_guards(Meta, ClauseVar, Right, S),
|
||||
each_clause(Meta, { 'catch', [error, Clause|T], Expr }, CS);
|
||||
each_clause({ 'catch', Meta, Clause, Expr }, CS);
|
||||
_ ->
|
||||
{ Clause, Safe } = rescue_guards(Meta, Left, Right, S),
|
||||
case Safe of
|
||||
true ->
|
||||
each_clause(Meta, { 'catch', [error, Clause|T], Expr }, S);
|
||||
each_clause({ 'catch', Meta, Clause, Expr }, S);
|
||||
false ->
|
||||
{ ClauseVar, CS } = elixir_scope:build_ex_var(?line(Meta), S),
|
||||
{ FinalClause, _ } = rescue_guards(Meta, ClauseVar, Right, S),
|
||||
@@ -44,17 +44,17 @@ each_clause(Meta, { rescue, [Condition|T], Expr }, S) ->
|
||||
{ { '.', Meta, ['Elixir.Exception', normalize] }, Meta, [ClauseVar] }
|
||||
] },
|
||||
FinalExpr = prepend_to_block(Meta, Match, Expr),
|
||||
each_clause(Meta, { 'catch', [error, FinalClause|T], FinalExpr }, CS)
|
||||
each_clause({ 'catch', Meta, FinalClause, FinalExpr }, CS)
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
each_clause(Meta, { 'catch', [error, Condition|T], Expr }, S)
|
||||
each_clause({ 'catch', Meta, [error, Condition], Expr }, S)
|
||||
end;
|
||||
|
||||
each_clause(Meta, {rescue,_,_}, S) ->
|
||||
each_clause({rescue,Meta,_,_}, S) ->
|
||||
elixir_errors:syntax_error(Meta, S#elixir_scope.file, "too many arguments given for rescue");
|
||||
|
||||
each_clause(Meta, {Key,_,_}, S) ->
|
||||
each_clause({Key,Meta,_,_}, S) ->
|
||||
elixir_errors:syntax_error(Meta, S#elixir_scope.file, "invalid key ~ts in try", [Key]).
|
||||
|
||||
%% Helpers
|
||||
@@ -79,11 +79,11 @@ normalize_rescue(_, { in, Meta, [Left, Right] }, S) ->
|
||||
_ when is_list(Right) ->
|
||||
is_valid_rescue_list(Right, S) andalso { Left, Right };
|
||||
_ ->
|
||||
Expanded = 'Elixir.Macro':expand(Right, elixir_scope:to_ex_env({ ?line(Meta), S })),
|
||||
case is_valid_rescue_list(Expanded, S) of
|
||||
{ Expanded, ES } = 'Elixir.Macro':expand_all(Right, elixir_scope:to_ex_env({ ?line(Meta), S }), S),
|
||||
case is_valid_rescue_list(Expanded, ES) of
|
||||
true -> { Left, Expanded };
|
||||
false ->
|
||||
elixir_errors:syntax_error(Meta, S#elixir_scope.file, "invalid use of operator \"in\" in rescue inside try")
|
||||
elixir_errors:syntax_error(Meta, ES#elixir_scope.file, "invalid use of operator \"in\" in rescue inside try")
|
||||
end
|
||||
end;
|
||||
|
||||
@@ -97,7 +97,7 @@ normalize_rescue(Meta, Condition, S) ->
|
||||
end.
|
||||
|
||||
%% Convert rescue clauses into guards.
|
||||
rescue_guards(_, Var, nil, _) -> { Var, false };
|
||||
rescue_guards(_, Var, nil, _) -> { [error, Var], false };
|
||||
|
||||
rescue_guards(Meta, Var, Guards, S) ->
|
||||
{ RawElixir, RawErlang } = rescue_each_var(Meta, Var, Guards),
|
||||
@@ -114,7 +114,7 @@ rescue_guards(Meta, Var, Guards, S) ->
|
||||
[join(Meta, 'and', [IsTuple, IsException, OrElse])|Erlang]
|
||||
end,
|
||||
{
|
||||
{ 'when', Meta, [Var, reverse_join(Meta, 'when', Final)] },
|
||||
[{ 'when', Meta, [error, Var, reverse_join(Meta, 'when', Final)] }],
|
||||
Safe
|
||||
}.
|
||||
|
||||
@@ -238,8 +238,8 @@ erlang_rescue_guard_for(Meta, Var, 'Elixir.ErlangError') ->
|
||||
%% Helpers
|
||||
|
||||
format_error({ rescue_no_match, Var, Alias }) ->
|
||||
VarBinary = 'Elixir.Macro':to_binary(Var),
|
||||
AliasBinary = 'Elixir.Macro':to_binary(Alias),
|
||||
VarBinary = 'Elixir.Macro':to_string(Var),
|
||||
AliasBinary = 'Elixir.Macro':to_string(Alias),
|
||||
Message = "rescue clause (~ts = ~ts) can never match, maybe you meant to write: ~ts in [~ts] ?",
|
||||
io_lib:format(Message, [AliasBinary, VarBinary, VarBinary, AliasBinary]).
|
||||
|
||||
@@ -269,4 +269,4 @@ prepend_to_block(_Meta, Expr, { '__block__', Meta, Args }) ->
|
||||
{ '__block__', Meta, [Expr|Args] };
|
||||
|
||||
prepend_to_block(Meta, Expr, Args) ->
|
||||
{ '__block__', Meta, [Expr, Args] }.
|
||||
{ '__block__', Meta, [Expr, Args] }.
|
||||
|
||||
@@ -15,13 +15,14 @@ defmodule AccessTest do
|
||||
|
||||
# Test nil at compilation time does not fail
|
||||
# and that @config[:foo] has proper precedence.
|
||||
@config nil
|
||||
nil = @config[:foo]
|
||||
|
||||
@config [foo: :bar]
|
||||
:bar = @config[:foo]
|
||||
|
||||
@mod :lists
|
||||
[1,2,3] = @mod.flatten([1,[2],3])
|
||||
[1, 2, 3] = @mod.flatten([1, [2], 3])
|
||||
|
||||
test :atom do
|
||||
exception = assert_raise RuntimeError, fn ->
|
||||
|
||||
@@ -16,8 +16,8 @@ defmodule BehaviourTest do
|
||||
test :docs do
|
||||
docs = Enum.sort(Sample.__behaviour__(:docs))
|
||||
assert docs == [
|
||||
{{:bar,2},13,"Bar"},
|
||||
{{:foo,2},10,"Foo"}
|
||||
{{:bar, 2}, 13, "Bar"},
|
||||
{{:foo, 2}, 10, "Foo"}
|
||||
]
|
||||
end
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ defmodule Binary.Chars.ListTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
test :basic do
|
||||
assert to_binary([ 1, "b", 3 ]) == <<1,98,3>>
|
||||
assert to_binary([ 1, "b", 3 ]) == <<1, 98, 3>>
|
||||
end
|
||||
|
||||
test :printable do
|
||||
@@ -76,8 +76,8 @@ defmodule Binary.Chars.ErrorsTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
test :list do
|
||||
assert to_binary([0,1, 2, 3,255]) == <<0,1,2,3,255>>
|
||||
assert to_binary([0,[1, "hello"], 2, [["bye"]]]) == <<0,1,104,101,108,108,111,2,98,121,101>>
|
||||
assert to_binary([0, 1, 2, 3, 255]) == <<0, 1, 2, 3, 255>>
|
||||
assert to_binary([0, [1, "hello"], 2, [["bye"]]]) == <<0, 1, 104, 101, 108, 108, 111, 2, 98, 121, 101>>
|
||||
|
||||
assert_raise Protocol.UndefinedError, "protocol Binary.Chars.List not implemented for [256]. Only iolists are supported", fn ->
|
||||
to_binary([256])
|
||||
@@ -92,7 +92,7 @@ defmodule Binary.Chars.ErrorsTest do
|
||||
|
||||
test :tuple do
|
||||
assert_raise Protocol.UndefinedError, "protocol Binary.Chars not implemented for {1,2,3}", fn ->
|
||||
to_binary({1,2,3})
|
||||
to_binary({1, 2, 3})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -136,6 +136,12 @@ defmodule Binary.Inspect.TupleTest do
|
||||
assert inspect(:something.new) == ":something[a: nil, b: nil]"
|
||||
end
|
||||
|
||||
defrecord Rec, value: 1
|
||||
|
||||
test :two_items_record do
|
||||
assert inspect({ Rec[value: 1], 1 }) == "{Binary.Inspect.TupleTest.Rec[value: 1],1}"
|
||||
end
|
||||
|
||||
test :empty do
|
||||
assert inspect({}) == "{}"
|
||||
end
|
||||
@@ -172,7 +178,7 @@ defmodule Binary.Inspect.ListTest do
|
||||
end
|
||||
|
||||
test :non_printable do
|
||||
assert inspect([{:b, 1},{:a,1}]) == "[b: 1, a: 1]"
|
||||
assert inspect([{:b, 1}, {:a, 1}]) == "[b: 1, a: 1]"
|
||||
end
|
||||
|
||||
test :unproper do
|
||||
|
||||
@@ -53,7 +53,7 @@ bar
|
||||
assert "\123" == "S"
|
||||
assert "\377" == "ÿ"
|
||||
assert "\128" == "\n8"
|
||||
assert "\18" == <<1,?8>>
|
||||
assert "\18" == <<1, ?8>>
|
||||
end
|
||||
|
||||
test :hex do
|
||||
@@ -62,10 +62,10 @@ 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}" == <<196, 143>>
|
||||
assert "\x{10FF}" == <<225, 131, 191>>
|
||||
assert "\x{10FFF}" == <<240, 144, 191, 191>>
|
||||
assert "\x{10FFFF}" == <<244, 143, 191, 191>>
|
||||
end
|
||||
|
||||
test :match do
|
||||
@@ -85,17 +85,17 @@ bar
|
||||
end
|
||||
|
||||
test :bitsyntax_with_utf do
|
||||
assert <<106,111,115,101>> == << "jose" :: utf8 >>
|
||||
assert <<106,111,115,101>> == << 'jose' :: utf8 >>
|
||||
assert <<106, 111, 115, 101>> == << "jose" :: utf8 >>
|
||||
assert <<106, 111, 115, 101>> == << 'jose' :: utf8 >>
|
||||
|
||||
assert <<0,106,0,111,0,115,0,101>> == << "jose" :: utf16 >>
|
||||
assert <<0,106,0,111,0,115,0,101>> == << 'jose' :: utf16 >>
|
||||
assert <<0, 106, 0, 111, 0, 115, 0, 101>> == << "jose" :: utf16 >>
|
||||
assert <<0, 106, 0, 111, 0, 115, 0, 101>> == << 'jose' :: utf16 >>
|
||||
|
||||
assert <<106,0,111,0,115,0,101,0>> == << "jose" :: [utf16, little] >>
|
||||
assert <<106,0,111,0,115,0,101,0>> == << 'jose' :: [utf16, little] >>
|
||||
assert <<106, 0, 111, 0, 115, 0, 101, 0>> == << "jose" :: [utf16, little] >>
|
||||
assert <<106, 0, 111, 0, 115, 0, 101, 0>> == << 'jose' :: [utf16, little] >>
|
||||
|
||||
assert <<0,0,0,106,0,0,0,111,0,0,0,115,0,0,0,101>> == << "jose" :: utf32 >>
|
||||
assert <<0,0,0,106,0,0,0,111,0,0,0,115,0,0,0,101>> == << 'jose' :: utf32 >>
|
||||
assert <<0, 0, 0, 106, 0, 0, 0, 111, 0, 0, 0, 115, 0, 0, 0, 101>> == << "jose" :: utf32 >>
|
||||
assert <<0, 0, 0, 106, 0, 0, 0, 111, 0, 0, 0, 115, 0, 0, 0, 101>> == << 'jose' :: utf32 >>
|
||||
end
|
||||
|
||||
@binary "new "
|
||||
@@ -148,4 +148,4 @@ bar
|
||||
defp is_match?(_, _) do
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -38,11 +38,11 @@ defmodule Bitwise.OperatorsTest do
|
||||
end
|
||||
|
||||
test :band do
|
||||
assert 1 &&& 1 == 1
|
||||
assert (1 &&& 1) == 1
|
||||
end
|
||||
|
||||
test :bor do
|
||||
assert 0 ||| 1 == 1
|
||||
assert (0 ||| 1) == 1
|
||||
end
|
||||
|
||||
test :bxor do
|
||||
@@ -50,10 +50,10 @@ defmodule Bitwise.OperatorsTest do
|
||||
end
|
||||
|
||||
test :bsl do
|
||||
assert 1 <<< 1 == 2
|
||||
assert (1 <<< 1) == 2
|
||||
end
|
||||
|
||||
test :bsr do
|
||||
assert 1 >>> 1 == 0
|
||||
assert (1 >>> 1) == 0
|
||||
end
|
||||
end
|
||||
|
||||
@@ -28,6 +28,14 @@ defmodule CodeTest do
|
||||
assert Code.eval_string("one", [], delegate_locals_to: __MODULE__) == { 1, [] }
|
||||
end
|
||||
|
||||
test :eval_options do
|
||||
assert Code.eval_string("is_atom(:foo) and is_record(1..2, Range) and K.is_list([])", [],
|
||||
functions: [{ Kernel, [is_atom: 1] }],
|
||||
macros: [{ Kernel, [..: 2, and: 2, is_record: 2]}],
|
||||
aliases: [{K, Kernel}],
|
||||
requires: [Kernel]) == { true, [] }
|
||||
end
|
||||
|
||||
test :eval_with_requires do
|
||||
assert Code.eval_string("Kernel.if true, do: :ok", [], requires: [Z, Kernel]) == { :ok, [] }
|
||||
end
|
||||
@@ -39,7 +47,7 @@ defmodule CodeTest do
|
||||
|
||||
test :eval_quoted_with_env do
|
||||
alias :lists, as: MyList
|
||||
assert Code.eval_quoted(quote(do: MyList.flatten [[1,2,3]]), [], __ENV__) == { [1,2,3],[] }
|
||||
assert Code.eval_quoted(quote(do: MyList.flatten [[1, 2, 3]]), [], __ENV__) == { [1, 2, 3],[] }
|
||||
end
|
||||
|
||||
test :require do
|
||||
@@ -65,25 +73,25 @@ defmodule CodeTest do
|
||||
assert :filename.absname(__FILE__) == __FILE__
|
||||
end
|
||||
|
||||
test :string_to_ast do
|
||||
assert Code.string_to_ast("1 + 2") == { :ok, { :+, [line: 1], [1, 2] } }
|
||||
assert { :error, _ } = Code.string_to_ast("a.1")
|
||||
test :string_to_quoted do
|
||||
assert Code.string_to_quoted("1 + 2") == { :ok, { :+, [line: 1], [1, 2] } }
|
||||
assert { :error, _ } = Code.string_to_quoted("a.1")
|
||||
end
|
||||
|
||||
test :string_to_ast_existing_atoms_only do
|
||||
assert :badarg = catch_error(Code.string_to_ast(":thereisnosuchatom", existing_atoms_only: true))
|
||||
assert :badarg = catch_error(Code.string_to_ast!(":thereisnosuchatom", existing_atoms_only: true))
|
||||
test :string_to_quoted_existing_atoms_only do
|
||||
assert :badarg = catch_error(Code.string_to_quoted(":thereisnosuchatom", existing_atoms_only: true))
|
||||
assert :badarg = catch_error(Code.string_to_quoted!(":thereisnosuchatom", existing_atoms_only: true))
|
||||
end
|
||||
|
||||
test :string_to_ast! do
|
||||
assert Code.string_to_ast!("1 + 2") == { :+, [line: 1], [1, 2] }
|
||||
test :string_to_quoted! do
|
||||
assert Code.string_to_quoted!("1 + 2") == { :+, [line: 1], [1, 2] }
|
||||
|
||||
assert_raise SyntaxError, fn ->
|
||||
Code.string_to_ast!("a.1")
|
||||
Code.string_to_quoted!("a.1")
|
||||
end
|
||||
|
||||
assert_raise TokenMissingError, fn ->
|
||||
Code.string_to_ast!("1 +")
|
||||
Code.string_to_quoted!("1 +")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ defmodule EnumTest.List do
|
||||
|
||||
test :empty? do
|
||||
assert Enum.empty?([])
|
||||
refute Enum.empty?([1,2,3])
|
||||
refute Enum.empty?([1, 2, 3])
|
||||
refute Enum.empty?(1..3)
|
||||
end
|
||||
|
||||
@@ -18,89 +18,89 @@ defmodule EnumTest.List do
|
||||
end
|
||||
|
||||
test :count do
|
||||
assert Enum.count([1,2,3]) == 3
|
||||
assert Enum.count([1, 2, 3]) == 3
|
||||
assert Enum.count([]) == 0
|
||||
end
|
||||
|
||||
test :count_fun do
|
||||
assert Enum.count([1,2,3], fn(x) -> rem(x, 2) == 0 end) == 1
|
||||
assert Enum.count([1, 2, 3], fn(x) -> rem(x, 2) == 0 end) == 1
|
||||
assert Enum.count([], fn(x) -> rem(x, 2) == 0 end) == 0
|
||||
end
|
||||
|
||||
test :all? do
|
||||
assert Enum.all?([2,4,6], fn(x) -> rem(x, 2) == 0 end)
|
||||
refute Enum.all?([2,3,4], fn(x) -> rem(x, 2) == 0 end)
|
||||
assert Enum.all?([2, 4, 6], fn(x) -> rem(x, 2) == 0 end)
|
||||
refute Enum.all?([2, 3, 4], fn(x) -> rem(x, 2) == 0 end)
|
||||
|
||||
assert Enum.all?([2,4,6])
|
||||
refute Enum.all?([2,nil,4])
|
||||
assert Enum.all?([2, 4, 6])
|
||||
refute Enum.all?([2, nil, 4])
|
||||
|
||||
assert Enum.all?([])
|
||||
end
|
||||
|
||||
test :any? do
|
||||
refute Enum.any?([2,4,6], fn(x) -> rem(x, 2) == 1 end)
|
||||
assert Enum.any?([2,3,4], fn(x) -> rem(x, 2) == 1 end)
|
||||
refute Enum.any?([2, 4, 6], fn(x) -> rem(x, 2) == 1 end)
|
||||
assert Enum.any?([2, 3, 4], fn(x) -> rem(x, 2) == 1 end)
|
||||
|
||||
refute Enum.any?([false,false,false])
|
||||
assert Enum.any?([false,true,false])
|
||||
refute Enum.any?([false, false, false])
|
||||
assert Enum.any?([false, true, false])
|
||||
|
||||
refute Enum.any?([])
|
||||
end
|
||||
|
||||
test :at do
|
||||
assert Enum.at([2,4,6], 0) == 2
|
||||
assert Enum.at([2,4,6], 2) == 6
|
||||
assert Enum.at([2,4,6], 4) == nil
|
||||
assert Enum.at([2,4,6], 4, :none) == :none
|
||||
assert Enum.at([2, 4, 6], 0) == 2
|
||||
assert Enum.at([2, 4, 6], 2) == 6
|
||||
assert Enum.at([2, 4, 6], 4) == nil
|
||||
assert Enum.at([2, 4, 6], 4, :none) == :none
|
||||
end
|
||||
|
||||
test :fetch! do
|
||||
assert Enum.fetch!([2,4,6], 0) == 2
|
||||
assert Enum.fetch!([2,4,6], 2) == 6
|
||||
assert Enum.fetch!([2, 4, 6], 0) == 2
|
||||
assert Enum.fetch!([2, 4, 6], 2) == 6
|
||||
assert_raise Enum.OutOfBoundsError, fn ->
|
||||
Enum.fetch!([2,4,6], 4)
|
||||
Enum.fetch!([2, 4, 6], 4)
|
||||
end
|
||||
end
|
||||
|
||||
test :drop do
|
||||
assert Enum.drop([1,2,3], 0) == [1,2,3]
|
||||
assert Enum.drop([1,2,3], 1) == [2,3]
|
||||
assert Enum.drop([1,2,3], 2) == [3]
|
||||
assert Enum.drop([1,2,3], 3) == []
|
||||
assert Enum.drop([1,2,3], 4) == []
|
||||
assert Enum.drop([1,2,3], -1) == [3]
|
||||
assert Enum.drop([1, 2, 3], 0) == [1, 2, 3]
|
||||
assert Enum.drop([1, 2, 3], 1) == [2, 3]
|
||||
assert Enum.drop([1, 2, 3], 2) == [3]
|
||||
assert Enum.drop([1, 2, 3], 3) == []
|
||||
assert Enum.drop([1, 2, 3], 4) == []
|
||||
assert Enum.drop([1, 2, 3], -1) == [3]
|
||||
assert Enum.drop([], 3) == []
|
||||
end
|
||||
|
||||
test :drop_while do
|
||||
assert Enum.drop_while([1,2,3,4,3,2,1], fn(x) -> x <= 3 end) == [4,3,2,1]
|
||||
assert Enum.drop_while([1,2,3], fn(_) -> false end) == [1,2,3]
|
||||
assert Enum.drop_while([1,2,3], fn(x) -> x <= 3 end) == []
|
||||
assert Enum.drop_while([1, 2, 3, 4, 3, 2, 1], fn(x) -> x <= 3 end) == [4, 3, 2, 1]
|
||||
assert Enum.drop_while([1, 2, 3], fn(_) -> false end) == [1, 2, 3]
|
||||
assert Enum.drop_while([1, 2, 3], fn(x) -> x <= 3 end) == []
|
||||
assert Enum.drop_while([], fn(_) -> false end) == []
|
||||
end
|
||||
|
||||
test :find do
|
||||
assert Enum.find([2,4,6], fn(x) -> rem(x, 2) == 1 end) == nil
|
||||
assert Enum.find([2,4,6], 0, fn(x) -> rem(x, 2) == 1 end) == 0
|
||||
assert Enum.find([2,3,4], fn(x) -> rem(x, 2) == 1 end) == 3
|
||||
assert Enum.find([2, 4, 6], fn(x) -> rem(x, 2) == 1 end) == nil
|
||||
assert Enum.find([2, 4, 6], 0, fn(x) -> rem(x, 2) == 1 end) == 0
|
||||
assert Enum.find([2, 3, 4], fn(x) -> rem(x, 2) == 1 end) == 3
|
||||
end
|
||||
|
||||
test :find_value do
|
||||
assert Enum.find_value([2,4,6], fn(x) -> rem(x, 2) == 1 end) == nil
|
||||
assert Enum.find_value([2,4,6], 0, fn(x) -> rem(x, 2) == 1 end) == 0
|
||||
assert Enum.find_value([2,3,4], fn(x) -> rem(x, 2) == 1 end)
|
||||
assert Enum.find_value([2, 4, 6], fn(x) -> rem(x, 2) == 1 end) == nil
|
||||
assert Enum.find_value([2, 4, 6], 0, fn(x) -> rem(x, 2) == 1 end) == 0
|
||||
assert Enum.find_value([2, 3, 4], fn(x) -> rem(x, 2) == 1 end)
|
||||
end
|
||||
|
||||
test :find_index do
|
||||
assert Enum.find_index([2,4,6], fn(x) -> rem(x, 2) == 1 end) == nil
|
||||
assert Enum.find_index([2,3,4], fn(x) -> rem(x, 2) == 1 end) == 1
|
||||
assert Enum.find_index([2, 4, 6], fn(x) -> rem(x, 2) == 1 end) == nil
|
||||
assert Enum.find_index([2, 3, 4], fn(x) -> rem(x, 2) == 1 end) == 1
|
||||
end
|
||||
|
||||
test :each do
|
||||
try do
|
||||
assert Enum.each([], fn(x) -> x end) == :ok
|
||||
|
||||
assert Enum.each([1,2,3], fn(x) -> Process.put(:enum_test_each, x * 2) end) == :ok
|
||||
assert Enum.each([1, 2, 3], fn(x) -> Process.put(:enum_test_each, x * 2) end) == :ok
|
||||
assert Process.get(:enum_test_each) == 6
|
||||
|
||||
assert Enum.each([], fn(x, idx) -> {x, idx} end) == :ok
|
||||
@@ -114,59 +114,59 @@ defmodule EnumTest.List do
|
||||
end
|
||||
|
||||
test :fetch do
|
||||
assert Enum.fetch([2,4,6], 0) == { :ok, 2 }
|
||||
assert Enum.fetch([2,4,6], 2) == { :ok, 6 }
|
||||
assert Enum.fetch([2,4,6], 4) == :error
|
||||
assert Enum.fetch([2, 4, 6], 0) == { :ok, 2 }
|
||||
assert Enum.fetch([2, 4, 6], 2) == { :ok, 6 }
|
||||
assert Enum.fetch([2, 4, 6], 4) == :error
|
||||
end
|
||||
|
||||
test :first do
|
||||
assert Enum.first([]) == nil
|
||||
assert Enum.first([1,2,3]) == 1
|
||||
assert Enum.first([1, 2, 3]) == 1
|
||||
end
|
||||
|
||||
test :filter do
|
||||
assert Enum.filter([1,2,3], fn(x) -> rem(x, 2) == 0 end) == [2]
|
||||
assert Enum.filter([2,4,6], fn(x) -> rem(x, 2) == 0 end) == [2,4,6]
|
||||
assert Enum.filter([1, 2, 3], fn(x) -> rem(x, 2) == 0 end) == [2]
|
||||
assert Enum.filter([2, 4, 6], fn(x) -> rem(x, 2) == 0 end) == [2, 4, 6]
|
||||
end
|
||||
|
||||
test :filter_with_match do
|
||||
assert Enum.filter([1,2,3], match?(1, &1)) == [1]
|
||||
assert Enum.filter([1,2,3], match?(x when x < 3, &1)) == [1,2]
|
||||
assert Enum.filter([1,2,3], match?(_, &1)) == [1,2,3]
|
||||
assert Enum.filter([1, 2, 3], match?(1, &1)) == [1]
|
||||
assert Enum.filter([1, 2, 3], match?(x when x < 3, &1)) == [1, 2]
|
||||
assert Enum.filter([1, 2, 3], match?(_, &1)) == [1, 2, 3]
|
||||
end
|
||||
|
||||
test :filter_map do
|
||||
assert Enum.filter_map([1,2,3], fn(x) -> rem(x, 2) == 0 end, &1 * 2) == [4]
|
||||
assert Enum.filter_map([2,4,6], fn(x) -> rem(x, 2) == 0 end, &1 * 2) == [4,8,12]
|
||||
assert Enum.filter_map([1, 2, 3], fn(x) -> rem(x, 2) == 0 end, &1 * 2) == [4]
|
||||
assert Enum.filter_map([2, 4, 6], fn(x) -> rem(x, 2) == 0 end, &1 * 2) == [4, 8, 12]
|
||||
end
|
||||
|
||||
test :reduce do
|
||||
assert Enum.reduce([], 1, fn(x, acc) -> x + acc end) == 1
|
||||
assert Enum.reduce([1,2,3], 1, fn(x, acc) -> x + acc end) == 7
|
||||
assert Enum.reduce([1, 2, 3], 1, fn(x, acc) -> x + acc end) == 7
|
||||
end
|
||||
|
||||
test :join_with_bin do
|
||||
assert Enum.join([], " = ") == ""
|
||||
assert Enum.join([1,2,3], " = ") == "1 = 2 = 3"
|
||||
assert Enum.join([1,"2",3], " = ") == "1 = 2 = 3"
|
||||
assert Enum.join([1,2,3]) == "123"
|
||||
assert Enum.join([1, 2, 3], " = ") == "1 = 2 = 3"
|
||||
assert Enum.join([1, "2", 3], " = ") == "1 = 2 = 3"
|
||||
assert Enum.join([1, 2, 3]) == "123"
|
||||
end
|
||||
|
||||
test :join_with_list do
|
||||
assert Enum.join([], ' = ') == ''
|
||||
assert Enum.join([1,2,3], ' = ') == '1 = 2 = 3'
|
||||
assert Enum.join([1,"2",3], ' = ') == '1 = 2 = 3'
|
||||
assert Enum.join([1, 2, 3], ' = ') == '1 = 2 = 3'
|
||||
assert Enum.join([1, "2", 3], ' = ') == '1 = 2 = 3'
|
||||
end
|
||||
|
||||
test :map_join_with_bin do
|
||||
assert Enum.map_join([], " = ", &1 * 2) == ""
|
||||
assert Enum.map_join([1,2,3], " = ", &1 * 2) == "2 = 4 = 6"
|
||||
assert Enum.map_join([1,2,3], &1 * 2) == "246"
|
||||
assert Enum.map_join([1, 2, 3], " = ", &1 * 2) == "2 = 4 = 6"
|
||||
assert Enum.map_join([1, 2, 3], &1 * 2) == "246"
|
||||
end
|
||||
|
||||
test :map_join_with_list do
|
||||
assert Enum.map_join([], ' = ', &1 * 2) == ''
|
||||
assert Enum.map_join([1,2,3], ' = ', &1 * 2) == '2 = 4 = 6'
|
||||
assert Enum.map_join([1, 2, 3], ' = ', &1 * 2) == '2 = 4 = 6'
|
||||
end
|
||||
|
||||
test :join_empty do
|
||||
@@ -179,7 +179,7 @@ defmodule EnumTest.List do
|
||||
|
||||
test :map do
|
||||
assert Enum.map([], fn x -> x * 2 end) == []
|
||||
assert Enum.map([1,2,3], fn x -> x * 2 end) == [2,4,6]
|
||||
assert Enum.map([1, 2, 3], fn x -> x * 2 end) == [2, 4, 6]
|
||||
|
||||
assert Enum.map([], fn x, idx -> x * idx end) == []
|
||||
assert Enum.map([1, 2, 3], fn _, idx -> idx end) == [0, 1, 2]
|
||||
@@ -187,53 +187,53 @@ defmodule EnumTest.List do
|
||||
|
||||
test :map_reduce do
|
||||
assert Enum.map_reduce([], 1, fn(x, acc) -> { x * 2, x + acc } end) == { [], 1 }
|
||||
assert Enum.map_reduce([1,2,3], 1, fn(x, acc) -> { x * 2, x + acc } end) == { [2,4,6], 7 }
|
||||
assert Enum.map_reduce([1, 2, 3], 1, fn(x, acc) -> { x * 2, x + acc } end) == { [2, 4, 6], 7 }
|
||||
end
|
||||
|
||||
test :partition do
|
||||
assert Enum.partition([1,2,3], fn(x) -> rem(x, 2) == 0 end) == { [2], [1,3] }
|
||||
assert Enum.partition([2,4,6], fn(x) -> rem(x, 2) == 0 end) == { [2,4,6], [] }
|
||||
assert Enum.partition([1, 2, 3], fn(x) -> rem(x, 2) == 0 end) == { [2], [1, 3] }
|
||||
assert Enum.partition([2, 4, 6], fn(x) -> rem(x, 2) == 0 end) == { [2, 4, 6], [] }
|
||||
end
|
||||
|
||||
test :reverse do
|
||||
assert Enum.reverse([]) == []
|
||||
assert Enum.reverse([1,2,3]) == [3,2,1]
|
||||
assert Enum.reverse([1, 2, 3]) == [3, 2, 1]
|
||||
end
|
||||
|
||||
test :sort do
|
||||
assert Enum.sort([5,3,2,4,1]) == [1,2,3,4,5]
|
||||
assert Enum.sort([5,3,2,4,1], &1 > &2) == [5,4,3,2,1]
|
||||
assert Enum.sort([5, 3, 2, 4, 1]) == [1, 2, 3, 4, 5]
|
||||
assert Enum.sort([5, 3, 2, 4, 1], &1 > &2) == [5, 4, 3, 2, 1]
|
||||
end
|
||||
|
||||
test :split do
|
||||
assert Enum.split([1,2,3], 0) == { [], [1,2,3] }
|
||||
assert Enum.split([1,2,3], 1) == { [1], [2,3] }
|
||||
assert Enum.split([1,2,3], 2) == { [1,2], [3] }
|
||||
assert Enum.split([1,2,3], 3) == { [1,2,3], [] }
|
||||
assert Enum.split([1,2,3], 4) == { [1,2,3], [] }
|
||||
assert Enum.split([1, 2, 3], 0) == { [], [1, 2, 3] }
|
||||
assert Enum.split([1, 2, 3], 1) == { [1], [2, 3] }
|
||||
assert Enum.split([1, 2, 3], 2) == { [1, 2], [3] }
|
||||
assert Enum.split([1, 2, 3], 3) == { [1, 2, 3], [] }
|
||||
assert Enum.split([1, 2, 3], 4) == { [1, 2, 3], [] }
|
||||
assert Enum.split([], 3) == { [], [] }
|
||||
assert Enum.split([1,2,3], -1) == { [1,2], [3] }
|
||||
assert Enum.split([1,2,3], -2) == { [1], [2,3] }
|
||||
assert Enum.split([1,2,3], -3) == { [], [1,2,3] }
|
||||
assert Enum.split([1,2,3], -10) == { [], [1,2,3] }
|
||||
assert Enum.split([1, 2, 3], -1) == { [1, 2], [3] }
|
||||
assert Enum.split([1, 2, 3], -2) == { [1], [2, 3] }
|
||||
assert Enum.split([1, 2, 3], -3) == { [], [1, 2, 3] }
|
||||
assert Enum.split([1, 2, 3], -10) == { [], [1, 2, 3] }
|
||||
end
|
||||
|
||||
test :split_while do
|
||||
assert Enum.split_while([1,2,3], fn(_) -> false end) == { [], [1,2,3] }
|
||||
assert Enum.split_while([1,2,3], fn(_) -> true end) == { [1,2,3], [] }
|
||||
assert Enum.split_while([1,2,3], fn(x) -> x > 2 end) == { [], [1,2,3] }
|
||||
assert Enum.split_while([1,2,3], fn(x) -> x > 3 end) == { [], [1,2,3] }
|
||||
assert Enum.split_while([1,2,3], fn(x) -> x < 3 end) == { [1,2], [3] }
|
||||
assert Enum.split_while([1, 2, 3], fn(_) -> false end) == { [], [1, 2, 3] }
|
||||
assert Enum.split_while([1, 2, 3], fn(_) -> true end) == { [1, 2, 3], [] }
|
||||
assert Enum.split_while([1, 2, 3], fn(x) -> x > 2 end) == { [], [1, 2, 3] }
|
||||
assert Enum.split_while([1, 2, 3], fn(x) -> x > 3 end) == { [], [1, 2, 3] }
|
||||
assert Enum.split_while([1, 2, 3], fn(x) -> x < 3 end) == { [1, 2], [3] }
|
||||
assert Enum.split_while([], fn(_) -> true end) == { [], [] }
|
||||
end
|
||||
|
||||
test :take do
|
||||
assert Enum.take([1,2,3], 0) == []
|
||||
assert Enum.take([1,2,3], 1) == [1]
|
||||
assert Enum.take([1,2,3], 2) == [1,2]
|
||||
assert Enum.take([1,2,3], 3) == [1,2,3]
|
||||
assert Enum.take([1,2,3], 4) == [1,2,3]
|
||||
assert Enum.take([1,2,3], -1) == [1,2]
|
||||
assert Enum.take([1, 2, 3], 0) == []
|
||||
assert Enum.take([1, 2, 3], 1) == [1]
|
||||
assert Enum.take([1, 2, 3], 2) == [1, 2]
|
||||
assert Enum.take([1, 2, 3], 3) == [1, 2, 3]
|
||||
assert Enum.take([1, 2, 3], 4) == [1, 2, 3]
|
||||
assert Enum.take([1, 2, 3], -1) == [1, 2]
|
||||
assert Enum.take([], 3) == []
|
||||
end
|
||||
|
||||
@@ -257,9 +257,9 @@ defmodule EnumTest.List do
|
||||
end
|
||||
|
||||
test :take_while do
|
||||
assert Enum.take_while([1,2,3], fn(x) -> x > 3 end) == []
|
||||
assert Enum.take_while([1,2,3], fn(x) -> x <= 1 end) == [1]
|
||||
assert Enum.take_while([1,2,3], fn(x) -> x <= 3 end) == [1,2,3]
|
||||
assert Enum.take_while([1, 2, 3], fn(x) -> x > 3 end) == []
|
||||
assert Enum.take_while([1, 2, 3], fn(x) -> x <= 1 end) == [1]
|
||||
assert Enum.take_while([1, 2, 3], fn(x) -> x <= 3 end) == [1, 2, 3]
|
||||
assert Enum.take_while([], fn(_) -> true end) == []
|
||||
end
|
||||
|
||||
@@ -269,8 +269,8 @@ defmodule EnumTest.List do
|
||||
end
|
||||
|
||||
test :uniq do
|
||||
assert Enum.uniq([1,2,3,2,1]) == [1,2,3]
|
||||
assert Enum.uniq([1,2,3,2,1], fn x -> x end) == [1,2,3]
|
||||
assert Enum.uniq([1, 2, 3, 2, 1]) == [1, 2, 3]
|
||||
assert Enum.uniq([1, 2, 3, 2, 1], fn x -> x end) == [1, 2, 3]
|
||||
end
|
||||
|
||||
test :zip do
|
||||
@@ -284,8 +284,8 @@ defmodule EnumTest.List do
|
||||
|
||||
test :max do
|
||||
assert Enum.max([1]) == 1
|
||||
assert Enum.max([1,2,3]) == 3
|
||||
assert Enum.max([1,[],:a,{}]) == []
|
||||
assert Enum.max([1, 2, 3]) == 3
|
||||
assert Enum.max([1, [], :a, {}]) == []
|
||||
assert_raise Enum.EmptyError, fn ->
|
||||
assert Enum.max([])
|
||||
end
|
||||
@@ -298,8 +298,8 @@ defmodule EnumTest.List do
|
||||
|
||||
test :min do
|
||||
assert Enum.min([1]) == 1
|
||||
assert Enum.min([1,2,3]) == 1
|
||||
assert Enum.min([[],:a,{}]) == :a
|
||||
assert Enum.min([1, 2, 3]) == 1
|
||||
assert Enum.min([[], :a, {}]) == :a
|
||||
assert_raise Enum.EmptyError, fn ->
|
||||
assert Enum.min([])
|
||||
end
|
||||
@@ -368,8 +368,8 @@ defmodule EnumTest.Range do
|
||||
|
||||
test :drop do
|
||||
range = Range.new(first: 1, last: 3)
|
||||
assert Enum.drop(range, 0) == [1,2,3]
|
||||
assert Enum.drop(range, 1) == [2,3]
|
||||
assert Enum.drop(range, 0) == [1, 2, 3]
|
||||
assert Enum.drop(range, 1) == [2, 3]
|
||||
assert Enum.drop(range, 2) == [3]
|
||||
assert Enum.drop(range, 3) == []
|
||||
assert Enum.drop(range, 4) == []
|
||||
@@ -380,14 +380,14 @@ defmodule EnumTest.Range do
|
||||
|
||||
test :drop_while do
|
||||
range = Range.new(first: 0, last: 6)
|
||||
assert Enum.drop_while(range, fn(x) -> x <= 3 end) == [4,5,6]
|
||||
assert Enum.drop_while(range, fn(_) -> false end) == [0,1,2,3,4,5,6]
|
||||
assert Enum.drop_while(range, fn(x) -> x <= 3 end) == [4, 5, 6]
|
||||
assert Enum.drop_while(range, fn(_) -> false end) == [0, 1, 2, 3, 4, 5, 6]
|
||||
|
||||
range = Range.new(first: 0, last: 3)
|
||||
assert Enum.drop_while(range, fn(x) -> x <= 3 end) == []
|
||||
|
||||
range = Range.new(first: 1, last: 0)
|
||||
assert Enum.drop_while(range, fn(_) -> false end) == [1,0]
|
||||
assert Enum.drop_while(range, fn(_) -> false end) == [1, 0]
|
||||
end
|
||||
|
||||
test :find do
|
||||
@@ -449,14 +449,14 @@ defmodule EnumTest.Range do
|
||||
assert Enum.filter(range, fn(x) -> rem(x, 2) == 0 end) == [2]
|
||||
|
||||
range = Range.new(first: 1, last: 6)
|
||||
assert Enum.filter(range, fn(x) -> rem(x, 2) == 0 end) == [2,4,6]
|
||||
assert Enum.filter(range, fn(x) -> rem(x, 2) == 0 end) == [2, 4, 6]
|
||||
end
|
||||
|
||||
test :filter_with_match do
|
||||
range = Range.new(first: 1, last: 3)
|
||||
assert Enum.filter(range, match?(1, &1)) == [1]
|
||||
assert Enum.filter(range, match?(x when x < 3, &1)) == [1,2]
|
||||
assert Enum.filter(range, match?(_, &1)) == [1,2,3]
|
||||
assert Enum.filter(range, match?(x when x < 3, &1)) == [1, 2]
|
||||
assert Enum.filter(range, match?(_, &1)) == [1, 2, 3]
|
||||
end
|
||||
|
||||
test :filter_map do
|
||||
@@ -464,7 +464,7 @@ defmodule EnumTest.Range do
|
||||
assert Enum.filter_map(range, fn(x) -> rem(x, 2) == 0 end, &1 * 2) == [4]
|
||||
|
||||
range = Range.new(first: 2, last: 6)
|
||||
assert Enum.filter_map(range, fn(x) -> rem(x, 2) == 0 end, &1 * 2) == [4,8,12]
|
||||
assert Enum.filter_map(range, fn(x) -> rem(x, 2) == 0 end, &1 * 2) == [4, 8, 12]
|
||||
end
|
||||
|
||||
test :reduce do
|
||||
@@ -511,10 +511,10 @@ defmodule EnumTest.Range do
|
||||
|
||||
test :map do
|
||||
range = Range.new(first: 1, last: 3)
|
||||
assert Enum.map(range, fn x -> x * 2 end) == [2,4,6]
|
||||
assert Enum.map(range, fn x -> x * 2 end) == [2, 4, 6]
|
||||
|
||||
range = Range.new(first: -1, last: -3)
|
||||
assert Enum.map(range, fn x -> x * 2 end) == [-2,-4,-6]
|
||||
assert Enum.map(range, fn x -> x * 2 end) == [-2, -4, -6]
|
||||
end
|
||||
|
||||
test :map_reduce do
|
||||
@@ -522,47 +522,47 @@ defmodule EnumTest.Range do
|
||||
assert Enum.map_reduce(range, 1, fn(x, acc) -> { x * 2, x + acc } end) == { [2, 0], 2 }
|
||||
|
||||
range = Range.new(first: 1, last: 3)
|
||||
assert Enum.map_reduce(range, 1, fn(x, acc) -> { x * 2, x + acc } end) == { [2,4,6], 7 }
|
||||
assert Enum.map_reduce(range, 1, fn(x, acc) -> { x * 2, x + acc } end) == { [2, 4, 6], 7 }
|
||||
end
|
||||
|
||||
test :partition do
|
||||
range = Range.new(first: 1, last: 3)
|
||||
assert Enum.partition(range, fn(x) -> rem(x, 2) == 0 end) == { [2], [1,3] }
|
||||
assert Enum.partition(range, fn(x) -> rem(x, 2) == 0 end) == { [2], [1, 3] }
|
||||
end
|
||||
|
||||
test :sort do
|
||||
assert Enum.sort(Range.new(first: 3, last: 1)) == [1,2,3]
|
||||
assert Enum.sort(Range.new(first: 2, last: 1)) == [1,2]
|
||||
assert Enum.sort(Range.new(first: 3, last: 1)) == [1, 2, 3]
|
||||
assert Enum.sort(Range.new(first: 2, last: 1)) == [1, 2]
|
||||
assert Enum.sort(Range.new(first: 1, last: 1)) == [1]
|
||||
|
||||
assert Enum.sort(Range.new(first: 3, last: 1), &1 > &2) == [3,2,1]
|
||||
assert Enum.sort(Range.new(first: 2, last: 1), &1 > &2) == [2,1]
|
||||
assert Enum.sort(Range.new(first: 3, last: 1), &1 > &2) == [3, 2, 1]
|
||||
assert Enum.sort(Range.new(first: 2, last: 1), &1 > &2) == [2, 1]
|
||||
assert Enum.sort(Range.new(first: 1, last: 1), &1 > &2) == [1]
|
||||
end
|
||||
|
||||
test :split do
|
||||
range = Range.new(first: 1, last: 3)
|
||||
assert Enum.split(range, 0) == { [], [1,2,3] }
|
||||
assert Enum.split(range, 1) == { [1], [2,3] }
|
||||
assert Enum.split(range, 2) == { [1,2], [3] }
|
||||
assert Enum.split(range, 3) == { [1,2,3], [] }
|
||||
assert Enum.split(range, 4) == { [1,2,3], [] }
|
||||
assert Enum.split(range, -1) == { [1,2], [3] }
|
||||
assert Enum.split(range, -2) == { [1], [2,3] }
|
||||
assert Enum.split(range, -3) == { [], [1,2,3] }
|
||||
assert Enum.split(range, -10) == { [], [1,2,3] }
|
||||
assert Enum.split(range, 0) == { [], [1, 2, 3] }
|
||||
assert Enum.split(range, 1) == { [1], [2, 3] }
|
||||
assert Enum.split(range, 2) == { [1, 2], [3] }
|
||||
assert Enum.split(range, 3) == { [1, 2, 3], [] }
|
||||
assert Enum.split(range, 4) == { [1, 2, 3], [] }
|
||||
assert Enum.split(range, -1) == { [1, 2], [3] }
|
||||
assert Enum.split(range, -2) == { [1], [2, 3] }
|
||||
assert Enum.split(range, -3) == { [], [1, 2, 3] }
|
||||
assert Enum.split(range, -10) == { [], [1, 2, 3] }
|
||||
|
||||
range = Range.new(first: 1, last: 0)
|
||||
assert Enum.split(range, 3) == { [1,0], [] }
|
||||
assert Enum.split(range, 3) == { [1, 0], [] }
|
||||
end
|
||||
|
||||
test :split_while do
|
||||
range = Range.new(first: 1, last: 3)
|
||||
assert Enum.split_while(range, fn(_) -> false end) == { [], [1,2,3] }
|
||||
assert Enum.split_while(range, fn(_) -> true end) == { [1,2,3], [] }
|
||||
assert Enum.split_while(range, fn(x) -> x > 2 end) == { [], [1,2,3] }
|
||||
assert Enum.split_while(range, fn(x) -> x > 3 end) == { [], [1,2,3] }
|
||||
assert Enum.split_while(range, fn(x) -> x < 3 end) == { [1,2], [3] }
|
||||
assert Enum.split_while(range, fn(_) -> false end) == { [], [1, 2, 3] }
|
||||
assert Enum.split_while(range, fn(_) -> true end) == { [1, 2, 3], [] }
|
||||
assert Enum.split_while(range, fn(x) -> x > 2 end) == { [], [1, 2, 3] }
|
||||
assert Enum.split_while(range, fn(x) -> x > 3 end) == { [], [1, 2, 3] }
|
||||
assert Enum.split_while(range, fn(x) -> x < 3 end) == { [1, 2], [3] }
|
||||
|
||||
range = Range.new(first: 1, last: 0)
|
||||
assert Enum.split_while(range, fn(_) -> true end) == { [1, 0], [] }
|
||||
@@ -572,25 +572,25 @@ defmodule EnumTest.Range do
|
||||
range = Range.new(first: 1, last: 3)
|
||||
assert Enum.take(range, 0) == []
|
||||
assert Enum.take(range, 1) == [1]
|
||||
assert Enum.take(range, 2) == [1,2]
|
||||
assert Enum.take(range, 3) == [1,2,3]
|
||||
assert Enum.take(range, 4) == [1,2,3]
|
||||
assert Enum.take(range, 2) == [1, 2]
|
||||
assert Enum.take(range, 3) == [1, 2, 3]
|
||||
assert Enum.take(range, 4) == [1, 2, 3]
|
||||
|
||||
range = Range.new(first: 1, last: 0)
|
||||
assert Enum.take(range, 3) == [1,0]
|
||||
assert Enum.take(range, 3) == [1, 0]
|
||||
end
|
||||
|
||||
test :take_while do
|
||||
range = Range.new(first: 1, last: 3)
|
||||
assert Enum.take_while(range, fn(x) -> x > 3 end) == []
|
||||
assert Enum.take_while(range, fn(x) -> x <= 1 end) == [1]
|
||||
assert Enum.take_while(range, fn(x) -> x <= 3 end) == [1,2,3]
|
||||
assert Enum.take_while(range, fn(x) -> x <= 3 end) == [1, 2, 3]
|
||||
assert Enum.take_while([], fn(_) -> true end) == []
|
||||
end
|
||||
|
||||
test :uniq do
|
||||
assert Enum.uniq(1..3) == [1,2,3]
|
||||
assert Enum.uniq(1..3, fn x -> x end) == [1,2,3]
|
||||
assert Enum.uniq(1..3) == [1, 2, 3]
|
||||
assert Enum.uniq(1..3, fn x -> x end) == [1, 2, 3]
|
||||
end
|
||||
|
||||
test :zip do
|
||||
@@ -618,8 +618,8 @@ defmodule EnumTest.Range do
|
||||
|
||||
test :min do
|
||||
assert Enum.min([1]) == 1
|
||||
assert Enum.min([1,2,3]) == 1
|
||||
assert Enum.min([[],:a,{}]) == :a
|
||||
assert Enum.min([1, 2, 3]) == 1
|
||||
assert Enum.min([[], :a, {}]) == :a
|
||||
|
||||
assert Enum.min(1..1, fn(x) -> :math.pow(-2, x) end) == 1
|
||||
assert Enum.min(1..3, fn(x) -> :math.pow(-2, x) end) == 3
|
||||
@@ -629,6 +629,8 @@ end
|
||||
defmodule EnumTest.Others do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
import ExUnit.CaptureIO
|
||||
|
||||
test :reverse do
|
||||
assert Enum.reverse(URI.query_decoder("foo=bar&baz=bat")) ==
|
||||
[{ "baz", "bat" }, { "foo", "bar" }]
|
||||
@@ -637,4 +639,18 @@ defmodule EnumTest.Others do
|
||||
test :count do
|
||||
assert Enum.count(URI.query_decoder("foo=bar&baz=bat")) == 2
|
||||
end
|
||||
|
||||
test :take do
|
||||
# Use IO to simulate side-effects
|
||||
reducible = fn(acc, fun) ->
|
||||
Enum.reduce([1, 2, 3], acc, fn(x, acc) ->
|
||||
IO.puts x
|
||||
fun.(x, acc)
|
||||
end)
|
||||
end
|
||||
|
||||
assert capture_io(fn ->
|
||||
Enum.take(reducible, 1)
|
||||
end) == "1\n"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -42,7 +42,7 @@ defmodule Kernel.ExceptionTest do
|
||||
assert Exception.format_module_fun_arity Foo, nil, 1 == "Foo.nil/1"
|
||||
assert Exception.format_module_fun_arity Foo, :bar, 1 == "Foo.bar/1"
|
||||
assert Exception.format_module_fun_arity Foo, :bar, [] == "Foo.bar()"
|
||||
assert Exception.format_module_fun_arity :foo, :bar, [1,2] == ":foo.bar(1, 2)"
|
||||
assert Exception.format_module_fun_arity :foo, :bar, [1, 2] == ":foo.bar(1, 2)"
|
||||
end
|
||||
|
||||
test :format_module_function_arity_with_special_function_name do
|
||||
|
||||
@@ -1,27 +1,14 @@
|
||||
Code.require_file "test_helper.exs", __DIR__
|
||||
|
||||
defmodule FileTest do
|
||||
import PathHelpers
|
||||
|
||||
use ExUnit.Case
|
||||
|
||||
import PathHelpers
|
||||
import Regex, only: [escape: 1]
|
||||
|
||||
defmodule Cp do
|
||||
use ExUnit.Case
|
||||
|
||||
# cp_r/c is managed in setup/teardown because if it is stored in
|
||||
# the repository, reltool can't build a release
|
||||
setup do
|
||||
src = fixture_path("cp_r")
|
||||
:file.make_symlink 'certainly/invalid', Path.join([src, "c"])
|
||||
:ok
|
||||
end
|
||||
|
||||
teardown do
|
||||
src = fixture_path("cp_r")
|
||||
File.rm Path.join([src, "c"])
|
||||
:ok
|
||||
end
|
||||
|
||||
test :cp_with_src_file_and_dest_file do
|
||||
src = fixture_path("file.txt")
|
||||
dest = tmp_path("sample.txt")
|
||||
@@ -186,13 +173,10 @@ defmodule FileTest do
|
||||
refute File.exists?(tmp_path("tmp/cp_r/b/3.txt"))
|
||||
|
||||
{ :ok, files } = File.cp_r(src, dest)
|
||||
assert length(files) == 8
|
||||
assert length(files) == 7
|
||||
assert tmp_path("tmp/cp_r/a") in files
|
||||
assert tmp_path("tmp/cp_r/c") in files
|
||||
assert tmp_path("tmp/cp_r/a/1.txt") in files
|
||||
|
||||
assert { :ok, 'certainly/invalid' } = :file.read_link(tmp_path("tmp/cp_r/c"))
|
||||
|
||||
assert File.exists?(tmp_path("tmp/cp_r/a/1.txt"))
|
||||
assert File.exists?(tmp_path("tmp/cp_r/a/a/2.txt"))
|
||||
assert File.exists?(tmp_path("tmp/cp_r/b/3.txt"))
|
||||
@@ -213,7 +197,7 @@ defmodule FileTest do
|
||||
refute File.exists?(tmp_path("tmp/b/3.txt"))
|
||||
|
||||
{ :ok, files } = File.cp_r(src, dest)
|
||||
assert length(files) == 8
|
||||
assert length(files) == 7
|
||||
|
||||
assert File.exists?(tmp_path("tmp/a/1.txt"))
|
||||
assert File.exists?(tmp_path("tmp/a/a/2.txt"))
|
||||
@@ -229,7 +213,7 @@ defmodule FileTest do
|
||||
|
||||
try do
|
||||
File.touch!(dest)
|
||||
assert File.cp_r(src, dest) == { :error, :enotdir }
|
||||
assert (File.cp_r(src, dest) |> is_io_error?)
|
||||
after
|
||||
File.rm_rf dest
|
||||
end
|
||||
@@ -245,7 +229,7 @@ defmodule FileTest do
|
||||
refute File.exists?(tmp_path("tmp/cp_r/b/3.txt"))
|
||||
|
||||
{ :ok, files } = File.cp_r(src, dest)
|
||||
assert length(files) == 8
|
||||
assert length(files) == 7
|
||||
|
||||
assert File.exists?(tmp_path("tmp/cp_r/a/1.txt"))
|
||||
assert File.exists?(tmp_path("tmp/cp_r/a/a/2.txt"))
|
||||
@@ -258,7 +242,7 @@ defmodule FileTest do
|
||||
test :cp_r_with_src_unknown do
|
||||
src = fixture_path("unknown")
|
||||
dest = tmp_path("tmp")
|
||||
assert File.cp_r(src, dest) == { :error, :enoent }
|
||||
assert File.cp_r(src, dest) == { :error, :enoent, src }
|
||||
end
|
||||
|
||||
test :cp_r_with_dir_and_file_conflict do
|
||||
@@ -268,7 +252,7 @@ defmodule FileTest do
|
||||
try do
|
||||
File.mkdir(dest)
|
||||
File.write!(Path.join(dest, "a"), "hello")
|
||||
assert File.cp_r(src, dest) == { :error, :enotdir }
|
||||
assert (File.cp_r(src, dest) |> is_io_error?)
|
||||
after
|
||||
File.rm_rf dest
|
||||
end
|
||||
@@ -286,7 +270,7 @@ defmodule FileTest do
|
||||
refute File.exists?(tmp_path("tmp/b/3.txt"))
|
||||
|
||||
{ :ok, files } = File.cp_r(src, dest)
|
||||
assert length(files) == 8
|
||||
assert length(files) == 7
|
||||
assert is_list(hd(files))
|
||||
|
||||
assert File.exists?(tmp_path("tmp/a/1.txt"))
|
||||
@@ -344,7 +328,7 @@ defmodule FileTest do
|
||||
refute File.exists?(tmp_path("tmp/a/a/2.txt"))
|
||||
refute File.exists?(tmp_path("tmp/b/3.txt"))
|
||||
|
||||
assert length(File.cp_r!(src, dest)) == 8
|
||||
assert length(File.cp_r!(src, dest)) == 7
|
||||
|
||||
assert File.exists?(tmp_path("tmp/a/1.txt"))
|
||||
assert File.exists?(tmp_path("tmp/a/a/2.txt"))
|
||||
@@ -357,7 +341,7 @@ defmodule FileTest do
|
||||
test :cp_r_with_src_unknown! do
|
||||
src = fixture_path("unknown")
|
||||
dest = tmp_path("tmp")
|
||||
assert_raise File.CopyError, "could not copy recursively from #{src} to #{dest}: no such file or directory", fn ->
|
||||
assert_raise File.CopyError, "could not copy recursively from #{src} to #{dest}. #{src}: no such file or directory", fn ->
|
||||
File.cp_r!(src, dest)
|
||||
end
|
||||
end
|
||||
@@ -369,13 +353,17 @@ defmodule FileTest do
|
||||
File.Stat[mode: src_mode] = File.stat! src
|
||||
File.Stat[mode: dest_mode] = File.stat! dest
|
||||
assert src_mode == dest_mode
|
||||
# on overwrite
|
||||
File.cp! src, dest, fn(_,_) -> true end
|
||||
|
||||
# On overwrite
|
||||
File.cp! src, dest, fn(_, _) -> true end
|
||||
File.Stat[mode: src_mode] = File.stat! src
|
||||
File.Stat[mode: dest_mode] = File.stat! dest
|
||||
assert src_mode == dest_mode
|
||||
end
|
||||
|
||||
defp is_io_error?(result) do
|
||||
elem(result, 1) in [:enotdir, :eio, :enoent, :eisdir]
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Queries do
|
||||
@@ -525,13 +513,13 @@ defmodule FileTest do
|
||||
|
||||
test :open_utf8_and_charlist do
|
||||
{ :ok, file } = File.open(fixture_path("utf8.txt"), [:charlist, :utf8])
|
||||
assert IO.gets(file, "") == [1056,1091,1089,1089,1082,1080,1081,10]
|
||||
assert IO.gets(file, "") == [1056, 1091, 1089, 1089, 1082, 1080, 1081, 10]
|
||||
assert File.close(file) == :ok
|
||||
end
|
||||
|
||||
test :open_respects_encoding do
|
||||
{ :ok, file } = File.open(fixture_path("utf8.txt"), [{:encoding, :latin1}])
|
||||
assert IO.gets(file, "") == <<195,144,194,160,195,145,194,131,195,145,194,129,195,145,194,129,195,144,194,186,195,144,194,184,195,144,194,185,10>>
|
||||
assert IO.gets(file, "") == <<195, 144, 194, 160, 195, 145, 194, 131, 195, 145, 194, 129, 195, 145, 194, 129, 195, 144, 194, 186, 195, 144, 194, 184, 195, 144, 194, 185, 10>>
|
||||
assert File.close(file) == :ok
|
||||
end
|
||||
|
||||
@@ -586,7 +574,7 @@ defmodule FileTest do
|
||||
fixture = fixture_path("file.txt")
|
||||
invalid = Path.join fixture, "test"
|
||||
assert File.exists?(fixture)
|
||||
assert File.mkdir(invalid) == { :error, :enotdir }
|
||||
assert (File.mkdir(invalid) |> is_io_error?)
|
||||
refute File.exists?(invalid)
|
||||
end
|
||||
|
||||
@@ -605,7 +593,7 @@ defmodule FileTest do
|
||||
fixture = fixture_path("file.txt")
|
||||
invalid = Path.join fixture, "test"
|
||||
assert File.exists?(fixture)
|
||||
assert_raise File.Error, "could not make directory #{invalid}: not a directory", fn ->
|
||||
assert_raise File.Error, %r"^could not make directory #{escape invalid}: (not a directory|no such file or directory)", fn ->
|
||||
File.mkdir!(invalid)
|
||||
end
|
||||
end
|
||||
@@ -667,7 +655,7 @@ defmodule FileTest do
|
||||
test :mkdir_p_with_invalid_path do
|
||||
assert File.exists?(fixture_path("file.txt"))
|
||||
invalid = Path.join fixture_path("file.txt"), "test/foo"
|
||||
assert File.mkdir(invalid) == { :error, :enotdir }
|
||||
assert (File.mkdir(invalid) |> is_io_error?)
|
||||
refute File.exists?(invalid)
|
||||
end
|
||||
|
||||
@@ -686,29 +674,21 @@ defmodule FileTest do
|
||||
fixture = fixture_path("file.txt")
|
||||
invalid = Path.join fixture, "test"
|
||||
assert File.exists?(fixture)
|
||||
assert_raise File.Error, "could not make directory (with -p) #{invalid}: not a directory", fn ->
|
||||
assert_raise File.Error, %r"^could not make directory \(with -p\) #{escape invalid}: (not a directory|no such file or directory)", fn ->
|
||||
File.mkdir_p!(invalid)
|
||||
end
|
||||
end
|
||||
|
||||
defp is_io_error?(result) do
|
||||
{:error,errorcode} = result
|
||||
errorcode in [:enotdir, :eio, :enoent, :eisdir]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
defmodule Rm do
|
||||
use ExUnit.Case
|
||||
|
||||
# cp_r/c is managed in setup/teardown because if it is stored in
|
||||
# the repository, reltool can't build a release
|
||||
setup do
|
||||
src = fixture_path("cp_r")
|
||||
:file.make_symlink 'certainly/invalid', Path.join([src, "c"])
|
||||
:ok
|
||||
end
|
||||
|
||||
teardown do
|
||||
src = fixture_path("cp_r")
|
||||
File.rm Path.join([src, "c"])
|
||||
:ok
|
||||
end
|
||||
|
||||
test :rm_file do
|
||||
fixture = tmp_path("tmp_test.txt")
|
||||
File.write(fixture, "test")
|
||||
@@ -748,7 +728,7 @@ defmodule FileTest do
|
||||
end
|
||||
|
||||
test :rmdir_with_file do
|
||||
assert File.rmdir(fixture_path("file.txt")) == { :error, :enotdir }
|
||||
assert (File.rmdir(fixture_path("file.txt")) |> is_io_error?)
|
||||
end
|
||||
|
||||
test :rmdir! do
|
||||
@@ -761,7 +741,7 @@ defmodule FileTest do
|
||||
|
||||
test :rmdir_with_file! do
|
||||
fixture = fixture_path("file.txt")
|
||||
assert_raise File.Error, "could not remove directory #{fixture}: not a directory", fn ->
|
||||
assert_raise File.Error, %r"^could not remove directory #{escape fixture}: (not a directory|I/O error)", fn ->
|
||||
File.rmdir!(fixture)
|
||||
end
|
||||
end
|
||||
@@ -776,7 +756,7 @@ defmodule FileTest do
|
||||
assert File.exists?(tmp_path("tmp/b/3.txt"))
|
||||
|
||||
{ :ok, files } = File.rm_rf(fixture)
|
||||
assert length(files) == 8
|
||||
assert length(files) == 7
|
||||
assert fixture in files
|
||||
assert tmp_path("tmp/a/1.txt") in files
|
||||
|
||||
@@ -810,7 +790,7 @@ defmodule FileTest do
|
||||
assert File.exists?(tmp_path("tmp/b/3.txt"))
|
||||
|
||||
{ :ok, files } = File.rm_rf(fixture)
|
||||
assert length(files) == 8
|
||||
assert length(files) == 7
|
||||
assert fixture in files
|
||||
assert (tmp_path("tmp/a/1.txt") |> to_char_list) in files
|
||||
|
||||
@@ -833,7 +813,7 @@ defmodule FileTest do
|
||||
|
||||
test :rm_rf_with_invalid do
|
||||
fixture = fixture_path "file.txt/path"
|
||||
assert File.rm_rf(fixture) == { :error, :enotdir }
|
||||
assert File.rm_rf(fixture) == { :ok, [] }
|
||||
end
|
||||
|
||||
test :rm_rf! do
|
||||
@@ -846,7 +826,7 @@ defmodule FileTest do
|
||||
assert File.exists?(tmp_path("tmp/b/3.txt"))
|
||||
|
||||
files = File.rm_rf!(fixture)
|
||||
assert length(files) == 8
|
||||
assert length(files) == 7
|
||||
assert fixture in files
|
||||
assert tmp_path("tmp/a/1.txt") in files
|
||||
|
||||
@@ -858,9 +838,11 @@ defmodule FileTest do
|
||||
|
||||
test :rm_rf_with_invalid! do
|
||||
fixture = fixture_path "file.txt/path"
|
||||
assert_raise File.Error, "could not remove files and directories recursively from #{fixture}: not a directory", fn ->
|
||||
File.rm_rf!(fixture)
|
||||
end
|
||||
assert File.rm_rf!(fixture) == []
|
||||
end
|
||||
|
||||
defp is_io_error?(result) do
|
||||
elem(result, 1) in [:enotdir, :eio, :enoent, :eisdir]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1057,20 +1039,24 @@ defmodule FileTest do
|
||||
end
|
||||
end
|
||||
|
||||
test :cwd_and_cd_with_utf8 do
|
||||
if :file.native_name_encoding == :utf8 do
|
||||
File.cd!(fixture_path("héllò"), fn ->
|
||||
if :file.native_name_encoding == :utf8 do
|
||||
test :cwd_and_cd_with_utf8 do
|
||||
File.mkdir_p(tmp_path("héllò"))
|
||||
|
||||
File.cd!(tmp_path("héllò"), fn ->
|
||||
assert Path.basename(File.cwd!) == "héllò"
|
||||
end)
|
||||
after
|
||||
File.rm_rf tmp_path("héllò")
|
||||
end
|
||||
end
|
||||
|
||||
test :invalid_cd do
|
||||
assert File.cd(fixture_path("file.txt")) == { :error, :enotdir }
|
||||
assert(File.cd(fixture_path("file.txt")) |> is_io_error?)
|
||||
end
|
||||
|
||||
test :invalid_cd! do
|
||||
message = "could not set current working directory to #{fixture_path("file.txt")}: not a directory"
|
||||
message = %r"^could not set current working directory to #{escape fixture_path("file.txt")}: (not a directory|no such file or directory)"
|
||||
assert_raise File.Error, message, fn ->
|
||||
File.cd!(fixture_path("file.txt"))
|
||||
end
|
||||
@@ -1115,7 +1101,7 @@ defmodule FileTest do
|
||||
|
||||
test :touch_with_failure do
|
||||
fixture = fixture_path("file.txt/bar")
|
||||
assert File.touch(fixture) == { :error, :enotdir }
|
||||
assert (File.touch(fixture) |> is_io_error?)
|
||||
end
|
||||
|
||||
test :touch_with_success! do
|
||||
@@ -1124,16 +1110,107 @@ defmodule FileTest do
|
||||
|
||||
test :touch_with_failure! do
|
||||
fixture = fixture_path("file.txt/bar")
|
||||
assert_raise File.Error, "could not touch #{fixture}: not a directory", fn ->
|
||||
assert_raise File.Error, %r"could not touch #{escape fixture}: (not a directory|no such file or directory)", fn ->
|
||||
File.touch!(fixture)
|
||||
end
|
||||
end
|
||||
|
||||
test :chmod_with_success do
|
||||
fixture = tmp_path("tmp_test.txt")
|
||||
|
||||
File.touch(fixture)
|
||||
try do
|
||||
assert File.chmod(fixture, 0100666) == :ok
|
||||
stat = File.stat!(fixture)
|
||||
assert stat.mode == 0100666
|
||||
|
||||
assert File.chmod(fixture, 0100777) == :ok
|
||||
stat = File.stat!(fixture)
|
||||
assert stat.mode == 0100777
|
||||
after
|
||||
File.rm(fixture)
|
||||
end
|
||||
end
|
||||
|
||||
test :chmod_with_success! do
|
||||
fixture = tmp_path("tmp_test.txt")
|
||||
|
||||
File.touch(fixture)
|
||||
try do
|
||||
assert File.chmod!(fixture, 0100666) == :ok
|
||||
stat = File.stat!(fixture)
|
||||
assert stat.mode == 0100666
|
||||
|
||||
assert File.chmod!(fixture, 0100777) == :ok
|
||||
stat = File.stat!(fixture)
|
||||
assert stat.mode == 0100777
|
||||
after
|
||||
File.rm(fixture)
|
||||
end
|
||||
end
|
||||
|
||||
test :chmod_with_failue do
|
||||
fixture = tmp_path("tmp_test.txt")
|
||||
File.rm(fixture)
|
||||
|
||||
assert File.chmod(fixture, 0100777) == {:error,:enoent}
|
||||
end
|
||||
|
||||
test :chmod_with_failue! do
|
||||
fixture = tmp_path("tmp_test.txt")
|
||||
File.rm(fixture)
|
||||
|
||||
message = %r"could not change mode for #{escape fixture}: no such file or directory"
|
||||
assert_raise File.Error, message, fn ->
|
||||
File.chmod!(fixture, 0100777)
|
||||
end
|
||||
end
|
||||
|
||||
test :chgrp_with_failue do
|
||||
fixture = tmp_path("tmp_test.txt")
|
||||
File.rm(fixture)
|
||||
|
||||
assert File.chgrp(fixture, 1) == {:error,:enoent}
|
||||
end
|
||||
|
||||
test :chgrp_with_failue! do
|
||||
fixture = tmp_path("tmp_test.txt")
|
||||
File.rm(fixture)
|
||||
|
||||
message = %r"could not change group for #{escape fixture}: no such file or directory"
|
||||
assert_raise File.Error, message, fn ->
|
||||
File.chgrp!(fixture, 1)
|
||||
end
|
||||
end
|
||||
|
||||
test :chown_with_failue do
|
||||
fixture = tmp_path("tmp_test.txt")
|
||||
File.rm(fixture)
|
||||
|
||||
assert File.chown(fixture, 1) == {:error,:enoent}
|
||||
end
|
||||
|
||||
test :chown_with_failue! do
|
||||
fixture = tmp_path("tmp_test.txt")
|
||||
File.rm(fixture)
|
||||
|
||||
message = %r"could not change owner for #{escape fixture}: no such file or directory"
|
||||
assert_raise File.Error, message, fn ->
|
||||
File.chown!(fixture, 1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
defp last_year do
|
||||
last_year :calendar.local_time
|
||||
end
|
||||
|
||||
defp last_year({ { year, month, day },time }) do
|
||||
defp last_year({ { year, month, day }, time }) do
|
||||
{ { year - 1, month, day }, time }
|
||||
end
|
||||
|
||||
defp is_io_error?(result) do
|
||||
{:error,errorcode} = result
|
||||
errorcode in [:enotdir, :eio, :enoent, :eisdir]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
defmodule WarningsSample do
|
||||
def hello(a), do: a
|
||||
def hello(b), do: b
|
||||
end
|
||||
@@ -28,8 +28,8 @@ defmodule GenEvent.BehaviourTest do
|
||||
|
||||
:gen_event.notify(pid, {:notification, 1})
|
||||
:gen_event.notify(pid, {:notification, 2})
|
||||
|
||||
assert :gen_event.call(pid, MyEventHandler, :notifications) == [1,2]
|
||||
|
||||
assert :gen_event.call(pid, MyEventHandler, :notifications) == [1, 2]
|
||||
assert :gen_event.call(pid, MyEventHandler, :notifications) == []
|
||||
end
|
||||
|
||||
|
||||
@@ -38,6 +38,13 @@ defmodule HashDictTest do
|
||||
assert HashDict.fetch(dict, 16) == :error
|
||||
end
|
||||
|
||||
test :equal? do
|
||||
assert HashDict.equal?(filled_dict(3), filled_dict(3)) == true
|
||||
|
||||
assert HashDict.equal?(HashDict.new([{:a, 1}, {:b, 2}]),
|
||||
HashDict.new([{:a, 2}, {:b, 3}])) == false
|
||||
end
|
||||
|
||||
test :has_key? do
|
||||
dict = filled_dict(8)
|
||||
assert HashDict.has_key? dict, 4
|
||||
@@ -155,7 +162,7 @@ defmodule HashDictTest do
|
||||
assert Enum.member?(dict, { 5, 5 })
|
||||
refute Enum.member?(dict, { 5, 8 })
|
||||
assert Enum.count(dict) == 10
|
||||
assert Enum.map(filled_dict(3), fn({ k, v }) -> k + v end) == [2,4,6]
|
||||
assert Enum.map(filled_dict(3), fn({ k, v }) -> k + v end) == [2, 4, 6]
|
||||
end
|
||||
|
||||
test :access do
|
||||
@@ -258,6 +265,12 @@ defmodule HashDictTest do
|
||||
assert HashDict.size(dict) == 122
|
||||
end
|
||||
|
||||
test :trie_contract do
|
||||
dict = filled_dict(120)
|
||||
dict = Enum.reduce 16..120, dict, fn(x, acc) -> HashDict.delete(acc, x) end
|
||||
assert (Enum.filter 1..120, fn(x) -> HashDict.get(dict, x) == x end) == (Enum.sort 1..15)
|
||||
end
|
||||
|
||||
defp smoke_test(range) do
|
||||
{ dict, _ } = Enum.reduce range, { HashDict.new, 1 }, fn(x, { acc, i }) ->
|
||||
acc = HashDict.put(acc, x, x)
|
||||
|
||||
@@ -11,7 +11,7 @@ defmodule Kernel.AliasTest do
|
||||
|
||||
test :alias_erlang do
|
||||
alias :lists, as: MyList
|
||||
assert MyList.flatten([1,[2],3]) == [1,2,3]
|
||||
assert MyList.flatten([1, [2], 3]) == [1, 2, 3]
|
||||
assert Elixir.MyList.Bar == :"Elixir.MyList.Bar"
|
||||
assert MyList.Bar == :"Elixir.lists.Bar"
|
||||
end
|
||||
@@ -34,7 +34,7 @@ defmodule Kernel.AliasTest do
|
||||
alias ABC, as: List
|
||||
end
|
||||
|
||||
assert List.flatten([1,[2],3]) == [1,2,3]
|
||||
assert List.flatten([1, [2], 3]) == [1, 2, 3]
|
||||
end
|
||||
|
||||
defmodule Elixir do
|
||||
|
||||
@@ -32,14 +32,14 @@ defmodule Kernel.CaseTest do
|
||||
|
||||
test :match_with_in do
|
||||
assert(case 3 do
|
||||
x in [1,2,3] ->
|
||||
x in [1, 2, 3] ->
|
||||
true
|
||||
end)
|
||||
|
||||
assert(case { 3, 3 } do
|
||||
{ x in [1,2,3], y } when y == 2 ->
|
||||
{ x in [1, 2, 3], y } when y == 2 ->
|
||||
false
|
||||
{ x in [1,2,3], y } when y == 1 when y == 3 ->
|
||||
{ x in [1, 2, 3], y } when y == 1 when y == 3 ->
|
||||
true
|
||||
end)
|
||||
end
|
||||
@@ -47,7 +47,7 @@ defmodule Kernel.CaseTest do
|
||||
test :in_operator_outside_case do
|
||||
x = 1
|
||||
y = 4
|
||||
assert x in [1,2,3], "in assertion"
|
||||
assert x in [1, 2, 3], "in assertion"
|
||||
assert not y in [1, 2, 3], "not in assertion"
|
||||
end
|
||||
|
||||
@@ -94,6 +94,6 @@ defmodule Kernel.CaseTest do
|
||||
{x, vx}
|
||||
end
|
||||
|
||||
defp with_in(x in [1,2,3], other) when is_atom(other), do: other
|
||||
defp with_in(x in [1, 2, 3], other) when is_atom(other), do: other
|
||||
defp with_in(_, _), do: false
|
||||
end
|
||||
end
|
||||
|
||||
@@ -80,25 +80,6 @@ defmodule Kernel.CLI.CompileTest do
|
||||
'Compiled #{fixture}\n'
|
||||
assert File.regular?(tmp_path "Elixir.CompileSample.beam")
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Kernel.CLI.ParallelCompilerTest do
|
||||
use ExUnit.Case, async: true
|
||||
import PathHelpers
|
||||
|
||||
test :files do
|
||||
fixtures = [fixture_path("compile_sample.ex")]
|
||||
assert [{ CompileSample, binary }] = Kernel.ParallelCompiler.files fixtures
|
||||
assert is_binary(binary)
|
||||
end
|
||||
|
||||
test :compile_code do
|
||||
output = elixirc('#{fixture_path("parallel_compiler")} -o #{tmp_path}')
|
||||
assert :string.str(output, 'message_from_foo') > 0,
|
||||
"Expected #{inspect output} to contain 'message_from_foo'"
|
||||
assert File.regular?(tmp_path "Elixir.Foo.beam")
|
||||
assert File.regular?(tmp_path "Elixir.Bar.beam")
|
||||
end
|
||||
|
||||
test :possible_deadlock do
|
||||
output = elixirc('#{fixture_path("parallel_deadlock")} -o #{tmp_path}')
|
||||
@@ -108,3 +89,33 @@ defmodule Kernel.CLI.ParallelCompilerTest do
|
||||
assert :string.str(output, bar) > 0, "expected bar.ex to miss module Foo"
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Kernel.CLI.ParallelCompilerTest do
|
||||
use ExUnit.Case
|
||||
import PathHelpers
|
||||
import ExUnit.CaptureIO
|
||||
|
||||
test :files do
|
||||
fixtures = [fixture_path("parallel_compiler/bar.ex"), fixture_path("parallel_compiler/foo.ex")]
|
||||
assert capture_io(fn ->
|
||||
assert [{ Bar, bar }, { Foo, _foo }] = Kernel.ParallelCompiler.files fixtures
|
||||
assert is_binary(bar)
|
||||
end) =~ "message_from_foo"
|
||||
end
|
||||
|
||||
test :warnings_as_errors do
|
||||
warnings_as_errors = Code.compiler_options[:warnings_as_errors]
|
||||
|
||||
try do
|
||||
Code.compiler_options(warnings_as_errors: true)
|
||||
|
||||
assert_raise Kernel.CompilationError, fn ->
|
||||
capture_io :stderr, fn ->
|
||||
Kernel.ParallelCompiler.files [fixture_path("warnings_sample.ex")]
|
||||
end
|
||||
end
|
||||
after
|
||||
Code.compiler_options(warnings_as_errors: warnings_as_errors)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,35 +4,35 @@ defmodule Kernel.ComprehensionTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
test :list_comprehensions do
|
||||
assert [4] == lc x inlist [1,2,3], rem(x, 2) == 0, do: x * 2
|
||||
assert [4] == lc x inlist [1, 2, 3], rem(x, 2) == 0, do: x * 2
|
||||
end
|
||||
|
||||
test :list_comprehensions_with_nil do
|
||||
assert [] == lc x inlist [1,2,3], nilly, do: x * 2
|
||||
assert [] == lc x inlist [1, 2, 3], nilly, do: x * 2
|
||||
end
|
||||
|
||||
test :list_comprehensions_with_truthy_object do
|
||||
assert [2,4,6] == lc x inlist [1,2,3], 1, do: x * 2
|
||||
assert [2, 4, 6] == lc x inlist [1, 2, 3], 1, do: x * 2
|
||||
end
|
||||
|
||||
test :list_comprehensions_with_inlist_of_bins do
|
||||
assert [2,4,6] == lc <<x>> inlist [<<1>>,<<2>>,<<3>>], do: x * 2
|
||||
assert [2, 4, 6] == lc <<x>> inlist [<<1>>, <<2>>, <<3>>], do: x * 2
|
||||
end
|
||||
|
||||
test :list_comprehensions_with_implicit_inbits do
|
||||
assert [2,4,6] == lc <<x>> inbits <<1,2,3>>, do: x * 2
|
||||
assert [2, 4, 6] == lc <<x>> inbits <<1, 2, 3>>, do: x * 2
|
||||
end
|
||||
|
||||
test :list_comprehensions_with_two_generators do
|
||||
assert [4, 5, 6, 8, 10, 12, 12, 15, 18] == lc x inlist [1,2,3], y inlist [4,5,6], do: x * y
|
||||
assert [4, 5, 6, 8, 10, 12, 12, 15, 18] == lc x inlist [1, 2, 3], y inlist [4, 5, 6], do: x * y
|
||||
end
|
||||
|
||||
test :list_comprehension_multiline do
|
||||
result = lc x inlist [1,2,3] do
|
||||
result = lc x inlist [1, 2, 3] do
|
||||
x * 2
|
||||
end
|
||||
|
||||
assert [2,4,6] == result
|
||||
assert [2, 4, 6] == result
|
||||
end
|
||||
|
||||
test :generator_precedence do
|
||||
@@ -40,35 +40,35 @@ defmodule Kernel.ComprehensionTest do
|
||||
end
|
||||
|
||||
test :bit_comprehensions do
|
||||
assert <<4>> == bc x inlist [1,2,3], rem(x, 2) == 0, do: <<x * 2>>
|
||||
assert <<4>> == bc x inlist [1, 2, 3], rem(x, 2) == 0, do: <<x * 2>>
|
||||
end
|
||||
|
||||
test :bit_comprehensions_with_nil do
|
||||
assert <<>> == bc x inlist [1,2,3], nilly, do: <<x * 2>>
|
||||
assert <<>> == bc x inlist [1, 2, 3], nilly, do: <<x * 2>>
|
||||
end
|
||||
|
||||
test :bit_comprehensions_with_truthy_object do
|
||||
assert <<2,4,6>> == bc x inlist [1,2,3], 1, do: <<x * 2>>
|
||||
assert <<2, 4, 6>> == bc x inlist [1, 2, 3], 1, do: <<x * 2>>
|
||||
end
|
||||
|
||||
test :bit_comprehensions_with_inlist_of_bins do
|
||||
assert <<2,4,6>> == bc <<x>> inlist [<<1>>,<<2>>,<<3>>], do: <<x * 2>>
|
||||
assert <<2, 4, 6>> == bc <<x>> inlist [<<1>>, <<2>>, <<3>>], do: <<x * 2>>
|
||||
end
|
||||
|
||||
test :bit_comprehensions_with_explicit_inbits do
|
||||
assert <<2,4,6>> == bc <<x>> inbits <<1,2,3>>, do: <<x * 2>>
|
||||
assert <<2, 4, 6>> == bc <<x>> inbits <<1, 2, 3>>, do: <<x * 2>>
|
||||
end
|
||||
|
||||
test :bit_comprehensions_with_two_generators do
|
||||
assert <<4, 5, 6, 8, 10, 12, 12, 15, 18>> == bc x inlist [1,2,3], y inlist [4,5,6], do: <<x*y>>
|
||||
assert <<4, 5, 6, 8, 10, 12, 12, 15, 18>> == bc x inlist [1, 2, 3], y inlist [4, 5, 6], do: <<x*y>>
|
||||
end
|
||||
|
||||
test :bit_comprehension_multiline do
|
||||
result = bc x inlist [1,2,3] do
|
||||
result = bc x inlist [1, 2, 3] do
|
||||
<<x * 2>>
|
||||
end
|
||||
|
||||
assert <<2,4,6>> == result
|
||||
assert <<2, 4, 6>> == result
|
||||
end
|
||||
|
||||
defp nilly, do: nil
|
||||
|
||||
@@ -29,7 +29,7 @@ defmodule Kernel.ClausesTest do
|
||||
assert with_in(0, :it_fails) == false
|
||||
end
|
||||
|
||||
defp with_in(x in [1,2,3], other) when is_atom(other), do: other
|
||||
defp with_in(x in [1, 2, 3], other) when is_atom(other), do: other
|
||||
defp with_in(_, _), do: false
|
||||
|
||||
test :clauses_without_implementation_can_have_default_args do
|
||||
@@ -39,4 +39,4 @@ defmodule Kernel.ClausesTest do
|
||||
|
||||
defp a_number(x // 0)
|
||||
defp a_number(x), do: x + 13
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,8 +10,8 @@ defmodule Kernel.DocTest do
|
||||
Code.load_file path
|
||||
|
||||
expected = [
|
||||
{{:example,1},5,:def,[{:var,[line: 6],nil}],"Some example"},
|
||||
{{:nodoc,1},8,:def,[{:"//",[line: 8],[{:var,[line: 8],nil},0]}],nil}
|
||||
{{:example, 1}, 5, :def, [{:var, [line: 6], nil}], "Some example"},
|
||||
{{:nodoc, 1}, 8, :def, [{:"//", [line: 8], [{:var, [line: 8], nil}, 0]}], nil}
|
||||
]
|
||||
|
||||
assert CompiledWithDocs.__info__(:docs) == expected
|
||||
|
||||
@@ -12,320 +12,535 @@ defmodule Kernel.ErrorsTest do
|
||||
defrecord Config, integer: 0
|
||||
|
||||
test :invalid_token do
|
||||
assert_compile_fail SyntaxError, "nofile:1: invalid token: \end", '\end\nlol\nbarbecue'
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: invalid token: \end",
|
||||
'\end\nlol\nbarbecue'
|
||||
end
|
||||
|
||||
test :sigil_terminator do
|
||||
assert_compile_fail TokenMissingError, "nofile:3: missing terminator: \" (for sigil %r\" starting at line 1)", '%r"foo\n\n'
|
||||
assert_compile_fail TokenMissingError, "nofile:3: missing terminator: } (for sigil %r{ starting at line 1)", '%r{foo\n\n'
|
||||
assert_compile_fail TokenMissingError,
|
||||
"nofile:3: missing terminator: \" (for sigil %r\" starting at line 1)",
|
||||
'%r"foo\n\n'
|
||||
|
||||
assert_compile_fail TokenMissingError,
|
||||
"nofile:3: missing terminator: } (for sigil %r{ starting at line 1)",
|
||||
'%r{foo\n\n'
|
||||
end
|
||||
|
||||
test :dot_terminator do
|
||||
assert_compile_fail TokenMissingError, "nofile:1: missing terminator: \" (for function name starting at line 1)", 'foo."bar'
|
||||
assert_compile_fail TokenMissingError,
|
||||
"nofile:1: missing terminator: \" (for function name starting at line 1)",
|
||||
'foo."bar'
|
||||
end
|
||||
|
||||
test :string_terminator do
|
||||
assert_compile_fail TokenMissingError, "nofile:1: missing terminator: \" (for string starting at line 1)", '"bar'
|
||||
assert_compile_fail TokenMissingError,
|
||||
"nofile:1: missing terminator: \" (for string starting at line 1)",
|
||||
'"bar'
|
||||
end
|
||||
|
||||
test :heredoc_terminator do
|
||||
assert_compile_fail TokenMissingError, "nofile:2: missing terminator: \"\"\" (for heredoc starting at line 1)", '"""\nbar'
|
||||
assert_compile_fail TokenMissingError,
|
||||
"nofile:2: missing terminator: \"\"\" (for heredoc starting at line 1)",
|
||||
'"""\nbar'
|
||||
end
|
||||
|
||||
test :invalid_partial do
|
||||
assert_compile_fail SyntaxError, "nofile:1: partial variable &2 cannot be defined without &1", '&2 + 3'
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: partial variable &2 cannot be defined without &1",
|
||||
'&2 + 3'
|
||||
end
|
||||
|
||||
test :unexpected_end do
|
||||
assert_compile_fail SyntaxError, "nofile:1: unexpected token: end", '1 end'
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: unexpected token: end",
|
||||
'1 end'
|
||||
end
|
||||
|
||||
test :syntax_error do
|
||||
assert_compile_fail SyntaxError, "nofile:1: syntax error before: '.'", '+.foo'
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: syntax error before: '.'",
|
||||
'+.foo'
|
||||
end
|
||||
|
||||
test :syntax_error_on_parens_call do
|
||||
assert_compile_fail SyntaxError, "nofile:1: invalid comma inside parenthesis. If you are making a function call, " <>
|
||||
"do not insert spaces in between the function name and the opening parentheses. " <>
|
||||
"Syntax error before: )", 'foo (hello, world)'
|
||||
"do not insert spaces in between the function name and the opening parentheses. " <>
|
||||
"Syntax error before: )", 'foo (hello, world)'
|
||||
end
|
||||
|
||||
test :syntax_error_with_no_token do
|
||||
assert_compile_fail TokenMissingError, "nofile:1: missing terminator: ) (for \"(\" starting at line 1)", 'case 1 ('
|
||||
assert_compile_fail TokenMissingError,
|
||||
"nofile:1: missing terminator: ) (for \"(\" starting at line 1)",
|
||||
'case 1 ('
|
||||
end
|
||||
|
||||
test :clause_with_defaults do
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:3: def hello/1 has default values and multiple clauses, " <>
|
||||
"use a separate clause for declaring defaults",
|
||||
'''
|
||||
defmodule ErrorsTest do
|
||||
def hello(arg // 0), do: nil
|
||||
def hello(arg // 1), do: nil
|
||||
end
|
||||
'''
|
||||
end
|
||||
|
||||
test :different_defs_with_defaults do
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:3: def hello/3 defaults conflicts with def hello/2",
|
||||
'''
|
||||
defmodule ErrorsTest do
|
||||
def hello(a, b // nil), do: a + b
|
||||
def hello(a, b // nil, c // nil), do: a + b + c
|
||||
end
|
||||
'''
|
||||
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:3: def hello/2 conflicts with defaults from def hello/3",
|
||||
'''
|
||||
defmodule ErrorsTest do
|
||||
def hello(a, b // nil, c // nil), do: a + b + c
|
||||
def hello(a, b // nil), do: a + b
|
||||
end
|
||||
'''
|
||||
end
|
||||
|
||||
test :bad_form do
|
||||
assert_compile_fail CompileError, "nofile:2: function bar/0 undefined", 'defmodule Foo do\ndef foo, do: bar\nend'
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:2: function bar/0 undefined",
|
||||
'''
|
||||
defmodule ErrorsTest do
|
||||
def foo, do: bar
|
||||
end
|
||||
'''
|
||||
end
|
||||
|
||||
test :unbound_var do
|
||||
assert_compile_fail SyntaxError, "nofile:1: unbound variable ^x", '^x = 1'
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: unbound variable ^x",
|
||||
'^x = 1'
|
||||
end
|
||||
|
||||
test :unbound_not_match do
|
||||
assert_compile_fail SyntaxError, "nofile:1: cannot use ^x outside of match clauses", '^x'
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: cannot use ^x outside of match clauses",
|
||||
'^x'
|
||||
end
|
||||
|
||||
test :unbound_expr do
|
||||
assert_compile_fail SyntaxError, "nofile:1: the unary operator ^ can only be used with variables, invalid expression ^x(1)", '^x(1)'
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: the unary operator ^ can only be used with variables, invalid expression ^x(1)",
|
||||
'^x(1)'
|
||||
end
|
||||
|
||||
test :name_for_defmodule do
|
||||
assert_compile_fail CompileError, "nofile:1: invalid module name: 3", 'defmodule 1 + 2, do: 3'
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:1: invalid module name: 3",
|
||||
'defmodule 1 + 2, do: 3'
|
||||
end
|
||||
|
||||
test :invalid_scope_for_function do
|
||||
assert_compile_fail SyntaxError, "nofile:1: cannot invoke def outside module", 'def Foo, do: 2'
|
||||
assert_compile_fail SyntaxError, "nofile:3: cannot invoke defmacro outside module", '\n\ndefmacro Foo, do: 2'
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: cannot invoke def outside module",
|
||||
'def Foo, do: 2'
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:3: cannot invoke defmacro outside module",
|
||||
'\n\ndefmacro Foo, do: 2'
|
||||
end
|
||||
|
||||
test :invalid_unquote do
|
||||
assert_compile_fail SyntaxError, "nofile:1: unquote called outside quote", 'unquote 1'
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: unquote called outside quote",
|
||||
'unquote 1'
|
||||
end
|
||||
|
||||
test :invalid_quote_args do
|
||||
assert_compile_fail SyntaxError, "nofile:1: invalid args for quote", 'quote 1'
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: invalid args for quote",
|
||||
'quote 1'
|
||||
end
|
||||
|
||||
test :invalid_parens do
|
||||
assert_compile_fail SyntaxError, "nofile:1: unexpected parenthesis after foo(1)", 'foo(1)(2)'
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: unexpected parenthesis after foo(1)",
|
||||
'foo(1)(2)'
|
||||
end
|
||||
|
||||
test :invalid_fn_args do
|
||||
assert_compile_fail TokenMissingError, "nofile:1: missing terminator: end (for \"fn\" starting at line 1)", 'fn 1'
|
||||
assert_compile_fail TokenMissingError,
|
||||
"nofile:1: missing terminator: end (for \"fn\" starting at line 1)",
|
||||
'fn 1'
|
||||
end
|
||||
|
||||
test :macro_conflict do
|
||||
assert_compile_fail CompileError, "nofile:1: imported Kernel.defrecord/2 conflicts with local function",
|
||||
'defmodule Foo do\ndefrecord(Kernel.ErrorsTest.MacroConflict, a: 1)\ndef defrecord(_, _), do: OMG\nend'
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:1: imported Kernel.defrecord/2 conflicts with local function",
|
||||
'''
|
||||
defmodule ErrorsTest do
|
||||
defrecord(Kernel.ErrorsTest.MacroConflict, a: 1)
|
||||
def defrecord(_, _), do: OMG
|
||||
end
|
||||
'''
|
||||
end
|
||||
|
||||
test :macro_with_undefined_local do
|
||||
assert_compile_fail UndefinedFunctionError, "undefined function: Foo.unknown/1",
|
||||
'defmodule Foo do\ndefmacrop bar, do: unknown(1)\ndef baz, do: bar()\nend'
|
||||
assert_compile_fail UndefinedFunctionError,
|
||||
"undefined function: ErrorsTest.unknown/1",
|
||||
'''
|
||||
defmodule ErrorsTest do
|
||||
defmacrop bar, do: unknown(1)
|
||||
def baz, do: bar()
|
||||
end
|
||||
'''
|
||||
end
|
||||
|
||||
test :private_macro do
|
||||
assert_compile_fail UndefinedFunctionError, "undefined function: Foo.foo/0",
|
||||
'defmodule Foo do\ndefmacrop foo, do: 1\ndefmacro bar, do: __MODULE__.foo\ndefmacro baz, do: bar\nend'
|
||||
assert_compile_fail UndefinedFunctionError,
|
||||
"undefined function: ErrorsTest.foo/0",
|
||||
'''
|
||||
defmodule ErrorsTest do
|
||||
defmacrop foo, do: 1
|
||||
defmacro bar, do: __MODULE__.foo
|
||||
defmacro baz, do: bar
|
||||
end
|
||||
'''
|
||||
end
|
||||
|
||||
test :function_definition_with_alias do
|
||||
assert_compile_fail SyntaxError, "nofile:1: function names should start with lowercase characters or underscore, invalid name Bar",
|
||||
'defmodule Foo do def Bar do :baz end\nend\n'
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:2: function names should start with lowercase characters or underscore, invalid name Bar",
|
||||
'''
|
||||
defmodule ErrorsTest do
|
||||
def Bar do
|
||||
:baz
|
||||
end
|
||||
end
|
||||
'''
|
||||
end
|
||||
|
||||
test :function_import_conflict do
|
||||
assert_compile_fail CompileError, "nofile:2: function exit/1 imported from both :erlang and Kernel, call is ambiguous",
|
||||
'defmodule Foo do import :erlang\n def foo, do: exit(:test)\nend'
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:3: function exit/1 imported from both :erlang and Kernel, call is ambiguous",
|
||||
'''
|
||||
defmodule ErrorsTest do
|
||||
import :erlang
|
||||
def foo, do: exit(:test)
|
||||
end
|
||||
'''
|
||||
end
|
||||
|
||||
test :import_invalid_macro do
|
||||
assert_compile_fail CompileError, "nofile:2: cannot import Kernel.invalid/1 because it doesn't exist",
|
||||
'defmodule Foo do\nimport Kernel, only: [invalid: 1]\nend'
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:1: cannot import Kernel.invalid/1 because it doesn't exist",
|
||||
'import Kernel, only: [invalid: 1]'
|
||||
end
|
||||
|
||||
test :unrequired_macro do
|
||||
assert_compile_fail CompileError, "nofile:2: tried to invoke macro Kernel.ErrorsTest.UnproperMacro.unproper/1 but module was not required. Required: Kernel, Kernel.Typespec, Record",
|
||||
'defmodule Foo do\nKernel.ErrorsTest.UnproperMacro.unproper([])\nend'
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:2: tried to invoke macro Kernel.ErrorsTest.UnproperMacro.unproper/1 " <>
|
||||
"but module was not required. Required: Kernel, Kernel.Typespec, Record",
|
||||
'''
|
||||
defmodule ErrorsTest do
|
||||
Kernel.ErrorsTest.UnproperMacro.unproper([])
|
||||
end
|
||||
'''
|
||||
end
|
||||
|
||||
test :def_defmacro_clause_change do
|
||||
assert_compile_fail CompileError, "nofile:3: defmacro foo/1 already defined as def",
|
||||
'defmodule Foo do\ndef foo(1), do: 1\ndefmacro foo(x), do: x\nend'
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:3: defmacro foo/1 already defined as def",
|
||||
'''
|
||||
defmodule ErrorsTest do
|
||||
def foo(1), do: 1
|
||||
defmacro foo(x), do: x
|
||||
end
|
||||
'''
|
||||
end
|
||||
|
||||
test :internal_function_overridden do
|
||||
assert_compile_fail CompileError, "nofile:1: function __info__/1 is internal and should not be overridden",
|
||||
'defmodule Foo do\ndef __info__(_), do: []\nend'
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:1: function __info__/1 is internal and should not be overridden",
|
||||
'''
|
||||
defmodule ErrorsTest do
|
||||
def __info__(_), do: []
|
||||
end
|
||||
'''
|
||||
end
|
||||
|
||||
test :no_macros do
|
||||
assert_compile_fail CompileError, "nofile:2: could not load macros from module :lists",
|
||||
'defmodule Foo do\nimport :macros, :lists\nend'
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:2: could not load macros from module :lists",
|
||||
'''
|
||||
defmodule ErrorsTest do
|
||||
import :macros, :lists
|
||||
end
|
||||
'''
|
||||
end
|
||||
|
||||
test :unloaded_module do
|
||||
assert_compile_fail CompileError, "nofile:1: module Certainly.Doesnt.Exist is not loaded and could not be found",
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:1: module Certainly.Doesnt.Exist is not loaded and could not be found",
|
||||
'import Certainly.Doesnt.Exist'
|
||||
end
|
||||
|
||||
test :scheduled_module do
|
||||
assert_compile_fail CompileError, "nofile:1: module Foo.Hygiene is not loaded but was defined. This happens because you are trying to use a module in the same context it is defined. Try defining the module outside the context that requires it.",
|
||||
'defmodule Foo do; defmodule Hygiene do; end; import Foo.Hygiene; end'
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:4: module ErrorsTest.Hygiene is not loaded but was defined. " <>
|
||||
"This happens because you are trying to use a module in the same context it is defined. " <>
|
||||
"Try defining the module outside the context that requires it.",
|
||||
'''
|
||||
defmodule ErrorsTest do
|
||||
defmodule Hygiene do
|
||||
end
|
||||
import ErrorsTest.Hygiene
|
||||
end
|
||||
'''
|
||||
end
|
||||
|
||||
test :already_compiled_module do
|
||||
assert_compile_fail ArgumentError, "could not call eval_quoted on module Record because it was already compiled",
|
||||
assert_compile_fail ArgumentError,
|
||||
"could not call eval_quoted on module Record " <>
|
||||
"because it was already compiled",
|
||||
'Module.eval_quoted Record, quote(do: 1), [], file: __FILE__'
|
||||
end
|
||||
|
||||
test :interpolation_error do
|
||||
assert_compile_fail TokenMissingError, "nofile:1: missing terminator: end (for \"do\" starting at line 1)", '"foo\#{case 1 do )}bar"'
|
||||
assert_compile_fail TokenMissingError,
|
||||
"nofile:1: missing terminator: end (for \"do\" starting at line 1)",
|
||||
'"foo\#{case 1 do )}bar"'
|
||||
end
|
||||
|
||||
test :cant_define_local_due_to_in_erlang_macros_conflict do
|
||||
assert_compile_fail CompileError, "nofile:1: cannot define local quote/1 because it conflicts with Elixir special forms",
|
||||
'defmodule Foo do\ndef quote(x), do: x\ndef bar(x), do: quote(do: x)\nend'
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:1: cannot define local quote/1 because it conflicts with Elixir special forms",
|
||||
'''
|
||||
defmodule ErrorsTest do
|
||||
def quote(x), do: x
|
||||
def bar(x), do: quote(do: x)
|
||||
end
|
||||
'''
|
||||
end
|
||||
|
||||
test :in_definition_module do
|
||||
assert_compile_fail CompileError, "nofile:1: cannot define module Foo because it is currently being defined",
|
||||
'defmodule Foo, do: (defmodule Elixir.Foo, do: true)'
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:1: cannot define module ErrorsTest because it is currently being defined",
|
||||
'defmodule ErrorsTest, do: (defmodule Elixir.ErrorsTest, do: true)'
|
||||
end
|
||||
|
||||
test :invalid_definition do
|
||||
assert_compile_fail SyntaxError, "nofile:1: invalid syntax in def 1.(hello)",
|
||||
'defmodule Foo, do: (def 1.(hello), do: true)'
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: invalid syntax in def 1.(hello)",
|
||||
'defmodule ErrorsTest, do: (def 1.(hello), do: true)'
|
||||
end
|
||||
|
||||
test :duplicated_bitstring_size do
|
||||
assert_compile_fail SyntaxError, "nofile:1: duplicated size definition for bitstring", '<<1 :: [size(12), size(13)]>>'
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: duplicated size definition for bitstring",
|
||||
'<<1 :: [size(12), size(13)]>>'
|
||||
end
|
||||
|
||||
test :invalid_bitstring_specified do
|
||||
assert_compile_fail SyntaxError, "nofile:1: unknown bitstring specifier :atom", '<<1 :: :atom>>'
|
||||
assert_compile_fail SyntaxError, "nofile:1: unknown bitstring specifier unknown", '<<1 :: unknown>>'
|
||||
assert_compile_fail SyntaxError, "nofile:1: unknown bitstring specifier another(12)", '<<1 :: another(12)>>'
|
||||
assert_compile_fail SyntaxError, "nofile:1: size in bitstring expects an integer or a variable as argument", '<<1 :: size(:a)>>'
|
||||
assert_compile_fail SyntaxError, "nofile:1: unit in bitstring expects an integer as argument", '<<1 :: unit(x)>>'
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: unknown bitstring specifier :atom",
|
||||
'<<1 :: :atom>>'
|
||||
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: unknown bitstring specifier unknown",
|
||||
'<<1 :: unknown>>'
|
||||
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: unknown bitstring specifier another(12)",
|
||||
'<<1 :: another(12)>>'
|
||||
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: size in bitstring expects an integer or a variable as argument",
|
||||
'<<1 :: size(:a)>>'
|
||||
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: unit in bitstring expects an integer as argument",
|
||||
'<<1 :: unit(x)>>'
|
||||
end
|
||||
|
||||
test :invalid_var! do
|
||||
assert_compile_fail SyntaxError, "nofile:1: expected var!(x) to expand to an existing variable or be a part of a match", 'var!(x)'
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: expected var!(x) to expand to an existing variable or be a part of a match",
|
||||
'var!(x)'
|
||||
end
|
||||
|
||||
test :invalid_alias do
|
||||
assert_compile_fail SyntaxError, "nofile:1: invalid args for alias, cannot create nested alias Sample.Lists",
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: invalid args for alias, cannot create nested alias Sample.Lists",
|
||||
'alias :lists, as: Sample.Lists'
|
||||
end
|
||||
|
||||
test :invalid_import_option do
|
||||
assert_compile_fail SyntaxError, "nofile:1: unsupported option ops given to import",
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: unsupported option ops given to import",
|
||||
'import :lists, [ops: 1]'
|
||||
end
|
||||
|
||||
test :invalid_access_protocol_not_available do
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:2: module Unknown is not loaded and could not be found",
|
||||
'''
|
||||
defmodule ErrorsTest do
|
||||
def sample(Unknown[integer: 0]), do: true
|
||||
end
|
||||
'''
|
||||
end
|
||||
|
||||
test :invalid_access_protocol_not_alias do
|
||||
assert_raise RuntimeError, "the access protocol cannot be used inside match clauses (for example, on the left hand side of a match or in function signatures)", fn ->
|
||||
defmodule Foo do
|
||||
assert_raise ArgumentError, "the access protocol cannot be used inside match clauses (for example, on the left hand side of a match or in function signatures)", fn ->
|
||||
defmodule ErrorsTest do
|
||||
def sample(config[integer: 0]), do: true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test :invalid_access_protocol_not_available do
|
||||
assert_compile_fail CompileError, "nofile:2: module Unknown is not loaded and could not be found",
|
||||
'defmodule Foo do\ndef sample(Unknown[integer: 0]), do: true\nend'
|
||||
end
|
||||
|
||||
test :invalid_access_protocol_not_record do
|
||||
assert_raise RuntimeError, "cannot use module Kernel.ErrorsTest in access protocol because it does not export __record__/1", fn ->
|
||||
defmodule Foo do
|
||||
assert_raise ArgumentError, "cannot use module Kernel.ErrorsTest in access protocol because it does not export __record__/1", fn ->
|
||||
defmodule ErrorsTest do
|
||||
def sample(Kernel.ErrorsTest[integer: 0]), do: true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test :invalid_access_protocol_not_keywords do
|
||||
assert_raise RuntimeError, "expected contents inside brackets to be a Keyword", fn ->
|
||||
defmodule Foo do
|
||||
assert_raise ArgumentError, "expected contents inside brackets to be a Keyword", fn ->
|
||||
defmodule ErrorsTest do
|
||||
def sample(Kernel.ErrorsTest.Config[0]), do: true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test :invalid_access_protocol_invalid_keywords do
|
||||
assert_raise RuntimeError, "record Kernel.ErrorsTest.Config does not have the keys: [:foo]", fn ->
|
||||
defmodule Foo do
|
||||
assert_raise ArgumentError, "record Kernel.ErrorsTest.Config does not have the keys: [:foo]", fn ->
|
||||
defmodule ErrorsTest do
|
||||
def sample(Kernel.ErrorsTest.Config[foo: :bar]), do: true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test :invalid_access_protocol_invalid_keywords_outside_match do
|
||||
# FIXME: why RuntimeError?
|
||||
assert_compile_fail RuntimeError, "record Kernel.ErrorsTest.Config does not have the keys: [:foo]",
|
||||
assert_compile_fail ArgumentError,
|
||||
"record Kernel.ErrorsTest.Config does not have the keys: [:foo]",
|
||||
'Kernel.ErrorsTest.Config[foo: :bar]'
|
||||
end
|
||||
|
||||
test :invalid_rescue_clause do
|
||||
assert_compile_fail SyntaxError, "nofile:1: invalid rescue clause. The clause should match on an alias, a variable or be in the `var in [alias]` format",
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:4: invalid rescue clause. The clause should match on an alias, a variable or be in the `var in [alias]` format",
|
||||
'try do\n1\nrescue\nUndefinedFunctionError[arity: 1] -> false\nend'
|
||||
end
|
||||
|
||||
test :invalid_bc_return do
|
||||
assert_compile_fail SyntaxError, "nofile:1: a bit comprehension expects a bit string << >> to be returned",
|
||||
'bc x inlist [1,2,3], do: x'
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: a bit comprehension expects a bit string << >> to be returned",
|
||||
'bc x inlist [1, 2, 3], do: x'
|
||||
end
|
||||
|
||||
test :invalid_bc_inbits_gen do
|
||||
assert_compile_fail SyntaxError, "nofile:1: a bit comprehension expects a bit string << >> to be used in inbits generators",
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: a bit comprehension expects a bit string << >> to be used in inbits generators",
|
||||
'bc x inbits "123", do: <<x>>'
|
||||
end
|
||||
|
||||
test :unbound_cond do
|
||||
assert_compile_fail ArgumentError, "unbound variable _ inside cond. If you want the last clause to match, you probably meant to use true ->",
|
||||
assert_compile_fail ArgumentError,
|
||||
"unbound variable _ inside cond. If you want the last clause to match, you probably meant to use true ->",
|
||||
'cond do _ -> true end'
|
||||
end
|
||||
|
||||
test :fun_different_arities do
|
||||
assert_compile_fail SyntaxError, "nofile:1: cannot mix clauses with different arities in function definition",
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: cannot mix clauses with different arities in function definition",
|
||||
'fn x -> x; x, y -> x + y end'
|
||||
end
|
||||
|
||||
test :new_line_error do
|
||||
assert_compile_fail SyntaxError, "nofile:3: syntax error before: newline",
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:3: syntax error before: newline",
|
||||
'if true do\n foo = [],\n baz\nend'
|
||||
end
|
||||
|
||||
test :invalid_var_or_function_on_guard do
|
||||
assert_compile_fail SyntaxError, "nofile:1: unknown variable something_that_does_not_exist or cannot invoke function something_that_does_not_exist/0 inside guard",
|
||||
('case [] do; [] when something_that_does_not_exist == [] -> :ok; end')
|
||||
end
|
||||
|
||||
test :bodyless_function do
|
||||
assert {{:module, M, _, {:foo,1}}, [], _} =
|
||||
:elixir.eval(to_char_list("defmodule M do\ndef foo(n)\nend"), [])
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:2: unknown variable something_that_does_not_exist or " <>
|
||||
"cannot invoke function something_that_does_not_exist/0 inside guard",
|
||||
'''
|
||||
case [] do
|
||||
[] when something_that_does_not_exist == [] -> :ok
|
||||
end
|
||||
'''
|
||||
end
|
||||
|
||||
test :bodyless_function_with_guard do
|
||||
assert_compile_fail SyntaxError, "nofile:2: missing keyword do in def",
|
||||
("defmodule Foo do\ndef foo(n) when is_number(n)\nend")
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:2: missing keyword do in def",
|
||||
'''
|
||||
defmodule ErrorsTest do
|
||||
def foo(n) when is_number(n)
|
||||
end
|
||||
'''
|
||||
end
|
||||
|
||||
test :invalid_function_on_match do
|
||||
assert_compile_fail SyntaxError, "nofile:1: cannot invoke function something_that_does_not_exist/0 inside match",
|
||||
('case [] do; something_that_does_not_exist() -> :ok; end')
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: cannot invoke function something_that_does_not_exist/0 inside match",
|
||||
'case [] do; something_that_does_not_exist() -> :ok; end'
|
||||
end
|
||||
|
||||
test :invalid_remote_on_match do
|
||||
assert_compile_fail SyntaxError, "nofile:1: cannot invoke remote function Hello.something_that_does_not_exist/0 inside match",
|
||||
('case [] do; Hello.something_that_does_not_exist() -> :ok; end')
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: cannot invoke remote function Hello.something_that_does_not_exist/0 inside match",
|
||||
'case [] do; Hello.something_that_does_not_exist() -> :ok; end'
|
||||
end
|
||||
|
||||
test :invalid_remote_on_guard do
|
||||
assert_compile_fail SyntaxError, "nofile:1: cannot invoke remote function Hello.something_that_does_not_exist/0 inside guard",
|
||||
('case [] do; [] when Hello.something_that_does_not_exist == [] -> :ok; end')
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: cannot invoke remote function Hello.something_that_does_not_exist/0 inside guard",
|
||||
'case [] do; [] when Hello.something_that_does_not_exist == [] -> :ok; end'
|
||||
end
|
||||
|
||||
test :macros_error_stacktrace do
|
||||
assert [{:erlang,:+,[1,:foo],_},{Foo,:sample,1,_}|_] =
|
||||
rescue_stacktrace("defmodule Foo do\ndefmacro sample(num), do: num + :foo\ndef other, do: sample(1)\nend")
|
||||
assert [{:erlang, :+, [1, :foo], _}, {ErrorsTest, :sample, 1, _}|_] =
|
||||
rescue_stacktrace(""")
|
||||
defmodule ErrorsTest do
|
||||
defmacro sample(num), do: num + :foo
|
||||
def other, do: sample(1)
|
||||
end
|
||||
"""
|
||||
end
|
||||
|
||||
test :macros_function_clause_stacktrace do
|
||||
assert [{__MODULE__,:sample,1,_}|_] =
|
||||
rescue_stacktrace("defmodule Foo do\nimport Kernel.ErrorsTest\nsample(1)\nend")
|
||||
assert [{__MODULE__, :sample, 1, _}|_] =
|
||||
rescue_stacktrace(""")
|
||||
defmodule ErrorsTest do
|
||||
import Kernel.ErrorsTest
|
||||
sample(1)
|
||||
end
|
||||
"""
|
||||
end
|
||||
|
||||
test :macros_interpreted_function_clause_stacktrace do
|
||||
assert [{Foo,:sample,1,_}|_] =
|
||||
rescue_stacktrace("defmodule Foo do\ndefmacro sample(0), do: 0\ndef other, do: sample(1)\nend")
|
||||
assert [{ErrorsTest, :sample, 1, _}|_] =
|
||||
rescue_stacktrace(""")
|
||||
defmodule ErrorsTest do
|
||||
defmacro sample(0), do: 0
|
||||
def other, do: sample(1)
|
||||
end
|
||||
"""
|
||||
end
|
||||
|
||||
test :macros_compiled_callback do
|
||||
assert [{Kernel.ErrorsTest,:__before_compile__,[Macro.Env[module: Foo]],_}|_] =
|
||||
rescue_stacktrace("defmodule Foo do\nModule.put_attribute(__MODULE__, :before_compile, Kernel.ErrorsTest)\nend")
|
||||
assert [{Kernel.ErrorsTest, :__before_compile__, [Macro.Env[module: ErrorsTest]], _}|_] =
|
||||
rescue_stacktrace(""")
|
||||
defmodule ErrorsTest do
|
||||
Module.put_attribute(__MODULE__, :before_compile, Kernel.ErrorsTest)
|
||||
end
|
||||
"""
|
||||
end
|
||||
|
||||
defmacro sample(0), do: 0
|
||||
|
||||
@@ -12,22 +12,22 @@ defmodule Kernel.ImportOnlyTest do
|
||||
test :import_with_only do
|
||||
require Kernel.ImportAvailable
|
||||
import :lists, only: Kernel.ImportAvailable.flatten
|
||||
assert flatten([1,[2],3]) == [1,2,3]
|
||||
assert flatten([1, [2], 3]) == [1, 2, 3]
|
||||
end
|
||||
|
||||
test :import_all do
|
||||
import :lists
|
||||
assert flatten([1,[2],3]) == [1,2,3]
|
||||
assert flatten([1, [2], 3]) == [1, 2, 3]
|
||||
end
|
||||
|
||||
test :import_except_none do
|
||||
import :lists, except: []
|
||||
assert flatten([1,[2],3]) == [1,2,3]
|
||||
assert flatten([1, [2], 3]) == [1, 2, 3]
|
||||
end
|
||||
|
||||
test :import_with_except_erlang do
|
||||
import :lists, except: [each: 2]
|
||||
assert flatten([1,[2],3]) == [1,2,3]
|
||||
assert flatten([1, [2], 3]) == [1, 2, 3]
|
||||
end
|
||||
|
||||
defmacrop dynamic_opts do
|
||||
@@ -36,7 +36,7 @@ defmodule Kernel.ImportOnlyTest do
|
||||
|
||||
test :import_with_dynamic_opts do
|
||||
import :lists, dynamic_opts
|
||||
assert flatten([1,[2],3]) == [1,2,3]
|
||||
assert flatten([1, [2], 3]) == [1, 2, 3]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -46,8 +46,8 @@ defmodule Kernel.DoubleImportTest do
|
||||
test :import_double_except do
|
||||
import :lists, except: [flatten: 1]
|
||||
import :lists, except: [each: 2]
|
||||
assert append([1], [2,3]) == [1,2,3]
|
||||
assert flatten([1,[2],3]) == [1,[2],3]
|
||||
assert append([1], [2, 3]) == [1, 2, 3]
|
||||
assert flatten([1, [2], 3]) == [1, [2], 3]
|
||||
end
|
||||
|
||||
def flatten(list), do: list
|
||||
@@ -61,7 +61,6 @@ end
|
||||
defmodule Kernel.Underscored do
|
||||
def hello(x), do: x
|
||||
def __underscore__(x), do: x
|
||||
def __s__(x), do: x
|
||||
end
|
||||
|
||||
defmodule Kernel.ExplicitUnderscored do
|
||||
@@ -87,11 +86,6 @@ defmodule Kernel.ImportUnderscoreTest do
|
||||
import Kernel.Underscored
|
||||
assert hello(2) == 2
|
||||
end
|
||||
|
||||
test :includes_sigil_like do
|
||||
import Kernel.Underscored
|
||||
assert __s__(3) == 3
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Kernel.ImportMacrosTest do
|
||||
@@ -135,6 +129,6 @@ defmodule Kernel.MultipleImportTest do
|
||||
test :import_many do
|
||||
[import(List), import(String)]
|
||||
assert capitalize("foo") == "Foo"
|
||||
assert flatten([1,[2],3]) == [1,2,3]
|
||||
assert flatten([1, [2], 3]) == [1, 2, 3]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -131,7 +131,7 @@ defmodule Kernel.OverridableTest do
|
||||
end
|
||||
|
||||
test "overridable definitions are private" do
|
||||
refute {:"with_super (overridable 0)",0} in Overridable.__info__(:exports)
|
||||
refute {:"with_super (overridable 0)", 0} in Overridable.__info__(:exports)
|
||||
end
|
||||
|
||||
test "invalid super call" do
|
||||
|
||||
@@ -49,11 +49,11 @@ defmodule Kernel.PartialApplicationTest do
|
||||
|
||||
test :partial_application_for_module_calls do
|
||||
fun = Enum.reverse(&1)
|
||||
assert fun.([1,2,3]) == [3,2,1]
|
||||
assert fun.([1, 2, 3]) == [3, 2, 1]
|
||||
end
|
||||
|
||||
test :partial_application_for_operator do
|
||||
assert Enum.map([1,2,3], &1 * 2) == [2,4,6]
|
||||
assert Enum.map([1, 2, 3], &1 * 2) == [2, 4, 6]
|
||||
end
|
||||
|
||||
defp minus(x, y) do
|
||||
|
||||
@@ -12,30 +12,30 @@ defmodule Kernel.QuoteTest do
|
||||
end
|
||||
|
||||
test :list do
|
||||
assert quote(do: [1,2,3]) == [1,2,3]
|
||||
assert quote(do: [1, 2, 3]) == [1, 2, 3]
|
||||
end
|
||||
|
||||
test :tuple do
|
||||
assert quote(do: { :a, 1 }) == {:a,1}
|
||||
assert quote(do: { :a, 1 }) == {:a, 1}
|
||||
end
|
||||
|
||||
test :keep_line do
|
||||
## DO NOT MOVE THIS LINE
|
||||
assert quote(line: :keep, do: bar(1,2,3)) == { :bar, [line: 24], [1,2,3] }
|
||||
assert quote(line: :keep, do: bar(1, 2, 3)) == { :bar, [line: 24], [1, 2, 3] }
|
||||
end
|
||||
|
||||
test :fixed_line do
|
||||
assert quote(line: 3, do: bar(1,2,3)) == { :bar, [line: 3], [1,2,3] }
|
||||
assert quote(line: 3, do: bar(1, 2, 3)) == { :bar, [line: 3], [1, 2, 3] }
|
||||
end
|
||||
|
||||
test :keep_location do
|
||||
## DO NOT MOVE THIS LINE
|
||||
assert quote(location: :keep, do: bar(1,2,3)) == {
|
||||
assert quote(location: :keep, do: bar(1, 2, 3)) == {
|
||||
:__scope__,
|
||||
[line: 33],
|
||||
[
|
||||
[file: __FILE__],
|
||||
[do: { :bar, [line: 33], [1,2,3] }]
|
||||
[do: { :bar, [line: 33], [1, 2, 3] }]
|
||||
]
|
||||
}
|
||||
end
|
||||
@@ -43,7 +43,7 @@ defmodule Kernel.QuoteTest do
|
||||
test :quote_line_var do
|
||||
## DO NOT MOVE THIS LINE
|
||||
line = __ENV__.line
|
||||
assert quote(line: line, do: bar(1,2,3)) == { :bar, [line: 45], [1,2,3] }
|
||||
assert quote(line: line, do: bar(1, 2, 3)) == { :bar, [line: 45], [1, 2, 3] }
|
||||
end
|
||||
|
||||
test :unquote_call do
|
||||
@@ -99,28 +99,36 @@ defmodule Kernel.QuoteTest do
|
||||
|
||||
test :splice_on_pipe do
|
||||
contents = [1, 2, 3]
|
||||
assert quote(do: [unquote_splicing(contents)|[1,2,3]]) == [1,2,3,1,2,3]
|
||||
assert quote(do: [unquote_splicing(contents)|[1, 2, 3]]) == [1, 2, 3, 1, 2, 3]
|
||||
end
|
||||
|
||||
test :splice_on_stab do
|
||||
{ fun, [] } =
|
||||
Code.eval_quoted(quote(do: fn(unquote_splicing([1,2,3])) -> :ok end), [])
|
||||
assert fun.(1,2,3) == :ok
|
||||
Code.eval_quoted(quote(do: fn(unquote_splicing([1, 2, 3])) -> :ok end), [])
|
||||
assert fun.(1, 2, 3) == :ok
|
||||
|
||||
{ fun, [] } =
|
||||
Code.eval_quoted(quote(do: fn(1, unquote_splicing([2,3])) -> :ok end), [])
|
||||
assert fun.(1,2,3) == :ok
|
||||
Code.eval_quoted(quote(do: fn(1, unquote_splicing([2, 3])) -> :ok end), [])
|
||||
assert fun.(1, 2, 3) == :ok
|
||||
end
|
||||
|
||||
test :when do
|
||||
assert {:->,_,[{[{:when,_,[1,2,3,4]}],_,5}]} = quote(do: (1, 2, 3 when 4 -> 5))
|
||||
assert {:->,_,[{[{:when,_,[1,2,3,4]}],_,5}]} = quote(do: ((1, 2, 3) when 4 -> 5))
|
||||
|
||||
assert {:->,_,[{[{:when,_,[1,2,3,{:when,_,[4,5]}]}],_,6}]} =
|
||||
quote(do: ((1, 2, 3) when 4 when 5 -> 6))
|
||||
end
|
||||
|
||||
test :stab do
|
||||
assert { :->, _, [{[],_}] } = (quote do -> end)
|
||||
assert { :->, _, [{[],_}] } = (quote do: (->))
|
||||
assert { :->, _, [{[], _, _}] } = (quote do -> end)
|
||||
assert { :->, _, [{[], _, _}] } = (quote do: (->))
|
||||
|
||||
assert { :->, _, [{[1],_}] } = (quote do 1 -> end)
|
||||
assert { :->, _, [{[1],_}] } = (quote do: (1 ->))
|
||||
assert { :->, _, [{[1], _, _}] } = (quote do 1 -> end)
|
||||
assert { :->, _, [{[1], _, _}] } = (quote do: (1 ->))
|
||||
|
||||
assert { :->, _, [{[],1}] } = (quote do -> 1 end)
|
||||
assert { :->, _, [{[],1}] } = (quote do: (-> 1))
|
||||
assert { :->, _, [{[], _, 1}] } = (quote do -> 1 end)
|
||||
assert { :->, _, [{[], _, 1}] } = (quote do: (-> 1))
|
||||
end
|
||||
|
||||
defmacrop dynamic_opts do
|
||||
@@ -128,7 +136,14 @@ defmodule Kernel.QuoteTest do
|
||||
end
|
||||
|
||||
test :quote_with_dynamic_opts do
|
||||
assert quote(dynamic_opts, do: bar(1,2,3)) == { :bar, [line: 3], [1,2,3] }
|
||||
assert quote(dynamic_opts, do: bar(1, 2, 3)) == { :bar, [line: 3], [1, 2, 3] }
|
||||
end
|
||||
|
||||
test :binding do
|
||||
assert quote(binding: [foo: 1 + 2], do: foo) == { :__block__, [], [
|
||||
{ :=, [], [{ :foo, [], Kernel.QuoteTest }, 3] },
|
||||
{ :foo, [], Kernel.QuoteTest }
|
||||
] }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ defmodule Kernel.RescueTest do
|
||||
named = ArgumentError
|
||||
|
||||
result = try do
|
||||
Certainly.Undefined.function(1,2,3)
|
||||
Certainly.Undefined.function(1, 2, 3)
|
||||
rescue
|
||||
x in [named] -> x.message
|
||||
catch
|
||||
@@ -149,7 +149,7 @@ defmodule Kernel.RescueTest do
|
||||
|
||||
test :tuple_badarg_error do
|
||||
result = try do
|
||||
:erlang.error({ :badarg, [1,2,3] })
|
||||
:erlang.error({ :badarg, [1, 2, 3] })
|
||||
rescue
|
||||
x in [ArgumentError] -> x.message
|
||||
end
|
||||
@@ -172,7 +172,7 @@ defmodule Kernel.RescueTest do
|
||||
string = "bad arity error: #{inspect(fun)} called with [1,2]"
|
||||
|
||||
result = try do
|
||||
fun.(1,2)
|
||||
fun.(1, 2)
|
||||
rescue
|
||||
x in [BadArityError] -> x.message
|
||||
end
|
||||
|
||||
@@ -3,19 +3,19 @@ Code.require_file "../test_helper.exs", __DIR__
|
||||
defmodule Kernel.SigilsTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
test :__b__ do
|
||||
test :sigil_b do
|
||||
assert %b(foo) == "foo"
|
||||
assert %b(f#{:o}o) == "foo"
|
||||
assert %b(f\no) == "f\no"
|
||||
end
|
||||
|
||||
test :__b__with_heredoc do
|
||||
test :sigil_b_with_heredoc do
|
||||
assert " foo\n\n" == %b"""
|
||||
f#{:o}o\n
|
||||
"""
|
||||
end
|
||||
|
||||
test :__B__ do
|
||||
test :sigil_B do
|
||||
assert %B(foo) == "foo"
|
||||
assert %B[foo] == "foo"
|
||||
assert %B{foo} == "foo"
|
||||
@@ -26,19 +26,19 @@ defmodule Kernel.SigilsTest do
|
||||
assert %B(f\no) == "f\\no"
|
||||
end
|
||||
|
||||
test :__B__with_heredoc do
|
||||
test :sigil_B_with_heredoc do
|
||||
assert " f\#{o}o\\n\n" == %B"""
|
||||
f#{o}o\n
|
||||
"""
|
||||
end
|
||||
|
||||
test :__c__ do
|
||||
test :sigil_c do
|
||||
assert %c(foo) == 'foo'
|
||||
assert %c(f#{:o}o) == 'foo'
|
||||
assert %c(f\no) == 'f\no'
|
||||
end
|
||||
|
||||
test :__C__ do
|
||||
test :sigil_C do
|
||||
assert %C(foo) == 'foo'
|
||||
assert %C[foo] == 'foo'
|
||||
assert %C{foo} == 'foo'
|
||||
@@ -49,7 +49,7 @@ defmodule Kernel.SigilsTest do
|
||||
assert %C(f\no) == 'f\\no'
|
||||
end
|
||||
|
||||
test :__w__ do
|
||||
test :sigil_w do
|
||||
assert %w(foo bar baz) == ["foo", "bar", "baz"]
|
||||
assert %w(foo #{:bar} baz) == ["foo", "bar", "baz"]
|
||||
|
||||
@@ -72,7 +72,7 @@ defmodule Kernel.SigilsTest do
|
||||
assert %w(Foo.#{Bar})c == ['Foo.Elixir.Bar']
|
||||
end
|
||||
|
||||
test :__W__ do
|
||||
test :sigil_W do
|
||||
assert %W(foo #{bar} baz) == ["foo", "\#{bar}", "baz"]
|
||||
|
||||
assert %W(
|
||||
|
||||
@@ -5,8 +5,12 @@ defmodule Kernel.WarningTest do
|
||||
|
||||
import ExUnit.CaptureIO
|
||||
|
||||
defp capture_err(fun) do
|
||||
capture_io(:stderr, fun)
|
||||
end
|
||||
|
||||
test :unused_variable do
|
||||
assert capture_io(fn ->
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample do
|
||||
def hello(arg), do: nil
|
||||
@@ -18,7 +22,7 @@ defmodule Kernel.WarningTest do
|
||||
end
|
||||
|
||||
test :unused_function do
|
||||
assert capture_io(fn ->
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample1 do
|
||||
defp hello, do: nil
|
||||
@@ -26,7 +30,7 @@ defmodule Kernel.WarningTest do
|
||||
"""
|
||||
end) =~ "function hello/0 is unused"
|
||||
|
||||
assert capture_io(fn ->
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample2 do
|
||||
defp hello(0), do: hello(1)
|
||||
@@ -35,7 +39,7 @@ defmodule Kernel.WarningTest do
|
||||
"""
|
||||
end) =~ "function hello/1 is unused"
|
||||
|
||||
assert capture_io(fn ->
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample3 do
|
||||
def a, do: nil
|
||||
@@ -45,13 +49,12 @@ defmodule Kernel.WarningTest do
|
||||
end
|
||||
"""
|
||||
end) =~ "function c/2 is unused"
|
||||
|
||||
after
|
||||
purge [Sample1, Sample2, Sample3]
|
||||
end
|
||||
|
||||
test :unused_cyclic_functions do
|
||||
assert capture_io(fn ->
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample do
|
||||
defp a, do: b
|
||||
@@ -64,7 +67,7 @@ defmodule Kernel.WarningTest do
|
||||
end
|
||||
|
||||
test :unused_macro do
|
||||
assert capture_io(fn ->
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample do
|
||||
defmacrop hello, do: nil
|
||||
@@ -76,25 +79,25 @@ defmodule Kernel.WarningTest do
|
||||
end
|
||||
|
||||
test :unused_default_args do
|
||||
assert capture_io(fn ->
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample1 do
|
||||
def a, do: b(1,2,3)
|
||||
def a, do: b(1, 2, 3)
|
||||
defp b(arg1 // 1, arg2 // 2, arg3 // 3), do: [arg1, arg2, arg3]
|
||||
end
|
||||
"""
|
||||
end) =~ "default arguments in b/3 are never used"
|
||||
|
||||
assert capture_io(fn ->
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample2 do
|
||||
def a, do: b(1,2)
|
||||
def a, do: b(1, 2)
|
||||
defp b(arg1 // 1, arg2 // 2, arg3 // 3), do: [arg1, arg2, arg3]
|
||||
end
|
||||
"""
|
||||
end) =~ "the first 2 default arguments in b/3 are never used"
|
||||
|
||||
assert capture_io(fn ->
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample3 do
|
||||
def a, do: b(1)
|
||||
@@ -103,7 +106,7 @@ defmodule Kernel.WarningTest do
|
||||
"""
|
||||
end) =~ "the first default argument in b/3 is never used"
|
||||
|
||||
assert capture_io(fn ->
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample4 do
|
||||
def a, do: b(1)
|
||||
@@ -116,7 +119,7 @@ defmodule Kernel.WarningTest do
|
||||
end
|
||||
|
||||
test :unused_import do
|
||||
assert capture_io(fn ->
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample1 do
|
||||
def hello, do: nil
|
||||
@@ -132,8 +135,75 @@ defmodule Kernel.WarningTest do
|
||||
purge [Sample1, Sample2]
|
||||
end
|
||||
|
||||
test :unused_guard do
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample1 do
|
||||
def is_atom_case do
|
||||
v = "bc"
|
||||
case v do
|
||||
_ when is_atom(v) -> :ok
|
||||
_ -> :fail
|
||||
end
|
||||
end
|
||||
end
|
||||
"""
|
||||
end) =~ "nofile:5: the guard for this clause evaluates to 'false'"
|
||||
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample2 do
|
||||
def is_binary_cond do
|
||||
v = "bc"
|
||||
cond do
|
||||
is_binary(v) -> :bin
|
||||
true -> :ok
|
||||
end
|
||||
end
|
||||
end
|
||||
"""
|
||||
end) =~ "nofile:6: this clause cannot match because a previous clause at line 5 always matches"
|
||||
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample3 do
|
||||
def is_binary_cond_case do
|
||||
v = "bc"
|
||||
case(is_binary(v)) do
|
||||
_ in [false, nil] -> :ok
|
||||
_ -> :bin
|
||||
end
|
||||
end
|
||||
end
|
||||
"""
|
||||
end) =~ "nofile:5: this clause cannot match because a previous clause at line 6 always matches"
|
||||
after
|
||||
purge [Sample1, Sample2, Sample3]
|
||||
end
|
||||
|
||||
test :used_import_via_alias do
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample1 do
|
||||
import List, only: [flatten: 1]
|
||||
|
||||
defmacro generate do
|
||||
List.duplicate(quote(do: flatten([1,2,3])), 100)
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Sample2 do
|
||||
import Sample1
|
||||
generate
|
||||
end
|
||||
"""
|
||||
end) == nil
|
||||
after
|
||||
purge [Sample1, Sample2]
|
||||
end
|
||||
|
||||
test :clause_not_match do
|
||||
assert capture_io(fn ->
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample do
|
||||
def hello, do: nil
|
||||
@@ -146,7 +216,7 @@ defmodule Kernel.WarningTest do
|
||||
end
|
||||
|
||||
test :clause_with_defaults_should_be_first do
|
||||
assert capture_io(fn ->
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample do
|
||||
def hello(arg), do: nil
|
||||
@@ -158,21 +228,8 @@ defmodule Kernel.WarningTest do
|
||||
purge Sample
|
||||
end
|
||||
|
||||
test :multiple_clauses_with_defaults do
|
||||
assert capture_io(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample do
|
||||
def hello(arg // 0), do: nil
|
||||
def hello(arg // 1), do: nil
|
||||
end
|
||||
"""
|
||||
end) =~ "def hello/1 has default values and multiple clauses, use a separate clause for declaring defaults"
|
||||
after
|
||||
purge Sample
|
||||
end
|
||||
|
||||
test :unused_with_local_with_overridable do
|
||||
assert capture_io(fn ->
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample do
|
||||
def hello, do: world
|
||||
@@ -187,7 +244,7 @@ defmodule Kernel.WarningTest do
|
||||
end
|
||||
|
||||
test :used_with_local_with_reattached_overridable do
|
||||
assert nil? capture_io(fn ->
|
||||
assert nil? capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample do
|
||||
def hello, do: world
|
||||
@@ -200,6 +257,30 @@ defmodule Kernel.WarningTest do
|
||||
purge Sample
|
||||
end
|
||||
|
||||
test :undefined_module_attribute do
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample do
|
||||
@foo
|
||||
end
|
||||
"""
|
||||
end) =~ "undefined module attribute @foo, please remove access to @foo or explicitly set it to nil before access"
|
||||
after
|
||||
purge Sample
|
||||
end
|
||||
|
||||
test :in_guard_empty_list do
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample do
|
||||
def a(x) when x in [], do: x
|
||||
end
|
||||
"""
|
||||
end) =~ "the guard for this clause evaluates to 'false'"
|
||||
after
|
||||
purge Sample
|
||||
end
|
||||
|
||||
defp purge(list) when is_list(list) do
|
||||
Enum.each list, purge(&1)
|
||||
end
|
||||
|
||||
@@ -11,19 +11,24 @@ defmodule KernelTest do
|
||||
assert (string =~ "ab+") == true
|
||||
assert (string =~ "bb") == false
|
||||
|
||||
assert_raise ArgumentError, "bad argument on the right side of =~: [\"^a\",\"*$\"]", fn ->
|
||||
string =~ ["^a", "*$"]
|
||||
end
|
||||
|
||||
assert_raise ArgumentError, "argument error", fn ->
|
||||
assert_raise FunctionClauseError, "no function clause matching in Kernel.=~/2", fn ->
|
||||
1234 =~ "hello"
|
||||
end
|
||||
|
||||
assert_raise ArgumentError, "argument error", fn ->
|
||||
assert_raise FunctionClauseError, "no function clause matching in Kernel.=~/2", fn ->
|
||||
1234 =~ %r"hello"
|
||||
end
|
||||
end
|
||||
|
||||
test :^ do
|
||||
x = 1
|
||||
|
||||
assert_raise MatchError, fn ->
|
||||
{ x, ^x } = { 2, 2 }
|
||||
x
|
||||
end
|
||||
end
|
||||
|
||||
test :nil? do
|
||||
assert nil?(nil)
|
||||
refute nil?(0)
|
||||
@@ -35,12 +40,12 @@ defmodule KernelTest do
|
||||
refute x(4)
|
||||
refute x([])
|
||||
|
||||
assert 2 in [1,2,3]
|
||||
assert 2 in [1, 2, 3]
|
||||
assert 2 in 1..3
|
||||
refute 4 in [1,2,3]
|
||||
refute 4 in [1, 2, 3]
|
||||
refute 4 in 1..3
|
||||
|
||||
list = [1,2,3]
|
||||
list = [1, 2, 3]
|
||||
assert 2 in list
|
||||
refute 4 in list
|
||||
end
|
||||
@@ -74,7 +79,7 @@ defmodule KernelTest do
|
||||
end
|
||||
|
||||
test :apply do
|
||||
assert apply(Enum, :reverse, [[1|[2,3]]]) == [3,2,1]
|
||||
assert apply(Enum, :reverse, [[1|[2, 3]]]) == [3, 2, 1]
|
||||
assert apply(fn x -> x * 2 end, [2]) == 4
|
||||
end
|
||||
|
||||
@@ -86,8 +91,29 @@ defmodule KernelTest do
|
||||
assert __ENV__.function == { :test_function_from___ENV__, 1 }
|
||||
end
|
||||
|
||||
defp x(value) when value in [1,2,3], do: true
|
||||
defp x(_), do: false
|
||||
test :binding do
|
||||
x = 1
|
||||
assert binding == [x: 1]
|
||||
assert binding([:x, :y]) == [x: 1]
|
||||
|
||||
x = 2
|
||||
assert binding == [x: 2]
|
||||
|
||||
y = 3
|
||||
assert binding == [x: 2, y: 3]
|
||||
end
|
||||
|
||||
test :binding_with_all_contexts do
|
||||
x = 1
|
||||
assert { { :x, nil }, 1 } in binding(true)
|
||||
|
||||
var!(x, :foo) = 2
|
||||
assert { { :x, :foo }, 2 } in binding(true)
|
||||
assert binding([x: :foo], true) == [{ { :x, :foo }, 2 }]
|
||||
end
|
||||
|
||||
defp x(value) when value in [1, 2, 3], do: true
|
||||
defp x(_), do: false
|
||||
|
||||
defmodule Conversions do
|
||||
use ExUnit.Case, async: true
|
||||
@@ -157,6 +183,70 @@ defmodule KernelTest do
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Runtime do
|
||||
use ExUnit.Case, async: true
|
||||
defp kernel, do: Kernel
|
||||
|
||||
test :not do
|
||||
assert kernel.not(true) == false
|
||||
assert kernel.not(false) == true
|
||||
end
|
||||
|
||||
test :! do
|
||||
assert kernel.!(true) == false
|
||||
assert kernel.!(nil) == true
|
||||
assert kernel.!(false) == true
|
||||
end
|
||||
|
||||
test :list do
|
||||
assert kernel.++([1], [2]) == [1, 2]
|
||||
assert kernel.--([1], [1]) == []
|
||||
end
|
||||
|
||||
test :math do
|
||||
assert kernel.+(1, 2) == 3
|
||||
assert kernel.-(2, 2) == 0
|
||||
assert kernel.*(2, 2) == 4
|
||||
assert kernel./(2, 2) == 1.0
|
||||
end
|
||||
|
||||
test :unary do
|
||||
assert kernel.+(1) == 1
|
||||
assert kernel.-(1) == -1
|
||||
end
|
||||
|
||||
test :send do
|
||||
kernel.<-(self, :hello)
|
||||
assert_received :hello
|
||||
end
|
||||
|
||||
test :comp do
|
||||
assert kernel.==(1, 1)
|
||||
refute kernel.==(1, 2)
|
||||
|
||||
assert kernel.!=(1, 2)
|
||||
refute kernel.!=(1, 1)
|
||||
|
||||
assert kernel.===(1, 1)
|
||||
refute kernel.===(1, 2)
|
||||
|
||||
assert kernel.!==(1, 2)
|
||||
refute kernel.!==(1, 1)
|
||||
|
||||
assert kernel.>(1, 0)
|
||||
refute kernel.>(1, 2)
|
||||
|
||||
assert kernel.<(0, 1)
|
||||
refute kernel.<(2, 1)
|
||||
|
||||
assert kernel.>=(1, 0)
|
||||
refute kernel.>=(1, 2)
|
||||
|
||||
assert kernel.<=(0, 1)
|
||||
refute kernel.<=(2, 1)
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Function do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
@@ -224,19 +314,19 @@ defmodule KernelTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
test :simple do
|
||||
assert [1,[2],3] |> List.flatten == [1,2,3]
|
||||
assert [1, [2], 3] |> List.flatten == [1, 2, 3]
|
||||
end
|
||||
|
||||
test :nested do
|
||||
assert [1,[2],3] |> List.flatten |> Enum.map(&1 * 2) == [2,4,6]
|
||||
assert [1, [2], 3] |> List.flatten |> Enum.map(&1 * 2) == [2, 4, 6]
|
||||
end
|
||||
|
||||
test :local do
|
||||
assert [1,[2],3] |> List.flatten |> local == [2,4,6]
|
||||
assert [1, [2], 3] |> List.flatten |> local == [2, 4, 6]
|
||||
end
|
||||
|
||||
test :map do
|
||||
assert Enum.map([1,2,3], &1 |> twice |> twice) == [4,8,12]
|
||||
assert Enum.map([1, 2, 3], &1 |> twice |> twice) == [4, 8, 12]
|
||||
end
|
||||
|
||||
test :atom do
|
||||
@@ -249,8 +339,6 @@ defmodule KernelTest do
|
||||
|
||||
import CompileAssertion
|
||||
|
||||
# FIXME: this mustn't work, but it doesn't call pipeline_op at all
|
||||
#assert_compile_fail ArgumentError, "Unsupported expression in pipeline |> operator: &1", "1 |> &1"
|
||||
assert_compile_fail ArgumentError, "Unsupported expression in pipeline |> operator: &1 * 2", "1 |> &1*2"
|
||||
assert_compile_fail ArgumentError, "Unsupported expression in pipeline |> operator: hd(&1)", "[1] |> hd(&1)"
|
||||
end
|
||||
@@ -340,14 +428,14 @@ defmodule KernelTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
test :less do
|
||||
destructure [x,y,z], [1,2,3,4,5]
|
||||
destructure [x, y, z], [1, 2, 3, 4, 5]
|
||||
assert x == 1
|
||||
assert y == 2
|
||||
assert z == 3
|
||||
end
|
||||
|
||||
test :more do
|
||||
destructure [a,b,c,d,e], [1,2,3]
|
||||
destructure [a, b, c, d, e], [1, 2, 3]
|
||||
assert a == 1
|
||||
assert b == 2
|
||||
assert c == 3
|
||||
@@ -356,26 +444,26 @@ defmodule KernelTest do
|
||||
end
|
||||
|
||||
test :equal do
|
||||
destructure [a,b,c], [1,2,3]
|
||||
destructure [a, b, c], [1, 2, 3]
|
||||
assert a == 1
|
||||
assert b == 2
|
||||
assert c == 3
|
||||
end
|
||||
|
||||
test :none do
|
||||
destructure [a,b,c], []
|
||||
destructure [a, b, c], []
|
||||
assert a == nil
|
||||
assert b == nil
|
||||
assert c == nil
|
||||
end
|
||||
|
||||
test :match do
|
||||
destructure [1,b,_], [1,2,3]
|
||||
destructure [1, b, _], [1, 2, 3]
|
||||
assert b == 2
|
||||
end
|
||||
|
||||
test :nil do
|
||||
destructure [a,b,c], a_nil
|
||||
destructure [a, b, c], a_nil
|
||||
assert a == nil
|
||||
assert b == nil
|
||||
assert c == nil
|
||||
@@ -384,11 +472,11 @@ 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
|
||||
|
||||
defp a_list, do: [1,2,3]
|
||||
defp a_list, do: [1, 2, 3]
|
||||
defp a_nil, do: nil
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,23 +6,23 @@ defmodule KeywordTest do
|
||||
test :literal do
|
||||
assert [B: 1] == [{ :B, 1 }]
|
||||
assert [foo?: :bar] == [{:foo?, :bar}]
|
||||
assert [||: 2, +: 1] == [{:||,2}, {:+,1}]
|
||||
assert [||: 2, +: 1] == [{:||, 2}, {:+, 1}]
|
||||
end
|
||||
|
||||
test :ambiguity do
|
||||
# This raises a warning, so let's leave it commented
|
||||
# assert quote(do: [a:b]) == [a: { :b, 0, :quoted }]
|
||||
assert [{ :::, _, [{ :a, _, _ },{ :b, _, _ }] }] = quote(do: [a::b])
|
||||
assert [{ :::, _, [{ :a, _, _ }, { :b, _, _ }] }] = quote(do: [a::b])
|
||||
end
|
||||
|
||||
test :optional_comma do
|
||||
[a: 1,
|
||||
b: 2,
|
||||
c: 3,]
|
||||
c: 3, ]
|
||||
end
|
||||
|
||||
test :from_enum do
|
||||
list = [{:b,2},{:a,1},{:c,3}]
|
||||
list = [{:b, 2}, {:a, 1}, {:c, 3}]
|
||||
dict = HashDict.new list
|
||||
assert Keyword.from_enum(list) == [b: 2, a: 1, c: 3]
|
||||
assert Keyword.equal?(Keyword.from_enum(dict), [a: 1, b: 2, c: 3])
|
||||
@@ -31,7 +31,7 @@ defmodule KeywordTest do
|
||||
test :keyword? do
|
||||
assert Keyword.keyword?([])
|
||||
assert Keyword.keyword?([a: 1])
|
||||
assert Keyword.keyword?([{Foo,1}])
|
||||
assert Keyword.keyword?([{Foo, 1}])
|
||||
refute Keyword.keyword?([{}])
|
||||
refute Keyword.keyword?(<<>>)
|
||||
end
|
||||
@@ -130,7 +130,7 @@ defmodule Keyword.DuplicatedTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
test :duplicated_entries do
|
||||
assert create_keywords == [{:first_key,1},{:first_key,2},{:second_key,2}]
|
||||
assert create_keywords == [{:first_key, 1}, {:first_key, 2}, {:second_key, 2}]
|
||||
end
|
||||
|
||||
test :get do
|
||||
@@ -141,7 +141,7 @@ defmodule Keyword.DuplicatedTest do
|
||||
end
|
||||
|
||||
test :get_values do
|
||||
assert Keyword.get_values(create_keywords, :first_key) == [1,2]
|
||||
assert Keyword.get_values(create_keywords, :first_key) == [1, 2]
|
||||
assert Keyword.get_values(create_keywords, :second_key) == [2]
|
||||
assert Keyword.get_values(create_keywords, :other_key) == []
|
||||
end
|
||||
|
||||
@@ -4,69 +4,69 @@ defmodule ListTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
test :cons_cell_precedence do
|
||||
assert [1|:lists.flatten([2,3])] == [1,2,3]
|
||||
assert [1|:lists.flatten([2, 3])] == [1, 2, 3]
|
||||
end
|
||||
|
||||
test :optional_comma do
|
||||
assert [1] == [ 1, ]
|
||||
assert [1,2,3] == [1,2,3,]
|
||||
assert [1, 2, 3] == [1, 2, 3, ]
|
||||
end
|
||||
|
||||
test :partial_application do
|
||||
assert ([&1, 2]).(1) == [1,2]
|
||||
assert ([&1, &2]).(1, 2) == [1,2]
|
||||
assert ([&2, &1]).(2, 1) == [1,2]
|
||||
assert ([&1, 2]).(1) == [1, 2]
|
||||
assert ([&1, &2]).(1, 2) == [1, 2]
|
||||
assert ([&2, &1]).(2, 1) == [1, 2]
|
||||
assert ([&1|&2]).(1, 2) == [1|2]
|
||||
assert ([&1, &2|&3]).(1,2,3) == [1,2|3]
|
||||
assert ([&1, &2|&3]).(1, 2, 3) == [1, 2|3]
|
||||
end
|
||||
|
||||
test :wrap do
|
||||
assert List.wrap([1,2,3]) == [1,2,3]
|
||||
assert List.wrap([1, 2, 3]) == [1, 2, 3]
|
||||
assert List.wrap(1) == [1]
|
||||
assert List.wrap(nil) == []
|
||||
end
|
||||
|
||||
test :flatten do
|
||||
assert List.flatten([1,2,3]) == [1,2,3]
|
||||
assert List.flatten([1,[2],3]) == [1,2,3]
|
||||
assert List.flatten([[1,[2],3]]) == [1,2,3]
|
||||
assert List.flatten([1, 2, 3]) == [1, 2, 3]
|
||||
assert List.flatten([1, [2], 3]) == [1, 2, 3]
|
||||
assert List.flatten([[1, [2], 3]]) == [1, 2, 3]
|
||||
|
||||
assert List.flatten([]) == []
|
||||
assert List.flatten([[]]) == []
|
||||
end
|
||||
|
||||
test :flatten_with_tail do
|
||||
assert List.flatten([1,2,3], [4,5]) == [1,2,3,4,5]
|
||||
assert List.flatten([1,[2],3], [4,5]) == [1,2,3,4,5]
|
||||
assert List.flatten([[1,[2],3]], [4,5]) == [1,2,3,4,5]
|
||||
assert List.flatten([1, 2, 3], [4, 5]) == [1, 2, 3, 4, 5]
|
||||
assert List.flatten([1, [2], 3], [4, 5]) == [1, 2, 3, 4, 5]
|
||||
assert List.flatten([[1, [2], 3]], [4, 5]) == [1, 2, 3, 4, 5]
|
||||
end
|
||||
|
||||
test :foldl do
|
||||
assert List.foldl([1,2,3], 0, fn x,y -> x + y end) == 6
|
||||
assert List.foldl([1,2,3], 10, fn x,y -> x + y end) == 16
|
||||
assert List.foldl([1,2,3,4], 0, fn x,y -> x - y end) == 2
|
||||
assert List.foldl([1, 2, 3], 0, fn x, y -> x + y end) == 6
|
||||
assert List.foldl([1, 2, 3], 10, fn x, y -> x + y end) == 16
|
||||
assert List.foldl([1, 2, 3, 4], 0, fn x, y -> x - y end) == 2
|
||||
end
|
||||
|
||||
test :foldr do
|
||||
assert List.foldr([1,2,3], 0, fn x,y -> x + y end) == 6
|
||||
assert List.foldr([1,2,3], 10, fn x,y -> x + y end) == 16
|
||||
assert List.foldr([1,2,3,4], 0, fn x,y -> x - y end) == -2
|
||||
assert List.foldr([1, 2, 3], 0, fn x, y -> x + y end) == 6
|
||||
assert List.foldr([1, 2, 3], 10, fn x, y -> x + y end) == 16
|
||||
assert List.foldr([1, 2, 3, 4], 0, fn x, y -> x - y end) == -2
|
||||
end
|
||||
|
||||
test :concat_1 do
|
||||
assert List.concat([[1,[2],3], [4], [5,6]]) == [1,[2],3,4,5,6]
|
||||
assert List.concat([[1, [2], 3], [4], [5, 6]]) == [1, [2], 3, 4, 5, 6]
|
||||
end
|
||||
|
||||
test :concat_2 do
|
||||
assert List.concat([1,[2],3], [4,5]) == [1,[2],3,4,5]
|
||||
assert List.concat([1, [2], 3], [4, 5]) == [1, [2], 3, 4, 5]
|
||||
end
|
||||
|
||||
test :reverse do
|
||||
assert Enum.reverse([1,2,3]) == [3,2,1]
|
||||
assert Enum.reverse([1, 2, 3]) == [3, 2, 1]
|
||||
end
|
||||
|
||||
test :duplicate do
|
||||
assert List.duplicate(1, 3) == [1,1,1]
|
||||
assert List.duplicate(1, 3) == [1, 1, 1]
|
||||
assert List.duplicate([1], 1) == [[1]]
|
||||
end
|
||||
|
||||
|
||||
@@ -7,6 +7,10 @@ defmodule Macro.ExternalTest do
|
||||
18 = __CALLER__.location[:line]
|
||||
__FILE__ = __CALLER__.location[:file]
|
||||
end
|
||||
|
||||
defmacro oror(left, right) do
|
||||
quote do: unquote(left) || unquote(right)
|
||||
end
|
||||
end
|
||||
|
||||
defmodule MacroTest do
|
||||
@@ -14,8 +18,7 @@ defmodule MacroTest do
|
||||
|
||||
# Changing the lines above will make compilation
|
||||
# fail since we are assertnig on the caller lines
|
||||
require Macro.ExternalTest
|
||||
Macro.ExternalTest.external()
|
||||
import Macro.ExternalTest
|
||||
|
||||
## Escape
|
||||
|
||||
@@ -29,23 +32,23 @@ defmodule MacroTest do
|
||||
end
|
||||
|
||||
test :escape_simply_returns_any_other_structure do
|
||||
assert [1,2,3] == Macro.escape([1,2,3])
|
||||
assert [1, 2, 3] == Macro.escape([1, 2, 3])
|
||||
end
|
||||
|
||||
test :escape_works_recursively do
|
||||
assert [1,{:{}, [], [:a,:b,:c]},3] == Macro.escape([1, { :a, :b, :c },3])
|
||||
assert [1,{:{}, [], [:a,:b,:c]}, 3] == Macro.escape([1, { :a, :b, :c }, 3])
|
||||
end
|
||||
|
||||
test :escape_with_unquote do
|
||||
contents = quote unquote: false, do: unquote(1)
|
||||
assert Macro.escape_quoted(contents) == 1
|
||||
assert Macro.escape(contents, unquote: true) == 1
|
||||
|
||||
contents = quote unquote: false, do: unquote(x)
|
||||
assert Macro.escape_quoted(contents) == { :x, [], MacroTest }
|
||||
assert Macro.escape(contents, unquote: true) == { :x, [], MacroTest }
|
||||
end
|
||||
|
||||
defp eval_escaped(contents) do
|
||||
{ eval, [] } = Code.eval_quoted(Macro.escape_quoted(contents))
|
||||
{ eval, [] } = Code.eval_quoted(Macro.escape(contents, unquote: true))
|
||||
eval
|
||||
end
|
||||
|
||||
@@ -70,153 +73,175 @@ defmodule MacroTest do
|
||||
end
|
||||
|
||||
test :escape_with_splicing do
|
||||
contents = quote unquote: false, do: [1,2,3,4,5]
|
||||
assert Macro.escape_quoted(contents) == [1,2,3,4,5]
|
||||
contents = quote unquote: false, do: [1, 2, 3, 4, 5]
|
||||
assert Macro.escape(contents, unquote: true) == [1, 2, 3, 4, 5]
|
||||
|
||||
contents = quote unquote: false, do: [1,2,unquote_splicing([3,4,5])]
|
||||
assert eval_escaped(contents) == [1,2,3,4,5]
|
||||
contents = quote unquote: false, do: [1, 2, unquote_splicing([3, 4, 5])]
|
||||
assert eval_escaped(contents) == [1, 2, 3, 4, 5]
|
||||
|
||||
contents = quote unquote: false, do: [unquote_splicing([1,2,3]), 4, 5]
|
||||
assert eval_escaped(contents) == [1,2,3,4,5]
|
||||
contents = quote unquote: false, do: [unquote_splicing([1, 2, 3]), 4, 5]
|
||||
assert eval_escaped(contents) == [1, 2, 3, 4, 5]
|
||||
|
||||
contents = quote unquote: false, do: [unquote_splicing([1,2,3]), unquote_splicing([4, 5])]
|
||||
assert eval_escaped(contents) == [1,2,3,4,5]
|
||||
contents = quote unquote: false, do: [unquote_splicing([1, 2, 3]), unquote_splicing([4, 5])]
|
||||
assert eval_escaped(contents) == [1, 2, 3, 4, 5]
|
||||
|
||||
contents = quote unquote: false, do: [1, unquote_splicing([2]), 3, unquote_splicing([4]), 5]
|
||||
assert eval_escaped(contents) == [1,2,3,4,5]
|
||||
assert eval_escaped(contents) == [1, 2, 3, 4, 5]
|
||||
|
||||
contents = quote unquote: false, do: [1, unquote_splicing([2]), 3, unquote_splicing([4])|[5]]
|
||||
assert eval_escaped(contents) == [1,2,3,4,5]
|
||||
assert eval_escaped(contents) == [1, 2, 3, 4, 5]
|
||||
end
|
||||
|
||||
## Expand aliases
|
||||
|
||||
test :expand_with_raw_atom do
|
||||
assert Macro.expand(quote(do: :foo), __ENV__) == :foo
|
||||
test :expand_once do
|
||||
assert { :||, _, _ } = Macro.expand_once(quote(do: oror(1, false)), __ENV__)
|
||||
end
|
||||
|
||||
test :expand_with_current_module do
|
||||
assert Macro.expand(quote(do: __MODULE__), __ENV__) == __MODULE__
|
||||
test :expand_once_with_raw_atom do
|
||||
assert Macro.expand_once(quote(do: :foo), __ENV__) == :foo
|
||||
end
|
||||
|
||||
test :expand_with_main do
|
||||
assert Macro.expand(quote(do: Elixir), __ENV__) == Elixir
|
||||
test :expand_once_with_current_module do
|
||||
assert Macro.expand_once(quote(do: __MODULE__), __ENV__) == __MODULE__
|
||||
end
|
||||
|
||||
test :expand_with_simple_alias do
|
||||
assert Macro.expand(quote(do: Foo), __ENV__) == Foo
|
||||
test :expand_once_with_main do
|
||||
assert Macro.expand_once(quote(do: Elixir), __ENV__) == Elixir
|
||||
end
|
||||
|
||||
test :expand_with_current_module_plus_alias do
|
||||
assert Macro.expand(quote(do: __MODULE__.Foo), __ENV__) == __MODULE__.Foo
|
||||
test :expand_once_with_simple_alias do
|
||||
assert Macro.expand_once(quote(do: Foo), __ENV__) == Foo
|
||||
end
|
||||
|
||||
test :expand_with_main_plus_alias do
|
||||
assert Macro.expand(quote(do: Elixir.Foo), __ENV__) == Foo
|
||||
test :expand_once_with_current_module_plus_alias do
|
||||
assert Macro.expand_once(quote(do: __MODULE__.Foo), __ENV__) == __MODULE__.Foo
|
||||
end
|
||||
|
||||
test :expand_with_custom_alias do
|
||||
test :expand_once_with_main_plus_alias do
|
||||
assert Macro.expand_once(quote(do: Elixir.Foo), __ENV__) == Foo
|
||||
end
|
||||
|
||||
test :expand_once_with_custom_alias do
|
||||
alias Foo, as: Bar
|
||||
assert Macro.expand(quote(do: Bar.Baz), __ENV__) == Foo.Baz
|
||||
assert Macro.expand_once(quote(do: Bar.Baz), __ENV__) == Foo.Baz
|
||||
end
|
||||
|
||||
test :expand_with_main_plus_custom_alias do
|
||||
test :expand_once_with_main_plus_custom_alias do
|
||||
alias Foo, as: Bar
|
||||
assert Macro.expand(quote(do: Elixir.Bar.Baz), __ENV__) == Elixir.Bar.Baz
|
||||
assert Macro.expand_once(quote(do: Elixir.Bar.Baz), __ENV__) == Elixir.Bar.Baz
|
||||
end
|
||||
|
||||
test :expand_with_op do
|
||||
assert Macro.expand(quote(do: Foo.bar.Baz), __ENV__) == (quote do
|
||||
test :expand_once_with_op do
|
||||
assert Macro.expand_once(quote(do: Foo.bar.Baz), __ENV__) == (quote do
|
||||
Foo.bar.Baz
|
||||
end)
|
||||
end
|
||||
|
||||
test :expand_with_erlang do
|
||||
assert Macro.expand(quote(do: :foo), __ENV__) == :foo
|
||||
test :expand_once_with_erlang do
|
||||
assert Macro.expand_once(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
|
||||
test :expand_once_local_macro do
|
||||
assert Macro.expand_once(quote(do: local_macro), __ENV__) == :local_macro
|
||||
end
|
||||
|
||||
test :expand_with_imported_macro do
|
||||
assert Macro.expand(quote(do: 1 || false), __ENV__) == (quote context: Kernel do
|
||||
test :expand_once_with_imported_macro do
|
||||
assert Macro.expand_once(quote(do: 1 || false), __ENV__) == (quote context: Kernel do
|
||||
case 1 do
|
||||
oror in [false, nil] -> false
|
||||
oror -> oror
|
||||
var!(oror, false) in [false, nil] -> false
|
||||
var!(oror, false) -> var!(oror, false)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
test :expand_with_require_macro do
|
||||
assert Macro.expand(quote(do: Kernel.||(1, false)), __ENV__) == (quote context: Kernel do
|
||||
test :expand_once_with_require_macro do
|
||||
assert Macro.expand_once(quote(do: Kernel.||(1, false)), __ENV__) == (quote context: Kernel do
|
||||
case 1 do
|
||||
oror in [false, nil] -> false
|
||||
oror -> oror
|
||||
var!(oror, false) in [false, nil] -> false
|
||||
var!(oror, false) -> var!(oror, false)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
test :expand_with_not_expandable_expression do
|
||||
expr = quote(do: other(1,2,3))
|
||||
assert Macro.expand(expr, __ENV__) == expr
|
||||
test :expand_once_with_not_expandable_expression do
|
||||
expr = quote(do: other(1, 2, 3))
|
||||
assert Macro.expand_once(expr, __ENV__) == expr
|
||||
end
|
||||
|
||||
@foo 1
|
||||
@bar Macro.expand(quote(do: @foo), __ENV__)
|
||||
@bar Macro.expand_once(quote(do: @foo), __ENV__)
|
||||
|
||||
test :expand_with_module_at do
|
||||
test :expand_once_with_module_at do
|
||||
assert @bar == 1
|
||||
end
|
||||
|
||||
test :expand do
|
||||
assert Macro.expand(quote(do: oror(1, false)), __ENV__) == (quote context: Kernel do
|
||||
case 1 do
|
||||
var!(oror, false) in [false, nil] -> false
|
||||
var!(oror, false) -> var!(oror, false)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
test :expand_all do
|
||||
assert Macro.expand_all(quote(do: oror(1, local_macro)), __ENV__) == (quote context: Kernel do
|
||||
case 1 do
|
||||
var!(oror, false) in [false, nil] -> :local_macro
|
||||
var!(oror, false) -> var!(oror, false)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
## to_binary
|
||||
|
||||
test :var_to_binary do
|
||||
assert Macro.to_binary(quote do: foo) == "foo"
|
||||
assert Macro.to_string(quote do: foo) == "foo"
|
||||
end
|
||||
|
||||
test :local_call_to_binary do
|
||||
assert Macro.to_binary(quote do: foo(1, 2, 3)) == "foo(1, 2, 3)"
|
||||
assert Macro.to_binary(quote do: foo([1, 2, 3])) == "foo([1, 2, 3])"
|
||||
assert Macro.to_string(quote do: foo(1, 2, 3)) == "foo(1, 2, 3)"
|
||||
assert Macro.to_string(quote do: foo([1, 2, 3])) == "foo([1, 2, 3])"
|
||||
end
|
||||
|
||||
test :remote_call_to_binary do
|
||||
assert Macro.to_binary(quote do: foo.bar(1, 2, 3)) == "foo.bar(1, 2, 3)"
|
||||
assert Macro.to_binary(quote do: foo.bar([1, 2, 3])) == "foo.bar([1, 2, 3])"
|
||||
assert Macro.to_string(quote do: foo.bar(1, 2, 3)) == "foo.bar(1, 2, 3)"
|
||||
assert Macro.to_string(quote do: foo.bar([1, 2, 3])) == "foo.bar([1, 2, 3])"
|
||||
end
|
||||
|
||||
test :low_atom_remote_call_to_binary do
|
||||
assert Macro.to_binary(quote do: :foo.bar(1, 2, 3)) == ":foo.bar(1, 2, 3)"
|
||||
assert Macro.to_string(quote do: :foo.bar(1, 2, 3)) == ":foo.bar(1, 2, 3)"
|
||||
end
|
||||
|
||||
test :big_atom_remote_call_to_binary do
|
||||
assert Macro.to_binary(quote do: Foo.Bar.bar(1, 2, 3)) == "Foo.Bar.bar(1, 2, 3)"
|
||||
assert Macro.to_string(quote do: Foo.Bar.bar(1, 2, 3)) == "Foo.Bar.bar(1, 2, 3)"
|
||||
end
|
||||
|
||||
test :remote_and_fun_call_to_binary do
|
||||
assert Macro.to_binary(quote do: foo.bar.(1, 2, 3)) == "foo.bar().(1, 2, 3)"
|
||||
assert Macro.to_binary(quote do: foo.bar.([1, 2, 3])) == "foo.bar().([1, 2, 3])"
|
||||
assert Macro.to_string(quote do: foo.bar.(1, 2, 3)) == "foo.bar().(1, 2, 3)"
|
||||
assert Macro.to_string(quote do: foo.bar.([1, 2, 3])) == "foo.bar().([1, 2, 3])"
|
||||
end
|
||||
|
||||
test :atom_call_to_binary do
|
||||
assert Macro.to_binary(quote do: :foo.(1, 2, 3)) == ":foo.(1, 2, 3)"
|
||||
assert Macro.to_string(quote do: :foo.(1, 2, 3)) == ":foo.(1, 2, 3)"
|
||||
end
|
||||
|
||||
test :aliases_call_to_binary do
|
||||
assert Macro.to_binary(quote do: Foo.Bar.baz(1, 2, 3)) == "Foo.Bar.baz(1, 2, 3)"
|
||||
assert Macro.to_binary(quote do: Foo.Bar.baz([1, 2, 3])) == "Foo.Bar.baz([1, 2, 3])"
|
||||
assert Macro.to_string(quote do: Foo.Bar.baz(1, 2, 3)) == "Foo.Bar.baz(1, 2, 3)"
|
||||
assert Macro.to_string(quote do: Foo.Bar.baz([1, 2, 3])) == "Foo.Bar.baz([1, 2, 3])"
|
||||
end
|
||||
|
||||
test :arrow_to_binary do
|
||||
assert Macro.to_binary(quote do: foo(1, (2 -> 3))) == "foo(1, (2 -> 3))"
|
||||
assert Macro.to_string(quote do: foo(1, (2 -> 3))) == "foo(1, (2 -> 3))"
|
||||
end
|
||||
|
||||
test :blocks_to_binary do
|
||||
assert Macro.to_binary(quote do: (1; 2; (:foo; :bar); 3)) <> "\n" == """
|
||||
assert Macro.to_string(quote do: (1; 2; (:foo; :bar); 3)) <> "\n" == """
|
||||
(
|
||||
1
|
||||
2
|
||||
@@ -230,7 +255,7 @@ defmodule MacroTest do
|
||||
end
|
||||
|
||||
test :if_else_to_binary do
|
||||
assert Macro.to_binary(quote do: (if foo, do: bar, else: baz)) <> "\n" == """
|
||||
assert Macro.to_string(quote do: (if foo, do: bar, else: baz)) <> "\n" == """
|
||||
if(foo) do
|
||||
bar
|
||||
else
|
||||
@@ -240,7 +265,7 @@ defmodule MacroTest do
|
||||
end
|
||||
|
||||
test :case_to_binary do
|
||||
assert Macro.to_binary(quote do: (case foo do true -> 0; false -> (1; 2) end)) <> "\n" == """
|
||||
assert Macro.to_string(quote do: (case foo do true -> 0; false -> (1; 2) end)) <> "\n" == """
|
||||
case(foo) do
|
||||
true ->
|
||||
0
|
||||
@@ -252,18 +277,17 @@ defmodule MacroTest do
|
||||
end
|
||||
|
||||
test :fn_to_binary do
|
||||
assert Macro.to_binary(quote do: (() -> x)) == "(() -> x)"
|
||||
assert Macro.to_binary(quote do: (fn -> 1 + 2 end)) == "fn -> 1 + 2 end"
|
||||
assert Macro.to_binary(quote do: (fn(x) -> x + 1 end)) == "fn x -> x + 1 end"
|
||||
assert Macro.to_string(quote do: (fn -> 1 + 2 end)) == "fn -> 1 + 2 end"
|
||||
assert Macro.to_string(quote do: (fn(x) -> x + 1 end)) == "fn x -> x + 1 end"
|
||||
|
||||
assert Macro.to_binary(quote do: (fn(x) -> y = x + 1; y end)) <> "\n" == """
|
||||
assert Macro.to_string(quote do: (fn(x) -> y = x + 1; y end)) <> "\n" == """
|
||||
fn x ->
|
||||
y = (x + 1)
|
||||
y
|
||||
end
|
||||
"""
|
||||
|
||||
assert Macro.to_binary(quote do: (fn(x) -> y = x + 1; y; (z) -> z end)) <> "\n" == """
|
||||
assert Macro.to_string(quote do: (fn(x) -> y = x + 1; y; (z) -> z end)) <> "\n" == """
|
||||
fn
|
||||
x ->
|
||||
y = (x + 1)
|
||||
@@ -274,12 +298,19 @@ defmodule MacroTest do
|
||||
"""
|
||||
end
|
||||
|
||||
test :when do
|
||||
assert Macro.to_string(quote do: (() -> x)) == "(() -> x)"
|
||||
assert Macro.to_string(quote do: (x when y -> z)) == "(x when y -> z)"
|
||||
assert Macro.to_string(quote do: (x, y when z -> w)) == "((x, y) when z -> w)"
|
||||
assert Macro.to_string(quote do: ((x, y) when z -> w)) == "((x, y) when z -> w)"
|
||||
end
|
||||
|
||||
test :partial_to_binary do
|
||||
assert Macro.to_binary(quote do: identity(&1)) == "identity(&1)"
|
||||
assert Macro.to_string(quote do: identity(&1)) == "identity(&1)"
|
||||
end
|
||||
|
||||
test :nested_to_binary do
|
||||
assert Macro.to_binary(quote do: (defmodule Foo do def foo do 1 + 1 end end)) <> "\n" == """
|
||||
assert Macro.to_string(quote do: (defmodule Foo do def foo do 1 + 1 end end)) <> "\n" == """
|
||||
defmodule(Foo) do
|
||||
def(foo) do
|
||||
1 + 1
|
||||
@@ -289,27 +320,27 @@ defmodule MacroTest do
|
||||
end
|
||||
|
||||
test :op_precedence_to_binary do
|
||||
assert Macro.to_binary(quote do: (1 + 2) * (3 - 4)) == "(1 + 2) * (3 - 4)"
|
||||
assert Macro.to_binary(quote do: ((1 + 2) * 3) - 4) == "((1 + 2) * 3) - 4"
|
||||
assert Macro.to_string(quote do: (1 + 2) * (3 - 4)) == "(1 + 2) * (3 - 4)"
|
||||
assert Macro.to_string(quote do: ((1 + 2) * 3) - 4) == "((1 + 2) * 3) - 4"
|
||||
end
|
||||
|
||||
test :containers_to_binary do
|
||||
assert Macro.to_binary(quote do: { 1, 2, 3 }) == "{1, 2, 3}"
|
||||
assert Macro.to_binary(quote do: [ 1, 2, 3 ]) == "[1, 2, 3]"
|
||||
assert Macro.to_binary(quote do: << 1, 2, 3 >>) == "<<1, 2, 3>>"
|
||||
assert Macro.to_string(quote do: { 1, 2, 3 }) == "{1, 2, 3}"
|
||||
assert Macro.to_string(quote do: [ 1, 2, 3 ]) == "[1, 2, 3]"
|
||||
assert Macro.to_string(quote do: << 1, 2, 3 >>) == "<<1, 2, 3>>"
|
||||
end
|
||||
|
||||
test :binary_ops_to_binary do
|
||||
assert Macro.to_binary(quote do: 1 + 2) == "1 + 2"
|
||||
assert Macro.to_binary(quote do: [ 1, 2 | 3 ]) == "[1, 2 | 3]"
|
||||
assert Macro.to_binary(quote do: [h|t] = [1,2,3]) == "[h | t] = [1, 2, 3]"
|
||||
assert Macro.to_string(quote do: 1 + 2) == "1 + 2"
|
||||
assert Macro.to_string(quote do: [ 1, 2 | 3 ]) == "[1, 2 | 3]"
|
||||
assert Macro.to_string(quote do: [h|t] = [1, 2, 3]) == "[h | t] = [1, 2, 3]"
|
||||
end
|
||||
|
||||
test :unary_ops_to_binary do
|
||||
assert Macro.to_binary(quote do: not 1) == "not 1"
|
||||
assert Macro.to_binary(quote do: not foo) == "not foo"
|
||||
assert Macro.to_binary(quote do: -1) == "-1"
|
||||
assert Macro.to_binary(quote do: @foo(bar)) == "@foo(bar)"
|
||||
assert Macro.to_string(quote do: not 1) == "not 1"
|
||||
assert Macro.to_string(quote do: not foo) == "not foo"
|
||||
assert Macro.to_string(quote do: -1) == "-1"
|
||||
assert Macro.to_string(quote do: @foo(bar)) == "@foo(bar)"
|
||||
end
|
||||
|
||||
## safe_term
|
||||
@@ -320,12 +351,12 @@ defmodule MacroTest do
|
||||
assert Macro.safe_term(quote do: -1) == :ok
|
||||
assert Macro.safe_term(quote do: +1) == :ok
|
||||
assert Macro.safe_term(quote do: []) == :ok
|
||||
assert Macro.safe_term(quote do: [1,2,3]) == :ok
|
||||
assert Macro.safe_term(quote do: [1, 2, 3]) == :ok
|
||||
assert Macro.safe_term(quote do: "") == :ok
|
||||
assert Macro.safe_term(quote do: {}) == :ok
|
||||
assert Macro.safe_term(quote do: {1,2}) == :ok
|
||||
assert Macro.safe_term(quote do: {1,2,3}) == :ok
|
||||
assert Macro.safe_term(quote do: {1,2,3,4}) == :ok
|
||||
assert Macro.safe_term(quote do: {1, 2}) == :ok
|
||||
assert Macro.safe_term(quote do: {1, 2, 3}) == :ok
|
||||
assert Macro.safe_term(quote do: {1, 2, 3, 4}) == :ok
|
||||
assert Macro.safe_term(quote do: Alias) == :ok
|
||||
end
|
||||
|
||||
@@ -341,8 +372,8 @@ defmodule MacroTest do
|
||||
assert Macro.extract_args(quote do: foo) == { :foo, [] }
|
||||
assert Macro.extract_args(quote do: foo()) == { :foo, [] }
|
||||
assert Macro.extract_args(quote do: :foo.()) == { :foo, [] }
|
||||
assert Macro.extract_args(quote do: foo(1,2,3)) == { :foo, [1,2,3] }
|
||||
assert Macro.extract_args(quote do: 1.(1,2,3)) == :error
|
||||
assert Macro.extract_args(quote do: foo(1, 2, 3)) == { :foo, [1, 2, 3] }
|
||||
assert Macro.extract_args(quote do: 1.(1, 2, 3)) == :error
|
||||
end
|
||||
|
||||
## env
|
||||
@@ -355,4 +386,4 @@ defmodule MacroTest do
|
||||
env = env.module(nil)
|
||||
assert env.stacktrace == [{ :elixir_compiler, :__FILE__, 2, [file: "foo", line: 12] }]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -102,7 +102,7 @@ defmodule ModuleTest do
|
||||
assert env.module == ModuleTest.OnDefinition
|
||||
assert kind == :def
|
||||
assert name == :hello
|
||||
assert [{ :foo, _, _ }, { :bar, _ ,_ }] = args
|
||||
assert [{ :foo, _, _ }, { :bar, _ , _ }] = args
|
||||
assert [] = guards
|
||||
assert [do: { :+, _, [{ :foo, _, _ }, { :bar, _, _ }] }] = expr
|
||||
end
|
||||
@@ -132,24 +132,24 @@ defmodule ModuleTest do
|
||||
## Attributes
|
||||
|
||||
test :reserved_attributes do
|
||||
assert List.keyfind(ExUnit.Server.__info__(:attributes), :behavior, 0) == {:behavior,[:gen_server]}
|
||||
assert List.keyfind(ExUnit.Server.__info__(:attributes), :behavior, 0) == {:behavior, [:gen_server]}
|
||||
end
|
||||
|
||||
test :persisted_attributes do
|
||||
assert [{:register_example,[:it_works]},{:register_example,[:still_works]}] ==
|
||||
assert [{:register_example, [:it_works]}, {:register_example, [:still_works]}] ==
|
||||
Enum.filter __MODULE__.__info__(:attributes), match?({ :register_example, _ }, &1)
|
||||
end
|
||||
|
||||
test :duplicated_attributes do
|
||||
assert [{:vsn,_},{:foo,[1]},{:foo,[2]},{:foo,[3]}] = ModuleTest.DuplicateAttribute.__info__(:attributes)
|
||||
assert [{:vsn, _}, {:foo, [1]}, {:foo, [2]}, {:foo, [3]}] = ModuleTest.DuplicateAttribute.__info__(:attributes)
|
||||
end
|
||||
|
||||
@some_attribute [1]
|
||||
@other_attribute [3,2,1]
|
||||
@other_attribute [3, 2, 1]
|
||||
|
||||
test :inside_function_attributes do
|
||||
assert [1] = @some_attribute
|
||||
assert [3,2,1] = @other_attribute
|
||||
assert [3, 2, 1] = @other_attribute
|
||||
end
|
||||
|
||||
## Naming
|
||||
@@ -183,6 +183,10 @@ defmodule ModuleTest do
|
||||
assert Module.to_binary(Hello.World) == "Hello.World"
|
||||
end
|
||||
|
||||
test :__MODULE__ do
|
||||
assert Code.eval_string("__MODULE__.Foo") |> elem(0) == Foo
|
||||
end
|
||||
|
||||
## Creation
|
||||
|
||||
test :defmodule do
|
||||
@@ -191,6 +195,12 @@ defmodule ModuleTest do
|
||||
end)
|
||||
end
|
||||
|
||||
test :defmodule_with_atom do
|
||||
assert match?({ :module, :root_defmodule, _, _ }, defmodule :root_defmodule do
|
||||
:ok
|
||||
end)
|
||||
end
|
||||
|
||||
test :create do
|
||||
contents =
|
||||
quote do
|
||||
@@ -228,7 +238,7 @@ defmodule ModuleTest do
|
||||
|
||||
test :definitions_in do
|
||||
in_module do
|
||||
def foo(1,2,3), do: 4
|
||||
def foo(1, 2, 3), do: 4
|
||||
|
||||
assert Module.definitions_in(__MODULE__) == [foo: 3]
|
||||
assert Module.definitions_in(__MODULE__, :def) == [foo: 3]
|
||||
|
||||
@@ -3,11 +3,13 @@ Code.require_file "test_helper.exs", __DIR__
|
||||
defmodule PathTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
test :wildcard do
|
||||
import PathHelpers
|
||||
|
||||
if :file.native_name_encoding == :utf8 do
|
||||
assert Path.wildcard(fixture_path("héllò")) == [fixture_path("héllò")]
|
||||
if :file.native_name_encoding == :utf8 do
|
||||
test :wildcard do
|
||||
import PathHelpers
|
||||
File.mkdir_p(tmp_path("héllò"))
|
||||
assert Path.wildcard(tmp_path("héllò")) == [tmp_path("héllò")]
|
||||
after
|
||||
File.rm_rf tmp_path("héllò")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -79,9 +81,9 @@ defmodule PathTest do
|
||||
end
|
||||
|
||||
test :absname_with_binary do
|
||||
assert Path.absname("/foo/bar") == "/foo/bar"
|
||||
assert Path.absname("/foo/bar/") == "/foo/bar"
|
||||
assert Path.absname("/foo/bar/../bar") == "/foo/bar/../bar"
|
||||
assert (Path.absname("/foo/bar") |> strip_drive_letter_if_windows) == "/foo/bar"
|
||||
assert (Path.absname("/foo/bar/") |> strip_drive_letter_if_windows) == "/foo/bar"
|
||||
assert (Path.absname("/foo/bar/../bar") |> strip_drive_letter_if_windows) == "/foo/bar/../bar"
|
||||
|
||||
assert Path.absname("bar", "/foo") == "/foo/bar"
|
||||
assert Path.absname("bar/", "/foo") == "/foo/bar"
|
||||
@@ -91,10 +93,10 @@ defmodule PathTest do
|
||||
end
|
||||
|
||||
test :absname_with_list do
|
||||
assert Path.absname('/foo/bar') == '/foo/bar'
|
||||
assert Path.absname('/foo/bar/') == '/foo/bar'
|
||||
assert Path.absname('/foo/bar/.') == '/foo/bar/.'
|
||||
assert Path.absname('/foo/bar/../bar') == '/foo/bar/../bar'
|
||||
assert (Path.absname('/foo/bar') |> strip_drive_letter_if_windows) == '/foo/bar'
|
||||
assert (Path.absname('/foo/bar/') |> strip_drive_letter_if_windows) == '/foo/bar'
|
||||
assert (Path.absname('/foo/bar/.') |> strip_drive_letter_if_windows) == '/foo/bar/.'
|
||||
assert (Path.absname('/foo/bar/../bar') |> strip_drive_letter_if_windows) == '/foo/bar/../bar'
|
||||
end
|
||||
|
||||
test :expand_path_with_user_home do
|
||||
@@ -113,26 +115,26 @@ defmodule PathTest do
|
||||
end
|
||||
|
||||
test :expand_path_with_binary do
|
||||
assert Path.expand("/foo/bar") == "/foo/bar"
|
||||
assert Path.expand("/foo/bar/") == "/foo/bar"
|
||||
assert Path.expand("/foo/bar/.") == "/foo/bar"
|
||||
assert Path.expand("/foo/bar/../bar") == "/foo/bar"
|
||||
assert (Path.expand("/foo/bar") |> strip_drive_letter_if_windows) == "/foo/bar"
|
||||
assert (Path.expand("/foo/bar/") |> strip_drive_letter_if_windows) == "/foo/bar"
|
||||
assert (Path.expand("/foo/bar/.") |> strip_drive_letter_if_windows)== "/foo/bar"
|
||||
assert (Path.expand("/foo/bar/../bar") |> strip_drive_letter_if_windows) == "/foo/bar"
|
||||
|
||||
assert Path.expand("bar", "/foo") == "/foo/bar"
|
||||
assert Path.expand("bar/", "/foo") == "/foo/bar"
|
||||
assert Path.expand("bar/.", "/foo") == "/foo/bar"
|
||||
assert Path.expand("bar/../bar", "/foo") == "/foo/bar"
|
||||
assert Path.expand("../bar/../bar", "/foo/../foo/../foo") == "/bar"
|
||||
assert (Path.expand("bar", "/foo") |> strip_drive_letter_if_windows)== "/foo/bar"
|
||||
assert (Path.expand("bar/", "/foo") |> strip_drive_letter_if_windows)== "/foo/bar"
|
||||
assert (Path.expand("bar/.", "/foo") |> strip_drive_letter_if_windows)== "/foo/bar"
|
||||
assert (Path.expand("bar/../bar", "/foo") |> strip_drive_letter_if_windows)== "/foo/bar"
|
||||
assert (Path.expand("../bar/../bar", "/foo/../foo/../foo")|> strip_drive_letter_if_windows) == "/bar"
|
||||
|
||||
full = Path.expand("foo/bar")
|
||||
assert Path.expand("bar/../bar", "foo") == full
|
||||
end
|
||||
|
||||
test :expand_path_with_list do
|
||||
assert Path.expand('/foo/bar') == '/foo/bar'
|
||||
assert Path.expand('/foo/bar/') == '/foo/bar'
|
||||
assert Path.expand('/foo/bar/.') == '/foo/bar'
|
||||
assert Path.expand('/foo/bar/../bar') == '/foo/bar'
|
||||
assert (Path.expand('/foo/bar')|> strip_drive_letter_if_windows) == '/foo/bar'
|
||||
assert (Path.expand('/foo/bar/')|> strip_drive_letter_if_windows) == '/foo/bar'
|
||||
assert (Path.expand('/foo/bar/.')|> strip_drive_letter_if_windows) == '/foo/bar'
|
||||
assert (Path.expand('/foo/bar/../bar')|> strip_drive_letter_if_windows) == '/foo/bar'
|
||||
end
|
||||
|
||||
test :relative_to_with_binary do
|
||||
@@ -248,4 +250,11 @@ defmodule PathTest do
|
||||
assert Path.split('foo') == ['foo']
|
||||
assert Path.split('/foo/bar') == ['/', 'foo', 'bar']
|
||||
end
|
||||
|
||||
if match? { :win32, _ }, :os.type do
|
||||
defp strip_drive_letter_if_windows([_d,?:|rest]), do: rest
|
||||
defp strip_drive_letter_if_windows(<<_d,?:,rest::binary>>), do: rest
|
||||
else
|
||||
defp strip_drive_letter_if_windows(path), do: path
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
Code.require_file "test_helper.exs", __DIR__
|
||||
|
||||
defmodule ProcessTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
test :self do
|
||||
assert is_pid(Process.self)
|
||||
end
|
||||
end
|
||||
@@ -67,10 +67,10 @@ defmodule ProtocolTest do
|
||||
assert_undef(ProtocolTest.WithAll, Number, 1)
|
||||
assert_undef(ProtocolTest.WithAll, Number, 1.1)
|
||||
assert_undef(ProtocolTest.WithAll, List, [])
|
||||
assert_undef(ProtocolTest.WithAll, List, [1,2,3])
|
||||
assert_undef(ProtocolTest.WithAll, List, [1, 2, 3])
|
||||
assert_undef(ProtocolTest.WithAll, Tuple, {})
|
||||
assert_undef(ProtocolTest.WithAll, Tuple, {1,2,3})
|
||||
assert_undef(ProtocolTest.WithAll, Tuple, {Bar,2,3})
|
||||
assert_undef(ProtocolTest.WithAll, Tuple, {1, 2, 3})
|
||||
assert_undef(ProtocolTest.WithAll, Tuple, {Bar, 2, 3})
|
||||
assert_undef(ProtocolTest.WithAll, BitString, "foo")
|
||||
assert_undef(ProtocolTest.WithAll, BitString, <<1>>)
|
||||
assert_undef(ProtocolTest.WithAll, PID, self)
|
||||
@@ -81,7 +81,7 @@ defmodule ProtocolTest do
|
||||
test :protocol_with_except do
|
||||
assert_undef(ProtocolTest.WithExcept, Any, :foo)
|
||||
assert_undef(ProtocolTest.WithExcept, Any, 1)
|
||||
assert_undef(ProtocolTest.WithExcept, Any, [1,2,3])
|
||||
assert_undef(ProtocolTest.WithExcept, Any, [1, 2, 3])
|
||||
assert_undef(ProtocolTest.WithExcept, Function, fn(x) -> x end)
|
||||
assert_undef(ProtocolTest.WithExcept, Tuple, {})
|
||||
end
|
||||
@@ -116,10 +116,10 @@ defmodule ProtocolTest do
|
||||
assert_protocol_for(ProtocolTest.WithAll, Number, 1)
|
||||
assert_protocol_for(ProtocolTest.WithAll, Number, 1.1)
|
||||
assert_protocol_for(ProtocolTest.WithAll, List, [])
|
||||
assert_protocol_for(ProtocolTest.WithAll, List, [1,2,3])
|
||||
assert_protocol_for(ProtocolTest.WithAll, List, [1, 2, 3])
|
||||
assert_protocol_for(ProtocolTest.WithAll, Tuple, {})
|
||||
assert_protocol_for(ProtocolTest.WithAll, Tuple, {1,2,3})
|
||||
assert_protocol_for(ProtocolTest.WithAll, Tuple, {Bar,2,3})
|
||||
assert_protocol_for(ProtocolTest.WithAll, Tuple, {1, 2, 3})
|
||||
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, Kernel.self)
|
||||
@@ -129,7 +129,7 @@ defmodule ProtocolTest do
|
||||
|
||||
test :protocol_docs do
|
||||
docs = ProtocolTest.WithAll.__info__(:docs)
|
||||
assert { { :blank, 1 }, _, :def, [{ :thing,_,nil }], "Blank" } = List.keyfind(docs, { :blank, 1 }, 0)
|
||||
assert { { :blank, 1 }, _, :def, [{ :thing, _, nil }], "Blank" } = List.keyfind(docs, { :blank, 1 }, 0)
|
||||
end
|
||||
|
||||
test :protocol_with_two_items do
|
||||
@@ -151,10 +151,10 @@ defmodule ProtocolTest do
|
||||
|
||||
test :protocol_callback do
|
||||
assert get_callbacks(ProtocolTest.WithOnly, :blank, 1) ==
|
||||
[{:type,17,:fun,[{:type,17,:product,[{:type,17,:t,[]}]},{:type,17,:term,[]}]}]
|
||||
[{:type, 17, :fun, [{:type, 17, :product, [{:type, 17, :t, []}]}, {:type, 17, :term, []}]}]
|
||||
|
||||
assert get_callbacks(ProtocolTest.WithExcept, :blank, 1) ==
|
||||
[{:type,11,:fun,[{:type,11,:product,[{:type,11,:t,[]}]},{:type,11,:boolean,[]}]}]
|
||||
[{:type, 11, :fun, [{:type, 11, :product, [{:type, 11, :t, []}]}, {:type, 11, :boolean, []}]}]
|
||||
end
|
||||
|
||||
defp get_callbacks(module, name, arity) do
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user