Skip to content

Commit f205b26

Browse files
authored
Update README.md
1 parent 7691d16 commit f205b26

File tree

1 file changed

+1
-242
lines changed

1 file changed

+1
-242
lines changed

README.md

Lines changed: 1 addition & 242 deletions
Original file line numberDiff line numberDiff line change
@@ -2,245 +2,4 @@
22

33
[![Build Status](https://travis-ci.org/MikeInnes/MacroTools.jl.svg?branch=master)](https://travis-ci.org/MikeInnes/MacroTools.jl)
44

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

0 commit comments

Comments
 (0)