Compare commits

...
84 Commits
Author SHA1 Message Date
José Valim 6a77057910 Release v0.10.3 2013-10-02 20:53:56 +02:00
José Valim e2be2579c5 Ensure both strings and atoms go through the same lookup 2013-10-02 20:43:54 +02:00
José Valim f9ec6cfb5f Merge pull request #1740 from yrashk/record-string-fields
Allow for string names when instantiating or updating records
2013-10-02 11:32:52 -07:00
Yurii Rashkovskii cc1d7c5823 Disallow mixed use of strings and atoms in record initialization/update
This allows provides better overall performance to these procedures.
2013-10-02 12:37:26 +02:00
Yurii Rashkovskii 7649a5dba4 Allow for string names when instantiating or updating records
This provides a great added benefit of allowing to import untrusted
dictionary-like structures without having to sanitize keys before
converting them to atoms, without the risk of running out of atoms.

The performance for atom field names remains the same, string names
are somewhat slower (as expected)
2013-10-01 15:52:24 +02:00
José Valim d221e9a161 Allow apps to be selected in umbrella projects
Closes #1731
Closes #1730
2013-09-30 08:59:13 +02:00
José Valim 4638831103 Update CHANGELOG 2013-09-29 22:07:54 +02:00
José Valim 60b2928678 Ensure &() works on blocks with one expressoin, closes #1704 2013-09-29 21:53:04 +02:00
José Valim 3f0c583045 Deprecate Regex.captures and String.valid_codepoint? 2013-09-29 21:47:12 +02:00
José Valim 0ce2fbb3e4 Move verify! to an is_doc guard in algebra 2013-09-29 19:32:08 +02:00
José Valim 04c5c80353 Merge pull request #1733 from meh/inspect-raise
Add validity checking for Inspect.Algebra documents
2013-09-29 06:11:28 -07:00
José Valim 1cb8f3a654 Merge pull request #1734 from madlep/fix_dict_behaviour_equals
`Dict.size` incorrectly returning `true` when left dict is subset of right dict
2013-09-29 06:11:18 -07:00
meh 7b025dd982 Add validity checking for Inspect.Algebra documents 2013-09-29 14:13:13 +02:00
Julian Doherty (madlep) eff51e4045 explicitly use Dict.size rather than just size in equal?
Fixes bug where `true` was incorrectly returned for `Dict.equal?` when
the 1st argument was a subset of the 2nd argument.

e.g:
```elixir
iex> h1 = HashDict.new(foo: 1, bar: 2)
iex> h2 = HashDict.new(foo: 1, bar: 2, baz: 3)
iex> Dict.equal?(h1, h2)
true # OOPS
iex> Dict.equal?(h2, h1)
false # works as expected other way round
```

The code to check equality was checking size (incorrectly), and then
checking to see if every element in the first dict was present in the
2nd.

However, `size` was being used instead of `Dict.size`. This ended up
calling `Kernel.size` instead - and would always return `true`
regardless of whever the actual size of the `Dict` arguments were the
same or not.
2013-09-29 22:12:57 +10:00
José Valim ea5df228ed String module now depends on Regex module 2013-09-29 13:39:45 +02:00
José Valim 769b69ad69 Ensure String.split/2 works with unicode 2013-09-29 13:35:17 +02:00
Bruce Williams 0e725fb906 Support String.split with an empty binary as the pattern.
As discussed in #1688, currently the following fails:

    iex> String.split "abc", ""
    ** (ArgumentError) argument error
        binary.erl:242: :binary.split/3

These changes add support for an empty binary pattern, piggybacking on
the empty regular expression split (vs using `String.graphemes`,
notably) so that the function continues to support the `:global`
option.

Users should probably be using `String.graphemes` for splitting, but
this keeps the API consistent while fixing this exception.

Closes #1688.

Conflicts:
	lib/elixir/lib/string.ex
