diff --git a/src/hz_sophia.erl b/src/hz_sophia.erl index 5b7c944..927fe3c 100644 --- a/src/hz_sophia.erl +++ b/src/hz_sophia.erl @@ -100,9 +100,26 @@ parse_expression2(Type, Tk, String, {integer, S, Row, Start, End}) -> end; parse_expression2(Type, Tk, String, {character, "[", Row, Start, _}) -> parse_list(Type, Tk, String, Row, Start); +parse_expression2(Type, Tk, String, {character, "{", Row, Start, _}) -> + parse_record_or_map(Type, Tk, String, Row, Start); parse_expression2(_, _, _, {_, S, Row, Start, End}) -> {error, {unexpected_token, S, Row, Start, End}}. +unknown_type() -> + {unknown_type, already_normalized, unknown_type}. + +expect_tokens([], Tk, String) -> + {ok, {Tk, String}}; +expect_tokens([Str | Rest], Tk, String) -> + case next_token(Tk, String) of + {ok, {{_, Str, _, _, _}, NewTk, NewString}} -> + expect_tokens(Rest, NewTk, NewString); + {ok, {{_, Actual, Row, Start, End}}} -> + {error, {unexpected_token, Actual, Row, Start, End}} + end. + +%%% List Parsing + parse_list({_, _, {list, [Inner]}}, Tk, String, Row, Start) -> parse_list_loop(Inner, Tk, String, Row, Start, []); parse_list({_, _, unknown_type}, Tk, String, Row, Start) -> @@ -137,8 +154,75 @@ parse_list_loop3(Inner, Tk, String, Row, Start, Acc) -> {error, Reason} end. -unknown_type() -> - {unknown_type, already_normalized, unknown_type}. +%%% Record parsing + +parse_record_or_map({_, _, {map, [KeyType, ValueType]}}, Tk, String, _, _) -> + parse_map(KeyType, ValueType, Tk, String, #{}); +parse_record_or_map({_, _, {record, Fields}}, Tk, String, _, _) -> + parse_record(Fields, Tk, String); +parse_record_or_map({_, _, unknown_type}, Tk, String, _, _) -> + case next_token(Tk, String) of + {ok, {{character, "}", _, _, _}, NewTk, NewString}} -> + {ok, {#{}, NewTk, NewString}}; + {ok, {{character, "[", _, _, _}, NewTk, NewString}} -> + parse_map2(unknown_type(), unknown_type(), NewTk, NewString, #{}); + {ok, {{alphanum, _, Row, Start, End}, _, _}} -> + {error, {unresolved_record, Row, Start, End}}; + {ok, {{_, S, Row, Start, End}, _, _}} -> + {error, {unexpected_token, S, Row, Start, End}} + end; +parse_record_or_map({O, N, _}, _, _, Row, Start) -> + {error, {wrong_type, O, N, map, Row, Start, Start}}. + +parse_record(Fields, Tk, String) -> + {error, not_yet_implemented}. + +%%% Map Parsing + +parse_map(KeyType, ValueType, Tk, String, Acc) -> + case next_token(Tk, String) of + {ok, {{character, "[", _, _, _}, NewTk, NewString}} -> + parse_map2(KeyType, ValueType, NewTk, NewString, Acc); + {ok, {{character, "}", _, _, _}, NewTk, NewString}} -> + {ok, {Acc, NewTk, NewString}}; + {ok, {{_, S, Row, Start, End}}} -> + {error, {unexpected_token, S, Row, Start, End}} + end. + +parse_map2(KeyType, ValueType, Tk, String, Acc) -> + case parse_expression(KeyType, Tk, String) of + {ok, {Result, NewTk, NewString}} -> + parse_map3(KeyType, ValueType, NewTk, NewString, Acc, Result); + {error, Reason} -> + wrap_error(Reason, {map_key, maps:size(Acc)}) + end. + +parse_map3(KeyType, ValueType, Tk, String, Acc, Key) -> + case expect_tokens(["]", "="], Tk, String) of + {ok, {NewTk, NewString}} -> + parse_map4(KeyType, ValueType, NewTk, NewString, Acc, Key); + {error, Reason} -> + {error, Reason} + end. + +parse_map4(KeyType, ValueType, Tk, String, Acc, Key) -> + case parse_expression(ValueType, Tk, String) of + {ok, {Result, NewTk, NewString}} -> + NewAcc = maps:put(Key, Result, Acc), + parse_map5(KeyType, ValueType, NewTk, NewString, NewAcc); + {error, Reason} -> + {error, Reason} + end. + +parse_map5(KeyType, ValueType, Tk, String, Acc) -> + case next_token(Tk, String) of + {ok, {{character, ",", _, _, _}, NewTk, NewString}} -> + parse_map(KeyType, ValueType, NewTk, NewString, Acc); + {ok, {{character, "}", _, _, _}, NewTk, NewString}} -> + {ok, {Acc, NewTk, NewString}}; + {ok, {{_, S, Row, Start, End}}} -> + {error, {unexpected_token, S, Row, Start, End}} + end. % TODO wrap_error(Reason, _) -> Reason. @@ -175,3 +259,9 @@ int_test() -> list_test() -> check_parser("[1, 2, 3]", [1, 2, 3]). +list_of_lists_test() -> + check_parser("[[], [1], [2, 3]]", [[], [1], [2, 3]]). + +maps_test() -> + check_parser("{[1] = 2, [3] = 4}", #{1 => 2, 3 => 4}). +