-
-
Save rkrx/cf8715aaa96c0c7b5b216d3b8d4dc232 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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