Compare commits

...
180 Commits
Author SHA1 Message Date
José Valim 32d4739dad Release v0.9.3 2013-06-23 23:15:58 +02:00
meh 650cfc0448 Use semver for development version
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2013-06-23 23:15:36 +02:00
José Valim d4d824022f Update docs for Record.import/2 2013-06-23 22:22:15 +02:00
José Valim c6284557e7 Rename sigils from __ to sigil_$, closes #1301 2013-06-23 20:38:37 +02:00
José Valim a8a2f86a23 Record type should be private by default, closes #1288 2013-06-23 20:20:47 +02:00
José Valim fc219e7dca IO.ANSI now uses binding 2013-06-23 19:32:58 +02:00
José Valim 96a103d222 Allow :binding as an option in quote
This commit also adds Macro.expand_once/2 and Macro.expand_all/2.
2013-06-23 19:23:32 +02:00
José Valim f525f5b8cb Add unquote: true support to Macro.escape 2013-06-23 16:32:07 +02:00
José Valim 5c30a687fa Inspect module before printing, closes #1309 2013-06-23 15:27:04 +02:00
José Valim 9ffe8b0e97 Clean up defdelegate docs 2013-06-23 13:36:01 +02:00
José Valim 12c8564a26 Update CHANGELOG after clean up on the operators table
by making it sane and clean in case we want to add
custom operators in the future.

The operator table with suggestions of extension points
(not yet implemented) can be found in the elixir_parser.yrl
file.

The only backwards incompatible change is the precedence
of the operators used by the Bitwise module.
2013-06-23 13:20:02 +02:00
José Valim 903aa60fec Finish operator table clean up 2013-06-23 13:18:25 +02:00
José Valim 969bc968ee Move ++, --, ** and // to new table 2013-06-23 13:18:25 +02:00
José Valim e941d78daf Move operator table clean up 2013-06-23 13:18:25 +02:00
José Valim 8d3475825b Group arrow-like ops together 2013-06-23 13:18:25 +02:00
José Valim 68c890e179 Move and and or operators to the new table 2013-06-23 13:18:25 +02:00
José Valim 6f73d7dbb9 Move comp_op to the new table 2013-06-23 13:18:25 +02:00
José Valim c2ac967123 Move all unary operators to new schema 2013-06-23 13:18:25 +02:00
José Valim dd573c9806 Initial work cleaning up op table 2013-06-23 13:18:25 +02:00
Yurii Rashkovskii 58ee415e63 Improve mix escript_app's documentation 2013-06-22 18:25:09 +01:00
José Valim 561397da7a Ensure escript_app doesn't break the app 2013-06-22 19:23:03 +02:00
José Valim 3d2438c625 Ensure esriptize does not try to read directories 2013-06-22 19:14:02 +02:00
José Valim a08710bfda Update CHANGELOG 2013-06-22 19:00:40 +02:00
José Valim 151695b43c Add tests for nested whens 2013-06-22 18:53:18 +02:00
José Valim 2d8259b432 Support (x, y) when z, closes #1313 2013-06-22 18:53:18 +02:00
José Valim ae03e10db5 Merge pull request #1300 from jdrexler/issue-1285
Added chgrp!, chown!, and chmod! to module File
2013-06-22 05:10:43 -07:00
José Valim 208fbaf5ef Fix typo (tks @ericmj) 2013-06-22 14:02:10 +02:00
José Valim 820ca0a57e Do not use Enum on bootstrap 2013-06-22 13:55:33 +02:00
José Valim cf309ede14 Add docs to Mix.Version and make version always the first arg 2013-06-22 13:43:41 +02:00
José Valim da0a95695d Allow types to be given to defrecordp
Part of #1288
2013-06-22 13:15:00 +02:00
José Valim b2541a1b93 Inline list funcs so we don't pay an extra dispatch cost 2013-06-22 13:15:00 +02:00
Johannes Drexler 5df40f7c42 Added functions for changing mode, owner and group to module File 2013-06-21 21:07:05 +02:00
José Valim 5e24dbaddb Merge pull request #1308 from justinweiss/patch-1
Fixed some incorrect markdown in binary_to_list/3's documentation.
2013-06-21 10:09:08 -07:00
Justin Weiss 3f8e3d4d38 Fixed some incorrect markdown in binary_to_list/3's documentation.
The docs for binary_to_list/3 weren't monospacing their reference to binary_to_list/1, which resulted in some bad italicization.
2013-06-21 10:38:04 -06:00
José Valim 391fee3ea2 ^x variables should always refer to the value before the expression
Closes #1307
2013-06-21 18:27:16 +02:00
José Valim 6cee8572e0 Clean up ExUnit tests 2013-06-21 15:16:02 +02:00
vanstee f2d9e802cd Include description and improve formatting
Switching from `AssertionError` to `ExpectationError` gives us a nicer
error message and allows us to easily include the incorrect result as a
description.

