* move stuff in here * reorganizing because zx needs to feel special * add base58/base64 explainer draft
245 lines
6.4 KiB
Plaintext
245 lines
6.4 KiB
Plaintext
*
|
|
* 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.
|
|
|