%% Copyright (c) 2008-2013 Robert Virding %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% File : lfe_trans.erl %% Author : Robert Virding %% Purpose : Lisp Flavoured Erlang translator. %%% Translate LFE code to/from vanilla Erlang AST. %%% %%% Note that we don't really check code here as such, we assume the %%% input is correct. If there is an error in the input we just fail. %%% This allows us to accept forms which are actually illegal but we %%% may special case, for example functions call in patterns which %%% will become macro expansions. -module(lfe_trans). -export([from_expr/1,from_expr/2,from_body/1,from_body/2,from_lit/1, to_expr/2,to_lit/2]). -import(lists, [map/2,foldl/3,mapfoldl/3,foldr/3,splitwith/2]). -define(Q(E), [quote,E]). %We do a lot of quoting -record(from, {vc=0 %Variable counter }). %% from_expr(AST) -> Sexpr. %% from_expr(AST, Variables) -> {Sexpr,Variables}. %% from_body([AST]) -> Sexpr. %% from_body([AST], Variables) -> {Sexpr,Variables}. %% Translate a vanilla Erlang expression into LFE. The main %% difficulty is in the handling of variables. The implicit matching %% of known variables in vanilla must be translated into explicit %% equality tests in guards (which is what the compiler does %% internally). For this we need to keep track of visible variables %% and detect when they reused in patterns. from_expr(E) -> {S,_,_} = from_expr(E, ordsets:new(), #from{}), S. from_expr(E, Vs0) -> Vt0 = ordsets:from_list(Vs0), %We are clean {S,Vt1,_} = from_expr(E, Vt0, #from{}), {S,ordsets:to_list(Vt1)}. from_body(Es) -> {Les,_,_} = from_body(Es, ordsets:new(), #from{}), [progn|Les]. from_body(Es, Vs0) -> Vt0 = ordsets:from_list(Vs0), %We are clean {Les,Vt1,_} = from_body(Es, Vt0, #from{}), {[progn|Les],ordsets:to_list(Vt1)}. %% from_expr(AST, VarTable, State) -> {Sexpr,VarTable,State}. from_expr({var,_,V}, Vt, St) -> {V,Vt,St}; %Unquoted atom from_expr({nil,_}, Vt, St) -> {[],Vt,St}; from_expr({integer,_,I}, Vt, St) -> {I,Vt,St}; from_expr({float,_,F}, Vt, St) -> {F,Vt,St}; from_expr({atom,_,A}, Vt, St) -> {?Q(A),Vt,St}; %Quoted atom from_expr({string,_,S}, Vt, St) -> {?Q(S),Vt,St}; %Quoted string from_expr({cons,_,H,T}, Vt0, St0) -> {Car,Vt1,St1} = from_expr(H, Vt0, St0), {Cdr,Vt2,St2} = from_expr(T, Vt1, St1), {from_cons(Car, Cdr),Vt2,St2}; %% {[cons,Car,Cdr],Vt2,St2}; from_expr({tuple,_,Es}, Vt0, St0) -> {Ss,Vt1,St1} = from_expr_list(Es, Vt0, St0), {[tuple|Ss],Vt1,St1}; from_expr({bin,_,Segs}, Vt0, St0) -> {Ss,Vt1,St1} = from_bitsegs(Segs, Vt0, St0), {[binary|Ss],Vt1,St1}; %% Core closure special forms. from_expr({'fun',_,{clauses,Cls}}, Vt, St0) -> {Lcls,St1} = from_fun_cls(Cls, Vt, St0), {['match-lambda'|Lcls],Vt,St1}; %Don't bother using lambda from_expr({'fun',_,{function,F,A}}, Vt, St0) -> %% Build a lambda. {Vs,St1} = new_from_vars(A, St0), {[lambda,Vs,[F|Vs]],Vt,St1}; from_expr({'fun',_,{function,M,F,A}}, Vt0, St0) -> %% Translate to call to erlang:make_fun/3. {Las,Vt1,St1} = from_expr_list([M,F,A], Vt0, St0), {[call,?Q(erlang),?Q(make_fun)|Las],Vt1,St1}; %% Core control special forms. from_expr({block,_,Es}, Vt0, St0) -> {Les,Vt1,St1} = from_body(Es, Vt0, St0), {[progn|Les],Vt1,St1}; from_expr({'case',_,E,Cls}, Vt0, St0) -> {Le,Vt1,St1} = from_expr(E, Vt0, St0), {Lcls,Vt2,St2} = from_icrt_cls(Cls, Vt1, St1), {['case',Le|Lcls],Vt2,St2}; from_expr({'if',_,Cls}, Vt0, St0) -> {Lcls,Vt1,St1} = from_icrt_cls(Cls, Vt0, St0), {['case',[]|Lcls],Vt1,St1}; from_expr({'receive',_,Cls}, Vt0, St0) -> {Lcls,Vt1,St1} = from_icrt_cls(Cls, Vt0, St0), {['receive'|Lcls],Vt1,St1}; from_expr({'receive',_,Cls,Timeout,Body}, Vt0, St0) -> {Lcls,Vt1,St1} = from_icrt_cls(Cls, Vt0, St0), {Lt,Vt2,St2} = from_expr(Timeout, Vt1, St1), {Lb,Vt3,St3} = from_body(Body, Vt2, St2), {['receive'|Lcls ++ [['after',Lt|Lb]]],Vt3,St3}; %% More complex special forms. These become LFE macros. from_expr({lc,_,E,Qs}, Vt0, St0) -> {Lqs,Vt1,St1} = from_lc_quals(Qs, Vt0, St0), {Le,Vt2,St2} = from_expr(E, Vt1, St1), {[lc,Lqs,Le],Vt2,St2}; from_expr({record,_,R,Fs}, Vt0, St0) -> %Create a record MR = list_to_atom("make-" ++ atom_to_list(R)), {Lfs,Vt1,St1} = from_rec_fields(Fs, Vt0, St0), {[MR|Lfs],Vt1,St1}; from_expr({record,_,E,R,Fs}, Vt0, St0) -> %Set fields in record SR = list_to_atom("set-" ++ atom_to_list(R)), {Le,Vt1,St1} = from_expr(E, Vt0, St0), {Lfs,Vt2,St2} = from_rec_fields(Fs, Vt1, St1), {[SR,Le|Lfs],Vt2,St2}; from_expr({record_field,_,E,R,{atom,_,F}}, Vt0, St0) -> %We KNOW! RF = list_to_atom(atom_to_list(R) ++ "-" ++ atom_to_list(F)), {Le,Vt1,St1} = from_expr(E, Vt0, St0), {[RF,Le],Vt1,St1}; from_expr({record_field,_,_,_}=M, Vt, St) -> %Pre R16 packages from_package_module(M, Vt, St); %% Function calls. from_expr({call,_,{remote,_,M,F},As}, Vt0, St0) -> %Remote function call {Lm,Vt1,St1} = from_expr(M, Vt0, St0), {Lf,Vt2,St2} = from_expr(F, Vt1, St1), {Las,Vt3,St3} = from_expr_list(As, Vt2, St2), {[call,Lm,Lf|Las],Vt3,St3}; from_expr({call,_,{atom,_,F},As}, Vt0, St0) -> %Local function call {Las,Vt1,St1} = from_expr_list(As, Vt0, St0), {[F|Las],Vt1,St1}; from_expr({call,_,F,As}, Vt0, St0) -> %F not an atom or remote {Lf,Vt1,St1} = from_expr(F, Vt0, St0), {Las,Vt2,St2} = from_expr_list(As, Vt1, St1), {[funcall,Lf|Las],Vt2,St2}; from_expr({'try',_,Es,Scs,Ccs,As}, Vt, St0) -> %% Currently erl_lint does not allow any exports! {Les,_,St1} = from_body(Es, Vt, St0), %% These maybe empty. {Lscs,_,St2} = if Scs =:= [] -> {[],[],St1}; true -> from_icrt_cls(Scs, Vt, St1) end, {Lccs,_,St3} = if Ccs =:= [] -> {[],[],St2}; true -> from_icrt_cls(Ccs, Vt, St2) end, {Las,_,St4} = from_body(As, Vt, St3), {['try',[progn|Les]| from_maybe('case', Lscs) ++ from_maybe('catch', Lccs) ++ from_maybe('after', Las)],Vt,St4}; from_expr({'catch',_,E}, Vt0, St0) -> {Le,Vt1,St1} = from_expr(E, Vt0, St0), {['catch',Le],Vt1,St1}; from_expr({match,_,P,E}, Vt0, St0) -> {Lp,Eqt,Vt1,St1} = from_pat(P, Vt0, St0), %The pattern {Le,Vt2,St2} = from_expr(E, Vt1, St1), %The expression {Lb,Vt3,St3} = from_expr(P, Vt2, St2), %Pattern as value expression Leg = from_eq_tests(Eqt), %Implicit guard tests {['let',[[Lp,['when'|Leg],Le]],Lb],Vt3,St3}; from_expr({op,_,Op,A}, Vt0, St0) -> {La,Vt1,St1} = from_expr(A, Vt0, St0), {[Op,La],Vt1,St1}; from_expr({op,_,Op,L,R}, Vt0, St0) -> {Ll,Vt1,St1} = from_expr(L, Vt0, St0), {Lr,Vt2,St2} = from_expr(R, Vt1, St1), {[Op,Ll,Lr],Vt2,St2}. %% from_package_module(Module, VarTable, State) -> {Module,VarTable,State}. %% We must handle the special case where in pre-R16 you could have %% packages with a dotted module path. It used a special record_field %% tuple. This does not work in R16 and later! from_package_module({record_field,_,_,_}=M, Vt, St) -> Segs = erl_parse:package_segments(M), A = list_to_atom(packages:concat(Segs)), {?Q(A),Vt,St}. from_maybe(_, []) -> []; from_maybe(Tag, Es) -> [[Tag|Es]]. from_cons(Car, [list|Es]) -> [list,Car|Es]; from_cons(Car, []) -> [list,Car]; from_cons(Car, Cdr) -> [cons,Car,Cdr]. %% from_body(Expressions, VarTable, State) -> {Body,VarTable,State}. %% Handle '=' specially here and translate into let containing rest %% of body. from_body([{match,_,_,_}=Match], Vt0,St0) -> %Last match {Lm,Vt1,St1} = from_expr(Match, Vt0, St0), %Must return pattern as value {[Lm],Vt1,St1}; from_body([{match,_,P,E}|Es], Vt0, St0) -> {Lp,Eqt,Vt1,St1} = from_pat(P, Vt0, St0), {Le,Vt2,St2} = from_expr(E, Vt1, St1), {Les,Vt3,St4} = from_body(Es, Vt2, St2), Leg = from_eq_tests(Eqt), %Implicit guard tests {[['let',[[Lp,['when'|Leg],Le]]|Les]],Vt3,St4}; from_body([E|Es], Vt0, St0) -> {Le,Vt1,St1} = from_expr(E, Vt0, St0), {Les,Vt2,St2} = from_body(Es, Vt1, St1), {[Le|Les],Vt2,St2}; from_body([], Vt, St) -> {[],Vt,St}. from_expr_list(Es, Vt, St) -> mapfoldl2(fun from_expr/3, Vt, St, Es). %% from_icrt_cls(Clauses, VarTable, State) -> {Clauses,VarTable,State}. %% from_icrt_cl(Clause, VarTable, State) -> {Clause,VarTable,State}. %% If/case/receive/try clauses. %% No ; in guards, so no guard sequence only one list of guard tests. from_icrt_cls(Cls, Vt, St) -> from_cls(fun from_icrt_cl/3, Vt, St, Cls). from_icrt_cl({clause,_,[],[G],B}, Vt0, St0) -> %If clause {Lg,Vt1,St1} = from_body(G, Vt0, St0), {Lb,Vt2,St2} = from_body(B, Vt1, St1), {['_',['when'|Lg]|Lb],Vt2,St2}; from_icrt_cl({clause,_,H,[],B}, Vt0, St0) -> {[Lh],Eqt,Vt1,St1} = from_pat_list(H, Vt0, St0), %List of one {Lb,Vt2,St2} = from_body(B, Vt1, St1), Leg = from_eq_tests(Eqt), {[Lh,['when'|Leg]|Lb],Vt2,St2}; from_icrt_cl({clause,_,H,[G],B}, Vt0, St0) -> {[Lh],Eqt,Vt1,St1} = from_pat_list(H, Vt0, St0), %List of one {Lg,Vt2,St2} = from_body(G, Vt1, St1), {Lb,Vt3,St3} = from_body(B, Vt2, St2), Leg = from_eq_tests(Eqt), {[Lh,['when'|Leg ++ Lg]|Lb],Vt3,St3}. %% from_fun_cls(Clauses, VarTable, State) -> {Clauses,State}. %% from_fun_cl(Clause, VarTable, State) -> {Clause,VarTable,State}. %% Function clauses, all variables in the patterns are new variables %% which shadow existing variables without equality tests. from_fun_cls(Cls, Vt, St0) -> {Lcls,_,St1} = from_cls(fun from_fun_cl/3, Vt, St0, Cls), {Lcls,St1}. from_fun_cl({clause,_,H,[],B}, Vt0, St0) -> {Lh,Eqt,Vtp,St1} = from_pat_list(H, [], St0), Vt1 = ordsets:union(Vtp, Vt0), %All variables so far {Lb,Vt2,St2} = from_body(B, Vt1, St1), Leg = from_eq_tests(Eqt), {[Lh,['when'|Leg]|Lb],Vt2,St2}; from_fun_cl({clause,_,H,[G],B}, Vt0, St0) -> {Lh,Eqt,Vtp,St1} = from_pat_list(H, [], St0), Vt1 = ordsets:union(Vtp, Vt0), %All variables so far {Lg,Vt2,St2} = from_body(G, Vt1, St1), {Lb,Vt3,St3} = from_body(B, Vt2, St2), Leg = from_eq_tests(Eqt), {[Lh,['when'|Leg ++ Lg]|Lb],Vt3,St3}. from_cls(Fun, Vt0, St0, [C]) -> {Lc,Vt1,St1} = Fun(C, Vt0, St0), {[Lc],Vt1,St1}; from_cls(Fun, Vt0, St0, [C|Cs]) -> {Lc,Vtc,St1} = Fun(C, Vt0, St0), {Lcs,Vtcs,St2} = from_cls(Fun, Vt0, St1, Cs), {[Lc|Lcs],ordsets:intersection(Vtc, Vtcs),St2}. from_eq_tests(Gs) -> [ ['=:=',V,V1] || {V,V1} <- Gs ]. %% from_lc_quals(Qualifiers, VarTable, State) -> {Qualifiers,VarTable,State}. from_lc_quals([{generate,_,P,E}|Qs], Vt0, St0) -> {Lp,Eqt,Vt1,St1} = from_pat(P, Vt0, St0), {Le,Vt2,St2} = from_expr(E, Vt1, St1), {Lqs,Vt3,St3} = from_lc_quals(Qs, Vt2, St2), Leg = from_eq_tests(Eqt), {[['<-',Lp,['when'|Leg]|Le]|Lqs],Vt3,St3}; from_lc_quals([T|Qs], Vt0, St0) -> {Lt,Vt1,St1} = from_expr(T, Vt0, St0), {Lqs,Vt2,St2} = from_lc_quals(Qs, Vt1, St1), {[Lt|Lqs],Vt2,St2}; from_lc_quals([], Vt, St) -> {[],Vt,St}. %% from_rec_fields(Recfields, VarTable, State) -> {Recfields,VarTable,State}. from_rec_fields([{record_field,_,{atom,_,F},E}|Fs], Vt0, St0) -> {Le,Vt1,St1} = from_expr(E, Vt0, St0), {Lfs,Vt2,St2} = from_rec_fields(Fs, Vt1, St1), {[F,Le|Lfs],Vt2,St2}; from_rec_fields([{record_field,_,{var,_,F},E}|Fs], Vt0, St0) -> %special case!! {Le,Vt1,St1} = from_expr(E, Vt0, St0), {Lfs,Vt2,St2} = from_rec_fields(Fs, Vt1, St1), {[F,Le|Lfs],Vt2,St2}; from_rec_fields([], Vt, St) -> {[],Vt,St}. %% from_bitsegs(Segs, VarTable, State) -> {Segs,VarTable,State}. from_bitsegs([{bin_element,_,Seg,Size,Type}|Segs], Vt0, St0) -> {S,Vt1,St1} = from_bitseg(Seg, Size, Type, Vt0, St0), {Ss,Vt2,St2} = from_bitsegs(Segs, Vt1, St1), {[S|Ss],Vt2,St2}; from_bitsegs([], Vt, St) -> {[],Vt,St}. %% So it won't get confused with strings. from_bitseg({integer,_,I}, default, default, Vt, St) -> {I,Vt,St}; from_bitseg({integer,_,I}, Size, Type, Vt0, St0) -> {Lsize,Vt1,St1} = from_bitseg_size(Size, Vt0, St0), {[I|from_bitseg_type(Type) ++ Lsize],Vt1,St1}; from_bitseg({float,_,F}, Size, Type, Vt0, St0) -> {Lsize,Vt1,St1} = from_bitseg_size(Size, Vt0, St0), {[F|from_bitseg_type(Type) ++ Lsize],Vt1,St1}; from_bitseg({string,_,S}, Size, Type, Vt0, St0) -> {Lsize,Vt1,St1} = from_bitseg_size(Size, Vt0, St0), {[S|from_bitseg_type(Type) ++ Lsize],Vt1,St1}; from_bitseg(E, Size, Type, Vt0, St0) -> {Le,Vt1,St1} = from_expr(E, Vt0, St0), {Lsize,Vt2,St2} = from_bitseg_size(Size, Vt1, St1), {[Le|from_bitseg_type(Type) ++ Lsize],Vt2,St2}. from_bitseg_size(default, Vt, St) -> {[],Vt,St}; from_bitseg_size(Size, Vt0, St0) -> {Ssize,Vt1,St1} = from_expr(Size, Vt0, St0), {[[size,Ssize]],Vt1,St1}. from_bitseg_type(default) -> []; from_bitseg_type(Ts) -> map(fun ({unit,U}) -> [unit,U]; (T) -> T end, Ts). new_from_var(#from{vc=C}=St) -> V = list_to_atom(lists:concat(["---",C,"---"])), {V,St#from{vc=C+1}}. new_from_vars(N, St) -> new_from_vars(N, St, []). new_from_vars(N, St0, Vs) when N > 0 -> {V,St1} = new_from_var(St0), new_from_vars(N-1, St1, [V|Vs]); new_from_vars(0, St, Vs) -> {Vs,St}. from_pat({var,_,'_'}, Vt, St) -> {'_',[],Vt,St}; %Special case _ from_pat({var,_,V}, Vt, St0) -> %Unquoted atom case ordsets:is_element(V, Vt) of %Is variable bound? true -> {V1,St1} = new_from_var(St0), %New var for pattern {V1,[{V,V1}],Vt,St1}; %Add to guard tests false -> {V,[],ordsets:add_element(V, Vt),St0} end; from_pat({nil,_}, Vt, St) -> {[],[],Vt,St}; from_pat({integer,_,I}, Vt, St) -> {I,[],Vt,St}; from_pat({float,_,F}, Vt, St) -> {F,[],Vt,St}; from_pat({atom,_,A}, Vt, St) -> {?Q(A),[],Vt,St}; %Quoted atom from_pat({string,_,S}, Vt, St) -> {?Q(S),[],Vt,St}; %Quoted string from_pat({cons,_,H,T}, Vt0, St0) -> {Car,Eqt1,Vt1,St1} = from_pat(H, Vt0, St0), {Cdr,Eqt2,Vt2,St2} = from_pat(T, Vt1, St1), {from_cons(Car, Cdr),Eqt1++Eqt2,Vt2,St2}; from_pat({tuple,_,Es}, Vt0, St0) -> {Ss,Eqt,Vt1,St1} = from_pat_list(Es, Vt0, St0), {[tuple|Ss],Eqt,Vt1,St1}; from_pat({bin,_,Segs}, Vt0, St0) -> {Ss,Eqt,Vt1,St1} = from_pat_bitsegs(Segs, Vt0, St0), {[binary|Ss],Eqt,Vt1,St1}; from_pat({record,_,R,Fs}, Vt0, St0) -> %Match a record MR = list_to_atom("match-" ++ atom_to_list(R)), {Sfs,Eqt,Vt1,St1} = from_pat_rec_fields(Fs, Vt0, St0), {[MR|Sfs],Eqt,Vt1,St1}; from_pat({match,_,P1,P2}, Vt0, St0) -> %Aliases {Lp1,Eqt1,Vt1,St1} = from_pat(P1, Vt0, St0), {Lp2,Eqt2,Vt2,St2} = from_pat(P2, Vt1, St1), {['=',Lp1,Lp2],Eqt1++Eqt2,Vt2,St2}; %% Basically illegal syntax which maybe generated by internal tools. from_pat({call,_,{atom,_,F},As}, Vt0, St0) -> %% This will never occur in real code but for macro expansions. {Las,Eqt,Vt1,St1} = from_pat_list(As, Vt0, St0), {[F|Las],Eqt,Vt1,St1}. from_pat_list([P|Ps], Vt0, St0) -> {Lp,Eqt,Vt1,St1} = from_pat(P, Vt0, St0), {Lps,Eqts,Vt2,St2} = from_pat_list(Ps, Vt1, St1), {[Lp|Lps],Eqt++Eqts,Vt2,St2}; from_pat_list([], Vt, St) -> {[],[],Vt,St}. %% from_pat_rec_fields(Recfields, VarTable, State) -> %% {Recfields,EqTable,VarTable,State}. from_pat_rec_fields([{record_field,_,{atom,_,F},P}|Fs], Vt0, St0) -> {Lp,Eqt,Vt1,St1} = from_pat(P, Vt0, St0), {Lfs,Eqts,Vt2,St2} = from_pat_rec_fields(Fs, Vt1, St1), {[F,Lp|Lfs],Eqt++Eqts,Vt2,St2}; from_pat_rec_fields([{record_field,_,{var,_,F},P}|Fs], Vt0, St0) -> %% Special case!! {Lp,Eqt,Vt1,St1} = from_pat(P, Vt0, St0), {Lfs,Eqts,Vt2,St2} = from_pat_rec_fields(Fs, Vt1, St1), {[F,Lp|Lfs],Eqt++Eqts,Vt2,St2}; from_pat_rec_fields([], Vt, St) -> {[],[],Vt,St}. %% from_pat_bitsegs(Segs, VarTable, State) -> {Segs,EqTable,VarTable,State}. from_pat_bitsegs([{bin_element,_,Seg,Size,Type}|Segs], Vt0, St0) -> {S,Eqt,Vt1,St1} = from_pat_bitseg(Seg, Size, Type, Vt0, St0), {Ss,Eqts,Vt2,St2} = from_pat_bitsegs(Segs, Vt1, St1), {[S|Ss],Eqt++Eqts,Vt2,St2}; from_pat_bitsegs([], Vt, St) -> {[],[],Vt,St}. from_pat_bitseg({string,_,S}, Size, Type, Vt0, St0) -> {Lsize,Vt1,St1} = from_pat_bitseg_size(Size, Vt0, St0), {[S|from_bitseg_type(Type) ++ Lsize],[],Vt1,St1}; from_pat_bitseg(P, Size, Type, Vt0, St0) -> {Lp,Eqt,Vt1,St1} = from_pat(P, Vt0, St0), {Lsize,Vt2,St2} = from_pat_bitseg_size(Size, Vt1, St1), {[Lp|from_bitseg_type(Type) ++ Lsize],Eqt,Vt2,St2}. from_pat_bitseg_size(default, Vt, St) -> {[],Vt,St}; from_pat_bitseg_size({var,_,V}, Vt, St) -> %Size vars never match {[[size,V]],Vt,St}; from_pat_bitseg_size(Size, Vt0, St0) -> {Ssize,_,Vt1,St1} = from_pat(Size, Vt0, St0), {[[size,Ssize]],Vt1,St1}. from_lit({nil,_}) -> []; from_lit({integer,_,I}) -> I; from_lit({float,_,F}) -> F; from_lit({atom,_,A}) -> A; %Quoted atom from_lit({string,_,S}) -> S; from_lit({cons,_,H,T}) -> [cons,from_lit(H),from_lit(T)]; from_lit({tuple,_,Es}) -> [tuple|from_lit_list(Es)]. from_lit_list(Es) -> [ from_lit(E) || E <- Es ]. -record(to, {vc=0 %Variable counter }). to_expr(E, L) -> {Le,_} = to_expr(E, L, orddict:new(), #to{}), Le. %% to_expr(Expr, LineNumber, VarTable, State) -> {Expr,State}. to_expr([], L, _, St) -> {{nil,L},St}; to_expr(I, L, _, St) when is_integer(I) -> {{integer,L,I},St}; to_expr(F, L, _, St) when is_float(F) -> {{float,L,F},St}; to_expr(V, L, Vt, St) when is_atom(V) -> %Unquoted atom to_expr_var(V, L, Vt, St); to_expr(T, L, Vt, St0) when is_tuple(T) -> {Es,St1} = to_expr_list(tuple_to_list(T), L, Vt, St0), {{tuple,L,Es},St1}; to_expr(?Q(V), L, _, St) -> {to_lit(V, L),St}; to_expr([cons,H,T], L, Vt, St0) -> {Eh,St1} = to_expr(H, L, Vt, St0), {Et,St2} = to_expr(T, L, Vt, St1), {{cons,L,Eh,Et},St2}; to_expr([car,E], L, Vt, St0) -> {Ee,St1} = to_expr(E, L, Vt, St0), {{call,L,{atom,L,hd},[Ee]},St1}; to_expr([cdr,E], L, Vt, St0) -> {Ee,St1} = to_expr(E, L, Vt, St0), {{call,L,{atom,L,tl},[Ee]},St1}; to_expr([list|Es], L, Vt, St) -> Fun = fun (E, {Tail,St0}) -> {Ee,St1} = to_expr(E, L, Vt, St0), {{cons,L,Ee,Tail},St1} end, foldr(Fun, {{nil,L},St}, Es); to_expr(['list*'|Es], L, Vt, St) -> %Macro to_expr_list_s(fun to_expr/4, L, Vt, St, Es); to_expr([tuple|Es], L, Vt, St0) -> {Ees,St1} = to_expr_list(Es, L, Vt, St0), {{tuple,L,Ees},St1}; to_expr([binary|Segs], L, Vt, St0) -> {Esegs,St1} = to_bitsegs(Segs, L, Vt, St0), {{bin,L,Esegs},St1}; %% Core closure special forms. to_expr([lambda,As|B], L, Vt, St0) -> {Ecl,St1} = to_fun_cl([As|B], L, Vt, St0), {{'fun',L,{clauses,[Ecl]}},St1}; to_expr(['match-lambda'|Cls], L, Vt, St0) -> {Ecls,St1} = to_fun_cls(Cls, L, Vt, St0), {{'fun',L,{clauses,Ecls}},St1}; to_expr(['fun',F,A], L, _, St) -> {{'fun',L,{function,F,A}},St}; to_expr(['fun',M,F,A], L, _, St) -> {{'fun',L,{function,M,F,A}},St}; to_expr(['let',Lbs|B], L, Vt0, St0) -> {Ebs,Vt1,St1} = to_let_bindings(Lbs, L, Vt0, St0), {Eb,St2} = to_body(B, L, Vt1, St1), {{block,L,Ebs ++ Eb},St2}; %% Core control special forms. to_expr([progn|B], L, Vt, St) -> to_block(B, L, Vt, St); to_expr(['if',Test,True], L, Vt, St) -> to_expr(['if',Test,True,?Q(false)], L, Vt, St); to_expr(['if',Test,True,False], L, Vt, St0) -> {Etest,St1} = to_expr(Test, L, Vt, St0), {Ecls,St2} = to_icrt_cls([[?Q(true),True],[?Q(false),False]], L, Vt, St1), {{'case',L,Etest,Ecls},St2}; to_expr(['case',E|Cls], L, Vt, St0) -> {Ee,St1} = to_expr(E, L, Vt, St0), {Ecls,St2} = to_icrt_cls(Cls, L, Vt, St1), {{'case',L,Ee,Ecls},St2}; to_expr(['receive'|Cls0], L, Vt, St0) -> %% Get the right receive form depending on whether there is an after. {Cls1,A} = splitwith(fun (['after'|_]) -> false; (_) -> true end, Cls0), {Ecls,St1} = to_icrt_cls(Cls1, L, Vt, St0), case A of [['after',T|B]] -> {Et,St2} = to_expr(T, L, Vt, St1), {Eb,St3} = to_body(B, L, Vt, St2), {{'receive',L,Ecls,Et,Eb},St3}; [] -> {{'receive',L,Ecls},St1} end; %% Special known macros. %% No record stuff here as they are macros which have been expanded. to_expr([lc,Qs|Es], L, Vt0, St0) -> {Eqs,Vt1,St1} = to_lc_quals(Qs, L, Vt0, St0), {Ees,St2} = to_block(Es, L, Vt1, St1), {{lc,L,Ees,Eqs},St2}; %% General function calls. to_expr([call,?Q(erlang),?Q(F)|As], L, Vt, St0) -> %% This is semantically the same but some tools behave differently %% (qlc_pt). {Eas,St1} = to_expr_list(As, L, Vt, St0), case is_erl_op(F, length(As)) of true -> {list_to_tuple([op,L,F|Eas]),St1}; false -> {{call,L,{remote,{atom,L,erlang},{atom,L,F}},Eas},St1} end; to_expr([call,M,F|As], L, Vt, St0) -> {Em,St1} = to_expr(M, L, Vt, St0), {Ef,St2} = to_expr(F, L, Vt, St1), {Eas,St3} = to_expr_list(As, L, Vt, St2), {{call,L,{remote,L,Em,Ef},Eas},St3}; to_expr([F|As], L, Vt, St0) when is_atom(F) -> %General function call {Eas,St1} = to_expr_list(As, L, Vt, St0), case is_erl_op(F, length(As)) of true -> {list_to_tuple([op,L,F|Eas]),St1}; false -> {{call,L,{atom,L,F},Eas},St1} end; to_expr(List, L, _, St) -> case is_integer_list(List) of true -> {{string,L,List},St}; false -> io:format("BOOM:~p\n", [List]), {integer,L,4711} %Not right! end. to_expr_var(V, L, Vt, St) -> Var = case orddict:find(V, Vt) of {ok,V1} -> V1; error -> V end, {{var,L,Var},St}. %% is_erl_op(Op, Arity) -> bool(). %% Is Op/Arity one of the known Erlang operators? is_erl_op(Op, Ar) -> erl_internal:arith_op(Op, Ar) orelse erl_internal:bool_op(Op, Ar) orelse erl_internal:comp_op(Op, Ar) orelse erl_internal:list_op(Op, Ar) orelse erl_internal:send_op(Op, Ar). to_body(Es, L, Vt, St) -> Fun = fun (E, St0) -> to_expr(E, L, Vt, St0) end, mapfoldl(Fun, St, Es). to_expr_list(Es, L, Vt, St) -> Fun = fun (E, St0) -> to_expr(E, L, Vt, St0) end, mapfoldl(Fun, St, Es). to_expr_list_s(Fun, L, Vt, St, [E]) -> Fun(E, L, Vt, St); to_expr_list_s(Fun, L, Vt, St0, [E|Es]) -> {Les,St1} = to_expr_list_s(Fun, L, Vt, St0, Es), {Le,St2} = Fun(E, L, Vt, St1), {{cons,L,Le,Les},St2}; to_expr_list_s(_, L, _, St, []) -> {{nil,L},St}. to_pat_list_s(Fun, L, Vt, St, [E]) -> Fun(E, L, Vt, St); to_pat_list_s(Fun, L, Vt0, St0, [E|Es]) -> {Les,Vt1,St1} = to_pat_list_s(Fun, L, Vt0, St0, Es), {Le,Vt2,St2} = Fun(E, L, Vt1, St1), {{cons,L,Le,Les},Vt2,St2}; to_pat_list_s(_, L, Vt, St, []) -> {{nil,L},Vt,St}. %% to_block(Expressions, LineNumber, VarTable, State) -> {Block,State}. %% Don't generate {block,...} if only one expression, though %% semantically the same some tools can't handle it (qlc_pt). to_block(Es, L, Vt, St0) -> case to_expr_list(Es, L, Vt, St0) of {[Ee],St1} -> {Ee,St1}; %No need to wrap {Ees,St1} -> {{block,L,Ees},St1} %Must wrap end. %% to_let_bindings(Bindings, LineNumber, VarTable, State) -> %% {Block,VarTable,State}. %% When we have a guard translate into a case but special case where %% we have an empty guard. to_let_bindings(Lbs, L, Vt, St) -> Fun = fun ([P,E], Vt0, St0) -> {Ep,Vt1,St1} = to_pat(P, L, Vt0, St0), {Ee,St2} = to_expr(E, L, Vt0, St1), {{match,L,Ep,Ee},Vt1,St2}; ([P,['when'],E], Vt0, St0) -> %Just to keep it short {Ep,Vt1,St1} = to_pat(P, L, Vt0, St0), {Ee,St2} = to_expr(E, L, Vt0, St1), {{match,L,Ep,Ee},Vt1,St2}; ([P,['when'|G],E], Vt0, St0) -> {Ee,St1} = to_expr(E, L, Vt0, St0), {Ep,Vt1,St2} = to_pat(P, L, Vt0, St1), {Eg,St3} = to_body(G, L, Vt1, St2), {{'case',L,Ee,[{clause,L,[Ep],Eg,[Ep]}]},Vt1,St3} end, mapfoldl2(Fun, Vt, St, Lbs). %% to_icrt_cls(Clauses, LineNumber, VarTable, State) -> {Clauses,State}. %% to_icrt_cl(Clause, LineNumber, VarTable, State) -> {Clause,State}. %% If/case/receive/try clauses. to_icrt_cls(Cls, L, Vt, St) -> Fun = fun (Cl, St0) -> to_icrt_cl(Cl, L, Vt, St0) end, mapfoldl(Fun, St, Cls). to_icrt_cl([P,['when'|G]|B], L, Vt0, St0) -> {Ep,Vt1,St1} = to_pat(P, L, Vt0, St0), {Eg,St2} = to_body(G, L, Vt1, St1), {Eb,St3} = to_body(B, L, Vt1, St2), {{clause,L,[Ep],Eg,Eb},St3}; to_icrt_cl([P|B], L, Vt0, St0) -> {Ep,Vt1,St1} = to_pat(P, L, Vt0, St0), {Eb,St2} = to_body(B, L, Vt1, St1), {{clause,L,[Ep],[],Eb},St2}. %% to_fun_cls(Clauses, LineNumber) -> Clauses. %% to_fun_cl(Clause, LineNumber) -> Clause. %% Function clauses. to_fun_cls(Cls, L, Vt, St) -> Fun = fun (Cl, St0) -> to_fun_cl(Cl, L, Vt, St0) end, mapfoldl(Fun, St, Cls). to_fun_cl([As,['when'|G]|B], L, Vt0, St0) -> {Eas,Vt1,St1} = to_pat_list(As, L, Vt0, St0), {Eg,St2} = to_body(G, L, Vt1, St1), {Eb,St3} = to_body(B, L, Vt1, St2), {{clause,L,Eas,Eg,Eb},St3}; to_fun_cl([As|B], L, Vt0, St0) -> {Eas,Vt1,St1} = to_pat_list(As, L, Vt0, St0), {Eb,St2} = to_body(B, L, Vt1, St1), {{clause,L,Eas,[],Eb},St2}. %% to_lc_quals(Qualifiers, LineNumber, VarTable, State) -> %% {Qualifiers,VarTable,State}. %% Can't use mapfoldl2 as guard habling modifies Qualifiers. to_lc_quals([['<-',P,E]|Qs], L, Vt0, St0) -> {Ep,Vt1,St1} = to_pat(P, L, Vt0, St0), {Ee,St2} = to_expr(E, L, Vt1, St1), {Eqs,Vt2,St3} = to_lc_quals(Qs, L, Vt1, St2), {[{generate,L,Ep,Ee}|Eqs],Vt2,St3}; to_lc_quals([['<-',P,['when'|G],E]|Qs], L, Vt, St) -> to_lc_quals([['<-',P,E]|G ++ Qs], L, Vt, St); %Move guards to tests to_lc_quals([T|Qs], L, Vt0, St0) -> {Et,St1} = to_expr(T, L, Vt0, St0), {Eqs,Vt1,St2} = to_lc_quals(Qs, L, Vt0, St1), {[Et|Eqs],Vt1,St2}; to_lc_quals([], _, Vt, St) -> {[],Vt,St}. %% to_bitsegs(Segs, LineNumber, VarTable, State) -> {Segs,State}. %% This gives a verbose value, but it is correct. to_bitsegs(Ss, L, Vt, St) -> Fun = fun (S, St0) -> to_bitseg(S, L, Vt, St0) end, mapfoldl(Fun, St, Ss). to_bitseg([Val|Specs]=F, L, Vt, St) -> case is_integer_list(F) of true -> {Size,Type} = to_bitspecs([]), to_bin_element(F, Size, Type, L, Vt, St); false -> {Size,Type} = to_bitspecs(Specs), to_bin_element(Val, Size, Type, L, Vt, St) end; to_bitseg(Val, L, Vt, St) -> {Size,Type} = to_bitspecs([]), to_bin_element(Val, Size, Type, L, Vt, St). to_bin_element(Val, Size, {Type,Unit,Sign,End}, L, Vt, St0) -> {Eval,St1} = to_expr(Val, L, Vt, St0), {Esiz,St2} = to_bin_size(Size, L, Vt, St1), {{bin_element,L,Eval,Esiz,[Type,to_bin_unit(Unit),Sign,End]},St2}. to_bin_size(default, _, _, St) -> {default,St}; to_bin_size(undefined, _, _, St) -> {default,St}; to_bin_size(Size, L, Vt, St) -> to_expr(Size, L, Vt, St). to_bin_unit(default) -> default; to_bin_unit(Unit) -> {unit,Unit}. %% to_bitspec(Specs) -> {Size,Type}. %% Get the error handling as we want it. to_bitspecs(Ss) -> case lfe_bits:get_bitspecs(Ss) of {ok,Sz,Ty} -> {Sz,Ty}; {error,Error} -> erlang:error(Error) end. new_to_var(#to{vc=C}=St) -> V = list_to_atom(lists:concat(["___",C,"___"])), {V,St#to{vc=C+1}}. %% to_pat(Pattern, LineNumber, VarTable, State) -> {Pattern,VarTable,State}. to_pat([], L, Vt, St) -> {{nil,L},Vt,St}; to_pat(I, L, Vt, St) when is_integer(I) -> {{integer,L,I},Vt,St}; to_pat(F, L, Vt, St) when is_float(F) -> {{float,L,F},Vt,St}; to_pat(V, L, Vt, St) when is_atom(V) -> %Unquoted atom to_pat_var(V, L, Vt, St); to_pat(T, L, Vt, St) when is_tuple(T) -> %Tuple literal Es = to_lit_list(tuple_to_list(T), L), {{tuple,L,Es},Vt,St}; to_pat(?Q(P), L, Vt, St) -> %Everything quoted here {to_lit(P, L),Vt,St}; to_pat([cons,H,T], L, Vt0, St0) -> {[Eh,Et],Vt1,St1} = to_pat_list([H,T], L, Vt0, St0), {{cons,L,Eh,Et},Vt1,St1}; to_pat([list|Es], L, Vt, St) -> Fun = fun (E, {Tail,Vt0,St0}) -> {Ee,Vt1,St1} = to_pat(E, L, Vt0, St0), {{cons,L,Ee,Tail},Vt1,St1} end, foldr(Fun, {{nil,L},Vt,St}, Es); to_pat(['list*'|Es], L, Vt, St) -> to_pat_list_s(fun to_pat/4, L, Vt, St, Es); to_pat([tuple|Es], L, Vt0, St0) -> {Ees,Vt1,St1} = to_pat_list(Es, L, Vt0, St0), {{tuple,L,Ees},Vt1,St1}; to_pat([binary|Segs], L, Vt0, St0) -> {Esegs,Vt1,St1} = to_pat_bitsegs(Segs, L, Vt0, St0), {{bin,L,Esegs},Vt1,St1}; to_pat(['=',P1,P2], L, Vt0, St0) -> %Alias {Ep1,Vt1,St1} = to_pat(P1, L, Vt0, St0), {Ep2,Vt2,St2} = to_pat(P2, L, Vt1, St1), {{match,L,Ep1,Ep2},Vt2,St2}. to_pat_list(Ps, L, Vt, St) -> Fun = fun (P, Vt0, St0) -> to_pat(P, L, Vt0, St0) end, mapfoldl2(Fun, Vt, St, Ps). to_pat_var(V, L, Vt, St0) -> case orddict:is_key(V, Vt) of true -> {V1,St1} = new_to_var(St0), {{var,L,V1},orddict:store(V, V1, Vt),St1}; false -> {{var,L,V},orddict:store(V, V, Vt),St0} end. %% to_pat_bitsegs(Segs, LineNumber, VarTable, State) -> {Segs,State}. %% This gives a verbose value, but it is correct. to_pat_bitsegs(Ss, L, Vt, St) -> Fun = fun (S, Vt0, St0) -> to_pat_bitseg(S, L, Vt0, St0) end, mapfoldl2(Fun, Vt, St, Ss). to_pat_bitseg([Val|Specs]=F, L, Vt, St) -> case is_integer_list(F) of true -> {Size,Type} = to_bitspecs([]), to_pat_bin_element(F, Size, Type, L, Vt, St); false -> {Size,Type} = to_bitspecs(Specs), to_pat_bin_element(Val, Size, Type, L, Vt, St) end; to_pat_bitseg(Val, L, Vt, St) -> {Size,Type} = to_bitspecs([]), to_pat_bin_element(Val, Size, Type, L, Vt, St). to_pat_bin_element(Val, Size, {Type,Unit,Sign,End}, L, Vt0, St0) -> {Eval,Vt1,St1} = to_pat(Val, L, Vt0, St0), {Esiz,Vt2,St2} = to_pat_bin_size(Size, L, Vt1, St1), {{bin_element,L,Eval,Esiz,[Type,to_bin_unit(Unit),Sign,End]},Vt2,St2}. to_pat_bin_size(default, _, Vt, St) -> {default,Vt,St}; to_pat_bin_size(undefined, _, Vt, St) -> {default,Vt,St}; to_pat_bin_size(Size, L, Vt, St) -> to_pat(Size, L, Vt, St). to_lit([], L) -> {nil,L}; to_lit(I, L) when is_integer(I) -> {integer,L,I}; to_lit(F, L) when is_float(F) -> {float,L,F}; to_lit(V, L) when is_atom(V) -> {atom,L,V}; %Quoted atom here! to_lit([H|T], L) -> {cons,L,to_lit(H, L),to_lit(T, L)}; to_lit(T, L) when is_tuple(T) -> {tuple,L,to_lit_list(tuple_to_list(T), L)}. to_lit_list(Ps, L) -> [ to_lit(P, L) || P <- Ps ]. is_integer_list([I|Is]) when is_integer(I) -> is_integer_list(Is); is_integer_list([]) -> true; is_integer_list(_) -> false. %% mapfoldl2(Fun, Acc1, Acc2, List) -> {List,Acc1,Acc2}. %% Like normal mapfoldl but with 2 accumulators. mapfoldl2(Fun, A0, B0, [E0|Es0]) -> {E1,A1,B1} = Fun(E0, A0, B0), {Es1,A2,B2} = mapfoldl2(Fun, A1, B1, Es0), {[E1|Es1],A2,B2}; mapfoldl2(_, A, B, []) -> {[],A,B}.