diff options
Diffstat (limited to 'src/asciideck_attributes_parser.erl')
-rw-r--r-- | src/asciideck_attributes_parser.erl | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/src/asciideck_attributes_parser.erl b/src/asciideck_attributes_parser.erl new file mode 100644 index 0000000..b89c3f4 --- /dev/null +++ b/src/asciideck_attributes_parser.erl @@ -0,0 +1,120 @@ +%% Copyright (c) 2017-2018, Loïc Hoguin <[email protected]> +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +%% Asciidoc User Guide 29 +-module(asciideck_attributes_parser). + +-export([parse/1]). + +-type attributes() :: #{ + %% The raw attribute list. + 0 := binary(), + %% Positional attributes. + pos_integer() => binary(), + %% Named attributes. + binary() => binary() +}. +-export_type([attributes/0]). + +-define(IS_WS(C), (C =:= $\s) or (C =:= $\t)). + +-spec parse(binary()) -> attributes(). +parse(Data) -> + parse(Data, #{0 => Data}, 1). + +parse(<<>>, Attrs, _) -> + Attrs; +parse(Data, Attrs, Nth) -> + case parse_attr(Data, <<>>) of + {Value, Rest} when Nth =/= undefined -> + parse(Rest, Attrs#{Nth => Value}, Nth + 1); + {Name, Value, Rest} -> + parse(Rest, Attrs#{Name => Value}, undefined) + end. + +parse_attr(<<>>, Acc) -> + {Acc, <<>>}; +%% Skip preceding whitespace. +parse_attr(<<C, R/bits>>, <<>>) when ?IS_WS(C) -> + parse_attr(R, <<>>); +%% Parse quoted positional attributes in their own function. +parse_attr(<<$", R/bits>>, <<>>) -> + parse_quoted_attr(R, <<>>); +%% We have a named attribute, parse the value. +parse_attr(<<$=, R/bits>>, Name) when Name =/= <<>> -> + parse_attr_value(R, asciideck_block_parser:trim(Name, trailing), <<>>); +%% We have a positional attribute. +parse_attr(<<$,, R/bits>>, Value) -> + {asciideck_block_parser:trim(Value, trailing), R}; +%% Continue. +parse_attr(<<C, R/bits>>, Acc) when C =/= $= -> + parse_attr(R, <<Acc/binary, C>>). + +%% Get everything until the next double quote. +parse_quoted_attr(<<$", R/bits>>, Acc) -> + parse_quoted_attr_end(R, Acc); +parse_quoted_attr(<<$\\, $", R/bits>>, Acc) -> + parse_quoted_attr(R, <<Acc/binary, $">>); +parse_quoted_attr(<<C, R/bits>>, Acc) -> + parse_quoted_attr(R, <<Acc/binary, C>>). + +%% Skip the whitespace until the next comma or eof. +parse_quoted_attr_end(<<>>, Value) -> + {Value, <<>>}; +parse_quoted_attr_end(<<$,, R/bits>>, Value) -> + {Value, R}; +parse_quoted_attr_end(<<C, R/bits>>, Value) when ?IS_WS(C) -> + parse_quoted_attr_end(R, Value). + +parse_attr_value(<<>>, Name, Acc) -> + {Name, Acc, <<>>}; +%% Skip preceding whitespace. +parse_attr_value(<<C, R/bits>>, Name, <<>>) when ?IS_WS(C) -> + parse_attr_value(R, Name, <<>>); +%% Parse quoted positional attributes in their own function. +parse_attr_value(<<$", R/bits>>, Name, <<>>) -> + {Value, Rest} = parse_quoted_attr(R, <<>>), + {Name, Value, Rest}; +%% Done. +parse_attr_value(<<$,, R/bits>>, Name, Value) -> + {Name, asciideck_block_parser:trim(Value, trailing), R}; +%% Continue. +parse_attr_value(<<C, R/bits>>, Name, Acc) -> + parse_attr_value(R, Name, <<Acc/binary, C>>). + +-ifdef(TEST). +attribute_0_test() -> + #{0 := <<"Hello,world,width=\"50\"">>} = parse(<<"Hello,world,width=\"50\"">>), + ok. + +parse_test() -> + #{} = parse(<<>>), + #{ + 1 := <<"Hello">> + } = parse(<<"Hello">>), + #{ + 1 := <<"quote">>, + 2 := <<"Bertrand Russell">>, + 3 := <<"The World of Mathematics (1956)">> + } = parse(<<"quote, Bertrand Russell, The World of Mathematics (1956)">>), + #{ + 1 := <<"22 times">>, + <<"backcolor">> := <<"#0e0e0e">>, + <<"options">> := <<"noborders,wide">> + } = parse(<<"\"22 times\", backcolor=\"#0e0e0e\", options=\"noborders,wide\"">>), + #{ + 1 := <<"A footnote, "with an image" image:smallnew.png[]">> + } = parse(<<"A footnote, "with an image" image:smallnew.png[]">>), + ok. +-endif. |