Current section
Files
Jump to
Current section
Files
src/ejpet_parser.erl
-module(ejpet_parser).
-author('nicolas.michel.lava@gmail.com').
-export([parse/1]).
-define(RESULT(E), {E, ejpet_memoize:build_key(E)}).
parse(Tokens) ->
pattern(Tokens).
pattern(Tokens = [open_paren, {inject, _} | _]) ->
expr(Tokens);
pattern([open_paren, {capture, Name} | Tail]) ->
{[close_paren | R], Expr} = expr(Tail),
{R, ?RESULT({capture, Expr, unicode:characters_to_binary(Name, utf8, utf8)})};
pattern(Tokens) ->
expr(Tokens).
expr([true | Tail]) ->
{Tail, ?RESULT(true)};
expr([false | Tail]) ->
{Tail, ?RESULT(false)};
expr([null | Tail]) ->
{Tail, ?RESULT(null)};
expr([Item = {number, _Value} | Tail]) ->
{Tail, ?RESULT(Item)};
expr([Item = {string, _String} | Tail]) ->
{Tail, ?RESULT(Item)};
expr([Item = {regex, _String} | Tail]) ->
{Tail, ?RESULT(Item)};
expr([underscore | Tail]) ->
{Tail, ?RESULT(any)};
expr([open_curvy_brace | Tail]) ->
expr_object(Tail);
expr([open_square_brace | Tail]) ->
expr_list(Tail);
expr([open_angle_brace | Tail]) ->
expr_iterable(Tail);
expr([open_angle_brace_bang | Tail]) ->
expr_descendant(Tail);
expr([open_paren, {inject, Name}, Type, close_paren | Tail])
when Type == string;
Type == number;
Type == boolean;
Type == regex ->
{Tail, ?RESULT({inject, Type, Name})}.
%% -----
expr_object([close_curvy_brace | Tail]) ->
{Tail, ?RESULT({object, any})};
expr_object(List) ->
expr_object_head(List).
expr_object_head(List) ->
{R, Expr} = expr_pair(List),
case R of
[coma | _Tail] ->
expr_object_tail(R, [Expr]);
[close_curvy_brace | Tail] ->
{Tail, ?RESULT({object, [Expr]})}
end.
expr_object_tail([close_curvy_brace | Tail], Acc) ->
{Tail, ?RESULT({object, lists:reverse(Acc)})};
expr_object_tail([coma | Tail], Acc) ->
{R, Expr} = expr_pair(Tail),
expr_object_tail(R, [Expr | Acc]).
expr_pair([{string, String}, column, underscore | Tail]) ->
{Tail, ?RESULT({pair, {string, String}, any})};
expr_pair([{string, String}, column | Tail]) ->
{R, Expr} = pattern(Tail),
{R, ?RESULT({pair, {string, String}, Expr})};
expr_pair([underscore, column | Tail]) ->
{R, Expr} = pattern(Tail),
{R, ?RESULT({pair, any, Expr})}.
%% -----
expr_list([close_square_brace | Tail]) ->
{Tail, ?RESULT({list, empty})};
expr_list([star, close_square_brace | Tail]) ->
{Tail, ?RESULT({list, any})};
expr_list([star, coma | Tail]) ->
expr_list_tail(Tail, []);
expr_list(List) ->
expr_list_head(List).
expr_list_head(L) ->
{R, Expr} = expr_list_span(L, []),
case R of
[close_square_brace | Tail] ->
{Tail, ?RESULT({list, lists:reverse([Expr])})};
Tail ->
expr_list_tail(Tail, [Expr])
end.
expr_list_tail(List, Acc) ->
{R, Expr} = expr_list_span(List, []),
case R of
[close_square_brace | Tail] ->
{Tail, ?RESULT({list, lists:reverse([?RESULT({find, Expr}) | Acc])})};
Tail ->
expr_list_tail(Tail, [?RESULT({find, Expr}) | Acc])
end.
expr_list_span(L, Acc) ->
{R, Expr} = pattern(L),
case R of
[close_square_brace | _] ->
{R, ?RESULT({span, lists:reverse([Expr | Acc]), eol})};
[coma, star, close_square_brace | Tail] ->
{[close_square_brace | Tail], ?RESULT({span, lists:reverse([Expr | Acc])})};
[coma, star, coma | Tail] ->
{Tail, ?RESULT({span, lists:reverse([Expr | Acc])})};
[coma | Tail] ->
expr_list_span(Tail, [Expr | Acc])
end.
%% -----
expr_iterable([close_angle_brace | Tail]) ->
{Tail, ?RESULT({iterable, any})};
expr_iterable(List) ->
expr_iterable_head(List).
expr_iterable_head(List) ->
{R, Expr} = pattern(List),
case R of
[coma | _Tail] -> % Expr ',' Tail
expr_iterable_tail(R, [Expr]);
[close_angle_brace, slash_g | Tail] -> % Expr '>' '/g'
{Tail, ?RESULT({iterable, [Expr], true})};
[close_angle_brace | Tail] -> % Expr '>'
{Tail, ?RESULT({iterable, [Expr], false})}
end.
expr_iterable_tail([close_angle_brace, slash_g | Tail], Acc) -> % '>' '/g'
{Tail, ?RESULT({iterable, lists:reverse(Acc), true})};
expr_iterable_tail([close_angle_brace | Tail], Acc) -> % '>'
{Tail, ?RESULT({iterable, lists:reverse(Acc), false})};
expr_iterable_tail([coma | Tail], Acc) -> % ',' Expr Tail
{R, Expr} = pattern(Tail),
expr_iterable_tail(R, [Expr | Acc]).
%% -----
expr_descendant(List) ->
expr_descendant_head(List).
expr_descendant_head(List) ->
{R, Expr} = pattern(List),
case R of
[coma | _Tail] -> % Expr ',' Tail
expr_descendant_tail(R, [Expr]);
[close_angle_brace_bang, slash_g | Tail] -> % Expr '>' '/g'
{Tail, ?RESULT({descendant, [Expr], true})};
[close_angle_brace_bang | Tail] -> % Expr '>'
{Tail, ?RESULT({descendant, [Expr], false})}
end.
expr_descendant_tail([close_angle_brace_bang, slash_g | Tail], Acc) -> % '>' '/g'
{Tail, ?RESULT({descendant, lists:reverse(Acc), true})};
expr_descendant_tail([close_angle_brace_bang | Tail], Acc) -> % '>'
{Tail, ?RESULT({descendant, lists:reverse(Acc), false})};
expr_descendant_tail([coma | Tail], Acc) -> % ',' Expr Tail
{R, Expr} = pattern(Tail),
expr_descendant_tail(R, [Expr | Acc]).