Current section
Files
Jump to
Current section
Files
src/mc_cmd.erl
-module(mc_cmd).
-export([parse/1, to_string/1]).
%% mc_cmd are simple command interface used by mu server. The textual representation is:
%% [Key:Value | "String"]+ ...
%% Keys and strings. Values are strings, integers or true or false. unkeyed Value
%% must be quoted string
%% internally the cmd is represented as a list
-spec parse(unicode:chardata()) -> incomplete | list().
parse(Str) ->
Trimmed = string:trim(Str, leading, "\s\t"),
case string:next_codepoint(Trimmed) of
[] -> incomplete;
[$\n | Remain] -> {[], Remain};
[$" | Remain] ->
case parse_quoted_string(Remain) of
incomplete -> incomplete;
{Term, Remain2} ->
case parse(Remain2) of
incomplete -> incomplete;
{List, Remain3} ->
{[unicode:characters_to_binary(Term) | List], Remain3}
end
end;
_ ->
case parse_key(Trimmed) of
incomplete -> incomplete;
{Key, Remain} ->
case parse_value(Remain) of
incomplete -> incomplete;
{Value, Remain2} ->
case parse(Remain2) of
incomplete -> incomplete;
{List, Remain3} -> {[{Key, Value} | List], Remain3}
end
end
end
end.
parse_key(Str) ->
{Leading, Trailing} = string:take(Str, ":", true),
case unicode:characters_to_binary(Leading) of
<<>> -> incomplete;
Word ->
[$: | Remain] = string:next_codepoint(Trailing),
{Word, Remain}
end.
parse_value(Str) ->
case string:next_codepoint(Str) of
[] -> incomplete;
[$\n | _Tail] -> {<<>>, Str};
[$" | Tail] ->
{Term, Remain} = parse_quoted_string(Tail),
{unicode:characters_to_binary(Term), Remain};
_ ->
{Leading, Trailing} = string:take(Str, "\s\t\r\n", true),
case string:is_empty(Trailing) of
true -> incomplete;
false ->
case string:to_integer(Str) of
{error, _Reason} -> {parse_word(Leading), Trailing};
{Int, Rest} -> {Int, Rest}
end
end
end.
parse_word(Str) ->
case unicode:characters_to_binary(Str) of
<<"true">> -> true;
<<"false">> -> false;
Word -> Word
end.
parse_quoted_string(Str) ->
case string:next_codepoint(Str) of
[] -> incomplete;
[$" | Tail] -> {"", Tail};
[$\\ | Tail] -> parse_quoted_string_after_escape(Tail);
[Head | Tail] ->
case parse_quoted_string(Tail) of
incomplete -> incomplete;
{Token, Remain} -> {[Head | Token], Remain}
end
end.
parse_quoted_string_after_escape(Str) ->
case string:next_codepoint(Str) of
[] -> incomplete;
[Head | Tail] ->
case parse_quoted_string(Tail) of
incomplete -> incomplete;
{Token, Remain} ->
%% emacs spec only defined escape for return, slash and double qoute
case Head of
$\\ -> {[$\\ | Token], Remain};
$" -> {[$" | Token], Remain};
$\n -> {Token, Remain};
_ -> {[$\\, Head | Token], Remain}
end
end
end.
value_string(true) -> <<"true">>;
value_string(false) -> <<"false">>;
value_string(I) when is_integer(I) -> integer_to_binary(I);
value_string(V) -> mc_sexp:escape(V).
-spec to_string(list()) -> unicode:chardata().
to_string(List) ->
lists:join($\s, lists:map(
fun each_to_string/1,
lists:filter(
fun(<<>>) -> false;
({}) -> false;
(_) -> true
end, List)
)) ++ "\n".
each_to_string(Str) when is_binary(Str) -> mc_sexp:quote_escape(Str);
each_to_string({Key, Value}) -> [Key, $:, value_string(Value)].