%% This file is a copy of erl_id_trans.erl from the R14B02 Erlang/OTP %% distribution, with modifications to make it implement Scheme %% Notation for Specializing Parameters without Currying %% (http://srfi.schemers.org/srfi-26/srfi-26.html). %% All modifications are (C) 2011-2013 VMware, Inc. %% %% ``The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved via the world wide web at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings %% AB. All Rights Reserved.'' %% -module(cut). -export([parse_transform/2]). parse_transform(Forms, _Options) -> %%io:format("Before:~n~p~n~n", [Forms]), put(var_count, 0), Forms1 = forms(Forms), %% io:format("After:~n~s~n~n", %% [erl_prettypr:format(erl_syntax:form_list(Forms1))]), Forms1. %% forms(Fs) -> lists:map(fun (F) -> form(F) end, Fs). forms([F0|Fs0]) -> F1 = form(F0), Fs1 = forms(Fs0), [F1|Fs1]; forms([]) -> []. %% -type form(Form) -> Form. form({attribute,Line,Attr,Val}) -> %The general attribute. {attribute,Line,Attr,Val}; form({function,Line,Name0,Arity0,Clauses0}) -> {Name,Arity,Clauses} = function(Name0, Arity0, Clauses0), {function,Line,Name,Arity,Clauses}; % Mnemosyne, ignore... form({rule,Line,Name,Arity,Body}) -> {rule,Line,Name,Arity,Body}; % Dont dig into this %% Extra forms from the parser. form({error,E}) -> {error,E}; form({warning,W}) -> {warning,W}; form({eof,Line}) -> {eof,Line}. %% -type function(atom(), integer(), [Clause]) -> {atom(),integer(),[Clause]}. function(Name, Arity, Clauses0) -> Clauses1 = clauses(Clauses0), {Name, Arity, Clauses1}. %% -type clauses([Clause]) -> [Clause]. clauses([C0|Cs]) -> C1 = clause(C0), [C1|clauses(Cs)]; clauses([]) -> []. %% -type clause(Clause) -> Clause. clause({clause, Line, Head, Guard, Body}) -> {clause, Line, Head, Guard, exprs(Body)}. %% -type pattern(Pattern) -> Pattern. %% N.B. Only valid patterns are included here. pattern({var,Line,V}) -> {var,Line,V}; pattern({match,Line,L0,R0}) -> L1 = pattern(L0), R1 = pattern(R0), {match,Line,L1,R1}; pattern({integer,Line,I}) -> {integer,Line,I}; pattern({char,Line,C}) -> {char,Line,C}; pattern({float,Line,F}) -> {float,Line,F}; pattern({atom,Line,A}) -> {atom,Line,A}; pattern({string,Line,S}) -> {string,Line,S}; pattern({nil,Line}) -> {nil,Line}; pattern({cons,Line,H0,T0}) -> H1 = pattern(H0), T1 = pattern(T0), {cons,Line,H1,T1}; pattern({tuple,Line,Ps0}) -> Ps1 = pattern_list(Ps0), {tuple,Line,Ps1}; %% OTP 17.0: EEP 443: Map pattern pattern({map, Line, Fields0}) -> Fields1 = map_fields(Fields0), {map, Line, Fields1}; %%pattern({struct,Line,Tag,Ps0}) -> %% Ps1 = pattern_list(Ps0), %% {struct,Line,Tag,Ps1}; pattern({record,Line,Name,Pfs0}) -> Pfs1 = pattern_fields(Pfs0), {record,Line,Name,Pfs1}; pattern({record_index,Line,Name,Field0}) -> Field1 = pattern(Field0), {record_index,Line,Name,Field1}; %% record_field occurs in query expressions pattern({record_field,Line,Rec0,Name,Field0}) -> Rec1 = expr(Rec0), Field1 = expr(Field0), {record_field,Line,Rec1,Name,Field1}; pattern({record_field,Line,Rec0,Field0}) -> Rec1 = expr(Rec0), Field1 = expr(Field0), {record_field,Line,Rec1,Field1}; pattern({bin,Line,Fs}) -> Fs2 = pattern_grp(Fs), {bin,Line,Fs2}; pattern({op,Line,Op,A}) -> {op,Line,Op,A}; pattern({op,Line,Op,L,R}) -> {op,Line,Op,L,R}. pattern_grp([{bin_element,L1,E1,S1,T1} | Fs]) -> S2 = case S1 of default -> default; _ -> expr(S1) end, T2 = case T1 of default -> default; _ -> bit_types(T1) end, [{bin_element,L1,expr(E1),S2,T2} | pattern_grp(Fs)]; pattern_grp([]) -> []. bit_types([]) -> []; bit_types([Atom | Rest]) when is_atom(Atom) -> [Atom | bit_types(Rest)]; bit_types([{Atom, Integer} | Rest]) when is_atom(Atom), is_integer(Integer) -> [{Atom, Integer} | bit_types(Rest)]. %% -type pattern_list([Pattern]) -> [Pattern]. %% These patterns are processed "in parallel" for purposes of variable %% definition etc. pattern_list([P0|Ps]) -> P1 = pattern(P0), [P1|pattern_list(Ps)]; pattern_list([]) -> []. %% -type pattern_fields([Field]) -> [Field]. %% N.B. Field names are full expressions here but only atoms are allowed %% by the *linter*!. pattern_fields([{record_field,Lf,{atom,La,F},P0}|Pfs]) -> P1 = pattern(P0), [{record_field,Lf,{atom,La,F},P1}|pattern_fields(Pfs)]; pattern_fields([{record_field,Lf,{var,La,'_'},P0}|Pfs]) -> P1 = pattern(P0), [{record_field,Lf,{var,La,'_'},P1}|pattern_fields(Pfs)]; pattern_fields([]) -> []. %% -type exprs([Expression]) -> [Expression]. %% These expressions are processed "sequentially" for purposes of variable %% definition etc. exprs([E0|Es]) -> E1 = expr(E0), [E1|exprs(Es)]; exprs([]) -> []. %% -type expr(Expression) -> Expression. expr({var, Line, V}) -> {var, Line, V}; expr({integer, Line, I}) -> {integer, Line, I}; expr({float, Line, F}) -> {float, Line, F}; expr({atom, Line, A}) -> {atom, Line, A}; expr({string, Line, S}) -> {string, Line, S}; expr({char, Line, C}) -> {char, Line, C}; expr({nil, Line}) -> {nil, Line}; expr({cons, Line, H0, T0} = Cons) -> %% We need to find cut vars in T0 _before_ recursing. case find_cons_cut_vars([Cons], T0) of {[], _H1T1} -> H1 = expr(H0), T1 = expr(T0), %% They see the same variables {cons, Line, H1, T1}; {Pattern, {cons, Line, H1, T1}} -> H2 = expr(H1), T2 = expr(T1), {'fun', Line, {clauses, [{clause, Line, Pattern, [], [{cons, Line, H2, T2}]}]}} end; expr({lc, Line, E0, Qs0}) -> %% Note that it is nonsensical to allow a cut on E0, as in all %% useful cases, it is defined by some expression of Qs0. Cuts are %% allowed only on generators of Qs0. Qs1 = lc_bc_quals(Qs0), E1 = expr(E0), Qs = find_comprehension_cut_vars(Qs1), case Qs of {[], _Qs2} -> {lc, Line, E1, Qs1}; {Pattern, Qs2} -> {'fun', Line, {clauses, [{clause, Line, Pattern, [], [{lc, Line, E1, Qs2}]}]}} end; expr({bc, Line, E0, Qs0}) -> %% Notes for {lc,...} above apply here too. Qs1 = lc_bc_quals(Qs0), E1 = expr(E0), Qs = find_comprehension_cut_vars(Qs1), case Qs of {[], _Qs2} -> {bc, Line, E1, Qs1}; {Pattern, Qs2} -> {'fun', Line, {clauses, [{clause, Line, Pattern, [], [{bc, Line, E1, Qs2}]}]}} end; expr({tuple, Line, Es0}) -> Es1 = expr_list(Es0), case find_cut_vars(Es1) of {[], _Es2} -> {tuple, Line, Es1}; {Pattern, Es2} -> {'fun', Line, {clauses, [{clause, Line, Pattern, [], [{tuple, Line, Es2}]}]}} end; %% OTP 17.0: EEP 443: Map construction expr({map, Line, Fields0}) -> Fields1 = map_fields(Fields0), case find_map_cut_vars(Fields1) of {[], _Fields2} -> {map, Line, Fields1}; {Pattern, Fields2} -> {'fun', Line, {clauses, [{clause, Line, Pattern, [], [{map, Line, Fields2}]}]}} end; %% OTP 17.0: EEP 443: Map update expr({map, Line, Expr0, Fields0}) -> Expr1 = expr(Expr0), Fields1 = map_fields(Fields0), case {find_cut_vars([Expr1]), find_map_cut_vars(Fields1)} of {{[], _Expr2}, {[], _Fields2}} -> {map, Line, Expr1, Fields1}; {{Pattern1, [Expr2]}, {Pattern2, Fields2}} -> {'fun', Line, {clauses, [{clause, Line, Pattern1++Pattern2, [], [{map, Line, Expr2, Fields2}]}]}} end; %%expr({struct,Line,Tag,Es0}) -> %% Es1 = pattern_list(Es0), %% {struct,Line,Tag,Es1}; expr({record_index, Line, Name, Field0}) -> %% The parser prevents Field0 from being a genuine expression, so %% can't do a cut here. Field1 = expr(Field0), {record_index, Line, Name, Field1}; expr({record, Line, Name, Inits0}) -> Inits1 = record_inits(Inits0), case find_record_cut_vars(Inits1) of {[], _Inits2} -> {record, Line, Name, Inits1}; {Pattern, Inits2} -> {'fun', Line, {clauses, [{clause, Line, Pattern, [], [{record, Line, Name, Inits2}]}]}} end; expr({record_field, Line, Rec0, Name, Field0}) -> Rec1 = expr(Rec0), Field1 = expr(Field0), case find_cut_vars([Rec1]) of {[], _Rec2} -> {record_field, Line, Rec1, Name, Field1}; {Pattern, [Rec2]} -> {'fun', Line, {clauses, [{clause, Line, Pattern, [], [{record_field, Line, Rec2, Name, Field1}]}]}} end; expr({record, Line, Rec0, Name, Upds0}) -> Rec1 = expr(Rec0), Upds1 = record_updates(Upds0), Rec = find_cut_vars([Rec1]), Upds = find_record_cut_vars(Upds1), case {Rec, Upds} of {{[], _Rec2}, {[], _Upds2}} -> {record, Line, Rec1, Name, Upds1}; {{Pattern1, [Rec2]}, {Pattern2, Upds2}} -> {'fun', Line, {clauses, [{clause, Line, Pattern1++Pattern2, [], [{record, Line, Rec2, Name, Upds2}]}]}} end; expr({record_field, Line, Rec0, Field0}) -> %% This only occurs within an mnesia query, let's not cut here Rec1 = expr(Rec0), Field1 = expr(Field0), {record_field, Line, Rec1, Field1}; expr({block, Line, Es0}) -> %% Unfold block into a sequence. %% Nonsensical to allow cuts here. Es1 = exprs(Es0), {block, Line, Es1}; expr({'if', Line, Cs0}) -> Cs1 = icr_clauses(Cs0), {'if', Line, Cs1}; expr({'case', Line, E0, Cs0}) -> E1 = expr(E0), Cs1 = icr_clauses(Cs0), case find_cut_vars([E1]) of {[], _E2} -> {'case', Line, E1, Cs1}; {Pattern, [E2]} -> {'fun', Line, {clauses, [{clause, Line, Pattern, [], [{'case', Line, E2, Cs1}]}]}} end; expr({'receive', Line, Cs0}) -> Cs1 = icr_clauses(Cs0), {'receive', Line, Cs1}; expr({'receive', Line, Cs0, To0, ToEs0}) -> To1 = expr(To0), ToEs1 = exprs(ToEs0), Cs1 = icr_clauses(Cs0), {'receive', Line, Cs1, To1, ToEs1}; expr({'try', Line, Es0, Scs0, Ccs0, As0}) -> %% It doesn't make sense to allow a cut on Es0 (the main %% expression) because it would have to be evaluated as an arg to %% the function, and thus would never be caught. Further, it %% doesn't even make sense to allow cuts in the after, because the %% only reason for using an after is for being able to side-effect %% in there, and again, it would have to be evaluated as an arg to %% the function. So no cuts at all allowed in try. Es1 = exprs(Es0), Scs1 = icr_clauses(Scs0), Ccs1 = icr_clauses(Ccs0), As1 = exprs(As0), {'try', Line, Es1, Scs1, Ccs1, As1}; expr({'fun', Line, Body}) -> case Body of {clauses, Cs0} -> Cs1 = fun_clauses(Cs0), {'fun', Line, {clauses, Cs1}}; {function, F, A} -> {'fun', Line, {function, F, A}}; {function, M, F, A} -> %% R10B-6: fun M:F/A. {'fun', Line, {function, M, F, A}} end; %% OTP 17.0: EEP 37: Funs with names expr({named_fun, Line, Name, Cs0}) -> Cs1 = fun_clauses(Cs0), {named_fun, Line, Name, Cs1}; expr({call, Line, F0, As0}) -> %% N.B. If F an atom then call to local function or BIF, if F a %% remote structure (see below) then call to other module, %% otherwise apply to "function". %% %% If F0 is a remote call then we want to allow cuts, but we don't %% want F0 to end up forming a separate function. Thus we have %% find_call_cut_vars and we brings cuts from within that up here. F1 = expr(F0), As1 = expr_list(As0), F = find_call_cut_vars(F1), As = find_cut_vars(As1), case {F, As} of {{[], _F2}, {[], _As2}} -> {call, Line, F1, As1}; {{Pattern1, [F2]}, {Pattern2, As2}} -> {'fun', Line, {clauses, [{clause, Line, Pattern1++Pattern2, [], [{call, Line, F2, As2}]}]}} end; expr({'catch', Line, E0}) -> %% No new variables added. %% See 'try' above for reasoning around no cuts here. E1 = expr(E0), {'catch', Line, E1}; expr({'query', Line, E0}) -> %% lc expression E1 = expr(E0), {'query', Line, E1}; expr({match, Line, P0, E0}) -> E1 = expr(E0), P1 = pattern(P0), {match, Line, P1, E1}; expr({bin, Line, Fs}) -> Fs1 = pattern_grp(Fs), case find_binary_cut_vars(Fs1) of {[], _Fs2} -> {bin, Line, Fs1}; {Pattern, Fs2} -> {'fun', Line, {clauses, [{clause, Line, Pattern, [], [{bin, Line, Fs2}]}]}} end; expr({op, Line, Op, A0}) -> A1 = expr(A0), case find_cut_vars([A1]) of {[], _A2} -> {op, Line, Op, A1}; {Pattern, [A2]} -> {'fun', Line, {clauses, [{clause, Line, Pattern, [], [{op, Line, Op, A2}]}]}} end; expr({op, Line, Op, L0, R0}) -> L1 = expr(L0), R1 = expr(R0), %% They see the same variables case find_cut_vars([L1, R1]) of {[], _L2R2} -> {op, Line, Op, L1, R1}; {Pattern, [L2, R2]} -> {'fun', Line, {clauses, [{clause, Line, Pattern, [], [{op, Line, Op, L2, R2}]}]}} end; %% The following are not allowed to occur anywhere! expr({remote, Line, M0, F0}) -> %% see {call,...} for why cuts aren't here. M1 = expr(M0), F1 = expr(F0), {remote, Line, M1, F1}. %% -type expr_list([Expression]) -> [Expression]. %% These expressions are processed "in parallel" for purposes of variable %% definition etc. expr_list([E0|Es]) -> E1 = expr(E0), [E1|expr_list(Es)]; expr_list([]) -> []. %% -type map_fields([MapField]) -> [MapField]. map_fields([{map_field_assoc, Line, ExpK0, ExpV0}|Fs]) -> ExpK1 = expr(ExpK0), ExpV1 = expr(ExpV0), [{map_field_assoc, Line, ExpK1, ExpV1}|map_fields(Fs)]; map_fields([{map_field_exact, Line, ExpK0, ExpV0}|Fs]) -> ExpK1 = expr(ExpK0), ExpV1 = expr(ExpV0), [{map_field_exact, Line, ExpK1, ExpV1}|map_fields(Fs)]; map_fields([]) -> []. %% -type record_inits([RecordInit]) -> [RecordInit]. %% N.B. Field names are full expressions here but only atoms are allowed %% by the *linter*!. record_inits([{record_field, Lf, {atom, La, F}, Val0}|Is]) -> Val1 = expr(Val0), [{record_field, Lf, {atom, La, F}, Val1}|record_inits(Is)]; record_inits([{record_field, Lf, {var, La, '_'}, Val0}|Is]) -> Val1 = expr(Val0), [{record_field, Lf, {var, La, '_'}, Val1}|record_inits(Is)]; record_inits([]) -> []. %% -type record_updates([RecordUpd]) -> [RecordUpd]. %% N.B. Field names are full expressions here but only atoms are allowed %% by the *linter*!. record_updates([{record_field, Lf, {atom, La, F}, Val0}|Us]) -> Val1 = expr(Val0), [{record_field, Lf, {atom, La, F}, Val1}|record_updates(Us)]; record_updates([]) -> []. %% -type icr_clauses([Clause]) -> [Clause]. icr_clauses([C0|Cs]) -> C1 = clause(C0), [C1|icr_clauses(Cs)]; icr_clauses([]) -> []. %% -type lc_bc_quals([Qualifier]) -> [Qualifier]. %% Allow filters to be both guard tests and general expressions. lc_bc_quals([{generate, Line, P0, E0}|Qs]) -> E1 = expr(E0), P1 = pattern(P0), [{generate, Line, P1, E1}|lc_bc_quals(Qs)]; lc_bc_quals([{b_generate, Line, P0, E0}|Qs]) -> E1 = expr(E0), P1 = pattern(P0), [{b_generate, Line, P1, E1}|lc_bc_quals(Qs)]; lc_bc_quals([E0|Qs]) -> E1 = expr(E0), [E1|lc_bc_quals(Qs)]; lc_bc_quals([]) -> []. %% -type fun_clauses([Clause]) -> [Clause]. fun_clauses([C0|Cs]) -> C1 = clause(C0), [C1|fun_clauses(Cs)]; fun_clauses([]) -> []. %% Turns out you can't abstract out binary types: %% 1> X = binary, Y = fun (Z) -> <> end. %% * 1: syntax error before: X %% I didn't know that. I still support that in cuts though you can't %% use it on the grounds that Erlang might fix this at some later %% point. find_binary_cut_vars(BinFields) -> cut_vars( fun ({bin_element, _Line, Var, Size, Type}) -> [V || V = {var, _Line1, '_'} <- [Var, Size, Type]]; (_) -> [] end, fun ({bin_element, Line, Var, Size, Type}, Vars) -> {[Var1, Size1, Type1], []} = lists:foldr( fun ({var, _Line, '_'}, {Res, [V|Vs]}) -> {[V|Res], Vs}; (V, {Res, Vs}) -> {[V|Res], Vs} end, {[], Vars}, [Var, Size, Type]), {bin_element, Line, Var1, Size1, Type1} end, BinFields). find_map_cut_vars(MapFields) -> cut_vars( fun ({map_field_assoc, _Line, {var, _Line1, '_'} = ExpK, {var, _Line2, '_'} = ExpV}) -> [ExpK, ExpV]; ({map_field_assoc, _Line, {var, _Line1, '_'} = ExpK, _ExpV}) -> [ExpK]; ({map_field_assoc, _Line, _ExpK, {var, _Line1, '_'} = ExpV}) -> [ExpV]; ({map_field_assoc, _Line, _ExpK, _ExpV}) -> []; ({map_field_exact, _Line, {var, _Line1, '_'} = ExpK, {var, _Line2, '_'} = ExpV}) -> [ExpK, ExpV]; ({map_field_exact, _Line, {var, _Line1, '_'} = ExpK, _ExpV}) -> [ExpK]; ({map_field_exact, _Line, _ExpK, {var, _Line1, '_'} = ExpV}) -> [ExpV]; ({map_field_exact, _Line, _ExpK, _ExpV}) -> []; (_) -> [] end, fun ({map_field_assoc, Line, _ExpK , _ExpV }, [ExpK, ExpV]) -> {map_field_assoc, Line, ExpK, ExpV}; ({map_field_assoc, Line, {var, _Line1, '_'}, ExpV }, [ExpK] ) -> {map_field_assoc, Line, ExpK, ExpV}; ({map_field_assoc, Line, ExpK , {var, _Line2, '_'}}, [ExpV] ) -> {map_field_assoc, Line, ExpK, ExpV}; ({map_field_exact, Line, _ExpK , _ExpV }, [ExpK, ExpV]) -> {map_field_assoc, Line, ExpK, ExpV}; ({map_field_exact, Line, {var, _Line1, '_'}, ExpV }, [ExpK] ) -> {map_field_assoc, Line, ExpK, ExpV}; ({map_field_exact, Line, ExpK , {var, _Line2, '_'}}, [ExpV] ) -> {map_field_assoc, Line, ExpK, ExpV} end, MapFields). find_record_cut_vars(RecFields) -> cut_vars( fun ({record_field, _Line, _FName, {var, _Line1, '_'} = Var}) -> [Var]; (_) -> [] end, fun ({record_field, Line, FName, _Var}, [Var]) -> {record_field, Line, FName, Var} end, RecFields). find_comprehension_cut_vars(Qs) -> cut_vars( fun ({generate, _Line, _P0, {var, _Line1, '_'} = Var}) -> [Var]; ({generate, _Line, _P0, _E0}) -> []; ({b_generate, _Line, _P0, {var, _Line1, '_'} = Var}) -> [Var]; ({b_generate, _Line, _P0, _E0}) -> []; (_) -> [] end, fun ({generate, Line, P0, _Var}, [Var]) -> {generate, Line, P0, Var}; ({b_generate, Line, P0, _Var}, [Var]) -> {b_generate, Line, P0, Var} end, Qs). find_call_cut_vars(F) -> cut_vars( fun ({remote, _Line, M0, F0}) -> [V || V = {var, _Line1, '_'} <- [M0,F0]]; ({var, _Line, '_'} = Var) -> [Var]; (_) -> [] end, fun ({remote, Line, M0, F0}, Vars) -> {[M1, F1], []} = lists:foldr( fun ({var, _Line, '_'}, {Res, [V|Vs]}) -> {[V|Res], Vs}; (V, {Res, Vs}) -> {[V|Res], Vs} end, {[], Vars}, [M0, F0]), {remote, Line, M1, F1}; ({var, _Line, _Var}, [Var]) -> Var end, [F]). find_cons_cut_vars(HeadsRev, {cons, _Line, _Head, Tail} = Cons) -> find_cons_cut_vars([Cons | HeadsRev], Tail); find_cons_cut_vars(HeadsRev, Other) -> Heads = lists:reverse([Other|HeadsRev]), {Pattern, Heads1} = cut_vars( fun ({cons, _Line, {var, _Line1, '_'} = Var, _Tail}) -> [Var]; ({var, _Line, '_'} = Var) -> [Var]; (_) -> [] end, fun ({cons, Line, {var, _Line1, '_'}, Tail}, [Var]) -> {cons, Line, Var, Tail}; ({var, _Line, '_'}, [Var]) -> Var end, Heads), {Pattern, lists:foldr( fun ({cons, Line, Head, _Tail}, Tail) -> {cons, Line, Head, Tail}; (Tail, undefined) -> Tail end, undefined, Heads1)}. find_cut_vars(As) -> cut_vars(fun ({var, _Line, '_'} = Var) -> [Var]; (_) -> [] end, fun (_, [{var, _Line, _Var} = Var]) -> Var end, As). cut_vars(TestFun, CombFun, AstFrag) -> cut_vars(TestFun, CombFun, AstFrag, [], []). cut_vars(_TestFun, _CombFun, [], Pattern, AstAcc) -> {lists:reverse(Pattern), lists:reverse(AstAcc)}; cut_vars(TestFun, CombFun, [Frag|AstFrags], Pattern, AstAcc) -> case TestFun(Frag) of [] -> cut_vars(TestFun, CombFun, AstFrags, Pattern, [Frag|AstAcc]); Vars -> Vars1 = [{var, Line, make_var_name()} || {var, Line, _} <- Vars], Frag1 = CombFun(Frag, Vars1), cut_vars(TestFun, CombFun, AstFrags, Vars1 ++ Pattern, [Frag1|AstAcc]) end. make_var_name() -> VarCount = get(var_count), put(var_count, VarCount+1), list_to_atom("__cut_" ++ integer_to_list(VarCount)).