From c27005c42467dcc76e058f50c579b3a2390d50c3 Mon Sep 17 00:00:00 2001 From: SpiveeWorks Date: Wed, 29 Jan 2025 17:59:13 +1100 Subject: [PATCH] Rename 'flatten' and so on to 'annotate' --- src/hz.erl | 108 +++++++++++++++++++++++++++-------------------------- 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/src/hz.erl b/src/hz.erl index d330a11..4cefd8d 100644 --- a/src/hz.erl +++ b/src/hz.erl @@ -1414,19 +1414,13 @@ prepare_aaci(ACI) -> {Name, OpaqueSpecs, TypeDefs} = convert_aci_types(ACI), % Now that we have the opaque types, we can dereference the function specs - % down to the concrete types they actually represent. - Specs = expand_contract_specs(OpaqueSpecs, TypeDefs, #{}), + % down to the concrete types they actually represent. We annotate each + % subexpression of this concrete type with other info too, in case it helps + % make error messages easier to understand. + Specs = annotate_function_specs(OpaqueSpecs, TypeDefs, #{}), {aaci, Name, Specs, TypeDefs}. -expand_contract_specs([], _Types, Specs) -> - Specs; -expand_contract_specs([{Name, ArgsOpaque, ResultOpaque} | Rest], Types, Specs) -> - {ok, Args} = flatten_opaque_types(ArgsOpaque, Types, []), - {ok, Result} = flatten_opaque_type(ResultOpaque, Types), - NewSpecs = maps:put(Name, {Args, Result}, Specs), - expand_contract_specs(Rest, Types, NewSpecs). - convert_aci_types(ACI) -> % Find the main contract, so we can get the specifications of its % entrypoints. @@ -1552,10 +1546,10 @@ opaque_type_name(Name) -> binary_to_list(Name). % together form an 'annotated type'. First, we need the fully opaque name, % "bazquux", then we need the normalized name, which is an opaque name with the % bare-minimum substitution needed to make the outer-most type-constructor an -% identifiable built-in, ADT, or record type, and then we need the flattened +% identifiable built-in, ADT, or record type, and then we need the dereferenced % type, which is the raw {variant, [{Name, Fields}, ...]} or % {record, [{Name, Type}]} expression that can be used in actual Sophia->FATE -% coercion. The type sub-expressions in these flattened types will each be +% coercion. The type sub-expressions in these dereferenced types will each be % fully annotated as well, i.e. they will each contain *all three* of the above % representations, so that coercion of subexpressions remains fast AND % informative. @@ -1566,16 +1560,24 @@ opaque_type_name(Name) -> binary_to_list(Name). % can simply render the normalized type expression and know that the error will % make sense. -flatten_opaque_type(T, Types) -> +annotate_function_specs([], _Types, Specs) -> + Specs; +annotate_function_specs([{Name, ArgsOpaque, ResultOpaque} | Rest], Types, Specs) -> + {ok, Args} = annotate_types(ArgsOpaque, Types, []), + {ok, Result} = annotate_type(ResultOpaque, Types), + NewSpecs = maps:put(Name, {Args, Result}, Specs), + annotate_function_specs(Rest, Types, NewSpecs). + +annotate_type(T, Types) -> case normalize_opaque_type(T, Types) of {ok, AlreadyNormalized, NOpaque, NExpanded} -> - flatten_opaque_type2(T, AlreadyNormalized, NOpaque, NExpanded, Types); + annotate_type2(T, AlreadyNormalized, NOpaque, NExpanded, Types); Error -> Error end. -flatten_opaque_type2(T, AlreadyNormalized, NOpaque, NExpanded, Types) -> - case flatten_normalized_type(NExpanded, Types) of +annotate_type2(T, AlreadyNormalized, NOpaque, NExpanded, Types) -> + case annotate_type_subexpressions(NExpanded, Types) of {ok, Flat} -> case AlreadyNormalized of true -> {ok, {T, already_normalized, Flat}}; @@ -1585,48 +1587,48 @@ flatten_opaque_type2(T, AlreadyNormalized, NOpaque, NExpanded, Types) -> Error end. -flatten_opaque_types([T | Rest], Types, Acc) -> - case flatten_opaque_type(T, Types) of - {ok, Type} -> flatten_opaque_types(Rest, Types, [Type | Acc]); +annotate_types([T | Rest], Types, Acc) -> + case annotate_type(T, Types) of + {ok, Type} -> annotate_types(Rest, Types, [Type | Acc]); Error -> Error end; -flatten_opaque_types([], _Types, Acc) -> +annotate_types([], _Types, Acc) -> {ok, lists:reverse(Acc)}. -flatten_opaque_bindings([{Name, T} | Rest], Types, Acc) -> - case flatten_opaque_type(T, Types) of - {ok, Type} -> flatten_opaque_bindings(Rest, Types, [{Name, Type} | Acc]); - Error -> Error - end; -flatten_opaque_bindings([], _Types, Acc) -> - {ok, lists:reverse(Acc)}. - -flatten_opaque_variants([{Name, Elems} | Rest], Types, Acc) -> - case flatten_opaque_types(Elems, Types, []) of - {ok, ElemsFlat} -> flatten_opaque_variants(Rest, Types, [{Name, ElemsFlat} | Acc]); - Error -> Error - end; -flatten_opaque_variants([], _Types, Acc) -> - {ok, lists:reverse(Acc)}. - -flatten_normalized_type(PrimitiveType, _Types) when is_atom(PrimitiveType) -> +annotate_type_subexpressions(PrimitiveType, _Types) when is_atom(PrimitiveType) -> {ok, PrimitiveType}; -flatten_normalized_type({variant, VariantsOpaque}, Types) -> - case flatten_opaque_variants(VariantsOpaque, Types, []) of +annotate_type_subexpressions({variant, VariantsOpaque}, Types) -> + case annotate_variants(VariantsOpaque, Types, []) of {ok, Variants} -> {ok, {variant, Variants}}; Error -> Error end; -flatten_normalized_type({record, FieldsOpaque}, Types) -> - case flatten_opaque_bindings(FieldsOpaque, Types, []) of +annotate_type_subexpressions({record, FieldsOpaque}, Types) -> + case annotate_bindings(FieldsOpaque, Types, []) of {ok, Fields} -> {ok, {record, Fields}}; Error -> Error end; -flatten_normalized_type({T, ElemsOpaque}, Types) -> - case flatten_opaque_types(ElemsOpaque, Types, []) of +annotate_type_subexpressions({T, ElemsOpaque}, Types) -> + case annotate_types(ElemsOpaque, Types, []) of {ok, Elems} -> {ok, {T, Elems}}; Error -> Error end. +annotate_bindings([{Name, T} | Rest], Types, Acc) -> + case annotate_type(T, Types) of + {ok, Type} -> annotate_bindings(Rest, Types, [{Name, Type} | Acc]); + Error -> Error + end; +annotate_bindings([], _Types, Acc) -> + {ok, lists:reverse(Acc)}. + +annotate_variants([{Name, Elems} | Rest], Types, Acc) -> + case annotate_types(Elems, Types, []) of + {ok, ElemsFlat} -> annotate_variants(Rest, Types, [{Name, ElemsFlat} | Acc]); + Error -> Error + end; +annotate_variants([], _Types, Acc) -> + {ok, lists:reverse(Acc)}. + normalize_opaque_type(T, Types) -> case type_is_expanded(T) of false -> normalize_opaque_type(T, Types, true); @@ -2228,11 +2230,11 @@ try_coerce(Type, Sophia, Fate) -> ok. coerce_int_test() -> - {ok, Type} = flatten_opaque_type(integer, #{}), + {ok, Type} = annotate_type(integer, #{}), try_coerce(Type, 123, 123). coerce_address_test() -> - {ok, Type} = flatten_opaque_type(address, #{}), + {ok, Type} = annotate_type(address, #{}), try_coerce(Type, "ak_2FTnrGfV8qsfHpaSEHpBrziioCpwwzLqSevHqfxQY3PaAAdARx", {address, <<164,136,155,90,124,22,40,206,255,76,213,56,238,123, @@ -2240,7 +2242,7 @@ coerce_address_test() -> 210,39,214>>}). coerce_contract_test() -> - {ok, Type} = flatten_opaque_type(contract, #{}), + {ok, Type} = annotate_type(contract, #{}), try_coerce(Type, "ct_2FTnrGfV8qsfHpaSEHpBrziioCpwwzLqSevHqfxQY3PaAAdARx", {contract, <<164,136,155,90,124,22,40,206,255,76,213,56,238,123, @@ -2248,35 +2250,35 @@ coerce_contract_test() -> 210,39,214>>}). coerce_bool_test() -> - {ok, Type} = flatten_opaque_type(boolean, #{}), + {ok, Type} = annotate_type(boolean, #{}), try_coerce(Type, true, true), try_coerce(Type, false, false). coerce_string_test() -> - {ok, Type} = flatten_opaque_type(string, #{}), + {ok, Type} = annotate_type(string, #{}), try_coerce(Type, "hello world", <<"hello world">>). coerce_list_test() -> - {ok, Type} = flatten_opaque_type({list, [string]}, #{}), + {ok, Type} = annotate_type({list, [string]}, #{}), try_coerce(Type, ["hello world", [65, 32, 65]], [<<"hello world">>, <<65, 32, 65>>]). coerce_map_test() -> - {ok, Type} = flatten_opaque_type({map, [string, {list, [integer]}]}, #{}), + {ok, Type} = annotate_type({map, [string, {list, [integer]}]}, #{}), try_coerce(Type, #{"a" => "a", "b" => "b"}, #{<<"a">> => "a", <<"b">> => "b"}). coerce_tuple_test() -> - {ok, Type} = flatten_opaque_type({tuple, [integer, string]}, #{}), + {ok, Type} = annotate_type({tuple, [integer, string]}, #{}), try_coerce(Type, {123, "456"}, {tuple, {123, <<"456">>}}). coerce_variant_test() -> - {ok, Type} = flatten_opaque_type({variant, [{"A", [integer]}, + {ok, Type} = annotate_type({variant, [{"A", [integer]}, {"B", [integer, integer]}]}, #{}), try_coerce(Type, {"A", 123}, {variant, [1, 2], 0, {123}}), try_coerce(Type, {"B", 456, 789}, {variant, [1, 2], 1, {456, 789}}). coerce_record_test() -> - {ok, Type} = flatten_opaque_type({record, [{"a", integer}, {"b", integer}]}, #{}), + {ok, Type} = annotate_type({record, [{"a", integer}, {"b", integer}]}, #{}), try_coerce(Type, #{"a" => 123, "b" => 456}, {tuple, {123, 456}}).