Fixes #1276

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2013-06-21 15:08:48 +02:00
José Valim 5247961185 Optimize temp_vars lookup 2013-06-21 11:43:41 +02:00
José Valim 53a2618cba Merge pull request #1302 from jwarwick/typos
Fixed doc typos
2013-06-20 23:36:04 -07:00
John Warwick d360addc65 Fixed ex_doc path in README 2013-06-20 21:37:47 -04:00
John Warwick 08e7da305c Fixed typos in file.ex docs 2013-06-20 21:35:47 -04:00
José Valim afab1f8d73 Fix typo on assertion message 2013-06-20 23:15:53 +02:00
José Valim 749266b649 Merge pull request #1299 from vanstee/assert-statement-if-boolean
Show original statement if assert or refute fails
2013-06-20 13:46:32 -07:00
José Valim c4dcaf540d Slight clean up on erlang tests 2013-06-20 22:44:22 +02:00
vanstee 561d6ea0d1 Show original statement if assert or refute fails
If the asserted statement evaluates to false, show the unevaluated
statement in the assertion error output message for more context about
the failure. Refute also now follows similar semantics.

Fixes #1276
2013-06-20 16:17:55 -04:00
José Valim 1574f61a8b Output next steps information after creating repos, closes #1297 2013-06-20 14:04:35 +02:00
José Valim 7dc941227f Ensure escriptize is performed recursively, closes #1296 2013-06-20 13:49:30 +02:00
José Valim ca026039a3 Stop parsing on -S, closes #1294 2013-06-20 12:25:00 +02:00
José Valim 4b06a2d316 Merge pull request #1293 from ToJans/WindowsBugfixing
Updated elixir.bat so one can also pass in non ".ex" files
2013-06-20 03:22:07 -07:00
Tom Janssens 00299fe7cb Removed "*.ex/file" parameter checking from elixir.bat
If it now finds an unknown parameter, it assumes all paremeters are parsed
and just passes all the rest to the elixir script.
2013-06-20 12:17:24 +02:00
José Valim 9e6b650efd Merge pull request #1295 from ToJans/WindowsRebarFix
Fix the rebar wrapper on windows
2013-06-20 03:00:49 -07:00
José Valim c56d0760f6 Update CHANGELOG 2013-06-20 12:00:02 +02:00
Tom Janssens aedc78536f Fix the rebar wrapper on windows
System.find_executable for rebar returns a rebar.cmd file, which should not
be wrapped by escript. We now have an extra check that verifies whether the
invoked file is indeed a cmd file.
2013-06-20 11:56:20 +02:00
José Valim 0c17fa0ab2 Use .bat executables on Windows 2013-06-20 09:08:51 +02:00
José Valim 752a09c988 Update error message in ExUnit 2013-06-20 08:36:49 +02:00
José Valim d5a8821ec1 Merge pull request #1292 from devinus/github-git-protocol
Use the significantly faster Git protocol for github: dependencies
2013-06-19 14:47:02 -07:00
Devin Torres aa7400aaad Use the significantly faster Git protocol for github: dependencies
See: http://git-scm.com/book/ch4-1.html#The-Git-Protocol
2013-06-19 16:17:46 -05:00
José Valim d849fc5dca Merge pull request #1289 from alco/typespec-doc-formatting
Fix formatting issues in Typespec's docs
2013-06-19 10:32:37 -07:00
Alexei Sholik 3d73d1a359 Fix formatting issues in Typespec's docs 2013-06-19 20:29:58 +03:00
José Valim 18bcef3ef4 Fix a bug when inspecting records nested in tuples 2013-06-19 19:17:15 +02:00
José Valim 115fd99bfb Move .gitattributes files to repo root 2013-06-19 18:42:44 +02:00
José Valim 879499b409 Deprecate (fun(...) -> ...) spec style
We are deprecating this style because it reads ambiguous
as a function that receives another function as argument.
2013-06-19 18:25:48 +02:00
José Valim e265771c6e Merge pull request #1287 from ToJans/WindowsBugfixing
Added .gitattribute for fixtures in tests, so CRLF is not autoconverted
2013-06-19 09:18:11 -07:00
Tom Janssens f9f728cb66 Added .gitattribute for fixtures in tests, so CRLF is not autoconverted 2013-06-19 18:02:50 +02:00
José Valim e321caee24 Merge pull request #1286 from ToJans/WindowsBugfixing
Fixed path tests when running on Windows: removed drive prefixes from check
2013-06-19 08:00:39 -07:00
Tom Janssens 5dd3a23752 Fixed path tests when running on Windows: removed drive prefixes from check 2013-06-19 16:48:26 +02:00
José Valim c572cb80fc Support binding/2 that goes across all contexts 2013-06-19 14:17:45 +02:00
José Valim b761f1a605 Wording on File's @moduledoc 2013-06-19 12:53:28 +02:00
José Valim c1f907b412 Clean up docs for File 2013-06-19 12:31:33 +02:00
José Valim c45d0f4f2f No longer support .beam files on mix local 2013-06-19 12:11:56 +02:00
José Valim 406f0dcffe qoute -> quote 2013-06-19 10:38:09 +02:00
José Valim 9dbc216c11 Merge pull request #1283 from andrzejsliwa/master
cleanup after refactoring of Code.string_to_qouted
2013-06-19 01:13:43 -07:00
Andrzej Sliwa 22b455fad4 cleanup after refactorization of Code.string_to_quoted 2013-06-19 09:03:42 +02:00
José Valim 9807b506eb Update CHANGELOG and minor fixes 2013-06-19 08:00:19 +02:00
José Valim 624423686d Merge pull request #1282 from andrzejsliwa/master
rename of Macro.to_binary and Code.string_to_ast regards to #1143
2013-06-18 22:57:38 -07:00
José Valim e5cba5e4b5 Merge pull request #1281 from meh/process-self
Add Process.self
2013-06-18 22:55:52 -07:00
meh 1f15bf72e7 Add Process.self 2013-06-19 00:23:02 +02:00
Andrzej Sliwa e6bc5a273c refactored/renamed from Code.string_to_ast to Code.string_to_quoted 2013-06-19 00:18:32 +02:00
Andrzej Sliwa 8fa6c4f96f refactor/rename Macro.to_binary to Macro.to_string with deprecation warning 2013-06-18 23:35:13 +02:00
José Valim b7e8f97de1 Merge pull request #1279 from ToJans/WindowsBugfixing
Windows bugfixing
2013-06-18 11:18:04 -07:00
José Valim 5fc68fe71b File.rm_rf! should not complain on invalid entries 2013-06-18 20:15:53 +02:00
Tom Janssens 808260cd1b Fixed unit test for File.Touch with failure on Windows 2013-06-18 20:13:55 +02:00
Tom Janssens d9152c08d8 Fixed a bug within File.rm_rf on windows 2013-06-18 20:09:23 +02:00
José Valim 4048e542d9 cp_r should pinpoint place of failure on errors 2013-06-18 19:52:06 +02:00
José Valim b08273700f Use regex on matching on some of File exceptions 2013-06-18 18:18:51 +02:00
José Valim 0d5512931e Merge pull request #1277 from ToJans/WindowsBugfixing
Some IO tests return alternate error codes on windows
2013-06-18 08:54:49 -07:00
Tom Janssens 7fc37de058 Some IO tests return alternate error codes on windows
Now these tests allow different kinds of error codes. However, I currently
duplicate the is_io_error? method; I would assume there would be an easier
way to do this; maybe just put it in a namespace and reference the
namespace?
2013-06-18 17:49:19 +02:00
José Valim c4dac6fd7a Catch invalid arguments for binding/1 2013-06-18 16:29:12 +02:00
José Valim d3122ddcc5 Add binding/0 and binding/1 for retrieving bindings 2013-06-18 16:14:03 +02:00
José Valim 74cfcda902 Validate eval options, closes #1238 2013-06-18 15:38:13 +02:00
José Valim 6fc6dd4352 Update CHANGELOG 2013-06-18 12:35:09 +02:00
José Valim 7014986f65 Add -f option to make clean_exbeam 2013-06-18 12:26:23 +02:00
José Valim 031f4e942f Get rid of unused variable warning 2013-06-18 12:20:37 +02:00
José Valim 9904bdce9d Warn on undefined module attributes, closes #963 2013-06-18 12:19:15 +02:00
José Valim 754418493f Merge pull request #1275 from ericmj/elixir-in-warning
Emit warning for 'x in []' in guards
2013-06-18 03:04:05 -07:00
Eric Meadows-Jonsson 27000ea1b4 Emit warning for 'x in []' in guards 2013-06-18 11:57:27 +02:00
José Valim 620ee4298c Raise proper warnings when default clauses from different definitions collide, closes #1244 2013-06-18 11:36:57 +02:00
José Valim 1d09da7e53 Use = instead of == on shell comparison 2013-06-18 10:42:49 +02:00
José Valim 5115256853 Use proper path separator in msys
For now, we are only checking the path on msys but
we may need to check it for cygwin too.

Thanks to @ToJans for providing guidance and debug
information to sort this issue out.
2013-06-18 10:30:51 +02:00
José Valim 0d715343de Merge pull request #1273 from rnowling/master
Fixed grammar and wrong word (exit, not exist) in Process.exit/2 documentation.
2013-06-17 23:20:57 -07:00
rnowling 441547d8e4 Fixed grammar and wrong word (exit, not exist) in Process.exit/2 documentation. 2013-06-17 23:25:23 -03:00
Yurii Rashkovskii 5e123e6519 Make sure we don't receive an EXIT message from the dependency tracker
It pollutes IEx and scripts execution

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2013-06-17 20:37:44 +02:00
José Valim 5b45ee5c8d Update CHANGELOG 2013-06-17 13:53:46 +02:00
José Valim 939942bdb3 Merge pull request #1270 from ToJans/master
With the new elixir.bat version the batch script only worked from the ./bin folder on windows
2013-06-17 04:46:46 -07:00
José Valim e64be43090 Update executable docs 2013-06-17 13:44:44 +02:00
Tom Janssens da898e1ac9 elixir.bat only worked from the bin folder
Apparently, when shifting parameters, the %~dp0 is not preserved, 
so it needs to keep this value. This will fix things like iex on Windows, which just crashed with the previous version.
2013-06-17 14:43:33 +03:00
José Valim 8dba46a0ac Merge pull request #1269 from ToJans/master
On Windows Erlang parameters were not getting passed to elixir.bat in a proper way
2013-06-17 04:19:31 -07:00
José Valim 633eb871ee Update CHANGELOG 2013-06-17 13:19:11 +02:00
José Valim 91c0fa71fa Avoid clash in between modules 2013-06-17 13:19:07 +02:00
José Valim 87da4e91bb Clean up error messages, move defaults warning to an error 2013-06-17 13:05:57 +02:00
Tom Janssens 7768828d9c Removeved the echo prefix from the elixir.bat file
Inline editing in github is dangerous!
2013-06-17 13:40:24 +03:00
Tom Janssens 8ece000489 Added parameter validation in the elixir.bat file
The batch file now allows both erlang and elixir commands before the ".ex" file; if any unknown parameter is passed before the ".ex file", an error is thrown.
2013-06-17 12:39:09 +02:00
José Valim 1297fa5953 Refactoring of some mix tasks 2013-06-17 12:11:43 +02:00
Tom Janssens c7a2442bc7 Make sure all Erlang parameters are extracted and parsed in elixir.bat 2013-06-17 11:19:44 +02:00
José Valim f88ee1c23a Do not check in utf8 directories into source control 2013-06-17 08:57:53 +02:00
Eric Meadows-Jonsson 64a22fc15d Merge pull request #1264 from edgurgel/mix-rebar-scripts
Fix rebar config scripts evaluation
2013-06-16 12:05:36 -07:00
Eduardo Gurgel 07cd772962 Fix rebar config scripts evaluation
Scripts are evaluated using the dependency directory as current directory

Closes #1262
2013-06-16 15:54:54 -03:00
José Valim 160a8255ef Merge pull request #1263 from elixir-lang/docs-standard
Change the bullet points on code.ex to follow the standard on this file
2013-06-16 09:20:16 -07:00
Rafael Mendonça França cca430522d Follow the bullet point standard on this file 2013-06-16 13:11:51 -03:00
Rafael Mendonça França db417110e7 Use the proper code highlight for functions 2013-06-16 13:11:28 -03:00
José Valim 67896b56c5 Ensure errors point to the proper -> clauses
This commit changes the -> clauses representation from:

    { :->, meta, list({ list(expr), expr }) }

To:

    { :->, meta, list({ list(expr), meta, expr }) }

Now clauses can store metadata, which allows us to provide
accurate reporting information.

Closes #1215
2013-06-16 17:27:14 +02:00
José Valim d21d603617 Avoid race condition on CLI tests 2013-06-16 13:57:52 +02:00
José Valim fdd108fc43 Improve error message matching on local.uninstall with archives 2013-06-16 13:23:13 +02:00
José Valim 19ff7a03f9 Ensure imports inside quotes are properly registered, closes #1225 2013-06-16 13:18:47 +02:00
José Valim 7f5744bde7 Ensure defmodule :atom does not define an alias 2013-06-16 13:10:59 +02:00
José Valim b87797f788 Return Foo for __MODULE__.Foo outside of a module 2013-06-16 13:10:59 +02:00
José Valim 48b19ec580 Improve mix test to rely on possible multiple messages 2013-06-16 13:10:59 +02:00
José Valim f2e200f832 Merge pull request #1261 from alco/ansi-doctest
Don't assume terminal availability in ANSI doctests
2013-06-16 03:07:23 -07:00
Alexei Sholik a541f53dd1 Don't assume terminal availability in ANSI doctests 2013-06-16 12:15:00 +03:00
José Valim 6ce77b1b49 Update CONTRIBUTING.md language 2013-06-16 10:07:35 +02:00
José Valim 5222c7737f Update docs for Macro.expand 2013-06-16 10:07:35 +02:00
José Valim b0cf65b5cd Merge pull request #1259 from elixir-lang/whitespaces
Uniform use of whitespace after commas on codebase
2013-06-15 22:52:38 -07:00
Rafael Mendonça França 7cdc25538e Uniform use of whitespace after commas on codebase
This make easier to read and also follow some standard in the code
2013-06-15 16:52:48 -03:00
José Valim b82cd8cbd0 Update CHANGELOG 2013-06-15 19:35:59 +02:00
José Valim 9365556ea4 Rename packages to archives 2013-06-15 19:33:43 +02:00
José Valim de376663f0 Merge pull request #1223 from jeremyjh/1124-mix-local-arch
add mix package and related tasks
2013-06-15 09:34:55 -07:00
José Valim f24ec671c2 Merge pull request #1258 from elixir-lang/fix-typo
Review documentation of `.split`, `.drop` and `.take` on `Dict`
2013-06-15 09:10:24 -07:00
José Valim c0f7a268c7 Avoid usage of defrecordp and move all parsing to Mix.Version.Parser 2013-06-15 18:09:48 +02:00
Rafael Mendonça França 7b4e0915b1 Remove documentation of .split, .drop and .take on HashDict and ListDict
They are already documented on Dict
2013-06-15 12:55:07 -03:00
Rafael Mendonça França 2ea7de7361 Fix Dict.drop documentation 2013-06-15 12:54:34 -03:00
José Valim 82cc435fd7 Merge pull request #1130 from meh/mix-version
Implement Mix.Version
2013-06-15 08:25:53 -07:00
José Valim 8eab556f55 Update CHANGELOG 2013-06-15 17:01:09 +02:00
José Valim 5a37594853 Handle --warnings-as-errors and other elixirc options in mix compile 2013-06-15 17:00:06 +02:00
Alexei Sholik 82f3fa00d0 Merge pull request #1256 from mururu/fix-typo
Fix typo in doc
2013-06-15 04:52:14 -07:00
Yuki Ito c97dc3bd15 Fix typo in doc 2013-06-15 20:35:15 +09:00
José Valim d6e99e2f5a Merge pull request #1255 from tonini/code-module-typo-fix
Fix typo in the @doc for Code.eval_string
2013-06-15 03:53:24 -07:00
Samuel Tonini 46b6f82473 fix typo in doc 2013-06-15 12:37:52 +02:00
José Valim c80e43ca42 Go to :binary.match(left, right) directly instead of hoping through String.contains? 2013-06-15 10:51:10 +02:00
José Valim 80e41f8b4b Allow +, -, and friends to be called dynamically
Our compiler still inline those calls. This commit simply
allows one to write:

    mod = Kernel
    mod.+(1, 2)
