diff --git a/src/cauder_eval.erl b/src/cauder_eval.erl index 620882e..84b1a93 100644 --- a/src/cauder_eval.erl +++ b/src/cauder_eval.erl @@ -1,9 +1,17 @@ +%%%----------------------------------------------------------------------------- +%%% @doc The CauDEr meta interpreter. +%%% This module provides an interpreter for Erlang expressions. +%%% The expressions are in the abstract syntax as returned by `cauder_syntax'. +%%% @see erl_eval +%%% @end +%%%----------------------------------------------------------------------------- + -module(cauder_eval). %% API --export([seq/3, abstract/1, concrete/1, is_value/1, is_reducible/2]). --export([match_rec_pid/6, match_rec_uid/4]). --export([clause_line/3]). +-export([exprs/3]). +-export([is_value/1, is_reducible/2]). +-export([match_receive_pid/6, match_receive_uid/4]). -include("cauder_message.hrl"). -include("cauder_stack.hrl"). @@ -34,35 +42,15 @@ -type label_send() :: #label_send{}. -type label_receive() :: #label_receive{}. +-type expression() :: cauder_syntax:abstract_expr(). +-type expressions() :: [cauder_syntax:abstract_expr()]. +-type expression_list() :: [expression()]. +-type clauses() :: [cauder_syntax:af_clause()]. + %%%============================================================================= %%% API %%%============================================================================= -%%------------------------------------------------------------------------------ -%% @doc Evaluates the first reducible expression from the given list. -%% -%% Evaluates the first reducible expression from the given list of expressions, -%% given an environment and a call stack, then returns a record with the updated -%% information and a label indicating the type of step performed. -%% -%% @see is_reducible/2 - --spec eval_list(Bindings, Expressions, Stack) -> Result when - Bindings :: cauder_bindings:bindings(), - Expressions :: [cauder_syntax:abstract_expr()], - Stack :: cauder_stack:stack(), - Result :: cauder_eval:result(). - -eval_list(Bs, [E | Es], Stk) -> - case is_reducible(E, Bs) of - true -> - R = #result{expr = Es1} = expr(Bs, E, Stk), - R#result{expr = Es1 ++ Es}; - false -> - R = #result{expr = Es1} = eval_list(Bs, Es, Stk), - R#result{expr = [E | Es1]} - end. - %%------------------------------------------------------------------------------ %% @doc Evaluates the given sequence of expression. %% @@ -76,13 +64,13 @@ eval_list(Bs, [E | Es], Stk) -> %% %% @see is_reducible/2 --spec seq(Bindings, Expressions, Stack) -> Result when +-spec exprs(Expressions, Bindings, Stack) -> Result when + Expressions :: expressions(), Bindings :: cauder_bindings:bindings(), - Expressions :: [cauder_syntax:abstract_expr()], Stack :: cauder_stack:stack(), Result :: cauder_eval:result(). -seq(Bs, [E | Es], Stk) -> +exprs([E | Es], Bs, Stk) -> case is_reducible(E, Bs) of false -> case Es of @@ -91,17 +79,25 @@ seq(Bs, [E | Es], Stk) -> {Entry, Stk1} = cauder_stack:pop(Stk), case Entry of {value, #s_function{env = Bs1, expr = Es1, var = Var}} -> - Es2 = cauder_syntax:replace_variable(Es1, setelement(2, Var, Line), concrete(E)), + Es2 = cauder_syntax:replace_variable( + Es1, + setelement(2, Var, Line), + cauder_syntax:concrete(E) + ), #result{env = Bs1, expr = Es2, stack = Stk1}; {value, #s_block{expr = Es1, var = Var}} -> - Es2 = cauder_syntax:replace_variable(Es1, setelement(2, Var, Line), concrete(E)), + Es2 = cauder_syntax:replace_variable( + Es1, + setelement(2, Var, Line), + cauder_syntax:concrete(E) + ), #result{env = Bs, expr = Es2, stack = Stk1} end; _ -> #result{env = Bs, expr = Es, stack = Stk} end; true -> - #result{env = Bs1, expr = Es1, stack = Stk1} = Result = expr(Bs, E, Stk), + #result{env = Bs1, expr = Es1, stack = Stk1} = Result = expr(E, Bs, Stk), case cauder_stack:pop(Stk1) of {{value, #s_function{env = Bs2, expr = Es2} = Entry}, Stk} -> Entry1 = Entry#s_function{env = Bs1, expr = Es1 ++ Es}, @@ -127,71 +123,69 @@ seq(Bs, [E | Es], Stk) -> %% @doc Evaluates the given `Expression' and returns a tuple with an updated %% environment, the expression that resulted from the evaluation, and a label. --spec expr(Bindings, Expression, Stack) -> Result when +-spec expr(Expression, Bindings, Stack) -> Result when + Expression :: expression(), Bindings :: cauder_bindings:bindings(), - Expression :: cauder_syntax:abstract_expr(), Stack :: cauder_stack:stack(), Result :: cauder_eval:result(). -expr(Bs, {var, Line, Name}, Stk) -> +expr({var, Line, Name}, Bs, Stk) -> {ok, Value} = cauder_bindings:find(Name, Bs), #result{env = Bs, expr = [{value, Line, Value}], stack = Stk}; -expr(Bs, E = {cons, Line, H0, T0}, Stk) -> +expr(E = {cons, Line, H0, T0}, Bs, Stk) -> case is_reducible(H0, Bs) of true -> - R = #result{expr = [H]} = expr(Bs, H0, Stk), + R = #result{expr = [H]} = expr(H0, Bs, Stk), case is_value(H) andalso is_value(T0) of - true -> R#result{expr = [{value, Line, [concrete(H) | concrete(T0)]}]}; + true -> R#result{expr = [{value, Line, [cauder_syntax:concrete(H) | cauder_syntax:concrete(T0)]}]}; false -> R#result{expr = [setelement(3, E, H)]} end; false -> case is_reducible(T0, Bs) of true -> - R = #result{expr = [T]} = expr(Bs, T0, Stk), + R = #result{expr = [T]} = expr(T0, Bs, Stk), case is_value(H0) andalso is_value(T) of - true -> R#result{expr = [{value, Line, [concrete(H0) | concrete(T)]}]}; - false -> R#result{expr = [setelement(4, E, T)]} + true -> + R#result{expr = [{value, Line, [cauder_syntax:concrete(H0) | cauder_syntax:concrete(T)]}]}; + false -> + R#result{expr = [setelement(4, E, T)]} end; false -> - #result{env = Bs, expr = [{value, Line, [concrete(H0) | concrete(T0)]}], stack = Stk} + #result{ + env = Bs, + expr = [{value, Line, [cauder_syntax:concrete(H0) | cauder_syntax:concrete(T0)]}], + stack = Stk + } end end; -expr(Bs, E = {tuple, Line, Es0}, Stk) -> - R = #result{expr = Es} = eval_list(Bs, Es0, Stk), +expr(E = {tuple, Line, Es0}, Bs, Stk) -> + R = #result{expr = Es} = expr_list(Es0, Bs, Stk), case is_value(Es) of true -> - Tuple = list_to_tuple(lists:map(fun concrete/1, Es)), + Tuple = list_to_tuple(lists:map(fun cauder_syntax:concrete/1, Es)), #result{env = Bs, expr = [{value, Line, Tuple}], stack = Stk}; false -> R#result{expr = [setelement(3, E, Es)]} end; -expr(Bs, {'if', Line, Cs}, Stk0) -> - case match_if(Bs, Cs) of - {match, Body} -> - Var = cauder_utils:temp_variable(Line), - Entry = #s_block{type = 'if', expr = Body, var = Var}, - Stk = cauder_stack:push(Entry, Stk0), - #result{env = Bs, expr = [Var], stack = Stk}; - nomatch -> - error(if_clause) - end; -expr(Bs0, E = {'case', Line, A, Cs}, Stk0) -> +expr({'if', Line, Cs}, Bs, Stk0) -> + Body = if_clauses(Cs, Bs), + Var = cauder_utils:temp_variable(Line), + Entry = #s_block{type = 'if', expr = Body, var = Var}, + Stk = cauder_stack:push(Entry, Stk0), + #result{env = Bs, expr = [Var], stack = Stk}; +expr(E = {'case', Line, A, Cs}, Bs0, Stk0) -> case is_reducible(A, Bs0) of true -> - eval_and_update({Bs0, A, Stk0}, {3, E}); + eval_and_update(3, E, Bs0, Stk0); false -> - case match_case(Bs0, Cs, A) of - {match, Bs, Body} -> - Var = cauder_utils:temp_variable(Line), - Entry = #s_block{type = 'case', expr = Body, var = Var}, - Stk = cauder_stack:push(Entry, Stk0), - #result{env = Bs, expr = [Var], stack = Stk}; - nomatch -> - error({case_clause, concrete(A)}) - end + {Body, Bs} = case_clauses(Cs, A, Bs0), + Var = cauder_utils:temp_variable(Line), + Entry = #s_block{type = 'case', expr = Body, var = Var}, + Stk = cauder_stack:push(Entry, Stk0), + #result{env = Bs, expr = [Var], stack = Stk} end; %% TODO Support receive with timeout -expr(Bs, {'receive', Line, Cs}, Stk0) -> +expr({'receive', Line, Cs}, Bs, Stk0) -> % TODO One of these variables is not necessary Var = cauder_utils:temp_variable(Line), VarBody = cauder_utils:temp_variable(Line), @@ -201,7 +195,7 @@ expr(Bs, {'receive', Line, Cs}, Stk0) -> #result{env = Bs, expr = [Var], stack = Stk, label = Label}; % TODO Support fun() as entry point argument? % TODO Handle calls to interpreted fun() from uninterpreted module -expr(Bs, {'make_fun', Line, Name, Cs}, Stk0) -> +expr({'make_fun', Line, Name, Cs}, Bs, Stk0) -> {ok, M} = cauder_stack:current_module(Stk0), Arity = length(element(3, hd(Cs))), Info = {{M, Name}, Bs, Cs}, @@ -217,190 +211,190 @@ expr(Bs, {'make_fun', Line, Name, Cs}, Stk0) -> _ -> error({argument_limit, Arity}) end, #result{env = Bs, expr = [{value, Line, Fun}], stack = Stk0}; -expr(Bs, E = {bif, Line, M, F, As}, Stk) -> +expr(E = {bif, Line, M, F, As}, Bs, Stk) -> case is_reducible(As, Bs) of true -> - eval_and_update({Bs, As, Stk}, {5, E}); + eval_and_update(5, E, Bs, Stk); false -> - Value = apply(M, F, lists:map(fun concrete/1, As)), + Value = apply(M, F, lists:map(fun cauder_syntax:concrete/1, As)), #result{env = Bs, expr = [{value, Line, Value}], stack = Stk} end; -expr(Bs, {self, Line}, Stk) -> +expr({self, Line}, Bs, Stk) -> Var = cauder_utils:temp_variable(Line), Label = #label_self{var = Var}, #result{env = Bs, expr = [Var], stack = Stk, label = Label}; -expr(Bs, {node, Line}, Stk) -> +expr({node, Line}, Bs, Stk) -> Var = cauder_utils:temp_variable(Line), Label = #label_node{var = Var}, #result{env = Bs, expr = [Var], stack = Stk, label = Label}; -expr(Bs, {nodes, Line}, Stk) -> +expr({nodes, Line}, Bs, Stk) -> Var = cauder_utils:temp_variable(Line), Label = #label_nodes{var = Var}, #result{env = Bs, expr = [Var], stack = Stk, label = Label}; -expr(Bs, E = {spawn, Line, Fun}, Stk) -> +expr(E = {spawn, Line, Fun}, Bs, Stk) -> case is_reducible(Fun, Bs) of true -> - eval_and_update({Bs, Fun, Stk}, {3, E}); + eval_and_update(3, E, Bs, Stk); false -> Var = cauder_utils:temp_variable(Line), Label = #label_spawn_fun{var = Var, function = Fun}, #result{env = Bs, expr = [Var], stack = Stk, label = Label} end; -expr(Bs, E = {spawn, Line, N, Fun}, Stk) -> +expr(E = {spawn, Line, N, Fun}, Bs, Stk) -> case is_reducible(N, Bs) of true -> - eval_and_update({Bs, N, Stk}, {3, E}); + eval_and_update(3, E, Bs, Stk); false -> case is_reducible(Fun, Bs) of true -> - eval_and_update({Bs, Fun, Stk}, {4, E}); + eval_and_update(4, E, Bs, Stk); false -> Var = cauder_utils:temp_variable(Line), - Label = #label_spawn_fun{var = Var, node = concrete(N), function = Fun}, + Label = #label_spawn_fun{var = Var, node = cauder_syntax:concrete(N), function = Fun}, #result{env = Bs, expr = [Var], stack = Stk, label = Label} end end; -expr(Bs, E = {spawn, Line, M, F, As}, Stk) -> +expr(E = {spawn, Line, M, F, As}, Bs, Stk) -> case is_reducible(M, Bs) of true -> - eval_and_update({Bs, M, Stk}, {3, E}); + eval_and_update(3, E, Bs, Stk); false -> case is_reducible(F, Bs) of true -> - eval_and_update({Bs, F, Stk}, {4, E}); + eval_and_update(4, E, Bs, Stk); false -> case is_reducible(As, Bs) of true -> - eval_and_update({Bs, As, Stk}, {5, E}); + eval_and_update(5, E, Bs, Stk); false -> Var = cauder_utils:temp_variable(Line), Label = #label_spawn_mfa{ var = Var, - module = concrete(M), - function = concrete(F), - args = concrete(As) + module = cauder_syntax:concrete(M), + function = cauder_syntax:concrete(F), + args = cauder_syntax:concrete(As) }, #result{env = Bs, expr = [Var], stack = Stk, label = Label} end end end; -expr(Bs, E = {spawn, Line, N, M, F, As}, Stk) -> +expr(E = {spawn, Line, N, M, F, As}, Bs, Stk) -> case is_reducible(N, Bs) of true -> - eval_and_update({Bs, N, Stk}, {3, E}); + eval_and_update(3, E, Bs, Stk); false -> case is_reducible(M, Bs) of true -> - eval_and_update({Bs, M, Stk}, {4, E}); + eval_and_update(4, E, Bs, Stk); false -> case is_reducible(F, Bs) of true -> - eval_and_update({Bs, F, Stk}, {5, E}); + eval_and_update(5, E, Bs, Stk); false -> case is_reducible(As, Bs) of true -> - eval_and_update({Bs, As, Stk}, {6, E}); + eval_and_update(6, E, Bs, Stk); false -> Var = cauder_utils:temp_variable(Line), Label = #label_spawn_mfa{ var = Var, - node = concrete(N), - module = concrete(M), - function = concrete(F), - args = concrete(As) + node = cauder_syntax:concrete(N), + module = cauder_syntax:concrete(M), + function = cauder_syntax:concrete(F), + args = cauder_syntax:concrete(As) }, #result{env = Bs, expr = [Var], stack = Stk, label = Label} end end end end; -expr(Bs, E = {start, Line, N}, Stk) -> +expr(E = {start, Line, N}, Bs, Stk) -> case is_reducible(N, Bs) of true -> - eval_and_update({Bs, N, Stk}, {3, E}); + eval_and_update(3, E, Bs, Stk); false -> Var = cauder_utils:temp_variable(Line), - Label = #label_start{var = Var, name = concrete(N)}, + Label = #label_start{var = Var, name = cauder_syntax:concrete(N)}, #result{env = Bs, expr = [Var], stack = Stk, label = Label} end; -expr(Bs, E = {start, Line, H, N}, Stk) -> +expr(E = {start, Line, H, N}, Bs, Stk) -> case is_reducible(H, Bs) of true -> - eval_and_update({Bs, H, Stk}, {3, E}); + eval_and_update(3, E, Bs, Stk); false -> case is_reducible(N, Bs) of true -> - eval_and_update({Bs, N, Stk}, {4, E}); + eval_and_update(4, E, Bs, Stk); false -> Var = cauder_utils:temp_variable(Line), - Label = #label_start{var = Var, name = concrete(N), host = concrete(H)}, + Label = #label_start{var = Var, name = cauder_syntax:concrete(N), host = cauder_syntax:concrete(H)}, #result{env = Bs, expr = [Var], stack = Stk, label = Label} end end; -expr(Bs, E = {Send, _, L, R}, Stk) when Send =:= 'send' orelse Send =:= 'send_op' -> +expr(E = {Send, _, L, R}, Bs, Stk) when Send =:= 'send' orelse Send =:= 'send_op' -> case is_reducible(L, Bs) of true -> - eval_and_update({Bs, L, Stk}, {3, E}); + eval_and_update(3, E, Bs, Stk); false -> case is_reducible(R, Bs) of true -> - eval_and_update({Bs, R, Stk}, {4, E}); + eval_and_update(4, E, Bs, Stk); false -> - Label = #label_send{dst = concrete(L), val = concrete(R)}, + Label = #label_send{dst = cauder_syntax:concrete(L), val = cauder_syntax:concrete(R)}, #result{env = Bs, expr = [R], stack = Stk, label = Label} end end; -expr(Bs0, E = {local_call, Line, F, As}, Stk0) -> +expr(E = {local_call, Line, F, As}, Bs0, Stk0) -> case is_reducible(As, Bs0) of true -> - eval_and_update({Bs0, As, Stk0}, {4, E}); + eval_and_update(4, E, Bs0, Stk0); false -> {ok, M} = cauder_stack:current_module(Stk0), A = length(As), {_, Cs} = cauder_utils:fundef_lookup({M, F, A}), - {match, Bs, Body} = match_fun(Cs, As), + {Body, Bs} = match_fun(Cs, As), Var = cauder_utils:temp_variable(Line), Entry = #s_function{mfa = {M, F, A}, env = Bs, expr = Body, var = Var}, Stk = cauder_stack:push(Entry, Stk0), #result{env = Bs0, expr = [Var], stack = Stk} end; -expr(Bs0, E = {remote_call, Line, M, F, As}, Stk0) -> +expr(E = {remote_call, Line, M, F, As}, Bs0, Stk0) -> case is_reducible(As, Bs0) of - true -> eval_and_update({Bs0, As, Stk0}, {5, E}); - false -> eval_remote_call(M, F, As, Stk0, Line, Bs0) + true -> eval_and_update(5, E, Bs0, Stk0); + false -> remote_call(M, F, As, Stk0, Line, Bs0) end; % TODO Handle calls to self/0, spawn/1, spawn/3 -expr(Bs0, E = {apply, Line, M0, F0, As}, Stk0) -> +expr(E = {apply, Line, M0, F0, As}, Bs0, Stk0) -> case is_reducible(M0, Bs0) of true -> - eval_and_update({Bs0, M0, Stk0}, {3, E}); + eval_and_update(3, E, Bs0, Stk0); false -> case is_reducible(F0, Bs0) of true -> - eval_and_update({Bs0, F0, Stk0}, {4, E}); + eval_and_update(4, E, Bs0, Stk0); false -> case is_reducible(As, Bs0) of true -> - eval_and_update({Bs0, As, Stk0}, {5, E}); + eval_and_update(5, E, Bs0, Stk0); false -> - M = concrete(M0), - F = concrete(F0), - eval_remote_call(M, F, As, Stk0, Line, Bs0) + M = cauder_syntax:concrete(M0), + F = cauder_syntax:concrete(F0), + remote_call(M, F, As, Stk0, Line, Bs0) end end end; -expr(Bs0, E = {apply_fun, Line, Fun, As}, Stk0) -> +expr(E = {apply_fun, Line, Fun, As}, Bs0, Stk0) -> case is_reducible(Fun, Bs0) of true -> - eval_and_update({Bs0, Fun, Stk0}, {3, E}); + eval_and_update(3, E, Bs0, Stk0); false -> case is_reducible(As, Bs0) of true -> - eval_and_update({Bs0, As, Stk0}, {4, E}); + eval_and_update(4, E, Bs0, Stk0); false -> A = length(As), - {env, [{{M, F}, Bs1, Cs}]} = erlang:fun_info(concrete(Fun), env), - {match, Bs2, Body} = match_fun(Cs, As), + {env, [{{M, F}, Bs1, Cs}]} = erlang:fun_info(cauder_syntax:concrete(Fun), env), + {Body, Bs2} = match_fun(Cs, As), Var = cauder_utils:temp_variable(Line), Bs = cauder_bindings:merge(Bs1, Bs2), Entry = #s_function{mfa = {M, F, A}, env = Bs, expr = Body, var = Var}, @@ -408,33 +402,34 @@ expr(Bs0, E = {apply_fun, Line, Fun, As}, Stk0) -> #result{env = Bs0, expr = [Var], stack = Stk} end end; -expr(Bs0, E = {match, _, Lhs, Rhs}, Stk) -> +expr(E = {match, _, Lhs, Rhs}, Bs0, Stk) -> case is_reducible(Lhs, Bs0) of true -> - eval_and_update({Bs0, Lhs, Stk}, {3, E}); + eval_and_update(3, E, Bs0, Stk); false -> case is_reducible(Rhs, Bs0) of true -> - eval_and_update({Bs0, Rhs, Stk}, {4, E}); + eval_and_update(4, E, Bs0, Stk); false -> - case match(Bs0, [Lhs], [Rhs]) of + Val = cauder_syntax:concrete(Rhs), + case match(Lhs, Val, Bs0) of {match, Bs} -> #result{env = Bs, expr = [Rhs], stack = Stk}; - nomatch -> error({badmatch, concrete(Rhs)}) + nomatch -> error({badmatch, Val}) end end end; -expr(Bs, E = {op, Line, Op, As}, Stk) -> +expr(E = {op, Line, Op, As}, Bs, Stk) -> case is_reducible(As, Bs) of true -> - eval_and_update({Bs, As, Stk}, {4, E}); + eval_and_update(4, E, Bs, Stk); false -> - Value = apply(erlang, Op, lists:map(fun concrete/1, As)), + Value = apply(erlang, Op, lists:map(fun cauder_syntax:concrete/1, As)), #result{env = Bs, expr = [{value, Line, Value}], stack = Stk} end; -expr(Bs, E = {'andalso', Line, Lhs, Rhs}, Stk) -> +expr(E = {'andalso', Line, Lhs, Rhs}, Bs, Stk) -> case is_reducible(Lhs, Bs) of true -> - eval_and_update({Bs, Lhs, Stk}, {3, E}); + eval_and_update(3, E, Bs, Stk); false -> case Lhs of {value, _, false} -> @@ -442,17 +437,17 @@ expr(Bs, E = {'andalso', Line, Lhs, Rhs}, Stk) -> {value, _, true} -> case is_reducible(Rhs, Bs) of true -> - eval_and_update({Bs, Rhs, Stk}, {4, E}); + eval_and_update(4, E, Bs, Stk); false -> - Value = apply(erlang, 'and', [concrete(Lhs), concrete(Rhs)]), + Value = apply(erlang, 'and', [cauder_syntax:concrete(Lhs), cauder_syntax:concrete(Rhs)]), #result{env = Bs, expr = [{value, Line, Value}], stack = Stk} end end end; -expr(Bs, E = {'orelse', Line, Lhs, Rhs}, Stk) -> +expr(E = {'orelse', Line, Lhs, Rhs}, Bs, Stk) -> case is_reducible(Lhs, Bs) of true -> - eval_and_update({Bs, Lhs, Stk}, {3, E}); + eval_and_update(3, E, Bs, Stk); false -> case Lhs of {value, _, true} -> @@ -460,15 +455,40 @@ expr(Bs, E = {'orelse', Line, Lhs, Rhs}, Stk) -> {value, _, false} -> case is_reducible(Rhs, Bs) of true -> - eval_and_update({Bs, Rhs, Stk}, {4, E}); + eval_and_update(4, E, Bs, Stk); false -> - Value = apply(erlang, 'or', [concrete(Lhs), concrete(Rhs)]), + Value = apply(erlang, 'or', [cauder_syntax:concrete(Lhs), cauder_syntax:concrete(Rhs)]), #result{env = Bs, expr = [{value, Line, Value}], stack = Stk} end end end. -eval_remote_call(M, F, As, Stk0, Line, Bs0) -> +%%------------------------------------------------------------------------------ +%% @doc Evaluates the first reducible expression from the given list. +%% +%% Evaluates the first reducible expression from the given list of expressions, +%% given an environment and a call stack, then returns a record with the updated +%% information and a label indicating the type of step performed. +%% +%% @see is_reducible/2 + +-spec expr_list(ExpressionList, Bindings, Stack) -> Result when + ExpressionList :: expression_list(), + Bindings :: cauder_bindings:bindings(), + Stack :: cauder_stack:stack(), + Result :: cauder_eval:result(). + +expr_list([E | Es], Bs, Stk) -> + case is_reducible(E, Bs) of + true -> + R = #result{expr = Es1} = expr(E, Bs, Stk), + R#result{expr = Es1 ++ Es}; + false -> + R = #result{expr = Es1} = expr_list(Es, Bs, Stk), + R#result{expr = [E | Es1]} + end. + +remote_call(M, F, As, Stk0, Line, Bs0) -> A = length(As), case cauder_utils:fundef_lookup({M, F, A}) of {Exported, Cs} -> @@ -478,64 +498,83 @@ eval_remote_call(M, F, As, Stk0, Line, Bs0) -> {ok, _} -> true = Exported; error when Stk0 =:= [] -> ok end, - {match, Bs, Body} = match_fun(Cs, As), + {Body, Bs} = match_fun(Cs, As), Var = cauder_utils:temp_variable(Line), Entry = #s_function{mfa = {M, F, A}, env = Bs, expr = Body, var = Var}, Stk = cauder_stack:push(Entry, Stk0), #result{env = Bs0, expr = [Var], stack = Stk}; error -> - Value = apply(M, F, lists:map(fun concrete/1, As)), + Value = apply(M, F, lists:map(fun cauder_syntax:concrete/1, As)), #result{env = Bs0, expr = [{value, Line, Value}], stack = Stk0} end. %%%============================================================================= --spec match_if(Bindings, Clauses) -> {match, Body} | nomatch when +-spec if_clauses(Clauses, Bindings) -> Body when + Clauses :: clauses(), Bindings :: cauder_bindings:bindings(), - Clauses :: cauder_syntax:af_clause_seq(), Body :: cauder_syntax:af_body(). -match_if(_, []) -> - nomatch; -match_if(Bs, [{'clause', _, [], G, B} | Cs]) -> - case concrete(eval_guard_seq(Bs, G)) of - true -> {match, B}; - false -> match_if(Bs, Cs) +if_clauses([{'clause', _, [], G, B} | Cs], Bs) -> + case guard_seq(G, Bs) of + true -> + B; + false -> + if_clauses(Cs, Bs) + end; +if_clauses([], _Bs) -> + erlang:error('if_clause'). + +-spec case_clauses(Clauses, Value, Bindings) -> {Body, ScopeBindings} | nomatch when + Clauses :: clauses(), + Value :: term(), + Bindings :: cauder_bindings:bindings(), + Body :: cauder_syntax:af_body(), + ScopeBindings :: cauder_bindings:bindings(). + +case_clauses(Cs, Val, Bs) -> + case match_clause(Cs, [Val], Bs) of + {B, Bs1} -> + {B, Bs1}; + nomatch -> + erlang:error({'case_clause', Val}) end. --spec match_case(Bindings, Clauses, Argument) -> {match, ScopeBindings, Body} | nomatch when +-spec receive_clauses(Clauses, Message, Bindings) -> {Body, NewBindings} | nomatch when + Clauses :: clauses(), + Message :: cauder_message:message(), Bindings :: cauder_bindings:bindings(), - Clauses :: cauder_syntax:af_clause_seq(), - Argument :: cauder_syntax:af_literal(), - ScopeBindings :: cauder_bindings:bindings(), - Body :: cauder_syntax:af_body(). + Body :: cauder_syntax:af_body(), + NewBindings :: cauder_bindings:bindings(). -match_case(Bs, Cs, V) -> match_clause(Bs, Cs, [V]). +receive_clauses(Cs, #message{val = Val}, Bs) -> + match_clause(Cs, [Val], Bs). --spec match_fun(Clauses, Arguments) -> {match, ScopeBindings, Body} | nomatch when - Clauses :: cauder_syntax:af_clause_seq(), - Arguments :: [cauder_syntax:af_literal()], - ScopeBindings :: cauder_bindings:bindings(), - Body :: cauder_syntax:af_body(). +-spec match_fun(Clauses, ValueList) -> {Body, ScopeBindings} | nomatch when + Clauses :: clauses(), + ValueList :: [term()], + Body :: cauder_syntax:af_body(), + ScopeBindings :: cauder_bindings:bindings(). -match_fun(Cs, Vs) -> match_clause(cauder_bindings:new(), Cs, Vs). +match_fun(Cs, Vals) -> + match_clause(Cs, Vals, cauder_bindings:new()). --spec match_rec_pid(Clauses, Bindings, RecipientPid, Mail, Sched, Sys) -> - {NewBindings, Body, {Message, QueuePosition}, NewMail} | nomatch +-spec match_receive_pid(Clauses, RecipientPid, Mail, Bindings, Sched, Sys) -> + {Body, {Message, QueuePosition}, NewMail, NewBindings} | nomatch when - Clauses :: cauder_syntax:af_clause_seq(), - Bindings :: cauder_bindings:bindings(), + Clauses :: clauses(), RecipientPid :: cauder_process:id(), Mail :: cauder_mailbox:mailbox(), + Bindings :: cauder_bindings:bindings(), Sched :: cauder_message:scheduler(), Sys :: cauder_system:system(), - NewBindings :: cauder_bindings:bindings(), Body :: cauder_syntax:af_body(), Message :: cauder_message:message(), QueuePosition :: pos_integer(), - NewMail :: cauder_mailbox:mailbox(). + NewMail :: cauder_mailbox:mailbox(), + NewBindings :: cauder_bindings:bindings(). -match_rec_pid(Cs, Bs, Pid, Mail, Sched, Sys) -> +match_receive_pid(Cs, Pid, Mail, Bs, Sched, Sys) -> case cauder_mailbox:find_destination(Pid, Mail) of [] -> nomatch; @@ -544,8 +583,8 @@ match_rec_pid(Cs, Bs, Pid, Mail, Sched, Sys) -> ?SCHEDULER_Manual -> FoldQueue = fun(Msg, Map1) -> - case match_rec(Cs, Bs, #message{uid = Uid} = Msg) of - {match, Bs1, Body} -> maps:put(Uid, {Bs1, Body, Msg}, Map1); + case receive_clauses(Cs, #message{uid = Uid} = Msg, Bs) of + {Body, Bs1} -> maps:put(Uid, {Bs1, Body, Msg}, Map1); nomatch -> skip end end, @@ -562,7 +601,7 @@ match_rec_pid(Cs, Bs, Pid, Mail, Sched, Sys) -> cauder:resume_task(), {Bs1, Body, Msg} = maps:get(Uid, MatchingBranchesMap), {QPos, NewMail} = cauder_mailbox:remove(Msg, Mail), - {Bs1, Body, {Msg, QPos}, NewMail}; + {Body, {Msg, QPos}, NewMail, Bs1}; % TODO Use suspend time {_SuspendTime, cancel} -> throw(cancel) @@ -572,8 +611,8 @@ match_rec_pid(Cs, Bs, Pid, Mail, Sched, Sys) -> MatchingBranches = lists:filtermap( fun(Queue) -> {value, Msg} = queue:peek(Queue), - case match_rec(Cs, Bs, Msg) of - {match, Bs1, Body} -> {true, {Bs1, Body, Msg}}; + case receive_clauses(Cs, Msg, Bs) of + {Body, Bs1} -> {true, {Bs1, Body, Msg}}; nomatch -> false end end, @@ -585,209 +624,181 @@ match_rec_pid(Cs, Bs, Pid, Mail, Sched, Sys) -> Length -> {Bs1, Body, Msg} = lists:nth(rand:uniform(Length), MatchingBranches), {QPos, NewMail} = cauder_mailbox:remove(Msg, Mail), - {Bs1, Body, {Msg, QPos}, NewMail} + {Body, {Msg, QPos}, NewMail, Bs1} end end end. --spec match_rec(Clauses, Bindings, Message) -> {match, NewBindings, Body} | nomatch when - Clauses :: cauder_syntax:af_clause_seq(), - Bindings :: cauder_bindings:bindings(), - Message :: cauder_message:message(), - NewBindings :: cauder_bindings:bindings(), - Body :: cauder_syntax:af_body(). - -match_rec(Cs, Bs0, #message{val = Value}) -> match_clause(Bs0, Cs, [abstract(Value)]). - --spec match_rec_uid(Clauses, Bindings, Uid, Mail) -> - {NewBindings, Body, {Message, QueuePosition}, NewMail} | nomatch +-spec match_receive_uid(Clauses, Uid, Mail, Bindings) -> + {Body, {Message, QueuePosition}, NewMail, NewBindings} | nomatch when - Clauses :: cauder_syntax:af_clause_seq(), - Bindings :: cauder_bindings:bindings(), + Clauses :: clauses(), Uid :: cauder_message:uid(), Mail :: cauder_mailbox:mailbox(), - NewBindings :: cauder_bindings:bindings(), + Bindings :: cauder_bindings:bindings(), Body :: cauder_syntax:af_body(), Message :: cauder_message:message(), QueuePosition :: pos_integer(), - NewMail :: cauder_mailbox:mailbox(). + NewMail :: cauder_mailbox:mailbox(), + NewBindings :: cauder_bindings:bindings(). -match_rec_uid(Cs, Bs0, Uid, Mail0) -> +match_receive_uid(Cs, Uid, Mail0, Bs0) -> case cauder_mailbox:take(Uid, Mail0) of error -> nomatch; {{Msg, QPos}, Mail1} -> - case match_clause(Bs0, Cs, [abstract(Msg#message.val)]) of - {match, Bs, Body} -> {Bs, Body, {Msg, QPos}, Mail1}; + case match_clause(Cs, [Msg#message.val], Bs0) of + {Body, Bs} -> {Body, {Msg, QPos}, Mail1, Bs}; nomatch -> nomatch end end. --spec match_clause(Bindings, Clauses, Arguments) -> {match, ScopeBindings, Body} | nomatch when +-spec match_clause(Clauses, ValueList, Bindings) -> {Body, ScopeBindings} | nomatch when + Clauses :: clauses(), + ValueList :: [term()], Bindings :: cauder_bindings:bindings(), - Clauses :: cauder_syntax:af_clause_seq(), - Arguments :: [cauder_syntax:af_literal()], - ScopeBindings :: cauder_bindings:bindings(), - Body :: cauder_syntax:af_body(). - -match_clause(_, [], _) -> - nomatch; -match_clause(Bs0, [{'clause', _, Ps, G, B} | Cs], Vs) -> - case match(Bs0, Ps, Vs) of - {match, Bs} -> - case concrete(eval_guard_seq(Bs, G)) of - true -> {match, Bs, B}; - false -> match_clause(Bs0, Cs, Vs) - end; - nomatch -> - match_clause(Bs0, Cs, Vs) - end. - --spec clause_line(Bindings, Clauses, Arguments) -> Line when - Bindings :: cauder_bindings:bindings(), - Clauses :: cauder_syntax:af_clause_seq(), - Arguments :: [cauder_syntax:af_literal()], - Line :: non_neg_integer(). - -clause_line(_, [], _) -> - -1; -clause_line(Bs0, [{'clause', Line, Ps, G, _} | Cs], Vs) -> - case match(Bs0, Ps, Vs) of - {match, Bs} -> - case concrete(eval_guard_seq(Bs, G)) of - true -> Line; - false -> clause_line(Bs0, Cs, Vs) + Body :: cauder_syntax:af_body(), + ScopeBindings :: cauder_bindings:bindings(). + +match_clause([{'clause', _, H, G, B} | Cs], Vals, Bs) -> + case match_list(H, Vals, Bs) of + {match, Bs1} -> + case guard_seq(G, Bs1) of + true -> {B, Bs1}; + false -> match_clause(Cs, Vals, Bs) end; nomatch -> - clause_line(Bs0, Cs, Vs) - end. + match_clause(Cs, Vals, Bs) + end; +match_clause([], _, _) -> + nomatch. %% Tries to match a list of values against a list of patterns using the given environment. %% The list of values should have no variables. --spec match(Bindings, Patterns, Arguments) -> {match, NewBindings} | nomatch when +-spec match(Pattern, Term, Bindings) -> {match, NewBindings} | nomatch when + Pattern :: cauder_syntax:af_pattern(), + Term :: term(), Bindings :: cauder_bindings:bindings(), - Patterns :: [cauder_syntax:af_pattern()], - Arguments :: [cauder_syntax:af_literal()], NewBindings :: cauder_bindings:bindings(). -match(Bs, [], []) -> - {match, Bs}; -match(Bs0, [Pat | Ps0], [{value, _, Val} | Vs0]) when length(Ps0) == length(Vs0) -> - case catch match1(Pat, Val, Bs0) of - {match, Bs} -> match(Bs, Ps0, Vs0); - nomatch -> nomatch - end; -match(_Bs, _Ps, _Vs) -> - nomatch. +match(Pat, Term, Bs0) -> + case catch match1(Pat, Term, Bs0) of + invalid -> + % TODO Convert pattern to term + erlang:error({illegal_pattern, Pat}); + Other -> + Other + end. -% TODO Organize arguments to be consistent --spec match1(Pattern, Term, Bindings) -> {match, NewBindings} | no_return() when +-spec match1(Pattern, Term, Bindings) -> {match, NewBindings} when Pattern :: cauder_syntax:af_pattern(), Term :: term(), Bindings :: cauder_bindings:bindings(), NewBindings :: cauder_bindings:bindings(). -match1({value, _, V}, V, Bs) -> - {match, Bs}; +match1({value, _, T0}, T, Bs) -> + case T of + T0 -> {match, Bs}; + _ -> throw(nomatch) + end; match1({var, _, '_'}, _, Bs) -> {match, Bs}; -match1({var, _, Name}, Value, Bs0) -> - case cauder_bindings:find(Name, Bs0) of - {ok, Value} -> - {match, Bs0}; +match1({var, _, Name}, Term, Bs) -> + case cauder_bindings:find(Name, Bs) of + {ok, Term} -> + {match, Bs}; {ok, _} -> throw(nomatch); error -> - Bs1 = cauder_bindings:add(Name, Value, Bs0), - {match, Bs1} + {match, cauder_bindings:add(Name, Term, Bs)} end; -match1({match, _, Pat1, Pat2}, Term, Bs0) -> - {match, Bs1} = match1(Pat1, Term, Bs0), +match1({match, _, Pat1, Pat2}, Term, Bs) -> + {match, Bs1} = match1(Pat1, Term, Bs), match1(Pat2, Term, Bs1); -match1({cons, _, H, T}, [H1 | T1], Bs0) -> - {match, Bs} = match1(H, H1, Bs0), - match1(T, T1, Bs); +match1({cons, _, H, T}, [H1 | T1], Bs) -> + {match, Bs1} = match1(H, H1, Bs), + match1(T, T1, Bs1); +match1({cons, _, _, _}, _, _Bs) -> + throw(nomatch); match1({tuple, _, Es}, Tuple, Bs) when length(Es) =:= tuple_size(Tuple) -> match_tuple(Es, Tuple, 1, Bs); match1(_, _, _) -> - throw(nomatch). + throw(invalid). --spec match_tuple(Values, Tuple, Index, Bindings) -> {match, NewBindings} | no_return() when - Values :: [cauder_syntax:af_literal()], +-spec match_tuple(Elements, Tuple, Index, Bindings) -> {match, NewBindings} | no_return() when + Elements :: [cauder_syntax:af_pattern()], Tuple :: tuple(), Index :: pos_integer(), Bindings :: cauder_bindings:bindings(), NewBindings :: cauder_bindings:bindings(). -match_tuple([], _, _, Bs) -> - {match, Bs}; match_tuple([E | Es], Tuple, I, Bs0) -> {match, Bs} = match1(E, element(I, Tuple), Bs0), - match_tuple(Es, Tuple, I + 1, Bs). + match_tuple(Es, Tuple, I + 1, Bs); +match_tuple([], _, _, Bs) -> + {match, Bs}. --spec eval_guard_seq(Bindings, Guards) -> Boolean when +-spec match_list(PatternList, TermList, Bindings) -> {match, NewBindings} | nomatch when + PatternList :: [cauder_syntax:af_pattern()], + TermList :: [term()], Bindings :: cauder_bindings:bindings(), + NewBindings :: cauder_bindings:bindings(). + +match_list([P | Ps], [T | Ts], Bs) -> + case match(P, T, Bs) of + {match, Bs1} -> + match_list(Ps, Ts, Bs1); + nomatch -> + nomatch + end; +match_list([], [], Bs) -> + {match, Bs}; +match_list(_Ps, _Ts, _Bs) -> + nomatch. + +%%%============================================================================= + +-spec guard_seq(Guards, Bindings) -> boolean() when Guards :: cauder_syntax:af_guard_seq(), - Boolean :: cauder_syntax:af_boolean(). + Bindings :: cauder_bindings:bindings(). -eval_guard_seq(_, []) -> - abstract(true); -eval_guard_seq(Bs, Gs) when is_list(Gs) -> +guard_seq([], _) -> + true; +guard_seq(Gs, Bs) when is_list(Gs) -> % In a guard sequence, guards are evaluated until one is true. The remaining guards, if any, are not evaluated. % See: https://erlang.org/doc/reference_manual/expressions.html#guard-sequences - abstract(lists:any(fun(G) -> concrete(eval_guard(Bs, G)) end, Gs)). + lists:any(fun(G) -> guard(G, Bs) end, Gs). --spec eval_guard(Bindings, Guard) -> Boolean when - Bindings :: cauder_bindings:bindings(), +-spec guard(Guard, Bindings) -> boolean() when Guard :: cauder_syntax:af_guard(), - Boolean :: cauder_syntax:af_boolean(). + Bindings :: cauder_bindings:bindings(). -eval_guard(Bs, G) when is_list(G) -> - abstract(lists:all(fun(Gt) -> concrete(eval_guard_test(Bs, Gt)) end, G)). +guard(G, Bs) when is_list(G) -> + lists:all(fun(Gt) -> guard_test(Gt, Bs) end, G). --spec eval_guard_test(Bindings, GuardTest) -> GuardTest | Boolean when - Bindings :: cauder_bindings:bindings(), +-spec guard_test(GuardTest, Bindings) -> boolean() when GuardTest :: cauder_syntax:af_guard_test(), - Boolean :: cauder_syntax:af_boolean(). + Bindings :: cauder_bindings:bindings(). -eval_guard_test(Bs, Gt) -> +guard_test(Gt, Bs) -> + % TODO erl_lint:is_guard_test/1 case is_reducible(Gt, Bs) of true -> - #result{expr = [Gt1]} = expr(Bs, Gt, cauder_stack:new()), - eval_guard_test(Bs, Gt1); + #result{expr = [Gt1]} = expr(Gt, Bs, cauder_stack:new()), + guard_test(Gt1, Bs); false -> - Gt + cauder_syntax:concrete(Gt) end. %%%============================================================================= -%%------------------------------------------------------------------------------ -%% @doc Converts the given Erlang term into its abstract form. - --spec abstract(Term) -> Literal when - Term :: term(), - Literal :: cauder_syntax:af_literal(). - -abstract(Value) -> {value, 0, Value}. - -%%------------------------------------------------------------------------------ -%% @doc Converts the given abstract literal element into the Erlang term that it -%% represents. - --spec concrete(Literal) -> Term when - Literal :: cauder_syntax:af_literal(), - Term :: term(). - -concrete({value, _, Value}) -> Value; -concrete({cons, _, {value, _, H}, {value, _, T}}) -> [H | T]. - %%------------------------------------------------------------------------------ %% @doc Checks if the given abstract expression (or list of expressions) can be %% reduced any further or not, given an environment. -spec is_reducible(Expression | [Expression], Bindings) -> IsReducible when - Expression :: cauder_syntax:abstract_expr(), + Expression :: expression(), Bindings :: cauder_bindings:bindings(), IsReducible :: boolean(). @@ -812,7 +823,7 @@ is_reducible(E, _) when is_tuple(E) -> %% @doc Checks if the given abstract expression is a literal value. -spec is_value(Expression | [Expression]) -> IsValue when - Expression :: cauder_syntax:abstract_expr(), + Expression :: expression(), IsValue :: boolean(). is_value([]) -> true; @@ -822,17 +833,21 @@ is_value({cons, _, H, T}) -> is_value(H) andalso is_value(T); is_value({tuple, _, Es}) -> is_value(Es); is_value(E) when is_tuple(E) -> false. --spec eval_and_update({Bindings, Expression | [Expression], Stack}, {Index, Tuple}) -> Result when +%%%============================================================================= + +-spec eval_and_update(Index, Expression, Bindings, Stack) -> Result when + Index :: pos_integer(), + Expression :: expression(), Bindings :: cauder_bindings:bindings(), - Expression :: cauder_syntax:abstract_expr(), Stack :: cauder_stack:stack(), - Index :: pos_integer(), - Tuple :: tuple(), Result :: cauder_eval:result(). -eval_and_update({Bs, Es, Stk}, {Index, Tuple}) when is_list(Es) -> - R = #result{expr = Es1} = eval_list(Bs, Es, Stk), - R#result{expr = [setelement(Index, Tuple, Es1)]}; -eval_and_update({Bs, E, Stk}, {Index, Tuple}) -> - R = #result{expr = [E1]} = expr(Bs, E, Stk), - R#result{expr = [setelement(Index, Tuple, E1)]}. +eval_and_update(N, Expr, Bs, Stk) -> + case element(N, Expr) of + Es when is_list(Es) -> + R = #result{expr = Es1} = expr_list(Es, Bs, Stk), + R#result{expr = [setelement(N, Expr, Es1)]}; + E -> + R = #result{expr = [E1]} = expr(E, Bs, Stk), + R#result{expr = [setelement(N, Expr, E1)]} + end. diff --git a/src/cauder_semantics_forwards.erl b/src/cauder_semantics_forwards.erl index 5aaad74..c79a32f 100644 --- a/src/cauder_semantics_forwards.erl +++ b/src/cauder_semantics_forwards.erl @@ -39,7 +39,7 @@ step(Pid, Sys, Sched, Mode) -> #process{stack = Stk0, env = Bs0, expr = Es0} = cauder_pool:get(Pid, Sys#system.pool), - Result = cauder_eval:seq(Bs0, Es0, Stk0), + Result = cauder_eval:exprs(Es0, Bs0, Stk0), case Result#result.label of #label_tau{} -> rule_local(Pid, Result, Sys); @@ -356,7 +356,7 @@ rule_spawn( node = LogEntry#log_spawn.node, pid = LogEntry#log_spawn.pid, mfa = {M, F, length(As)}, - expr = [cauder_syntax:remote_call(M, F, lists:map(fun cauder_eval:abstract/1, As))] + expr = [cauder_syntax:remote_call(M, F, lists:map(fun cauder_syntax:abstract/1, As))] } end, TraceAction = #trace_spawn{ @@ -439,11 +439,11 @@ rule_receive( Sched, #system{mail = Mail, pool = Pool, log = Log0} = Sys ) -> - {{Bs1, Es1, {Msg, QPos}, Mail1}, Log1} = + {{Es1, {Msg, QPos}, Mail1, Bs1}, Log1} = case Mode of normal -> - Match = cauder_eval:match_rec_pid(Cs, Bs, Pid, Mail, Sched, Sys), - {_, _, {#message{uid = Uid}, _}, _} = Match, + Match = cauder_eval:match_receive_pid(Cs, Pid, Mail, Bs, Sched, Sys), + {_, {#message{uid = Uid}, _}, _, _} = Match, %% If the chosen message is the same specified in the log don't invalidate the log Log = case cauder_log:pop_receive(Pid, Log0) of @@ -453,7 +453,7 @@ rule_receive( {Match, Log}; replay -> {#log_receive{uid = Uid}, Log} = cauder_log:pop_receive(Pid, Log0), - Match = cauder_eval:match_rec_uid(Cs, Bs, Uid, Mail), + Match = cauder_eval:match_receive_uid(Cs, Uid, Mail, Bs), {Match, Log} end, @@ -601,10 +601,10 @@ check_reducibility(Cs, Pid, #system{mail = Mail, log = Log} = Sys, Mode, 'receiv IsMatch = case Mode of normal -> - cauder_eval:match_rec_pid(Cs, Bs, Pid, Mail, ?SCHEDULER_Random, Sys) =/= nomatch; + cauder_eval:match_receive_pid(Cs, Pid, Mail, Bs, ?SCHEDULER_Random, Sys) =/= nomatch; replay -> {value, #log_receive{uid = Uid}} = cauder_log:peek(Pid, Log), - cauder_eval:match_rec_uid(Cs, Bs, Uid, Mail) =/= nomatch + cauder_eval:match_receive_uid(Cs, Uid, Mail, Bs) =/= nomatch end, case IsMatch of true -> diff --git a/src/cauder_syntax.erl b/src/cauder_syntax.erl index b2f8c21..42c6f76 100644 --- a/src/cauder_syntax.erl +++ b/src/cauder_syntax.erl @@ -10,6 +10,7 @@ %% API -export([clauses/1, expr_list/1]). +-export([abstract/1, concrete/1]). -export([replace_variable/3]). -export([to_abstract_expr/1]). -export([remote_call/3]). @@ -571,6 +572,28 @@ exception(Class, Reason) -> %%%============================================================================= +%%------------------------------------------------------------------------------ +%% @doc Converts the given Erlang term into its abstract form. + +-spec abstract(Term) -> Literal when + Term :: term(), + Literal :: cauder_syntax:af_literal(). + +abstract(Value) -> {value, 0, Value}. + +%%------------------------------------------------------------------------------ +%% @doc Converts the given abstract literal element into the Erlang term that it +%% represents. + +-spec concrete(Literal) -> Term when + Literal :: cauder_syntax:af_literal(), + Term :: term(). + +concrete({value, _, Value}) -> Value; +concrete({cons, _, {value, _, H}, {value, _, T}}) -> [H | T]. + +%%%============================================================================= + %%------------------------------------------------------------------------------ %% @doc Replaces all occurrences of the given `Variable' in each one of the %% `Expressions' with the given literal `Value'. @@ -601,14 +624,14 @@ replace_variable({cons, Line, H0, T0}, Var, Val) -> H = replace_variable(H0, Var, Val), T = replace_variable(T0, Var, Val), case cauder_eval:is_value(H) andalso cauder_eval:is_value(T) of - true -> {value, Line, [cauder_eval:concrete(H) | cauder_eval:concrete(T)]}; + true -> {value, Line, [cauder_syntax:concrete(H) | cauder_syntax:concrete(T)]}; false -> {cons, Line, H, T} end; replace_variable({tuple, Line, Es0}, Var, Val) -> Es = replace_variable(Es0, Var, Val), case cauder_eval:is_value(Es) of true -> - Tuple = list_to_tuple(lists:map(fun cauder_eval:concrete/1, Es)), + Tuple = list_to_tuple(lists:map(fun cauder_syntax:concrete/1, Es)), {value, Line, Tuple}; false -> {tuple, Line, Es} @@ -845,6 +868,5 @@ set_line(Node, Line) -> erl_syntax:set_pos(Node, erl_anno:new(Line)). remote_call(M, F, As) -> A = length(As), - {_, Cs} = cauder_utils:fundef_lookup({M, F, A}), - Line = cauder_eval:clause_line(cauder_bindings:new(), Cs, As), + {_, [{'clause', Line, _Ps, _G, _B} | _]} = cauder_utils:fundef_lookup({M, F, A}), {remote_call, Line, M, F, lists:map(fun(V) -> setelement(2, V, Line) end, As)}.