From fdb87b529f944a5363c1a2922de46bef38b5ceaa Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Wed, 9 Mar 2016 23:31:27 +0000 Subject: [PATCH 01/10] Add argument to koan to allow it to be tested --- lib/ast_manger.ex | 24 ++++++++++++++++++++++++ lib/koans.ex | 10 ++++++++-- lib/runner.ex | 7 +++++++ test/ast_mangler_test.exs | 35 +++++++++++++++++++++++++++++++++++ test/power_test.exs | 9 +++++++++ 5 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 lib/ast_manger.ex create mode 100644 test/ast_mangler_test.exs create mode 100644 test/power_test.exs diff --git a/lib/ast_manger.ex b/lib/ast_manger.ex new file mode 100644 index 0000000..fb4a238 --- /dev/null +++ b/lib/ast_manger.ex @@ -0,0 +1,24 @@ +defmodule ASTMangler do + def expand([do: thing], replacement) do + [do: expand(thing, replacement)] + end + + def expand({fun, context, args}, replacement) do + new_args = replace(args, replacement) + {fun, context, new_args} + end + + def replace(args, b) do + args + |> Enum.find_index(fn(x) -> x == :__ end) + |> replace(args, b) + end + + def replace(nil, [], _b), do: [] + def replace(nil, [ast], b) do + [expand(ast,b)] + end + def replace(index, list, b) do + List.update_at(list, index, fn(_)-> b end) + end +end diff --git a/lib/koans.ex b/lib/koans.ex index 0cd67fe..2875ce9 100644 --- a/lib/koans.ex +++ b/lib/koans.ex @@ -1,11 +1,17 @@ defmodule Koans do defmacro koan(name, body) do compiled_name = String.to_atom(name) + x = quote do: answer + mangled_body = ASTMangler.expand(body, x) quote do @koans unquote(compiled_name) - def unquote(compiled_name)() do + def unquote(compiled_name)(answer \\ :nothing) do try do - unquote(body) + if answer == :nothing do + unquote(body) + else + unquote(mangled_body) + end :ok rescue e in _ -> e diff --git a/lib/runner.ex b/lib/runner.ex index 279686a..c6e29b7 100644 --- a/lib/runner.ex +++ b/lib/runner.ex @@ -40,6 +40,13 @@ defmodule Runner do end end + def test_single_koan(module, name, answer) do + case apply(module, name, []) do + :ok -> :passed + error -> IO.inspect error + end + end + def run_koan(module, name) do case apply(module, name, []) do :ok -> :passed diff --git a/test/ast_mangler_test.exs b/test/ast_mangler_test.exs new file mode 100644 index 0000000..c02be73 --- /dev/null +++ b/test/ast_mangler_test.exs @@ -0,0 +1,35 @@ +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 "something" do + n = 1 + 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 diff --git a/test/power_test.exs b/test/power_test.exs new file mode 100644 index 0000000..ee4248c --- /dev/null +++ b/test/power_test.exs @@ -0,0 +1,9 @@ +defmodule PowerTest do + use ExUnit.Case + + test "something" do + [first | _] = Equalities.all_koans + + assert :passed == Runner.test_single_koan(Equalities, first, true) + end +end From 95c620129da57acd30a25c90d30d810e064e64fc Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 10 Mar 2016 00:14:29 +0000 Subject: [PATCH 02/10] Test multiple koans together --- lib/ast_mangler.ex | 25 +++++++++++++++++++++++++ lib/runner.ex | 2 +- test/power_test.exs | 6 ++++-- 3 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 lib/ast_mangler.ex diff --git a/lib/ast_mangler.ex b/lib/ast_mangler.ex new file mode 100644 index 0000000..2c1fa3d --- /dev/null +++ b/lib/ast_mangler.ex @@ -0,0 +1,25 @@ +defmodule ASTMangler do + def expand([], _), do: [] + def expand({fun, context, args}, replacement) do + new_args = replace(args, replacement) + {fun, context, new_args} + end + def expand([do: thing], replacement) do + [do: expand(thing, replacement)] + end + def expand(n, _), do: n + + def replace(args, b) do + args + |> Enum.find_index(fn(x) -> x == :__ end) + |> replace(args, b) + end + + def replace(nil, ast, b) when is_list(ast) do + Enum.map(ast, fn(element) -> expand(element, b) end) + end + + def replace(index, list, b) when is_integer(index) do + List.update_at(list, index, fn(_)-> b end) + end +end diff --git a/lib/runner.ex b/lib/runner.ex index c6e29b7..ee502e1 100644 --- a/lib/runner.ex +++ b/lib/runner.ex @@ -41,7 +41,7 @@ defmodule Runner do end def test_single_koan(module, name, answer) do - case apply(module, name, []) do + case apply(module, name, [answer]) do :ok -> :passed error -> IO.inspect error end diff --git a/test/power_test.exs b/test/power_test.exs index ee4248c..7c84339 100644 --- a/test/power_test.exs +++ b/test/power_test.exs @@ -2,8 +2,10 @@ defmodule PowerTest do use ExUnit.Case test "something" do - [first | _] = Equalities.all_koans + koans = Equalities.all_koans + answers = [true, 1] - assert :passed == Runner.test_single_koan(Equalities, first, true) + combined = Enum.zip(koans, answers) + Enum.each(combined, fn({koan, answer}) -> assert :passed == Runner.test_single_koan(Equalities, koan, answer) end) end end From eb1da4b1fc638bf578e77c84d34d42db55c4c0fb Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 10 Mar 2016 16:44:29 +0000 Subject: [PATCH 03/10] Show how to test koans for Equalities --- lib/ast_manger.ex | 24 ------------------------ lib/ast_mangler.ex | 1 + lib/koans.ex | 3 +-- lib/runner.ex | 7 ------- test/ast_mangler_test.exs | 4 ---- test/koans_harness_test.exs | 22 ++++++++++++++++++++++ test/power_test.exs | 11 ----------- 7 files changed, 24 insertions(+), 48 deletions(-) delete mode 100644 lib/ast_manger.ex create mode 100644 test/koans_harness_test.exs delete mode 100644 test/power_test.exs diff --git a/lib/ast_manger.ex b/lib/ast_manger.ex deleted file mode 100644 index fb4a238..0000000 --- a/lib/ast_manger.ex +++ /dev/null @@ -1,24 +0,0 @@ -defmodule ASTMangler do - def expand([do: thing], replacement) do - [do: expand(thing, replacement)] - end - - def expand({fun, context, args}, replacement) do - new_args = replace(args, replacement) - {fun, context, new_args} - end - - def replace(args, b) do - args - |> Enum.find_index(fn(x) -> x == :__ end) - |> replace(args, b) - end - - def replace(nil, [], _b), do: [] - def replace(nil, [ast], b) do - [expand(ast,b)] - end - def replace(index, list, b) do - List.update_at(list, index, fn(_)-> b end) - end -end diff --git a/lib/ast_mangler.ex b/lib/ast_mangler.ex index 2c1fa3d..1ee45c4 100644 --- a/lib/ast_mangler.ex +++ b/lib/ast_mangler.ex @@ -9,6 +9,7 @@ defmodule ASTMangler do end def expand(n, _), do: n + def replace(nil, _), do: nil def replace(args, b) do args |> Enum.find_index(fn(x) -> x == :__ end) diff --git a/lib/koans.ex b/lib/koans.ex index 2875ce9..449e951 100644 --- a/lib/koans.ex +++ b/lib/koans.ex @@ -1,8 +1,7 @@ defmodule Koans do defmacro koan(name, body) do compiled_name = String.to_atom(name) - x = quote do: answer - mangled_body = ASTMangler.expand(body, x) + mangled_body = ASTMangler.expand(body, quote do: answer) quote do @koans unquote(compiled_name) def unquote(compiled_name)(answer \\ :nothing) do diff --git a/lib/runner.ex b/lib/runner.ex index ee502e1..279686a 100644 --- a/lib/runner.ex +++ b/lib/runner.ex @@ -40,13 +40,6 @@ defmodule Runner do end end - def test_single_koan(module, name, answer) do - case apply(module, name, [answer]) do - :ok -> :passed - error -> IO.inspect error - end - end - def run_koan(module, name) do case apply(module, name, []) do :ok -> :passed diff --git a/test/ast_mangler_test.exs b/test/ast_mangler_test.exs index c02be73..9cb66b7 100644 --- a/test/ast_mangler_test.exs +++ b/test/ast_mangler_test.exs @@ -23,10 +23,6 @@ defmodule ASTManglerTest do end end - test "something" do - n = 1 - end - test "complex example" do ast = [do: {:assert, [line: 5], [{:==, [line: 5], [true, :__]}]}] diff --git a/test/koans_harness_test.exs b/test/koans_harness_test.exs new file mode 100644 index 0000000..39b0b21 --- /dev/null +++ b/test/koans_harness_test.exs @@ -0,0 +1,22 @@ +defmodule KoansHarnessTest do + use ExUnit.Case + + test "Equalities" do + answers = [true, 1, 2, 1, :something] + + assert all_pass?(Equalities, answers) == [:passed, :passed, :passed, :passed, :passed] + end + + def all_pass?(module, answers) do + module.all_koans + |> Enum.zip(answers) + |> Enum.map(fn({koan, answer}) -> KoansHarnessTest.test_single_koan(module, koan, answer) end) + end + + def test_single_koan(module, name, answer) do + case apply(module, name, [answer]) do + :ok -> :passed + error -> {:error, name, error} + end + end +end diff --git a/test/power_test.exs b/test/power_test.exs deleted file mode 100644 index 7c84339..0000000 --- a/test/power_test.exs +++ /dev/null @@ -1,11 +0,0 @@ -defmodule PowerTest do - use ExUnit.Case - - test "something" do - koans = Equalities.all_koans - answers = [true, 1] - - combined = Enum.zip(koans, answers) - Enum.each(combined, fn({koan, answer}) -> assert :passed == Runner.test_single_koan(Equalities, koan, answer) end) - end -end From a6bcd89e7ac7e838528aa4d6f1e96b846f6ff4a0 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 10 Mar 2016 23:16:24 +0000 Subject: [PATCH 04/10] Split koan into two functions with different arity --- lib/koans.ex | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/koans.ex b/lib/koans.ex index 449e951..9d817ba 100644 --- a/lib/koans.ex +++ b/lib/koans.ex @@ -4,13 +4,18 @@ defmodule Koans do mangled_body = ASTMangler.expand(body, quote do: answer) quote do @koans unquote(compiled_name) - def unquote(compiled_name)(answer \\ :nothing) do + def unquote(compiled_name)() do try do - if answer == :nothing do - unquote(body) - else - unquote(mangled_body) - end + unquote(body) + :ok + rescue + e in _ -> e + end + end + + def unquote(compiled_name)(answer) do + try do + unquote(mangled_body) :ok rescue e in _ -> e From 4eabc8ac61eaf1a1347e43826b97b230e447edb6 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 10 Mar 2016 23:16:51 +0000 Subject: [PATCH 05/10] Reuse run_koan for testing --- lib/runner.ex | 14 ++++++++------ test/koans_harness_test.exs | 9 +-------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/lib/runner.ex b/lib/runner.ex index 279686a..2d05c27 100644 --- a/lib/runner.ex +++ b/lib/runner.ex @@ -30,7 +30,11 @@ defmodule Runner do koans = module.all_koans 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) if Enum.count(koans) == Enum.count(passed) do @@ -40,12 +44,10 @@ defmodule Runner do end end - def run_koan(module, name) do - case apply(module, name, []) do + def run_koan(module, name, args \\ []) do + case apply(module, name, args) do :ok -> :passed - error -> - Display.show_failure(error, module, name) - :failed + error -> {:failed, error, module, name} end end end diff --git a/test/koans_harness_test.exs b/test/koans_harness_test.exs index 39b0b21..8b27af5 100644 --- a/test/koans_harness_test.exs +++ b/test/koans_harness_test.exs @@ -10,13 +10,6 @@ defmodule KoansHarnessTest do def all_pass?(module, answers) do module.all_koans |> Enum.zip(answers) - |> Enum.map(fn({koan, answer}) -> KoansHarnessTest.test_single_koan(module, koan, answer) end) - end - - def test_single_koan(module, name, answer) do - case apply(module, name, [answer]) do - :ok -> :passed - error -> {:error, name, error} - end + |> Enum.map(fn({koan, answer}) -> Runner.run_koan(module, koan, [answer]) end) end end From e48cc261a90c0e846814462b4b23cd918a6f24ed Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Thu, 10 Mar 2016 23:21:27 +0000 Subject: [PATCH 06/10] Strip out answers from Equalities --- lib/koans/01_equalities.ex | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/koans/01_equalities.ex b/lib/koans/01_equalities.ex index f636f25..c9b85bc 100644 --- a/lib/koans/01_equalities.ex +++ b/lib/koans/01_equalities.ex @@ -2,22 +2,22 @@ defmodule Equalities do use Koans koan "We shall contemplate truth by testing reality, via equality" do - assert true == true + assert true == :__ end koan "To understand reality, we must compare our expectations against reality" do - assert 2 == 1 + 1 + assert 2 == 1 + :__ end koan "Some things may appear different, but be the same" do - assert 1 == 2 / 2 + assert 1 == 2 / :__ end koan "Something is not equal to nothing" do - assert !(1 == nil) + assert !(:__ == nil) end koan "When things cannot be equal, they must be different" do - refute :something == 4 + refute :__ == 4 end end From 3d5da67c8d30bfa9623b6832ed090d308fe9f417 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Fri, 11 Mar 2016 12:59:46 +0000 Subject: [PATCH 07/10] Improve readability of test harness --- lib/ast_mangler.ex | 2 -- test/koans_harness_test.exs | 23 +++++++++++++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/ast_mangler.ex b/lib/ast_mangler.ex index 1ee45c4..746adf9 100644 --- a/lib/ast_mangler.ex +++ b/lib/ast_mangler.ex @@ -15,11 +15,9 @@ defmodule ASTMangler do |> Enum.find_index(fn(x) -> x == :__ end) |> replace(args, b) end - def replace(nil, ast, b) when is_list(ast) do Enum.map(ast, fn(element) -> expand(element, b) end) end - def replace(index, list, b) when is_integer(index) do List.update_at(list, index, fn(_)-> b end) end diff --git a/test/koans_harness_test.exs b/test/koans_harness_test.exs index 8b27af5..f0edc7a 100644 --- a/test/koans_harness_test.exs +++ b/test/koans_harness_test.exs @@ -2,14 +2,29 @@ defmodule KoansHarnessTest do use ExUnit.Case test "Equalities" do - answers = [true, 1, 2, 1, :something] + answers = [true, + 1, + 2, + 1, + :something] - assert all_pass?(Equalities, answers) == [:passed, :passed, :passed, :passed, :passed] + assert test_all(Equalities, answers) == :all_passed end - def all_pass?(module, answers) do + def test_all(module, answers) do module.all_koans |> Enum.zip(answers) - |> Enum.map(fn({koan, answer}) -> Runner.run_koan(module, koan, [answer]) end) + |> run_all(module) + |> passed? + |> results end + + def run_all(pairs, module) do + Enum.map(pairs, fn ({koan, answer}) -> Runner.run_koan(module, koan, [answer]) end) + end + + def passed?(answers), do: {Enum.all?(answers, fn(element) -> element == :passed end), answers} + + def results({true, _}), do: :all_passed + def results({false, answers}), do: answers end From 0eb715d4ca75a2f0604a9508689d2cc5310dcc5e Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Fri, 11 Mar 2016 13:00:20 +0000 Subject: [PATCH 08/10] Extract answers for Lists --- lib/koans/02_lists.ex | 37 ++++++++++++++++++++----------------- test/koans_harness_test.exs | 23 +++++++++++++++++++++++ 2 files changed, 43 insertions(+), 17 deletions(-) diff --git a/lib/koans/02_lists.ex b/lib/koans/02_lists.ex index f8041b3..aa789ca 100644 --- a/lib/koans/02_lists.ex +++ b/lib/koans/02_lists.ex @@ -2,67 +2,70 @@ defmodule Lists do use Koans koan "We can see what is ahead" do - assert List.first([1, 2, 3]) == 1 + assert List.first([1, 2, 3]) == :__ end koan "Checking what's trailing is also simple" do - assert List.last([1, 2, 3]) == 3 + assert List.last([1, 2, 3]) == :__ end koan "Diversity is embraced" do - assert [1, 2] ++ [:a, "b"] == [1, 2, :a, "b"] + assert [1, 2] ++ [:a, "b"] == :__ end koan "Things can evolve" do - assert [1, 2, 3] -- [3] == [1, 2] + assert [1, 2, 3] -- [3] == :__ end 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 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 koan "Replication is also possible" do - assert List.duplicate("life", 3) == ["life", "life", "life"] + assert List.duplicate("life", 3) == :__ end 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 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 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 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 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 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], -1, 4) == [1, 2, 3, 4] + assert List.insert_at([1, 2, 3], 10, 4) == :__ + end + + koan "Sometimes its faster to loop around back" do + assert List.insert_at([1, 2, 3], -1, 4) == :__ end 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 koan "Wrapping other values is a handy option" do - assert List.wrap("value") == ["value"] + assert List.wrap("value") == :__ end 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 diff --git a/test/koans_harness_test.exs b/test/koans_harness_test.exs index f0edc7a..d3e6b21 100644 --- a/test/koans_harness_test.exs +++ b/test/koans_harness_test.exs @@ -11,6 +11,29 @@ defmodule KoansHarnessTest do assert test_all(Equalities, answers) == :all_passed 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}] + ] + + assert test_all(Lists, answers) == :all_passed + end + def test_all(module, answers) do module.all_koans |> Enum.zip(answers) From 031258e086a9d7c3eae6d537cc5e51cc7e9e514b Mon Sep 17 00:00:00 2001 From: Uku Taht Date: Sat, 12 Mar 2016 16:11:32 +0200 Subject: [PATCH 09/10] Use koan assertions to fail the tests --- lib/koans.ex | 8 ++------ test/koans_harness_test.exs | 11 ++--------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/lib/koans.ex b/lib/koans.ex index 9d817ba..c313581 100644 --- a/lib/koans.ex +++ b/lib/koans.ex @@ -14,12 +14,8 @@ defmodule Koans do end def unquote(compiled_name)(answer) do - try do - unquote(mangled_body) - :ok - rescue - e in _ -> e - end + unquote(mangled_body) + :ok end end end diff --git a/test/koans_harness_test.exs b/test/koans_harness_test.exs index d3e6b21..192cb17 100644 --- a/test/koans_harness_test.exs +++ b/test/koans_harness_test.exs @@ -8,7 +8,7 @@ defmodule KoansHarnessTest do 1, :something] - assert test_all(Equalities, answers) == :all_passed + test_all(Equalities, answers) end test "Lists" do @@ -31,23 +31,16 @@ defmodule KoansHarnessTest do [{1, 3, 5}, {2, 4, 6}] ] - assert test_all(Lists, answers) == :all_passed + test_all(Lists, answers) end def test_all(module, answers) do module.all_koans |> Enum.zip(answers) |> run_all(module) - |> passed? - |> results end def run_all(pairs, module) do Enum.map(pairs, fn ({koan, answer}) -> Runner.run_koan(module, koan, [answer]) end) end - - def passed?(answers), do: {Enum.all?(answers, fn(element) -> element == :passed end), answers} - - def results({true, _}), do: :all_passed - def results({false, answers}), do: answers end From 9c803ff72cc25cf82558724d2d8f49c4d39c8451 Mon Sep 17 00:00:00 2001 From: Felipe Sere Date: Sat, 12 Mar 2016 15:05:26 +0000 Subject: [PATCH 10/10] Use prewalk to inject value into AST --- lib/ast_mangler.ex | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/lib/ast_mangler.ex b/lib/ast_mangler.ex index 746adf9..eed4281 100644 --- a/lib/ast_mangler.ex +++ b/lib/ast_mangler.ex @@ -1,24 +1,8 @@ defmodule ASTMangler do - def expand([], _), do: [] - def expand({fun, context, args}, replacement) do - new_args = replace(args, replacement) - {fun, context, new_args} + def expand(ast, replacement) do + Macro.prewalk(ast, fn(node) -> update(node, replacement) end) end - def expand([do: thing], replacement) do - [do: expand(thing, replacement)] - end - def expand(n, _), do: n - def replace(nil, _), do: nil - def replace(args, b) do - args - |> Enum.find_index(fn(x) -> x == :__ end) - |> replace(args, b) - end - def replace(nil, ast, b) when is_list(ast) do - Enum.map(ast, fn(element) -> expand(element, b) end) - end - def replace(index, list, b) when is_integer(index) do - List.update_at(list, index, fn(_)-> b end) - end + def update(:__, replacement), do: replacement + def update(node, _), do: node end