Skip to content

Instantly share code, notes, and snippets.

@sgeos
Created January 31, 2016 17:12
Show Gist options
  • Save sgeos/a9369d4ad047fe0a6524 to your computer and use it in GitHub Desktop.
Save sgeos/a9369d4ad047fe0a6524 to your computer and use it in GitHub Desktop.
import Html
quote(do: markup do: text("hi")) |> Code.eval_quoted
markup do: text("hi")
defmodule Html do
@external_resource tags_path = Path.join([__DIR__, "tags.txt"])
@tags (for line <- File.stream!(tags_path, [], :line) do
line
|> String.strip
|> String.to_atom
end)
defmacro markup(do: block) do
quote do
{:ok, var!(buffer, Html)} = start_buffer([])
unquote(Macro.postwalk(block, &postwalk/1))
result = render(var!(buffer, Html))
:ok = stop_buffer(var!(buffer, Html))
result
end
end
def postwalk({:text, _meta, [string]}) do
quote do
put_buffer(var!(buffer, Html), to_string(unquote(string)))
end
end
def postwalk({tag_name, _meta, [[do: inner]]})
when tag_name in @tags do
quote do
tag(unquote(tag_name), [], do: unquote(inner))
end
end
def postwalk({tag_name, _meta, [attrs, [do: inner]]})
when tag_name in @tags do
quote do
tag(unquote(tag_name), unquote(attrs), do: unquote(inner))
end
end
def postwalk(ast), do: ast
def start_buffer(state) do
Agent.start_link(fn -> state end)
end
def stop_buffer(buffer) do
Agent.stop(buffer)
end
def put_buffer(buffer, content) do
Agent.update(buffer, &[content | &1])
end
def render(buffer) do
Agent.get(buffer, &(&1))
|> Enum.reverse
|> Enum.join("")
end
defmacro tag(name, attrs \\ [], do: inner) do
quote do
put_buffer var!(buffer, Html), open_tag(unquote_splicing([name, attrs]))
unquote(postwalk(inner))
put_buffer var!(buffer, Html), unquote("</#{name}>")
end
end
def open_tag(name, []), do: "<#{name}>"
def open_tag(name, attrs) do
attr_html = for {key, value} <- attrs, into: "", do: " #{key}=\"#{value}\""
"<#{name}#{attr_html}>"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment