Skip to content

Commit f7665cf

Browse files
committed
up tp Chapter 2 - Compile-time hooks
1 parent a6503f4 commit f7665cf

File tree

11 files changed

+559
-12
lines changed

11 files changed

+559
-12
lines changed

README.md

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,26 @@ Moreover, don't expect direct quotes, changing the sentences vocbulary helps me
1616
<details>
1717
<summary>Chapter 1 - The Language of Macros</summary>
1818

19-
- [What are Macros?](chapters/chapter1.md/#what-are-macros)
20-
- [The Abstract Syntax Tree](#the-abstract-syntax-tree)
21-
- [Trying It All Together](#trying-it-all-together)
22-
- [Macro Rules](#macro-rules)
23-
- [The Abstract Syntax Tree - Demystified](#the-abstract-syntax-tree-demystified)
24-
- [High-Level Syntax vs. Low-level AST](#high-level-syntax-vs-low-level-ast)
25-
- [AST Literals](#ast-literals)
26-
- [Macros: The Building Blocks of Elixir](#macros-the-building-blocks-of-elixir)
27-
- [Macro Expansion](#macro-expansion)
28-
- [Code Injection and the Caller's Context](#code-injection-and-the-callers-context)
19+
- [What are Macros?](chapters/chapter1.md#what-are-macros)
20+
- [The Abstract Syntax Tree](chapters/chapter1.md#the-abstract-syntax-tree)
21+
- [Trying It All Together](chapters/chapter1.md#trying-it-all-together)
22+
- [Macro Rules](chapters/chapter1.md#macro-rules)
23+
- [The Abstract Syntax Tree - Demystified](chapters/chapter1.md#the-abstract-syntax-tree-demystified)
24+
- [High-Level Syntax vs. Low-level AST](chapters/chapter1.md#high-level-syntax-vs-low-level-ast)
25+
- [AST Literals](chapters/chapter1.md#ast-literals)
26+
- [Macros: The Building Blocks of Elixir](chapters/chapter1.md#macros-the-building-blocks-of-elixir)
27+
- [Macro Expansion](chapters/chapter1.md#macro-expansion)
28+
- [Code Injection and the Caller's Context](chapters/chapter1.md#code-injection-and-the-callers-context)
2929

3030
</details>
3131

3232
<details>
3333
<summary>Chapter 2 - Extending Elixir with Metaprogramming</summary>
34-
stuff
34+
35+
- [Re-Creating the if Macro](chapters/chapter2.md#re-creating-the-if-macro)
36+
- [Adding a while Loop to Elixir](chapters/chapter2.md#adding-a-while-loop-to-elixir)
37+
- [Smarter Testing with Macros](chapters/chapter2.md#smarter-testing-with-macros)
38+
- [Extending Modules](chapters/chapter2.md#extending-modules)
39+
- [Using Module Attributes for Code Generation](chapters/chapter2.md#using-module-attributes-for-code-generation)
40+
3541
</details>

assertion/assert_step1.exs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
defmodule Assertion do
2+
# {:==, [context: Elixir, import: Kernel], [5, 5]}
3+
defmacro assert({operator, _, [lhs, rhs]}) do
4+
quote bind_quoted: [operator: operator, lhs: lhs, rhs: rhs] do
5+
Assertion.Test.assert(operator, lhs, rhs)
6+
end
7+
end
8+
end

assertion/assert_step2.exs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
defmodule Assertion do
2+
defmacro assert({operator, _, [lhs, rhs]}) do
3+
quote bind_quoted: [operator: operator, lhs: lhs, rhs: rhs] do
4+
Assertion.Test.assert(operator, lhs, rhs)
5+
end
6+
end
7+
end
8+
9+
defmodule Assertion.Test do
10+
def pass do
11+
[:green, :bright, "PASSED!"]
12+
|> IO.ANSI.format()
13+
|> IO.puts()
14+
end
15+
16+
def fail(lhs, rhs) do
17+
fail =
18+
[:red, :bright, "FAILED:"]
19+
|> IO.ANSI.format()
20+
21+
IO.puts("""
22+
#{fail}
23+
Expected: #{lhs}
24+
but received: #{rhs}
25+
""")
26+
end
27+
28+
def assert(operator, lhs, rhs) do
29+
case operator do
30+
:== -> if lhs == rhs, do: pass(), else: fail(lhs, rhs)
31+
:> -> if lhs > rhs, do: pass(), else: fail(lhs, rhs)
32+
:< -> if lhs < rhs, do: pass(), else: fail(lhs, rhs)
33+
end
34+
end
35+
end

assertion/math_test_import.exs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
defmodule MathTest do
2+
import Assertion
3+
4+
def run do
5+
assert 5 == 5
6+
assert 10 > 0
7+
assert 1 > 2
8+
assert 10 * 10 == 100
9+
assert 10 * 10 == 101
10+
end
11+
end

callers-context/.iex.exs

Lines changed: 0 additions & 1 deletion
This file was deleted.

chapters/chapter1.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,3 +334,47 @@ nil
334334
- This hygiene seems like a fancy version of just being in or out of scope, perhaps that's the point.
335335
- You can override this hygiene by pre-pending `var!`. For example, `if var!(meaning_to_life) == 42 do ....`
336336
- When working with macros, it's important to be aware of what context a macro is executing in and to respect hygiene.
337+
338+
[callers_context.exs](callers-context/callers_context.exs)
339+
340+
<details>
341+
<summary>callers_context.exs</summary>
342+
343+
```elixir
344+
defmodule Mod do
345+
defmacro definfo do
346+
IO.puts("In macro's context (#{__MODULE__}).")
347+
348+
quote do
349+
IO.puts("In caller's context (#{__MODULE__}).")
350+
351+
def friendly_info do
352+
IO.puts("""
353+
My name is #{__MODULE__}
354+
My functions are #{inspect(__info__(:functions))}
355+
""")
356+
end
357+
end
358+
end
359+
end
360+
361+
defmodule MyModule do
362+
require Mod
363+
Mod.definfo()
364+
end
365+
```
366+
367+
Output:
368+
369+
```elixir
370+
iex(1)> c "callers_context.exs
371+
In macro's context (Elixir.Mod).
372+
In caller's context (Elixir.MyModule).
373+
[MyModule, Mod]
374+
375+
iex(2)> MyModule.friendly_info
376+
My name is Elixir.MyModule
377+
My functions are [friendly_info: 0]
378+
379+
:ok
380+
```

0 commit comments

Comments
 (0)