Skip to content

Instantly share code, notes, and snippets.

@rkrx

rkrx/test.ex Secret

Last active Mar 22, 2018
Embed
What would you like to do?
defmodule Twig.PreParser.Tools do
def find_next_candidate_of_one_of_these(source, tags) do
{:ok, index, len} = find_next_position_of_one_of_these(source, tags, 0)
{:ok, index, source |> Enum.slice(index, len)}
end
def find_next_position_of_one_of_these(source, tags) do
source = source |> to_charlist
find_next_position_of_one_of_these(source, tags, 0)
end
defp find_next_position_of_one_of_these(source, tags, index) do
case source |> starts_with_one_of_these(tags) do
nil -> case source do
[_ | rest] -> find_next_position_of_one_of_these(rest, tags, index + 1)
[] -> {:err, nil, nil}
end
tag -> {:ok, index, tag |> Enum.count}
end
end
defp starts_with_one_of_these(source, tags) do
case tags do
[tag | rest] -> case source |> starts_with?(tag) do
true -> tag
false -> starts_with_one_of_these(source, rest)
end
[] -> nil
end
end
defp starts_with?(source, tag) do
source |> to_string |> String.starts_with?(tag |> to_string)
end
end
defmodule Twig.PreParser.Tags do
def get_open_tags(tags) do
Enum.map(tags, &(elem(&1, 1)))
end
def get_symbol_for_open_tag(tags, tag) do
case tags do
[{symbol, ^tag, _} | _] -> symbol
[_ | rest] -> get_symbol_for_open_tag(rest, tag)
[] -> raise "Opening-tag not found: " <> to_string(tag)
end
end
def get_tag_by_symbol(tags, symbol, tag) do
case tags do
[{^symbol, open_tag, close_tag} | _] -> Map.get(%{:open => open_tag, :close => close_tag}, tag)
[_ | rest] -> get_tag_by_symbol(rest, symbol, tag)
[] -> raise "Symbol not found: " <> to_string(symbol)
end
end
end
defmodule Twig.PreParser do
def parse(source, tags) do
source = source |> to_charlist
parse(:tmpl, source, 0, tags)
end
defp parse(:tmpl, source, offset, tags) do
open_tags = Twig.PreParser.Tags.get_open_tags(tags)
res = Twig.PreParser.Tools.find_next_position_of_one_of_these(source, open_tags)
case res do
{:ok, pos, tag_length} ->
{content, tag, prog} = split2(source, pos, tag_length)
symbol = Twig.PreParser.Tags.get_symbol_for_open_tag(tags, tag)
[{:content, offset, content}] ++ parse(symbol, prog, offset + pos + tag_length, tags)
{:err, _nil1, _nil2} -> [{:content, offset, source}]
end
end
defp parse(:cmmt, source, offset, tags) do
close_tag = Twig.PreParser.Tags.get_tag_by_symbol(tags, :cmmt, :close)
result = Twig.PreParser.Tools.find_next_position_of_one_of_these(source, [close_tag])
case result do
{:ok, pos, tag_length} ->
{comment, _tag, content} = split2(source, pos, tag_length)
[{:comment, offset, comment}] ++ parse(:tmpl, content, offset + pos + tag_length, tags)
{:err, _nil1, _nil2} -> raise "Expected close-tag before end of file: " <> close_tag
end
end
defp parse(type, source, offset, tags) do
close_tag = Twig.PreParser.Tags.get_tag_by_symbol(tags, type, :close)
{:ok, rest, prog_length, _} = parseProg(source, close_tag, 0)
[{type, offset, source |> Enum.take(prog_length)}] ++ parse(:tmpl, rest, offset + prog_length, tags)
end
defp parseProg(source, close_tag, offset) do
{:ok, pos, len} = Twig.PreParser.Tools.find_next_position_of_one_of_these(source, ['(', '[', '{', '"', '\'', close_tag])
{_, found_tag, rest} = split2(source, pos, len)
find_closing = fn (source, tag, offset) ->
{:ok, rest, offset, len} = parseProg(source, tag, offset + pos + len)
parseProg(rest, close_tag, offset + len)
end
case found_tag do
'(' -> find_closing.(rest, ')', offset)
'[' -> find_closing.(rest, ']', offset)
'{' -> find_closing.(rest, '}', offset)
'"' -> find_closing.(rest, '"', offset)
'\'' -> find_closing.(rest, '\'', offset)
^close_tag -> {:ok, rest, offset + pos, len}
end
end
defp split2(input, offset1, offset2) do
{a, rest} = input |> Enum.split(offset1)
{b, c} = rest |> Enum.split(offset2)
{a, b, c}
end
end
my_tags = [{
:expr, '{{', '}}'
}, {
:stmt, '{%', '%}'
}, {
:cmmt, '{#', '#}'
}]
IO.inspect Twig.PreParser.parse("<html>{# fn({test: {}}) #}</html>", my_tags)
IO.inspect Twig.PreParser.parse("<html>{{ fn({test: {}}) }}</html>", my_tags)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment