diff --git a/src/gmser_dyn_types.erl b/src/gmser_dyn_types.erl new file mode 100644 index 0000000..cec7a30 --- /dev/null +++ b/src/gmser_dyn_types.erl @@ -0,0 +1,48 @@ +-module(gmser_dyn_types). + +-export([ from_list/2 ]). +-export([ next_code/1 ]). + +next_code(#{codes := Codes}) -> + lists:max(maps:keys(Codes)) + 1. + +add_type(Tag, Code, Template, Types) -> + elem_to_type({Tag, Code, Template}, Types). + +from_list(L, Types) -> + lists:foldl(fun elem_to_type/2, Types, L). + +elem_to_type({Tag, Code, Template}, Acc) when is_atom(Tag), is_integer(Code) -> + #{codes := Codes, rev := Rev, templates := Temps} = Acc, + case {is_map_key(Tag, Rev), is_map_key(Code, Codes)} of + {false, false} -> + Acc#{ codes := Codes#{Code => Tag} + , rev := Rev#{Tag => Code} + , templates => Temps#{Tag => Template} + }; + {true, _} -> error({duplicate_tag, Tag}); + {_, true} -> error({duplicate_code, Code}) + end; +elem_to_type({modify, {Tag, Template}}, Acc) -> + #{codes := _, rev := Rev, templates := Templates} = Acc, + _ = maps:get(Tag, Rev), + Templates1 = Templates#{Tag := Template}, + Acc#{templates := Templates1}; +elem_to_type({labels, Lbls}, Acc) -> + lists:foldl(fun add_label/2, Acc, Lbls); +elem_to_type({vsn, V}, Acc) -> + Acc#{vsn => V}; +elem_to_type(Elem, _) -> + error({invalid_type, Elem}). + +add_label({L, Code}, #{labels := Lbls, rev_labels := RevLbls} = Acc) + when is_atom(L), is_integer(Code), Code > 0 -> + case {is_map_key(L, Lbls), is_map_key(Code, RevLbls)} of + {false, false} -> + Acc#{labels := Lbls#{L => Code}, + rev_labels := RevLbls#{Code => L}}; + {true, _} -> error({duplicate_label, L}); + {_, true} -> error({duplicate_label_code, Code}) + end; +add_label(Elem, _) -> + error({invalid_label, Elem}).