2013-09-29 13:30:55 +02:00
José Valim 4b317a75d8 Save typespecs attributes with proper line numbers, closes #1613 2013-09-29 13:15:33 +02:00
José Valim 2977a46865 Improve guards on IEx.Helpers, closes #1535 2013-09-29 13:01:58 +02:00
José Valim d92b2e6831 Avoid calling Kernel:raise from erl 2013-09-25 11:34:37 +02:00
José Valim 0a7f6e7cd2 Merge pull request #1725 from alco/assert_receive
Add a tricky pattern example for assert_receive
2013-09-25 02:23:11 -07:00
Yurii Rashkovskii 8bd43ed677 Merge pull request #1726 from yrashk/raise-macro
Kernel.raise should be a macro to avoid messing up the stack trace
2013-09-25 02:22:36 -07:00
Yurii Rashkovskii bcf8563d38 Kernel.raise should be a macro to avoid messing up the stack trace 2013-09-25 11:20:42 +02:00
Alexei Sholik 352379112a Add a tricky pattern example for assert_receive 2013-09-25 12:07:56 +03:00
José Valim f8c66c8516 Remove unecessary vars cache 2013-09-24 13:21:58 +02:00
José Valim f090045e99 Merge pull request #1720 from edgurgel/patch-1
Minor english correction
2013-09-23 02:42:58 -07:00
Eduardo Gurgel 4256daf6a7 Minor english correction
[ci skip]
2013-09-22 16:46:58 -03:00
José Valim 8c77ada8d2 Merge pull request #1716 from ericmj/mix-deps-compile-false
Fix `compile: false` dependency option
2013-09-17 14:44:36 -07:00
Eric Meadows-Jonsson 63ed270c68 Fix compile: false dependency option 2013-09-17 23:35:02 +02:00
José Valim acdc55bd90 Get rid of the r helper since it causes conflicts related to modules dependencies 2013-09-17 12:35:45 -03:00
José Valim d2bfd10299 Merge pull request #1715 from ericmj/mix-deps-order
Preserve correct order when deps are overriding
2013-09-16 15:19:40 -07:00
Eric Meadows-Jonsson b2e32e4ccd Don't compile deps too many times 2013-09-16 23:42:59 +02:00
Eric Meadows-Jonsson 0c80905f63 Preserve correct order when deps are overriding 2013-09-16 23:42:47 +02:00
José Valim 429fc2094b Merge pull request #1693 from pminten/stream-generate
Add Stream.unfold
2013-09-15 10:12:45 -07:00
José Valim e8c3ed2881 Merge pull request #1714 from pminten/mix-new-default-sup
Make --sup the default for mix
2013-09-15 10:11:23 -07:00
Peter Minten 3d583e7cdc Make --sup the default for mix
Closes #1706
2013-09-15 12:00:02 +02:00
Peter Minten 7b1b0d93d8 Add Stream.unfold
This generates a possibly finite stream of values.
2013-09-15 11:20:56 +02:00
José Valim 9839bfa4ad Deprecate previous partial application syntax 2013-09-14 09:56:40 -03:00
José Valim 0b640d985c Document the dot as a special form
We include common examples, quoted expressions, and more.
2013-09-13 20:53:03 -03:00
José Valim b161d80b4c IEx now respects signals sent from user_drv, closes #1566 2013-09-12 19:33:44 -03:00
José Valim 748b70d796 Slightly tidy up IEx.Server 2013-09-12 11:44:05 -03:00
José Valim 0267f312b1 Fix handling of nested do expressions in EEx, closes #1710 2013-09-12 10:59:48 -03:00
José Valim 8a45ca8905 Merge pull request #1711 from osaut/docs_regex
An example for Regex.compile and correction of small typos in Regex docs.
2013-09-12 06:44:14 -07:00
Olivier Saut 05c3e98f64 Correction of small typos in Regex docs. 2013-09-12 15:38:48 +02:00
José Valim 212acf437a Provide better exceptions for invalid unquote args, closes #1680 2013-09-11 12:19:41 -03:00
José Valim fc0af0542b Improve doctest stripping, closes #1678 2013-09-11 11:34:12 -03:00
José Valim 14272c98b4 Improve invalid match and guard errors, closes #1673 2013-09-11 11:21:10 -03:00
José Valim 42ffd31e0e Record docs adjustments 2013-09-11 10:32:45 -03:00
Eric Meadows-Jonsson 7aeb1b6930 Minor style fix in Enum.take_every/2 2013-09-11 15:01:13 +02:00
José Valim f3f64cdba2 Move to capture syntax 2013-09-10 23:33:53 -03:00
José Valim 5b8a30be6b Update CHANGELOG 2013-09-10 18:51:14 -03:00
José Valim e90b877575 Provide better error message for mix -e, closes #1639 2013-09-10 18:45:58 -03:00
José Valim 3d2ad9ac77 Adjust code to style used in the codebase 2013-09-10 18:43:53 -03:00
José Valim 881bdd5534 Merge pull request #1635 from Raynes/takenth
Add a take_every function
2013-09-10 14:21:10 -07:00
José Valim c3bdfc9ba7 No need to mark typedoc as persisted if we are manually persisting it 2013-09-10 12:46:22 -03:00
José Valim 262e8b5f22 Avoid duplicatation by making the typedoc signature always an empty list 2013-09-10 12:31:20 -03:00
José Valim 2f5afcaab9 Merge pull request #1637 from pminten/typedoc-rebased
Typedoc rebased
2013-09-10 08:01:18 -07:00
José Valim 4e42b75e17 Merge pull request #1614 from alexrp/master
Move Mix.Version to Version in kernel.
2013-09-10 05:51:10 -07:00
alexrp c6f117d60f Move Mix.Version to Version in kernel. 2013-09-10 14:42:04 +02:00
José Valim 80c0ff84fb Merge pull request #1700 from Irio/master
List types in docs giving the idea of aliases
2013-09-09 13:24:55 -07:00
José Valim 59b4b5b9bc Merge pull request #1702 from ericmj/io-docs
Update IO docs and clarify bin* functions
2013-09-09 07:44:08 -07:00
José Valim 2945939f3e Merge pull request #1703 from ericmj/quote-docs
Update quote docs
2013-09-09 07:43:51 -07:00
Eric Meadows-Jonsson 3d9aa3e978 Update quote docs
:binding => :bind_quoted.
2013-09-09 16:35:48 +02:00
Eric Meadows-Jonsson 860fcc6eaa Update IO docs and clarify bin* functions 2013-09-09 16:27:30 +02:00
José Valim 73f40cb10e Merge pull request #1698 from pminten/rename-iterator-error
Rename File.IteratorError -> IO.StreamError
2013-09-09 05:50:34 -07:00
José Valim 8b56252e1b Merge pull request #1699 from alg/multiple-heredocs-handling
Handling of multiple heredocs on the same line
2013-09-09 05:38:39 -07:00
Irio Irineu Musskopf Junior b8a0717873 List types in docs giving the idea of aliases
The order was changed to follow to explanation in the next paragraph.
2013-09-07 16:05:43 -03:00
Aleksey Gureiev 8dbfcba237 added handling of multiple heredocs on the same line 2013-09-07 17:59:40 +03:00
Peter Minten 5bb4f43e9d Rename File.IteratorError -> IO.StreamError
Iterators were replaced with streams a while ago
2013-09-07 16:38:00 +02:00
José Valim 808f8981b3 Merge pull request #1689 from bruce/string-regex-empty-split-docs
Improve String.split and Regex.split docs for empty patterns.
2013-09-07 03:43:55 -07:00
Bruce Williams 73049ac355 Improve String.split and Regex.split docs
Include examples showing that splitting with an empty pattern results
in a final (trim-able) empty string as discussed in #1688.
2013-09-06 15:46:23 -07:00
José Valim d77ccf9415 Improve inspect docs 2013-09-06 12:02:04 -03:00
José Valim 9e644dfba1 Document issues and improve IEx booting process 2013-09-05 18:18:01 -03:00
José Valim e00eedec45 Fix bug on Regex.scan/3 when capturing groups and the regex has no groups 2013-09-05 13:13:57 -03:00
José Valim 4016864db2 Improve IEx out of bounds message 2013-09-04 09:34:08 -03:00
José Valim 38c7edd203 Merge pull request #1685 from ericmj/lc-docs
Make clear behaviour of assignments in comprehensions
2013-09-04 04:32:23 -07:00
Eric Meadows-Jonsson fe3ab0cb77 Make clear behaviour of assignments in comprehensions 2013-09-04 12:18:44 +02:00
José Valim 9f81ac89e5 Remove previously deprecated code 2013-09-03 15:37:02 -03:00
José Valim cd4c1976c9 Update CHANGELOG and move to new style 2013-09-03 15:23:48 -03:00
José Valim 87be134a5b Bump version 2013-09-03 15:14:56 -03:00
Peter Minten a5ea8a63bf Use typedoc attribute to store typedocs.
This requires a bit of a workaround because `typedoc` is also used to store the
doc of the next type to be defined. We store typedocs in the docs table when
types are defined and later filter them out and put them into `typedoc`.
2013-08-22 16:30:24 +02:00
Peter Minten 742258340c Remove unneeded empty lines in the typedoc tests. 2013-08-22 16:30:24 +02:00
Peter Minten 1be3335f0b Add a @typedoc attribute to document types
Also a new `Kernel.Typespec.beam_typedocs` function to get the typedoc
information.
2013-08-22 16:30:24 +02:00
Anthony Grimes 307198c750 Add a take_every function. 2013-08-22 00:18:50 -07:00
125 changed files with 1679 additions and 1128 deletions
+88 -53
View File
@@ -1,6 +1,41 @@
# v0.10.3 (2013-10-02)
* Enhancements
* [Enum] Add `Enum.take_every/2`
* [IEx] IEx now respects signals sent from the Ctrl+G menu
* [Kernel] Allow documentation for types with `@typedoc`
* [Mix] Allow apps to be selected in umbrella projects
* [Record] Generated record functions `new` and `update` also take options with strings as keys
* [Stream] Add `Stream.unfold/1`
* Bug fixes
* [Dict] Fix a bug when a HashDict was marked as equal when one was actually a subset of the other
* [EEx] Solve issue where `do` blocks inside templates were not properly aligned
* [ExUnit] Improve checks and have better error reports on poorly aligned doctests
* [Kernel] Fix handling of multiple heredocs on the same line
* [Kernel] Provide better error messages for match, guard and quoting errors
* [Kernel] Make `Kernel.raise/2` a macro to avoid messing up stacktraces
* [Kernel] Ensure `&()` works on quoted blocks with only one expression
* [Mix] Address an issue where a dependency was not compiled in the proper order when specified in different projects
* [Mix] Ensure `compile: false` is a valid mechanism for disabling the compilation of dependencies
* [Regex] Fix bug on `Regex.scan/3` when capturing groups and the regex has no groups
* [String] Fix a bug with `String.split/2` when given an empty pattern
* [Typespec] Guarantee typespecs error reports point to the proper line
* Deprecations
* [Kernel] The previous partial application syntax (without the `&` operator) has now been deprecated
* [Regex] `Regex.captures/3` is deprecated in favor of `Regex.named_captures/3`
* [String] `String.valid_codepoint?/1` is deprecated in favor of pattern matching with `<<_ :: utf8 >>`
* Backwards incompatible changes
* [IEx] The `r/0` helper has been removed as it caused surprising behaviour when many modules with dependencies were accumulated
* [Mix] `Mix.Version` was renamed to `Version`
* [Mix] `File.IteratorError` was renamed to `IO.StreamError`
* [Mix] `mix new` now defaults to the `--sup` option, use `--bare` to get the previous behaviour
# v0.10.2 (2013-09-03)
* enhancements
* Enhancements
* [CLI] Add `--verbose` to elixirc, which now is non-verbose by default
* [Dict] Add `Dict.Behaviour` as a convenience to create your own dictionaries
* [Enum] Add `Enum.split/2`, `Enum.reduce/2`, `Enum.flat_map/2`, `Enum.chunks/2`, `Enum.chunks/4`, `Enum.chunks_by/2`, `Enum.concat/1` and `Enum.concat/2`
@@ -24,7 +59,7 @@
* [String] Add `String.ljust/2`, `String.rjust/2`, `String.ljust/3` and `String.rjust/3`
* [URI] `URI.parse/1` supports IPv6 addresses
* bug fix
* Bug fixes
* [Behaviour] Do not compile behaviour docs if docs are disabled on compilation
* [ExUnit] Doctests no longer eat too much space and provides detailed reports for poorly indented lines
* [File] Fix a bug where `File.touch(file, datetime)` was not setting the proper datetime when the file did not exist
@@ -42,7 +77,7 @@
* [String] `String.slice/3` still returns the proper result when there is no length to be extracted
* [System] `System.get_env/0` now returns a list of tuples as previously advertised
* deprecations
* Deprecations
* [Dict] `Dict.update/3` is deprecated in favor of `Dict.update!/3`
* [Enum] `Enum.min/2` and `Enum.max/2` are deprecated in favor of `Enum.min_by/2` and `Enum.max_by/2`
* [Enum] `Enum.join/2` and `Enum.map_join/3` with a char list are deprecated
@@ -55,7 +90,7 @@
* [Macro] `Macro.unescape_binary/1` and `Macro.unescape_binary/2` are deprecated in favor of `Macro.unescape_string/1` and `Macro.unescape_string/2`
* [Mix] `:umbrella` option for umbrella paths has been deprecated in favor of `:in_umbrella`
* backwards incompatible changes
* Backwards incompatible changes
* [IO] IO functions now only accept iolists as arguments
* [Kernel] `Binary.Chars` was renamed to `String.Chars`
* [Kernel] The previous ambiguous import syntax `import :functions, Foo` was removed in favor of `import Foo, only: :functions`
@@ -63,7 +98,7 @@
# v0.10.1 (2013-08-03)
* enhancements
* Enhancements
* [Behaviour] Add support for `defmacrocallback/1`
* [Enum] Add `Enum.shuffle/1`
* [ExUnit] The `:trace` option now also reports run time for each test
@@ -86,7 +121,7 @@
* [String] Add `Regex.scan/3` now supports capturing groups
* [String] Add `String.reverse/1`
* bug fix
* Bug fixes
* [Behaviour] Ensure callbacks are stored in the definition order
* [CLI] Speed up boot time on Elixir .bat files
* [IEx] Reduce cases where IEx parser can get stuck
@@ -101,19 +136,19 @@
* [OptionParser] Ensure `:keep` and `:integer`|`:float` can be given together as options
* [OptionParser] Ensure `--no-flag` sets `:flag` to false when `:flag` is a registered boolean switch
* deprecations
* Deprecations
* [Kernel] `function(Mod.fun/arity)` and `function(fun/arity)` are deprecated in favor of `&Mod.fun/arity` and `&fun/arity`
* [Kernel] `function/3` is deprecated in favor of `Module.function/3`
* [Kernel] `Kernel.ParallelCompiler` now receives a set of callbacks instead of a single one
* [Mix] `:test_coverage` option now expect keywords arguments and the `--cover` flag is now treated as a boolean
* backwards incompatible changes
* Backwards incompatible changes
* [Regex] `Regex.scan/3` now always returns a list of lists, normalizing the result, instead of list with mixed lists and binaries
* [System] `System.halt/2` was removed since the current Erlang implementation of such function is bugged
# v0.10.0 (2013-07-15)
* enhancements
* Enhancements
* [ExUnit] Support `trace: true` option which gives detailed reporting on test runs
* [HashDict] Optimize `HashDict` to store pairs in a cons cell reducing storage per key by half
* [Kernel] Add pretty printing support for inspect
@@ -126,7 +161,7 @@
* [Mix] Projects can now define an `:elixir` key to outline supported Elixir versions
* [Typespec] Improve error messages to contain file, line and the typespec itself
* bug fix
* Bug fixes
* [CLI] Elixir can now run on Unix directories with `:` in its path
* [Kernel] `match?/2` does not leak variables to outer scope
* [Kernel] Keep `head|tail` format when splicing at the tail
@@ -135,7 +170,7 @@
* [Mix] Empty projects no longer leave empty ebin files on `mix compile`
* [Module] Calling `Module.register_attribute/3` no longer automatically changes it to persisted or accumulated
* deprecations
* Deprecations
* [Enum] Receiving the index of iteration in `Enum.map/2` and `Enum.each/2` is deprecated in favor of `Stream.with_index/1`
* [File] `File.iterator/1` and `File.biniterator/1` are deprecated in favor of `IO.stream/1` and `IO.binstream/1`
* [File] `File.iterator!/2` and `File.biniterator!/2` are deprecated in favor of `File.stream!/2` and `File.binstream!/2`
@@ -144,7 +179,7 @@
* [Mix] Deprecate `mix run EXPR` in favor of `mix run -e EXPR`
* [Record] `Record.__index__/2` deprecated in favor of `Record.__record__(:index, key)`
* backwards incompatible changes
* Backwards incompatible changes
* [Kernel] The `Binary.Inspect` protocol has been renamed to `Inspect`
* [Kernel] Tighten up the grammar rules regarding parentheses omission, previously the examples below would compile but now they raise an error message:
@@ -157,7 +192,7 @@
# v0.9.3 (2013-06-23)
* enhancements
* 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
@@ -171,7 +206,7 @@
* [Mix] `github: ...` shortcut now uses the faster `git` schema instead of `https`
* [Record] Allow types to be given to `defrecordp`
* bug fix
* Bug fixes
* [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
@@ -180,12 +215,12 @@
* [Kernel] Allow `(x, y) when z` in function clauses and try expressions
* [Mix] Mix now properly evaluates rebar scripts
* deprecations
* 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
* 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
@@ -195,7 +230,7 @@
# v0.9.2 (2013-06-13)
* enhancements
* Enhancements
* [ExUnit] `capture_io` now captures prompt by default
* [Mix] Automatically import git dependencies from Rebar
* [Mix] Support for dependencies directly from the umbrella application
@@ -203,7 +238,7 @@
* [String] Add `String.contains?`
* [URI] Implement `Binary.Chars` (aka `to_binary`) for `URI.Info`
* bug fix
* Bug fixes
* [HashDict] Ensure HashDict uses exact match throughout its implementation
* [IEx] Do not interpret ANSI codes in IEx results
* [IEx] Ensure `--cookie` is set before accessing remote shell
@@ -211,21 +246,21 @@
* [Mix] Fix usage of shell expressions in `Mix.Shell.cmd`
* [Mix] Start the application by default on escripts
* deprecations
* Deprecations
* [Regex] `Regex.index/2` is deprecated in favor `Regex.run/3`
* [Kernel] `super` no longer supports implicit arguments
* backwards incompatible changes
* Backwards incompatible changes
* [Kernel] The `=~` operator now returns true or false instead of an index
# v0.9.1 (2013-05-30)
* enhancements
* Enhancements
* [IEx] Limit the number of entries kept in history and allow it to be configured
* [Kernel] Add `String.start_with?` and `String.end_with?`
* [Typespec] Allow keywords, e.g. `[foo: integer, bar: boolean | module]`, in typespecs
* bug fix
* Bug fixes
* [Dict] `Enum.to_list` and `Dict.to_list` now return the same results for dicts
* [IEx] Enable shell customization via the `IEx.Options` module
* [Kernel] Fix a bug where `unquote_splicing` did not work on the left side of a stab op
@@ -236,12 +271,12 @@
# v0.9.0 (2013-05-23)
* enhancements
* Enhancements
* [ExUnit] `ExUnit.CaptureIO` now accepts an input to be used during capture
* [IEx] Add support for .iex files that are loaded during shell's boot process
* [IEx] Add `import_file/1` helper
* backwards incompatible changes
* Backwards incompatible changes
* [Enum] `Enum.Iterator` was replaced by the more composable and functional `Enumerable` protocol which supports reductions
* [File] `File.iterator/1` and `File.biniterator/1` have been removed in favor of the safe `File.iterator!/1` and `File.biniterator!/1` ones
* [Kernel] Erlang R15 is no longer supported
@@ -249,7 +284,7 @@
# v0.8.3 (2013-05-22)
* enhancements
* Enhancements
* [CLI] Flags `-p` and `-pr` fails if pattern match no files
* [CLI] Support `--hidden` and `--cookie` flags for distributed Erlang
* [Enum] Add `Enum.to_list/1`, `Enum.member?/2`, `Enum.uniq/2`, `Enum.max/1`, `Enum.max/2`, `Enum.min/1` and `Enum.min/2`
@@ -272,7 +307,7 @@
* [Mix] Load beam files available at `MIX_PATH` on CLI usage
* [String] Add `String.valid?` and `String.valid_character?`
* bug fix
* Bug fixes
* [ExUnit] Handle exit messages from in ExUnit
* [ExUnit] Failures on ExUnit's setup_all now invalidates all tests
* [Kernel] Ensure we don't splice keyword args unecessarily
@@ -284,7 +319,7 @@
* [Regex] Fix badmatch with `Regex.captures(%r/(.)/g, "cat")`
* [URI] Downcase host and scheme and URIs
* deprecations
* Deprecations
* [Code] `Code.eval` is deprecated in favor of `Code.eval_string`
* [Exception] `Exception.format_entry` is deprecated in favor of `Exception.format_stacktrace_entry`
* [ExUnit] `assert left inlist right` is deprecated in favor of `assert left in right`
@@ -293,7 +328,7 @@
* [Kernel] `var_context` in quote was deprecated in favor of `context`
* [Kernel] `Enum.at!` and `Dict.get!` is deprecated in favor of `Enum.fetch!` and `Dict.fetch!`
* backwards incompatible changes
* Backwards incompatible changes
* [Dict] `List.Dict` was moved to `ListDict`
* [IO] `IO.gets`, `IO.getn` and friends now return binaries when reading from stdio
* [Kernel] Precedence of `|>` has changed to lower to support constructs like `1..5 |> Enum.to_list`
@@ -301,7 +336,7 @@
# v0.8.2 (2013-04-20)
* enhancements
* Enhancements
* [ExUnit] Use ANSI escape codes in CLI output
* [ExUnit] Include suite run time on CLI results
* [ExUnit] Add support to doctests, allowing test cases to be generated from code samples
@@ -314,7 +349,7 @@
* [Mix] Support `--cover` on mix test and `test_coverage` on Mixfiles
* [Record] Each record now provides `Record.options` with the options supported by its `new` and `update` functions
* bug fix
* Bug fixes
* [Binary] inspect no longer escapes standalone hash `#`
* [IEx] The `h` helper can now retrieve docs for special forms
* [Kernel] Record optimizations were not being triggered in functions inside the record module
@@ -322,11 +357,11 @@
* [Kernel] Fix a bug where nested records could not use the Record[] syntax
* [Path] Fix a bug on `Path.expand` when expanding paths starting with `~`
* deprecations
* Deprecations
* [Kernel] `setelem/3` is deprecated in favor of `set_elem/3`
* [Kernel] `function(:is_atom, 1)` is deprecated in favor of `function(is_atom/1)`
* backwards incompatible changes
* Backwards incompatible changes
* [Kernel] `unquote` now only applies to the closest quote. If your code contains a quote that contains another quote that calls unquote, it will no longer work. Use `Macro.escape` instead and pass your quoted contents up in steps, for example:
quote do
@@ -341,7 +376,7 @@
# v0.8.1 (2013-02-17)
* enhancements
* Enhancements
* [ExUnit] Tests can now receive metadata set on setup/teardown callbacks
* [ExUnit] Add support to ExUnit.CaseTemplate to share callbacks in between test cases
* [IO] Add `IO.ANSI` to make it easy to write ANSI escape codes
@@ -354,13 +389,13 @@
* [OptionParser] Switches are now overridden by default but can be kept in order if chosen
* [Typespec] Better error reporting for invalid typespecs
* bug fix
* Bug fixes
* [Mix] Allow Mix projects to be generated with just one letter
* backwards incompatible changes
* Backwards incompatible changes
* [Kernel] `before_compile` and `after_compile` callbacks now receive the environment as first argument instead of the module
* deprecations
* Deprecations
* [ExUnit] Explicitly defined test/setup/teardown functions are deprecated
* [Kernel] Tidy up and clean `quote` API
* [Kernel] Old `:local.(args)` syntax is deprecated
@@ -368,7 +403,7 @@
# v0.8.0 (2013-01-28)
* enhancements
* Enhancements
* [Binary] Support `<< "string" :: utf8 >>` as in Erlang
* [Binary] Support `\a` escape character in binaries
* [Binary] Support syntax shortcut for specifying size in bit syntax
@@ -391,16 +426,16 @@
* [String] Add `String.capitalize` and `String.slice`
* [System] Add `System.tmp_dir`, `System.cwd` and `System.user_home`
* bug fix
* Bug fixes
* [Kernel] `import` with `only` accepts functions starting with underscore
* [String] `String.first` and `String.last` return nil for empty binaries
* [String] `String.rstrip` and `String.lstrip` now verify if argument is a binary
* [Typespec] Support `...` inside typespec's lists
* backwards incompatible changes
* Backwards incompatible changes
* [Kernel] The AST now allows metadata to be attached to each node. This means the second item in the AST is no longer an integer (representing the line), but a keywords list. Code that relies on the line information from AST or that manually generate AST nodes need to be properly updated
* deprecations
* Deprecations
* [Dict] Deprecate `Binary.Dict` and `OrdDict` in favor of `HashDict` and `ListDict`
* [File] Deprecate path related functions in favor of the module `Path`
* [Kernel] The `/>` operator has been deprecated in favor of `|>`
@@ -410,7 +445,7 @@
# v0.7.2 (2012-12-04)
* enhancements
* Enhancements
* [CLI] `--debug-info` is now true by default
* [ExUnit] Make ExUnit exit happen in two steps allowing developers to add custom `at_exit` hooks
* [IEx] Many improvements to helpers functions `h/1`, `s/1` and others
@@ -423,12 +458,12 @@
* [Record] Record accessors are now optimized and can be up to 6x faster in some cases
* [String] Support `\xXX` and `\x{HEX}` escape sequences in strings, char lists and regexes
* bug fix
* Bug fixes
* [Bootstrap] Compiling Elixir source no longer fails if environment variables contain utf-8 entries
* [IEx] IEx will now wait for all command line options to be processed before starting
* [Kernel] Ensure proper stacktraces when showing deprecations
* deprecations
* Deprecations
* [Enum] `Enum.qsort` is deprecated in favor of `Enum.sort`
* [List] `List.sort` and `List.uniq` have been deprecated in favor of their `Enum` counterparts
* [Record] Default-based generated functions are deprecated
@@ -436,7 +471,7 @@
# v0.7.1 (2012-11-18)
* enhancements
* Enhancements
* [IEx] Only show documented functions and also show docs for default generated functions
* [IO] Add `IO.binread`, `IO.binwrite` and `IO.binreadline` to handle raw binary file operations
* [ExUnit] Add support for user configuration at `HOME/.ex_unit.exs`
@@ -454,20 +489,20 @@
* [Typespec] Define a default type `t` for protocols and records
* [Typespec] Add support for the access protocol in typespecs
* bug fix
* Bug fixes
* [Kernel] Fix an issue where variables inside clauses remained unassigned
* [Kernel] Ensure `defoverridable` functions can be referred in many clauses
* [Kernel] Allow keywords as function names when following a dot (useful when integrating with erlang libraries)
* [File] File is opened by default on binary mode instead of utf-8
* deprecations
* Deprecations
* [Behaviour] `defcallback/1` is deprecated in favor of `defcallback/2` which matches erlang `@callbacks`
* [Enum] `Enum.times` is deprecated in favor of using ranges
* [System] `halt` moved to `System` module
# v0.7.0 (2012-10-20)
* enhancements
* Enhancements
* [Behaviour] Add Behaviour with a simple callback DSL to define callbacks
* [Binary] Add a Dict binary that converts its keys to binaries on insertion
* [Binary] Optimize `Binary.Inspect` and improve inspect for floats
@@ -493,7 +528,7 @@
* [Regex] Add `:return` option to `Regex.run` and `Regex.scan`
* [String] Add a String module responsible for handling UTf-8 binaries
* bug fix
* Bug fixes
* [File] `File.cp` and `File.cp_r` now preserves the file's mode
* [IEx] Fix a bug where printing to `:stdio` on `IEx` was causing it to hang
* [Macro] Fix a bug where quoted expressions were not behaving the same as their non-quoted counterparts
@@ -501,12 +536,12 @@
* [Mix] Mix now exits with status 1 in case of failures
* [Protocol] Avoid false positives on protocol dispatch (a bug caused the dispatch to be triggered to an invalid protocol)
* backwards incompatible changes
* Backwards incompatible changes
* [ExUnit] `setup` and `teardown` callbacks now receives the test name as second argument
* [Kernel] Raw function definition with `def/4`, `defp/4`, `defmacro/4`, `defmacrop/4` now evaluates all arguments. The previous behaviour was accidental and did not properly evaluate all arguments
* [Kernel] Change tuple-related (`elem` and `setelem`), Enum functions (`find_index`, `nth!` and `times`) and List functions (List.key*) to zero-index
* deprecations
* Deprecations
* [Code] `Code.require_file` and `Code.load_file` now expect the full name as argument
* [Enum] `List.reverse/1` and `List.zip/2` were moved to `Enum`
* [GenServer] Rename `GenServer.Behavior` to `GenServer.Behaviour`
@@ -516,14 +551,14 @@
# v0.6.0 (2012-08-01)
* incompatible changes
* Backwards incompatible changes
* [Kernel] Compile files now follow `Elixir-ModuleName` convention to solve issues with Erlang embedded mode. This removes the `__MAIN__` pseudo-variable as modules are now located inside `Elixir` namespace
* [Kernel] `__using__` callback triggered by `use` now receives just one argument. Caller information can be accessed via macros using `__CALLER__`
* [Kernel] Comprehensions syntax changed to be more compatible with Erlang behavior
* [Kernel] loop and recur are removed in favor of recursion with named functions
* [Module] Removed data functions in favor of unifying the attributes API
* deprecations
* Deprecations
* [Access] The semantics of the access protocol were reduced from a broad query API to simple data structure key-based access
* [ExUnit] Some assertions are deprecated in favor of simply using `assert()`
* [File] `File.read_info` is deprecated in favor of `File.stat`
@@ -535,7 +570,7 @@
* [Module] `Module.function_defined?` is deprecated in favor of `Module.defines?`
* [Module] `Module.defined_functions` is deprecated in favor of `Module.definitions_in`
* enhancements
* Enhancements
* [Enum] Enhance Enum protocol to support `Enum.count`
* [Enum] Optimize functions when a list is given as collection
* [Enum] Add `find_index`, `nth!` and others
+1 -1
View File
@@ -1 +1 @@
0.10.2
0.10.3
+1 -1
View File
@@ -38,4 +38,4 @@ readlink_f () {
SELF=$(readlink_f "$0")
SCRIPT_PATH=$(dirname "$SELF")
ELIXIR_NO_CLI=1 exec "$SCRIPT_PATH"/elixir --no-halt --erl "-nouser -s Elixir.IEx.CLI" +iex "$@"
ELIXIR_NO_CLI=1 exec "$SCRIPT_PATH"/elixir --no-halt --erl "-user Elixir.IEx.CLI" +iex "$@"
+3 -3
View File
@@ -31,7 +31,7 @@ defmodule EEx.Compiler do
defp generate_buffer([{ :start_expr, line, mark, chars }|t], buffer, scope, state) do
{ contents, t } = generate_buffer(t, "", [chars|scope], state.dict([]).line(line).start_line(line))
buffer = state.engine.handle_expr(buffer, mark, contents)
generate_buffer(t, buffer, scope, state.dict([]))
generate_buffer(t, buffer, scope, state)
end
defp generate_buffer([{ :middle_expr, line, _, chars }|t], buffer, [current|scope], state) do
@@ -68,7 +68,7 @@ defmodule EEx.Compiler do
else
key = length(state.dict)
placeholder = '__EEX__(' ++ integer_to_list(key) ++ ');'
{ current ++ placeholder ++ new_lines ++ chars, state.update_dict([{key, buffer}|&1]) }
{ current ++ placeholder ++ new_lines ++ chars, state.update_dict(&[{key, buffer}|&1]) }
end
end
@@ -108,7 +108,7 @@ defmodule EEx.Compiler do
end
defp insert_quotes(list, dict) when is_list(list) do
Enum.map list, insert_quotes(&1, dict)
Enum.map list, &insert_quotes(&1, dict)
end
defp insert_quotes(other, _dict) do
+1 -1
View File
@@ -136,7 +136,7 @@ defmodule EEx.Tokenizer do
end
defp end_index(tokens) do
Enum.find_index(tokens, match?({ :end, _ }, &1)) || :infinity
Enum.find_index(tokens, &match?({ :end, _ }, &1)) || :infinity
end
defp check_spaces(string, token) do
+15
View File
@@ -264,6 +264,21 @@ foo
assert_eval expected, string
end
test "evaluates nested do expressions" do
string = """
<% y = ["a", "b", "c"] %>
<%= cond do %>
<% "a" in y -> %>
Good
<% true -> %>
<% if true do %>true<% else %>false<% end %>
Bad
<% end %>
"""
assert_eval "\n\n Good\n \n", string
end
test "unicode" do
template = """
• <%= "•" %> •
-1
View File
@@ -17,7 +17,6 @@
module=nil, %% the current module
function=nil, %% the current function
vars=[], %% a dict of defined variables and their alias
list_vars=nil, %% a list of vars passed down to Macro.Env
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
+1 -1
View File
@@ -166,7 +166,7 @@ defmodule Code do
end
defp validate_requires(kind, requires) do
valid = is_list(requires) and Enum.all?(requires, is_atom(&1))
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]"
+2 -2
View File
@@ -99,7 +99,7 @@ defmodule Dict.Behaviour do
defoverridable values: 1
def equal?(dict1, dict2) do
case size(dict1) == size(dict2) do
case Dict.size(dict1) == Dict.size(dict2) do
false -> false
true ->
try do
@@ -122,4 +122,4 @@ defmodule Dict.Behaviour do
defoverridable merge: 2, merge: 3
end
end
end
end
+31 -28
View File
@@ -473,7 +473,7 @@ defmodule Enum do
## Examples
iex> Enum.filter_map([1, 2, 3], fn(x) -> rem(x, 2) == 0 end, &1 * 2)
iex> Enum.filter_map([1, 2, 3], fn(x) -> rem(x, 2) == 0 end, &(&1 * 2))
[4]
"""
@@ -648,14 +648,9 @@ defmodule Enum do
"""
@spec join(t) :: String.t
@spec join(t, String.t | char_list) :: String.t | char_list
@spec join(t, String.t) :: String.t
def join(collection, joiner // "")
def join(collection, joiner) when is_list(joiner) do
IO.write "Enum.join/2 with a char list is deprecated, please use do an explicit conversion instead\n#{Exception.format_stacktrace}"
:unicode.characters_to_list join(collection, :unicode.characters_to_binary(joiner))
end
def join(collection, joiner) when is_binary(joiner) do
Enumerable.reduce(collection, "", fn
entry, "" -> to_string(entry)
@@ -708,14 +703,9 @@ defmodule Enum do
"""
@spec map_join(t, (element -> any)) :: String.t
@spec map_join(t, String.t | char_list, (element -> any)) :: String.t | char_list
@spec map_join(t, String.t, (element -> any)) :: String.t
def map_join(collection, joiner // "", mapper)
def map_join(collection, joiner, mapper) when is_list(joiner) do
IO.write "Enum.map_join/3 with a char list is deprecated, please use do an explicit conversion instead\n#{Exception.format_stacktrace}"
:unicode.characters_to_list map_join(collection, :unicode.characters_to_binary(joiner), mapper)
end
def map_join(collection, joiner, mapper) when is_binary(joiner) do
Enumerable.reduce(collection, "", fn
entry, "" -> to_string(mapper, entry)
@@ -902,8 +892,8 @@ defmodule Enum do
end
def sort(collection) do
fun = &1 <= &2
Enumerable.reduce(collection, [], sort_reducer(&1, &2, fun)) |>
fun = &(&1 <= &2)
Enumerable.reduce(collection, [], &sort_reducer(&1, &2, fun)) |>
sort_terminator(fun)
end
@@ -922,7 +912,7 @@ defmodule Enum do
end
def sort(collection, fun) do
Enumerable.reduce(collection, [], sort_reducer(&1, &2, fun)) |>
Enumerable.reduce(collection, [], &sort_reducer(&1, &2, fun)) |>
sort_terminator(fun)
end
@@ -1041,6 +1031,31 @@ defmodule Enum do
take(list, count)
end
@doc """
Returns a collection of every `nth` items in the collection,
starting with the first.
## Examples
iex> Enum.take_every(1..10, 2)
[1, 3, 5, 7, 9]
"""
@spec take_every(t, integer) :: list
def take_every(_collection, 0), do: []
def take_every(collection, nth) do
res = Enumerable.reduce(collection, nil, fn
x, { acc, ^nth } -> { [x | acc], 1 }
_, { acc, count } -> { acc, count + 1 }
x, nil -> { [x], 1 }
end)
case res do
nil -> []
{ list, _ } -> :lists.reverse(list)
end
end
@doc """
Takes the items at the beginning of `collection` while `fun` returns `true`.
Expects an ordered collection.
@@ -1169,12 +1184,6 @@ defmodule Enum do
reduce(collection, &Kernel.max(&1, &2))
end
@doc false
def max(coll, fun) do
IO.write "Enum.max/2 is deprecated, please use Enum.max_by/2 instead\n#{Exception.format_stacktrace}"
max_by(coll, fun)
end
@doc """
Returns the maximum value as calculated by the given function.
Raises `EmptyError` if the collection is empty.
@@ -1229,12 +1238,6 @@ defmodule Enum do
reduce(collection, &Kernel.min(&1, &2))
end
@doc false
def min(coll, fun) do
IO.write "Enum.min/2 is deprecated, please use Enum.min_by/2 instead\n#{Exception.format_stacktrace}"
min_by(coll, fun)
end
@doc """
Returns the minimum value as calculated by the given function.
Raises `EmptyError` if the collection is empty.
+1 -8
View File
@@ -47,13 +47,6 @@ defexception File.CopyError, [reason: nil, action: "", source: nil, destination:
end
end
defexception File.IteratorError, reason: nil do
def message(exception) do
formatted = iolist_to_binary(:file.format_error(reason exception))
"error during file iteration: #{formatted}"
end
end
defmodule File do
@moduledoc """
This module contains functions to manipulate files.
@@ -945,7 +938,7 @@ defmodule File do
"""
def ls(path // ".") do
case F.list_dir(path) do
{ :ok, file_list } -> { :ok, Enum.map(file_list, to_string(&1)) }
{ :ok, file_list } -> { :ok, Enum.map(file_list, &to_string(&1)) }
{ :error, _ } = error -> error
end
end
+2 -2
View File
@@ -60,7 +60,7 @@ defmodule GenFSM.Behaviour do
end
def short_paid(:coin, state_data) do
{ :next_state, :paid_in_full, state_data.update_coins(&1 + 1) }
{ :next_state, :paid_in_full, &state_data.update_coins(&1 + 1) }
end
def short_paid(:request_coffee, state_data) do
@@ -84,7 +84,7 @@ defmodule GenFSM.Behaviour do
def paid_in_full(:coin, state_data) do
{ :next_state, :paid_in_full, state_data.update_coins(&1 + 1) }
{ :next_state, :paid_in_full, &state_data.update_coins(&1 + 1) }
end
def paid_in_full(:request_coffee, _state_data) do
+2 -8
View File
@@ -107,12 +107,6 @@ defmodule HashDict do
dict
end
@doc false
def update(dict, key, fun) when is_function(fun, 1) do
IO.write "HashDict.update/3 is deprecated, please use HashDict.update!/3 instead\n#{Exception.format_stacktrace}"
update!(dict, key, fun)
end
def update!(dict, key, fun) when is_function(fun, 1) do
case dict_put(dict, key, { :update, nil, fun }) do
{ dict, 0 } ->
@@ -159,13 +153,13 @@ defmodule HashDict do
def merge(dict1, dict2, callback) when is_record(dict1, HashDict) and is_record(dict2, HashDict) and elem(dict1, 1) < elem(dict2, 1) do
dict_fold dict1, dict2, fn [k|v1], acc ->
update(acc, k, v1, callback.(k, v1, &1))
update(acc, k, v1, &callback.(k, v1, &1))
end
end
def merge(dict1, dict2, callback) when is_record(dict1, HashDict) and is_record(dict2, HashDict) do
dict_fold dict2, dict1, fn [k|v2], acc ->
update(acc, k, v2, callback.(k, &1, v2))
update(acc, k, v2, &callback.(k, &1, v2))
end
end
+1 -1
View File
@@ -103,7 +103,7 @@ defmodule HashSet do
end
def to_list(set) do
set_fold(set, [], [&1|&2]) |> :lists.reverse
set_fold(set, [], &[&1|&2]) |> :lists.reverse
end
def put(set, member) do
+5 -5
View File
@@ -267,9 +267,9 @@ defimpl Inspect, for: List do
:io_lib.printable_list(thing) ->
<< ?', Inspect.BitString.escape(String.from_char_list!(thing), ?') :: binary, ?' >>
keyword?(thing) && not opts.raw ->
surround_many("[", thing, "]", opts.limit, keyword(&1, opts))
surround_many("[", thing, "]", opts.limit, &keyword(&1, opts))
true ->
surround_many("[", thing, "]", opts.limit, Kernel.inspect(&1, opts))
surround_many("[", thing, "]", opts.limit, &Kernel.inspect(&1, opts))
end
end
@@ -317,7 +317,7 @@ defimpl Inspect, for: Tuple do
def inspect(tuple, opts) do
unless opts.raw do
record_inspect(tuple, opts)
end || surround_many("{", tuple_to_list(tuple), "}", opts.limit, Kernel.inspect(&1, opts))
end || surround_many("{", tuple_to_list(tuple), "}", opts.limit, &Kernel.inspect(&1, opts))
end
## Helpers
@@ -331,7 +331,7 @@ defimpl Inspect, for: Tuple do
else
surround_record(name, fields, tail, opts)
end
end || surround_many("{", [name|tail], "}", opts.limit, Kernel.inspect(&1, opts))
end || surround_many("{", [name|tail], "}", opts.limit, &Kernel.inspect(&1, opts))
end
defp record_fields(name) do
@@ -351,7 +351,7 @@ defimpl Inspect, for: Tuple do
concat(
Inspect.Atom.inspect(name, opts),
surround_many("[", Enum.zip(fields, tail), "]", opts.limit, keyword(&1, opts))
surround_many("[", Enum.zip(fields, tail), "]", opts.limit, &keyword(&1, opts))
)
end
+40 -7
View File
@@ -82,6 +82,28 @@ defmodule Inspect.Algebra do
defrecordp :doc_break, str: " " :: binary
defrecordp :doc_group, doc: :doc_nil :: t
defmacrop is_doc(doc) do
if __CALLER__.in_guard? do
do_is_doc(doc)
else
var = quote do: doc
quote do
unquote(var) = unquote(doc)
unquote(do_is_doc(var))
end
end
end
defp do_is_doc(doc) do
quote do
unquote(doc) |> is_binary or
unquote(doc) |> is_integer or
unquote(doc) == :doc_nil or
(unquote(doc) |> is_tuple and
elem(unquote(doc), 0) in [:doc_cons, :doc_nest, :doc_break, :doc_group])
end
end
@doc """
Returns `:doc_nil` which is a document entity used to represent
nothingness. Takes no arguments.
@@ -107,13 +129,17 @@ defmodule Inspect.Algebra do
"""
@spec concat(t, t) :: doc_cons_t
def concat(x, y), do: doc_cons(left: x, right: y)
def concat(x, y) when is_doc(x) and is_doc(y) do
doc_cons(left: x, right: y)
end
@doc """
Concatenates a list of documents.
"""
@spec concat([t]) :: doc_cons_t
def concat(docs), do: folddoc(docs, concat(&1, &2))
def concat(docs) do
folddoc(docs, &concat(&1, &2))
end
@doc """
Nests document entity `x` positions deep. Nesting will be
@@ -127,8 +153,13 @@ defmodule Inspect.Algebra do
"""
@spec nest(t, non_neg_integer) :: doc_nest_t
def nest(x, 0), do: x
def nest(x, i) when is_integer(i), do: doc_nest(indent: i, doc: x)
def nest(x, 0) when is_doc(x) do
x
end
def nest(x, i) when is_doc(x) and is_integer(i) do
doc_nest(indent: i, doc: x)
end
@doc %S"""
Document entity representing a break. This break can
@@ -198,7 +229,9 @@ defmodule Inspect.Algebra do
"""
@spec group(t) :: doc_group_t
def group(d), do: doc_group(doc: d)
def group(d) when is_doc(d) do
doc_group(doc: d)
end
@doc """
Inserts a mandatory single space between two document entities.
@@ -273,11 +306,11 @@ defmodule Inspect.Algebra do
## Examples
iex> doc = Inspect.Algebra.surround_many("[", Enum.to_list(1..5), "]", :infinity, integer_to_binary(&1))
iex> doc = Inspect.Algebra.surround_many("[", Enum.to_list(1..5), "]", :infinity, &integer_to_binary(&1))
iex> Inspect.Algebra.pretty(doc, 5)
"[1,\n 2,\n 3,\n 4,\n 5]"
iex> doc = Inspect.Algebra.surround_many("[", Enum.to_list(1..5), "]", 3, integer_to_binary(&1))
iex> doc = Inspect.Algebra.surround_many("[", Enum.to_list(1..5), "]", 3, &integer_to_binary(&1))
iex> Inspect.Algebra.pretty(doc, 20)
"[1, 2, 3, ...]"
+16 -21
View File
@@ -1,7 +1,17 @@
defexception IO.StreamError, reason: nil do
def message(exception) do
formatted = iolist_to_binary(:file.format_error(reason exception))
"error during streaming: #{formatted}"
end
end
defmodule IO do
@moduledoc """
Module responsible for doing IO. Many functions in this
module expects an IO device and an io data encoded in UTF-8.
Use the bin* functions if the data is binary, useful when
working with raw bytes or when no unicode conversion should
be performed.
An IO device must be a pid or an atom representing a process.
For convenience, Elixir provides `:stdio` and `:stderr` as
@@ -18,9 +28,6 @@ defmodule IO do
* A list of binaries or a list of char lists (as described above);
* If none of the above, `to_string` is invoked on the
given argument;
"""
import :erlang, only: [group_leader: 0]
@@ -160,7 +167,7 @@ defmodule IO do
Gets a number of bytes from the io device. If the
io device is a unicode device, `count` implies
the number of unicode codepoints to be retrieved.
Otherwise, `count` is the number of raw bytes to be retrieved.
Otherwise, `count` is the number of raw bytes to be retrieved.
It returns:
* `data` - The input characters.
@@ -220,17 +227,11 @@ defmodule IO do
Here is an example on how we mimic an echo server
from the command line:
Enum.each IO.stream(:stdio, :line), IO.write(&1)
Enum.each IO.stream(:stdio, :line), &IO.write(&1)
"""
def stream(device, line_or_bytes) do
stream(map_dev(device), line_or_bytes, &1, &2)
end
@doc false
def stream(device) do
IO.write "IO.stream(device) is deprecated, please use IO.stream(device, :line) instead\n#{Exception.format_stacktrace}"
stream(device, :line)
fn(acc, f) -> stream(map_dev(device), line_or_bytes, acc, f) end
end
@doc """
@@ -240,13 +241,7 @@ defmodule IO do
This reads the io as a raw binary.
"""
def binstream(device, line_or_bytes) do
binstream(map_dev(device), line_or_bytes, &1, &2)
end
@doc false
def binstream(device) do
IO.write "IO.binstream(device) is deprecated, please use IO.binstream(device, :line) instead\n#{Exception.format_stacktrace}"
binstream(device, :line)
fn(acc, f) -> binstream(map_dev(device), line_or_bytes, acc, f) end
end
@doc false
@@ -255,7 +250,7 @@ defmodule IO do
:eof ->
acc
{ :error, reason } ->
raise File.IteratorError, reason: reason
raise IO.StreamError, reason: reason
data ->
stream(device, what, fun.(data, acc), fun)
end
@@ -267,7 +262,7 @@ defmodule IO do
:eof ->
acc
{ :error, reason } ->
raise File.IteratorError, reason: reason
raise IO.StreamError, reason: reason
data ->
binstream(device, what, fun.(data, acc), fun)
end
+42 -74
View File
@@ -1,5 +1,3 @@
import Kernel, except: [raise: 1, raise: 2]
defmodule Kernel do
@moduledoc """
`Kernel` provides the default macros and functions
@@ -479,20 +477,6 @@ defmodule Kernel do
:erlang.binary_to_existing_atom(binary, encoding)
end
@doc false
def binary_to_list(binary) do
IO.write "binary_to_list/1 is deprecated. Please use String.to_char_list!/1 instead "
IO.write "unless you are working with bytes, then you should use :binary.bin_to_list/1\n#{Exception.format_stacktrace}"
:erlang.binary_to_list(binary)
end
@doc false
def binary_to_list(binary, start, stop) do
IO.write "binary_to_list/3 is deprecated. Please use String.to_char_list!/1 instead "
IO.write "unless you are working with bytes, then you should use :binary.bin_to_list/3\n#{Exception.format_stacktrace}"
:erlang.binary_to_list(binary, start, stop)
end
@doc """
Returns an Erlang term which is the result of decoding the binary
object `binary`, which must be encoded according to the Erlang external
@@ -873,13 +857,6 @@ defmodule Kernel do
:erlang.list_to_atom(list)
end
@doc false
def list_to_binary(list) do
IO.write "list_to_binary/1 is deprecated. Please use String.from_char_list!/1 instead "
IO.write "unless you are working with bytes, then you should use iolist_to_binary/1\n#{Exception.format_stacktrace}"
:erlang.list_to_binary(list)
end
@doc """
Returns a bitstring which is made from the integers and bitstrings in `bitstring_list`.
(the last tail in `bitstring_list` is allowed to be a bitstring.)
@@ -2015,8 +1992,7 @@ defmodule Kernel do
case is_atom(expanded) do
false ->
raise ArgumentError,
message: "invalid arguments for use, expected an atom or alias as argument"
:erlang.error ArgumentError.exception(message: "invalid arguments for use, expected an atom or alias as argument")
true ->
quote do
require unquote(expanded)
@@ -2026,7 +2002,14 @@ defmodule Kernel do
end
@doc %S"""
Inspect the given arguments according to the `Inspect` protocol.
Inspect the given argument according to the `Inspect` protocol.
The second argument is a keywords list with options to control
inspection.
The second argument may also be an instance of the `Inspect.Opts`
record and, in such cases, instead of returning a string, it
returns an algebra document which can be converted to a string
via `Inspect.Algebra`.
## Options
@@ -2061,7 +2044,7 @@ defmodule Kernel do
representation of an Elixir term. In such cases, the inspected result
must start with `#`. For example, inspecting a function will return:
inspect &1 + &2
inspect &(&1 + &2)
#=> #Function<...>
"""
@@ -2103,12 +2086,6 @@ defmodule Kernel do
quote do: String.Chars.to_string(unquote(arg))
end
@doc false
def to_binary(chars) do
IO.write "to_binary/1 is deprecated, please use to_string/1 instead\n#{Exception.format_stacktrace}"
String.Chars.to_string(chars)
end
@doc """
Convert the argument to a list according to the List.Chars protocol.
@@ -2184,12 +2161,12 @@ defmodule Kernel do
Match can also be used to filter or find a value in an enumerable:
list = [{:a, 1}, {:b, 2}, {:a, 3}]
Enum.filter list, match?({:a, _}, &1)
Enum.filter list, &match?({:a, _}, &1)
Guard clauses can also be given to the match:
list = [{:a, 1}, {:b, 2}, {:a, 3}]
Enum.filter list, match?({:a, x } when x < 2, &1)
Enum.filter list, &match?({:a, x } when x < 2, &1)
However, variables assigned in the match will not be available
outside of the function call:
@@ -2208,7 +2185,7 @@ defmodule Kernel do
end
defmacro match?(left, right) do
{ left, _ } = falsify_var(left, [], falsify_all(&1, &2))
{ left, _ } = falsify_var(left, [], &falsify_all/2)
quote do
case unquote(right) do
@@ -2237,7 +2214,7 @@ defmodule Kernel do
defp falsify_var({ :when, meta, [left, right] }, acc, fun) do
{ left, acc } = falsify_var(left, acc, fun)
{ right, acc } = falsify_var(right, acc, falsify_selected(&1, &2))
{ right, acc } = falsify_var(right, acc, &falsify_selected/2)
{ { :when, meta, [left, right] }, acc }
end
@@ -2258,7 +2235,7 @@ defmodule Kernel do
end
defp falsify_var(list, acc, fun) when is_list(list) do
:lists.mapfoldl(falsify_var(&1, &2, fun), acc, list)
:lists.mapfoldl(&falsify_var(&1, &2, fun), acc, list)
end
defp falsify_var(other, acc, _fun) do
@@ -2652,8 +2629,8 @@ defmodule Kernel do
new_acc =
case condition do
{ :_, _, atom } when is_atom(atom) ->
raise ArgumentError, message: <<"unbound variable _ inside cond. ",
"If you want the last clause to match, you probably meant to use true ->">>
:erlang.error ArgumentError.exception(message: <<"unbound variable _ inside cond. ",
"If you want the last clause to match, you probably meant to use true ->">>)
x when is_atom(x) and not x in [false, nil] ->
clause
_ ->
@@ -3055,12 +3032,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:
@@ -3095,7 +3072,7 @@ defmodule Kernel do
end
defp pipeline_op(_, arg) do
raise ArgumentError, message: "unsupported expression in pipeline |> operator: #{Macro.to_string arg}"
:erlang.error ArgumentError.exception(message: "unsupported expression in pipeline |> operator: #{Macro.to_string arg}")
end
@doc """
@@ -3120,12 +3097,10 @@ defmodule Kernel do
"""
@spec raise(binary | atom | tuple) :: no_return
def raise(msg) when is_binary(msg) do
:erlang.error RuntimeError[message: msg]
end
def raise(exception) do
raise(exception, [])
defmacro raise(msg) when is_binary(msg) do
quote do
:erlang.error RuntimeError[message: unquote(msg)]
end
end
@doc """
@@ -3146,8 +3121,16 @@ defmodule Kernel do
"""
@spec raise(tuple | atom, list) :: no_return
def raise(exception, args) do
:erlang.error exception.exception(args)
defmacro raise(exception, args // []) do
quote do
exception = unquote(exception)
case exception do
e when is_binary(e) ->
:erlang.error RuntimeError.new(message: exception)
_ ->
:erlang.error exception.exception(unquote(args))
end
end
end
@doc """
@@ -3288,15 +3271,15 @@ defmodule Kernel do
{ :error, _ } ->
:elixir_aliases.ensure_loaded(caller.line, caller.file, atom, caller.context_modules)
_ ->
raise ArgumentError, message: "cannot use module #{inspect atom} in access protocol because it does not export __record__/1"
:erlang.error ArgumentError.exception(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 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)" >>
true -> :erlang.error ArgumentError.exception(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
end
@@ -3347,14 +3330,14 @@ defmodule Kernel do
funs = Macro.escape(funs, unquote: true)
quote bind_quoted: [funs: funs, opts: opts] do
target = Keyword.get(opts, :to) ||
raise(ArgumentError, message: "Expected to: to be given as argument")
:erlang.error ArgumentError.exception(message: "Expected to: to be given as argument")
append_first = Keyword.get(opts, :append_first, false)
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_string(fun)}"
:error -> :erlang.error ArgumentError.exception(message: "invalid syntax in defdelegate #{Macro.to_string(fun)}")
end
actual_args =
@@ -3388,12 +3371,6 @@ defmodule Kernel do
string
end
@doc false
defmacro sigil_B(string, []) do
IO.write "%B() is deprecated, please use %S() instead\n#{Exception.format_stacktrace}"
string
end
@doc """
Handles the sigil %s. It returns a string as if it was double quoted
string, unescaping characters and replacing interpolations.
@@ -3410,12 +3387,6 @@ defmodule Kernel do
{ :<<>>, line, Macro.unescape_tokens(pieces) }
end
@doc false
defmacro sigil_b({ :<<>>, line, pieces }, []) do
IO.write "%b() is deprecated, please use %s() instead\n#{Exception.format_stacktrace}"
{ :<<>>, line, Macro.unescape_tokens(pieces) }
end
@doc """
Handles the sigil %C. It simply returns a char list
without escaping characters and without interpolations.
@@ -3466,13 +3437,13 @@ defmodule Kernel do
"""
defmacro sigil_r({ :<<>>, _line, [string] }, options) when is_binary(string) do
binary = Macro.unescape_string(string, Regex.unescape_map(&1))
binary = Macro.unescape_string(string, fn(x) -> Regex.unescape_map(x) end)
regex = Regex.compile!(binary, :binary.list_to_bin(options))
Macro.escape(regex)
end
defmacro sigil_r({ :<<>>, line, pieces }, options) do
binary = { :<<>>, line, Macro.unescape_tokens(pieces, Regex.unescape_map(&1)) }
binary = { :<<>>, line, Macro.unescape_tokens(pieces, fn(x) -> Regex.unescape_map(x) end) }
quote do: Regex.compile!(unquote(binary), unquote(:binary.list_to_bin(options)))
end
@@ -3612,11 +3583,8 @@ defmodule Kernel do
defp split_words(string, modifiers) do
mod = case modifiers do
[] -> ?s
[mod] when mod in [?b] ->
IO.write "%w()b is deprecated, please use %w()s instead\n#{Exception.format_stacktrace}"
?s
[mod] when mod in [?s, ?a, ?c] -> mod
_else -> raise ArgumentError, message: "modifier must be one of: s, a, c"
_else -> :erlang.error ArgumentError.exception(message: "modifier must be one of: s, a, c")
end
case is_binary(string) do
+27 -27
View File
@@ -15,12 +15,12 @@ defmodule Kernel.CLI do
System.argv(argv)
run fn ->
command_results = Enum.map(Enum.reverse(config.commands), process_command(&1, config))
command_results = Enum.map(Enum.reverse(config.commands), &process_command(&1, config))
command_errors = lc { :error, msg } inlist command_results, do: msg
errors = Enum.reverse(config.errors) ++ command_errors
if errors != [] do
Enum.each(errors, IO.puts(:stderr, &1))
Enum.each(errors, &IO.puts(:stderr, &1))
System.halt(1)
end
end, config.halt
@@ -77,7 +77,7 @@ defmodule Kernel.CLI do
defp shared_option?(list, config, callback) do
case process_shared(list, config) do
{ [h|hs], _ } when h == hd(list) ->
new_config = config.update_errors ["#{h} : Unknown option" | &1]
new_config = config.update_errors &["#{h} : Unknown option" | &1]
callback.(hs, new_config)
{ new_list, new_config } ->
callback.(new_list, new_config)
@@ -120,17 +120,17 @@ defmodule Kernel.CLI do
end
defp process_shared(["-pa", h|t], config) do
Enum.each Path.wildcard(Path.expand(h)), Code.prepend_path(&1)
Enum.each Path.wildcard(Path.expand(h)), &Code.prepend_path(&1)
process_shared t, config
end
defp process_shared(["-pz", h|t], config) do
Enum.each Path.wildcard(Path.expand(h)), Code.append_path(&1)
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]
process_shared t, config.update_commands &[{:app, h}|&1]
end
defp process_shared(["--no-halt"|t], config) do
@@ -138,15 +138,15 @@ defmodule Kernel.CLI do
end
defp process_shared(["-e", h|t], config) do
process_shared t, config.update_commands [{:eval, h}|&1]
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]
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]
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
@@ -176,15 +176,15 @@ defmodule Kernel.CLI do
end
defp process_argv(["-S", h|t], config) do
{ config.update_commands([{:script, h}|&1]), t }
{ config.update_commands(&[{:script, h}|&1]), t }
end
defp process_argv([h|t] = list, config) do
case h do
"-" <> _ ->
shared_option? list, config, process_argv(&1, &2)
shared_option? list, config, &process_argv(&1, &2)
_ ->
{ config.update_commands([{:file, h}|&1]), t }
{ config.update_commands(&[{:file, h}|&1]), t }
end
end
@@ -203,19 +203,19 @@ defmodule Kernel.CLI do
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])
process_compiler t, config.update_compiler_options(&[{:warnings_as_errors, true}|&1])
end
defp process_compiler(["--verbose"|t], config) do
@@ -225,15 +225,15 @@ defmodule Kernel.CLI do
defp process_compiler([h|t] = list, config) do
case h do
"-" <> _ ->
shared_option? list, config, process_compiler(&1, &2)
shared_option? list, config, &process_compiler(&1, &2)
_ ->
pattern = if :filelib.is_dir(h), do: "#{h}/**/*.ex", else: h
process_compiler t, config.update_compile [pattern|&1]
process_compiler t, config.update_compile &[pattern|&1]
end
end
defp process_compiler([], config) do
{ config.update_commands([{:compile, config.compile}|&1]), [] }
{ config.update_commands(&[{:compile, config.compile}|&1]), [] }
end
# Process iex options
@@ -253,15 +253,15 @@ defmodule Kernel.CLI do
end
defp process_iex(["-S", h|t], config) do
{ config.update_commands([{:script, h}|&1]), t }
{ config.update_commands(&[{:script, h}|&1]), t }
end
defp process_iex([h|t] = list, config) do
case h do
"-" <> _ ->
shared_option? list, config, process_iex(&1, &2)
shared_option? list, config, &process_iex(&1, &2)
_ ->
{ config.update_commands([{:file, h}|&1]), t }
{ config.update_commands(&[{:file, h}|&1]), t }
end
end
@@ -315,10 +315,10 @@ defmodule Kernel.CLI do
defp process_command({:require, pattern}, _config) when is_binary(pattern) do
files = Path.wildcard(pattern)
files = Enum.uniq(files)
files = Enum.filter files, :filelib.is_regular(&1)
files = Enum.filter files, &:filelib.is_regular(&1)
if files != [] do
Enum.map files, Code.require_file(&1)
Enum.map files, &Code.require_file(&1)
:ok
else
{ :error, "-r : No files matched pattern #{pattern}" }
@@ -328,7 +328,7 @@ defmodule Kernel.CLI do
defp process_command({:parallel_require, pattern}, _config) when is_binary(pattern) do
files = Path.wildcard(pattern)
files = Enum.uniq(files)
files = Enum.filter files, :filelib.is_regular(&1)
files = Enum.filter files, &:filelib.is_regular(&1)
if files != [] do
Kernel.ParallelRequire.files(files)
@@ -341,9 +341,9 @@ defmodule Kernel.CLI do
defp process_command({:compile, patterns}, config) do
:filelib.ensure_dir(:filename.join(config.output, "."))
files = Enum.map patterns, Path.wildcard(&1)
files = Enum.map patterns, &Path.wildcard(&1)
files = Enum.uniq(Enum.concat(files))
files = Enum.filter files, :filelib.is_regular(&1)
files = Enum.filter files, &:filelib.is_regular(&1)
if files != [] do
Code.compiler_options(config.compiler_options)
+2 -2
View File
@@ -75,7 +75,7 @@ defmodule Kernel.RecordRewriter do
if :lists.member({ function, length(args) + 1 }, optimizable) do
case record_field_info(function) do
{ kind, field } ->
if index = Enum.find_index(fields, field == &1) do
if index = Enum.find_index(fields, &(field == &1)) do
optimize_record_accessor_call(line, res, kind, field, index, left, args)
end
nil ->
@@ -352,7 +352,7 @@ defmodule Kernel.RecordRewriter do
end
defp extract_vars({ :tuple, _, args }, vars) do
[Enum.map(args, extract_vars(&1, []))|vars]
[Enum.map(args, &extract_vars(&1, []))|vars]
end
defp extract_vars(_, vars) do
+152 -28
View File
@@ -80,12 +80,12 @@ defmodule Kernel.SpecialForms do
<<102, rest :: binary>>
The type can be integer, float, binary, bytes, bitstring,
bits, utf8, utf16 or utf32, e.g.:
The type can be integer, float, bitstring/bits, binary/bytes,
utf8, utf16 or utf32, e.g.:
<<102 :: float, rest :: binary>>
Integer can be any arbitrary precision integer. A float is an
An integer can be any arbitrary precision integer. A float is an
IEEE 754 binary32 or binary64 floating point number. A bitstring
is an arbitrary series of bits. A binary is a special case of
bitstring that has a total size divisible by 8.
@@ -179,7 +179,120 @@ defmodule Kernel.SpecialForms do
defmacro :<<>>.(args)
@doc """
`alias` is used to setup atom aliases, often useful with modules names.
Defines a remote call or an alias.
The dot (`.`) in Elixir can be used for remote calls:
iex> String.downcase("FOO")
"foo"
In this example above, we have used `.` to invoke `downcase` in the
`String` alias, passing "FOO" as argument. We can also use the dot
for creating aliases:
iex> Hello.World
Hello.World
This time, we have joined two aliases, defining the final alias
`Hello.World`.
## Syntax
The right side of `.` may be a word starting in upcase, which represents
an alias, a word starting with lowercase or underscore, any valid language
operator or any name wrapped in single- or double-quotes. Those are all valid
examples:
iex> Kernel.Sample
Kernel.Sample
iex> Kernel.length([1,2,3])
3
iex> Kernel.+(1, 2)
3
iex> Kernel."length"([1,2,3])
3
iex> Kernel.'+'(1, 2)
3
Note that `Kernel."HELLO"` will be treated as a remote call and not an alias.
This choice was done so every time single- or double-quotes are used, we have
a remote call irregardless of the quote contents. This decision is also reflected
in the quoted expressions discussed below.
## Runtime (dynamic) behaviour
The result returned by `.` is always specified by the right-side:
iex> x = String
iex> x.downcase("FOO")
"foo"
iex> x.Sample
String.Sample
In case the right-side is also dynamic, `.`'s behaviour can be reproduced
at runtime via `apply/3` and `Module.concat/2`:
iex> apply(Kernel, :+, [1,2])
3
iex> Module.concat(Kernel, Sample)
Kernel.Sample
## Quoted expression
When `.` is used, the quoted expression may take two distinct
forms. When the right side starts with a lowercase letter (or
underscore):
iex> quote do: String.downcase("FOO")
{{:., [], [{:__aliases__, [alias: false], [:String]}, :downcase]}, [], ["FOO"]}
Notice we have an inner tuple, containing the atom `:.` representing
the dot as first element:
{:., [], [{:__aliases__, [alias: false], [:String]}, :downcase]}
This tuple follows the general quoted expression structure in Elixir,
with the name as first argument, some keyword list as metadata as second,
and the number of arguments as third. In this case, the arguments is the
alias `String` and the atom `:downcase`. The second argument is **always**
an atom:
iex> quote do: String."downcase"("FOO")
{{:., [], [{:__aliases__, [alias: false], [:String]}, :downcase]}, [], ["FOO"]}
The tuple containing `:.` is wrapped in another tuple, which actually
represents the function call, and has `"FOO"` as argument.
When the right side is an alias (i.e. starts with uppercase), we get instead:
iex> quote do: Hello.World
{:__aliases__, [alias: false], [:Hello, :World]}
We got into more details about aliases in the `__aliases__` special form
documentation.
## Unquoting
We can also use unquote to generate a remote call in a quoted expression:
iex> x = :downcase
iex> quote do: String.unquote(x)("FOO")
{{:., [], [{:__aliases__, [alias: false], [:String]}, :downcase]}, [], ["FOO"]}
Similar to `Kernel."HELLO"`, `unquote(x)` will always generate a remote call,
independent of the value of `x`. To generate an alias via the quoted expression,
one needs to rely on `Module.concat/2`:
iex> x = Sample
iex> quote do: Module.concat(String, unquote(x))
{{:., [], [{:__aliases__, [alias: false], [:Module]}, :concat]}, [],
[{:__aliases__, [alias: false], [:String]}, Sample]}
"""
defmacro (:.).(left, right)
@doc """
`alias` is used to setup aliases, often useful with modules names.
## Examples
@@ -217,8 +330,7 @@ defmodule Kernel.SpecialForms do
defmacro alias(module, opts)
@doc """
`require` is used to require the presence of external
modules so macros can be invoked.
Requires a given module to be compiled and loaded.
## Examples
@@ -246,6 +358,8 @@ defmodule Kernel.SpecialForms do
defmacro require(module, opts)
@doc """
Imports function and macros from other modules.
`import` allows one to easily access functions or macros from
others modules without using the qualified name.
@@ -335,14 +449,16 @@ defmodule Kernel.SpecialForms do
defmacro import(module, opts)
@doc """
Returns the current environment information as a `Macro.Env`
record. In the environment you can access the current filename,
Returns the current environment information as a `Macro.Env[]` record.
In the environment you can access the current filename,
line numbers, set up aliases, the current function and others.
"""
defmacro __ENV__
@doc """
Returns the current module name as an atom or `nil` otherwise.
Although the module can be accessed in the `__ENV__`, this macro
is a convenient shortcut.
"""
@@ -350,6 +466,7 @@ defmodule Kernel.SpecialForms do
@doc """
Returns the current file name as a binary.
Although the file can be accessed in the `__ENV__`, this macro
is a convenient shortcut.
"""
@@ -395,8 +512,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 resolution context;
* `:binding` - Passes a binding to the macro. Whenever a binding is given,
`unquote` is automatically disabled;
* `:bind_quoted` - Passes a binding to the macro. Whenever a binding is given,
`unquote` is automatically disabled;
## Macro literals
@@ -709,8 +826,8 @@ defmodule Kernel.SpecialForms do
end
end
In fact, the `:binding` option is recommended every time one
desires to inject a value into the quote.
In fact, the `:bind_quoted` option is recommended every time
one desires to inject a value into the quote.
"""
defmacro quote(opts, block)
@@ -820,6 +937,9 @@ defmodule Kernel.SpecialForms do
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}]
Variable assignments in the `do` block of the comprehension
are not reflected outside of the block.
Note: Unlike Erlang, Elixir comprehension filters
never behave as guards when it comes to errors. Errors in
list comprehensions will always be raised. Consider this
@@ -837,10 +957,11 @@ defmodule Kernel.SpecialForms do
defmacro lc(args)
@doc """
Defines a bit comprehension. It follows the same syntax as
a list comprehension but expects each element returned to
be a bitstring. For example, here is how to remove all
spaces from a string:
Defines a bit comprehension.
It follows the same syntax and behaviour as a list comprehension
but expects each element returned to be a bitstring. For example,
here is how to remove all spaces from a string:
iex> bc <<c>> inbits " hello world ", c != ? , do: <<c>>
"helloworld"
@@ -849,6 +970,8 @@ defmodule Kernel.SpecialForms do
defmacro bc(args)
@doc """
Internal special form for block expressions.
This is the special form used whenever we have a block
of expressions in Elixir. This special form is private
and should not be invoked directly:
@@ -860,7 +983,7 @@ defmodule Kernel.SpecialForms do
defmacro __block__(args)
@doc """
Captures a call as an anonymous function.
Captures an anonymous function.
The most common format to capture a function is via module,
name and arity:
@@ -938,30 +1061,31 @@ defmodule Kernel.SpecialForms do
defmacro unquote(name)(expr)
@doc """
This is the special form used whenever we have to temporarily
change the scope information of a block. Used when `quote` is
invoked with `location: :keep` to execute a given block as if
it belonged to another file.
Internal special form for modifying scope information.
Used when `quote` is invoked with `location: :keep` to execute
a given block as if it belonged to another file.
quote location: :keep, do: 1
#=> { :__scope__, [line: 1], [[file: "iex"],[do: 1]] }
Check `quote/1` for more information.
Check `quote/2` for more information.
"""
defmacro __scope__(opts, args)
@doc """
This is the special form used to hold aliases information.
Internal special form to hold aliases information.
It is usually compiled to an atom:
quote do: Foo.Bar #=>
{ :__aliases__, [], [:Foo,:Bar] }
iex> quote do: Foo.Bar
{:__aliases__, [alias: false], [:Foo, :Bar]}
Elixir represents `Foo.Bar` as `__aliases__` so calls can be
unambiguously identified by the operator `:.`. For example:
quote do: Foo.bar #=>
{{:.,[],[{:__aliases__,[],[:Foo]},:bar]},[],[]}
iex> quote do: Foo.bar
{{:., [], [{:__aliases__, [alias: false], [:Foo]}, :bar]}, [], []}
Whenever an expression iterator sees a `:.` as the tuple key,
it can be sure that it represents a call and the second argument
@@ -978,7 +1102,7 @@ defmodule Kernel.SpecialForms do
4) When the head element of aliases is not an atom, it is expanded at runtime:
quote do: some_var.Foo
{:__aliases__,[],[{:some_var,[],:quoted},:Bar]}
{:__aliases__, [], [{:some_var, [], Elixir}, :Foo]}
Since `some_var` is not available at compilation time, the compiler
expands such expression to:
+51 -7
View File
@@ -159,7 +159,7 @@ defmodule Kernel.Typespec do
@doc """
Defines a `type`, `typep` or `opaque` by receiving Erlang's typespec.
"""
def define_type(module, kind, { name, _, vars } = type) when kind in [:type, :typep, :opaque] do
def define_type(caller, kind, { name, _, vars } = type) when kind in [:type, :typep, :opaque] do
{ kind, export } =
case kind do
:type -> { :type, true }
@@ -167,12 +167,35 @@ defmodule Kernel.Typespec do
:opaque -> { :opaque, true }
end
module = caller.module
arity = length(vars)
Module.compile_typespec module, kind, type
if export, do:
Module.compile_typespec(module, :export_type, [{ name, length(vars) }])
if export do
Module.compile_typespec(module, :export_type, [{ name, arity }])
end
define_doc(caller, kind, name, arity, export)
type
end
defp define_doc(caller, kind, name, arity, export) do
module = caller.module
doc = Module.get_attribute(module, :typedoc)
if doc do
if export do
Module.add_doc(module, caller.line, kind, { name, arity }, doc)
else
:elixir_errors.warn "#{caller.file}:#{caller.line}: type #{name}/#{arity} is private, " <>
"@typedoc's are always discarded for private types\n"
end
end
Module.delete_attribute(module, :typedoc)
end
@doc """
Defines a `spec` by receiving Erlang's typespec.
"""
@@ -193,7 +216,7 @@ defmodule Kernel.Typespec do
for modules being compiled.
"""
def defines_type?(module, name, arity) do
finder = match?({ ^name, _, vars } when length(vars) == arity, &1)
finder = &match?({ ^name, _, vars } when length(vars) == arity, &1)
:lists.any(finder, Module.get_attribute(module, :type)) or
:lists.any(finder, Module.get_attribute(module, :opaque))
end
@@ -204,7 +227,7 @@ defmodule Kernel.Typespec do
"""
def defines_spec?(module, name, arity) do
tuple = { name, arity }
:lists.any(match?(^tuple, &1), Module.get_attribute(module, :spec))
:lists.any(&match?(^tuple, &1), Module.get_attribute(module, :spec))
end
@doc """
@@ -213,7 +236,7 @@ defmodule Kernel.Typespec do
"""
def defines_callback?(module, name, arity) do
tuple = { name, arity }
:lists.any(match?(^tuple, &1), Module.get_attribute(module, :callback))
:lists.any(&match?(^tuple, &1), Module.get_attribute(module, :callback))
end
@doc """
@@ -255,6 +278,27 @@ defmodule Kernel.Typespec do
quote do: unquote(name)(unquote_splicing(args)) :: unquote(typespec_to_ast(type))
end
@doc """
Returns all type docs available from the module's beam code.
It is returned as a list of tuples where the first element is the pair of type
name and arity and the second element is the documentation.
The module has to have a corresponding beam file on the disk which can be
located by the runtime system.
"""
# This is in Kernel.Typespec because it works very similar to beam_types and
# uses some of the introspection available here.
def beam_typedocs(module) do
case abstract_code(module) do
{ :ok, abstract_code } ->
type_docs = lc { :attribute, _, :typedoc, tup } inlist abstract_code, do: tup
List.flatten(type_docs)
_ ->
[]
end
end
@doc """
Returns all types available from the module's beam code.
@@ -368,7 +412,7 @@ defmodule Kernel.Typespec do
vars = lc { :var, _, _ } = var inlist args, do: var
type = { name, spec, vars }
define_type(caller.module, kind, type)
define_type(caller, kind, type)
end
@doc false
+4 -10
View File
@@ -356,22 +356,16 @@ defmodule Keyword do
:lists.keymember(key, 1, keywords)
end
@doc false
def update(dict, key, fun) when is_function(fun, 1) do
IO.write "Keyword.update/3 is deprecated, please use Keyword.update!/3 instead\n#{Exception.format_stacktrace}"
update!(dict, key, fun)
end
@doc """
Updates the `key` with the given function. If the `key` does
not exist, raises `KeyError`.
## Examples
iex> Keyword.update!([a: 1], :a, &1 * 2)
iex> Keyword.update!([a: 1], :a, &(&1 * 2))
[a: 2]
iex> Keyword.update!([a: 1], :b, &1 * 2)
iex> Keyword.update!([a: 1], :b, &(&1 * 2))
** (KeyError) key not found: :b
"""
@@ -394,10 +388,10 @@ defmodule Keyword do
## Examples
iex> Keyword.update([a: 1], :a, 13, &1 * 2)
iex> Keyword.update([a: 1], :a, 13, &(&1 * 2))
[a: 2]
iex> Keyword.update([a: 1], :b, 11, &1 * 2)
iex> Keyword.update([a: 1], :b, 11, &(&1 * 2))
[a: 1, b: 11]
"""
+1 -13
View File
@@ -12,18 +12,6 @@ defmodule List do
@compile :inline_list_funcs
@doc false
def concat(list) when is_list(list) do
IO.write "List.concat/1 is deprecated, please use Enum.concat/1 instead\n#{Exception.format_stacktrace}"
:lists.append(list)
end
@doc false
def concat(list, elements) when is_list(list) and is_list(elements) do
IO.write "List.concat/2 is deprecated, please use Enum.concat/2 instead\n#{Exception.format_stacktrace}"
list ++ elements
end
@doc """
Deletes the given item from the list. Returns a list without the item.
If the item occurs more than once in the list, just the first occurrence
@@ -307,7 +295,7 @@ defmodule List do
"""
def unzip(list) when is_list(list) do
:lists.map tuple_to_list(&1), zip(list)
:lists.map &tuple_to_list/1, zip(list)
end
@doc """
-6
View File
@@ -111,12 +111,6 @@ defmodule ListDict do
lc { k, _ } = tuple inlist dict, not :lists.member(k, keys), do: tuple
end
@doc false
def update(dict, key, fun) when is_function(fun, 1) do
IO.write "ListDict.update/3 is deprecated, please use ListDict.update!/3 instead\n#{Exception.format_stacktrace}"
update!(dict, key, fun)
end
def update!([{key, value}|dict], key, fun) do
[{key, fun.(value)}|delete(dict, key)]
end
+7 -18
View File
@@ -5,6 +5,7 @@ defmodule Macro do
This module provides conveniences for working with macros.
"""
@typedoc "Abstract Syntax Tree (AST) node"
@type t :: { t, t } | { t, Keyword.t, t } | atom | number | binary | list
@doc false
@@ -95,18 +96,6 @@ defmodule Macro do
:elixir_quote.escape(expr, Keyword.get(opts, :unquote, false)) |> elem(0)
end
@doc false
def unescape_binary(chars) do
IO.write "Macro.unescape_binary/1 is deprecated, please use Macro.unescape_string/1 instead\n#{Exception.format_stacktrace}"
unescape_string(chars)
end
@doc false
def unescape_binary(chars, map) do
IO.write "Macro.unescape_binary/2 is deprecated, please use Macro.unescape_string/2 instead\n#{Exception.format_stacktrace}"
unescape_string(chars, map)
end
@doc %S"""
Unescape the given chars. This is the unescaping behavior
used by default in Elixir single- and double-quoted strings.
@@ -174,7 +163,7 @@ defmodule Macro do
Using the unescape_map defined above is easy:
Macro.unescape_string "example\\n", unescape_map(&1)
Macro.unescape_string "example\\n", &unescape_map(&1)
"""
@spec unescape_string(String.t, (non_neg_integer -> non_neg_integer | false)) :: String.t
@@ -336,7 +325,7 @@ defmodule Macro do
defmacrop kw_keywords, do: [:do, :catch, :rescue, :after, :else]
defp is_kw_blocks?([_|_] = kw) do
Enum.all?(kw, match?({x, _} when x in kw_keywords, &1))
Enum.all?(kw, &match?({x, _} when x in kw_keywords, &1))
end
defp is_kw_blocks?(_), do: false
@@ -529,7 +518,7 @@ defmodule Macro do
aliases ->
aliases = lc alias inlist aliases, do: (expand_once(alias, env, cache) |> elem(0))
case :lists.all(is_atom(&1), aliases) do
case :lists.all(&is_atom/1, aliases) do
true ->
receiver = :elixir_aliases.concat(aliases)
:elixir_tracker.record_alias(receiver, env.module)
@@ -611,7 +600,7 @@ defmodule Macro do
defp to_erl_env(_env, cache), do: cache
defp is_partial?(args) do
:lists.any(match?({ :&, _, [_] }, &1), args)
:lists.any(&match?({ :&, _, [_] }, &1), args)
end
@doc """
@@ -686,7 +675,7 @@ defmodule Macro do
end
defp expand_all_until({ list, cache }, env) when is_list(list) do
:lists.mapfoldl(expand_all(&1, env, &2), cache, list)
:lists.mapfoldl(&expand_all(&1, env, &2), cache, list)
end
defp expand_all_until({ other, cache }, _env) do
@@ -712,7 +701,7 @@ defmodule Macro do
end
defp do_safe_term({ left, right }), do: do_safe_term(left) || do_safe_term(right)
defp do_safe_term(terms) when is_list(terms), do: Enum.find_value(terms, do_safe_term(&1))
defp do_safe_term(terms) when is_list(terms), do: Enum.find_value(terms, &do_safe_term(&1))
defp do_safe_term(terms) when is_tuple(terms), do: { :unsafe, terms }
defp do_safe_term(_), do: nil
end
+8 -5
View File
@@ -477,10 +477,11 @@ defmodule Module do
end
@doc """
Attaches documentation to a given function. It expects
the module the function belongs to, the line (a non negative
Attaches documentation to a given function or type. It expects
the module the function/type belongs to, the line (a non negative
integer), the kind (def or defmacro), a tuple representing
the function and its arity and the documentation, which should
the function and its arity, the function signature (the signature
should be omitted for types) and the documentation, which should
be either a binary or a boolean.
## Examples
@@ -491,12 +492,14 @@ defmodule Module do
end
"""
def add_doc(_module, _line, kind, _tuple, _signature, doc) when kind in [:defp, :defmacrop] do
def add_doc(module, line, kind, tuple, signature // [], doc)
def add_doc(_module, _line, kind, _tuple, _signature, doc) when kind in [:defp, :defmacrop, :typep] do
if doc, do: { :error, :private_doc }, else: :ok
end
def add_doc(module, line, kind, tuple, signature, doc) when
kind in [:def, :defmacro] and (is_binary(doc) or is_boolean(doc) or doc == nil) do
kind in [:def, :defmacro, :type, :opaque] and (is_binary(doc) or is_boolean(doc) or doc == nil) do
assert_not_compiled!(:add_doc, module)
table = docs_table_for(module)
+3 -3
View File
@@ -157,7 +157,7 @@ defmodule Module.DispatchTracker do
neighbours = (lc { _, _ } = t inlist neighbours, do: t) |> :ordsets.from_list
remaining = :ordsets.subtract(neighbours, vertices)
vertices = :ordsets.union(neighbours, vertices)
:lists.foldl(reduce_reachable(d, &1, &2), vertices, remaining)
:lists.foldl(&reduce_reachable(d, &1, &2), vertices, remaining)
end
defp to_pid(pid) when is_pid(pid), do: pid
@@ -249,7 +249,7 @@ defmodule Module.DispatchTracker do
end
defp has_imports?(d, mod) do
Enum.any?(:digraph.in_neighbours(d, mod), match?({ :import, _, _ }, &1))
Enum.any?(:digraph.in_neighbours(d, mod), &match?({ :import, _, _ }, &1))
end
# Yanks a local node. Returns its in and out vertices in a tuple.
@@ -282,7 +282,7 @@ defmodule Module.DispatchTracker do
@doc false
def collect_unused_locals(pid, private) do
reachable = reachable(pid)
:lists.foldl(collect_unused_locals(&1, &2, reachable), [], private)
:lists.foldl(&collect_unused_locals(&1, &2, reachable), [], private)
end
defp collect_unused_locals({ tuple, kind, 0 }, acc, reachable) do
+55 -24
View File
@@ -77,9 +77,6 @@ defmodule Record do
example, is only available inside the `User` module and nowhere else.
You can find more information in `Kernel.defrecordp/2` docs.
In general, though, records are used as part of a module's public
interface. For such use cases, Elixir provides record modules.
## defrecord
By using `defrecord`, a developer can make a Record definition
@@ -97,7 +94,7 @@ defmodule Record do
user() #=> User[name: "José", age: 25]
user(age: 26) #=> User[name: "José", age: 26]
Notice that now, since the record definition is accessible, Elixir
Notice that now since the record definition is accessible, Elixir
shows the record nicely formatted, no longer as a simple tuple. We
can get the raw formatting by passing `raw: true` to `inspect`:
@@ -112,9 +109,9 @@ defmodule Record do
User[] #=> User[name: "José", age: 25]
User[age: 26] #=> User[name: "José", age: 26]
The macro name is replaced by the module name and the parenthesis
are replaced by brackets. When the shortcut syntax is used, there
is no need to import the record.
The macro name is replaced by the module name and the parentheses
are replaced by brackets. When this syntax is used, there is no
need to import the record.
Before we sum up the differences between `defrecord` and
`defrecordp`, there is one last functionality introduced by
@@ -142,6 +139,12 @@ defmodule Record do
records flexibility at the cost of performance since
there is more work happening at runtime.
The above calls (new and update) can interchangeably accept both
atom and string keys for field names, however not both at the same time.
Please also note that atom keys are faster. This feature allows to
"sanitize" untrusted dictionaries and initialize/update records without
using `binary_to_existing_atom/1`.
To sum up, `defrecordp` should be used when you don't want
to expose the record information while `defrecord` should be used
whenever you want to share a record within your code or with other
@@ -175,7 +178,7 @@ defmodule Record do
to_string WeekDate[year: 2013, week: 26, week_day: 4]
"2013-W26-4"
A protocol can be implemented for any record defined via `defrecord`.
A protocol can be implemented for any record.
"""
@type t :: tuple
@@ -627,9 +630,8 @@ defmodule Record do
# an ordered dict of options (opts) and it will try to fetch
# the given key from the ordered dict, falling back to the
# default value if one does not exist.
selective = lc { k, v } inlist values do
quote do: Keyword.get(opts, unquote(k), unquote(v))
end
atom_selective = lc { k, v } inlist values, do: initialize_lookup(k, v)
string_selective = lc { k, v } inlist values, do: initialize_lookup(atom_to_binary(k), v)
quote do
@doc false
@@ -637,7 +639,17 @@ defmodule Record do
@doc false
def new([]), do: { __MODULE__, unquote_splicing(defaults) }
def new(opts) when is_list(opts), do: { __MODULE__, unquote_splicing(selective) }
def new([{key, _}|_] = opts) when is_atom(key), do: { __MODULE__, unquote_splicing(atom_selective) }
def new([{key, _}|_] = opts) when is_binary(key), do: { __MODULE__, unquote_splicing(string_selective) }
end
end
defp initialize_lookup(k, v) do
quote do
case :lists.keyfind(unquote(k), 1, opts) do
false -> unquote(v)
{_, v} -> v
end
end
end
@@ -728,15 +740,14 @@ defmodule Record do
# Define an updater method that receives a
# keyword list and updates the record.
defp updater(values) do
fields =
lc {key, _default} inlist values do
index = find_index(values, key, 1)
quote do
Keyword.get(keywords, unquote(key), elem(record, unquote(index)))
end
end
atom_fields =
lc {key, _default} inlist values, do: updater_lookup(key, key, values)
contents = quote do: { __MODULE__, unquote_splicing(fields) }
string_fields =
lc {key, _default} inlist values, do: updater_lookup(atom_to_binary(key), key, values)
atom_contents = quote do: { __MODULE__, unquote_splicing(atom_fields) }
string_contents = quote do: { __MODULE__, unquote_splicing(string_fields) }
quote do
@doc false
@@ -744,8 +755,22 @@ defmodule Record do
record
end
def update(keywords, record) do
unquote(contents)
def update([{key, _}|_] = keywords, record) when is_atom(key) do
unquote(atom_contents)
end
def update([{key, _}|_] = keywords, record) when is_binary(key) do
unquote(string_contents)
end
end
end
defp updater_lookup(k, key, values) do
v = find_index(values, key, 1)
quote do
case :lists.keyfind(unquote(k), 1, keywords) do
false -> elem(record, unquote(v))
{_, value} -> value
end
end
end
@@ -768,6 +793,7 @@ defmodule Record do
defp core_specs(values) do
types = lc { _, _, spec } inlist values, do: spec
options = if values == [], do: [], else: [options_specs(values)]
values_specs = if values == [], do: [], else: values_specs(values)
quote do
unless Kernel.Typespec.defines_type?(__MODULE__, :t, 0) do
@@ -775,7 +801,7 @@ defmodule Record do
end
unless Kernel.Typespec.defines_type?(__MODULE__, :options, 0) do
@type options :: unquote(options)
@type options :: unquote(options) | [{String.t, unquote(values_specs)}]
end
@spec new :: t
@@ -793,6 +819,11 @@ defmodule Record do
{ :|, [], [{ k, v }, acc] }
end, { k, v }, t
end
defp values_specs([{ _, _, v }|t]) do
:lists.foldl fn { _, _, v }, acc ->
{ :|, [], [v, acc] }
end, v, t
end
defp accessor_specs([{ :__exception__, _, _ }|t], 1, acc) do
accessor_specs(t, 2, acc)
@@ -814,7 +845,7 @@ defmodule Record do
## Helpers
defp is_keyword(list) when is_list(list), do: :lists.all(is_keyword_tuple(&1), list)
defp is_keyword(list) when is_list(list), do: :lists.all(&is_keyword_tuple/1, list)
defp is_keyword(_), do: false
defp is_keyword_tuple({ x, _ }) when is_atom(x), do: true
+32 -13
View File
@@ -1,10 +1,10 @@
defmodule Regex do
@moduledoc %S"""
Regular expressions for Elixir built on top of the re module
Regular expressions for Elixir built on top of the `re` module
in the Erlang Standard Library. More information can be found
in the re documentation: http://www.erlang.org/doc/man/re.html
in the [`re` documentation](http://www.erlang.org/doc/man/re.html).
Regular expressions in Elixir can be created using Regex.compile!
Regular expressions in Elixir can be created using `Regex.compile!`
or using the special form with `%r`:
# A simple regular expressions that matches foo anywhere in the string
@@ -13,10 +13,10 @@ defmodule Regex do
# A regular expression with case insensitive options and handling for unicode chars
%r/foo/iu
The re module provides several options, the ones available in Elixir, followed by
The `re` module provides several options, the ones available in Elixir, followed by
their shortcut in parenthesis, are:
* `unicode` (u) - enable unicode specific patterns like \p
* `unicode` (u) - enables unicode specific patterns like \p
* `caseless` (i) - add case insensitivity
* `dotall` (s) - causes dot to match newlines and also set newline to anycrlf.
The new line setting can be overridden by setting `(*CR)` or `(*LF)` or
@@ -27,8 +27,8 @@ defmodule Regex do
allow `#` to delimit comments
* `firstline` (f) - forces the unanchored pattern to match before or at the first
newline, though the matched text may continue over the newline
* `ungreedy` (r) - invert the "greediness" of the regexp
* `groups` (g) - compile with info about groups available
* `ungreedy` (r) - inverts the "greediness" of the regexp
* `groups` (g) - compiles with info about groups available
The options not available are:
@@ -53,10 +53,18 @@ defmodule Regex do
The given options can either be a binary with the characters
representing the same regex options given to the `%r` sigil,
or a list of options, as expected by the Erlang `re` docs.
or a list of options, as expected by the [Erlang `re` docs](http://www.erlang.org/doc/man/re.html).
It returns `{ :ok, regex }` in case of success,
`{ :error, reason }` otherwise.
## Examples
iex> Regex.compile("foo")
{:ok, %r"foo"}
iex> Regex.compile("*foo")
{:error, {'nothing to repeat', 0}}
"""
@spec compile(binary, binary | [term]) :: t
def compile(source, options // "")
@@ -148,21 +156,27 @@ defmodule Regex do
end
end
@doc false
def captures(regex, string, options // []) do
IO.write "Regex.captures/3 is deprecated, please use Regex.named_captures/3\n#{Exception.format_stacktrace}"
named_captures(regex, string, options)
end
@doc """
Returns the given captures as a keyword list or `nil` if no captures
are found. Requires the regex to be compiled with the groups option.
## Examples
iex> Regex.captures(%r/c(?<foo>d)/g, "abcd")
iex> Regex.named_captures(%r/c(?<foo>d)/g, "abcd")
[foo: "d"]
iex> Regex.captures(%r/a(?<foo>b)c(?<bar>d)/g, "abcd")
iex> Regex.named_captures(%r/a(?<foo>b)c(?<bar>d)/g, "abcd")
[foo: "b", bar: "d"]
iex> Regex.captures(%r/a(?<foo>b)c(?<bar>d)/g, "efgh")
iex> Regex.named_captures(%r/a(?<foo>b)c(?<bar>d)/g, "efgh")
nil
"""
def captures(regex(groups: groups) = regex, string, options // []) do
def named_captures(regex(groups: groups) = regex, string, options // []) do
options = Keyword.put_new(options, :capture, :groups)
results = run(regex, string, options)
if results, do: Enum.zip(groups, results)
@@ -249,13 +263,14 @@ defmodule Regex do
options = [{ :capture, captures, return }, :global]
case :re.run(string, compiled, options) do
:match -> []
:nomatch -> []
{ :match, results } -> results
end
end
@doc """
Split the given target into the number of parts specified.
Splits the given target into the number of parts specified.
If no number of parts is given, it defaults to `:infinity`.
## Examples
@@ -265,6 +280,10 @@ defmodule Regex do
["a","b-c"]
iex> Regex.split(%r/-/, "abc")
["abc"]
iex> Regex.split(%r//, "abc")
["a", "b", "c", ""]
iex> Regex.split(%r//, "abc", trim: true)
["a", "b", "c"]
"""
def split(regex, string, options // [])
+42 -17
View File
@@ -8,7 +8,7 @@ defmodule Stream do
iex> range = 1..5
1..5
iex> Enum.map range, &1 * 2
iex> Enum.map range, &(&1 * 2)
[2,4,6,8,10]
In the example above, as we mapped over the range, the elements being
@@ -16,8 +16,8 @@ defmodule Stream do
module allows us to map the range, without triggering its enumeration:
iex> range = 1..3
iex> stream = Stream.map(range, &1 * 2)
iex> Enum.map(stream, &1 + 1)
iex> stream = Stream.map(range, &(&1 * 2))
iex> Enum.map(stream, &(&1 + 1))
[3,5,7]
Notice we started with a range and then we created a stream that is
@@ -34,9 +34,9 @@ defmodule Stream do
example:
1..3 |>
Enum.map(IO.inspect(&1)) |>
Enum.map(&1 * 2) |>
Enum.map(IO.inspect(&1))
Enum.map(&IO.inspect(&1)) |>
Enum.map(&(&1 * 2)) |>
Enum.map(&IO.inspect(&1))
1
2
3
@@ -50,9 +50,9 @@ defmodule Stream do
was iterated three times. Let's see an example with streams:
stream = 1..3 |>
Stream.map(IO.inspect(&1)) |>
Stream.map(&1 * 2) |>
Stream.map(IO.inspect(&1))
Stream.map(&IO.inspect(&1)) |>
Stream.map(&(&1 * 2)) |>
Stream.map(&IO.inspect(&1))
Enum.to_list(stream)
1
2
@@ -81,8 +81,8 @@ defmodule Stream do
This module also allows us to create streams from any enumerable:
iex> stream = Stream.map([1,2,3], &1 * 2)
iex> Enum.map(stream, &1 + 1)
iex> stream = Stream.map([1,2,3], &(&1 * 2))
iex> Enum.map(stream, &(&1 + 1))
[3,5,7]
By simply passing a list (which is an enumerable) as the first argument
@@ -187,7 +187,7 @@ defmodule Stream do
"""
@spec cycle(Enumerable.t) :: t
def cycle(enumerable) do
do_cycle(enumerable, &1, &2)
&do_cycle(enumerable, &1, &2)
end
defp do_cycle(enumerable, acc, fun) do
@@ -225,7 +225,7 @@ defmodule Stream do
## Examples
iex> stream = Stream.drop_while(1..10, &1 <= 5)
iex> stream = Stream.drop_while(1..10, &(&1 <= 5))
iex> Enum.to_list(stream)
[6,7,8,9,10]
@@ -271,7 +271,7 @@ defmodule Stream do
## Examples
iex> Stream.iterate(0, &1+1) |> Enum.take(5)
iex> Stream.iterate(0, &(&1+1)) |> Enum.take(5)
[0,1,2,3,4]
"""
@@ -365,7 +365,7 @@ defmodule Stream do
@spec repeatedly((() -> element)) :: t
def repeatedly(generator_fun)
when is_function(generator_fun, 0) do
do_repeatedly(generator_fun, &1, &2)
&do_repeatedly(generator_fun, &1, &2)
end
defp do_repeatedly(generator_fun, acc, fun) do
@@ -388,7 +388,7 @@ defmodule Stream do
"""
@spec take(Enumerable.t, non_neg_integer) :: t
def take(_enumerable, 0), do: Lazy[enumerable: [], fun: & &1]
def take(_enumerable, 0), do: Lazy[enumerable: [], fun: &(&1)]
def take(enumerable, n) when n > 0 do
Lazy[enumerable: enumerable,
@@ -407,7 +407,7 @@ defmodule Stream do
## Examples
iex> stream = Stream.take_while(1..100, &1 <= 5)
iex> stream = Stream.take_while(1..100, &(&1 <= 5))
iex> Enum.to_list(stream)
[1,2,3,4,5]
@@ -427,6 +427,31 @@ defmodule Stream do
acc: true]
end
@doc """
Emit a sequence of values and accumulators. Successive values are generated by
calling `next_fun` with the previous accumulator.
If the return value is nil iteration ends.
## Examples
iex> Stream.unfold(5, fn 0 -> nil; n -> {n, n-1} end) |> Enum.to_list()
[5, 4, 3, 2, 1]
"""
@spec unfold(acc, (acc -> { element, acc } | nil)) :: t
def unfold(acc, f) do
fn acc1, f1 ->
do_unfold(acc, f, acc1, f1)
end
end
defp do_unfold(gen_acc, gen_fun, acc, fun) do
case gen_fun.(gen_acc) do
nil -> acc
{ v, new_gen_acc } -> do_unfold(new_gen_acc, gen_fun, fun.(v, acc), fun)
end
end
@doc """
Creates a stream where each item in the enumerable will
be accompanied by its index.
+32 -26
View File
@@ -155,7 +155,7 @@ defmodule String do
@spec split(t) :: [t]
defdelegate split(binary), to: String.Unicode
@doc """
@doc %S"""
Divides a string into substrings based on a pattern,
returning a list of these substrings. The pattern can
be a string, a list of strings or a regular expression.
@@ -168,6 +168,8 @@ defmodule String do
## Examples
Splitting with a string pattern:
iex> String.split("a,b,c", ",")
["a", "b", "c"]
iex> String.split("a,b,c", ",", global: false)
@@ -175,15 +177,30 @@ defmodule String do
iex> String.split(" a b c ", " ", trim: true)
["a", "b", "c"]
A list of patterns:
iex> String.split("1,2 3,4", [" ", ","])
["1", "2", "3", "4"]
A regular expression:
iex> String.split("a,b,c", %r{,})
["a", "b", "c"]
iex> String.split("a,b,c", %r{,}, global: false)
["a", "b,c"]
iex> String.split("a,b", %r{\\.})
["a,b"]
iex> String.split(" a b c ", %r{\s}, trim: true)
["a", "b", "c"]
Splitting on empty patterns returns codepoints:
iex> String.split("abc", %r{})
["a", "b", "c", ""]
iex> String.split("abc", "")
["a", "b", "c", ""]
iex> String.split("abc", "", trim: true)
["a", "b", "c"]
iex> String.split("abc", "", global: false)
["a", "bc"]
"""
@spec split(t, t | [t] | Regex.t) :: [t]
@@ -192,6 +209,8 @@ defmodule String do
def split("", _pattern, _options), do: [""]
def split(binary, "", options), do: split(binary, %r""u, options)
def split(binary, pattern, options) when is_regex(pattern) do
Regex.split(pattern, binary, options)
end
@@ -642,27 +661,14 @@ defmodule String do
def valid_character?(<<_ :: utf8>> = codepoint), do: valid?(codepoint)
def valid_character?(_), do: false
@doc %S"""
Checks whether `str` is a valid codepoint.
@doc false
def valid_codepoint?(binary) do
IO.write "String.valid_codepoint?/1 is deprecated, please use match against << _ :: utf8 >> instead\n#{Exception.format_stacktrace}"
do_valid_codepoint?(binary)
end
Note that the empty string is considered invalid, as are
strings containing multiple codepoints.
## Examples
iex> String.valid_codepoint?("a")
true
iex> String.valid_codepoint?("ø")
true
iex> String.valid_codepoint?(<<0xffff :: 16>>)
false
iex> String.valid_codepoint?("asdf")
false
"""
@spec valid_codepoint?(codepoint) :: boolean
def valid_codepoint?(<<_ :: utf8>>), do: true
def valid_codepoint?(_), do: false
defp do_valid_codepoint?(<<_ :: utf8>>), do: true
defp do_valid_codepoint?(_), do: false
@doc """
Returns unicode graphemes in the string.
@@ -947,7 +953,7 @@ defmodule String do
@spec starts_with?(t, t | [t]) :: boolean
def starts_with?(string, prefixes) when is_list(prefixes) do
Enum.any?(prefixes, do_starts_with(string, &1))
Enum.any?(prefixes, &do_starts_with(string, &1))
end
def starts_with?(string, prefix) do
@@ -983,7 +989,7 @@ defmodule String do
@spec ends_with?(t, t | [t]) :: boolean
def ends_with?(string, suffixes) when is_list(suffixes) do
Enum.any?(suffixes, do_ends_with(string, &1))
Enum.any?(suffixes, &do_ends_with(string, &1))
end
def ends_with?(string, suffix) do
@@ -1022,7 +1028,7 @@ defmodule String do
@spec contains?(t, t | [t]) :: boolean
def contains?(string, matches) when is_list(matches) do
Enum.any?(matches, do_contains(string, &1))
Enum.any?(matches, &do_contains(string, &1))
end
def contains?(string, match) do
+1 -1
View File
@@ -54,7 +54,7 @@ defmodule URI do
that implements the `String.Chars` protocol (i.e. can be converted
to a binary).
"""
def encode_query(l), do: Enum.map_join(l, "&", pair(&1))
def encode_query(l), do: Enum.map_join(l, "&", &pair/1)
@doc """
Given a query string of the form "key1=value1&key=value2...", produces an
@@ -1,13 +1,13 @@
defmodule Mix.Version do
defmodule Version do
@moduledoc %S"""
This module provides functions for parsing and matching
versions with requirements.
A version is a string or a `Mix.Version.Schema` generated
after parsing via `Mix.Version.parse/1`. A requirement is
A version is a string or a `Version.Schema` generated
after parsing via `Version.parse/1`. A requirement is
a string that follows a specific format.
`Mix.Version` parsing and requirements follows
`Version` parsing and requirements follows
[SemVer 2.0 schema](http://semver.org/) and you will get
the most of Mix' version system by following it. In order
to support integration with projects that may
@@ -54,8 +54,8 @@ defmodule Mix.Version do
"""
@type t :: String.t | Mix.Version.Schema.t
@type requirement :: String.t | Mix.Version.Requirement.t
@type t :: String.t | Version.Schema.t
@type requirement :: String.t | Version.Requirement.t
@type matchable :: { major :: String.t | non_neg_integer,
minor :: non_neg_integer | nil,
@@ -83,7 +83,7 @@ defmodule Mix.Version do
"""
@spec match?(t, requirement) :: boolean
def match?(version, requirement) when is_binary(requirement) do
case Mix.Version.Parser.parse_requirement(requirement) do
case Version.Parser.parse_requirement(requirement) do
{ :ok, req } ->
match?(version, req)
{ :error, reason } ->
@@ -109,20 +109,20 @@ defmodule Mix.Version do
"""
@spec valid?(String.t | Schema.t) :: boolean
def valid?(string) when is_binary(string) do
Mix.Version.Parser.valid_version?(string)
Version.Parser.valid_version?(string)
end
def valid?(Mix.Version.Schema[major: nil]), do: false
def valid?(Mix.Version.Schema[]), do: true
def valid?(Version.Schema[major: nil]), do: false
def valid?(Version.Schema[]), do: true
@doc """
Parse a version into a matchable value.
"""
@spec parse(String.t) :: { :ok, Schema.t } | { :error, term }
def parse(string) when is_binary(string) do
case Mix.Version.Parser.parse_version(string) do
case Version.Parser.parse_version(string) do
{ :ok, matchable } -> from_matchable(matchable).source(string).build(get_build(string))
{ :error, _ } -> Mix.Version.Schema[source: string]
{ :error, _ } -> Version.Schema[source: string]
end
end
@@ -139,17 +139,17 @@ defmodule Mix.Version do
@doc """
Get the matchable representation.
"""
@spec to_matchable(String.t | Schema.t) :: Mix.Version.matchable
@spec to_matchable(String.t | Schema.t) :: Version.matchable
def to_matchable(Schema[major: nil, source: source]) do
{ source, nil, nil, [] }
end
def to_matchable(Mix.Version.Schema[major: major, minor: minor, patch: patch, pre: nil]) do
def to_matchable(Version.Schema[major: major, minor: minor, patch: patch, pre: nil]) do
{ major, minor, patch, [] }
end
def to_matchable(Mix.Version.Schema[major: major, minor: minor, patch: patch, pre: pre]) do
{ major, minor, patch, Mix.Version.Parser.parse_pre(pre) }
def to_matchable(Version.Schema[major: major, minor: minor, patch: patch, pre: pre]) do
{ major, minor, patch, Version.Parser.parse_pre(pre) }
end
def to_matchable(string) do
@@ -157,11 +157,11 @@ defmodule Mix.Version do
end
@doc """
Convert a matchable to a `Mix.Version`.
Convert a matchable to a `Version`.
"""
@spec from_matchable(Mix.Version.matchable) :: Schema.t
@spec from_matchable(Version.matchable) :: Schema.t
def from_matchable({ source, nil, nil, nil }) when is_binary(source) do
Mix.Version.Schema[source: source]
Version.Schema[source: source]
end
def from_matchable({ major, minor, patch, pre }) do
@@ -184,7 +184,7 @@ defmodule Mix.Version do
end
end
Mix.Version.Schema[major: major, minor: minor, patch: patch, pre: pre, source: source]
Version.Schema[major: major, minor: minor, patch: patch, pre: pre, source: source]
end
defmodule Parser.DSL do
@@ -251,12 +251,12 @@ defmodule Mix.Version do
end
deflexer acc do
Enum.filter(Enum.reverse(acc), &1 != :' ')
Enum.filter(Enum.reverse(acc), &(&1 != :' '))
end
@version_regex %r/^(\d+)(?:\.(\d+)(?:\.(\d+))?)?(?:\-([^\s]+))?(?:\+[^\d]+)?$/
@spec parse_requirement(String.t) :: { :ok, Mix.Version.Requirement.t } | { :error, binary | atom }
@spec parse_requirement(String.t) :: { :ok, Version.Requirement.t } | { :error, binary | atom }
def parse_requirement(source) do
lexed = lexer(source, [])
@@ -280,7 +280,7 @@ defmodule Mix.Version do
defp nillify(""), do: nil
defp nillify(o), do: o
@spec parse_version(String.t) :: { :ok, Mix.Version.matchable } | { :error, :invalid_version }
@spec parse_version(String.t) :: { :ok, Version.matchable } | { :error, :invalid_version }
def parse_version(string) when is_binary(string) do
if valid_version?(string) do
destructure [_, major, minor, patch, pre], Regex.run(@version_regex, string)
@@ -364,7 +364,7 @@ defmodule Mix.Version do
end
defp approximate(version) do
Mix.Version.from_matchable(case Regex.run(@version_regex, version) do
Version.from_matchable(case Regex.run(@version_regex, version) do
[_, major] ->
{ binary_to_integer(major) + 1, 0, 0, [] }
@@ -387,19 +387,19 @@ defmodule Mix.Version do
end
defp to_condition([:'==', version | _]) do
version = Mix.Version.to_matchable(version)
version = Version.to_matchable(version)
{ :'==', :'$_', { :const, version } }
end
defp to_condition([:'!=', version | _]) do
version = Mix.Version.to_matchable(version)
version = Version.to_matchable(version)
{ :'/=', :'$_', { :const, version } }
end
defp to_condition([:'~>', version | _]) do
from = Mix.Version.parse(version)
from = Version.parse(version)
to = approximate(version)
{ :andalso, to_condition([:'>=', to_string(from)]),
@@ -407,7 +407,7 @@ defmodule Mix.Version do
end
defp to_condition([:'>', version | _]) do
{ major, minor, patch, pre } = Mix.Version.to_matchable(version)
{ major, minor, patch, pre } = Version.to_matchable(version)
{ :andalso, { :not, { :is_binary, :'$1' } },
{ :orelse, { :'>', {{ :'$1', :'$2', :'$3' }},
@@ -423,7 +423,7 @@ defmodule Mix.Version do
end
defp to_condition([:'>=', version | _]) do
matchable = Mix.Version.to_matchable(version)
matchable = Version.to_matchable(version)
{ :orelse, { :andalso, { :not, { :is_binary, :'$1' } },
{ :'==', :'$_', { :const, matchable } } },
@@ -431,7 +431,7 @@ defmodule Mix.Version do
end
defp to_condition([:'<', version | _]) do
{ major, minor, patch, pre } = Mix.Version.to_matchable(version)
{ major, minor, patch, pre } = Version.to_matchable(version)
{ :andalso, { :not, { :is_binary, :'$1' } },
{ :orelse, { :'<', {{ :'$1', :'$2', :'$3' }},
@@ -447,7 +447,7 @@ defmodule Mix.Version do
end
defp to_condition([:'<=', version | _]) do
matchable = Mix.Version.to_matchable(version)
matchable = Version.to_matchable(version)
{ :orelse, { :andalso, { :not, { :is_binary, :'$1' } },
{ :'==', :'$_', { :const, matchable } } },
@@ -468,26 +468,26 @@ defmodule Mix.Version do
end
end
defimpl String.Chars, for: Mix.Version.Schema do
def to_string(Mix.Version.Schema[source: source]) do
defimpl String.Chars, for: Version.Schema do
def to_string(Version.Schema[source: source]) do
source
end
end
defimpl Inspect, for: Mix.Version.Schema do
defimpl Inspect, for: Version.Schema do
def inspect(self, _opts) do
"#Mix.Version.Schema<" <> to_string(self) <> ">"
"#Version.Schema<" <> to_string(self) <> ">"
end
end
defimpl String.Chars, for: Mix.Version.Requirement do
defimpl String.Chars, for: Version.Requirement do
def to_string({ _, source, _ }) do
source
end
end
defimpl Inspect, for: Mix.Version.Requirement do
defimpl Inspect, for: Version.Requirement do
def inspect({ _, source, _ }, _opts) do
"#Mix.Version.Requirement<" <> source <> ">"
"#Version.Requirement<" <> source <> ">"
end
end
+2 -2
View File
@@ -50,9 +50,9 @@ defmodule String.Unicode do
seqs = Enum.map File.stream!(seqs_path), fn(line) ->
[ _name, codepoints ] = :binary.split(line, ";", [:global])
codepoints = :binary.split(codepoints, " ", [:global])
codepoints = Enum.map codepoints, Regex.replace(%r/\s+/, &1, "")
codepoints = Enum.map codepoints, &Regex.replace(%r/\s+/, &1, "")
codepoints = Enum.filter codepoints, fn(x) -> size(x) > 0 end
Enum.map codepoints, to_binary.(&1)
Enum.map codepoints, to_binary
end
# Downcase
+1
View File
@@ -225,6 +225,7 @@ core_main() ->
<<"lib/elixir/lib/inspect/algebra.ex">>,
<<"lib/elixir/lib/inspect.ex">>,
<<"lib/elixir/lib/range.ex">>,
<<"lib/elixir/lib/regex.ex">>,
<<"lib/elixir/lib/string.ex">>,
<<"lib/elixir/lib/string/chars.ex">>,
<<"lib/elixir/lib/io.ex">>,
+10 -23
View File
@@ -8,9 +8,7 @@
lookup_definition/2,
delete_definition/2,
wrap_definition/6,
wrap_definition/8,
store_definition/7,
store_definition/9,
store_each/8,
unwrap_stored_definitions/2,
format_error/1]).
@@ -72,25 +70,17 @@ delete_definition(Module, Tuple) ->
%% before evaluating the function body), we would see both definitions.
wrap_definition(Kind, Meta, Call, Expr, CheckClauses, S) ->
do_wrap_definition(Kind, Meta, CheckClauses, [
Call, Expr, elixir_scope:serialize(S)
Line = ?line(Meta),
?wrap_call(Line, ?MODULE, store_definition, [
{atom, Line, Kind},
{integer, Line, Line},
{atom, Line, CheckClauses},
{var, Line, '_@MODULE'},
Call,
Expr,
elixir_scope:serialize(S)
]).
wrap_definition(Kind, Meta, Name, Args, Guards, Expr, CheckClauses, S) ->
do_wrap_definition(Kind, Meta, CheckClauses, [
Name, Args, Guards, Expr, elixir_scope:serialize(S)
]).
do_wrap_definition(Kind, Meta, CheckClauses, Extra) ->
Line = ?line(Meta),
Invoke =
[{atom, Line, Kind},
{integer, Line, Line},
{atom, Line, CheckClauses},
{var, Line, '_@MODULE'}] ++ Extra,
?wrap_call(Line, ?MODULE, store_definition, Invoke).
% Invoked by the wrap definition with the function abstract tree.
% Each function is then added to the function table.
@@ -139,10 +129,7 @@ store_definition(Kind, Line, CheckClauses, Module, Name, Args, Guards, Body, #el
[store_each(false, Kind, File, Location, Table, CTable, 0,
default_function_for(Kind, Name, Default)) || Default <- Defaults],
{ Name, Arity };
store_definition(Kind, Line, CheckClauses, Module, Name, Args, Guards, Body, RawS) ->
store_definition(Kind, Line, CheckClauses, Module, Name, Args, Guards, Body, elixir_scope:deserialize(RawS)).
{ Name, Arity }.
%% @on_definition
+17 -13
View File
@@ -15,7 +15,7 @@ warn(Warning) ->
elixir_code_server:cast(register_warning),
io:put_chars(standard_error, Warning).
%% Handle inspecting for exceptions
%% Handle inspecting for exceptions modules
inspect(Atom) when is_atom(Atom) ->
'Elixir.Inspect.Atom':inspect(Atom).
@@ -169,6 +169,10 @@ handle_file_error(File, {Line,erl_lint,{unsafe_var,Var,{In,_Where}}}) ->
Message = io_lib:format("cannot define variable ~ts inside ~ts", [format_var(Var), Translated]),
raise(Line, File, 'Elixir.CompileError', iolist_to_binary(Message));
handle_file_error(File, {Line,erl_lint,{spec_fun_undefined,{M,F,A}}}) ->
Message = io_lib:format("spec for undefined function ~ts.~ts/~B", [inspect(M), F, A]),
raise(Line, File, 'Elixir.CompileError', iolist_to_binary(Message));
handle_file_error(File, {Line,Module,Desc}) ->
form_error(Line, File, Module, Desc).
@@ -178,18 +182,6 @@ assert_no_function_scope(_Meta, _Kind, #elixir_scope{function=nil}) -> [];
assert_no_function_scope(Meta, Kind, S) ->
syntax_error(Meta, S#elixir_scope.file, "cannot invoke ~ts inside a function", [Kind]).
assert_no_match_or_guard_scope(Meta, Kind, S) ->
assert_no_match_scope(Meta, Kind, S),
assert_no_guard_scope(Meta, Kind, S).
assert_no_match_scope(Meta, Kind, #elixir_scope{context=match} = S) ->
syntax_error(Meta, S#elixir_scope.file, "cannot invoke ~ts inside match clause", [Kind]);
assert_no_match_scope(_Meta, _Kind, _S) -> [].
assert_no_guard_scope(Meta, Kind, #elixir_scope{context=guard} = S) ->
syntax_error(Meta, S#elixir_scope.file, "cannot invoke ~ts inside guard", [Kind]);
assert_no_guard_scope(_Meta, _Kind, _S) -> [].
assert_module_scope(Meta, Kind, #elixir_scope{module=nil,file=File}) ->
syntax_error(Meta, File, "cannot invoke ~ts outside module", [Kind]);
assert_module_scope(_Meta, _Kind, #elixir_scope{module=Module}) -> Module.
@@ -198,6 +190,18 @@ assert_function_scope(Meta, Kind, #elixir_scope{function=nil,file=File}) ->
syntax_error(Meta, File, "cannot invoke ~ts outside function", [Kind]);
assert_function_scope(_Meta, _Kind, #elixir_scope{function=Function}) -> Function.
assert_no_match_or_guard_scope(Meta, Kind, S) ->
assert_no_match_scope(Meta, Kind, S),
assert_no_guard_scope(Meta, Kind, S).
assert_no_match_scope(Meta, _Kind, #elixir_scope{context=match} = S) ->
compile_error(Meta, S#elixir_scope.file, "invalid pattern in match clause", []);
assert_no_match_scope(_Meta, _Kind, _S) -> [].
assert_no_guard_scope(Meta, _Kind, #elixir_scope{context=guard} = S) ->
compile_error(Meta, S#elixir_scope.file, "invalid pattern in guard", []);
assert_no_guard_scope(_Meta, _Kind, _S) -> [].
%% Helpers
raise(Meta, File, Kind, Message) when is_list(Meta) ->
+3
View File
@@ -48,6 +48,9 @@ capture(Meta, { { '.', _, [_, Fun] }, _, Args } = Expr, S) when is_atom(Fun), is
capture(Meta, { { '.', _, [_] }, _, Args } = Expr, S) when is_list(Args) ->
do_capture(Meta, Expr, S, false);
capture(Meta, { '__block__', _, [Expr] }, S) ->
capture(Meta, Expr, S);
capture(Meta, { '__block__', _, _ } = Expr, S) ->
Message = "invalid args for &, block expressions are not allowed, got: ~ts",
syntax_error(Meta, S#elixir_scope.file, Message, ['Elixir.Macro':to_string(Expr)]);
+1 -10
View File
@@ -189,16 +189,6 @@ translate({Kind, Meta, [Call, Expr]}, S) when ?defs(Kind) ->
(not QC#elixir_quote.unquoted) andalso (not QE#elixir_quote.unquoted),
{ elixir_def:wrap_definition(Kind, Meta, TCall, TExpr, CheckClauses, SE), SE };
translate({Kind, Meta, [Name, Args, Guards, Expr]}, S) when ?defs(Kind) ->
elixir_errors:deprecation(Meta, S#elixir_scope.file, "~ts/4 is deprecated, please use ~ts/2 instead", [Kind, Kind]),
assert_module_scope(Meta, Kind, S),
assert_no_function_scope(Meta, Kind, S),
{ TName, SN } = translate_each(Name, S),
{ TArgs, SA } = translate_each(Args, SN),
{ TGuards, SG } = translate_each(Guards, SA),
{ TExpr, SE } = translate_each(Expr, SG),
{ elixir_def:wrap_definition(Kind, Meta, TName, TArgs, TGuards, TExpr, false, SE), SE };
%% Apply - Optimize apply by checking what doesn't need to be dispatched dynamically
translate({ apply, Meta, [Left, Right, Args] }, S) when is_list(Args) ->
@@ -319,6 +309,7 @@ expand_module(_Raw, Module, S) ->
elixir_aliases:concat([S#elixir_scope.module, Module]).
is_reserved_data(moduledoc) -> true;
is_reserved_data(typedoc) -> true;
is_reserved_data(doc) -> true;
is_reserved_data(_) -> false.
+52 -16
View File
@@ -23,7 +23,6 @@ eval_quoted(Module, Quoted, RawBinding, Opts) ->
scope_for_eval(Module, #elixir_scope{} = S) ->
S#elixir_scope{module=Module};
scope_for_eval(Module, Opts) ->
scope_for_eval(Module, elixir:scope_for_eval(Opts)).
@@ -69,8 +68,9 @@ compile(Line, Module, Block, Vars, #elixir_scope{context_modules=FileModules} =
{ Base, Export, Private, Def, Defmacro, Functions } = elixir_def:unwrap_stored_definitions(FileList, Module),
{ All, Forms0 } = functions_form(Line, File, Module, Base, Export, Def, Defmacro, Functions, C),
Forms1 = specs_form(Line, Module, Private, Defmacro, Forms0, C),
Forms1 = specs_form(Module, Private, Defmacro, Forms0, C),
Forms2 = attributes_form(Line, File, Module, Forms1),
Forms3 = typedocs_form(Module, Forms2),
case ets:lookup(data_table(Module), 'on_load') of
[] -> ok;
@@ -82,11 +82,12 @@ compile(Line, Module, Block, Vars, #elixir_scope{context_modules=FileModules} =
elixir_tracker:ensure_no_function_conflict(Line, File, Module, AllFunctions),
elixir_tracker:ensure_all_imports_used(Line, File, Module),
elixir_tracker:warn_unused_local(File, Module, Private),
warn_unused_docs(Line, File, Module, All),
warn_invalid_docs(Line, File, Module, All),
warn_unused_docs(Line, File, Module),
Final = [
{ attribute, Line, file, { FileList, Line } },
{ attribute, Line, module, Module } | Forms2
{ attribute, Line, module, Module } | Forms3
],
Binary = load_form(Line, Final, S),
@@ -198,9 +199,21 @@ attributes_form(Line, _File, Module, Current) ->
ets:foldl(Transform, Current, Table).
%% Add typedocs to the form
typedocs_form(Module, Current) ->
Table = docs_table(Module),
Transform = fun({ Tuple, Line, Kind, _Sig, Doc }, Acc) ->
case Kind of
type -> [{ attribute, Line, typedoc, { Tuple, Doc } } | Acc];
opaque -> [{ attribute, Line, typedoc, { Tuple, Doc } } | Acc];
_ -> Acc
end
end,
ets:foldl(Transform, Current, Table).
%% Specs
specs_form(Line, Module, Private, Defmacro, Forms, C) ->
specs_form(Module, Private, Defmacro, Forms, C) ->
Defmacrop = [Tuple || { Tuple, defmacrop, _, _, _ } <- Private],
case elixir_compiler:get_opt(internal, C) of
true -> Forms;
@@ -212,16 +225,17 @@ specs_form(Line, Module, Private, Defmacro, Forms, C) ->
'Elixir.Module':delete_attribute(Module, spec),
'Elixir.Module':delete_attribute(Module, callback),
Temp = specs_attributes(Line, spec, Forms, Specs),
specs_attributes(Line, callback, Temp, Callbacks)
Temp = specs_attributes(spec, Forms, Specs),
specs_attributes(callback, Temp, Callbacks)
end.
specs_attributes(Line, Type, Forms, Specs) ->
specs_attributes(Type, Forms, Specs) ->
Keys = lists:foldl(fun({ Tuple, Value }, Acc) ->
lists:keystore(Tuple, 1, Acc, { Tuple, Value } )
lists:keystore(Tuple, 1, Acc, { Tuple, Value })
end, [], Specs),
lists:foldl(fun({ Tuple, _ }, Acc) ->
Values = [V || { K, V } <- Specs, K == Tuple],
{ type, Line, _, _ } = hd(Values),
[{ attribute, Line, Type, { Tuple, Values } }|Acc]
end, Forms, Keys).
@@ -282,18 +296,31 @@ check_module_availability(Line, File, Module, Compiler) ->
[]
end.
warn_unused_docs(_Line, _File, 'Elixir.Kernel', _All) -> ok;
warn_unused_docs(_Line, _File, 'Elixir.Kernel.SpecialForms', _All) -> ok;
warn_unused_docs(_Line, File, Module, All) ->
warn_invalid_docs(_Line, _File, 'Elixir.Kernel', _All) -> ok;
warn_invalid_docs(_Line, _File, 'Elixir.Kernel.SpecialForms', _All) -> ok;
warn_invalid_docs(_Line, File, Module, All) ->
ets:foldl(fun
({ Tuple, Line, _, _, _ }, Acc) ->
({ _, _, Kind, _, _ }, _) when Kind == type; Kind == opaque ->
ok;
({ Tuple, Line, _, _, _ }, _) ->
case lists:member(Tuple, All) of
true -> Acc;
false ->
elixir_errors:handle_file_warning(File, { Line, ?MODULE, { invalid_doc, Tuple } })
elixir_errors:handle_file_warning(File, { Line, ?MODULE, { invalid_doc, Tuple } });
true ->
ok
end
end, ok, docs_table(Module)).
warn_unused_docs(Line, File, Module) ->
lists:foreach(fun(Attribute) ->
case ets:member(data_table(Module), Attribute) of
true ->
elixir_errors:handle_file_warning(File, { Line, ?MODULE, { unused_doc, Attribute } });
_ ->
ok
end
end, [typedoc]).
% EXTRA FUNCTIONS
add_info_function(Line, File, Module, Export, Def, Defmacro, C) ->
@@ -329,7 +356,10 @@ module_clause(Module) ->
{ clause, 0, [{ atom, 0, module }], [], [{ atom, 0, Module }] }.
docs_clause(Module, true) ->
Docs = ordsets:from_list(ets:tab2list(docs_table(Module))),
Docs = ordsets:from_list(
[{Tuple, Line, Kind, Sig, Doc} ||
{Tuple, Line, Kind, Sig, Doc} <- ets:tab2list(docs_table(Module)),
Kind =/= type, Kind =/= opaque]),
{ clause, 0, [{ atom, 0, docs }], [], [elixir_utils:elixir_to_erl(Docs)] };
docs_clause(_Module, _) ->
@@ -389,6 +419,12 @@ munge_stacktrace(Info, []) ->
format_error({ invalid_doc, { Name, Arity } }) ->
io_lib:format("docs provided for nonexistent function or macro ~ts/~B", [Name, Arity]);
format_error({ unused_doc, typedoc }) ->
"@typedoc provided but no type follows it";
format_error({ unused_doc, doc }) ->
"@doc provided but no definition follows it";
format_error({ internal_function_overridden, { Name, Arity } }) ->
io_lib:format("function ~ts/~B is internal and should not be overridden", [Name, Arity]);
+1
View File
@@ -11,6 +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_errors:deprecation(Meta, S#elixir_scope.file, "partial application without capture is deprecated"),
elixir_fn:fn(Meta, [{ Final, Meta, Block }], SC);
_ -> error
end;
+45 -29
View File
@@ -1,6 +1,7 @@
%% Implements Elixir quote.
-module(elixir_quote).
-export([escape/2, erl_escape/3, erl_quote/4, linify/2, unquote/6, join/5]).
-export([escape/2, erl_escape/3, erl_quote/4,
linify/2, unquote/4, tail_join/3, join/2]).
-include("elixir.hrl").
%% Apply the line from site call on quoted contents.
@@ -24,10 +25,10 @@ do_linify(_, Else) -> Else.
%% Some expressions cannot be unquoted at compilation time.
%% This function is responsible for doing runtime unquoting.
unquote(_File, _Line, Meta, Left, { '__aliases__', _, Args }, nil) ->
unquote(Meta, Left, { '__aliases__', _, Args }, nil) ->
{ '__aliases__', Meta, [Left|Args] };
unquote(_File, _Line, Meta, Left, Right, nil) when is_atom(Right) ->
unquote(Meta, Left, Right, nil) when is_atom(Right) ->
case atom_to_list(Right) of
"Elixir." ++ _ ->
{ '__aliases__', Meta, [Left, Right] };
@@ -35,33 +36,48 @@ unquote(_File, _Line, Meta, Left, Right, nil) when is_atom(Right) ->
{ { '.', Meta, [Left, Right] }, Meta, [] }
end;
unquote(_File, _Line, Meta, Left, { Right, _, Context }, nil) when is_atom(Right), is_atom(Context) ->
unquote(Meta, Left, { Right, _, Context }, nil) when is_atom(Right), is_atom(Context) ->
{ { '.', Meta, [Left, Right] }, Meta, [] };
unquote(_File, _Line, Meta, Left, { Right, _, Args }, nil) when is_atom(Right) ->
unquote(Meta, Left, { Right, _, Args }, nil) when is_atom(Right) ->
{ { '.', Meta, [Left, Right] }, Meta, Args };
unquote(File, Line, _Meta, _Left, Right, nil) ->
elixir_errors:syntax_error(Line, File, "expected unquote after dot to return an atom, "
"an alias or a quoted call, got: ~ts", ['Elixir.Macro':to_string(Right)]);
unquote(_Meta, _Left, Right, nil) ->
argument_error(<<"expected unquote after dot to return an atom, an alias or a quoted call, got: ",
('Elixir.Macro':to_string(Right))/binary>>);
unquote(_File, _Line, Meta, Left, Right, Args) when is_atom(Right) ->
unquote(Meta, Left, Right, Args) when is_atom(Right) ->
{ { '.', Meta, [Left, Right] }, Meta, Args };
unquote(_File, _Line, Meta, Left, { Right, _, Context }, Args) when is_atom(Right), is_atom(Context) ->
unquote(Meta, Left, { Right, _, Context }, Args) when is_atom(Right), is_atom(Context) ->
{ { '.', Meta, [Left, Right] }, Meta, Args };
unquote(File, Line, _Meta, _Left, Right, _Args) ->
elixir_errors:syntax_error(Line, File, "expected unquote after dot with args to return an atom "
"or a quoted call, got: ~ts", ['Elixir.Macro':to_string(Right)]).
unquote(_Meta, _Left, Right, _Args) ->
argument_error(<<"expected unquote after dot with args to return an atom or a quoted call, got: ",
('Elixir.Macro':to_string(Right))/binary>>).
join(_File, _Line, Left, Right, Rest) when is_list(Left), is_list(Right), is_list(Rest) ->
Rest ++ Left ++ Right;
join(Left, Right) when is_list(Right) ->
validate_join(Left),
Left ++ Right.
join(_File, _Line, Left, Right, Rest) ->
[H|T] = lists:reverse(Rest ++ Left),
tail_join(Left, Right, Tail) when is_list(Right), is_list(Tail) ->
validate_join(Left),
Tail ++ Left ++ Right;
tail_join(Left, Right, Tail) when is_list(Left) ->
validate_join(Left),
[H|T] = lists:reverse(Tail ++ Left),
lists:reverse([{ '|', [], [H, Right] }|T]).
validate_join(List) when is_list(List) ->
ok;
validate_join(List) when not is_list(List) ->
argument_error(<<"expected a list with quoted expressions in unquote_splicing/1, got: ",
('Elixir.Kernel':inspect(List))/binary>>).
argument_error(Message) ->
error('Elixir.ArgumentError':exception([{message,Message}])).
%% Escapes the given expression. It is similar to quote, but
%% lines are kept and hygiene mechanisms are disabled.
escape(Expr, Unquote) ->
@@ -214,9 +230,9 @@ do_quote(Other, Q, _) ->
%% Quote helpers
do_quote_call(Left, Meta, Expr, Args, Q, S) ->
All = [?line(Meta), meta(Meta, Q), Left, { unquote, Meta, [Expr] }, Args],
All = [meta(Meta, Q), Left, { unquote, Meta, [Expr] }, Args],
{ TAll, TQ } = lists:mapfoldl(fun(X, Acc) -> do_quote(X, Acc, S) end, Q, All),
{ { { '.', Meta, [elixir_quote, unquote] }, Meta, [{ '__FILE__', [], nil }|TAll] }, TQ }.
{ { { '.', Meta, [elixir_quote, unquote] }, Meta, TAll }, TQ }.
do_quote_fa(Target, Meta, Args, F, A, Q, S) ->
NewMeta =
@@ -264,24 +280,24 @@ do_splice([{ '|', Meta, [{ unquote_splicing, _, [Left] }, Right] }|T], #elixir_q
%% 1, 2 and 3, which could even be unquotes.
{ TT, QT } = do_splice(T, Q, S, [], []),
{ TR, QR } = do_quote(Right, QT, S),
%% Do the joining at runtime when we are aware of the values.
Args = [{ '__FILE__', [], nil }, ?line(Meta), Left, TR, TT],
{ { { '.', Meta, [elixir_quote, join] }, Meta, Args }, QR#elixir_quote{unquoted=true} };
{ do_runtime_join(Meta, tail_join, [Left, TR, TT]), QR#elixir_quote{unquoted=true} };
do_splice(List, Q, S) ->
do_splice(List, Q, S, [], []).
do_splice([{ unquote_splicing, _, [Expr] }|T], #elixir_quote{unquote=true} = Q, S, Buffer, Acc) ->
do_splice(T, Q#elixir_quote{unquoted=true}, S, [], do_splice_join(do_splice_join(Expr, Buffer), Acc));
do_splice([{ unquote_splicing, Meta, [Expr] }|T], #elixir_quote{unquote=true} = Q, S, Buffer, Acc) ->
do_splice(T, Q#elixir_quote{unquoted=true}, S, [], do_runtime_join(Meta, join, [Expr, do_join(Buffer, Acc)]));
do_splice([H|T], Q, S, Buffer, Acc) ->
{ TH, TQ } = do_quote(H, Q, S),
do_splice(T, TQ, S, [TH|Buffer], Acc);
do_splice([], Q, _S, Buffer, Acc) ->
{ do_splice_join(Buffer, Acc), Q }.
{ do_join(Buffer, Acc), Q }.
do_splice_join(Left, []) -> Left;
do_splice_join([], Right) -> Right;
do_splice_join(Left, Right) -> { { '.', [], ['Elixir.Kernel', '++'] }, [], [Left, Right] }.
do_join(Left, []) -> Left;
do_join([], Right) -> Right;
do_join(Left, Right) -> { { '.', [], ['Elixir.Kernel', '++'] }, [], [Left, Right] }.
do_runtime_join(Meta, Fun, Args) ->
{ { '.', Meta, [elixir_quote, Fun] }, Meta, Args }.
+5 -8
View File
@@ -81,24 +81,21 @@ build_ex_var(Line, Key, Name, S) when is_integer(Line) ->
% Handle Macro.Env conversion
to_erl_env({ 'Elixir.Macro.Env', Module, File, _Line, Function, Aliases, Context,
Requires, Functions, Macros, ContextModules, MacroAliases, Vars }) ->
Requires, Functions, Macros, ContextModules, MacroAliases, _Vars }) ->
#elixir_scope{module=Module,file=File,
function=Function,aliases=Aliases,context=Context,
requires=Requires,macros=Macros,functions=Functions,
context_modules=ContextModules,macro_aliases=MacroAliases,
list_vars=Vars}.
context_modules=ContextModules,macro_aliases=MacroAliases}.
to_ex_env({ Line, #elixir_scope{module=Module,file=File,
function=Function,aliases=Aliases,context=Context,
requires=Requires,macros=Macros,functions=Functions,
context_modules=ContextModules,macro_aliases=MacroAliases,
vars=Vars,list_vars=ListVars} }) when is_integer(Line) ->
context_modules=ContextModules,macro_aliases=MacroAliases, vars=Vars} }) when is_integer(Line) ->
{ 'Elixir.Macro.Env', Module, File, Line, Function, Aliases,
Context, Requires, Functions, Macros, ContextModules, MacroAliases,
list_vars(ListVars, Vars) }.
list_vars(Vars) }.
list_vars(nil, Vars) -> [K || { K, _ } <- Vars];
list_vars(Other, _) -> Other.
list_vars(Vars) -> [K || { K, _ } <- Vars].
% Provides a tuple with only the scope information we want to serialize.
+3
View File
@@ -658,6 +658,9 @@ remove_heredoc_spaces([], Buffer, _Spaces, _Original) ->
%% the remaining of the document and the number of spaces the heredoc
%% is aligned.
extract_heredoc_body(_Line, Marker, [{line,NewLine}|Rest], Buffer) ->
extract_heredoc_body(NewLine, Marker, Rest, Buffer);
extract_heredoc_body(Line, Marker, Rest, Buffer) ->
case extract_heredoc_line(Marker, Rest, Buffer, 0) of
{ ok, NewBuffer, NewRest } ->
+6 -2
View File
@@ -397,7 +397,9 @@ translate_each({ Atom, Meta, Args } = Original, S) when is_atom(Atom) ->
elixir_dispatch:dispatch_import(Meta, Atom, Args, S, Callback);
Else -> Else
end;
Else -> Else
Else ->
elixir_errors:deprecation(Meta, S#elixir_scope.file, "partial application without capture is deprecated"),
Else
end;
%% Remote calls
@@ -437,7 +439,9 @@ translate_each({ { '.', _, [Left, Right] }, Meta, Args } = Original, S) when is_
end;
Else -> Else
end;
Else -> Else
Else ->
elixir_errors:deprecation(Meta, S#elixir_scope.file, "partial application without capture is deprecated"),
Else
end;
%% Anonymous function calls
+4
View File
@@ -214,6 +214,10 @@ defmodule DictTest.Common do
dict2 = Dict.put(dict2, :a, 3)
refute Dict.equal?(dict1, dict2)
dict3 = HashDict.new(a: 2, b: 3, f: 5, c: 123, z: 666)
refute Dict.equal?(dict1, dict3)
refute Dict.equal?(dict3, dict1)
end
test "unsupported dict" do
+36 -23
View File
@@ -152,14 +152,14 @@ defmodule EnumTest.List do
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 :flat_map do
@@ -185,15 +185,15 @@ defmodule EnumTest.List do
end
test :map_join 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)) == ""
assert Enum.map_join([1, 2, 3], " = ", &(&1 * 2)) == "2 = 4 = 6"
assert Enum.map_join([1, 2, 3], &(&1 * 2)) == "246"
end
test :join_empty do
fun = fn (acc, _) -> acc end
assert Enum.join(fun, ".") == ""
assert Enum.map_join(fun, ".", &1 + 0) == ""
assert Enum.map_join(fun, ".", &(&1 + 0)) == ""
end
test :map do
@@ -229,7 +229,7 @@ defmodule EnumTest.List do
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)) == [5, 4, 3, 2, 1]
end
test :split do
@@ -264,6 +264,13 @@ defmodule EnumTest.List do
assert Enum.take([], 3) == []
end
test :take_every do
assert Enum.take_every([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 2) == [1, 3, 5, 7, 9]
assert Enum.take_every([], 2) == []
assert Enum.take_every([1, 2], 2) == [1]
assert Enum.take_every([1, 2, 3], 0) == []
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]
@@ -377,10 +384,10 @@ defmodule EnumTest.Range do
test :any? do
range = Range.new(first: 0, last: 5)
refute Enum.any?(range, &1 > 10)
refute Enum.any?(range, &(&1 > 10))
range = Range.new(first: 0, last: 5)
assert Enum.any?(range, &1 > 3)
assert Enum.any?(range, &(&1 > 3))
range = Range.new(first: 1, last: 0)
assert Enum.any?(range)
@@ -510,17 +517,17 @@ defmodule EnumTest.Range do
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?(1, &1)) == [1]
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
range = Range.new(first: 1, last: 3)
assert Enum.filter_map(range, fn(x) -> rem(x, 2) == 0 end, &1 * 2) == [4]
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 :flat_map do
@@ -558,11 +565,11 @@ defmodule EnumTest.Range do
test :map_join do
range = Range.new(first: 1, last: 0)
assert Enum.map_join(range, " = ", &1 * 2) == "2 = 0"
assert Enum.map_join(range, " = ", &(&1 * 2)) == "2 = 0"
range = Range.new(first: 1, last: 3)
assert Enum.map_join(range, " = ", &1 * 2) == "2 = 4 = 6"
assert Enum.map_join(range, &1 * 2) == "246"
assert Enum.map_join(range, " = ", &(&1 * 2)) == "2 = 4 = 6"
assert Enum.map_join(range, &(&1 * 2)) == "246"
end
test :map do
@@ -597,9 +604,9 @@ defmodule EnumTest.Range do
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: 1, last: 1), &1 > &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
@@ -642,6 +649,12 @@ defmodule EnumTest.Range do
assert Enum.take(range, 3) == [1, 0]
end
test :take_every do
assert Enum.take_every(1..10, 2) == [1, 3, 5, 7, 9]
assert Enum.take_every(1..2, 2) == [1]
assert Enum.take_every(1..3, 0) == []
end
test :take_while do
range = Range.new(first: 1, last: 3)
assert Enum.take_while(range, fn(x) -> x > 3 end) == []
+13
View File
@@ -84,6 +84,19 @@ defmodule Kernel.ExceptionTest do
assert ErlangError.new(original: :sample).message == "erlang error: :sample"
end
test :raise_preserves_the_stacktrace do
stacktrace =
try do
raise "a"
rescue _ ->
[top|_] = System.stacktrace
top
end
file = to_char_list(__FILE__)
assert {Kernel.ExceptionTest, :test_raise_preserves_the_stacktrace, _,
[file: ^file, line: 90]} = stacktrace # line #90 is sensitive
end
defp empty_tuple, do: {}
defp a_tuple, do: { :foo, :bar, :baz }
defp a_list, do: [ :foo, :bar, :baz ]
+2 -2
View File
@@ -550,7 +550,7 @@ defmodule FileTest do
test :open_a_file_with_function do
file = fixture_path("file.txt")
assert File.open(file, IO.read(&1, :line)) == { :ok, "FOO\n" }
assert File.open(file, &IO.read(&1, :line)) == { :ok, "FOO\n" }
end
test :open_a_missing_file! do
@@ -562,7 +562,7 @@ defmodule FileTest do
test :open_a_file_with_function! do
file = fixture_path("file.txt")
assert File.open!(file, IO.read(&1, :line)) == "FOO\n"
assert File.open!(file, &IO.read(&1, :line)) == "FOO\n"
end
end
+8 -8
View File
@@ -85,31 +85,31 @@ defmodule HashDictTest do
test :update do
dict = filled_dict(8)
dict = HashDict.update!(dict, 1, &1 * 2)
dict = HashDict.update!(dict, 1, &(&1 * 2))
assert HashDict.get(dict, 1) == 2
assert HashDict.size(dict) == 8
assert_raise KeyError, fn ->
HashDict.update!(dict, 11, &1 * 2)
HashDict.update!(dict, 11, &(&1 * 2))
end
dict = HashDict.update(dict, 3, 3, &1 * 2)
dict = HashDict.update(dict, 3, 3, &(&1 * 2))
assert HashDict.get(dict, 3) == 6
assert HashDict.size(dict) == 8
dict = HashDict.update(dict, 11, 13, &1 * 2)
dict = HashDict.update(dict, 11, 13, &(&1 * 2))
assert HashDict.get(dict, 11) == 13
assert HashDict.size(dict) == 9
assert_raise KeyError, fn->
HashDict.update!(dict, 11.0, &1 * 2)
HashDict.update!(dict, 11.0, &(&1 * 2))
end
dict = HashDict.update(dict, 11.0, 15, &1 * 2)
dict = HashDict.update(dict, 11.0, 15, &(&1 * 2))
assert HashDict.get(dict, 11.0) == 15
assert HashDict.size(dict) == 10
dict = HashDict.update(dict, 11.0, 15, &1 * 2)
dict = HashDict.update(dict, 11.0, 15, &(&1 * 2))
assert HashDict.get(dict, 11.0) == 30
assert HashDict.get(dict, 11) == 13
assert HashDict.size(dict) == 10
@@ -302,6 +302,6 @@ defmodule HashDictTest do
end
defp filled_dict(range) do
Enum.reduce 1..range, HashDict.new, HashDict.put(&2, &1, &1)
Enum.reduce 1..range, HashDict.new, &HashDict.put(&2, &1, &1)
end
end
+1 -1
View File
@@ -110,7 +110,7 @@ defmodule HashSetTest do
end
defp filled_set(range) do
Enum.reduce 1..range, HashSet.new, HashSet.put(&2, &1)
Enum.reduce 1..range, HashSet.new, &HashSet.put(&2, &1)
end
end
@@ -101,9 +101,9 @@ bar
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 >>
end
test :literal do
+28 -6
View File
@@ -45,12 +45,6 @@ defmodule Kernel.ErrorsTest do
'"""\nbar'
end
test :invalid_partial do
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",
@@ -119,6 +113,16 @@ defmodule Kernel.ErrorsTest do
'''
end
test :invalid_match_pattern do
assert_compile_fail CompileError,
"nofile:2: invalid pattern in match clause",
'''
case true do
true && true -> true
end
'''
end
test :different_defs_with_defaults do
assert_compile_fail CompileError,
"nofile:3: def hello/3 defaults conflicts with def hello/2",
@@ -571,6 +575,24 @@ defmodule Kernel.ErrorsTest do
'case [] do; [] when Hello.something_that_does_not_exist == [] -> :ok; end'
end
test :typespec_errors do
assert_compile_fail CompileError,
"nofile:2: type foo() undefined",
'''
defmodule Example do
@type omg :: foo
end
'''
assert_compile_fail CompileError,
"nofile:2: spec for undefined function Example.omg/0",
'''
defmodule Example do
@spec omg :: atom
end
'''
end
test :macros_error_stacktrace do
assert [{:erlang, :+, [1, :foo], _}, {ErrorsTest, :sample, 1, _}|_] =
rescue_stacktrace(""")
@@ -83,6 +83,10 @@ defmodule Kernel.FnTest do
assert (&(&1.file(&2))).(__ENV__, "Hello").file == "Hello"
end
test "capture block like" do
assert (&(!is_atom(&1))).(:foo) == false
end
test "capture other" do
assert (& &1).(:ok) == :ok
@@ -1,62 +0,0 @@
Code.require_file "../test_helper.exs", __DIR__
defmodule Kernel.PartialApplicationTest do
use ExUnit.Case, async: true
test :partial_with_simple_call_and_one_item do
fun = minus(10, &1)
assert fun.(5) == 5
assert fun.(3) == 7
end
test :partial_with_simple_call_and_all_items do
fun = minus(&1, &2)
assert fun.(10, 5) == 5
assert fun.(13, 6) == 7
end
test :partial_with_funcall_and_one_item do
fun = minus(&1, &2)
fun = fun.(10, &1)
assert fun.(5) == 5
assert fun.(3) == 7
end
test :partial_with_funcall_and_all_items do
fun = minus(&1, &2)
fun = fun.(&1, &2)
assert fun.(10, 5) == 5
assert fun.(13, 6) == 7
end
test :partial_with_opposite_order do
fun = minus(&2, &1)
assert fun.(10, 5) == -5
assert fun.(13, 6) == -7
end
test :partial_with_duplicated do
fun = minus(&1, &1)
assert fun.(10) == 0
assert fun.(13) == 0
end
test :partial_with_internal_macro do
fun = if(&1, do: 1, else: 2)
assert fun.(20) == 1
assert fun.(nil) == 2
end
test :partial_application_for_module_calls do
fun = Enum.reverse(&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]
end
defp minus(x, y) do
x - y
end
end
+1 -1
View File
@@ -59,7 +59,7 @@ defmodule Kernel.QuoteTest do
assert Code.eval_quoted(quote(do: Foo.unquote(Bar))) == { Elixir.Foo.Bar, [] }
assert Code.eval_quoted(quote(do: Foo.unquote(quote do: Bar))) == { Elixir.Foo.Bar, [] }
assert_raise SyntaxError, fn ->
assert_raise ArgumentError, fn ->
quote(do: foo.unquote(1))
end
end
@@ -249,7 +249,7 @@ defmodule Kernel.RecordRewriterTest do
test "updater call is rewriten" do
{ clause, rewriten } =
{ clause(fn(x = Range[]) -> x.update_first(&1 + 1) end), clause(fn(x = Range[]) -> Range.update_first(&1 + 1, x) end) }
{ clause(fn(x = Range[]) -> x.update_first(&(&1 + 1)) end), clause(fn(x = Range[]) -> Range.update_first(&(&1 + 1), x) end) }
assert optimize_clause(clause) == { rewriten, [x: Range], { Range, nil } }
end
+28 -1
View File
@@ -402,8 +402,35 @@ defmodule Kernel.WarningTest do
purge [Sample]
end
test :typedoc_on_typep do
assert capture_err(fn ->
Code.eval_string """
defmodule Sample do
@typedoc "Something"
@typep priv :: any
@spec foo() :: priv
def foo(), do: nil
end
"""
end) =~ "nofile:3: type priv/0 is private, @typedoc's are always discarded for private types"
after
purge [Sample]
end
test :typedoc_with_no_type do
assert capture_err(fn ->
Code.eval_string """
defmodule Sample do
@typedoc "Something"
end
"""
end) =~ "nofile:1: @typedoc provided but no type follows it"
after
purge [Sample]
end
defp purge(list) when is_list(list) do
Enum.each list, purge(&1)
Enum.each list, &purge/1
end
defp purge(module) when is_atom(module) do
+5 -5
View File
@@ -308,7 +308,7 @@ defmodule KernelTest do
end
test "nested pipelines" 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 call" do
@@ -316,12 +316,12 @@ defmodule KernelTest do
end
test "pipeline with capture" 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 "non-call" do
assert 1 |> (&1*2).() == 2
assert [1] |> hd(&1).() == 1
assert 1 |> (&(&1*2)).() == 2
assert [1] |> (&hd(&1)).() == 1
import CompileAssertion
assert_compile_fail ArgumentError, "unsupported expression in pipeline |> operator: 2", "1 |> 2"
@@ -330,7 +330,7 @@ defmodule KernelTest do
defp twice(a), do: a * 2
defp local(list) do
Enum.map(list, &1 * 2)
Enum.map(list, &(&1 * 2))
end
end
+4 -4
View File
@@ -111,15 +111,15 @@ defmodule KeywordTest do
end
test :update do
assert Keyword.update!([a: 1], :a, &1 * 2) == [a: 2]
assert Keyword.update!([a: 1], :a, &(&1 * 2)) == [a: 2]
assert_raise KeyError, fn ->
assert Keyword.update!([a: 1], :b, &1 * 2)
assert Keyword.update!([a: 1], :b, &(&1 * 2))
end
end
test :update_with_initial do
assert Keyword.update([a: 1], :a, 13, &1 * 2) == [a: 2]
assert Keyword.update([a: 1], :b, 11, &1 * 2) == [a: 1, b: 11]
assert Keyword.update([a: 1], :a, 13, &(&1 * 2)) == [a: 2]
assert Keyword.update([a: 1], :b, 11, &(&1 * 2)) == [a: 1, b: 11]
end
defp create_empty_keywords, do: []
+20 -5
View File
@@ -13,11 +13,11 @@ defmodule ListTest do
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, 2) == [1|2]
assert ([&1, &2|&3]).(1, 2, 3) == [1, 2|3]
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]
end
test :wrap do
@@ -133,4 +133,19 @@ defmodule ListTest do
assert List.replace_at([1, 2, 3], -1, 0) == [1, 2, 0]
assert List.replace_at([1, 2, 3], -4, 0) == [1, 2, 3]
end
test :heredoc do
assert [ "a\n" ] == [ """ ]
a
"""
assert [ "a\n", "b\n", "c\n", 1 ] == [ """, """, """, 1 ]
a
"""
b
"""
c
"""
end
end
+1 -1
View File
@@ -125,7 +125,7 @@ defmodule ModuleTest do
test :registered_attributes do
assert [{:register_example, [:it_works]}, {:register_example, [:still_works]}] ==
Enum.filter __MODULE__.__info__(:attributes), match?({ :register_example, _ }, &1)
Enum.filter __MODULE__.__info__(:attributes), &match?({ :register_example, _ }, &1)
end
@some_attribute [1]
+2 -2
View File
@@ -47,8 +47,8 @@ defmodule RangeTest do
assert Enum.count(1..3) == 3
assert Enum.count(3..1) == 3
assert Enum.map(1..3, &1 * 2) == [2, 4, 6]
assert Enum.map(3..1, &1 * 2) == [6, 4, 2]
assert Enum.map(1..3, &(&1 * 2)) == [2, 4, 6]
assert Enum.map(3..1, &(&1 * 2)) == [6, 4, 2]
end
test :inspect do
+19 -1
View File
@@ -70,6 +70,8 @@ defmodule RecordTest.Macros do
defrecord Record, [a: 1, b: 2]
end
defrecord RecordTest.FooTest, foo: nil, bar: nil
defmodule RecordTest do
use ExUnit.Case, async: true
@@ -92,7 +94,7 @@ defmodule RecordTest do
test :dynamic_update do
record = RecordTest.DynamicName.new
assert record.update_a(10 + &1).a == 10
assert record.update_a(&(10 + &1)).a == 10
end
test :is_record do
@@ -162,6 +164,22 @@ defmodule RecordTest do
assert is_record(namespace, :xmlNamespace)
end
test :string_names do
a = RecordTest.FooTest.new([{"foo", 1}, {"bar", 1}])
assert a.foo == 1
assert a.bar == 1
a = a.update([{"foo", 2}, {"bar", 2}])
assert a.foo == 2
assert a.bar == 2
end
test :string_names_import do
record = RecordTest.FileInfo.new([{"type", :regular}, {"access", 100}])
assert record.type == :regular
assert record.access == 100
assert record.update([{"access", 101}]).access == 101
end
defp empty_tuple, do: {}
defp a_tuple, do: { :foo, :bar, :baz }
defp a_list, do: [ :foo, :bar, :baz ]
+8 -7
View File
@@ -68,13 +68,13 @@ defmodule Regex.BinaryTest do
assert Regex.match?(%r/foo$/, "afoo")
end
test :captures do
assert Keyword.equal? Regex.captures(%r/(?<foo>c)(?<bar>d)/g, 'abcd'), [bar: 'd', foo: 'c']
assert Regex.captures(%r/c(?<foo>d)/g, 'abcd') == [foo: 'd']
assert Regex.captures(%r/c(?<foo>d)/g, 'no_match') == nil
assert Regex.captures(%r/c(?<foo>d|e)/g, 'abcd abce') == [foo: 'd']
assert Regex.captures(%r/c(?<foo>d)/g, 'abcd', return: :binary) == [foo: "d"]
assert Regex.captures(%r/c(.)/g, 'cat') == []
test :named_captures do
assert Keyword.equal? Regex.named_captures(%r/(?<foo>c)(?<bar>d)/g, 'abcd'), [bar: 'd', foo: 'c']
assert Regex.named_captures(%r/c(?<foo>d)/g, 'abcd') == [foo: 'd']
assert Regex.named_captures(%r/c(?<foo>d)/g, 'no_match') == nil
assert Regex.named_captures(%r/c(?<foo>d|e)/g, 'abcd abce') == [foo: 'd']
assert Regex.named_captures(%r/c(?<foo>d)/g, 'abcd', return: :binary) == [foo: "d"]
assert Regex.named_captures(%r/c(.)/g, 'cat') == []
end
test :sigil_R do
@@ -107,6 +107,7 @@ defmodule Regex.BinaryTest do
end
test :scan_with_groups do
assert Regex.scan(%r/cd/g, 'abcd', capture: :groups) == []
assert Regex.scan(%r/c(?<foo>d)/g, 'abcd', capture: :groups) == [['d']]
assert Regex.scan(%r/c(?<foo>d)/g, 'no_match', capture: :groups) == []
assert Regex.scan(%r/c(?<foo>d|e)/g, 'abcd abce', capture: :groups) == [['d'], ['e']]
+28 -15
View File
@@ -4,10 +4,10 @@ defmodule StreamTest do
use ExUnit.Case, async: true
test "streams as enumerables" do
stream = Stream.map([1,2,3], &1 * 2)
stream = Stream.map([1,2,3], &(&1 * 2))
# Reduce
assert Enum.map(stream, &1 + 1) == [3,5,7]
assert Enum.map(stream, &(&1 + 1)) == [3,5,7]
# Member
assert Enum.member?(stream, 4)
refute Enum.member?(stream, 1)
@@ -16,10 +16,10 @@ defmodule StreamTest do
end
test "streams are composable" do
stream = Stream.map([1,2,3], &1 * 2)
stream = Stream.map([1,2,3], &(&1 * 2))
assert is_lazy(stream)
stream = Stream.map(stream, &1 + 1)
stream = Stream.map(stream, &(&1 + 1))
assert is_lazy(stream)
assert Enum.to_list(stream) == [3,5,7]
@@ -77,15 +77,15 @@ defmodule StreamTest do
end
test :drop_while do
stream = Stream.drop_while(1..10, &1 <= 5)
stream = Stream.drop_while(1..10, &(&1 <= 5))
assert is_lazy(stream)
assert Enum.to_list(stream) == [6,7,8,9,10]
assert Enum.to_list(Stream.drop_while(1..5, &1 <= 0)) == [1,2,3,4,5]
assert Enum.to_list(Stream.drop_while(1..3, &1 <= 5)) == []
assert Enum.to_list(Stream.drop_while(1..5, &(&1 <= 0))) == [1,2,3,4,5]
assert Enum.to_list(Stream.drop_while(1..3, &(&1 <= 5))) == []
nats = Stream.iterate(1, &(&1 + 1))
assert Stream.drop_while(nats, &1 <= 5) |> Enum.take(5) == [6,7,8,9,10]
assert Stream.drop_while(nats, &(&1 <= 5)) |> Enum.take(5) == [6,7,8,9,10]
end
test :filter do
@@ -98,18 +98,18 @@ defmodule StreamTest do
end
test :iterate do
stream = Stream.iterate(0, &1+2)
stream = Stream.iterate(0, &(&1+2))
assert Enum.take(stream, 5) == [0,2,4,6,8]
stream = Stream.iterate(5, &1+2)
stream = Stream.iterate(5, &(&1+2))
assert Enum.take(stream, 5) == [5,7,9,11,13]
# Only calculate values if needed
stream = Stream.iterate("HELLO", raise(&1))
stream = Stream.iterate("HELLO", &raise/1)
assert Enum.take(stream, 1) == ["HELLO"]
end
test :map do
stream = Stream.map([1,2,3], &1 * 2)
stream = Stream.map([1,2,3], &(&1 * 2))
assert is_lazy(stream)
assert Enum.to_list(stream) == [2,4,6]
@@ -162,12 +162,12 @@ defmodule StreamTest do
end
test :take_while do
stream = Stream.take_while(1..1000, &1 <= 5)
stream = Stream.take_while(1..1000, &(&1 <= 5))
assert is_lazy(stream)
assert Enum.to_list(stream) == [1,2,3,4,5]
assert Enum.to_list(Stream.take_while(1..1000, &1 <= 0)) == []
assert Enum.to_list(Stream.take_while(1..3, &1 <= 5)) == [1,2,3]
assert Enum.to_list(Stream.take_while(1..1000, &(&1 <= 0))) == []
assert Enum.to_list(Stream.take_while(1..3, &(&1 <= 5))) == [1,2,3]
nats = Stream.iterate(1, &(&1 + 1))
assert Enum.to_list(Stream.take_while(nats, &(&1 <= 5))) == [1,2,3,4,5]
@@ -175,6 +175,19 @@ defmodule StreamTest do
stream = Stream.drop(1..100, 5)
assert Stream.take_while(stream, &(&1 < 11)) |> Enum.to_list == [6,7,8,9,10]
end
test :unfold do
stream = Stream.unfold(10, fn x -> if x > 0, do: {x, x-1}, else: nil end)
assert Enum.take(stream, 5) == [10, 9, 8, 7, 6]
stream = Stream.unfold(5, fn x -> if x > 0, do: {x, x-1}, else: nil end)
assert Enum.to_list(stream) == [5, 4, 3, 2, 1]
# Only calculate values if needed
stream = Stream.unfold(1, fn x -> if x > 0, do: {x, x-1}, else: throw(:boom) end)
assert Enum.take(stream, 1) == [1]
stream = Stream.unfold(5, fn x -> if x > 0, do: {x, x-1}, else: nil end)
assert Enum.to_list(Stream.take(stream, 2)) == [5, 4]
end
test :with_index do
stream = Stream.with_index([1,2,3])
+6 -10
View File
@@ -36,6 +36,10 @@ defmodule StringTest do
assert String.split(" a b c ", " ", trim: true) == ["a", "b", "c"]
assert String.split(" a b c ", " ", trim: true, global: false) == ["a b c "]
assert String.split("abé", "") == ["a", "b", "é", ""]
assert String.split("abé", "", global: false) == ["a", "bé"]
assert String.split("abé", "", trim: true) == ["a", "b", "é"]
end
test :split_with_regex do
@@ -196,7 +200,8 @@ defmodule StringTest do
end
test :mixed_codepoints do
assert String.codepoints("ϖͲϥЫݎߟΈټϘለДШव׆ש؇؊صلټܗݎޥޘ߉ऌ૫ሏᶆ℆ℙℱ ⅚Ⅷ↠∈⌘①ffi") == ["ϖ", "Ͳ", "ϥ", "Ы", "ݎ", "ߟ", "Έ", "ټ", "Ϙ", "ለ", "Д", "Ш", "व", "׆", "ש", "؇", "؊", "ص", "ل", "ټ", "ܗ", "ݎ", "ޥ", "ޘ", "߉", "ऌ", "૫", "ሏ", "ᶆ", "℆", "ℙ", "ℱ", " ", "⅚", "Ⅷ", "↠", "∈", "⌘", "①", "ffi"]
assert String.codepoints("ϖͲϥЫݎߟΈټϘለДШव׆ש؇؊صلټܗݎޥޘ߉ऌ૫ሏᶆ℆ℙℱ ⅚Ⅷ↠∈⌘①ffi") ==
["ϖ", "Ͳ", "ϥ", "Ы", "ݎ", "ߟ", "Έ", "ټ", "Ϙ", "ለ", "Д", "Ш", "व", "׆", "ש", "؇", "؊", "ص", "ل", "ټ", "ܗ", "ݎ", "ޥ", "ޘ", "߉", "ऌ", "૫", "ሏ", "ᶆ", "℆", "ℙ", "ℱ", " ", "⅚", "Ⅷ", "↠", "∈", "⌘", "①", "ffi"]
end
test :graphemes do
@@ -298,15 +303,6 @@ defmodule StringTest do
refute String.valid_character?("ab")
end
test :valid_codepoint? do
assert String.valid_codepoint?("a")
assert String.valid_codepoint?("ø")
assert String.valid_codepoint?("あ")
refute String.valid_codepoint?(<<0xffff :: 16>>)
refute String.valid_codepoint?("ab")
end
test :to_integer do
assert String.to_integer("12") === {12, ""}
assert String.to_integer("-12") === {-12, ""}
+3 -3
View File
@@ -25,8 +25,8 @@ defmodule TupleTest do
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 }
end
end
+25
View File
@@ -488,4 +488,29 @@ defmodule Typespec.TypeTest do
after
Code.compiler_options debug_info: false
end
test "typedoc retrieval" do
Code.compiler_options debug_info: true
{ :module, T, binary, _ } = defmodule T do
@typedoc "A"
@type a :: any
@typep b :: any
@typedoc "C"
@opaque c(x, y) :: {x, y}
@type d :: any
@spec uses_b() :: b
def uses_b(), do: nil
end
:code.delete(T)
:code.purge(T)
assert [
{{:c, 2}, "C"},
{{:a, 0}, "A"}
] = Kernel.Typespec.beam_typedocs(binary)
after
Code.compiler_options debug_info: false
end
end
@@ -1,9 +1,9 @@
Code.require_file "../test_helper.exs", __DIR__
Code.require_file "test_helper.exs", __DIR__
defmodule Mix.VersionTest do
defmodule VersionTest do
use ExUnit.Case, async: true
alias Mix.Version.Parser, as: P
alias Mix.Version, as: V
alias Version.Parser, as: P
alias Version, as: V
test "lexes specifications properly" do
assert P.lexer("== != > >= < <= ~>", []) == [:'==', :'!=', :'>', :'>=', :'<', :'<=', :'~>']
+2 -2
View File
@@ -119,9 +119,9 @@ function_call_with_parens_args_and_nested_when_test() ->
%% Partial application
require_partial_application_test() ->
{ Fun, _ } = eval("List.flatten(&1)"),
{ Fun, _ } = eval("&List.flatten(&1)"),
Fun = fun 'Elixir.List':flatten/1.
import_partial_application_test() ->
{ Fun, _ } = eval("is_atom(&1)"),
{ Fun, _ } = eval("&is_atom(&1)"),
Fun = fun erlang:is_atom/1.
+8 -5
View File
@@ -247,9 +247,9 @@ defmodule ExUnit.Assertions do
@doc """
Asserts a message was or is going to be received. Unlike
`assert_received`, it has a default timeout of 100 miliseconds.
`assert_received`, it has a default timeout of 100 milliseconds.
The given `expected` argument must be a pattern.
The given `expected` argument has to be a pattern.
## Examples
@@ -263,17 +263,20 @@ defmodule ExUnit.Assertions do
assert_receive { :hello, _ }
x = 5
assert_receive { :count, ^x }
"""
defmacro assert_receive(expected, timeout // 100, message // nil) do
do_assert_receive(expected, timeout, message)
end
@doc """
Asserts a message was received and is in the current process mailbox.
The given `expected` argument must to be a match pattern.
Asserts a message was received and is in the current process' mailbox.
Timeout is set to 0, so there is no waiting time.
The given `expected` argument has to be a pattern.
## Examples
self <- :hello
+2 -2
View File
@@ -145,7 +145,7 @@ defmodule ExUnit.DocTest do
tests = Enum.filter(extract(module), fn(test) ->
fa = test.fun_arity
Enum.all?(except, &1 != fa) and Enum.all?(only, &1 == fa)
Enum.all?(except, &(&1 != fa)) and Enum.all?(only, &(&1 == fa))
end)
Enum.map_reduce(tests, 1, fn(test, acc) ->
@@ -383,7 +383,7 @@ defmodule ExUnit.DocTest do
end
defp strip_indent(line, indent) do
line |> String.slice(indent, String.length(line))
String.slice(line, indent, String.length(line)) || ""
end
defp extract_tests([], _line, "", "", [], _) do
+5 -5
View File
@@ -61,13 +61,13 @@ defmodule ExUnit.Runner do
defp wait_until_available(config) do
receive do
{ _pid, :case_finished, _test_case } ->
loop config.update_taken_cases(&1-1)
loop config.update_taken_cases(&(&1-1))
end
end
defp spawn_cases(config, cases) do
Enum.each cases, spawn_case(config, &1)
loop config.update_taken_cases(&1+length(cases))
Enum.each cases, &spawn_case(config, &1)
loop config.update_taken_cases(&(&1+length(cases)))
end
defp spawn_case(config, test_case) do
@@ -97,7 +97,7 @@ defmodule ExUnit.Runner do
tests = Enum.map tests, fn test -> test.failure({ :invalid, test_case }) end
self_pid <- { self, :case_finished, test_case, tests }
else
Enum.each tests, run_test(config, &1, context)
Enum.each tests, &run_test(config, &1, context)
test_case = try do
case_name.__ex_unit__(:teardown_all, context)
@@ -113,7 +113,7 @@ defmodule ExUnit.Runner do
receive do
{ ^case_pid, :case_finished, test_case, tests } ->
Enum.map tests, config.formatter.test_finished(config.formatter_id, &1)
Enum.map tests, &config.formatter.test_finished(config.formatter_id, &1)
config.formatter.case_finished(config.formatter_id, test_case)
pid <- { case_pid, :case_finished, test_case }
{ :DOWN, ^case_ref, :process, ^case_pid, { error, stacktrace } } ->
+2 -2
View File
@@ -56,11 +56,11 @@ defmodule ExUnit.Server do
end
def handle_cast({:add_async_case, name}, config) do
{ :noreply, config.update_async_cases [name|&1] }
{ :noreply, config.update_async_cases &[name|&1] }
end
def handle_cast({:add_sync_case, name}, config) do
{ :noreply, config.update_sync_cases [name|&1] }
{ :noreply, config.update_sync_cases &[name|&1] }
end
def handle_cast(request, config) do
@@ -141,7 +141,6 @@ defmodule ExUnit.DocTestTest.IndentationNotEnough do
def not_enough, do: :ok
end
defmodule ExUnit.DocTestTest do
use ExUnit.Case
+37 -27
View File
@@ -171,10 +171,23 @@ defmodule IEx do
match?({ :ok, true }, :application.get_env(:iex, :started))
end
@doc """
Returns `string` escaped using the specified color.
ANSI escapes in `string` are not processed in any way.
"""
def color(color_name, string) do
colors = IEx.Options.get(:colors)
enabled = colors[:enabled]
IO.ANSI.escape_fragment("%{#{colors[color_name]}}", enabled)
<> string <> IO.ANSI.escape_fragment("%{reset}", enabled)
end
## Callbacks
# This is a callback invoked by Erlang shell utilities
# when someone press Ctrl+G and adds 's Elixir.IEx'.
@doc false
def start(config // []) do
def start(config // [], callback // fn -> end) do
spawn fn ->
config =
case config do
@@ -182,9 +195,14 @@ defmodule IEx do
opts -> boot_config(opts)
end
Process.flag(:trap_exit, true)
case :init.notify_when_started(self()) do
:started -> :ok
_ -> :init.wait_until_started()
end
start_iex()
callback.()
set_expand_fun()
run_after_spawn()
IEx.Server.start(config)
@@ -192,19 +210,6 @@ defmodule IEx do
end
@doc false
def dont_display_result, do: :"do not show this result in iex"
## Boot Helpers
defp start_iex do
:application.start(:elixir)
:application.start(:iex)
end
@doc """
Returns the default config used to launch IEx. This config is also used by
`IEx.TestFramework`.
"""
def boot_config(opts) do
scope = :elixir.scope_for_eval(
file: "iex",
@@ -218,6 +223,22 @@ defmodule IEx do
]
end
@doc false
def dont_display_result, do: :"do not show this result in iex"
## Helpers
defp start_iex do
:application.start(:elixir)
:application.start(:iex)
# Disable ANSI-escape-sequence-based coloring on Windows
# Can be overriden in .iex
if match?({ :win32, _ }, :os.type()) do
IEx.Options.set :colors, enabled: false
end
end
defp set_expand_fun do
gl = Process.group_leader
glnode = node gl
@@ -226,7 +247,7 @@ defmodule IEx do
ensure_module_exists glnode, IEx.Remsh
expand_fun = IEx.Remsh.expand node
else
expand_fun = IEx.Autocomplete.expand &1
expand_fun = &IEx.Autocomplete.expand(&1)
end
:io.setopts gl, [expand_fun: expand_fun, binary: true, encoding: :unicode]
@@ -242,15 +263,4 @@ defmodule IEx do
defp run_after_spawn do
lc fun inlist Enum.reverse(after_spawn), do: fun.()
end
@doc """
Returns `string` escaped using the specified color. ANSI escapes in `string`
are not processed in any way.
"""
def color(color_name, string) do
colors = IEx.Options.get(:colors)
enabled = colors[:enabled]
IO.ANSI.escape_fragment("%{#{colors[color_name]}}", enabled)
<> string <> IO.ANSI.escape_fragment("%{reset}", enabled)
end
end
+83 -26
View File
@@ -1,3 +1,39 @@
# IEx sets the Erlang user to be IEx.CLI via the command line.
# While this works most of the time, there are some problems
# that may happen depending on how the booting process goes.
# Those problems need to be manually tested in case changes are
# done to this file.
#
# 1. In some situations, printing something before the shell
# starts becomes very slow. To verify this feature, just
# get an existing project and run:
#
# $ mix clean
# $ iex -S mix
#
# If the printing of data is slower than usual. This particular
# bug has arisen;
#
# 2. In some situations, connecting to a remote node via --remsh
# is not possible. This can be tested by starting two iex nodes:
#
# $ iex --sname foo
# $ iex --sname bar --remsh foo@localhost
#
# 3. When still using --remsh, we need to guarantee the arguments
# are processed on the local node and not the remote one. For such,
# one can replace the last line above by:
#
# $ iex --sname bar --remsh foo@localhost -e IO.inspect node
#
# And verify that the local node name is printed.
#
# 4. Finally, in some other circunstances, printing messages may become
# borked. This can be verified with:
#
# $ iex -e ":error_logger.info_msg("foo~nbar", [])"
#
# By the time those instructions have been written, all tests above pass.
defmodule IEx.CLI do
@moduledoc false
@@ -15,15 +51,11 @@ defmodule IEx.CLI do
"""
def start do
if tty_works? do
:user.start_out
:elixir.start_cli
tty
:user_drv.start([:"tty_sl -c -e", tty_args])
else
:user.start
:elixir.start_cli
IO.puts "Warning: could not run smart terminal, falling back to dumb one"
config = [dot_iex_path: find_dot_iex(:init.get_plain_arguments)]
IEx.start(config)
local_start()
end
end
@@ -40,30 +72,55 @@ defmodule IEx.CLI do
end
end
defp tty do
plain_args = :init.get_plain_arguments
config = [dot_iex_path: find_dot_iex(plain_args)]
function = fn -> IEx.start(config) end
args =
if remote = get_remsh(plain_args) do
if is_alive do
case :rpc.call remote, :code, :ensure_loaded, [IEx] do
{ :badrpc, reason } ->
abort "Could not contact remote node #{remote}, reason: #{inspect reason}. Aborting..."
{ :module, IEx } ->
{ remote, :erlang, :apply, [function, []] }
_ ->
abort "Could not find IEx on remote node #{remote}. Aborting..."
end
else
abort "In order to use --remsh, you need to name the current node using --name or --sname. Aborting..."
defp tty_args do
if remote = get_remsh(:init.get_plain_arguments) do
if is_alive do
case :rpc.call remote, :code, :ensure_loaded, [IEx] do
{ :badrpc, reason } ->
abort "Could not contact remote node #{remote}, reason: #{inspect reason}. Aborting..."
{ :module, IEx } ->
{ remote, :erlang, :apply, [remote_start_function, []] }
_ ->
abort "Could not find IEx on remote node #{remote}. Aborting..."
end
else
{ :erlang, :apply, [function, []] }
abort "In order to use --remsh, you need to name the current node using --name or --sname. Aborting..."
end
else
{ :erlang, :apply, [local_start_function, []] }
end
end
:user_drv.start([:"tty_sl -c -e", args])
def local_start do
IEx.start(config(), fn -> :elixir.start_cli end)
end
defp local_start_function do
&local_start/0
end
defp remote_start_function do
ref = make_ref
config = config()
parent = spawn_link fn ->
receive do
{ :begin, ^ref, other } ->
:elixir.start_cli
other <- { :done, ref }
end
end
fn ->
IEx.start(config, fn ->
parent <- { :begin, ref, self }
receive do: ({ :done, ^ref } -> :ok)
end)
end
end
defp config do
[dot_iex_path: find_dot_iex(:init.get_plain_arguments)]
end
defp abort(msg) do
+18 -27
View File
@@ -22,7 +22,6 @@ defmodule IEx.Helpers do
* `ls/1` — lists the contents of the specified directory
* `m/0` — prints loaded modules
* `pwd/0` — prints the current working directory
* `r/0` — recompile and reload all modules that were previously reloaded
* `r/1` — recompiles and reloads the given module's source file
* `s/1` — prints spec information
* `t/1` — prints type information
@@ -62,8 +61,14 @@ defmodule IEx.Helpers do
c "baz.ex"
#=> [Baz]
"""
def c(files, path // ".") do
{ erls, exs } = Enum.partition(List.wrap(files), &String.ends_with?(&1, ".erl"))
def c(files, path // ".") when is_binary(path) do
files = List.wrap(files)
unless Enum.all?(files, &is_binary/1) do
raise ArgumentError, message: "expected a binary or a list of binaries as argument"
end
{ erls, exs } = Enum.partition(files, &String.ends_with?(&1, ".erl"))
modules = Enum.map(erls, fn(source) ->
{ module, binary } = compile_erlang(source)
@@ -83,8 +88,8 @@ defmodule IEx.Helpers do
end
@doc """
Prints the list of all loaded modules with paths to their corresponding .beam
files.
Prints the list of all loaded modules with paths to
their corresponding `.beam` files.
"""
def m do
all = Enum.map :code.all_loaded, fn { mod, file } -> { inspect(mod), file } end
@@ -260,7 +265,7 @@ defmodule IEx.Helpers do
"""
def v do
inspect_opts = IEx.Options.get(:inspect)
IEx.History.each(print_history_entry(&1, inspect_opts))
IEx.History.each(&print_history_entry(&1, inspect_opts))
end
defp print_history_entry(config, inspect_opts) do
@@ -278,21 +283,13 @@ defmodule IEx.Helpers do
IEx.History.nth(n).result
end
@doc """
Reloads all modules that have already been reloaded with `r/1` at any point
in the current IEx session.
"""
def r do
List.flatten(Enum.map(iex_reloaded, do_r(&1)))
end
@doc """
Recompiles and reloads the specified module's source file.
Please note that all the modules defined in the same file as `module`
are recompiled and reloaded.
"""
def r(module) do
def r(module) when is_atom(module) do
case do_r(module) do
mods when is_list(mods) -> { module, mods }
other -> other
@@ -306,11 +303,9 @@ defmodule IEx.Helpers do
:nosource
String.ends_with?(source, ".erl") ->
Process.put(:iex_reloaded, :ordsets.add_element(module, iex_reloaded))
[ compile_erlang(source) |> elem(0) ]
[compile_erlang(source) |> elem(0)]
true ->
Process.put(:iex_reloaded, :ordsets.add_element(module, iex_reloaded))
Enum.map(Code.load_file(source), fn {name, _} -> name end)
end
end
@@ -319,7 +314,7 @@ defmodule IEx.Helpers do
Load the given module's beam code (and ensures any previous
old version was properly purged before).
"""
def l(module) do
def l(module) when is_atom(module) do
:code.purge(module)
:code.load_file(module)
end
@@ -342,10 +337,6 @@ defmodule IEx.Helpers do
end
end
defp iex_reloaded do
Process.get(:iex_reloaded) || :ordsets.new
end
defp source(module) do
source = module.module_info(:compile)[:source]
@@ -365,7 +356,7 @@ defmodule IEx.Helpers do
@doc """
Changes the current working directory to the given path.
"""
def cd(directory) do
def cd(directory) when is_binary(directory) do
case File.cd(expand_home(directory)) do
:ok -> pwd
{ :error, :enoent } ->
@@ -377,7 +368,7 @@ defmodule IEx.Helpers do
Produces a simple list of a directory's contents.
If `path` points to a file, prints its full path.
"""
def ls(path // ".") do
def ls(path // ".") when is_binary(path) do
path = expand_home(path)
case File.ls(path) do
{ :ok, items } ->
@@ -404,7 +395,7 @@ defmodule IEx.Helpers do
defp ls_print(path, list) do
# print items in multiple columns (2 columns in the worst case)
lengths = Enum.map(list, String.length(&1))
lengths = Enum.map(list, &String.length(&1))
maxlen = maxlength(lengths)
width = min(maxlen, 30) + 5
ls_print(path, list, width)
@@ -423,7 +414,7 @@ defmodule IEx.Helpers do
end
defp maxlength(list) do
Enum.reduce(list, 0, max(&1, &2))
Enum.reduce(list, 0, &max(&1, &2))
end
defp format_item(path, representation) do
+14 -7
View File
@@ -1,13 +1,18 @@
defmodule IEx.History do
@moduledoc false
@doc """
Initializes IEx process variables. All history
information is kept in the process dictionary.
"""
def init do
Process.put(:iex_history_start_counter, 1)
Process.put(:iex_history_counter, 1)
end
### append ###
@doc """
Appends one entry to the history with the given counter.
"""
def append(entry, counter) do
Process.put({:iex_history, counter}, entry)
Process.put(:iex_history_counter, counter+1)
@@ -93,8 +98,9 @@ defmodule IEx.History do
:erlang.garbage_collect()
end
### each ###
@doc """
Enumerates each item in the history.
"""
def each(fun) do
each(Process.get(:iex_history_start_counter),
Process.get(:iex_history_counter),
@@ -111,8 +117,9 @@ defmodule IEx.History do
:ok
end
### nth ###
@doc """
Gets the nth item in the history.
"""
def nth(n) do
entry = case n do
n when n >= 0 ->
@@ -122,7 +129,7 @@ defmodule IEx.History do
Process.get({:iex_history, counter + n})
end
if nil?(entry) do
raise "Out of bounds"
raise "v(#{n}) is out of bounds"
end
entry
end
+4 -4
View File
@@ -147,7 +147,7 @@ defmodule IEx.Introspection do
Enum.find docs, fn(doc) ->
case elem(doc, 0) do
{ ^function, max } when max > min ->
defaults = Enum.count elem(doc, 3), match?({ ://, _, _ }, &1)
defaults = Enum.count elem(doc, 3), &match?({ ://, _, _ }, &1)
min + defaults >= max
_ ->
false
@@ -156,7 +156,7 @@ defmodule IEx.Introspection do
end
defp print_doc({ { fun, _ }, _line, kind, args, doc }) do
args = Enum.map_join(args, ", ", print_doc_arg(&1))
args = Enum.map_join(args, ", ", &print_doc_arg(&1))
IO.puts IEx.color(:info, "* #{kind} #{fun}(#{args})\n")
if doc, do: IO.write IEx.color(:info, doc)
end
@@ -252,8 +252,8 @@ defmodule IEx.Introspection do
end
defp beam_specs(module) do
specs = Enum.map(Kernel.Typespec.beam_specs(module), {:spec, &1})
callbacks = Enum.map(Kernel.Typespec.beam_callbacks(module), {:callback, &1})
specs = Enum.map(Kernel.Typespec.beam_specs(module), &{:spec, &1})
callbacks = Enum.map(Kernel.Typespec.beam_callbacks(module), &{:callback, &1})
Enum.concat(specs, callbacks)
end
+102 -94
View File
@@ -4,14 +4,14 @@ defmodule IEx.Server do
@doc """
Eval loop for an IEx session. Its responsibilities include:
* loading of .iex files
* reading input
* trapping exceptions in the code being evaluated
* keeping expression history
* loading of .iex files
* reading input
* trapping exceptions in the code being evaluated
* keeping expression history
"""
def start(config) do
init_systems()
IEx.History.init
{ _, _, scope } = :elixir.eval('require IEx.Helpers', [], 0, config.scope)
config = config.scope(scope)
@@ -26,40 +26,52 @@ defmodule IEx.Server do
old_flag = Process.flag(:trap_exit, true)
self_pid = self
# We have one loop for receiving input and
# another loop for evaluating contents.
pid = spawn_link(fn -> input_loop(self_pid) end)
try do
do_loop(config.input_pid(pid))
eval_loop(config.input_pid(pid))
after
Process.exit(pid, :normal)
Process.flag(:trap_exit, old_flag)
end
end
defp do_loop(config) do
## Eval loop
defp eval_loop(config) do
prefix = config.cache != []
config.input_pid <- { :do_input, prefix, config.counter }
config.input_pid <- { :do_input, self, prefix, config.counter }
wait_input(config)
end
defp wait_input(config) do
pid = config.input_pid
receive do
{ :input, line } ->
unless line == :eof do
new_config =
try do
counter = config.counter
code = config.cache
eval(code, line, counter, config)
catch
kind, error ->
print_error(kind, Exception.normalize(kind, error), System.stacktrace)
config.cache('')
end
do_loop(new_config)
end
{ :input, ^pid, data } when is_binary(data) ->
new_config =
try do
line = String.to_char_list!(data)
counter = config.counter
code = config.cache
eval(code, line, counter, config)
catch
kind, error ->
print_error(kind, Exception.normalize(kind, error), System.stacktrace)
config.cache('')
end
eval_loop(new_config)
{ :input, ^pid, :eof } ->
:ok
{ :input, ^pid, { :error, :interrupted } } ->
io_error "** (EXIT) interrupted"
eval_loop(config.cache(''))
{ :input, ^pid, { :error, :terminated } } ->
:ok
{ :EXIT, _pid, :normal } ->
wait_input(config)
{ :EXIT, pid, reason } ->
@@ -68,16 +80,6 @@ defmodule IEx.Server do
end
end
defp init_systems() do
IEx.History.init
# Disable ANSI-escape-sequence-based coloring on Windows
# Can be overriden in .iex
if match?({ :win32, _ }, :os.type()) do
IEx.Options.set :colors, enabled: false
end
end
# Instead of doing just `:elixir.eval`, we first parse the expression to see
# if it's well formed. If parsing succeeds, we evaluate the AST as usual.
#
@@ -114,7 +116,7 @@ defmodule IEx.Server do
config = config.cache(code).scope(nil).result(result)
update_history(config)
config.update_counter(&1+1).cache('').binding(new_binding).scope(scope).result(nil)
config.update_counter(&(&1+1)).cache('').binding(new_binding).scope(scope).result(nil)
{ :error, { line_no, error, token } } ->
if token == [] do
@@ -128,70 +130,10 @@ defmodule IEx.Server do
end
end
# Locates and loads an .iex file from one of predefined locations. Returns
# new config.
defp load_dot_iex(config, path // nil) do
candidates = if path do
[path]
else
Enum.map [".iex", "~/.iex"], Path.expand(&1)
end
path = Enum.find candidates, fn path -> File.regular?(path) end
if nil?(path) do
config
else
try do
code = File.read!(path)
scope = :elixir.scope_for_eval(config.scope, file: path)
# Evaluate the contents in the same environment do_loop will run in
{ _result, binding, scope } =
:elixir.eval(String.to_char_list!(code),
config.binding,
0,
scope)
scope = :elixir.scope_for_eval(scope, file: "iex")
config.binding(binding).scope(scope)
catch
kind, error ->
print_error(kind, Exception.normalize(kind, error), System.stacktrace)
System.halt(1)
end
end
end
defp update_history(config) do
IEx.History.append(config, config.counter)
end
defp input_loop(iex_pid) do
receive do
{ :do_input, prefix, counter } ->
iex_pid <- { :input, io_get(prefix, counter) }
end
input_loop(iex_pid)
end
defp io_get(prefix, counter) do
prefix = if prefix, do: "..."
prompt =
if is_alive do
"#{prefix || remote_prefix}(#{node})#{counter}> "
else
"#{prefix || "iex"}(#{counter})> "
end
case IO.gets(:stdio, prompt) do
:eof -> :eof
{ :error, _ } -> ''
data -> String.to_char_list!(data)
end
end
defp io_put(result) do
IO.puts :stdio, IEx.color(:eval_result, inspect(result, inspect_opts))
end
@@ -208,10 +150,76 @@ defmodule IEx.Server do
end
end
## Load dot iex helpers
# Locates and loads an .iex file from one of predefined locations. Returns
# new config.
defp load_dot_iex(config, path // nil) do
candidates = if path do
[path]
else
Enum.map [".iex", "~/.iex"], &Path.expand/1
end
path = Enum.find candidates, &File.regular?/1
if nil?(path) do
config
else
eval_dot_iex(config, path)
end
end
defp eval_dot_iex(config, path) do
try do
code = File.read!(path)
scope = :elixir.scope_for_eval(config.scope, file: path)
# Evaluate the contents in the same environment eval_loop will run in
{ _result, binding, scope } =
:elixir.eval(String.to_char_list!(code),
config.binding,
0,
scope)
scope = :elixir.scope_for_eval(scope, file: "iex")
config.binding(binding).scope(scope)
catch
kind, error ->
print_error(kind, Exception.normalize(kind, error), System.stacktrace)
System.halt(1)
end
end
## Input loop
defp input_loop(pid) do
receive do
{ :do_input, ^pid, prefix, counter } ->
pid <- { :input, self, io_get(prefix, counter) }
end
input_loop(pid)
end
defp io_get(prefix, counter) do
prefix = if prefix, do: "..."
prompt =
if is_alive do
"#{prefix || remote_prefix}(#{node})#{counter}> "
else
"#{prefix || "iex"}(#{counter})> "
end
IO.gets(:stdio, prompt)
end
defp remote_prefix do
if node == node(:erlang.group_leader), do: "iex", else: "rem"
end
## Error handling
defp print_error(:error, exception, stacktrace) do
print_stacktrace stacktrace, fn ->
"** (#{inspect exception.__record__(:name)}) #{exception.message}"
+6 -14
View File
@@ -112,7 +112,7 @@ defmodule IEx.HelpersTest do
:ok
"""
assert "** (RuntimeError) Out of bounds" <> _
assert "** (RuntimeError) v(0) is out of bounds" <> _
= capture_iex("v(0)")
assert capture_iex("1\n2\nv(2)") == "1\n2\n2"
assert capture_iex("1\n2\nv(2)") == capture_iex("1\n2\nv(-1)")
@@ -133,7 +133,7 @@ defmodule IEx.HelpersTest do
assert ["ebin", "lib", "mix.exs", "test"]
= capture_io(fn -> ls end)
|> String.split
|> Enum.map(String.strip(&1))
|> Enum.map(&String.strip(&1))
|> Enum.sort
assert capture_io(fn -> ls "~" end) == capture_io(fn -> ls System.user_home end)
end
@@ -266,20 +266,13 @@ defmodule IEx.HelpersTest do
cleanup_modules([Sample])
end
test "r helper basic" do
assert r == []
test "r helper unavailable" do
assert_raise UndefinedFunctionError, "undefined function: :non_existent_module.module_info/1", fn ->
r :non_existent_module
end
# There is no source file for the module defined in IEx
assert ":ok\n** (Code.LoadError) could not load" <> _
= capture_iex("{:module, Sample, _, {:run,0}} = #{String.strip test_module_code}; :ok\nr Sample")
after
cleanup_modules([Sample])
end
test "r helper" do
test "r helper elixir" do
assert_raise UndefinedFunctionError, "undefined function: Sample.run/0", fn ->
Sample.run
end
@@ -296,8 +289,7 @@ defmodule IEx.HelpersTest do
Sample.run
end
assert [Sample] = r()
end) =~ %r"^.*?sample\.ex:1: redefining module Sample\n.*?sample\.ex:1: redefining module Sample\n$"
end) =~ %r"^.*?sample\.ex:1: redefining module Sample\n$"
end
after
# Clean up old version produced by the r helper
@@ -376,7 +368,7 @@ defmodule IEx.HelpersTest do
try do
fun.()
after
Enum.each names, File.rm(&1)
Enum.each names, &File.rm/1
end
end
+2 -2
View File
@@ -29,11 +29,11 @@ defmodule IEx.InteractionTest do
end
test "empty history at the start" do
assert "** (RuntimeError) Out of bounds" <> _ = capture_iex("v(-1)")
assert "** (RuntimeError) v(-1) is out of bounds" <> _ = capture_iex("v(-1)")
end
test "empty history at the start redux" do
assert "** (RuntimeError) Out of bounds" <> _ = capture_iex("v(1)")
assert "** (RuntimeError) v(1) is out of bounds" <> _ = capture_iex("v(1)")
end
test "no break" do
+3 -3
View File
@@ -45,9 +45,9 @@ defmodule IEx.OptionsTest do
test "history size" do
opts = [history_size: 3]
assert capture_iex("1\n2\n3\nv(1)", opts) == "1\n2\n3\n1"
assert "1\n2\n3\n4\n** (RuntimeError) Out of bounds" <> _ = capture_iex("1\n2\n3\n4\nv(1)", opts)
assert "1\n2\n3\n4\n** (RuntimeError) Out of bounds" <> _ = capture_iex("1\n2\n3\n4\nv(-4)", opts)
assert "1\n2\n3\n4\n2\n** (RuntimeError) Out of bounds" <> _ = capture_iex("1\n2\n3\n4\nv(2)\nv(2)", opts)
assert "1\n2\n3\n4\n** (RuntimeError) v(1) is out of bounds" <> _ = capture_iex("1\n2\n3\n4\nv(1)", opts)
assert "1\n2\n3\n4\n** (RuntimeError) v(-4) is out of bounds" <> _ = capture_iex("1\n2\n3\n4\nv(-4)", opts)
assert "1\n2\n3\n4\n2\n** (RuntimeError) v(2) is out of bounds" <> _ = capture_iex("1\n2\n3\n4\nv(2)\nv(2)", opts)
end
test "bad option" do
+6
View File
@@ -25,6 +25,12 @@ defmodule Mix.CLI do
args
end
defp get_task(["-" <> _|_]) do
Mix.shell.error "** (Mix) Cannot implicitly pass flags to default mix task, " <>
"please invoke instead: mix #{Mix.project[:default_task]}"
exit(1)
end
defp get_task([h|t]) do
{ h, t }
end
+12 -9
View File
@@ -29,7 +29,7 @@ defmodule Mix.Deps do
{ app :: atom, requirement :: String.t, opts :: Keyword.t }
The application name must be an atom, the version requirement must
be a string according to the specification outline in `Mix.Version`
be a string according to the specification outline in `Version`
and opts is a keyword lists that may include options for the underlying
SCM or options used by Mix. Each set of options is documented below.
@@ -105,22 +105,25 @@ defmodule Mix.Deps do
defdelegate children(), to: Mix.Deps.Retriever
@doc """
Return all dependencies depending on the given dependencies.
Return all given dependencies and their depending dependencies.
"""
def depending(deps, all_deps // all)
def with_depending(deps, all_deps // all) do
deps ++ do_with_depending(deps, all_deps)
|> Enum.uniq(&(&1.app))
end
def depending([], _all_deps) do
defp do_with_depending([], _all_deps) do
[]
end
def depending(deps, all_deps) do
defp do_with_depending(deps, all_deps) do
dep_names = Enum.map(deps, fn dep -> dep.app end)
parents = Enum.filter all_deps, fn dep ->
Enum.any?(dep.deps, fn child_dep -> child_dep.app in dep_names end)
end
parents ++ depending(parents, all_deps)
do_with_depending(parents, all_deps) ++ parents
end
@doc """
@@ -321,10 +324,10 @@ defmodule Mix.Deps do
end)
[ opts[:dest] | sub_dirs ]
|> Enum.map(Path.wildcard(&1))
|> Enum.map(&Path.wildcard(&1))
|> Enum.concat
|> Enum.map(Path.join(&1, "ebin"))
|> Enum.filter(File.dir?(&1))
|> Enum.map(&Path.join(&1, "ebin"))
|> Enum.filter(&File.dir?(&1))
end
def load_paths(Mix.Dep[manager: manager, opts: opts]) when manager in [:make, nil] do
+17 -12
View File
@@ -83,7 +83,9 @@ defmodule Mix.Deps.Converger do
# Look for an overriding dep in the upper breadths, if
# found return a new acc without the overriden dep and
# with the proper status set on the overrider
# with the proper status set on the overrider. The
# overrider is moved to the front of the accumulator to
# preserve the position of the removed dep.
defp overriden_deps(acc, upper_breadths, dep) do
overriden = Enum.any?(upper_breadths, fn(other) ->
other.app == dep.app
@@ -92,18 +94,21 @@ defmodule Mix.Deps.Converger do
if overriden do
Mix.Dep[app: app] = dep
Enum.map(acc, fn other ->
Mix.Dep[app: other_app, opts: other_opts] = other
{ overrider, acc } =
Enum.reduce(acc, { nil , [] }, fn other, { overrider, acc } ->
Mix.Dep[app: other_app, opts: other_opts] = other
cond do
app == other_app && (other_opts[:override] || converge?(dep, other)) ->
other
app == other_app ->
other.status({ :overriden, dep })
true ->
other
end
end)
cond do
app == other_app && (other_opts[:override] || converge?(dep, other)) ->
{ other, acc }
app == other_app ->
{ other.status({ :overriden, dep }), acc }
true ->
{ overrider, [other|acc] }
end
end)
[ overrider | Enum.reverse(acc) ]
end
end
+4 -4
View File
@@ -11,7 +11,7 @@ defmodule Mix.Deps.Retriever do
def children() do
scms = Mix.SCM.available
from = current_source(:mix)
Enum.map(Mix.project[:deps], update(&1, scms, from))
Enum.map(Mix.project[:deps], &update(&1, scms, from))
end
@doc """
@@ -52,7 +52,7 @@ defmodule Mix.Deps.Retriever do
# The manager must be nil because mix supports mix,
# rebar and make dependencies/managers.
(Mix.project[:deps] || []) |> Enum.map(update(&1, scms, from))
(Mix.project[:deps] || []) |> Enum.map(&update(&1, scms, from))
end) |> Enum.concat
end
@@ -62,7 +62,7 @@ defmodule Mix.Deps.Retriever do
from = current_source(:rebar)
# Rebar dependencies are always managed by rebar.
Mix.Rebar.deps(config) |> Enum.map(update(&1, scms, from, :rebar))
Mix.Rebar.deps(config) |> Enum.map(&update(&1, scms, from, :rebar))
end) |> Enum.concat
end
@@ -208,7 +208,7 @@ defmodule Mix.Deps.Retriever do
defp vsn_match?(nil, _actual), do: true
defp vsn_match?(req, actual) when is_regex(req), do: actual =~ req
defp vsn_match?(req, actual) when is_binary(req) do
Mix.Version.match?(actual, req)
Version.match?(actual, req)
end
defp mixfile?(dep) do
+3 -3
View File
@@ -12,14 +12,14 @@ defmodule Mix.Local do
Append archives paths into Erlang code path.
"""
def append_archives do
Enum.each(archives_ebin, Code.append_path(&1))
Enum.each(archives_ebin, &Code.append_path(&1))
end
@doc """
Append mix paths into Erlang code path.
"""
def append_paths do
Enum.each(Mix.Utils.mix_paths, Code.append_path(&1))
Enum.each(Mix.Utils.mix_paths, &Code.append_path(&1))
end
@doc """
@@ -28,7 +28,7 @@ defmodule Mix.Local do
def all_tasks, do: Mix.Task.load_tasks(archives_ebin)
defp archives_ebin do
Path.join(archives_path, "*.ez") |> Path.wildcard |> Enum.map(archive_ebin(&1))
Path.join(archives_path, "*.ez") |> Path.wildcard |> Enum.map(&archive_ebin(&1))
end
defp archive_ebin(archive_file) do
+25 -15
View File
@@ -141,27 +141,37 @@ defmodule Mix.Project do
def recur(post_config // [], fun) do
if apps_path = config[:apps_path] do
paths = Path.wildcard(Path.join(apps_path, "*"))
paths = Enum.filter(paths, File.dir?(&1))
projects = Enum.map paths, fn path ->
dir = Path.basename(path)
app = dir |> String.downcase |> binary_to_atom
{ app, path }
end
projects = topsort_projects(projects, Path.expand(apps_path))
results = Enum.map projects, fn { app, app_path } ->
in_project(app, app_path, post_config, fun)
end
results
paths
|> Enum.filter(&File.dir?(&1))
|> extract_projects
|> filter_projects(config[:apps])
|> topsort_projects(Path.expand(apps_path))
|> recur_projects(post_config, fun)
else
# Note that post_config isnt used for this case
[fun.(get)]
end
end
defp extract_projects(paths) do
lc path inlist paths do
app = path |> Path.basename |> String.downcase |> binary_to_atom
{ app, path }
end
end
defp filter_projects(pairs, nil), do: pairs
defp filter_projects(pairs, apps) when is_list(apps) do
lc { app, _ } = pair inlist pairs, app in apps, do: pair
end
defp recur_projects(pairs, post_config, fun) do
lc { app, path } inlist pairs do
in_project(app, path, post_config, fun)
end
end
@doc """
Runs the given `fun` inside the given project by changing
the current working directory and loading the given project
@@ -200,7 +210,7 @@ defmodule Mix.Project do
def load_paths do
paths =
recur(fn _ ->
Enum.map(config[:load_paths], Path.expand(&1))
Enum.map(config[:load_paths], &Path.expand(&1))
end) |> Enum.concat
paths ++ compile_paths
end
+3 -3
View File
@@ -63,7 +63,7 @@ defmodule Mix.Rebar do
def deps(config) do
if deps = config[:deps] do
deps_dir = config[:deps_dir] || "deps"
Enum.map(deps, parse_dep(&1, deps_dir))
Enum.map(deps, &parse_dep(&1, deps_dir))
else
[]
end
@@ -78,9 +78,9 @@ defmodule Mix.Rebar do
if sub_dirs = config[:sub_dirs] do
sub_dirs = sub_dirs
|> Enum.map(Path.wildcard(&1))
|> Enum.map(&Path.wildcard(&1))
|> Enum.concat
|> Enum.filter(File.dir?(&1))
|> Enum.filter(&File.dir?(&1))
Enum.map(sub_dirs, fn(dir) ->
recur(dir, fun)
+1 -1
View File
@@ -94,7 +94,7 @@ defmodule Mix.SCM.Git do
defp get_lock_rev(_), do: nil
defp get_lock_opts(opts) do
lock_opts = Enum.find_value [:branch, :ref, :tag], List.keyfind(opts, &1, 0)
lock_opts = Enum.find_value [:branch, :ref, :tag], &List.keyfind(opts, &1, 0)
lock_opts = List.wrap(lock_opts)
if opts[:submodules] do
lock_opts ++ [submodules: true]
+1 -5
View File
@@ -14,11 +14,7 @@ defmodule Mix.SCM.Path do
cond do
raw = opts[:path] ->
Keyword.put opts, :dest, Path.expand(raw)
opts[:umbrella] || opts[:in_umbrella] ->
if opts[:umbrella] do
Mix.shell.info ":umbrella option in path dependency is deprecated in favor of :in_umbrella"
end
opts[:in_umbrella] ->
path = "../#{app}"
opts
+11 -13
View File
@@ -1,11 +1,9 @@
defmodule Mix.Server do
@moduledoc false
alias :ordsets, as: Ordset
use GenServer.Behaviour
defrecord Config, tasks: Ordset.new, projects: [], mixfile: [],
shell: Mix.Shell.IO, scm: Ordset.new, env: nil, post_config: []
defrecord Config, tasks: :ordsets.new, projects: [], mixfile: [],
shell: Mix.Shell.IO, scm: :ordsets.new, env: nil, post_config: []
defrecord Project, name: nil, config: nil, rec_enabled?: true, io_done: false
@@ -48,11 +46,11 @@ defmodule Mix.Server do
end
def handle_call(:clear_tasks, _from, config) do
{ :reply, config.tasks, config.tasks(Ordset.new) }
{ :reply, config.tasks, config.tasks(:ordsets.new) }
end
def handle_call({ :has_task?, task, app }, _from, config) do
{ :reply, Ordset.is_element({task, app}, config.tasks), config }
{ :reply, :ordsets.is_element({task, app}, config.tasks), config }
end
def handle_call(:pop_project, _from, config) do
@@ -102,35 +100,35 @@ defmodule Mix.Server do
end
def handle_cast({ :add_task, task, app }, config) do
{ :noreply, config.update_tasks Ordset.add_element({task, app}, &1) }
{ :noreply, config.update_tasks &:ordsets.add_element({task, app}, &1) }
end
def handle_cast({ :delete_task, task, app }, config) do
{ :noreply, config.update_tasks Ordset.del_element({task, app}, &1) }
{ :noreply, config.update_tasks &:ordsets.del_element({task, app}, &1) }
end
def handle_cast({ :delete_task, task }, config) do
{ :noreply, config.update_tasks Ordset.filter(fn {t, _} -> t != task end, &1) }
{ :noreply, config.update_tasks &:ordsets.filter(fn {t, _} -> t != task end, &1) }
end
def handle_cast({ :push_project, name, conf }, config) do
conf = Keyword.merge(conf, config.post_config)
project = Project[name: name, config: conf]
config = config.post_config([])
.update_projects([project|&1])
.update_projects(&[project|&1])
{ :noreply, config }
end
def handle_cast({ :post_config, value }, config) do
{ :noreply, config.update_post_config(&1 |> Keyword.merge(value)) }
{ :noreply, config.update_post_config(&Keyword.merge(&1, value)) }
end
def handle_cast({ :add_scm, mod }, config) do
{ :noreply, config.update_scm Ordset.add_element(mod, &1) }
{ :noreply, config.update_scm &:ordsets.add_element(mod, &1) }
end
def handle_cast({ :mixfile_cache, app, new }, config) do
{ :noreply, config.update_mixfile(&1 |> Keyword.merge([{ app, new }])) }
{ :noreply, config.update_mixfile(&Keyword.merge(&1, [{ app, new }])) }
end
def handle_cast(:clear_mixfile_cache, config) do
+1 -1
View File
@@ -12,7 +12,7 @@ defmodule Mix.Shell.IO do
"""
def cmd(command) do
put_app
Mix.Shell.cmd(command, IO.write(&1))
Mix.Shell.cmd(command, &IO.write(&1))
end
@doc """
+1 -1
View File
@@ -23,7 +23,7 @@ defmodule Mix.Shell.Process do
## Examples
flush IO.inspect(&1)
flush &IO.inspect(&1)
"""
def flush(callback // fn(x) -> x end) do
+2 -2
View File
@@ -17,7 +17,7 @@ defmodule Mix.Task do
defmacro __using__(_opts) do
quote do
Enum.each [:shortdoc, :hidden, :recursive],
Module.register_attribute(__MODULE__, &1, persist: true)
&Module.register_attribute(__MODULE__, &1, persist: true)
@behaviour Mix.Task
end
@@ -34,7 +34,7 @@ defmodule Mix.Task do
def load_tasks(paths) do
Enum.reduce(paths, [], fn(path, matches) ->
{ :ok, files } = :erl_prim_loader.list_dir(path |> to_char_list)
Enum.reduce(files, matches, match_tasks(&1, &2))
Enum.reduce(files, matches, &match_tasks/2)
end)
end
+1 -1
View File
@@ -18,7 +18,7 @@ defmodule Mix.Tasks.Clean do
manifests = Mix.Tasks.Compile.manifests
Enum.each(manifests, fn(manifest) ->
Enum.each Mix.Utils.read_manifest(manifest),
&1 |> String.split("\t") |> hd |> File.rm
&(&1 |> String.split("\t") |> hd |> File.rm)
File.rm(manifest)
end)

Some files were not shown because too many files have changed in this diff Show More