%% @doc Code generation functions.
|
-module(glc_code).
|
-compile({nowarn_unused_function, {abstract_module,2}}).
|
-compile({nowarn_unused_function, {abstract_tables,1}}).
|
-compile({nowarn_unused_function, {abstract_reset,0}}).
|
-compile({nowarn_unused_function, {abstract_filter,3}}).
|
-compile({nowarn_unused_function, {abstract_filter_,4}}).
|
-compile({nowarn_unused_function, {abstract_opfilter,6}}).
|
-compile({nowarn_unused_function, {abstract_all,4}}).
|
-compile({nowarn_unused_function, {abstract_any,4}}).
|
-compile({nowarn_unused_function, {abstract_with,3}}).
|
-compile({nowarn_unused_function, {abstract_within,3}}).
|
-compile({nowarn_unused_function, {abstract_getkey,4}}).
|
-compile({nowarn_unused_function, {abstract_getkey_,4}}).
|
-compile({nowarn_unused_function, {abstract_getparam,3}}).
|
-compile({nowarn_unused_function, {param_variable,1}}).
|
-compile({nowarn_unused_function, {field_variable,1}}).
|
-compile({nowarn_unused_function, {field_variable_,1}}).
|
-compile({nowarn_unused_function, {compile_forms,2}}).
|
-compile({nowarn_unused_function, {load_binary,2}}).
|
|
|
-export([
|
compile/2
|
]).
|
|
-define(erl, erl_syntax).
|
|
-record(module, {
|
'query' :: term(),
|
tables :: [{atom(), atom()}],
|
qtree :: term(),
|
store :: term()
|
}).
|
|
-type syntaxTree() :: erl_syntax:syntaxTree().
|
|
-record(state, {
|
event = undefined :: syntaxTree(),
|
fields = [] :: [{atom(), syntaxTree()}],
|
fieldc = 0 :: non_neg_integer(),
|
paramvars = [] :: [{term(), syntaxTree()}],
|
paramstab = undefined :: atom(),
|
countstab = undefined :: atom()
|
}).
|
|
-type nextFun() :: fun((#state{}) -> [syntaxTree()]).
|
|
compile(Module, ModuleData) ->
|
{ok, forms, Forms} = abstract_module(Module, ModuleData),
|
{ok, Module, Binary} = compile_forms(Forms, [nowarn_unused_vars]),
|
{ok, loaded, Module} = load_binary(Module, Binary),
|
{ok, Module}.
|
|
%% abstract code generation functions
|
|
%% @private Generate an abstract dispatch module.
|
-spec abstract_module(atom(), #module{}) -> {ok, forms, list()}.
|
abstract_module(Module, Data) ->
|
Forms = [?erl:revert(E) || E <- abstract_module_(Module, Data)],
|
case lists:keyfind(errors, 1, erl_syntax_lib:analyze_forms(Forms)) of
|
false -> {ok, forms, Forms};
|
{_, []} -> {ok, forms, Forms};
|
{_, [_|_]}=Errors -> Errors
|
end.
|
|
%% @private Generate an abstract dispatch module.
|
-spec abstract_module_(atom(), #module{}) -> [?erl:syntaxTree()].
|
abstract_module_(Module, #module{tables=Tables,
|
qtree=Tree, store=Store}=Data) ->
|
{_, ParamsTable} = lists:keyfind(params, 1, Tables),
|
{_, CountsTable} = lists:keyfind(counters, 1, Tables),
|
AbstractMod = [
|
%% -module(Module)
|
?erl:attribute(?erl:atom(module), [?erl:atom(Module)]),
|
%% -export([
|
?erl:attribute(
|
?erl:atom(export),
|
[?erl:list([
|
%% get/1
|
?erl:arity_qualifier(
|
?erl:atom(get),
|
?erl:integer(1)),
|
%% info/1
|
?erl:arity_qualifier(
|
?erl:atom(info),
|
?erl:integer(1)),
|
%% reset_counters/1
|
?erl:arity_qualifier(
|
?erl:atom(reset_counters),
|
?erl:integer(1)),
|
%% table/1
|
?erl:arity_qualifier(
|
?erl:atom(table),
|
?erl:integer(1)),
|
?erl:arity_qualifier(
|
?erl:atom(runjob),
|
?erl:integer(2)),
|
%% handle/1
|
?erl:arity_qualifier(
|
?erl:atom(handle),
|
?erl:integer(1))])]),
|
%% ]).
|
%% get(Name) -> Term.
|
?erl:function(
|
?erl:atom(get),
|
abstract_get(Data) ++
|
[?erl:clause(
|
[?erl:underscore()], none,
|
[?erl:abstract({error, undefined})])]),
|
%% info(Name) -> Term.
|
?erl:function(
|
?erl:atom(info),
|
abstract_info(Data) ++
|
[?erl:clause(
|
[?erl:underscore()], none,
|
[abstract_apply(erlang, error, [?erl:atom(badarg)])])]),
|
%% reset_counters(Name) -> boolean().
|
?erl:function(
|
?erl:atom(reset_counters),
|
abstract_reset() ++
|
[?erl:clause(
|
[?erl:underscore()], none,
|
[abstract_apply(erlang, error, [?erl:atom(badarg)])])]),
|
%% table(Name) -> atom().
|
?erl:function(
|
?erl:atom(table),
|
abstract_tables(Tables) ++
|
[?erl:clause(
|
[?erl:underscore()], none,
|
[abstract_apply(erlang, error, [?erl:atom(badarg)])])]),
|
%% handle(Event) - entry function
|
?erl:function(
|
?erl:atom(handle),
|
[?erl:clause([?erl:variable("Event")], none,
|
[abstract_count(input),
|
?erl:application(none,
|
?erl:atom(handle_), [?erl:variable("Event")])])]),
|
?erl:function(
|
?erl:atom(runjob),
|
[?erl:clause([?erl:variable("Fun"), ?erl:variable("Event")], none,
|
[abstract_count(job_input),
|
?erl:application(none,
|
?erl:atom(job_), [?erl:variable("Fun"),
|
?erl:variable("Event")])])]),
|
%% input_(Node, App, Pid, Tags, Values) - filter roots
|
?erl:function(
|
?erl:atom(handle_),
|
[?erl:clause([?erl:variable("Event")], none,
|
abstract_filter(Tree, Data, #state{
|
event=?erl:variable("Event"),
|
paramstab=ParamsTable,
|
countstab=CountsTable}))]),
|
?erl:function(
|
?erl:atom(job_),
|
[?erl:clause([?erl:variable("Fun"),
|
?erl:variable("Meta")], none,
|
|
[?erl:application(none,
|
?erl:atom(job_result), [
|
?erl:catch_expr(
|
abstract_apply(glc_run, execute, [
|
?erl:variable("Fun"),
|
?erl:list([?erl:variable("Meta"),
|
?erl:abstract(Store)])
|
])),
|
?erl:variable("Meta")])
|
]
|
)]),
|
?erl:function(
|
?erl:atom(job_result),
|
abstract_runjob(Data)
|
)
|
],
|
%% Transform Term -> Key to Key -> Term
|
gr_param:transform(ParamsTable),
|
AbstractMod.
|
|
%% @private Return the clauses of the table/1 function.
|
abstract_tables(Tables) ->
|
[?erl:clause(
|
[?erl:abstract(K)], none,
|
[?erl:abstract(V)])
|
|| {K, V} <- Tables].
|
|
abstract_query_find(K, Store) ->
|
case lists:keyfind(K, 1, Store) of
|
{_, Val} ->
|
{ok, Val};
|
_ ->
|
{error, notfound}
|
end.
|
|
%% @private Return the original query as an expression.
|
abstract_query({with, Query, _}) ->
|
[?erl:abstract(Query)];
|
abstract_query([{with, _Query, _}|_] = I) ->
|
[?erl:abstract([Query || {with, Query, _} <- I])];
|
%[?erl:abstract(_Query)];
|
abstract_query({any, [{with, _Q, _A}|_] = I}) ->
|
Queries = glc_lib:reduce(glc:any([Q || {with, Q, _} <- I])),
|
[?erl:abstract(Queries)];
|
abstract_query({all, [{with, _Q, _A}|_] = I}) ->
|
Queries = glc_lib:reduce(glc:all([Q || {with, Q, _} <- I])),
|
[?erl:abstract(Queries)];
|
abstract_query(Query) ->
|
[?erl:abstract(Query)].
|
|
|
%% @private Return the clauses of the get/1 function.
|
abstract_get(#module{'query'=_Query, store=undefined}) ->
|
[];
|
abstract_get(#module{'query'=_Query, store=Store}) ->
|
[?erl:clause([?erl:abstract(K)], none,
|
abstract_query(abstract_query_find(K, Store)))
|
|| {K, _} <- Store].
|
|
%% @private
|
abstract_runjob(#module{'query'=_Query, store=_Store}) ->
|
Time = abstract_apply(erlang, '/', [?erl:variable("Time"),
|
?erl:abstract(1000000)]),
|
[?erl:clause([?erl:variable("JobResult"),
|
?erl:variable("Meta")], none,
|
[
|
?erl:case_expr(?erl:variable("JobResult"),
|
[
|
?erl:clause(
|
[?erl:tuple([?erl:variable("Time"), ?erl:variable("Result")])],
|
none,
|
[?erl:case_expr(?erl:variable("Result"),
|
[
|
?erl:clause(
|
[?erl:tuple([?erl:atom(error),?erl:variable("Reason")])],
|
none,
|
[abstract_count(input), abstract_count(job_error),
|
?erl:application(none, ?erl:atom(handle_),
|
abstract_job(Time, [?erl:tuple([?erl:atom(error),
|
?erl:variable("Reason")])])),
|
abstract_count(job_time, ?erl:variable("Time")),
|
?erl:tuple([?erl:variable("Time"),
|
?erl:tuple([?erl:atom(error),
|
?erl:variable("Reason")])])]),
|
|
?erl:clause(
|
[?erl:variable("Result")],
|
none,
|
[abstract_count(input), abstract_count(job_run),
|
?erl:application(none, ?erl:atom(handle_),
|
abstract_job(Time)),
|
abstract_count(job_time, ?erl:variable("Time")),
|
?erl:tuple([?erl:variable("Time"),
|
?erl:variable("Result")])])
|
])
|
])
|
])
|
]
|
)].
|
|
abstract_job(Time) ->
|
abstract_job(Time, []).
|
abstract_job(Time, Error) ->
|
Pairs = abstract_apply(gre, pairs, [?erl:variable("Meta")]),
|
Runtime = ?erl:list([?erl:tuple([?erl:atom(runtime), Time])]),
|
[abstract_apply(gre, make,
|
[abstract_apply(erlang, '++', [?erl:list(Error),
|
abstract_apply(erlang, '++', [Pairs, Runtime])]),
|
?erl:abstract([list])])].
|
|
%% @private Return the clauses of the info/1 function.
|
abstract_info(#module{'query'=Query}) ->
|
[?erl:clause([?erl:abstract(K)], none, V)
|
|| {K, V} <- [
|
{'query', abstract_query(Query)},
|
{input, abstract_getcount(input)},
|
{filter, abstract_getcount(filter)},
|
{output, abstract_getcount(output)},
|
{job_input, abstract_getcount(job_input)},
|
{job_run, abstract_getcount(job_run)},
|
{job_time, abstract_getcount(job_time)},
|
{job_error, abstract_getcount(job_error)}
|
]].
|
|
|
abstract_reset() ->
|
[?erl:clause([?erl:abstract(K)], none, V)
|
|| {K, V} <- [
|
{all, abstract_resetcount([input, filter, output,
|
job_input, job_run,
|
job_time, job_error])},
|
{input, abstract_resetcount(input)},
|
{filter, abstract_resetcount(filter)},
|
{output, abstract_resetcount(output)},
|
{job_input, abstract_resetcount(job_input)},
|
{job_run, abstract_resetcount(job_run)},
|
{job_time, abstract_resetcount(job_time)},
|
{job_error, abstract_resetcount(job_error)}
|
]].
|
|
|
%% @private Return a list of expressions to apply a filter.
|
%% @todo Allow mulitple functions to be specified using `with/2'.
|
-spec abstract_filter(glc_ops:op() | [glc_ops:op()], #module{}, #state{}) -> [syntaxTree()].
|
abstract_filter({Type, [{with, _Cond, _Fun}|_] = I}, Data, State) when Type =:= all; Type =:= any ->
|
Cond = glc_lib:reduce(glc:Type([Q || {with, Q, _} <- I])),
|
abstract_filter_(Cond,
|
_OnMatch=fun(State2) ->
|
Funs = [ F || {with, _, F} <- I ],
|
[abstract_count(output)] ++
|
abstract_with(Funs, Data, State2) end,
|
_OnNomatch=fun(_State2) -> [abstract_count(filter)] end, State);
|
abstract_filter([{with, _Cond, _Fun}|_] = I, Data, State) ->
|
OnNomatch = fun(_State2) -> [abstract_count(filter, 0)] end,
|
Funs = lists:foldr(fun({with, Cond, Fun}, Acc) ->
|
[{Cond, Fun, Data}|Acc]
|
end, [], I),
|
abstract_within(Funs, OnNomatch, State);
|
abstract_filter({with, Cond, Fun}, Data, State) ->
|
abstract_filter_(Cond,
|
_OnMatch=fun(State2) ->
|
[abstract_count(output)] ++
|
abstract_with(Fun, Data, State2) end,
|
_OnNomatch=fun(_State2) -> [abstract_count(filter)] end, State);
|
abstract_filter(Cond, _Data, State) ->
|
abstract_filter_(Cond,
|
_OnMatch=fun(_State2) -> [abstract_count(output)] end,
|
_OnNomatch=fun(_State2) -> [abstract_count(filter)] end, State).
|
|
%% @private Return a list of expressions to apply a filter.
|
%% A filter expects two continuation functions which generates the expressions
|
%% to apply when the filter matches or fails to match. The state passed to the
|
%% functions will contain all the variable bindings of previously accessed
|
%% fields and parameters.
|
-spec abstract_filter_(glc_ops:op(), nextFun(), nextFun(), #state{}) ->
|
syntaxTree().
|
abstract_filter_({null, true}, OnMatch, _OnNomatch, State) ->
|
OnMatch(State);
|
abstract_filter_({null, false}, _OnMatch, OnNomatch, State) ->
|
OnNomatch(State);
|
abstract_filter_({Key, '*'}, OnMatch, OnNomatch, State) ->
|
abstract_getkey(Key,
|
_OnMatch=fun(#state{}=State2) -> OnMatch(State2) end,
|
_OnNomatch=fun(State2) -> OnNomatch(State2) end, State);
|
abstract_filter_({Key, '!'}, OnMatch, OnNomatch, State) ->
|
abstract_getkey(Key,
|
_OnNomatch=fun(State2) -> OnNomatch(State2) end,
|
_OnMatch=fun(#state{}=State2) -> OnMatch(State2) end,
|
State);
|
abstract_filter_({Key, Op, Value}, OnMatch, OnNomatch, State)
|
when Op =:= '>'; Op =:= '='; Op =:= '!='; Op =:= '<';
|
Op =:= '>='; Op =:= '=<'; Op =:= '<=' ->
|
Op2 = case Op of
|
'=' -> '=:=';
|
'!=' -> '=/=';
|
'<=' -> '=<';
|
Op -> Op
|
end,
|
abstract_opfilter(Key, Op2, Value, OnMatch, OnNomatch, State);
|
abstract_filter_({'any', Conds}, OnMatch, OnNomatch, State) ->
|
abstract_any(Conds, OnMatch, OnNomatch, State);
|
abstract_filter_({'all', Conds}, OnMatch, OnNomatch, State) ->
|
abstract_all(Conds, OnMatch, OnNomatch, State).
|
|
%% @private Return a branch based on a built in operator.
|
-spec abstract_opfilter(atom(), atom(), term(), nextFun(),
|
nextFun(), #state{}) -> [syntaxTree()].
|
abstract_opfilter(Key, Opname, Value, OnMatch, OnNomatch, State) ->
|
abstract_getkey(Key,
|
_OnMatch=fun(#state{}=State2) ->
|
[?erl:case_expr(
|
abstract_apply(erlang, Opname, [
|
?erl:variable(field_variable(Key)),
|
?erl:abstract(Value)
|
]),
|
[?erl:clause([?erl:atom(true)], none,
|
OnMatch(State2)),
|
?erl:clause([?erl:atom(false)], none,
|
OnNomatch(State2))])] end,
|
_OnNomatch=fun(State2) -> OnNomatch(State2) end, State).
|
|
%% @private Generate an `all' filter.
|
%% An `all' filter is evaluated by testing all conditions that must hold. If
|
%% any of the conditions does not hold the evaluation is short circuted at that
|
%% point. This means that the `OnNomatch' branch is executed once for each
|
%% condition. The `OnMatch' branch is only executed once.
|
-spec abstract_all([glc_ops:op()], nextFun(), nextFun(), #state{}) ->
|
[syntaxTree()].
|
abstract_all([H|T], OnMatch, OnNomatch, State) ->
|
abstract_filter_(H,
|
_OnMatch=fun(State2) -> abstract_all(T, OnMatch, OnNomatch, State2)
|
end, OnNomatch, State);
|
abstract_all([], OnMatch, _OnNomatch, State) ->
|
OnMatch(State).
|
|
%% @private
|
-spec abstract_any([glc_ops:op()], nextFun(), nextFun(), #state{}) ->
|
[syntaxTree()].
|
abstract_any([H|T], OnMatch, OnNomatch, State) ->
|
abstract_filter_(H, OnMatch,
|
_OnNomatch=fun(State2) -> abstract_any(T, OnMatch, OnNomatch, State2)
|
end, State);
|
abstract_any([], _OnMatch, OnNomatch, State) ->
|
OnNomatch(State).
|
|
%% @private
|
-spec abstract_with(fun((gre:event()) -> term()),
|
#module{}, #state{}) -> [syntaxTree()].
|
abstract_with([Fun0|_] = Funs, Data, State)
|
when is_function(Fun0, 1); is_function(Fun0, 2) ->
|
abstract_getparam(Funs, fun(#state{event=Event, paramvars=Params}) ->
|
lists:map(fun(Fun) ->
|
{_, Fun2} = lists:keyfind(Fun, 1, Params),
|
abstract_with_({Fun, Fun2}, Event, Data)
|
end, Funs)
|
end, State);
|
abstract_with(Fun, Data, State) when is_function(Fun, 1); is_function(Fun, 2) ->
|
abstract_getparam(Fun, fun(#state{event=Event, paramvars=Params}) ->
|
{_, Fun2} = lists:keyfind(Fun, 1, Params),
|
[abstract_with_({Fun, Fun2}, Event, Data)]
|
end, State).
|
|
abstract_within([{H, Fun, Data}|T], OnNomatch, State) ->
|
OnMatch = fun(State2) -> [abstract_count(output)] ++
|
abstract_with(Fun, Data, State2)
|
++ abstract_within(T, OnNomatch, State2)
|
end,
|
abstract_filter_(H, OnMatch,
|
_OnNomatch=fun(State2) ->
|
[abstract_count(filter)] ++
|
abstract_within(T, OnNomatch, State2)
|
end, State);
|
abstract_within([], OnNomatch, State) ->
|
OnNomatch(State).
|
|
abstract_with_({Fun, Fun2}, Event, #module{store=Store}) ->
|
?erl:application(none, Fun2,
|
case Fun of
|
_ when is_function(Fun, 1) ->
|
[Event];
|
_ when is_function(Fun, 2) ->
|
[Event, ?erl:abstract(Store)]
|
end).
|
|
%% @private Bind the value of a field to a variable.
|
%% If the value of a field has already been bound to a variable the previous
|
%% binding is reused over re-accessing the value. The `OnMatch' function is
|
%% expected to access the variable stored in the state record. The `OnNomatch'
|
%% function must not attempt to access the variable.
|
-spec abstract_getkey(atom(), nextFun(), nextFun(), #state{}) ->
|
[syntaxTree()].
|
abstract_getkey(Key, OnMatch, OnNomatch, #state{fields=Fields}=State) ->
|
case lists:keyfind(Key, 1, Fields) of
|
{Key, _Variable} -> OnMatch(State);
|
false -> abstract_getkey_(Key, OnMatch, OnNomatch, State)
|
end.
|
|
|
-spec abstract_getkey_(atom(), nextFun(), nextFun(), #state{}) ->
|
[syntaxTree()].
|
abstract_getkey_(Key, OnMatch, OnNomatch, #state{
|
event=Event, fields=Fields}=State) ->
|
[?erl:case_expr(
|
abstract_apply(gre, find, [?erl:atom(Key), Event]),
|
[?erl:clause([
|
?erl:tuple([
|
?erl:atom(true),
|
?erl:variable(field_variable(Key))])], none,
|
OnMatch(State#state{
|
fields=[{Key, ?erl:variable(field_variable(Key))}
|
|Fields]})),
|
?erl:clause([
|
?erl:atom(false)], none,
|
OnNomatch(State))
|
]
|
)].
|
|
%% @private Bind the value of a parameter to a variable.
|
%% During code generation the parameter value is used as the identity of the
|
%% parameter. At runtime a unique integer is used as the identity.
|
-spec abstract_getparam(term(), nextFun(), #state{}) -> [syntaxTree()].
|
abstract_getparam([_|_]=Terms, OnBound, #state{paramvars=_Params, fields=_Fields,
|
paramstab=_ParamsTable}=State)
|
when is_list(Terms) ->
|
|
{Keys, Bound} = lists:foldl(fun(Term, {Acc0, #state{paramvars=Params,
|
paramstab=ParamsTable}=State0}) ->
|
case lists:keyfind(Term, 1, Params) of
|
{_, _Variable} ->
|
{Acc0, State0};
|
|
false ->
|
Key = abstract_getparam_key(Term, ParamsTable),
|
Lookup = abstract_apply(gr_param, lookup_element,
|
[abstract_apply(table, [?erl:atom(params)]),
|
?erl:abstract(Key)]),
|
Expr = ?erl:match_expr(param_variable(Key), Lookup),
|
State1 = State0#state{paramvars=[{Term, param_variable(Key)}|Params]},
|
{[Expr|Acc0], State1}
|
|
end
|
end, {[], State}, Terms),
|
Keys ++ OnBound(Bound);
|
abstract_getparam(Term, OnBound, #state{paramvars=Params}=State) ->
|
case lists:keyfind(Term, 1, Params) of
|
{_, _Variable} -> OnBound(State);
|
%% parameter not bound to variable in this scope.
|
false -> abstract_getparam([Term], OnBound, State)
|
end.
|
|
abstract_getparam_key(Term, ParamsTable) ->
|
case gr_param:lookup(ParamsTable, Term) of
|
[{_, Key2}] ->
|
Key2;
|
[] ->
|
Key2 = gr_param:info_size(ParamsTable),
|
gr_param:insert(ParamsTable, {Term, Key2}),
|
Key2
|
end.
|
|
%% @private Generate a variable name for the value of a field.
|
-spec field_variable(atom()) -> string().
|
field_variable(Key) ->
|
"Field_" ++ field_variable_(atom_to_list(Key)).
|
|
%% @private Escape non-alphanumeric values.
|
-spec field_variable_(string()) -> string().
|
field_variable_([H|T]) when H >= $0, H =< $9 ->
|
[H|field_variable_(T)];
|
field_variable_([H|T]) when H >= $A, H =< $Z ->
|
[H|field_variable_(T)];
|
field_variable_([H|T]) when H >= $a, H =< $z ->
|
[H|field_variable_(T)];
|
field_variable_([H|T]) ->
|
"_" ++ integer_to_list(H, 16) ++ "_" ++ field_variable_(T);
|
field_variable_([]) ->
|
[].
|
|
%% @private Generate a variable name for the value of a parameter.
|
-spec param_variable(integer()) -> syntaxTree().
|
param_variable(Key) ->
|
?erl:variable("Param_" ++ integer_to_list(Key)).
|
|
%% @ private Generate a list of field variable names.
|
%% Walk the query tree and generate a safe variable name string for each field
|
%% that is accessed by the conditions in the query. Only allow alpha-numeric.
|
%%-spec field_variables(glc_ops:op()) -> [{atom(), string()}].
|
%%field_variables(Query) ->
|
%% lists:usort(field_variables_(Query)).
|
|
%%-spec field_variables(glc_ops:op()) -> [{atom(), string()}].
|
%%field_variables_({Key, '=', _Term}) ->
|
%% [{Key, field_variable(Key)}].
|
|
|
|
%% @private Return an expression to increment a counter.
|
%% @todo Pass state record. Only Generate code if `statistics' is enabled.
|
-spec abstract_count(atom()) -> syntaxTree().
|
abstract_count(Counter) ->
|
abstract_count(Counter, 1).
|
abstract_count(Counter, Value) when is_integer(Value) ->
|
abstract_apply(gr_counter, update_counter,
|
[abstract_apply(table, [?erl:atom(counters)]),
|
?erl:abstract(Counter),
|
?erl:abstract({2,Value})]);
|
abstract_count(Counter, Value) ->
|
abstract_apply(gr_counter, update_counter,
|
[abstract_apply(table, [?erl:atom(counters)]),
|
?erl:abstract(Counter),
|
?erl:tuple([?erl:abstract(2), Value])
|
]).
|
|
|
%% @private Return an expression to get the value of a counter.
|
%% @todo Pass state record. Only Generate code if `statistics' is enabled.
|
-spec abstract_getcount(atom()) -> [syntaxTree()].
|
abstract_getcount(Counter) when is_atom(Counter) ->
|
abstract_getcount(?erl:abstract(Counter));
|
abstract_getcount(Counter) ->
|
[abstract_apply(gr_counter, lookup_element,
|
[abstract_apply(table, [?erl:atom(counters)]), Counter])].
|
|
%% @private Return an expression to reset a counter.
|
-spec abstract_resetcount(atom() | [filter | input | output |
|
job_input | job_run | job_time | job_error ])
|
-> [syntaxTree()].
|
abstract_resetcount(Counter) ->
|
[abstract_apply(gr_counter, reset_counters,
|
[abstract_apply(table, [?erl:atom(counters)]),
|
?erl:abstract(Counter)])].
|
|
|
|
%% abstract code util functions
|
|
|
%% @private Compile an abstract module.
|
-spec compile_forms(term(), [term()]) -> {ok, atom(), binary()}.
|
compile_forms(Forms, Opts) ->
|
case compile:forms(Forms, Opts) of
|
{ok, Module, Binary} ->
|
{ok, Module, Binary};
|
{ok, Module, Binary, _Warnings} ->
|
{ok, Module, Binary};
|
Error ->
|
erlang:error({compile_forms, Error})
|
end.
|
|
%% @private Load a module binary.
|
-spec load_binary(atom(), binary()) -> {ok, loaded, atom()}.
|
load_binary(Module, Binary) ->
|
case code:load_binary(Module, "", Binary) of
|
{module, Module} -> {ok, loaded, Module};
|
{error, Reason} -> exit({error_loading_module, Module, Reason})
|
end.
|
|
%% @private Apply an exported function.
|
-spec abstract_apply(atom(), atom(), [syntaxTree()]) -> syntaxTree().
|
abstract_apply(Module, Function, Arguments) ->
|
?erl:application(?erl:atom(Module), ?erl:atom(Function), Arguments).
|
|
%% @private Apply a module local function.
|
-spec abstract_apply(atom(), [syntaxTree()]) -> syntaxTree().
|
abstract_apply(Function, Arguments) ->
|
?erl:application(?erl:atom(Function), Arguments).
|