Current section
Files
Jump to
Current section
Files
src/commonmark@internal@parser@inline.erl
-module(commonmark@internal@parser@inline).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([parse_text/1]).
-export_type([inline_lexer/0, inline_wrapper/0]).
-type inline_lexer() :: {entity, binary(), binary()} |
{escaped, binary()} |
{word, binary()} |
{white_space, binary()} |
less_than |
greater_than |
backtick |
tilde |
asterisk |
underscore |
open_bracket |
close_bracket |
image_start |
open_square_bracket |
close_square_bracket |
single_quote |
double_quote |
exclamation |
soft_line_break |
{hard_line_break, binary()}.
-type inline_wrapper() :: {lexed_element, inline_lexer()} |
{email_autolink, list(inline_wrapper())} |
{uri_autolink, list(inline_wrapper())} |
{backtick_string, integer()} |
{tilde_string, integer()} |
{asterisk_string, integer()} |
{underscore_string, integer()} |
{uri_image, binary(), binary(), gleam@option:option(binary())} |
{uri_link, list(inline_wrapper()), binary(), gleam@option:option(binary())} |
{emphasis, list(inline_wrapper()), commonmark@ast:emphasis_marker()} |
{strong_emphasis, list(inline_wrapper()), commonmark@ast:emphasis_marker()} |
{code_span, integer(), list(inline_wrapper())} |
{strikethrough, integer(), list(inline_wrapper())}.
-spec parse_code_span(integer(), list(inline_wrapper())) -> list(inline_wrapper()).
parse_code_span(Size, Previous) ->
case gleam@list:split_while(
Previous,
fun(N) -> N /= {backtick_string, Size} end
) of
{_, []} ->
[{backtick_string, Size} | Previous];
{Wrapped, [_ | Rest]} ->
[{code_span, Size, lists:reverse(Wrapped)} | Rest]
end.
-spec is_not_less_than(inline_wrapper()) -> boolean().
is_not_less_than(V) ->
case V of
{lexed_element, less_than} ->
false;
_ ->
true
end.
-spec parse_strikethrough(integer(), list(inline_wrapper())) -> list(inline_wrapper()).
parse_strikethrough(Size, Previous) ->
case gleam@list:split_while(
Previous,
fun(N) -> N /= {tilde_string, Size} end
) of
{_, []} ->
[{tilde_string, Size} | Previous];
{Wrapped, [_ | Rest]} ->
[{strikethrough, Size, lists:reverse(Wrapped)} | Rest]
end.
-spec parse_emphasis(inline_wrapper(), list(inline_wrapper())) -> {list(inline_wrapper()),
list(inline_wrapper())}.
parse_emphasis(Final, Previous) ->
{Wrapped, Rest} = gleam@list:split_while(
Previous,
fun(N) -> case {N, Final} of
{{asterisk_string, L}, {asterisk_string, R}} ->
not (((L > 0) andalso (R > 0)) andalso ((((L + R) rem 3) /= 0)
orelse (((L rem 3) =:= 0) andalso ((R rem 3) =:= 0))));
{{underscore_string, L}, {underscore_string, R}} ->
not (((L > 0) andalso (R > 0)) andalso ((((L + R) rem 3) /= 0)
orelse (((L rem 3) =:= 0) andalso ((R rem 3) =:= 0))));
{_, _} ->
true
end end
),
case {Final, Rest} of
{{asterisk_string, R@1}, [{asterisk_string, L@1} | Rest@1]} when (L@1 >= 2) andalso (R@1 >= 2) ->
{[{asterisk_string, R@1 - 2}],
[{strong_emphasis,
lists:reverse(Wrapped),
asterisk_emphasis_marker},
{asterisk_string, L@1 - 2} |
Rest@1]};
{{asterisk_string, R@2}, [{asterisk_string, L@2} | Rest@2]} ->
{[{asterisk_string, R@2 - 1}],
[{emphasis, lists:reverse(Wrapped), asterisk_emphasis_marker},
{asterisk_string, L@2 - 1} |
Rest@2]};
{{underscore_string, R@3}, [{underscore_string, L@3} | Rest@3]} when (L@3 >= 2) andalso (R@3 >= 2) ->
{[{underscore_string, R@3 - 2}],
[{strong_emphasis,
lists:reverse(Wrapped),
underscore_emphasis_marker},
{underscore_string, L@3 - 2} |
Rest@3]};
{{underscore_string, R@4}, [{underscore_string, L@4} | Rest@4]} ->
{[{underscore_string, R@4 - 1}],
[{emphasis, lists:reverse(Wrapped), underscore_emphasis_marker},
{underscore_string, L@4 - 1} |
Rest@4]};
{_, _} ->
{[], [Final | Previous]}
end.
-spec is_not_end_of_href(inline_wrapper()) -> boolean().
is_not_end_of_href(V) ->
case V of
{lexed_element, greater_than} ->
false;
{lexed_element, soft_line_break} ->
false;
{lexed_element, {hard_line_break, _}} ->
false;
_ ->
true
end.
-spec is_not_link_or_image_start(inline_wrapper()) -> boolean().
is_not_link_or_image_start(V) ->
case V of
{lexed_element, image_start} ->
false;
{lexed_element, open_square_bracket} ->
false;
{lexed_element, close_square_bracket} ->
false;
_ ->
true
end.
-spec trim_left(binary()) -> binary().
trim_left(X) ->
case X of
<<" "/utf8, X@1/binary>> ->
trim_left(X@1);
<<"\t"/utf8, X@2/binary>> ->
trim_left(X@2);
_ ->
X
end.
-spec trim_right(binary()) -> binary().
trim_right(X) ->
_pipe = X,
_pipe@1 = gleam@string:reverse(_pipe),
_pipe@2 = trim_left(_pipe@1),
gleam@string:reverse(_pipe@2).
-spec do_finalise_plain_text(
list(commonmark@ast:inline_node()),
list(commonmark@ast:inline_node()),
boolean()
) -> list(commonmark@ast:inline_node()).
do_finalise_plain_text(Ast, Acc, Trim_ends) ->
case {Ast, Acc} of
{[], [{plain_text, Y} | Ys]} when Trim_ends ->
_pipe = [{plain_text, trim_right(Y)} | Ys],
lists:reverse(_pipe);
{[], _} ->
_pipe@1 = Acc,
lists:reverse(_pipe@1);
{[{plain_text, <<""/utf8>>} | Xs], _} ->
do_finalise_plain_text(Xs, Acc, Trim_ends);
{[{strong_emphasis, Content, Marker} | Xs@1], _} ->
do_finalise_plain_text(
Xs@1,
[{strong_emphasis,
do_finalise_plain_text(Content, [], false),
Marker} |
Acc],
Trim_ends
);
{[{emphasis, Content@1, Marker@1} | Xs@2], _} ->
do_finalise_plain_text(
Xs@2,
[{emphasis,
do_finalise_plain_text(Content@1, [], Trim_ends),
Marker@1} |
Acc],
Trim_ends
);
{[{strike_through, Content@2} | Xs@3], _} ->
do_finalise_plain_text(
Xs@3,
[{strike_through,
do_finalise_plain_text(Content@2, [], Trim_ends)} |
Acc],
Trim_ends
);
{[{plain_text, X} | Xs@4], [{plain_text, Y@1} | Ys@1]} ->
do_finalise_plain_text(
Xs@4,
[{plain_text, <<Y@1/binary, X/binary>>} | Ys@1],
Trim_ends
);
{[{plain_text, X@1} | Xs@5], []} when Trim_ends ->
do_finalise_plain_text(
Xs@5,
[{plain_text, trim_left(X@1)} | Acc],
Trim_ends
);
{[{plain_text, X@1} | Xs@5], [hard_line_break | _]} when Trim_ends ->
do_finalise_plain_text(
Xs@5,
[{plain_text, trim_left(X@1)} | Acc],
Trim_ends
);
{[{plain_text, X@1} | Xs@5], [soft_line_break | _]} when Trim_ends ->
do_finalise_plain_text(
Xs@5,
[{plain_text, trim_left(X@1)} | Acc],
Trim_ends
);
{[hard_line_break = X@2 | Xs@6], [{plain_text, Y@2} | Ys@2]} when Trim_ends ->
do_finalise_plain_text(
Xs@6,
[X@2, {plain_text, trim_right(Y@2)} | Ys@2],
Trim_ends
);
{[soft_line_break = X@2 | Xs@6], [{plain_text, Y@2} | Ys@2]} when Trim_ends ->
do_finalise_plain_text(
Xs@6,
[X@2, {plain_text, trim_right(Y@2)} | Ys@2],
Trim_ends
);
{[X@3 | Xs@7], _} ->
do_finalise_plain_text(Xs@7, [X@3 | Acc], Trim_ends)
end.
-spec replace_insecure_byte(integer()) -> integer().
replace_insecure_byte(N) ->
case gleam@list:contains([0], N) of
true ->
16#fffd;
false ->
N
end.
-spec translate_numerical_entity({ok, integer()} | {error, nil}, list(binary())) -> {ok,
{list(binary()), binary()}} |
{error, nil}.
translate_numerical_entity(Codepoint, Rest) ->
_pipe = Codepoint,
_pipe@1 = gleam@result:map(_pipe, fun replace_insecure_byte/1),
_pipe@2 = gleam@result:'try'(_pipe@1, fun gleam@string:utf_codepoint/1),
gleam@result:map(
_pipe@2,
fun(Cp) -> {Rest, gleam_stdlib:utf_codepoint_list_to_string([Cp])} end
).
-spec match_entity(list(binary())) -> {ok, {list(binary()), binary(), binary()}} |
{error, nil}.
match_entity(Input) ->
_pipe = commonmark@internal@parser@entity:match_html_entity(Input),
gleam@result:try_recover(
_pipe,
fun(_) ->
_assert_subject = gleam@regex:from_string(
<<"^#([0-9]{1,7});"/utf8>>
),
{ok, Dec_entity} = 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 => <<"commonmark/internal/parser/inline"/utf8>>,
function => <<"match_entity"/utf8>>,
line => 183})
end,
_assert_subject@1 = gleam@regex:from_string(
<<"^#([xX]([0-9a-fA-F]{1,6}));"/utf8>>
),
{ok, Hex_entity} = case _assert_subject@1 of
{ok, _} -> _assert_subject@1;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@1,
module => <<"commonmark/internal/parser/inline"/utf8>>,
function => <<"match_entity"/utf8>>,
line => 184})
end,
Potential = begin
_pipe@1 = gleam@list:take(Input, 9),
gleam@string:join(_pipe@1, <<""/utf8>>)
end,
case {gleam@regex:scan(Dec_entity, Potential),
gleam@regex:scan(Hex_entity, Potential)} of
{[{match, Full, [{some, N}]}], _} ->
_pipe@2 = N,
_pipe@3 = gleam@int:parse(_pipe@2),
_pipe@4 = translate_numerical_entity(
_pipe@3,
gleam@list:drop(Input, gleam@string:length(Full))
),
gleam@result:map(
_pipe@4,
fun(R) ->
{erlang:element(1, R), N, erlang:element(2, R)}
end
);
{_, [{match, Full@1, [{some, M}, {some, N@1}]}]} ->
_pipe@5 = N@1,
_pipe@6 = gleam@int:base_parse(_pipe@5, 16),
_pipe@7 = translate_numerical_entity(
_pipe@6,
gleam@list:drop(Input, gleam@string:length(Full@1))
),
gleam@result:map(
_pipe@7,
fun(R@1) ->
{erlang:element(1, R@1), M, erlang:element(2, R@1)}
end
);
{_, _} ->
{error, nil}
end
end
).
-spec do_lex_inline_text(list(binary()), list(inline_lexer())) -> list(inline_lexer()).
do_lex_inline_text(Input, Acc) ->
case Input of
[] ->
_pipe = Acc,
lists:reverse(_pipe);
[<<"<"/utf8>> | Xs] ->
do_lex_inline_text(Xs, [less_than | Acc]);
[<<">"/utf8>> | Xs@1] ->
do_lex_inline_text(Xs@1, [greater_than | Acc]);
[<<"!"/utf8>>, <<"["/utf8>> | Xs@2] ->
do_lex_inline_text(Xs@2, [image_start | Acc]);
[<<"["/utf8>> | Xs@3] ->
do_lex_inline_text(Xs@3, [open_square_bracket | Acc]);
[<<"]"/utf8>> | Xs@4] ->
do_lex_inline_text(Xs@4, [close_square_bracket | Acc]);
[<<"("/utf8>> | Xs@5] ->
do_lex_inline_text(Xs@5, [open_bracket | Acc]);
[<<")"/utf8>> | Xs@6] ->
do_lex_inline_text(Xs@6, [close_bracket | Acc]);
[<<"'"/utf8>> | Xs@7] ->
do_lex_inline_text(Xs@7, [single_quote | Acc]);
[<<"\""/utf8>> | Xs@8] ->
do_lex_inline_text(Xs@8, [double_quote | Acc]);
[<<"!"/utf8>> | Xs@9] ->
do_lex_inline_text(Xs@9, [exclamation | Acc]);
[<<"`"/utf8>> | Xs@10] ->
do_lex_inline_text(Xs@10, [backtick | Acc]);
[<<"~"/utf8>> | Xs@11] ->
do_lex_inline_text(Xs@11, [tilde | Acc]);
[<<"_"/utf8>> | Xs@12] ->
do_lex_inline_text(Xs@12, [underscore | Acc]);
[<<"*"/utf8>> | Xs@13] ->
do_lex_inline_text(Xs@13, [asterisk | Acc]);
[<<"\\"/utf8>>, <<"\n"/utf8>> | Xs@14] ->
do_lex_inline_text(
Xs@14,
[{hard_line_break, <<"\\\n"/utf8>>} | Acc]
);
[<<" "/utf8>>, <<" "/utf8>>, <<"\n"/utf8>> | Xs@15] ->
do_lex_inline_text(
Xs@15,
[{hard_line_break, <<" \n"/utf8>>} | Acc]
);
[<<"\n"/utf8>> | Xs@16] ->
do_lex_inline_text(Xs@16, [soft_line_break | Acc]);
[<<"&"/utf8>> | Xs@17] ->
case match_entity(Xs@17) of
{ok, {Rest, E, Replacement}} ->
do_lex_inline_text(Rest, [{entity, E, Replacement} | Acc]);
{error, _} ->
case Acc of
[{word, T} | Rest@1] ->
do_lex_inline_text(
Xs@17,
[{word, <<T/binary, "&"/utf8>>} | Rest@1]
);
_ ->
do_lex_inline_text(
Xs@17,
[{word, <<"&"/utf8>>} | Acc]
)
end
end;
[<<"\\"/utf8>>, G | Xs@18] ->
case gleam@list:contains(
[<<"!"/utf8>>,
<<"\""/utf8>>,
<<"#"/utf8>>,
<<"$"/utf8>>,
<<"%"/utf8>>,
<<"&"/utf8>>,
<<"'"/utf8>>,
<<"("/utf8>>,
<<")"/utf8>>,
<<"*"/utf8>>,
<<"+"/utf8>>,
<<","/utf8>>,
<<"-"/utf8>>,
<<"."/utf8>>,
<<"/"/utf8>>,
<<":"/utf8>>,
<<";"/utf8>>,
<<"<"/utf8>>,
<<"="/utf8>>,
<<">"/utf8>>,
<<"?"/utf8>>,
<<"@"/utf8>>,
<<"["/utf8>>,
<<"]"/utf8>>,
<<"\\"/utf8>>,
<<"^"/utf8>>,
<<"_"/utf8>>,
<<"`"/utf8>>,
<<"{"/utf8>>,
<<"|"/utf8>>,
<<"}"/utf8>>,
<<"~"/utf8>>],
G
) of
true ->
do_lex_inline_text(Xs@18, [{escaped, G} | Acc]);
false ->
case Acc of
[{word, T@1} | Rest@2] ->
do_lex_inline_text(
Xs@18,
[{word,
<<<<T@1/binary, "\\"/utf8>>/binary,
G/binary>>} |
Rest@2]
);
_ ->
do_lex_inline_text(
Xs@18,
[{word, <<"\\"/utf8, G/binary>>} | Acc]
)
end
end;
[<<" "/utf8>> = W | Xs@19] ->
case Acc of
[{white_space, Ww} | Rest@3] ->
do_lex_inline_text(
Xs@19,
[{white_space, <<Ww/binary, W/binary>>} | Rest@3]
);
_ ->
do_lex_inline_text(Xs@19, [{white_space, W} | Acc])
end;
[<<"\t"/utf8>> = W | Xs@19] ->
case Acc of
[{white_space, Ww} | Rest@3] ->
do_lex_inline_text(
Xs@19,
[{white_space, <<Ww/binary, W/binary>>} | Rest@3]
);
_ ->
do_lex_inline_text(Xs@19, [{white_space, W} | Acc])
end;
[X | Xs@20] ->
case Acc of
[{word, T@2} | Rest@4] ->
do_lex_inline_text(
Xs@20,
[{word, <<T@2/binary, X/binary>>} | Rest@4]
);
_ ->
do_lex_inline_text(Xs@20, [{word, X} | Acc])
end
end.
-spec list_to_text(list(inline_wrapper())) -> binary().
list_to_text(Els) ->
_pipe = gleam@list:map(Els, fun to_text/1),
gleam@string:join(_pipe, <<""/utf8>>).
-spec to_text(inline_wrapper()) -> binary().
to_text(El) ->
case El of
{lexed_element, {word, S}} ->
S;
{lexed_element, {white_space, S}} ->
S;
{lexed_element, {escaped, S@1}} ->
S@1;
{lexed_element, less_than} ->
<<"<"/utf8>>;
{lexed_element, greater_than} ->
<<">"/utf8>>;
{lexed_element, backtick} ->
<<"`"/utf8>>;
{lexed_element, tilde} ->
<<"~"/utf8>>;
{lexed_element, asterisk} ->
<<"*"/utf8>>;
{lexed_element, underscore} ->
<<"_"/utf8>>;
{lexed_element, {hard_line_break, _}} ->
<<"\n"/utf8>>;
{lexed_element, soft_line_break} ->
<<"\n"/utf8>>;
{lexed_element, {entity, _, E}} ->
E;
{lexed_element, open_bracket} ->
<<"("/utf8>>;
{lexed_element, close_bracket} ->
<<")"/utf8>>;
{lexed_element, image_start} ->
<<"!["/utf8>>;
{lexed_element, open_square_bracket} ->
<<"["/utf8>>;
{lexed_element, close_square_bracket} ->
<<"]"/utf8>>;
{lexed_element, single_quote} ->
<<"'"/utf8>>;
{lexed_element, double_quote} ->
<<"\""/utf8>>;
{lexed_element, exclamation} ->
<<"!"/utf8>>;
{code_span, _, Contents} ->
list_to_text(Contents);
{emphasis, Contents, _} ->
list_to_text(Contents);
{strong_emphasis, Contents, _} ->
list_to_text(Contents);
{backtick_string, Count} ->
gleam@string:repeat(<<"`"/utf8>>, Count);
{asterisk_string, Count@1} ->
gleam@string:repeat(<<"*"/utf8>>, Count@1);
{underscore_string, Count@2} ->
gleam@string:repeat(<<"_"/utf8>>, Count@2);
{tilde_string, Count@3} ->
gleam@string:repeat(<<"~"/utf8>>, Count@3);
{strikethrough, _, Content} ->
list_to_text(Content);
{email_autolink, Ls} ->
list_to_text(Ls);
{uri_autolink, Ls} ->
list_to_text(Ls);
{uri_image, Alt, _, _} ->
Alt;
{uri_link, Content@1, _, _} ->
list_to_text(Content@1)
end.
-spec list_to_string(list(inline_wrapper())) -> binary().
list_to_string(Els) ->
_pipe = gleam@list:map(Els, fun to_string/1),
gleam@string:join(_pipe, <<""/utf8>>).
-spec to_string(inline_wrapper()) -> binary().
to_string(El) ->
case El of
{lexed_element, {hard_line_break, S}} ->
S;
{lexed_element, {word, S}} ->
S;
{lexed_element, {white_space, S}} ->
S;
{lexed_element, {escaped, S@1}} ->
<<"\\"/utf8, S@1/binary>>;
{lexed_element, less_than} ->
<<"<"/utf8>>;
{lexed_element, greater_than} ->
<<">"/utf8>>;
{lexed_element, backtick} ->
<<"`"/utf8>>;
{lexed_element, tilde} ->
<<"~"/utf8>>;
{lexed_element, asterisk} ->
<<"*"/utf8>>;
{lexed_element, underscore} ->
<<"_"/utf8>>;
{lexed_element, soft_line_break} ->
<<"\n"/utf8>>;
{lexed_element, {entity, E, _}} ->
<<"&"/utf8, E/binary>>;
{lexed_element, open_bracket} ->
<<"("/utf8>>;
{lexed_element, close_bracket} ->
<<")"/utf8>>;
{lexed_element, image_start} ->
<<"!["/utf8>>;
{lexed_element, open_square_bracket} ->
<<"["/utf8>>;
{lexed_element, close_square_bracket} ->
<<"]"/utf8>>;
{lexed_element, single_quote} ->
<<"'"/utf8>>;
{lexed_element, double_quote} ->
<<"\""/utf8>>;
{lexed_element, exclamation} ->
<<"!"/utf8>>;
{emphasis, Contents, Marker} ->
case Marker of
asterisk_emphasis_marker ->
<<<<"*"/utf8, (list_to_string(Contents))/binary>>/binary,
"*"/utf8>>;
underscore_emphasis_marker ->
<<<<"_"/utf8, (list_to_string(Contents))/binary>>/binary,
"_"/utf8>>
end;
{strong_emphasis, Contents@1, Marker@1} ->
case Marker@1 of
asterisk_emphasis_marker ->
<<<<"**"/utf8, (list_to_string(Contents@1))/binary>>/binary,
"**"/utf8>>;
underscore_emphasis_marker ->
<<<<"__"/utf8, (list_to_string(Contents@1))/binary>>/binary,
"__"/utf8>>
end;
{backtick_string, Count} ->
gleam@string:repeat(<<"`"/utf8>>, Count);
{asterisk_string, Count@1} ->
gleam@string:repeat(<<"*"/utf8>>, Count@1);
{underscore_string, Count@2} ->
gleam@string:repeat(<<"_"/utf8>>, Count@2);
{code_span, Count@3, Content} ->
<<<<(gleam@string:repeat(<<"`"/utf8>>, Count@3))/binary,
(list_to_string(Content))/binary>>/binary,
(gleam@string:repeat(<<"`"/utf8>>, Count@3))/binary>>;
{tilde_string, Count@4} ->
gleam@string:repeat(<<"~"/utf8>>, Count@4);
{strikethrough, Count@5, Content@1} ->
<<<<(gleam@string:repeat(<<"~"/utf8>>, Count@5))/binary,
(list_to_string(Content@1))/binary>>/binary,
(gleam@string:repeat(<<"~"/utf8>>, Count@5))/binary>>;
{email_autolink, Ls} ->
<<<<"<"/utf8, (list_to_string(Ls))/binary>>/binary, ">"/utf8>>;
{uri_autolink, Ls@1} ->
<<<<"<"/utf8, (list_to_string(Ls@1))/binary>>/binary, ">"/utf8>>;
{uri_image, Alt, Href, Title} ->
Title@1 = begin
_pipe = Title,
_pipe@1 = gleam@option:map(
_pipe,
fun(T) -> <<<<" '"/utf8, T/binary>>/binary, "'"/utf8>> end
),
gleam@option:unwrap(_pipe@1, <<""/utf8>>)
end,
<<<<<<<<<<<<""/utf8>>;
{uri_link, Content@2, Href@1, Title@2} ->
Title@3 = begin
_pipe@2 = Title@2,
_pipe@3 = gleam@option:map(
_pipe@2,
fun(T@1) ->
<<<<" '"/utf8, T@1/binary>>/binary, "'"/utf8>>
end
),
gleam@option:unwrap(_pipe@3, <<""/utf8>>)
end,
<<<<<<<<<<<<"["/utf8, (list_to_string(Content@2))/binary>>/binary,
"](<"/utf8>>/binary,
Href@1/binary>>/binary,
">"/utf8>>/binary,
Title@3/binary>>/binary,
")"/utf8>>
end.
-spec parse_autolink(list(inline_wrapper())) -> {ok, inline_wrapper()} |
{error, nil}.
parse_autolink(Href) ->
_assert_subject = gleam@regex:from_string(
<<"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"/utf8>>
),
{ok, Email_regex} = 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 => <<"commonmark/internal/parser/inline"/utf8>>,
function => <<"parse_autolink"/utf8>>,
line => 274})
end,
_assert_subject@1 = gleam@regex:from_string(
<<"^[a-zA-Z][-a-zA-Z+.]{1,31}:[^ \t]+$"/utf8>>
),
{ok, Uri_regex} = case _assert_subject@1 of
{ok, _} -> _assert_subject@1;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@1,
module => <<"commonmark/internal/parser/inline"/utf8>>,
function => <<"parse_autolink"/utf8>>,
line => 278})
end,
Href_str = list_to_string(Href),
case {gleam@regex:check(Email_regex, Href_str),
gleam@regex:check(Uri_regex, Href_str)} of
{true, _} ->
{ok, {email_autolink, Href}};
{_, true} ->
{ok, {uri_autolink, Href}};
{false, false} ->
{error, nil}
end.
-spec do_parse_inline_wrappers(list(inline_lexer()), list(inline_wrapper())) -> list(inline_wrapper()).
do_parse_inline_wrappers(Lexed, Acc) ->
case Lexed of
[] ->
_pipe = Acc,
lists:reverse(_pipe);
[greater_than | Ls] ->
case gleam@list:split_while(Acc, fun is_not_less_than/1) of
{_, []} ->
do_parse_inline_wrappers(
Ls,
[{lexed_element, greater_than} | Acc]
);
{To_wrap, [_ | Rest]} ->
case parse_autolink(
begin
_pipe@1 = To_wrap,
lists:reverse(_pipe@1)
end
) of
{ok, Wrapped} ->
do_parse_inline_wrappers(Ls, [Wrapped | Rest]);
{error, _} ->
do_parse_inline_wrappers(
Ls,
[{lexed_element, greater_than} | Acc]
)
end
end;
[backtick, backtick | Ls@1] ->
case Acc of
[{backtick_string, Count} | Rest@1] ->
do_parse_inline_wrappers(
[backtick | Ls@1],
[{backtick_string, Count + 1} | Rest@1]
);
_ ->
do_parse_inline_wrappers(
[backtick | Ls@1],
[{backtick_string, 1} | Acc]
)
end;
[backtick | Ls@2] ->
Acc@1 = case Acc of
[{backtick_string, Count@1} | Rest@2] ->
parse_code_span(Count@1 + 1, Rest@2);
_ ->
parse_code_span(1, Acc)
end,
do_parse_inline_wrappers(Ls@2, Acc@1);
[asterisk, asterisk | Ls@3] ->
case Acc of
[{asterisk_string, Count@2} | Rest@3] ->
do_parse_inline_wrappers(
[asterisk | Ls@3],
[{asterisk_string, Count@2 + 1} | Rest@3]
);
_ ->
do_parse_inline_wrappers(
[asterisk | Ls@3],
[{asterisk_string, 1} | Acc]
)
end;
[asterisk | Ls@4] ->
case Acc of
[{asterisk_string, Count@3} | Rest@4] ->
do_parse_inline_wrappers(
Ls@4,
[{asterisk_string, Count@3 + 1} | Rest@4]
);
_ ->
do_parse_inline_wrappers(Ls@4, [{asterisk_string, 1} | Acc])
end;
[underscore, underscore | Ls@5] ->
case Acc of
[{underscore_string, Count@4} | Rest@5] ->
do_parse_inline_wrappers(
[underscore | Ls@5],
[{underscore_string, Count@4 + 1} | Rest@5]
);
_ ->
do_parse_inline_wrappers(
[underscore | Ls@5],
[{underscore_string, 1} | Acc]
)
end;
[underscore | Ls@6] ->
case Acc of
[{underscore_string, Count@5} | Rest@6] ->
do_parse_inline_wrappers(
Ls@6,
[{underscore_string, Count@5 + 1} | Rest@6]
);
_ ->
do_parse_inline_wrappers(
Ls@6,
[{underscore_string, 1} | Acc]
)
end;
[tilde, tilde | Ls@7] ->
case gleam@list:first(Acc) of
{ok, {tilde_string, Count@6}} ->
do_parse_inline_wrappers(
[tilde | Ls@7],
[{tilde_string, Count@6 + 1} | gleam@list:drop(Acc, 1)]
);
_ ->
do_parse_inline_wrappers(
[tilde | Ls@7],
[{tilde_string, 1} | Acc]
)
end;
[tilde | Ls@8] ->
{Count@8, Acc@2} = case gleam@list:first(Acc) of
{ok, {tilde_string, Count@7}} ->
{Count@7 + 1, gleam@list:drop(Acc, 1)};
_ ->
{1, Acc}
end,
do_parse_inline_wrappers(Ls@8, [{tilde_string, Count@8} | Acc@2]);
[image_start = V | Ls@9] ->
do_parse_inline_wrappers(Ls@9, [{lexed_element, V} | Acc]);
[exclamation = V | Ls@9] ->
do_parse_inline_wrappers(Ls@9, [{lexed_element, V} | Acc]);
[single_quote = V | Ls@9] ->
do_parse_inline_wrappers(Ls@9, [{lexed_element, V} | Acc]);
[double_quote = V | Ls@9] ->
do_parse_inline_wrappers(Ls@9, [{lexed_element, V} | Acc]);
[open_bracket = V | Ls@9] ->
do_parse_inline_wrappers(Ls@9, [{lexed_element, V} | Acc]);
[close_bracket = V | Ls@9] ->
do_parse_inline_wrappers(Ls@9, [{lexed_element, V} | Acc]);
[open_square_bracket = V | Ls@9] ->
do_parse_inline_wrappers(Ls@9, [{lexed_element, V} | Acc]);
[close_square_bracket = V | Ls@9] ->
do_parse_inline_wrappers(Ls@9, [{lexed_element, V} | Acc]);
[{entity, _, _} = V | Ls@9] ->
do_parse_inline_wrappers(Ls@9, [{lexed_element, V} | Acc]);
[{escaped, _} = V | Ls@9] ->
do_parse_inline_wrappers(Ls@9, [{lexed_element, V} | Acc]);
[less_than = V | Ls@9] ->
do_parse_inline_wrappers(Ls@9, [{lexed_element, V} | Acc]);
[{hard_line_break, _} = V | Ls@9] ->
do_parse_inline_wrappers(Ls@9, [{lexed_element, V} | Acc]);
[soft_line_break = V | Ls@9] ->
do_parse_inline_wrappers(Ls@9, [{lexed_element, V} | Acc]);
[{white_space, _} = V | Ls@9] ->
do_parse_inline_wrappers(Ls@9, [{lexed_element, V} | Acc]);
[{word, _} = V | Ls@9] ->
do_parse_inline_wrappers(Ls@9, [{lexed_element, V} | Acc])
end.
-spec parse_link_title(list(inline_wrapper()), binary(), list(inline_wrapper())) -> {ok,
{inline_wrapper(), list(inline_wrapper())}} |
{error, nil}.
parse_link_title(Contents, Href, Ls) ->
Is_not = fun(X) -> fun(Y) -> X /= Y end end,
case Ls of
[{lexed_element, single_quote} | Ls@1] ->
case gleam@list:split_while(
Ls@1,
Is_not({lexed_element, single_quote})
) of
{Title,
[_,
{lexed_element, {white_space, _}},
{lexed_element, close_bracket} |
Ls@2]} ->
{ok,
{{uri_link,
Contents,
Href,
{some, list_to_string(Title)}},
Ls@2}};
{Title, [_, {lexed_element, close_bracket} | Ls@2]} ->
{ok,
{{uri_link,
Contents,
Href,
{some, list_to_string(Title)}},
Ls@2}};
_ ->
{error, nil}
end;
[{lexed_element, double_quote} | Ls@3] ->
case gleam@list:split_while(
Ls@3,
Is_not({lexed_element, double_quote})
) of
{Title@1,
[_,
{lexed_element, {white_space, _}},
{lexed_element, close_bracket} |
Ls@4]} ->
{ok,
{{uri_link,
Contents,
Href,
{some, list_to_string(Title@1)}},
Ls@4}};
{Title@1, [_, {lexed_element, close_bracket} | Ls@4]} ->
{ok,
{{uri_link,
Contents,
Href,
{some, list_to_string(Title@1)}},
Ls@4}};
_ ->
{error, nil}
end;
[{lexed_element, open_bracket} | Ls@5] ->
case gleam@list:split_while(
Ls@5,
Is_not({lexed_element, close_bracket})
) of
{Title@2,
[_,
{lexed_element, {white_space, _}},
{lexed_element, close_bracket} |
Ls@6]} ->
{ok,
{{uri_link,
Contents,
Href,
{some, list_to_string(Title@2)}},
Ls@6}};
{Title@2, [_, {lexed_element, close_bracket} | Ls@6]} ->
{ok,
{{uri_link,
Contents,
Href,
{some, list_to_string(Title@2)}},
Ls@6}};
_ ->
{error, nil}
end;
_ ->
{error, nil}
end.
-spec parse_link(list(inline_wrapper()), list(inline_wrapper())) -> {ok,
{inline_wrapper(), list(inline_wrapper())}} |
{error, nil}.
parse_link(Contents, Ls) ->
case Ls of
[{lexed_element, open_bracket},
{lexed_element, less_than},
{lexed_element, greater_than},
{lexed_element, close_bracket} |
Ls@1] ->
{ok, {{uri_link, Contents, <<""/utf8>>, none}, Ls@1}};
[{lexed_element, open_bracket}, {lexed_element, close_bracket} | Ls@1] ->
{ok, {{uri_link, Contents, <<""/utf8>>, none}, Ls@1}};
[{lexed_element, open_bracket},
{lexed_element, {word, Href}},
{lexed_element, close_bracket} |
Ls@2] ->
{ok, {{uri_link, Contents, Href, none}, Ls@2}};
[{lexed_element, open_bracket},
{lexed_element, {word, Href@1}},
{lexed_element, {white_space, _}} |
Ls@3] ->
parse_link_title(Contents, Href@1, Ls@3);
[{lexed_element, open_bracket}, {lexed_element, less_than} | Ls@4] ->
case gleam@list:split_while(Ls@4, fun is_not_end_of_href/1) of
{Href@2,
[{lexed_element, greater_than},
{lexed_element, close_bracket} |
Ls@5]} ->
{ok,
{{uri_link, Contents, list_to_string(Href@2), none},
Ls@5}};
{Href@3,
[{lexed_element, greater_than},
{lexed_element, {white_space, _}} |
Ls@6]} ->
parse_link_title(Contents, list_to_string(Href@3), Ls@6);
_ ->
{error, nil}
end;
_ ->
{error, nil}
end.
-spec parse_image_title(binary(), binary(), list(inline_wrapper())) -> {ok,
{inline_wrapper(), list(inline_wrapper())}} |
{error, nil}.
parse_image_title(Alt, Href, Ls) ->
Is_not = fun(X) -> fun(Y) -> X /= Y end end,
case Ls of
[{lexed_element, single_quote} | Ls@1] ->
case gleam@list:split_while(
Ls@1,
Is_not({lexed_element, single_quote})
) of
{Title,
[_,
{lexed_element, {white_space, _}},
{lexed_element, close_bracket} |
Ls@2]} ->
{ok,
{{uri_image, Alt, Href, {some, list_to_string(Title)}},
Ls@2}};
{Title, [_, {lexed_element, close_bracket} | Ls@2]} ->
{ok,
{{uri_image, Alt, Href, {some, list_to_string(Title)}},
Ls@2}};
_ ->
{error, nil}
end;
[{lexed_element, double_quote} | Ls@3] ->
case gleam@list:split_while(
Ls@3,
Is_not({lexed_element, double_quote})
) of
{Title@1,
[_,
{lexed_element, {white_space, _}},
{lexed_element, close_bracket} |
Ls@4]} ->
{ok,
{{uri_image, Alt, Href, {some, list_to_string(Title@1)}},
Ls@4}};
{Title@1, [_, {lexed_element, close_bracket} | Ls@4]} ->
{ok,
{{uri_image, Alt, Href, {some, list_to_string(Title@1)}},
Ls@4}};
_ ->
{error, nil}
end;
[{lexed_element, open_bracket} | Ls@5] ->
case gleam@list:split_while(
Ls@5,
Is_not({lexed_element, close_bracket})
) of
{Title@2,
[_,
{lexed_element, {white_space, _}},
{lexed_element, close_bracket} |
Ls@6]} ->
{ok,
{{uri_image, Alt, Href, {some, list_to_string(Title@2)}},
Ls@6}};
{Title@2, [_, {lexed_element, close_bracket} | Ls@6]} ->
{ok,
{{uri_image, Alt, Href, {some, list_to_string(Title@2)}},
Ls@6}};
_ ->
{error, nil}
end;
_ ->
{error, nil}
end.
-spec parse_image(binary(), list(inline_wrapper())) -> {ok,
{inline_wrapper(), list(inline_wrapper())}} |
{error, nil}.
parse_image(Alt, Ls) ->
case Ls of
[{lexed_element, open_bracket},
{lexed_element, less_than},
{lexed_element, greater_than},
{lexed_element, close_bracket} |
Ls@1] ->
{ok, {{uri_image, Alt, <<""/utf8>>, none}, Ls@1}};
[{lexed_element, open_bracket}, {lexed_element, close_bracket} | Ls@1] ->
{ok, {{uri_image, Alt, <<""/utf8>>, none}, Ls@1}};
[{lexed_element, open_bracket},
{lexed_element, {word, Href}},
{lexed_element, close_bracket} |
Ls@2] ->
{ok, {{uri_image, Alt, Href, none}, Ls@2}};
[{lexed_element, open_bracket},
{lexed_element, {word, Href@1}},
{lexed_element, {white_space, _}} |
Ls@3] ->
parse_image_title(Alt, Href@1, Ls@3);
[{lexed_element, open_bracket}, {lexed_element, less_than} | Ls@4] ->
case gleam@list:split_while(Ls@4, fun is_not_end_of_href/1) of
{Href@2,
[{lexed_element, greater_than},
{lexed_element, close_bracket} |
Ls@5]} ->
{ok, {{uri_image, Alt, list_to_string(Href@2), none}, Ls@5}};
{Href@3,
[{lexed_element, greater_than},
{lexed_element, {white_space, _}} |
Ls@6]} ->
parse_image_title(Alt, list_to_string(Href@3), Ls@6);
_ ->
{error, nil}
end;
_ ->
{error, nil}
end.
-spec do_late_binding(list(inline_wrapper()), list(inline_wrapper())) -> list(inline_wrapper()).
do_late_binding(Wrapped, Acc) ->
case Wrapped of
[] ->
_pipe = Acc,
lists:reverse(_pipe);
[{lexed_element, close_square_bracket} | Ls] ->
case gleam@list:split_while(Acc, fun is_not_link_or_image_start/1) of
{To_wrap, [{lexed_element, open_square_bracket} | Rest]} ->
case parse_link(
begin
_pipe@1 = To_wrap,
lists:reverse(_pipe@1)
end,
Ls
) of
{ok, {Wrapped@1, Ls@1}} ->
do_late_binding(Ls@1, [Wrapped@1 | Rest]);
{error, _} ->
do_late_binding(
Ls,
[{lexed_element, close_square_bracket} | Acc]
)
end;
{To_wrap@1, [{lexed_element, image_start} | Rest@1]} ->
case parse_image(
begin
_pipe@2 = To_wrap@1,
_pipe@3 = lists:reverse(_pipe@2),
list_to_text(_pipe@3)
end,
Ls
) of
{ok, {Wrapped@2, Ls@2}} ->
do_late_binding(Ls@2, [Wrapped@2 | Rest@1]);
{error, _} ->
do_late_binding(
Ls,
[{lexed_element, close_square_bracket} | Acc]
)
end;
_ ->
do_late_binding(
Ls,
[{lexed_element, close_square_bracket} | Acc]
)
end;
[{tilde_string, Count} | Xs] ->
case Count =< 2 of
true ->
do_late_binding(Xs, parse_strikethrough(Count, Acc));
false ->
do_late_binding(Xs, [{tilde_string, Count} | Acc])
end;
[{asterisk_string, N} = Str | Xs@1] when N > 0 ->
{Prefix, Acc@1} = parse_emphasis(Str, Acc),
do_late_binding(gleam@list:concat([Prefix, Xs@1]), Acc@1);
[{underscore_string, N} = Str | Xs@1] when N > 0 ->
{Prefix, Acc@1} = parse_emphasis(Str, Acc),
do_late_binding(gleam@list:concat([Prefix, Xs@1]), Acc@1);
[X | Xs@2] ->
do_late_binding(Xs@2, [X | Acc])
end.
-spec do_parse_inline_ast(
list(inline_wrapper()),
list(commonmark@ast:inline_node())
) -> list(commonmark@ast:inline_node()).
do_parse_inline_ast(Wrapped, Acc) ->
case Wrapped of
[] ->
_pipe = Acc,
lists:reverse(_pipe);
[{email_autolink, L} | Ws] ->
do_parse_inline_ast(Ws, [{email_autolink, list_to_string(L)} | Acc]);
[{uri_autolink, L@1} | Ws@1] ->
do_parse_inline_ast(
Ws@1,
[{uri_autolink, list_to_string(L@1)} | Acc]
);
[{uri_image, Contents, Href, Title} | Ws@2] ->
do_parse_inline_ast(Ws@2, [{image, Contents, Title, Href} | Acc]);
[{uri_link, Contents@1, Href@1, Title@1} | Ws@3] ->
do_parse_inline_ast(
Ws@3,
[{link, do_parse_inline_ast(Contents@1, []), Title@1, Href@1} |
Acc]
);
[{backtick_string, Count} | Ws@4] ->
do_parse_inline_ast(
Ws@4,
[{plain_text, gleam@string:repeat(<<"`"/utf8>>, Count)} | Acc]
);
[{code_span, _, Contents@2} | Ws@5] ->
_assert_subject = gleam@regex:from_string(<<"^ (.*) $"/utf8>>),
{ok, R} = 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 => <<"commonmark/internal/parser/inline"/utf8>>,
function => <<"do_parse_inline_ast"/utf8>>,
line => 702})
end,
C = begin
_pipe@1 = Contents@2,
_pipe@2 = list_to_string(_pipe@1),
gleam@string:replace(_pipe@2, <<"\n"/utf8>>, <<" "/utf8>>)
end,
case gleam@regex:scan(R, C) of
[{match, _, [{some, Span}]}] ->
do_parse_inline_ast(Ws@5, [{code_span, Span} | Acc]);
_ ->
do_parse_inline_ast(Ws@5, [{code_span, C} | Acc])
end;
[{tilde_string, Count@1} | Ws@6] ->
do_parse_inline_ast(
Ws@6,
[{plain_text, gleam@string:repeat(<<"~"/utf8>>, Count@1)} | Acc]
);
[{strikethrough, _, Contents@3} | Ws@7] ->
do_parse_inline_ast(
Ws@7,
[{strike_through, do_parse_inline_ast(Contents@3, [])} | Acc]
);
[{emphasis, Contents@4, Marker} | Ws@8] ->
do_parse_inline_ast(
Ws@8,
[{emphasis, do_parse_inline_ast(Contents@4, []), Marker} | Acc]
);
[{strong_emphasis, Contents@5, Marker@1} | Ws@9] ->
do_parse_inline_ast(
Ws@9,
[{strong_emphasis,
do_parse_inline_ast(Contents@5, []),
Marker@1} |
Acc]
);
[{asterisk_string, Count@2} | Ws@10] ->
do_parse_inline_ast(
Ws@10,
[{plain_text, gleam@string:repeat(<<"*"/utf8>>, Count@2)} | Acc]
);
[{underscore_string, Count@3} | Ws@11] ->
do_parse_inline_ast(
Ws@11,
[{plain_text, gleam@string:repeat(<<"_"/utf8>>, Count@3)} | Acc]
);
[{lexed_element, {escaped, S}} | Ws@12] ->
do_parse_inline_ast(Ws@12, [{plain_text, S} | Acc]);
[{lexed_element, {entity, _, R@1}} | Ws@13] ->
do_parse_inline_ast(Ws@13, [{plain_text, R@1} | Acc]);
[{lexed_element, soft_line_break} | Ws@14] ->
do_parse_inline_ast(Ws@14, [soft_line_break | Acc]);
[{lexed_element, {hard_line_break, _}} | Ws@15] ->
do_parse_inline_ast(Ws@15, [hard_line_break | Acc]);
[{lexed_element, open_bracket} | Ws@16] ->
do_parse_inline_ast(Ws@16, [{plain_text, <<"("/utf8>>} | Acc]);
[{lexed_element, close_bracket} | Ws@17] ->
do_parse_inline_ast(Ws@17, [{plain_text, <<")"/utf8>>} | Acc]);
[{lexed_element, image_start} | Ws@18] ->
do_parse_inline_ast(Ws@18, [{plain_text, <<"!["/utf8>>} | Acc]);
[{lexed_element, open_square_bracket} | Ws@19] ->
do_parse_inline_ast(Ws@19, [{plain_text, <<"["/utf8>>} | Acc]);
[{lexed_element, close_square_bracket} | Ws@20] ->
do_parse_inline_ast(Ws@20, [{plain_text, <<"]"/utf8>>} | Acc]);
[{lexed_element, single_quote} | Ws@21] ->
do_parse_inline_ast(Ws@21, [{plain_text, <<"'"/utf8>>} | Acc]);
[{lexed_element, double_quote} | Ws@22] ->
do_parse_inline_ast(Ws@22, [{plain_text, <<"\""/utf8>>} | Acc]);
[{lexed_element, exclamation} | Ws@23] ->
do_parse_inline_ast(Ws@23, [{plain_text, <<"!"/utf8>>} | Acc]);
[{lexed_element, asterisk} | Ws@24] ->
do_parse_inline_ast(Ws@24, [{plain_text, <<"*"/utf8>>} | Acc]);
[{lexed_element, underscore} | Ws@25] ->
do_parse_inline_ast(Ws@25, [{plain_text, <<"_"/utf8>>} | Acc]);
[{lexed_element, backtick} | Ws@26] ->
do_parse_inline_ast(Ws@26, [{plain_text, <<"`"/utf8>>} | Acc]);
[{lexed_element, tilde} | Ws@27] ->
do_parse_inline_ast(Ws@27, [{plain_text, <<"~"/utf8>>} | Acc]);
[{lexed_element, greater_than} | Ws@28] ->
do_parse_inline_ast(Ws@28, [{plain_text, <<">"/utf8>>} | Acc]);
[{lexed_element, less_than} | Ws@29] ->
do_parse_inline_ast(Ws@29, [{plain_text, <<"<"/utf8>>} | Acc]);
[{lexed_element, {white_space, T}} | Ws@30] ->
do_parse_inline_ast(Ws@30, [{plain_text, T} | Acc]);
[{lexed_element, {word, T@1}} | Ws@31] ->
do_parse_inline_ast(Ws@31, [{plain_text, T@1} | Acc])
end.
-spec parse_text(binary()) -> list(commonmark@ast:inline_node()).
parse_text(Text) ->
_pipe = Text,
_pipe@1 = gleam@string:to_graphemes(_pipe),
_pipe@2 = do_lex_inline_text(_pipe@1, []),
_pipe@3 = do_parse_inline_wrappers(_pipe@2, []),
_pipe@4 = do_late_binding(_pipe@3, []),
_pipe@5 = do_parse_inline_ast(_pipe@4, []),
do_finalise_plain_text(_pipe@5, [], true).