Doc update for hz_sophia and hz_aaci and some minor fixes #30
+69
-10
@@ -4,26 +4,28 @@
|
|||||||
-copyright("Jarvis Carroll <spiveehere@gmail.com>").
|
-copyright("Jarvis Carroll <spiveehere@gmail.com>").
|
||||||
-license("GPL-3.0-or-later").
|
-license("GPL-3.0-or-later").
|
||||||
|
|
||||||
-export([parse_literal/1, parse_literal/2]).
|
-export([parse_literal/2, parse_literal/1]).
|
||||||
-export([fate_to_list/1, fate_to_list/2, fate_to_iolist/1, fate_to_iolist/2]).
|
-export([fate_to_list/1, fate_to_list/2, fate_to_iolist/1, fate_to_iolist/2]).
|
||||||
|
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
|
||||||
-spec parse_literal(Sophia) -> {ok, FATE} | {error, Reason}
|
|
||||||
when Sophia :: string(),
|
|
||||||
FATE :: gmb_fate_data:fate_type(),
|
|
||||||
Reason :: term().
|
|
||||||
|
|
||||||
parse_literal(String) ->
|
|
||||||
parse_literal(unknown_type(), String).
|
|
||||||
|
|
||||||
-spec parse_literal(Type, Sophia) -> {ok, FATE} | {error, Reason}
|
-spec parse_literal(Type, Sophia) -> {ok, FATE} | {error, Reason}
|
||||||
when Type :: hz_aaci:annotated_type(),
|
when Type :: hz_aaci:annotated_type(),
|
||||||
Sophia :: string(),
|
Sophia :: string(),
|
||||||
FATE :: gmb_fate_data:fate_type(),
|
FATE :: gmb_fate_data:fate_type(),
|
||||||
Reason :: term().
|
Reason :: term().
|
||||||
|
|
||||||
|
%% @doc
|
||||||
|
%% Parse a typed Sophia expression into a FATE term
|
||||||
|
%% The Sophia expression must consist only of literals, thus making a 'Sophia
|
||||||
|
%% term', which means no arithmetic, no function calls, no variables, etc.
|
||||||
|
%% The FATE term is in the format that gmbytecode expects as input, for forming
|
||||||
|
%% contract calls, etc. Used by the hz module to implement the 'sophia' format.
|
||||||
|
%%
|
||||||
|
%% The function takes type information retrieved from the AACI data structure,
|
||||||
|
%% which is used to interpret record types and variant types, but is also used
|
||||||
|
%% to check inputs and generate errors.
|
||||||
|
|
||||||
parse_literal(Type, String) ->
|
parse_literal(Type, String) ->
|
||||||
case parse_expression(Type, {1, 1}, String) of
|
case parse_expression(Type, {1, 1}, String) of
|
||||||
{ok, {Result, NewPos, NewString}} ->
|
{ok, {Result, NewPos, NewString}} ->
|
||||||
@@ -43,6 +45,28 @@ parse_literal2(Result, Pos, String) ->
|
|||||||
{error, Reason}
|
{error, Reason}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
-spec parse_literal(Sophia) -> {ok, FATE} | {error, Reason}
|
||||||
|
when Sophia :: string(),
|
||||||
|
FATE :: gmb_fate_data:fate_type(),
|
||||||
|
Reason :: term().
|
||||||
|
|
||||||
|
%% @doc
|
||||||
|
%% Parse an untyped Sophia expression into a FATE term
|
||||||
|
%% Like parse_literal/2, but will not produce type errors. This function can
|
||||||
|
%% still produce parsing errors, and can produce errors when variants or
|
||||||
|
%% records are encountered, since they can't be parsed unless you have type
|
||||||
|
%% information.
|
||||||
|
%%
|
||||||
|
%% Note that since records are implemented as tuples, if you are trying to call
|
||||||
|
%a function that you know takes a record, but you don't have type information
|
||||||
|
%% available in the context where the expression is being passed, then tuples
|
||||||
|
%% can be used instead. This does not work if you have type information,
|
||||||
|
%% though, as tuples and records are different Sophia/AACI types.
|
||||||
|
|
||||||
|
parse_literal(String) ->
|
||||||
|
parse_literal(unknown_type(), String).
|
||||||
|
|
||||||
%%% Tokenizer
|
%%% Tokenizer
|
||||||
|
|
||||||
-define(IS_LATIN_UPPER(C), (((C) >= $A) and ((C) =< $Z))).
|
-define(IS_LATIN_UPPER(C), (((C) >= $A) and ((C) =< $Z))).
|
||||||
@@ -927,6 +951,19 @@ wrap_error(Reason, _) -> Reason.
|
|||||||
when FATE :: gmb_fate_data:fate_type(),
|
when FATE :: gmb_fate_data:fate_type(),
|
||||||
Sophia :: string().
|
Sophia :: string().
|
||||||
|
|
||||||
|
%% @doc
|
||||||
|
%% Print a FATE term from gmbytecode in Sophia syntax
|
||||||
|
%% FATE terms usually come from using gmbytecode to decode the result of an
|
||||||
|
%% on-chain transaction.
|
||||||
|
%%
|
||||||
|
%% This function does not use any type information to interpret the data, and
|
||||||
|
%% so can make mistakes. It's okay for interpreting tuples, lists, maps,
|
||||||
|
%% integers, and strings, but it will misinterpret the types of records and
|
||||||
|
%% unicode characters, and will crash the process if variants are encountered.
|
||||||
|
%%
|
||||||
|
%% fate_to_list/2 should be used whenever possible, especially since
|
||||||
|
%% transaction results are type checked by nodes at runtime.
|
||||||
|
|
||||||
fate_to_list(Term) ->
|
fate_to_list(Term) ->
|
||||||
fate_to_list(unknown_type(), Term).
|
fate_to_list(unknown_type(), Term).
|
||||||
|
|
||||||
@@ -935,10 +972,27 @@ fate_to_list(Term) ->
|
|||||||
FATE :: gmb_fate_data:fate_type(),
|
FATE :: gmb_fate_data:fate_type(),
|
||||||
Sophia :: string().
|
Sophia :: string().
|
||||||
|
|
||||||
|
|
||||||
|
%% @doc
|
||||||
|
%% Print a FATE term from gmbytecode in Sophia syntax
|
||||||
|
%% Like fate_to_list/1, but now type information from the AACI data structure
|
||||||
|
%% can be provided, in order to correctly interpret types like records,
|
||||||
|
%% variants, and unicode characters. If the type information you provide is
|
||||||
|
%% incorrect for the FATE term provided, then the function will fall back to
|
||||||
|
%% untyped pretty printing like in fate_to_list/1, but this is not recommended,
|
||||||
|
%% as correct type information should always be available.
|
||||||
|
|
||||||
fate_to_list(Type, Term) ->
|
fate_to_list(Type, Term) ->
|
||||||
IOList = fate_to_iolist(Type, Term),
|
IOList = fate_to_iolist(Type, Term),
|
||||||
unicode:characters_to_list(IOList).
|
unicode:characters_to_list(IOList).
|
||||||
|
|
||||||
|
%% @doc
|
||||||
|
%% Print a FATE term in Sophia syntax, without concatenating
|
||||||
|
%% The fate_to_list/1 function builds an iolist, and then concatenates it into
|
||||||
|
%% a list. If you are going to put the term into a bigger iolist directly
|
||||||
|
%% after, or write it to a streaming device, then it can save effort and memory
|
||||||
|
%% to just use the iolist directly.
|
||||||
|
|
||||||
-spec fate_to_iolist(FATE) -> Sophia
|
-spec fate_to_iolist(FATE) -> Sophia
|
||||||
when FATE :: gmb_fate_data:fate_type(),
|
when FATE :: gmb_fate_data:fate_type(),
|
||||||
Sophia :: iolist().
|
Sophia :: iolist().
|
||||||
@@ -951,6 +1005,11 @@ fate_to_iolist(Term) ->
|
|||||||
FATE :: gmb_fate_data:fate_type(),
|
FATE :: gmb_fate_data:fate_type(),
|
||||||
Sophia :: iolist().
|
Sophia :: iolist().
|
||||||
|
|
||||||
|
%% @doc
|
||||||
|
%% Print a FATE term in Sophia syntax, without concatenating
|
||||||
|
%% Prints using type information, like fate_to_list/2, but without spending
|
||||||
|
%% time or memory concatenating the result into a list, like fate_to_iolist/1.
|
||||||
|
|
||||||
% Special case for singleton records, since they are erased during compilation.
|
% Special case for singleton records, since they are erased during compilation.
|
||||||
fate_to_iolist({_, _, {record, [{FieldName, FieldType}]}}, Term) ->
|
fate_to_iolist({_, _, {record, [{FieldName, FieldType}]}}, Term) ->
|
||||||
singleton_record_to_iolist(FieldName, FieldType, Term);
|
singleton_record_to_iolist(FieldName, FieldType, Term);
|
||||||
|
|||||||
Reference in New Issue
Block a user