anchor.diff:          full_outer  -> " full outer join ";
           left_outer  -> " left outer join ";
           right_outer -> " right outer join "
  -    end ++
  -    foldi(Tab1) ++
  -    foldi(OptPartition1) ++
  -    foldi(OnOrUsing);
  -foldi({JoinType, Tab1, OnOrUsing})
  -when ((JoinType =:= join)
  -      orelse (JoinType =:= join_inner)) ->
  -    case JoinType of
  +    end ++ TabStr ++ OptPartition1Str ++ OnOrUsingStr
  +    , NewCtx6};
  +foldi({JoinType, Tab, OnOrUsing} = ST, Fun, Ctx)
  + when (is_function(Fun,2) andalso (
  +        (JoinType =:= join) orelse
  +        (JoinType =:= join_inner)
  +      )) ->
  +    NewCtx = Fun(ST, Ctx),
  +    NewCtx1 = Fun(JoinType, NewCtx),
  +    {TabStr, NewCtx2} = foldi(Tab, Fun, NewCtx1),
  +    {OnOrUsingStr, NewCtx3} = foldi(OnOrUsing, Fun, NewCtx2),
  +    {case JoinType of
           join        -> " join ";
           join_inner -> " inner join "
  -    end ++
  -    foldi(Tab1) ++
  -    foldi(OnOrUsing);
  -foldi({partition_by,Fields}) ->
  -    " partition by (" ++ string:join([binary_to_list(F) || F<-Fields], ",") ++ ")";
  -foldi({on, Condition}) -> " on " ++ foldi(Condition);
  -foldi({using, ColumnList}) -> " using (" ++ string:join([binary_to_list(C) || C<-ColumnList], ",") ++ ")";
  -foldi(natural) -> " natural";
  -foldi({Tab, [J|_] = Joins})
  -when (is_tuple(J) andalso (is_binary(Tab) orelse is_tuple(Tab))) ->
  -    foldi(Tab)++[foldi(Join) || Join <- Joins];
  +     end ++ TabStr ++ OnOrUsingStr
  +    , NewCtx3};
  +foldi({partition_by,Fields} = ST, Fun, Ctx)
  + when is_function(Fun,2) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {FieldsStr, NewCtx1} = lists:foldl(fun(F, {Acc, CtxAcc}) ->
  +            {Acc++[binary_to_list(F)], Fun(F, CtxAcc)}
  +        end,
  +        {[], NewCtx},
  +        Fields),
  +    {" partition by (" ++ string:join(FieldsStr, ",") ++ ")"
  +    , NewCtx1};
  +foldi({on, Condition} = ST, Fun, Ctx)
  + when is_function(Fun,2) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {CondStr, NewCtx1} = foldi(Condition, Fun, NewCtx),
  +    {" on " ++ CondStr
  +    , NewCtx1};
  +foldi({using, ColumnList} = ST, Fun, Ctx)
  + when is_function(Fun,2) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {ColumnListStr, NewCtx1} = lists:foldl(fun(C, {Acc, CtxAcc}) ->
  +            {Acc++[binary_to_list(C)], Fun(C, CtxAcc)}
  +        end,
  +        {[], NewCtx},
  +        ColumnList),
  +    {" using (" ++ string:join(ColumnListStr, ",") ++ ")"
  +    , NewCtx1};
  +foldi(natural, Fun, Ctx)
  + when is_function(Fun,2) ->
  +    {" natural", Fun(natural, Ctx)};
  +foldi({Tab, [J|_] = Joins} = ST, Fun, Ctx)
  + when is_function(Fun,2) andalso
  +        (is_tuple(J) andalso (is_binary(Tab) orelse is_tuple(Tab))) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {TabStr, NewCtx1} = foldi(Tab, Fun, NewCtx),
  +    {JoinsStr, NewCtx2} = lists:foldl(fun(Join, {Acc, CtxAcc}) ->
  +            {SubAcc, CtxAcc1} = foldi(Join, Fun, CtxAcc),
  +            {Acc++[SubAcc], CtxAcc1}
  +        end,
  +        {[], NewCtx1},
  +        Joins),
  +    {TabStr++JoinsStr
  +    , NewCtx2};
   
   % betwen operator
  -foldi({'between', A, B, C}) ->
  -    A1 = if is_binary(A) -> binary_to_list(A); true -> foldi(A) end,
  -    B1 = if is_binary(B) -> binary_to_list(B); true -> foldi(B) end,
  -    C1 = if is_binary(C) -> binary_to_list(C); true -> foldi(C) end,
  -    lists:flatten([A1,  " between ", B1, " and ", C1]);
  +foldi({'between', A, B, C} = ST, Fun, Ctx)
  + when is_function(Fun,2) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {A1, NewCtx1}  = if is_binary(A) -> {binary_to_list(A), NewCtx}; true -> foldi(A, Fun, NewCtx) end,
  +    {B1, NewCtx2} = if is_binary(B) -> {binary_to_list(B), NewCtx1}; true -> foldi(B, Fun, NewCtx1) end,
  +    {C1, NewCtx3} = if is_binary(C) -> {binary_to_list(C), NewCtx2}; true -> foldi(C, Fun, NewCtx2) end,
  +    {lists:flatten([A1,  " between ", B1, " and ", C1])
  +    , NewCtx3};
   
   % PL/SQL concatenate operator
  -foldi({'||', Args}) ->
  -    string:join(
  -    [case A of
  -        A when is_binary(A) -> binary_to_list(A);
  -        A -> foldi(A)
  -    end
  -    || A <- Args], " || ");
  +foldi({'||', Args} = ST, Fun, Ctx)
  + when is_function(Fun,2) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {ArgsStr, NewCtx1} = lists:foldl(fun(A, {Acc, CtxAcc}) ->
  +            case A of
  +                A when is_binary(A) -> {Acc++[binary_to_list(A)], Fun(A, CtxAcc)};
  +                A ->
  +                    {SubAcc, CtxAcc1} = foldi(A, Fun, CtxAcc),
  +                    {Acc++[SubAcc], CtxAcc1}
  +            end
  +        end,
  +        {[], NewCtx},
  +        Args),
  +    {string:join(ArgsStr, " || ")
  +    , NewCtx1};
   
   % All aliases
  -foldi({as, A, B}) when is_binary(A), is_binary(B) -> lists:flatten([binary_to_list(A), " ", binary_to_list(B)]);
  -foldi({as, A, B}) when is_binary(B)               -> lists:flatten([foldi(A), " ", binary_to_list(B)]);
  -foldi({as, A}) when is_binary(A)                  -> lists:flatten(["as ", binary_to_list(A)]);
  -foldi(Tab) when is_binary(Tab)                    -> binary_to_list(Tab);
  +foldi({as, A, B} = ST, Fun, Ctx)
  + when is_function(Fun,2), is_binary(A), is_binary(B) ->
  +    NewCtx = Fun(ST, Ctx),
  +    NewCtx1 = Fun(A, NewCtx),
  +    NewCtx2 = Fun(B, NewCtx1),
  +    {lists:flatten([binary_to_list(A), " ", binary_to_list(B)])
  +    , NewCtx2};
  +foldi({as, A, B} = ST, Fun, Ctx)
  +    when is_function(Fun,2), is_binary(B) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {AStr, NewCtx1} = foldi(A, Fun, NewCtx), 
  +    NewCtx2 = Fun(B, NewCtx1),
  +    {lists:flatten([AStr, " ", binary_to_list(B)])
  +    , NewCtx2};
  +foldi({as, A} = ST, Fun, Ctx)
  + when is_function(Fun,2), is_binary(A) ->
  +    NewCtx = Fun(ST, Ctx),
  +    NewCtx1 = Fun(A, NewCtx),
  +    {lists:flatten(["as ", binary_to_list(A)])
  +    , NewCtx1};
  +foldi(Tab, Fun, Ctx)
  + when is_function(Fun,2), is_binary(Tab) ->
  +    NewCtx = Fun(Tab, Ctx),
  +    {binary_to_list(Tab)
  +    , NewCtx};
   
   % Union
  -foldi({union, A, B})                              -> lists:flatten(["(", foldi(A), " union ", foldi(B), ")"]);
  +foldi({union, A, B} = ST, Fun, Ctx)
  + when is_function(Fun,2) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {AStr, NewCtx1} = foldi(A, Fun, NewCtx),
  +    {BStr, NewCtx2} = foldi(B, Fun, NewCtx1),
  +    {lists:flatten(["(", AStr, " union ", BStr, ")"])
  +    , NewCtx2};
   
   % All where clauses
  -foldi({where, {}}) -> "";
  -foldi({where, Where}) when is_tuple(Where) -> "where " ++ foldi(Where);
  +foldi({where, {}} = ST, Fun, Ctx)
  + when is_function(Fun,2) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {""
  +    , NewCtx};
  +foldi({where, Where} = ST, Fun, Ctx)
  + when is_function(Fun,2), is_tuple(Where) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {WhereStr, NewCtx1} = foldi(Where, Fun, NewCtx),
  +    {"where " ++ WhereStr
  +    , NewCtx1};
   
   % Like operator
  -foldi({'like',Var,Like,OptEsc}) ->
  -    foldi(Var) ++ " like " ++ foldi(Like) ++
  +foldi({'like',Var,Like,OptEsc} = ST, Fun, Ctx)
  + when is_function(Fun,2) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {VarStr, NewCtx1} = foldi(Var, Fun, NewCtx),
  +    {LikeStr, NewCtx2} = foldi(Like, Fun, NewCtx1),
  +    NewCtx3 = Fun(OptEsc, NewCtx2),
  +    {VarStr ++ " like " ++ LikeStr ++
       if byte_size(OptEsc) > 0 -> " escape "++binary_to_list(OptEsc);
          true -> ""
  -    end;
  +    end
  +    , NewCtx3};
   
   % In operator
   % for right hand non list argument extra parenthesis added
  -foldi({'in', L, {'list', _} = R}) when is_binary(L) ->
  -    lists:flatten([binary_to_list(L), " in ", foldi(R)]);
  -foldi({'in', L, R}) when is_binary(L), is_tuple(R) ->
  -    lists:flatten([binary_to_list(L), " in (", foldi(R), ")"]);
  +foldi({'in', L, {'list', _} = R} = ST, Fun, Ctx)
  + when is_function(Fun,2), is_binary(L) ->
  +    NewCtx = Fun(ST, Ctx),
  +    NewCtx1 = Fun(L, NewCtx),
  +    {RStr, NewCtx2} = foldi(R, Fun, NewCtx1),
  +    {lists:flatten([binary_to_list(L), " in ", RStr])
  +    , NewCtx2};
  +foldi({'in', L, R} = ST, Fun, Ctx)
  + when is_function(Fun,2), is_binary(L), is_tuple(R) ->
  +    NewCtx = Fun(ST, Ctx),
  +    NewCtx1 = Fun(L, NewCtx),
  +    {RStr, NewCtx2} = foldi(R, Fun, NewCtx1),
  +    {lists:flatten([binary_to_list(L), " in (", RStr, ")"])
  +    , NewCtx2};
   
   % Optional Returning phrase
  -foldi({R, Sel, Var}) when (R =:= return) orelse (R =:= returning) ->
  -    " " ++ atom_to_list(R)++" "
  -    ++
  -    string:join([case S of
  -            S when is_binary(S) -> binary_to_list(S);
  -            S -> foldi(S)
  -        end || S <- Sel], ",")
  +foldi({R, Sel, Var} = ST, Fun, Ctx)
  + when is_function(Fun,2) andalso (
  +        (R =:= return) orelse
  +        (R =:= returning)
  +      ) ->
  +    NewCtx = Fun(ST, Ctx),
  +    NewCtx1 = Fun(R, NewCtx),
  +    {SelStr, NewCtx2} = lists:foldl(fun(S, {Acc, CtxAcc}) ->
  +            case S of
  +                S when is_binary(S) -> {Acc++[binary_to_list(S)], Fun(S, CtxAcc)};
  +                S ->
  +                    {SubAcc, CtxAcc1} = foldi(S, Fun, CtxAcc),
  +                    {Acc++[SubAcc], CtxAcc1}
  +            end
  +        end,
  +        {[], NewCtx1},
  +        Sel),
  +    {VarStr, NewCtx3} = lists:foldl(fun({param, V}, {Acc, CtxAcc}) ->
  +            case V of
  +                V when is_binary(V) -> {Acc++[binary_to_list(V)], Fun(V, CtxAcc)};
  +                V ->
  +                    {SubAcc, CtxAcc1} = foldi(V, Fun, CtxAcc),
  +                    {Acc++[SubAcc], CtxAcc1}
  +            end
  +        end,
  +        {[], NewCtx2},
  +        Var),
  +    {" "++atom_to_list(R)++" "++string:join(SelStr, ",")
       ++ " INTO " ++
  -    string:join([case V of
  -            V when is_binary(V) -> binary_to_list(V);
  -            V -> foldi(V)
  -        end || {param, V} <- Var], ",");
  -foldi({R, {}}) when (R =:= return) orelse (R =:= returning) -> "";
  +    string:join(VarStr, ",")
  +    , NewCtx3};
  +foldi({R, {}}, Fun, Ctx)
  + when is_function(Fun,2) andalso (
  +        (R =:= return) orelse
  +        (R =:= returning)
  +      ) ->
  +    {"", Fun(R, Ctx)};
   
   % Boolean and arithmetic binary operators handled with precedence
   % *,/ > +,- > and > or
  -foldi({Op, L, R}) when is_atom(Op), is_tuple(L), is_tuple(R) ->
  -    Fl = case {Op, element(1, L)} of
  -        {'*', Ol} when Ol =:= '-'; Ol =:= '+' -> lists:flatten(["(", foldi(L), ")"]);
  -        {'/', Ol} when Ol =:= '-'; Ol =:= '+' -> lists:flatten(["(", foldi(L), ")"]);
  -        {'and', 'or'}                         -> lists:flatten(["(", foldi(L), ")"]);
  -        _ -> foldi(L)
  +foldi({Op, L, R} = ST, Fun, Ctx)
  + when is_function(Fun,2), is_atom(Op), is_tuple(L), is_tuple(R) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {Fl, NewCtx1} = case {Op, element(1, L)} of
  +        {'*', Ol} when Ol =:= '-'; Ol =:= '+' -> {Ls, NC1} = foldi(L, Fun, NewCtx), {lists:flatten(["(",Ls,")"]), NC1};
  +        {'/', Ol} when Ol =:= '-'; Ol =:= '+' -> {Ls, NC1} = foldi(L, Fun, NewCtx), {lists:flatten(["(",Ls,")"]), NC1};
  +        {'and', 'or'}                         -> {Ls, NC1} = foldi(L, Fun, NewCtx), {lists:flatten(["(",Ls,")"]), NC1};
  +        _ -> foldi(L, Fun, NewCtx)
       end,
  -    Fr = case {Op, element(1, R)} of
  -        {'*', Or} when Or =:= '-'; Or =:= '+' -> lists:flatten(["(", foldi(R), ")"]);
  -        {'/', Or} when Or =:= '-'; Or =:= '+' -> lists:flatten(["(", foldi(R), ")"]);
  -        {'and', 'or'}                         -> lists:flatten(["(", foldi(R), ")"]);
  -        _ -> foldi(R)
  +    NewCtx2 = Fun(Op, NewCtx1),
  +    {Fr, NewCtx3} = case {Op, element(1, R)} of
  +        {'*', Or} when Or =:= '-'; Or =:= '+' -> {Rs, NC2} = foldi(R, Fun, NewCtx2), {lists:flatten(["(",Rs,")"]), NC2};
  +        {'/', Or} when Or =:= '-'; Or =:= '+' -> {Rs, NC2} = foldi(R, Fun, NewCtx2), {lists:flatten(["(",Rs,")"]), NC2};
  +        {'and', 'or'}                         -> {Rs, NC2} = foldi(R, Fun, NewCtx2), {lists:flatten(["(",Rs,")"]), NC2};
  +        _ -> foldi(R, Fun, NewCtx2)
       end,
  -    lists:flatten([Fl, " ", atom_to_list(Op), " ", Fr]);
  -foldi({Op, L, R}) when is_atom(Op), is_binary(L), is_tuple(R) ->
  -    Fr = case {Op, element(1, R)} of
  -        {'*', Or} when Or =:= '-'; Or =:= '+' -> lists:flatten(["(", foldi(R), ")"]);
  -        {'/', Or} when Or =:= '-'; Or =:= '+' -> lists:flatten(["(", foldi(R), ")"]);
  -        _ -> foldi(R)
  +    {lists:flatten([Fl, " ", atom_to_list(Op), " ", Fr])
  +    , NewCtx3};
  +foldi({Op, L, R} = ST, Fun, Ctx)
  + when is_function(Fun,2), is_atom(Op), is_binary(L), is_tuple(R) ->
  +    NewCtx = Fun(ST, Ctx),
  +    NewCtx1 = Fun(L, NewCtx),
  +    NewCtx2 = Fun(Op, NewCtx1),
  +    {Fr, NewCtx3} = case {Op, element(1, R)} of
  +        {'*', Or} when Or =:= '-'; Or =:= '+' -> {Rs, NC} = foldi(R, Fun, NewCtx2), {lists:flatten(["(",Rs,")"]), NC};
  +        {'/', Or} when Or =:= '-'; Or =:= '+' -> {Rs, NC} = foldi(R, Fun, NewCtx2), {lists:flatten(["(",Rs,")"]), NC};
  +        _ -> foldi(R, Fun, NewCtx2)
       end,
  -    lists:flatten([binary_to_list(L), " ", atom_to_list(Op), " ", Fr]);
  -foldi({Op, L, R}) when is_atom(Op), is_tuple(L), is_binary(R) ->
  -    Fl = case {Op, element(1, L)} of
  -        {'*', Ol} when Ol =:= '-'; Ol =:= '+' -> lists:flatten(["(", foldi(L), ")"]);
  -        {'/', Ol} when Ol =:= '-'; Ol =:= '+' -> lists:flatten(["(", foldi(L), ")"]);
  -        _ -> foldi(L)
  +    {lists:flatten([binary_to_list(L), " ", atom_to_list(Op), " ", Fr])
  +    , NewCtx3};
  +foldi({Op, L, R} = ST, Fun, Ctx)
  + when is_function(Fun,2), is_atom(Op), is_tuple(L), is_binary(R) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {Fl, NewCtx1} = case {Op, element(1, L)} of
  +        {'*', Ol} when Ol =:= '-'; Ol =:= '+' -> {Ls, NC} = foldi(L, Fun, NewCtx), {lists:flatten(["(",Ls,")"]), NC};
  +        {'/', Ol} when Ol =:= '-'; Ol =:= '+' -> {Ls, NC} = foldi(L, Fun, NewCtx), {lists:flatten(["(",Ls,")"]), NC};
  +        _ -> foldi(L, Fun, NewCtx)
       end,
  -    lists:flatten([Fl, " ", atom_to_list(Op), " ", binary_to_list(R)]);
  -foldi({Op, L, R}) when is_atom(Op), is_binary(L), is_binary(R) ->    lists:flatten([binary_to_list(L), " ", atom_to_list(Op), " ", binary_to_list(R)]);
  +    NewCtx2 = Fun(Op, NewCtx1),
  +    NewCtx3 = Fun(R, NewCtx2),
  +    {lists:flatten([Fl, " ", atom_to_list(Op), " ", binary_to_list(R)])
  +    , NewCtx3};
  +foldi({Op, L, R} = ST, Fun, Ctx)
  + when is_function(Fun,2), is_atom(Op), is_binary(L), is_binary(R) ->
  +    NewCtx = Fun(ST, Ctx),
  +    NewCtx1 = Fun(L, NewCtx),
  +    NewCtx2 = Fun(Op, NewCtx1),
  +    NewCtx3 = Fun(R, NewCtx2),
  +    {lists:flatten([binary_to_list(L), " ", atom_to_list(Op), " ", binary_to_list(R)])
  +    , NewCtx3};
   
   % Unary - and 'not' operators
  -foldi({Op, A}) when Op =:= '-'; Op =:= 'not' ->
  +foldi({Op, A} = ST, Fun, Ctx)
  + when is_function(Fun,2) andalso (Op =:= '-' orelse Op =:= 'not') ->
  +    NewCtx = Fun(ST, Ctx),
  +    NewCtx1 = Fun(Op, NewCtx),
       case A of
  -        A when is_binary(A) -> lists:flatten([atom_to_list(Op), " (", binary_to_list(A), ")"]);
  -        A                   -> lists:flatten([atom_to_list(Op)," (", foldi(A), ")"])
  +        A when is_binary(A) ->
  +            NewCtx2 = Fun(A, NewCtx1),
  +            {lists:flatten([atom_to_list(Op), " (", binary_to_list(A), ")"])
  +            , NewCtx2};
  +        A ->
  +            {As, NewCtx2} = foldi(A, Fun, NewCtx1),
  +            {lists:flatten([atom_to_list(Op)," (", As, ")"])
  +            , NewCtx2}
       end;
   
   % funs
  -foldi({'fun', N, Args}) when is_binary(N) ->
  -    binary_to_list(N) ++ "(" ++
  -    string:join(
  -        [case A of
  -            A when is_binary(A) -> binary_to_list(A);
  -            A when is_tuple(A) ->
  -                case lists:member(element(1, A), [ 'select', 'insert', 'create table'
  -                                                 , 'create user', 'alter user'
  -                                                 , 'truncate table', 'update', 'delete'
  -                                                 , 'grant', 'revoke']) of
  -                    true -> "(" ++ string:strip(foldi(A)) ++ ")";
  -                    _ -> foldi(A)
  -                end;
  -            A -> foldi(A)
  -        end
  -        || A <- Args]
  -    , ", ")
  -    ++ ")";
  +foldi({'fun', N, Args} = ST, Fun, Ctx)
  + when is_function(Fun,2), is_binary(N) ->
  +    NewCtx = Fun(ST, Ctx),
  +    NewCtx1 = Fun(N, NewCtx),
  +    {ArgsStr, NewCtx2} = lists:foldl(fun(A, {Acc, CtxAcc}) ->
  +            case A of
  +                A when is_binary(A) -> {Acc++[binary_to_list(A)], Fun(A, CtxAcc)};
  +                A when is_tuple(A) ->
  +                    case lists:member(element(1, A), [ 'select', 'insert', 'create table'
  +                                                     , 'create user', 'alter user'
  +                                                     , 'truncate table', 'update', 'delete'
  +                                                     , 'grant', 'revoke']) of
  +                        true ->
  +                            {SubAcc, CtxAcc1} = foldi(A, Fun, CtxAcc),
  +                            {Acc++["(" ++ string:strip(SubAcc) ++ ")"], CtxAcc1};
  +                        _ ->
  +                            {SubAcc, CtxAcc1} = foldi(A, Fun, CtxAcc),
  +                            {Acc++[SubAcc], CtxAcc1}
  +                    end;
  +                A ->
  +                    {SubAcc, CtxAcc1} = foldi(A, Fun, CtxAcc),
  +                    {Acc++[SubAcc], CtxAcc1}
  +            end
  +        end,
  +        {[], NewCtx1},
  +        Args),
  +    {binary_to_list(N) ++ "(" ++
  +    string:join(ArgsStr, ", ")
  +    ++ ")"
  +    , NewCtx2};
   
   % hierarchical query
  -foldi({'hierarchical query', {}}) -> "";
  -foldi({'hierarchical query', {Part1, Part2}}) -> lists:flatten([foldi(Part1), " ", foldi(Part2)]);
  -foldi({'start with', StartWith}) -> lists:flatten([" start with ", foldi(StartWith)]);
  -foldi({'connect by', NoCycle, ConnectBy}) ->
  -    lists:flatten(["connect by "
  -                  , if byte_size(NoCycle) > 0 -> foldi(NoCycle)++" "; true -> "" end
  -                  , foldi(ConnectBy)]);
  -foldi({'prior', Field}) -> lists:flatten(["prior ", foldi(Field)]);
  +foldi({'hierarchical query', {}} = ST, Fun, Ctx)
  + when is_function(Fun,2) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {"", NewCtx};
  +foldi({'hierarchical query', {Part1, Part2}} = ST, Fun, Ctx)
  + when is_function(Fun,2) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {Part1Str, NewCtx1} = foldi(Part1, Fun, NewCtx),
  +    {Part2Str, NewCtx2} = foldi(Part2, Fun, NewCtx1),
  +    {lists:flatten([Part1Str, " ", Part2Str])
  +    , NewCtx2};
  +foldi({'start with', StartWith} = ST, Fun, Ctx)
  + when is_function(Fun,2) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {StartWithStr, NewCtx1} = foldi(StartWith, Fun, NewCtx),
  +    {lists:flatten([" start with ", StartWithStr])
  +    , NewCtx1};
  +foldi({'connect by', NoCycle, ConnectBy} = ST, Fun, Ctx)
  + when is_function(Fun,2) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {NoCycleStr, NewCtx1} = foldi(NoCycle, Fun, NewCtx),
  +    {ConnectByStr, NewCtx2} = foldi(ConnectBy, Fun, NewCtx1),
  +    {lists:flatten(["connect by "
  +                  , if byte_size(NoCycle) > 0 -> NoCycleStr++" "; true -> "" end
  +                  , ConnectByStr])
  +    , NewCtx2};
  +foldi({'prior', Field} = ST, Fun, Ctx)
  + when is_function(Fun,2) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {FieldsStr, NewCtx1} = foldi(Field, Fun, NewCtx),
  +    {lists:flatten(["prior ", FieldsStr])
  +    , NewCtx1};
   
   % lists
  -foldi({'list', Elms}) ->
  -    "(" ++
  -    string:join(
  -        [case E of
  -            E when is_binary(E) -> binary_to_list(E);
  -            E -> foldi(E)
  -        end
  -        || E <- Elms]
  -    , ", ")
  -    ++ ")";
  -
  -foldi({'param', P}) ->
  +foldi({'list', Elms} = ST, Fun, Ctx)
  + when is_function(Fun,2) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {ElmsStr, NewCtx1} = lists:foldl(fun(E, {Acc, CtxAcc}) ->
  +            case E of
  +                E when is_binary(E) -> {Acc++[binary_to_list(E)], Fun(E, CtxAcc)};
  +                E ->
  +                    {SubAcc, CtxAcc1} = foldi(E, Fun, CtxAcc),
  +                    {Acc++[SubAcc], CtxAcc1}
  +            end
  +        end,
  +        {[], NewCtx},
  +        Elms),
  +    {"(" ++
  +     string:join(ElmsStr, ", ")
  +     ++ ")"
  +    , NewCtx1};
  +
  +foldi({'param', P} = ST, Fun, Ctx)
  + when is_function(Fun,2) ->
  +    NewCtx = Fun(ST, Ctx),
  +    NewCtx1 = Fun(P, NewCtx),
       case P of
  -        P when is_binary(P) -> binary_to_list(P);
  -        P -> P
  +        P when is_binary(P) -> {binary_to_list(P), NewCtx1};
  +        P -> {P, NewCtx1}
       end;
   
  -foldi({'case', When, Then, Else}) ->
  -    "case when " ++ foldi(When) ++
  -    " then " ++ foldi(Then) ++
  -    case Else of
  -        {} -> "";
  -        Else -> " else " ++ foldi(Else)
  -    end ++
  -    " end";
  +foldi({'case', When, Then, Else} = ST, Fun, Ctx)
  + when is_function(Fun,2) ->
  +    NewCtx = Fun(ST, Ctx),
  +    {WhenStr, NewCtx1} = foldi(When, Fun, NewCtx),
  +    {ThenStr, NewCtx2} = foldi(Then, Fun, NewCtx1),
  +    {ElseStr, NewCtx3} = case Else of
  +        {} -> {"", NewCtx2};
  +        Else ->
  +            {EStr, NewCtx21} = foldi(Else, Fun, NewCtx2),
  +            {" else " ++ EStr, NewCtx21}
  +    end,
  +    {"case when " ++WhenStr++" then "++ThenStr++ElseStr++" end"
  +    , NewCtx3};
   
   %
   % UNSUPPORTED
   %
  -foldi(PTree) ->
  +foldi(PTree, Fun, Ctx)
  + when is_function(Fun,2) ->
  +    Fun(PTree, Ctx),
       io:format(user, "Parse tree not supported ~p~n", [PTree]),
       throw({"Parse tree not supported",PTree}).
   
  
positive.code:         {ok, {[{ParseTree,_}|_], Tokens}} -> 
              if ShowParseTree ->
          	    io:format(user, "~p~n", [ParseTree]),
                  NSql = foldi(ParseTree),
                  io:format(user,  "~n> " ++ NSql ++ "~n", []),
                  {ok, {[{NPTree,_}|_], NToks}} = sqlparse:parsetree(NSql),
                  try
  
negative.code: -export([init/1]).
  
  % parser and compiler interface
  -export([fold/1, fold/3, parsetree/1, is_reserved/1]).
  
  -ifdef(TEST).
  % eunit helper function
  