Current section
Files
Jump to
Current section
Files
lib/x/parser.ex
defmodule X.Parser do
@moduledoc """
X template parser module.
"""
alias X.Ast
defguardp is_text_token(token) when elem(token, 0) in [:tag_text, :tag_output]
@doc ~S"""
Converts given tokens into X template AST.
## Example
iex> X.Parser.call([
...> {:tag_start, {1, 1}, 'div', [], nil, nil, false, false, false},
...> {:tag_start, {6, 1}, 'span', [{:tag_attr, {12, 1}, 'class', 'test', false}],
...> nil, nil, false, false, false},
...> {:tag_output, {25, 1}, 'a ', true},
...> {:tag_end, {32, 1}, 'span'},
...> {:tag_end, {39, 1}, 'div'}
...> ])
[
{{:tag_start, {1, 1}, 'div', [], nil, nil, false, false, false},
[
{{:tag_start, {6, 1}, 'span',
[{:tag_attr, {12, 1}, 'class', 'test', false}], nil, nil, false, false,
false},
[
{{:text_group, {25, 1}, 'span'},
[{{:tag_output, {25, 1}, 'a ', true}, []}]}
]}
]}
]
"""
@spec call([Ast.token()]) :: [Ast.leaf()]
def call(tokens) do
{result, _} = parse(tokens, nil, [])
result
end
@spec parse([Ast.token()], charlist() | nil, [Ast.leaf()]) :: {[Ast.leaf()], [Ast.token()]}
defp parse(list = [token | _], scope, acc) when is_text_token(token) do
{children, rest} = parse_text_group(list)
[{head, _} | _] = children
parse(rest, scope, [
{{:text_group, elem(head, 1), scope}, children}
| acc
])
end
defp parse([token = {:tag_start, _, _, _, _, _, singleton, selfclosed, _} | tail], scope, acc)
when singleton or selfclosed do
parse(tail, scope, [{token, []} | acc])
end
defp parse([token = {:tag_start, _, name, _, _, _, _, _, _} | tail], scope, acc) do
{children, rest} = parse(tail, name, [])
parse(rest, scope, [{token, children} | acc])
end
defp parse([{:tag_end, cur, name} | tail], scope, acc) do
case scope do
^name ->
{:lists.reverse(acc), tail}
_ ->
throw({:unexpected_tag, cur, scope, name})
end
end
defp parse([token = {:tag_comment, _, _} | tail], scope, acc) do
parse(tail, scope, [{token, []} | acc])
end
defp parse([], _, acc) do
{:lists.reverse(acc), []}
end
@spec parse_text_group([Ast.token()]) :: {[Ast.leaf()], [Ast.token()]}
defp parse_text_group([token | tail]) when is_text_token(token) do
{acc, rest} = parse_text_group(tail)
{[{token, []} | acc], rest}
end
defp parse_text_group(list) do
{[], list}
end
end