fewd/src/wfc_eval.erl
2025-10-02 08:44:17 -07:00

85 lines
2.7 KiB
Erlang

-module(wfc_eval).
-export_type([
]).
-export([
eval/1, eval/2
]).
-type context() :: wfc_eval_context:context().
-type expr() :: wfc_read:expr().
-type sentence() :: wfc_sentence:sentence().
-type eval_result() :: noop | sentence().
%-type op() :: {op, '+' | '*'}.
%-type val() :: {val, 0 | 1}.
%-type ltr() :: wfc_ltr:ltr().
%-type snowflake() :: {snowflake, binary()}.
%-type pattern() :: {pattern, binary()}.
%-type sexp() :: {sexp, [expr()]}.
%-type expr() :: sexp() | ltr() | op() | snowflake() | pattern() | val().
-spec eval(expr()) -> {ok, eval_result(), context()} | {error, string()}.
eval(Expr) ->
eval(Expr, wfc_eval_context:default()).
-spec eval(expr(), context()) -> {ok, eval_result(), context()} | {error, string()}.
eval({sexp, Args}, Ctx) -> eval_sexp(Args, Ctx);
eval({val, 0}, Ctx) -> {ok, wfc_sentence:zero(), Ctx};
eval({val, 1}, Ctx) -> {ok, wfc_sentence:one(), Ctx};
eval({pattern, Pat}, Ctx) ->
case wfc_eval_context:resolve_pattern(Pat, Ctx) of
{ok, Sentence} -> {ok, Sentence, Ctx};
Error -> Error
end;
eval(Ltr = {c, _}, Ctx) ->
case wfc:to_sentence(Ltr) of
{ok, S} -> {ok, S, Ctx};
Error -> Error
end;
eval(Expr, _) -> {error, wfc_utils:str("wfc_eval:eval: cannot evaluate expression: ~p", [Expr])}.
eval_sexp(Args = [{snowflake, <<"define">>}, {pattern, Pat}, Expr], Ctx0) ->
case eval(Expr, Ctx0) of
{ok, noop, _} ->
{error, wfc_utils:str("wfc_eval:eval_sexp: define X Y: Y evaluated to noop: ~w", [Args])};
{ok, Sentence, _} ->
case wfc_eval_context:define(Pat, Sentence, Ctx0) of
{ok, NewContext} -> {ok, noop, NewContext};
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};
Error -> Error
end;
eval_sexp([{op, '*'} | Exprs], Ctx) ->
case eval_sexp_args(Exprs, Ctx, []) of
{ok, Sentences, NewCtx} -> {ok, wfc_sentence:mul(Sentences), NewCtx};
Error -> Error
end;
eval_sexp(Args, Ctx) ->
{error, wfc_utils:str("wfc_eval:eval_sexp: bad sexp: Args=~tw; Ctx=~tw", [Args, Ctx])}.
eval_sexp_args(Exprs, Ctx0, Acc) ->
case Exprs of
[] -> {ok, lists:reverse(Acc), Ctx0};
[E | Rest] ->
case eval(E, Ctx0) of
{ok, noop, Ctx1} -> eval_sexp_args(Rest, Ctx1, Acc);
{ok, S, Ctx1} -> eval_sexp_args(Rest, Ctx1, [S | Acc]);
Error ->
Error
end
end.