wfc stuff
This commit is contained in:
parent
60b149d520
commit
5e559b540b
10
src/wfc_utils.erl
Normal file
10
src/wfc_utils.erl
Normal file
@ -0,0 +1,10 @@
|
||||
% @doc misc utility functions
|
||||
-module(wfc_utils).
|
||||
|
||||
-export([err/2, emsg/2]).
|
||||
|
||||
err(Fmt, Pat) ->
|
||||
{error, emsg(Fmt, Pat)}.
|
||||
|
||||
emsg(Fmt, Pat) ->
|
||||
unicode:characters_to_list(io_lib:format(Fmt, Pat)).
|
||||
83
src/wfc_wfchar.erl
Normal file
83
src/wfc_wfchar.erl
Normal file
@ -0,0 +1,83 @@
|
||||
% @doc called wfchar to disambiguate from
|
||||
%
|
||||
% mathematically, this is a variable like "a", "b", "c", etc
|
||||
-module(wfc_wfchar).
|
||||
|
||||
-export_type([
|
||||
wfchar/0
|
||||
]).
|
||||
|
||||
-export([
|
||||
validate/1,
|
||||
from_binary/1, to_binary/1
|
||||
]).
|
||||
|
||||
% @doc
|
||||
% a character is binary data that would be a valid atom
|
||||
%
|
||||
% [a-z][A-Za-z0-9_]+
|
||||
%
|
||||
% - leads with lowercase letter
|
||||
% - underscores ok
|
||||
% - numbers ok
|
||||
% - no non-ascii chars
|
||||
|
||||
-opaque wfchar() :: {c, binary()}.
|
||||
|
||||
%%-----------------------
|
||||
%% constructors/destructors
|
||||
%%-----------------------
|
||||
|
||||
-spec validate(WfChar) -> Result
|
||||
when WfChar :: wfchar(),
|
||||
Result :: ok
|
||||
| {error, Reason :: string()}.
|
||||
% @doc validate if a candidate wfchar is well-formed and the inner binary is
|
||||
% legal
|
||||
|
||||
validate({c, Binary}) ->
|
||||
case from_binary(Binary) of
|
||||
{ok, {c, X}} when X =:= Binary ->
|
||||
ok;
|
||||
Error ->
|
||||
Error
|
||||
end;
|
||||
validate(Term) ->
|
||||
Error = wfc_utils:emsg("wfc_wfchar:validate failed; malformed wfchar: ~p", [Term]),
|
||||
{error, Error}.
|
||||
|
||||
|
||||
-spec from_binary(Binary) -> Result
|
||||
when Binary :: binary(),
|
||||
Result :: {ok, wfchar()}
|
||||
| {error, Reason :: string()}.
|
||||
% @doc
|
||||
% make sure the binary is a legal wfchar and wrap it in a {c, Binary} tuple
|
||||
% @end
|
||||
|
||||
%% initial char must be [a-z]
|
||||
from_binary(<<L:8, Rest/binary>>) when $a =< L, L =< $z ->
|
||||
new2(<<L>>, Rest);
|
||||
from_binary(Char = <<L:8, _/binary>>) ->
|
||||
{error, wfc_utils:emsg("wfc_wfchar:from_binary(~p): illegal lead character: ~p", [Char, L])};
|
||||
from_binary(Char) ->
|
||||
wfc_utils:err("wfc_wfchar:from_binary(~p): malformed argument", [Char]).
|
||||
|
||||
%% rest must be [A-Za-z0-9_]
|
||||
new2(Acc, <<L:8, Rest/binary>>) when ($A =< L andalso L =< $Z)
|
||||
orelse ($a =< L andalso L =< $z)
|
||||
orelse ($0 =< L andalso L =< $9)
|
||||
orelse (L =:= $_) ->
|
||||
new2(<<Acc/binary, L>>, Rest);
|
||||
new2(Acc, <<>>) ->
|
||||
{ok, {c, Acc}};
|
||||
new2(Acc, Rest = <<BadChar:8, _/binary>>) ->
|
||||
WholeChar = <<Acc/binary, Rest/binary>>,
|
||||
Error = wfc_utils:emsg("wfc_wfchar:new2(~p, ~p): illegal character in wfchar: ~p; WholeChar: ~p", [Acc, Rest, BadChar, WholeChar]),
|
||||
{error, Error}.
|
||||
|
||||
|
||||
|
||||
-spec to_binary(wfchar()) -> binary().
|
||||
|
||||
to_binary({c, X}) -> X.
|
||||
82
src/wfc_word.erl
Normal file
82
src/wfc_word.erl
Normal file
@ -0,0 +1,82 @@
|
||||
% @doc a word is an ordset of wfchars
|
||||
%
|
||||
% multiplication is implied in a word
|
||||
%
|
||||
% empty word is 1
|
||||
%
|
||||
% anything times itself equals itself, so duplication is ignored
|
||||
-module(wfc_word).
|
||||
|
||||
-export_type([
|
||||
word/0
|
||||
]).
|
||||
|
||||
-export([
|
||||
%% constructors
|
||||
one/0,
|
||||
validate/1,
|
||||
from_wfchars/1, to_wfchars/1,
|
||||
%% ops
|
||||
mul/2, mul/1
|
||||
]).
|
||||
|
||||
-opaque word() :: {w, ordsets:ordset(wfc_wfchar:wfchar())}.
|
||||
|
||||
%%----------------------------
|
||||
%% constructor
|
||||
%%----------------------------
|
||||
|
||||
-spec one() -> word().
|
||||
|
||||
one() ->
|
||||
{w, []}.
|
||||
|
||||
|
||||
validate(_) -> error(nyi).
|
||||
to_wfchars(_) -> error(nyi).
|
||||
|
||||
-spec from_wfchars(WfChars) -> Result
|
||||
when WfChars :: list(wfc_char:wfchar()),
|
||||
Result :: {ok, word()}
|
||||
| {error, Reason :: string()}.
|
||||
|
||||
from_wfchars(Chars) ->
|
||||
from_wfchars(ordsets:from_list(Chars), []).
|
||||
|
||||
|
||||
%% validate each char
|
||||
from_wfchars([WfChar | Rest], Acc) ->
|
||||
case wfc_wfchar:validate(WfChar) of
|
||||
ok -> from_wfchars(Rest, [WfChar | Acc]);
|
||||
Error -> Error
|
||||
end;
|
||||
% done, all good
|
||||
from_wfchars([], Acc) ->
|
||||
{ok, {w, lists:reverse(Acc)}}.
|
||||
|
||||
|
||||
%%----------------------------
|
||||
%% ops
|
||||
%%----------------------------
|
||||
|
||||
-spec mul(word(), word()) -> Result
|
||||
when Result :: {ok, word()}
|
||||
| {error, Reason :: string()}.
|
||||
% @doc product of two words
|
||||
|
||||
mul({w, X}, {w, Y}) ->
|
||||
case from_wfchars(ordsets:union(X, Y)) of
|
||||
Result = {ok, _} -> Result;
|
||||
Error -> Error
|
||||
end.
|
||||
|
||||
|
||||
-spec mul(Words) -> Result
|
||||
when Words :: [word()],
|
||||
Result :: {ok, word()}
|
||||
| {error, Reason},
|
||||
Reason :: string().
|
||||
% @doc multiply a list of words together
|
||||
|
||||
mul([Word | Rest]) -> mul(Word, mul(Rest));
|
||||
mul([]) -> {ok, one()}.
|
||||
Loading…
x
Reference in New Issue
Block a user