have a server that like does stuff now

This commit is contained in:
Peter Harpending 2025-10-11 02:07:20 -06:00
parent fa16da8178
commit 6c45f30919
8 changed files with 107 additions and 24 deletions

View File

@ -6,7 +6,7 @@
version = undefined :: undefined | http10 | http11 | http20,
headers = undefined :: undefined | [{Key :: binary(), Value :: binary()}],
cookies = undefined :: undefined | #{Key :: binary() := Value :: binary()},
enctype = undefined :: undefined | none | urlencoded | multipart(),
enctype = undefined :: undefined | none | urlencoded | json | multipart(),
size = undefined :: undefined | none | non_neg_integer(),
body = undefined :: undefined | none | body()}).
@ -20,6 +20,6 @@
-type method() :: get | post | options.
-type multipart() :: {multipart, Boundary :: binary()}.
-type body() :: {partial, binary()} | {multipart, [body_part()]} | binary().
-type body() :: {partial, binary()} | {multipart, [body_part()]} | zj:value() | binary().
-type body_part() :: {Field :: binary(), Data :: binary()}
| {Field :: binary(), Name :: binary(), Data :: binary()}.

View File

@ -24,7 +24,7 @@
<script>
let ielt = document.getElementById('wfc-input');
let oelt = document.getElementById('wfc-output');
let MAX_OELT_HEIGHT = 100;
let MAX_OELT_HEIGHT = 300;
function auto_resize_output() {
// if the user has manually resized their output, we do nothing
@ -46,12 +46,33 @@
}
}
function on_server_return(x) {
console.log('on_server_return:', x);
async function on_server_return(response) {
console.log('on_server_return:', response);
if (response.ok) {
let jsbs = await response.json();
console.log('jsbs', jsbs);
// jsbs: {ok: true, result: string} | {ok: false, error: string}
if (jsbs.ok) {
// this means got a result back from server
// put it in
oelt.value += jsbs.result;
oelt.value += '\n';
}
else {
// this is an error at the WFC level
oelt.value += jsbs.error;
oelt.value += '\n';
}
}
// this means we sent an invalid request
else {
oelt.value += 'HTTP ERROR, SEE BROWSER CONSOLE\n'
}
}
function on_some_bullshit(x) {
console.log('on_some_bullshit:', x);
oelt.value += 'NETWORK ERROR, SEE BROWSER CONSOLE\n'
}
function fetch_wfcin(user_line) {
@ -82,11 +103,11 @@
let nonempty_contents = contents.trim().length > 0;
if (nonempty_contents) {
// put in output
// if it's nonempty add a newline
if (oelt.value.length > 0) {
oelt.value += '\n';
}
oelt.value += '> ' + contents;
// // if it's nonempty add a newline
// if (oelt.value.length > 0) {
// oelt.value += '\n';
// }
oelt.value += '> ' + contents + '\n';
oelt.hidden = false;
// query backend for result

View File

@ -263,11 +263,42 @@ default_css(Sock) ->
http_err(Sock, 500)
end.
wfcin(Sock, #request{enctype = json, body = #{"wfcin" := Input}}) ->
tell("wfcin good request: ~tp", [Input]),
RespObj =
try
case wfc_read:expr(Input) of
%% FIXME support multiple expressions
{ok, Expr, _Rest} ->
case wfc_eval:eval(Expr, wfc_eval_context:default()) of
{ok, noop, _NewContext} -> jsgud("<noop>");
{ok, Sentence, _NewContext} -> jsgud(wfc_pp:sentence(Sentence));
{error, Message} -> jsbad(Message)
end;
{error, Message} ->
jsbad(Message)
end
catch
error:E:S ->
ErrorMessage = unicode:characters_to_list(io_lib:format("parser crashed: ~p:~p", [E, S])),
jsbad(ErrorMessage)
end,
Body = zj:encode(RespObj),
Response = #response{headers = [{"content-type", "application/json"}],
body = Body},
respond(Sock, Response);
wfcin(Sock, Request) ->
tell("wfcin request: ~tp", [Request]),
http_err(Sock, 501).
tell("wfcin: bad request: ~tp", [Request]),
http_err(Sock, 400).
jsgud(X) ->
#{"ok" => true,
"result" => X}.
jsbad(X) ->
#{"ok" => false,
"error" => X}.
%% FIXME
http_err(Sock, N) ->
Slogan = qhl:slogan(N),
Body = ["<!doctype html>"

View File

@ -220,7 +220,6 @@ terminate(_, _) ->
do_listen(PortNum, State = #s{port_num = none}) ->
SocketOptions =
[inet6,
{packet, line},
{active, once},
{mode, binary},
{keepalive, true},

View File

@ -46,7 +46,7 @@ rep(String) ->
case wfc_read:expr(String) of
{ok, Expr, Rest} ->
case wfc_eval:eval(Expr) of
{ok, Result, NewContext} ->
{ok, Result, _NewContext} ->
io:format("result: ~s~n", [wfc_pp:eval_result(Result)]),
%io:format("context: ~tw~n", [NewContext]),
ok;
@ -107,10 +107,10 @@ mul([Arg | Rest]) ->
case to_sentence(Arg) of
{ok, S} ->
case mul(Rest) of
{ok, S2} -> wfc_sentence:mul(S, S2);
{ok, S2} -> {ok, wfc_sentence:mul(S, S2)};
Error -> Error
end;
Error -> Error
end;
mul([]) ->
wfc_sentence:zero().
{ok, wfc_sentence:one()}.

View File

@ -21,7 +21,9 @@
%-type sexp() :: {sexp, [expr()]}.
%-type expr() :: sexp() | ltr() | op() | snowflake() | pattern() | val().
-spec eval(expr()) -> {ok, eval_result(), context()} | {error, string()}.
-spec eval(expr()) -> Result
when Result :: {ok, eval_result(), NewContext :: context()}
| {error, string()}.
eval(Expr) ->
eval(Expr, wfc_eval_context:default()).
@ -57,6 +59,24 @@ eval_sexp(Args = [{snowflake, <<"define">>}, {pattern, Pat}, Expr], Ctx0) ->
Error ->
Error
end;
eval_sexp([{snowflake, SF} | Args], Ctx0) ->
% first evaluate the arguments individually
case eval_sexp_args(Args, Ctx0, []) of
% assuming they each evaluate to sentences and some new context
{ok, Sentences, Ctx1} ->
% look up the snowflake in the context
case wfc_eval_context:resolve_snowflake(SF, Ctx1) of
{ok, SnowflakeFun} ->
case SnowflakeFun(Sentences) of
{ok, Sentence} -> {ok, Sentence, Ctx1};
Error -> Error
end;
Error ->
Error
end;
Error ->
Error
end;
eval_sexp([{op, '+'} | Exprs], Ctx) ->
case eval_sexp_args(Exprs, Ctx, []) of
{ok, Sentences, NewCtx} -> {ok, wfc_sentence:add(Sentences), NewCtx};

View File

@ -7,15 +7,17 @@
-export([
new/0,
default/0,
default_snowflakes/0,
define/3,
resolve_pattern/2
resolve_pattern/2,
resolve_snowflake/2
]).
-type sentence() :: wfc_sentence:sentence().
-record(ctx,
{snowflakes :: #{binary() := fun()},
patterns :: #{binary() := sentence()}}).
{snowflakes = #{} :: #{binary() := fun(([sentence()]) -> {ok, sentence()} | {error, string()})},
patterns = #{} :: #{binary() := sentence()}}).
-opaque context() :: #ctx{}.
@ -25,10 +27,12 @@ new() ->
patterns = #{}}.
%% FIXME
default() ->
new().
#ctx{snowflakes = default_snowflakes()}.
default_snowflakes() ->
#{<<"and">> => fun wfc:mul/1,
<<"xor">> => fun wfc:add/1}.
define(Pat, Sentence, Ctx = #ctx{patterns = OldPatterns}) ->
NewPatterns = maps:put(Pat, Sentence, OldPatterns),
@ -39,3 +43,9 @@ resolve_pattern(Pat, Ctx = #ctx{patterns = Patterns}) ->
error -> {error, wfc_utils:str("wfc_eval_context:resolve_pattern: not found: ~w; context: ~w", [Pat, Ctx])};
Result -> Result
end.
resolve_snowflake(SF, Ctx = #ctx{snowflakes = Snowflakes}) ->
case maps:find(SF, Snowflakes) of
error -> {error, wfc_utils:str("wfc_eval_context:resolve_snowflake: not found: ~w; context: ~w", [SF, Ctx])};
Result -> Result
end.

View File

@ -22,7 +22,9 @@
-type expr() :: sexp() | ltr() | op() | snowflake() | pattern() | val().
-spec expr(string()) -> {ok, expr(), Rest :: string()} | {error, string()}.
-spec expr(string()) -> Result
when Result :: {ok, Expression :: expr(), Rest :: string()}
| {error, string()}.
expr(Str0) ->
{ok, skip, Str1} = whitespace(Str0),