diff --git a/lectures/05/lec5a.ex b/lectures/05/lec5a.ex new file mode 100644 index 0000000..1fa30d9 --- /dev/null +++ b/lectures/05/lec5a.ex @@ -0,0 +1,32 @@ +defmodule M do + def if_fun(predicate, do: do_block, else: else_block) do + cond do + predicate -> do_block + true -> else_block + end + end + + defmacro inspect(x) do + IO.inspect(x) + end + + defmacro if_macro(predicate, do: do_block, else: else_block) do + quote do + cond do + unquote(predicate) -> unquote(do_block) + true -> unquote(else_block) + end + end + end + + defmacro while(condition, do: do_block) do + quote do + try do + for _ <- Stream.cycle [:ok] do + if unquote(condition), do: unquote(do_block), + else: throw :break + end + catch :throw, :break -> :ok end + end + end +end diff --git a/lectures/05/lec5b.ex b/lectures/05/lec5b.ex new file mode 100644 index 0000000..d51c8b1 --- /dev/null +++ b/lectures/05/lec5b.ex @@ -0,0 +1,31 @@ +defmodule M do + defmacro context() do + # macro's context + IO.inspect(__MODULE__) + quote do + # caller's context + IO.inspect(unquote(__MODULE__)) + IO.inspect(__MODULE__) + end + end + + defmacro twice(x) do + quote do + unquote(x) + unquote(x) + end + end + + defmacro twice_v2(x) do + quote bind_quoted: [x: x] do + x + x + end + end +end + +defmodule Test do + import M + + def test() do + context() + end +end diff --git a/lectures/05/lec5c.ex b/lectures/05/lec5c.ex new file mode 100644 index 0000000..85adb88 --- /dev/null +++ b/lectures/05/lec5c.ex @@ -0,0 +1,16 @@ +defmodule M do + defmacro say(msg) do + name = String.to_atom("say_#{msg}") + quote do + def name() do + IO.puts(unquote(msg)) + end + end + end +end + +defmodule Test do + import M + + say("hello") +end diff --git a/lectures/05/lec5d.ex b/lectures/05/lec5d.ex new file mode 100644 index 0000000..aa0bcae --- /dev/null +++ b/lectures/05/lec5d.ex @@ -0,0 +1,9 @@ +# unquote fragments +defmodule M do + for {k, v} <- [homer: 55, bart: 76] do + def unquote(k)(), do: unquote(v) + end +end + +M.homer() +M.bart() diff --git a/lectures/05/main.c b/lectures/05/main.c new file mode 100644 index 0000000..047b105 --- /dev/null +++ b/lectures/05/main.c @@ -0,0 +1,8 @@ +#include + +#define CHECK(pred) printf("%s ... %s\n", pred ? "passed" : "FAILED", #pred) + +int main(void) { + CHECK(1 + 1 == 2); + CHECK(1 + 2 == 4); +} diff --git a/lectures/05/notes.ex b/lectures/05/notes.ex new file mode 100644 index 0000000..00fdcb5 --- /dev/null +++ b/lectures/05/notes.ex @@ -0,0 +1,23 @@ +# see abstract syntax tree (ast) +quote do: 1 + 2 +# {:+, [context: Elixir, imports: [{1, Kernel}, {2, Kernel}]], [1, 2]} +# +# + +# / \ +# 1 2 +# + +alias String, as: S +# {:__aliases__, [alias: String], [:S]} + +### Abstract literal +quote do: 1 # -> 1 +quote do: "hello" # -> "hello" +quote do: [1, 2, 3] # -> [1, 2, 3] # a list of ast literals is an ast literal +quote do: {1, 2} # -> {1, 2} # doubles are ast literals but not triples + + +### Macro - something that takes an AST and returns an AST +x = 2 +quote do: x # -> {:x, [], Elixir} # doesnt evaluate it, just ast of expression +quote do: unquote(x) # -> 2 diff --git a/lectures/05/testing.ex b/lectures/05/testing.ex new file mode 100644 index 0000000..3867c44 --- /dev/null +++ b/lectures/05/testing.ex @@ -0,0 +1,56 @@ +defmodule Testing do + defmacro check(predicate) do + test = Macro.to_string(predicate) + quote do + cond do + unquote(predicate) -> IO.puts("passed ... #{unquote(test)}") + true -> IO.puts("FAILED ... #{unquote(test)}") + end + end + end + + defmacro test(description, do: block) do + name = "test_#{description}" + fn_name = String.to_atom(name) + quote do + @tests [unquote(fn_name) | @tests] + def unquote(fn_name)() do + IO.puts(unquote(name) <> ":") + unquote(block) + end + end + end + + defmacro __before_compile__(_env) do + # defmacro make_run_tests() do + quote do + def run_tests() do + Enum.each(Enum.reverse(@tests), fn f -> apply(__MODULE__, f, []) end) + end + end + end + + defmacro __using__(_opts) do + quote do + import unquote(__MODULE__) + @tests [] + @before_compile unquote(__MODULE__) + end + end +end + +defmodule Tests do + use Testing # this requires Testing and call Testing.__using__ + + test "equality" do + check(1 + 1 == 2) + check(1 + 2 == 4) + end + + test "greater" do + check(1 > 2) + check(1 < 2) + end + + # make_run_tests() +end