2013-06-15 10:45:12 +02:00
José Valim 9e16cf36ed There is no need for =~ to be a macro 2013-06-15 09:46:19 +02:00
José Valim 1d8e39a2df Only change compilation status if warnings_as_errors 2013-06-15 09:46:10 +02:00
José Valim 21db352039 Always print warnings to stderr 2013-06-15 09:32:53 +02:00
José Valim 45f033d15a Add a test for warnings_as_errors option 2013-06-15 08:41:27 +02:00
José Valim 8eba2fcba0 Merge pull request #1214 from baldmountain/feature/898-warnings-as-errors
Feature/898 warnings as errors
2013-06-14 23:20:25 -07:00
José Valim e476c06798 Merge pull request #1253 from Nicd/patch-1
Fix compiler_options docblock formatting errors
2013-06-14 23:08:22 -07:00
Nicd 0b6b7ea0e4 Fix compiler_options docblock formatting errors 2013-06-15 01:39:21 +03:00
José Valim e62ef92a4b Ensure Enum.take/2 doesn't peek ahead 2013-06-14 18:43:05 +02:00
José Valim 7be271876e Merge pull request #1245 from josephwilk/hash_dict_equals_bug
Bug: HashDict equal?
2013-06-14 04:40:49 -07:00
josephwilk 7c639f9252 document equal? 2013-06-14 11:49:16 +02:00
josephwilk f03e707e8e Bug: equal? on HashDict was not comparing the two dicts 2013-06-14 11:49:10 +02:00
Jeremy Huffman 38e63451d6 local.uninstall warn on uninstall package task 2013-06-13 21:37:34 -04:00
Jeremy Huffman 5e1366c946 changes from PR discussion
1: ensure_loaded?

