2 Commits

Author SHA1 Message Date
pharpend 673bc3ae52 caching works correctly 2025-10-11 17:24:11 -06:00
pharpend a942d79869 snowflakes work except define 2025-10-11 15:04:00 -06:00
8 changed files with 239 additions and 21 deletions
+120
View File
@@ -0,0 +1,120 @@
% @doc storing map #{cookie := Context}
-module(fd_cache).
-behavior(gen_server).
-export([
start_link/0,
query/1, set/2, unset/1,
%%---
%% everything below here runs in process context
%%--
%% gen_server callbacks
init/1, handle_call/3, handle_cast/2, handle_info/2,
code_change/3, terminate/2
]).
-include("$zx_include/zx_logger.hrl").
-type context() :: wfc_eval_context:context().
-record(s,
{cookies = #{} :: #{Cookie :: binary() := context()}}).
% -type state() :: #s{}.
%%--------------------------------
%% api (runs in context of caller)
%%--------------------------------
-spec start_link() -> {ok, pid()} | {error, term()}.
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, none, []).
-spec query(Cookie) -> {ok, Context} | error
when Cookie :: binary(),
Context :: context().
query(Cookie) ->
gen_server:call(?MODULE, {query, Cookie}).
-spec set(Cookie, Context) -> ok
when Cookie :: binary(),
Context :: context().
set(Cookie, Context) ->
gen_server:cast(?MODULE, {set, Cookie, Context}).
-spec unset(Cookie) -> ok
when Cookie :: binary().
unset(Cookie) ->
gen_server:cast(?MODULE, {unset, Cookie}).
%%----------------------
%% gen-server bs
%%----------------------
init(none) ->
log(info, "starting fd_cache"),
InitState = #s{},
{ok, InitState}.
handle_call({query, Cookie}, _, State) ->
Result = do_query(Cookie, State),
{reply, Result, State};
handle_call(Unexpected, From, State) ->
tell("~tp: unexpected call from ~tp: ~tp", [?MODULE, Unexpected, From]),
{noreply, State}.
handle_cast({set, Cookie, Context}, State) ->
NewState = do_set(Cookie, Context, State),
{noreply, NewState};
handle_cast({unset, Cookie}, State) ->
NewState = do_unset(Cookie, State),
{noreply, NewState};
handle_cast(Unexpected, State) ->
tell("~tp: unexpected cast: ~tp", [?MODULE, Unexpected]),
{noreply, State}.
handle_info(Unexpected, State) ->
tell("~tp: unexpected info: ~tp", [?MODULE, Unexpected]),
{noreply, State}.
code_change(_, State, _) ->
{ok, State}.
terminate(_, _) ->
ok.
%%---------------------
%% doers
%%---------------------
do_set(Cookie, Context, State = #s{cookies = Cookies}) ->
NewCookies = maps:put(Cookie, Context, Cookies),
NewState = State#s{cookies = NewCookies},
NewState.
do_unset(Cookie, State = #s{cookies = Cookies}) ->
NewCookies = maps:remove(Cookie, Cookies),
NewState = State#s{cookies = NewCookies},
NewState.
do_query(Cookie, _State = #s{cookies = Cookies}) ->
maps:find(Cookie, Cookies).
+28 -10
View File
@@ -263,34 +263,52 @@ default_css(Sock) ->
http_err(Sock, 500) http_err(Sock, 500)
end. end.
wfcin(Sock, #request{enctype = json, body = #{"wfcin" := Input}}) -> wfcin(Sock, #request{enctype = json,
cookies = Cookies,
body = #{"wfcin" := Input}}) ->
tell("wfcin good request: ~tp", [Input]), tell("wfcin good request: ~tp", [Input]),
RespObj = {Cookie, Ctx0} = ctx(Cookies),
{RespObj, NewCtx} =
%% FIXME: this should really be a new process
try try
case wfc_read:expr(Input) of case wfc_read:expr(Input) of
%% FIXME support multiple expressions
{ok, Expr, _Rest} -> {ok, Expr, _Rest} ->
case wfc_eval:eval(Expr, wfc_eval_context:default()) of case wfc_eval:eval(Expr, Ctx0) of
{ok, noop, _NewContext} -> jsgud("<noop>"); {ok, noop, Ctx1} -> {jsgud("<noop>"), Ctx1};
{ok, Sentence, _NewContext} -> jsgud(wfc_pp:sentence(Sentence)); {ok, Sentence, Ctx1} -> {jsgud(wfc_pp:sentence(Sentence)), Ctx1};
{error, Message} -> jsbad(Message) {error, Message} -> {jsbad(Message), Ctx0}
end; end;
{error, Message} -> {error, Message} ->
jsbad(Message) {jsbad(Message), Ctx0}
end end
catch catch
error:E:S -> error:E:S ->
ErrorMessage = unicode:characters_to_list(io_lib:format("parser crashed: ~p:~p", [E, S])), ErrorMessage = unicode:characters_to_list(io_lib:format("parser crashed: ~p:~p", [E, S])),
jsbad(ErrorMessage) {jsbad(ErrorMessage), Ctx0}
end, end,
% update cache with new context
ok = fd_cache:set(Cookie, NewCtx),
Body = zj:encode(RespObj), Body = zj:encode(RespObj),
Response = #response{headers = [{"content-type", "application/json"}], Response = #response{headers = [{"content-type", "application/json"},
{"set-cookie", ["wfc=", Cookie]}],
body = Body}, body = Body},
respond(Sock, Response); respond(Sock, Response);
wfcin(Sock, Request) -> wfcin(Sock, Request) ->
tell("wfcin: bad request: ~tp", [Request]), tell("wfcin: bad request: ~tp", [Request]),
http_err(Sock, 400). http_err(Sock, 400).
ctx(#{<<"wfc">> := Cookie}) ->
case fd_cache:query(Cookie) of
{ok, Context} -> {Cookie, Context};
error -> {Cookie, wfc_eval_context:default()}
end;
ctx(_) ->
{new_cookie(), wfc_eval_context:default()}.
new_cookie() ->
binary:encode_hex(crypto:strong_rand_bytes(8)).
jsgud(X) -> jsgud(X) ->
#{"ok" => true, #{"ok" => true,
"result" => X}. "result" => X}.
+7 -1
View File
@@ -42,5 +42,11 @@ init([]) ->
5000, 5000,
supervisor, supervisor,
[fd_clients]}, [fd_clients]},
Children = [Clients], Cache = {fd_cache,
{fd_cache, start_link, []},
permanent,
5000,
worker,
[fd_cache]},
Children = [Clients, Cache],
{ok, {RestartStrategy, Children}}. {ok, {RestartStrategy, Children}}.
+2 -1
View File
@@ -13,7 +13,8 @@
reps/1, reps/1,
reps/2, reps/2,
add/1, add/1,
mul/1 mul/1,
zero/0, one/0
]). ]).
+1 -1
View File
@@ -219,7 +219,7 @@ dot({bm, {rc, 1, Same}, Bits1}, {bm, {rc, Same, 1}, Bits2}) ->
parity(SummandBits). parity(SummandBits).
-spec mand(bitstring(), bitstring()) -> bitstring(). -spec mand(bm(), bm()) -> bm().
% @doc bitwise-and two matrices of the same shape % @doc bitwise-and two matrices of the same shape
mand({bm, Shape, Bits1}, {bm, Shape, Bits2}) -> mand({bm, Shape, Bits1}, {bm, Shape, Bits2}) ->
+5
View File
@@ -59,6 +59,11 @@ eval_sexp(Args = [{snowflake, <<"define">>}, {pattern, Pat}, Expr], Ctx0) ->
Error -> Error ->
Error Error
end; end;
eval_sexp(_Args = [{snowflake, <<"undefine">>}, {pattern, Pat}], Ctx0) ->
case wfc_eval_context:undefine(Pat, Ctx0) of
{ok, NewContext} -> {ok, noop, NewContext};
Error -> Error
end;
eval_sexp([{snowflake, SF} | Args], Ctx0) -> eval_sexp([{snowflake, SF} | Args], Ctx0) ->
% first evaluate the arguments individually % first evaluate the arguments individually
case eval_sexp_args(Args, Ctx0, []) of case eval_sexp_args(Args, Ctx0, []) of
+64 -3
View File
@@ -9,6 +9,7 @@
default/0, default/0,
default_snowflakes/0, default_snowflakes/0,
define/3, define/3,
undefine/2,
resolve_pattern/2, resolve_pattern/2,
resolve_snowflake/2 resolve_snowflake/2
]). ]).
@@ -32,20 +33,80 @@ default() ->
default_snowflakes() -> default_snowflakes() ->
#{<<"and">> => fun wfc:mul/1, #{<<"and">> => fun wfc:mul/1,
<<"xor">> => fun wfc:add/1}. <<"xor">> => fun wfc:add/1,
<<"implies">> => fun snf_implies/1,
<<"ior">> =>
fun IOR([S1 | Rest]) ->
case IOR(Rest) of
{ok, S2} -> {ok, sf_ior(S1, S2)};
Error -> Error
end;
IOR([]) ->
{ok, wfc:one()}
end,
<<"not">> =>
fun ([S]) -> {ok, sf_not(S)};
(Bad) -> {error, wfc_utils:str("not/1: wrong number of arguments: ~p", [Bad])}
end,
<<"impliedby">> =>
fun ([A, B]) -> {ok, sf_impliedby(A, B)};
(Bad) -> {error, wfc_utils:str("impliedby/2: wrong number of arguments: ~p", [Bad])}
end,
<<"iff">> =>
fun ([A, B]) -> {ok, sf_iff(A, B)};
(Bad) -> {error, wfc_utils:str("iff/2: wrong number of arguments: ~p", [Bad])}
end
}.
snf_implies([A, B]) -> {ok, sf_implies(A, B)};
snf_implies(Bad) -> {error, wfc_utils:str("implies/2: wrong number of arguments: ~p", [Bad])}.
sf_ior(A, B) ->
wfc_sftt:appl_ttf(fun ttf_ior/2, [A, B]).
sf_not(A) ->
{ok, Result} = wfc:add([wfc_sentence:one(), A]),
Result.
sf_implies(A, B) ->
wfc_sftt:appl_ttf(fun ttf_implies/2, [A, B]).
sf_impliedby(A, B) ->
sf_implies(B, A).
sf_iff(A, B) ->
{ok, Result} = wfc:mul([sf_implies(A, B), sf_impliedby(A, B)]),
Result.
ttf_ior(0, 0) -> 0;
ttf_ior(1, 0) -> 1;
ttf_ior(0, 1) -> 1;
ttf_ior(1, 1) -> 1.
ttf_implies(0, 0) -> 1;
ttf_implies(1, 0) -> 0;
ttf_implies(0, 1) -> 1;
ttf_implies(1, 1) -> 1.
define(Pat, Sentence, Ctx = #ctx{patterns = OldPatterns}) -> define(Pat, Sentence, Ctx = #ctx{patterns = OldPatterns}) ->
NewPatterns = maps:put(Pat, Sentence, OldPatterns), NewPatterns = maps:put(Pat, Sentence, OldPatterns),
{ok, Ctx#ctx{patterns = NewPatterns}}. {ok, Ctx#ctx{patterns = NewPatterns}}.
undefine(Pat, Ctx = #ctx{patterns = OldPatterns}) ->
NewPatterns = maps:remove(Pat, OldPatterns),
{ok, Ctx#ctx{patterns = NewPatterns}}.
resolve_pattern(Pat, Ctx = #ctx{patterns = Patterns}) -> resolve_pattern(Pat, Ctx = #ctx{patterns = Patterns}) ->
case maps:find(Pat, Patterns) of case maps:find(Pat, Patterns) of
error -> {error, wfc_utils:str("wfc_eval_context:resolve_pattern: not found: ~w; context: ~w", [Pat, Ctx])}; error -> {error, wfc_utils:str("wfc_eval_context:resolve_pattern: not found: ~s; context: ~w", [Pat, Ctx])};
Result -> Result Result -> Result
end. end.
resolve_snowflake(SF, Ctx = #ctx{snowflakes = Snowflakes}) -> resolve_snowflake(SF, Ctx = #ctx{snowflakes = Snowflakes}) ->
case maps:find(SF, Snowflakes) of case maps:find(SF, Snowflakes) of
error -> {error, wfc_utils:str("wfc_eval_context:resolve_snowflake: not found: ~w; context: ~w", [SF, Ctx])}; error -> {error, wfc_utils:str("wfc_eval_context:resolve_snowflake: not found: ~s; context: ~w", [SF, Ctx])};
Result -> Result Result -> Result
end. end.
+12 -5
View File
@@ -12,7 +12,7 @@
arity/1, arity/1,
tt/1, sf/1, tt/1, sf/1,
sf_to_tt/1, tt_to_sf/1, sf_to_tt/1, tt_to_sf/1,
eval/2, appl_ttf/2, appl/2,
bfls/1, bfls/1,
bfl/2 bfl/2
]). ]).
@@ -73,11 +73,18 @@ sf(List) ->
{sf, wfc_bm:col(List)}. {sf, wfc_bm:col(List)}.
-spec eval(sf() | tt(), [wfc:sentence()]) -> wfc:sentence(). -spec appl_ttf(fun(), [wfc:sentence()]) -> wfc:sentence().
eval(TT = {tt, _}, Sentences) -> appl_ttf(Fun, Sentences) ->
eval(tt_to_sf(TT), Sentences); SF = ttfun_to_sf(Fun),
eval(SF = {sf, SFBM}, Sentences) -> appl(SF, Sentences).
-spec appl(sf() | tt(), [wfc:sentence()]) -> wfc:sentence().
appl(TT = {tt, _}, Sentences) ->
appl(tt_to_sf(TT), Sentences);
appl(SF = {sf, SFBM}, Sentences) ->
Arity = arity(SF), Arity = arity(SF),
Arity = length(Sentences), Arity = length(Sentences),
% [0, 0, 0], [1, 0, 0], ... % [0, 0, 0], [1, 0, 0], ...