-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.