Packages

A simple HTML parser using Gleam

Current section

Files

Jump to
html_parser src html_parser.erl
Raw

src/html_parser.erl

-module(html_parser).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([get_attrs/1, get_first_element/1, as_list/1, as_tree/1]).
-export_type([element/0, attribute/0, current_element_type/0]).
-type element() :: empty_element |
{start_element, binary(), list(attribute()), list(element())} |
{end_element, binary()} |
{content, binary()}.
-type attribute() :: {attribute, binary(), binary()}.
-type current_element_type() :: start | 'end' | none.
-spec trim_space_to_elem_begin(binary()) -> binary().
trim_space_to_elem_begin(In) ->
case In of
<<" "/utf8, Remain/binary>> ->
trim_space_to_elem_begin(Remain);
<<"\n"/utf8, Remain/binary>> ->
trim_space_to_elem_begin(Remain);
<<"\t"/utf8, Remain/binary>> ->
trim_space_to_elem_begin(Remain);
<<"<"/utf8, Remain@1/binary>> ->
<<"<"/utf8, Remain@1/binary>>;
_ ->
In
end.
-spec remove_quotes(binary()) -> binary().
remove_quotes(In) ->
Remove_first_quote = fun(Str) -> case Str of
<<"\""/utf8, Remain/binary>> ->
Remain;
_ ->
Str
end end,
_pipe = In,
_pipe@1 = Remove_first_quote(_pipe),
_pipe@2 = gleam@string:reverse(_pipe@1),
_pipe@3 = Remove_first_quote(_pipe@2),
gleam@string:reverse(_pipe@3).
-spec do_get_attrs(binary(), binary(), binary(), boolean()) -> {list(attribute()),
binary()}.
do_get_attrs(In, Key, Val, Finding_value) ->
case In of
<<""/utf8>> ->
case {Key, Val} of
{<<""/utf8>>, <<""/utf8>>} ->
{[], <<""/utf8>>};
{_, <<""/utf8>>} ->
{[], Key};
{_, _} ->
{[{attribute, Key, remove_quotes(Val)}], <<""/utf8>>}
end;
<<">"/utf8>> ->
case {Key, Val} of
{<<""/utf8>>, <<""/utf8>>} ->
{[], <<""/utf8>>};
{_, <<""/utf8>>} ->
{[], Key};
{_, _} ->
{[{attribute, Key, remove_quotes(Val)}], <<""/utf8>>}
end;
<<">"/utf8, Remain/binary>> ->
case {Key, Val} of
{<<""/utf8>>, <<""/utf8>>} ->
{[], Remain};
{_, _} ->
{[{attribute, Key, remove_quotes(Val)}], Remain}
end;
<<" "/utf8, Remain@1/binary>> when not Finding_value ->
do_get_attrs(Remain@1, Key, Val, Finding_value);
<<"\n"/utf8, Remain@1/binary>> when not Finding_value ->
do_get_attrs(Remain@1, Key, Val, Finding_value);
<<"\t"/utf8, Remain@1/binary>> when not Finding_value ->
do_get_attrs(Remain@1, Key, Val, Finding_value);
<<" "/utf8, Remain@2/binary>> ->
{Attrs, Remain_after_attr} = do_get_attrs(
Remain@2,
<<""/utf8>>,
<<""/utf8>>,
false
),
{[{attribute, Key, remove_quotes(Val)} | Attrs], Remain_after_attr};
<<"\n"/utf8, Remain@2/binary>> ->
{Attrs, Remain_after_attr} = do_get_attrs(
Remain@2,
<<""/utf8>>,
<<""/utf8>>,
false
),
{[{attribute, Key, remove_quotes(Val)} | Attrs], Remain_after_attr};
<<"\t"/utf8, Remain@2/binary>> ->
{Attrs, Remain_after_attr} = do_get_attrs(
Remain@2,
<<""/utf8>>,
<<""/utf8>>,
false
),
{[{attribute, Key, remove_quotes(Val)} | Attrs], Remain_after_attr};
<<"="/utf8, Remain@3/binary>> ->
do_get_attrs(Remain@3, Key, <<""/utf8>>, true);
_ ->
_assert_subject = gleam@string:pop_grapheme(In),
{ok, {Head, Remain@4}} = case _assert_subject of
{ok, {_, _}} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"html_parser"/utf8>>,
function => <<"do_get_attrs"/utf8>>,
line => 120})
end,
case Finding_value of
true ->
do_get_attrs(
Remain@4,
Key,
<<Val/binary, Head/binary>>,
Finding_value
);
false ->
do_get_attrs(
Remain@4,
<<Key/binary, Head/binary>>,
<<""/utf8>>,
Finding_value
)
end
end.
-spec get_attrs(binary()) -> {list(attribute()), binary()}.
get_attrs(In) ->
_pipe = In,
_pipe@1 = trim_space_to_elem_begin(_pipe),
do_get_attrs(_pipe@1, <<""/utf8>>, <<""/utf8>>, false).
-spec do_get_first_element(binary(), binary(), current_element_type()) -> {element(),
binary()}.
do_get_first_element(In, Out, Currently_parsing) ->
case In of
<<"</"/utf8, Remain/binary>> ->
case Out of
<<""/utf8>> ->
do_get_first_element(Remain, Out, 'end');
_ ->
{{content, Out}, <<"</"/utf8, Remain/binary>>}
end;
<<"<"/utf8, Remain@1/binary>> ->
case Out of
<<""/utf8>> ->
do_get_first_element(Remain@1, Out, start);
_ ->
{{content, Out}, <<"<"/utf8, Remain@1/binary>>}
end;
<<">"/utf8, Remain@2/binary>> ->
case Currently_parsing of
start ->
{{start_element, Out, [], []}, Remain@2};
'end' ->
{{end_element, Out}, Remain@2};
none ->
{{content, Out}, Remain@2}
end;
<<" "/utf8, Remain@3/binary>> when Currently_parsing =:= start ->
{Attrs, Remain_after_attr} = get_attrs(Remain@3),
{{start_element, Out, Attrs, []}, Remain_after_attr};
<<"\n"/utf8, Remain@3/binary>> when Currently_parsing =:= start ->
{Attrs, Remain_after_attr} = get_attrs(Remain@3),
{{start_element, Out, Attrs, []}, Remain_after_attr};
<<"\t"/utf8, Remain@3/binary>> when Currently_parsing =:= start ->
{Attrs, Remain_after_attr} = get_attrs(Remain@3),
{{start_element, Out, Attrs, []}, Remain_after_attr};
<<""/utf8>> ->
{empty_element, <<""/utf8>>};
_ ->
_assert_subject = gleam@string:pop_grapheme(In),
{ok, {Head, Remain@4}} = case _assert_subject of
{ok, {_, _}} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"html_parser"/utf8>>,
function => <<"do_get_first_element"/utf8>>,
line => 81})
end,
do_get_first_element(
Remain@4,
<<Out/binary, Head/binary>>,
Currently_parsing
)
end.
-spec get_first_element(binary()) -> {element(), binary()}.
get_first_element(In) ->
_pipe = In,
_pipe@1 = trim_space_to_elem_begin(_pipe),
do_get_first_element(_pipe@1, <<""/utf8>>, none).
-spec as_list(binary()) -> list(element()).
as_list(In) ->
case In of
<<""/utf8>> ->
[];
_ ->
{First, Remain} = get_first_element(In),
[First | as_list(Remain)]
end.
-spec do_as_tree(binary(), element()) -> {element(), binary()}.
do_as_tree(In, Current) ->
{Next, Remain} = get_first_element(In),
{start_element, Cur_name, Cur_attrs, Cur_children} = case Current of
{start_element, _, _, _} -> Current;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"html_parser"/utf8>>,
function => <<"do_as_tree"/utf8>>,
line => 164})
end,
case Next of
{end_element, Name} when Name =:= Cur_name ->
{{start_element,
Cur_name,
Cur_attrs,
begin
_pipe = Cur_children,
lists:reverse(_pipe)
end},
Remain};
{end_element, _} ->
do_as_tree(Remain, Current);
_ ->
{Child_tree, Remain_after_child} = do_as_tree(Remain, Next),
do_as_tree(
Remain_after_child,
{start_element,
Cur_name,
Cur_attrs,
[Child_tree | Cur_children]}
)
end.
-spec as_tree(binary()) -> element().
as_tree(In) ->
{First, Remain} = get_first_element(In),
{Result, _} = do_as_tree(Remain, First),
Result.