Skip to content

Commit cdbfb0c

Browse files
committed
Merge branch 'master' into mji/exceptions
2 parents bf29ba4 + 4af2b1a commit cdbfb0c

16 files changed

+580
-279
lines changed

.gitignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,16 @@
11
*.jl.cov
2+
*.jl.*.cov
23
*.jl.mem
4+
5+
.DS_Store
6+
7+
docs/build/
8+
docs/site/
9+
10+
*.ipynb_checkpoints
11+
**/*.ipynb_checkpoints
12+
**/**/*.ipynb_checkpoints
13+
14+
_*.dat
15+
*.swp
16+
__pycache__/

.travis.yml

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
language: julia
22
os:
33
- linux
4-
- osx
54
julia:
6-
- 0.6
5+
- 1.0
76
- nightly
87
matrix:
98
allow_failures:
109
- julia: nightly
1110
notifications:
1211
email: false
13-
# uncomment the following lines to override the default test script
14-
#script:
15-
# - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
16-
# - julia --check-bounds=yes -e 'Pkg.clone(pwd()); Pkg.build("MacroTools"); Pkg.test("MacroTools"; coverage=true)'
12+
13+
jobs:
14+
include:
15+
- stage: "Documentation"
16+
julia: 1.0
17+
os: linux
18+
script:
19+
- julia --project=docs/ -e 'using Pkg; Pkg.instantiate(); Pkg.add(PackageSpec(path=pwd()))'
20+
- julia --project=docs/ docs/make.jl
21+
after_success: skip
22+
23+
# uncomment this to enable test coverage
24+
# after_success:
25+
# - julia -e 'import Pkg; cd(Pkg.dir("Luxor")); Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder())'

Manifest.toml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# This file is machine-generated - editing it directly is not advised
2+
3+
[[Base64]]
4+
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
5+
6+
[[DataStructures]]
7+
deps = ["InteractiveUtils", "OrderedCollections"]
8+
git-tree-sha1 = "1fe8fad5fc84686dcbc674aa255bc867a64f8132"
9+
uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
10+
version = "0.17.5"
11+
12+
[[Distributed]]
13+
deps = ["Random", "Serialization", "Sockets"]
14+
uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"
15+
16+
[[InteractiveUtils]]
17+
deps = ["Markdown"]
18+
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
19+
20+
[[Logging]]
21+
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
22+
23+
[[Markdown]]
24+
deps = ["Base64"]
25+
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
26+
27+
[[OrderedCollections]]
28+
deps = ["Random", "Serialization", "Test"]
29+
git-tree-sha1 = "c4c13474d23c60d20a67b217f1d7f22a40edf8f1"
30+
uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
31+
version = "1.1.0"
32+
33+
[[Random]]
34+
deps = ["Serialization"]
35+
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
36+
37+
[[Serialization]]
38+
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
39+
40+
[[Sockets]]
41+
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"
42+
43+
[[Test]]
44+
deps = ["Distributed", "InteractiveUtils", "Logging", "Random"]
45+
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

Project.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name = "MacroTools"
2+
uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
3+
version = "0.5.4"
4+
5+
[deps]
6+
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
7+
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
8+
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
9+
10+
[compat]
11+
DataStructures = "0.17"
12+
julia = "1"
13+
14+
[extras]
15+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
16+
17+
[targets]
18+
test = ["Test"]

README.md

Lines changed: 3 additions & 241 deletions
Original file line numberDiff line numberDiff line change
@@ -1,244 +1,6 @@
11
# MacroTools.jl
22

3-
This library provides helpful tools for writing macros, notably a very simple
4-
but powerful templating system and some functions that have proven useful to me (see
5-
[utils.jl](src/utils.jl).)
3+
[![Build Status](https://travis-ci.org/MikeInnes/MacroTools.jl.svg?branch=master)](https://travis-ci.org/MikeInnes/MacroTools.jl)
4+
[![](https://img.shields.io/badge/docs-stable-blue.svg)](https://mikeinnes.github.io/MacroTools.jl/stable)
65

7-
## Template Matching
8-
9-
Template matching enables macro writers to deconstruct Julia
10-
expressions in a more declarative way, and without having to know in
11-
great detail how syntax is represented internally. For example, say you
12-
have a type definition:
13-
14-
```julia
15-
ex = quote
16-
struct Foo
17-
x::Int
18-
y
19-
end
20-
end
21-
```
22-
23-
If you know what you're doing, you can pull out the name and fields via:
24-
25-
```julia
26-
julia> if isexpr(ex.args[2], :struct)
27-
(ex.args[2].args[2], ex.args[2].args[3].args)
28-
end
29-
(:Foo,{:( # line 3:),:(x::Int),:( # line 4:),:y})
30-
```
31-
32-
But this is hard to write – since you have to deconstruct the `type`
33-
expression by hand – and hard to read, since you can't tell at a glance
34-
what's being achieved. On top of that, there's a bunch of messy stuff to
35-
deal with like pesky `begin` blocks which wrap a single expression, line
36-
numbers, etc. etc.
37-
38-
Enter MacroTools:
39-
40-
```julia
41-
julia> using MacroTools
42-
43-
julia> @capture(ex, struct T_ fields__ end)
44-
true
45-
46-
julia> T, fields
47-
(:Foo, [:(x::Int), :y])
48-
```
49-
50-
Symbols like `T_` underscore are treated as catchalls which match any
51-
expression, and the expression they match is bound to the
52-
(underscore-less) variable, as above.
53-
54-
Because `@capture` doubles as a test as well as extracting values, you can
55-
easily handle unexpected input (try writing this by hand):
56-
57-
```julia
58-
@capture(ex, f_{T_}(xs__) = body_) ||
59-
error("expected a function with a single type parameter")
60-
```
61-
62-
Symbols like `f__` (double underscored) are similar, but slurp a sequence of
63-
arguments into an array. For example:
64-
65-
```julia
66-
julia> @capture(:[1, 2, 3, 4, 5, 6, 7], [1, a_, 3, b__, c_])
67-
true
68-
69-
julia> a, b, c
70-
(2,[4,5,6],7)
71-
```
72-
73-
Slurps don't have to be at the end of an expression, but like the
74-
Highlander there can only be one (per expression).
75-
76-
### Matching on expression type
77-
78-
`@capture` can match expressions by their type, which is either the `head` of `Expr`
79-
objects or the `typeof` atomic stuff like `Symbol`s and `Int`s. For example:
80-
81-
```julia
82-
@capture(ex, foo(x_String_string))
83-
```
84-
85-
This will match a call to the `foo` function which has a single argument, which
86-
may either be a `String` object or a `Expr(:string, ...)`
87-
(e.g. `@capture(:(foo("$(a)")), foo(x_String_string))`). Julia string literals
88-
may be parsed into either type of object, so this is a handy way to catch both.
89-
90-
Another common use case is to catch symbol literals, e.g.
91-
92-
```julia
93-
@capture(ex,
94-
struct T_Symbol
95-
fields__
96-
end)
97-
```
98-
99-
which will match e.g. `struct Foo ...` but not `struct Foo{V} ...`
100-
101-
### Unions
102-
103-
`@capture` can also try to match the expression against one pattern or another,
104-
for example:
105-
106-
```julia
107-
@capture(ex, f_(args__) = body_ | function f_(args__) body_ end)
108-
```
109-
110-
will match both kinds of function syntax (though it's easier to use
111-
`shortdef` to normalise definitions). You can also do this within
112-
expressions, e.g.
113-
114-
```julia
115-
@capture(ex, (f_{T_}|f_)(args__) = body_)
116-
```
117-
118-
matches a function definition, with a single type parameter bound to `T` if possible.
119-
If not, `T = nothing`.
120-
121-
## Expression Walking
122-
123-
If you've ever written any more interesting macros, you've probably found
124-
yourself writing recursive functions to work with nested `Expr` trees.
125-
MacroTools' `prewalk` and `postwalk` functions factor out the recursion, making
126-
macro code much more concise and robust.
127-
128-
These expression-walking functions essentially provide a kind of
129-
find-and-replace for expression trees. For example:
130-
131-
```julia
132-
julia> using MacroTools: prewalk, postwalk
133-
134-
julia> postwalk(x -> x isa Integer ? x + 1 : x, :(2+3))
135-
:(3 + 4)
136-
```
137-
138-
In other words, look at each item in the tree; if it's an integer, add one, if not, leave it alone.
139-
140-
We can do more complex things if we combine this with `@capture`. For example, say we want to insert an extra argument into all function calls:
141-
142-
```julia
143-
julia> ex = quote
144-
x = f(y, g(z))
145-
return h(x)
146-
end
147-
148-
julia> postwalk(x -> @capture(x, f_(xs__)) ? :($f(5, $(xs...))) : x, ex)
149-
quote # REPL[20], line 2:
150-
x = f(5, y, g(5, z)) # REPL[20], line 3:
151-
return h(5, x)
152-
end
153-
```
154-
155-
Most of the time, you can use `postwalk` without worrying about it, but we also
156-
provide `prewalk`. The difference is the order in which you see sub-expressions;
157-
`postwalk` sees the leaves of the `Expr` tree first and the whole expression
158-
last, while `prewalk` is the opposite.
159-
160-
```julia
161-
julia> postwalk(x -> @show(x) isa Integer ? x + 1 : x, :(2+3*4));
162-
x = :+
163-
x = 2
164-
x = :*
165-
x = 3
166-
x = 4
167-
x = :(4 * 5)
168-
x = :(3 + 4 * 5)
169-
170-
julia> prewalk(x -> @show(x) isa Integer ? x + 1 : x, :(2+3*4));
171-
x = :(2 + 3 * 4)
172-
x = :+
173-
x = 2
174-
x = :(3 * 4)
175-
x = :*
176-
x = 3
177-
x = 4
178-
```
179-
180-
A significant difference is that `prewalk` will walk into whatever expression
181-
you return.
182-
183-
```julia
184-
julia> postwalk(x -> @show(x) isa Integer ? :(a+b) : x, 2)
185-
x = 2
186-
:(a + b)
187-
188-
julia> prewalk(x -> @show(x) isa Integer ? :(a+b) : x, 2)
189-
x = 2
190-
x = :+
191-
x = :a
192-
x = :b
193-
:(a + b)
194-
```
195-
196-
This makes it somewhat more prone to infinite loops; for example, if we returned
197-
`:(1+b)` instead of `:(a+b)`, `prewalk` would hang trying to expand all of the
198-
`1`s in the expression.
199-
200-
With these tools in hand, a useful general pattern for macros is:
201-
202-
```julia
203-
macro foo(ex)
204-
postwalk(ex) do x
205-
@capture(x, some_pattern) || return x
206-
return new_x
207-
end
208-
end
209-
```
210-
211-
## Function definitions
212-
213-
`splitdef(def)` matches a function definition of the form
214-
215-
```julia
216-
function name{params}(args; kwargs)::rtype where {whereparams}
217-
body
218-
end
219-
```
220-
221-
and returns `Dict(:name=>..., :args=>..., etc.)`. The definition can be rebuilt by
222-
calling `MacroTools.combinedef(dict)`, or explicitly with
223-
224-
```julia
225-
rtype = get(dict, :rtype, :Any)
226-
all_params = [get(dict, :params, [])..., get(dict, :whereparams, [])...]
227-
:(function $(dict[:name]){$(all_params...)}($(dict[:args]...);
228-
$(dict[:kwargs]...))::$rtype
229-
$(dict[:body])
230-
end)
231-
```
232-
233-
`splitarg(arg)` matches function arguments (whether from a definition or a function call)
234-
such as `x::Int=2` and returns `(arg_name, arg_type, slurp, default)`. `default` is
235-
`nothing` when there is none. For example:
236-
237-
```julia
238-
> map(splitarg, (:(f(y, a=2, x::Int=nothing, args...))).args[2:end])
239-
4-element Array{Tuple{Symbol,Symbol,Bool,Any},1}:
240-
(:y, :Any, false, nothing)
241-
(:a, :Any, false, 2)
242-
(:x, :Int, false, :nothing)
243-
(:args, :Any, true, nothing)
244-
```
6+
MacroTools provides a library of tools for working with Julia code and expressions. This includes a powerful template-matching system and code-walking tools that let you do deep transformations of code in a few lines. See the [docs](http://mikeinnes.github.io/MacroTools.jl/) for more info.

REQUIRE

Lines changed: 0 additions & 2 deletions
This file was deleted.

0 commit comments

Comments
 (0)