Current section
Files
Jump to
Current section
Files
src/css_select@parser.erl
-module(css_select@parser).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/css_select/parser.gleam").
-export([new/0, parse/2]).
-export_type([parser/0, parse_error/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-opaque parser() :: {parser,
splitter:splitter(),
splitter:splitter(),
splitter:splitter()}.
-type parse_error() :: {parse_error, binary()}.
-file("src/css_select/parser.gleam", 32).
?DOC(
" Create a new parser instance with pre-built splitters.\n"
"\n"
" Use this when parsing multiple selectors to avoid the overhead\n"
" of creating splitters on each call.\n"
"\n"
" ```gleam\n"
" let parser = parser.new()\n"
" parser.parse_simple_selector_with_parser(parser, \"div#foo\")\n"
" parser.parse_simple_selector_with_parser(parser, \".bar\")\n"
" ```\n"
).
-spec new() -> parser().
new() ->
{parser,
splitter:new([<<"."/utf8>>, <<"#"/utf8>>, <<"["/utf8>>, <<":"/utf8>>]),
splitter:new(
[<<"^="/utf8>>,
<<"$="/utf8>>,
<<"*="/utf8>>,
<<"="/utf8>>,
<<"]"/utf8>>]
),
splitter:new([<<"\""/utf8>>])}.
-file("src/css_select/parser.gleam", 120).
-spec parse_attr_value(binary(), parser()) -> {binary(), binary()}.
parse_attr_value(Input, Parser) ->
case Input of
<<"\""/utf8, Rest/binary>> ->
{Parts, _, Remaining} = splitter_ffi:split(
erlang:element(4, Parser),
Rest
),
case Remaining of
<<"\""/utf8, After/binary>> ->
{Parts, After};
_ ->
{Parts, Remaining}
end;
_ ->
splitter_ffi:split_before(erlang:element(3, Parser), Input)
end.
-file("src/css_select/parser.gleam", 106).
-spec parse_attr_with_op(
fun((binary(), binary()) -> css_select@selector:attribute_selector()),
binary(),
binary(),
parser()
) -> {css_select@selector:attribute_selector(), binary()}.
parse_attr_with_op(Constructor, Key, Input, Parser) ->
{Value, After_value} = parse_attr_value(Input, Parser),
case After_value of
<<"]"/utf8, Remaining/binary>> ->
{Constructor(Key, Value), Remaining};
_ ->
{Constructor(Key, Value), After_value}
end.
-file("src/css_select/parser.gleam", 90).
-spec parse_bracket_attr(binary(), parser()) -> {css_select@selector:attribute_selector(),
binary()}.
parse_bracket_attr(Input, Parser) ->
{Key, Op_and_rest} = splitter_ffi:split_before(
erlang:element(3, Parser),
Input
),
case Op_and_rest of
<<"]"/utf8, Remaining/binary>> ->
{{attribute_exists, Key}, Remaining};
<<"^="/utf8, Rest/binary>> ->
parse_attr_with_op(
fun(Field@0, Field@1) -> {attribute_prefix, Field@0, Field@1} end,
Key,
Rest,
Parser
);
<<"$="/utf8, Rest@1/binary>> ->
parse_attr_with_op(
fun(Field@0, Field@1) -> {attribute_suffix, Field@0, Field@1} end,
Key,
Rest@1,
Parser
);
<<"*="/utf8, Rest@2/binary>> ->
parse_attr_with_op(
fun(Field@0, Field@1) -> {attribute_includes, Field@0, Field@1} end,
Key,
Rest@2,
Parser
);
<<"="/utf8, Rest@3/binary>> ->
parse_attr_with_op(
fun(Field@0, Field@1) -> {attribute_equal, Field@0, Field@1} end,
Key,
Rest@3,
Parser
);
_ ->
{{attribute_exists, Key}, Op_and_rest}
end.
-file("src/css_select/parser.gleam", 64).
-spec parse_attrs(binary(), parser()) -> list(css_select@selector:attribute_selector()).
parse_attrs(Input, Parser) ->
case Input of
<<""/utf8>> ->
[];
<<"."/utf8, Rest/binary>> ->
{Name, Remaining} = splitter_ffi:split_before(
erlang:element(2, Parser),
Rest
),
[{class, Name} | parse_attrs(Remaining, Parser)];
<<"#"/utf8, Rest@1/binary>> ->
{Name@1, Remaining@1} = splitter_ffi:split_before(
erlang:element(2, Parser),
Rest@1
),
[{id, Name@1} | parse_attrs(Remaining@1, Parser)];
<<":"/utf8, Rest@2/binary>> ->
{Name@2, Remaining@2} = splitter_ffi:split_before(
erlang:element(2, Parser),
Rest@2
),
[{psuedo, Name@2} | parse_attrs(Remaining@2, Parser)];
<<"["/utf8, Rest@3/binary>> ->
{Attr, Remaining@3} = parse_bracket_attr(Rest@3, Parser),
[Attr | parse_attrs(Remaining@3, Parser)];
_ ->
[]
end.
-file("src/css_select/parser.gleam", 49).
?DOC(
" Parse a CSS selector string using a pre-created `Parser`.\n"
"\n"
" This avoids the overhead of creating splitters on each call.\n"
" Create a `Parser` once with `new()` and reuse it for all parsing.\n"
).
-spec parse(parser(), binary()) -> {ok, css_select@selector:selector()} |
{error, parse_error()}.
parse(Parser, Input) ->
{Tag_part, Rest} = splitter_ffi:split_before(
erlang:element(2, Parser),
Input
),
Tag = case Tag_part of
<<""/utf8>> ->
any;
_ ->
{tag, Tag_part}
end,
Attrs = parse_attrs(Rest, Parser),
{ok, {element_selector, Tag, Attrs}}.