8
lib/ast_mangler.ex
Normal file
8
lib/ast_mangler.ex
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
defmodule ASTMangler do
|
||||||
|
def expand(ast, replacement) do
|
||||||
|
Macro.prewalk(ast, fn(node) -> update(node, replacement) end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(:__, replacement), do: replacement
|
||||||
|
def update(node, _), do: node
|
||||||
|
end
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
defmodule Koans do
|
defmodule Koans do
|
||||||
defmacro koan(name, body) do
|
defmacro koan(name, body) do
|
||||||
compiled_name = String.to_atom(name)
|
compiled_name = String.to_atom(name)
|
||||||
|
mangled_body = ASTMangler.expand(body, quote do: answer)
|
||||||
quote do
|
quote do
|
||||||
@koans unquote(compiled_name)
|
@koans unquote(compiled_name)
|
||||||
def unquote(compiled_name)() do
|
def unquote(compiled_name)() do
|
||||||
@@ -11,6 +12,11 @@ defmodule Koans do
|
|||||||
e in _ -> e
|
e in _ -> e
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def unquote(compiled_name)(answer) do
|
||||||
|
unquote(mangled_body)
|
||||||
|
:ok
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -2,22 +2,22 @@ defmodule Equalities do
|
|||||||
use Koans
|
use Koans
|
||||||
|
|
||||||
koan "We shall contemplate truth by testing reality, via equality" do
|
koan "We shall contemplate truth by testing reality, via equality" do
|
||||||
assert true == true
|
assert true == :__
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "To understand reality, we must compare our expectations against reality" do
|
koan "To understand reality, we must compare our expectations against reality" do
|
||||||
assert 2 == 1 + 1
|
assert 2 == 1 + :__
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Some things may appear different, but be the same" do
|
koan "Some things may appear different, but be the same" do
|
||||||
assert 1 == 2 / 2
|
assert 1 == 2 / :__
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Something is not equal to nothing" do
|
koan "Something is not equal to nothing" do
|
||||||
assert !(1 == nil)
|
assert !(:__ == nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "When things cannot be equal, they must be different" do
|
koan "When things cannot be equal, they must be different" do
|
||||||
refute :something == 4
|
refute :__ == 4
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,67 +2,70 @@ defmodule Lists do
|
|||||||
use Koans
|
use Koans
|
||||||
|
|
||||||
koan "We can see what is ahead" do
|
koan "We can see what is ahead" do
|
||||||
assert List.first([1, 2, 3]) == 1
|
assert List.first([1, 2, 3]) == :__
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Checking what's trailing is also simple" do
|
koan "Checking what's trailing is also simple" do
|
||||||
assert List.last([1, 2, 3]) == 3
|
assert List.last([1, 2, 3]) == :__
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Diversity is embraced" do
|
koan "Diversity is embraced" do
|
||||||
assert [1, 2] ++ [:a, "b"] == [1, 2, :a, "b"]
|
assert [1, 2] ++ [:a, "b"] == :__
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Things can evolve" do
|
koan "Things can evolve" do
|
||||||
assert [1, 2, 3] -- [3] == [1, 2]
|
assert [1, 2, 3] -- [3] == :__
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Evolution can have different forms" do
|
koan "Evolution can have different forms" do
|
||||||
assert List.delete([1, 2, 2, 3], 2) == [1, 2, 3]
|
assert List.delete([1, 2, 2, 3], 2) == :__
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Precision is also valued" do
|
koan "Precision is also valued" do
|
||||||
assert List.delete_at([1, 2, 3], 1) == [1, 3]
|
assert List.delete_at([1, 2, 3], 1) == :__
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Replication is also possible" do
|
koan "Replication is also possible" do
|
||||||
assert List.duplicate("life", 3) == ["life", "life", "life"]
|
assert List.duplicate("life", 3) == :__
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Sometimes levelling the playing field is desired" do
|
koan "Sometimes levelling the playing field is desired" do
|
||||||
assert List.flatten([1, [2, 3], 4, [5]]) == [1, 2, 3, 4, 5]
|
assert List.flatten([1, [2, 3], 4, [5]]) == :__
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Same rules apply to new members that arrive late" do
|
koan "Same rules apply to new members that arrive late" do
|
||||||
assert List.flatten([1, [2, 3]], [4]) == [1, 2, 3, 4]
|
assert List.flatten([1, [2, 3]], [4]) == :__
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Order can also be specified for new members" do
|
koan "Order can also be specified for new members" do
|
||||||
assert List.insert_at([1, 2, 3], 1, 4) == [1, 4, 2, 3]
|
assert List.insert_at([1, 2, 3], 1, 4) == :__
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "We can replace things at specified positions" do
|
koan "We can replace things at specified positions" do
|
||||||
assert List.replace_at([1, 2, 3], 0, 10) == [10, 2, 3]
|
assert List.replace_at([1, 2, 3], 0, 10) == :__
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Replacing something which is not" do
|
koan "Replacing something which is not" do
|
||||||
assert List.replace_at([1, 2, 3], 10, 0) == [1, 2, 3]
|
assert List.replace_at([1, 2, 3], 10, 0) == :__
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Order is bound by nature's laws" do
|
koan "Order is bound by nature's laws" do
|
||||||
assert List.insert_at([1, 2, 3], 10, 4) == [1, 2, 3, 4]
|
assert List.insert_at([1, 2, 3], 10, 4) == :__
|
||||||
assert List.insert_at([1, 2, 3], -1, 4) == [1, 2, 3, 4]
|
end
|
||||||
|
|
||||||
|
koan "Sometimes its faster to loop around back" do
|
||||||
|
assert List.insert_at([1, 2, 3], -1, 4) == :__
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "We can also transform ourselves completely" do
|
koan "We can also transform ourselves completely" do
|
||||||
assert List.to_tuple([1, 2, 3]) == {1, 2, 3}
|
assert List.to_tuple([1, 2, 3]) == :__
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Wrapping other values is a handy option" do
|
koan "Wrapping other values is a handy option" do
|
||||||
assert List.wrap("value") == ["value"]
|
assert List.wrap("value") == :__
|
||||||
end
|
end
|
||||||
|
|
||||||
koan "Zipping can be a useful operation" do
|
koan "Zipping can be a useful operation" do
|
||||||
assert List.zip([[1, 2], [3, 4], [5, 6]]) == [{1, 3, 5}, {2, 4, 6}]
|
assert List.zip([[1, 2], [3, 4], [5, 6]]) == :__
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -30,7 +30,11 @@ defmodule Runner do
|
|||||||
|
|
||||||
koans = module.all_koans
|
koans = module.all_koans
|
||||||
passed = Enum.take_while(koans, fn(name) ->
|
passed = Enum.take_while(koans, fn(name) ->
|
||||||
run_koan(module, name) == :passed
|
case run_koan(module, name) do
|
||||||
|
:passed -> true
|
||||||
|
{:failed, error, module, name} -> Display.show_failure(error, module, name)
|
||||||
|
false
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
if Enum.count(koans) == Enum.count(passed) do
|
if Enum.count(koans) == Enum.count(passed) do
|
||||||
@@ -40,12 +44,10 @@ defmodule Runner do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def run_koan(module, name) do
|
def run_koan(module, name, args \\ []) do
|
||||||
case apply(module, name, []) do
|
case apply(module, name, args) do
|
||||||
:ok -> :passed
|
:ok -> :passed
|
||||||
error ->
|
error -> {:failed, error, module, name}
|
||||||
Display.show_failure(error, module, name)
|
|
||||||
:failed
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
31
test/ast_mangler_test.exs
Normal file
31
test/ast_mangler_test.exs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
defmodule ASTManglerTest do
|
||||||
|
use ExUnit.Case, async: true
|
||||||
|
|
||||||
|
test "simple replacement" do
|
||||||
|
ast = quote do: 1 + :__
|
||||||
|
|
||||||
|
mangled = ASTMangler.expand(ast, 37)
|
||||||
|
assert {:+, [context: ASTManglerTest, import: Kernel], [1, 37]} == mangled
|
||||||
|
end
|
||||||
|
|
||||||
|
def complex_example do
|
||||||
|
[head | tail] = [1,2,3,4]
|
||||||
|
|
||||||
|
assert head == 1
|
||||||
|
assert tail == [2,3,4]
|
||||||
|
end
|
||||||
|
|
||||||
|
def foo(answer) do
|
||||||
|
if answer == :yes do
|
||||||
|
:bar
|
||||||
|
else
|
||||||
|
:batz
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "complex example" do
|
||||||
|
ast = [do: {:assert, [line: 5], [{:==, [line: 5], [true, :__]}]}]
|
||||||
|
|
||||||
|
assert [do: {:assert, [line: 5], [{:==, [line: 5], [true, true]}]}] == ASTMangler.expand(ast, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
46
test/koans_harness_test.exs
Normal file
46
test/koans_harness_test.exs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
defmodule KoansHarnessTest do
|
||||||
|
use ExUnit.Case
|
||||||
|
|
||||||
|
test "Equalities" do
|
||||||
|
answers = [true,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
:something]
|
||||||
|
|
||||||
|
test_all(Equalities, answers)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "Lists" do
|
||||||
|
answers = [1,
|
||||||
|
3,
|
||||||
|
[1,2,:a,"b"],
|
||||||
|
[1,2],
|
||||||
|
[1,2,3],
|
||||||
|
[1,3],
|
||||||
|
["life", "life", "life"],
|
||||||
|
[1, 2, 3, 4, 5],
|
||||||
|
[1, 2, 3, 4],
|
||||||
|
[1, 4, 2, 3],
|
||||||
|
[10, 2, 3],
|
||||||
|
[1, 2, 3],
|
||||||
|
[1, 2, 3, 4],
|
||||||
|
[1, 2, 3, 4],
|
||||||
|
{1, 2, 3},
|
||||||
|
["value"],
|
||||||
|
[{1, 3, 5}, {2, 4, 6}]
|
||||||
|
]
|
||||||
|
|
||||||
|
test_all(Lists, answers)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_all(module, answers) do
|
||||||
|
module.all_koans
|
||||||
|
|> Enum.zip(answers)
|
||||||
|
|> run_all(module)
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_all(pairs, module) do
|
||||||
|
Enum.map(pairs, fn ({koan, answer}) -> Runner.run_koan(module, koan, [answer]) end)
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user