3: ebin

2: assume path/ebin to be packaged

6: accept project path & assume ebin

8: include priv in archive

5: local.uninstall handles packages too

7: local.install [] will install a package

7: mix new .gitignore *.ez files
2013-06-13 21:37:34 -04:00
Jeremy Huffman 0470ebf5af implement mix package and related tasks re #1124 2013-06-13 21:37:34 -04:00
José Valim b71642045d Merge pull request #1240 from fishcakez/hashdict-contract
Improve efficiency of HashDict contract
2013-06-13 11:00:19 -07:00
James Fish 582e814422 Improve efficiency of HashDict contract
When contracting in size the HashDict does not need to relocate
(re-hash) key-value pairs. It is not required because each of the
deepest nodes in a trie become a bucket on contraction. This bucket
contains all the key-value pairs in that node.
2013-06-13 18:11:51 +01:00
José Valim d15ad4da80 Never use the dependency rebar 2013-06-13 17:31:48 +02:00
José Valim bc0089ff42 Simplify out of date deps error message 2013-06-13 17:14:03 +02:00
José Valim bff8a6ae75 Remove deprecated code branches 2013-06-13 16:48:31 +02:00
José Valim 6d4ad4ade6 Start v0.9.3.dev 2013-06-13 16:26:25 +02:00
meh 057691722e Implement Mix.Version, closes #1115 2013-06-09 15:36:24 +02:00
Geoffrey Clements 0e2752f7c2 print compiled even if compilation_status is not :ok it may have been from another file. Halt on an :error 2013-06-09 07:49:38 -04:00
Geoffrey Clements 76b2255d7b Don't pass warnings-as-errors to module since it isn't needed 2013-06-09 07:49:38 -04:00
Geoffrey Clements 93d5128db3 Implement José's suggestions.
1. Detect all places in elixir_errors:handle_file_warning/3 where we
output something with io
2. Change those places to go through an internal function that will
call elixir_compiler:get_opt and figure out if we should print to
stdout or stderr and act accordingly
3. In the same function above, whenever we print a warning as an error,
we should store somewhere that a warning as an error happened. I
recommend storing that in the elixir_code_server gen server
4. Provide a Code.compilation_status that would return it
2013-06-09 07:49:38 -04:00
Geoffrey Clements 6d98020923 Unwind a lot of the wrong direction, output warnings based on warnings-as-errors setting 2013-06-09 07:49:38 -04:00
Geoffrey Clements 77110f970b Update parallel compiler callback documentation 2013-06-09 07:49:38 -04:00
Geoffrey Clements cf6ce9a3fd Fix tests that expect the parallel compiler callback to only pass one parameter. 2013-06-09 07:49:38 -04:00
Geoffrey Clements be0dea7c0a Pass exit status to callback so we can decide there whether to stop or not. 2013-06-09 07:49:38 -04:00
Geoffrey Clements b0b444bbd3 a little closer, status is passed to main process 2013-06-09 07:49:37 -04:00
Geoffrey Clements 3e45188e88 Experiment with how to pass back status. process dictionary doesn't work. 2013-06-09 07:49:37 -04:00
Geoffrey Clements 51171cb8e1 Mostly done. Still need to fix warning printout. 2013-06-09 07:49:37 -04:00
163 changed files with 4568 additions and 2371 deletions
+1
View File
@@ -0,0 +1 @@
lib/elixir/test/elixir/fixtures/*.txt text eol=lf
+38
View File
@@ -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
View File
@@ -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**
+3
View File
@@ -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")
+2 -2
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -1 +1 @@
0.9.2
0.9.3
+13 -4
View File
@@ -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
View File
@@ -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 %*
+1
View File
@@ -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
+1
View File
@@ -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
+3
View File
@@ -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
+1 -1
View File
@@ -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
+3 -3
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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 %>
"""
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 ->
+11
View File
@@ -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
View File
@@ -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
View File
@@ -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
+29 -26
View File
@@ -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
+15 -3
View File
@@ -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
+91 -12
View File
@@ -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)
+52 -56
View File
@@ -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
+3 -3
View File
@@ -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
View File
@@ -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]
"""
+3 -17
View File
@@ -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
View File
@@ -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
View File
@@ -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
+5 -5
View File
@@ -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:
+10 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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: []
+6 -6
View File
@@ -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
+10 -1
View File
@@ -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) ->
+1
View File
@@ -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) ->
+23 -21
View File
@@ -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
}.
+14 -1
View File
@@ -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
+1 -1
View File
@@ -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",
+30 -17
View File
@@ -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 -10
View File
@@ -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) ->
+22 -9
View File
@@ -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},
+10 -5
View File
@@ -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).
+9 -12
View File
@@ -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'},
{'->','*'},
{'<<>>','*'},
{'{}','*'},
{'[]','*'},
+7 -7
View File
@@ -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);
+70 -5
View File
@@ -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
View File
@@ -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) ->
+1 -1
View File
@@ -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;
+19 -6
View File
@@ -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 }.
+11 -7
View File
@@ -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
View File
@@ -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;
+1 -1
View File
@@ -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) ->
+37 -31
View File
@@ -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 -18
View File
@@ -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
+18 -18
View File
@@ -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] }.
+2 -1
View File
@@ -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 ->
+2 -2
View File
@@ -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
+4 -4
View File
@@ -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
+14 -14
View File
@@ -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
+4 -4
View File
@@ -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
+19 -11
View File
@@ -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
+149 -133
View File
@@ -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
+1 -1
View File
@@ -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
+144 -67
View File
@@ -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
+14 -1
View File
@@ -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)
+2 -2
View File
@@ -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
+6 -6
View File
@@ -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
+30 -19
View File
@@ -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
+2 -2
View File
@@ -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
+323 -108
View File
@@ -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
+8 -14
View File
@@ -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
+34 -19
View File
@@ -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(
+112 -31
View File
@@ -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
+114 -26
View File
@@ -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
+7 -7
View File
@@ -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
+23 -23
View File
@@ -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
+125 -94
View File
@@ -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
+17 -7
View File
@@ -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]
+34 -25
View File
@@ -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
+9
View File
@@ -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
+10 -10
View File
@@ -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