* * As far as I can tell, AWCP isn't formally defined anywhere. I * figured this out by fuzzing the messaging protocol. So I suppose * this is a candidate for a formal definition. * * This currently does not implement the full kitchen sink * functionality, only what is needed for the limited functionality that * sidekick provides. That said, the framework and design pattern laid * out here can easily be extended to implement the entire kitchen sink. * * The pattern here is to define all of the types involved. At the end, * an interface called `AWCP_Aepp` is defined. This enumerates all of * the functions that you an aepp needs to have defined in order to do * stuff with a waellet. * * The functions are listed in the order that they are used in practice. * So for instance, * * - before you can `connection.open` with the waellet, you must wait * for the waellet to `connection.announcePresence` * - before you can `address.subscribe` the waellet, you must wait * for the waellet to `connection.open` * * An implementation of `AWCP_Aepp` is given in the `msgr.ts` file in * this directory. In particular, msgr implements the "selective ignore" * special behavior needed to deal with `connection.announcePresence`. * * `skylight.ts` (parent directory) includes some convenience functions * wrapped on top of msgr. In particular, it black-boxes away things * like "increment the message id each time you send a new message" * * Moreover, `skylight.ts` includes some subset of porcelain (dwim) * functions like "just connect to the wallet, do what I mean", which * does the "wait for `connection.announcePresence`, then do * `connection.open`, then do `address.subscribe`" dance. * * Crucially, skylight only contains porcelain functions that are of the * flavor of black-boxing away complexity related to talking to the * waellet. For instance, Skylight will never directly communicate with * a node. * * This "design pattern" of "define the types for a messaging protocol, * and separately implement it, then black-box away the complexity in a * porcelain module" will probably also be done for talking to a node * and talking to a compiler. * * sidekick.ts (parent directory) includes programmer-facing porcelain * functions such as "I just want to perform a transaction". In other * words, sidekick.ts black-boxes away the complexity in coordinating * between the compiler, the node, and the waellet. * This is entirely types and type definitions * * See: * - JSON RPC 2.0 definition: https://www.jsonrpc.org/specification * - Typescript generics: https://www.typescriptlang.org/docs/handbook/2/generics.html * * @module */ % Every operation, calculation, and concept, no matter how % arbitrarily complex, reduces to adding integers together. % There are no new concepts in QAnal. Everything is just % putting lipstick on adding integers together. % % -- Dr. Ajay Kumar PHD, The Founder % % a word is the smallest unit in a reduced sum. In for instance % 1 + a + ab, the words are 1, a, and ab, which are represented as % the sets {}, {a}, and {a, b}, respectively. % % - a word is a tuple {w, SetOfWFChars} % - the empty set means 1 % % - a wfchar is a Binary % - if you wish to use pf/1, the binary must be string-formattable % % in WF algebra, anything times itself equals itself, therefore we % don't need to keep track of exponents. That is why the set % representation makes sense. % % with a word, multiplication is implied % with a sentence, summation is implied -module(wfc_word). -vsn("1.0.0"). -export_type([ wfchar/0, word/0 ]). -export([ one/0, is_one/1, is_valid_word/1, from_binary/1, from_list/1, to_list/1, times/1, times/2, pf/1, pp/1 ]). -type wfchar() :: binary(). -type word() :: {w, sets:set(wfchar())}. %%% API -spec one() -> word(). % @doc The word corresponding to the concept "1"; it is a tagged % tuple of {w, EmptySet}. one() -> {w, sets:new()}. -spec is_one(term()) -> boolean(). % @doc a word is one if it {w, EmptySet}. is_one(Word) -> Word =:= one(). -spec is_valid_word(term()) -> boolean(). % @doc % a word is valid if exactly one of these conditions are true: % % - is empty % - contains only valid wfchars % % return false on anything failing to pattern match {w, Set} is_valid_word({w, Set}) -> Chars = sets:to_list(Set), lists:all(fun is_valid_char/1, Chars); is_valid_word(_) -> false. is_valid_char(X) -> is_binary(X). -spec from_binary(binary()) -> word(). % @doc % Convert a binary into a word from_binary(Bin) when is_binary(Bin) -> Set = sets:from_list([Bin]), Word = {w, Set}, true = is_valid_word(Word), Word. -spec from_list([binary()]) -> word(). % @doc % Given a list of binaries, take their "product" and put it into a % word. from_list(Binaries) -> Set = sets:from_list(Binaries), ResultWord = {w, Set}, true = is_valid_word(ResultWord), ResultWord. -spec to_list(word()) -> [binary()]. % @doc % pull out the set in the tagged tuple, convert it to a list, and % return the SORTED list of BINARIES % @end to_list({w, Set}) -> Chars = sets:to_list(Set), lists:sort(Chars). -spec times([word()]) -> word(). % @doc product of a list of words times(Words) -> Result = times_acc(Words, one()), true = is_valid_word(Result), Result. times_acc([], FinalAcc) -> FinalAcc; times_acc([W | Ws], Acc) -> NewAcc = times(W, Acc), times_acc(Ws, NewAcc). -spec times(word(), word()) -> word(). % @doc % Multiply two words. This amounts to just taking the union of the % characters contained in the words % @end times({w, L}, {w, R}) -> % take the unions of the things it contains LR = sets:union(L, R), Word = {w, LR}, true = is_valid_word(Word), Word. -spec pp(word()) -> ok. % @doc pretty print a word (wraps an io:format/2 call around pf/1). pp(Word) -> io:format("~ts~n", [pf(Word)]). -spec pf(word()) -> iolist(). % @doc % returns iolist % % "(*)" if word is 1 % "(* a b c)" if word is the set containing {a,b,c} pf(Word) -> true = is_valid_word(Word), Chars = to_list(Word), Strs = pf_wfchars(Chars, []), ["(*", Strs, ")"]. pf_wfchars([Binary | Rest], Accum) when is_binary(Binary) -> BinStr = io_lib:format("~s", [Binary]), NewAccum = [Accum, " ", BinStr], pf_wfchars(Rest, NewAccum); pf_wfchars([], Accum) -> Accum.