Current section

Files

Jump to
efene src fn_pp.erl
Raw

src/fn_pp.erl

%% Copyright 2015 Mariano Guerra
%%
%% 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.
-module(fn_pp).
-export([print/1]).
-include("efene.hrl").
ind(0) -> "";
ind(1) -> " ";
ind(2) -> " ";
ind(3) -> " ";
ind(4) -> " ";
ind(Indent) -> string:copies(" ", Indent).
fmt(Str, Fmt, true, Indent, Args) -> [Str, ind(Indent), io_lib:format(Fmt, Args), "\n"];
fmt(Str, Fmt, false, Indent, Args) -> [Str, ind(Indent), io_lib:format(Fmt, Args)].
print(Nodes) when is_list(Nodes) -> print(Nodes, "", true, 0, [], "\n");
print(Node) -> print_single(Node).
%print(Nodes, Indent) -> print(Nodes, "", true, Indent, [], "\n").
print_single(Node) -> print(Node, "", false, 0).
print_single(Node, Indent) -> string:strip(lists:flatten(print(Node, "", true, Indent)), left).
print(Nodes, Str, Nl, Indent) when is_list(Nodes) -> print(Nodes, Str, Nl, Indent, [], "");
print({attr, _L, Path, noparams, noresult}, Str, Nl, Indent) ->
fmt(Str, "@~s", Nl, Indent, [print_path(Path)]);
print({attr, _L, Path, Params, noresult}, Str, Nl, Indent) ->
fmt(Str, "@~s(~s)", Nl, Indent, [print_path(Path), print_seq(Params)]);
print({attr, _L, Path, noparams, Result}, Str, Nl, Indent) ->
fmt(Str, "@~s -> ~s", Nl, Indent, [print_path(Path), print_single(Result)]);
print({attr, _L, Path, Params, Result}, Str, Nl, Indent) ->
fmt(Str, "@~s(~s) -> ~s", Nl, Indent, [print_path(Path), print_seq(Params), print_single(Result)]);
print(?E(_L, call_do, {first, Call, Fun}), Str, Nl, Indent) ->
fmt(Str, "~s <- ~s", Nl, Indent, [print(Call), print(Fun)]);
print(?E(_L, call_do, {last, Call, Fun}), Str, Nl, Indent) ->
fmt(Str, "~s <<- ~s", Nl, Indent, [print(Call), print(Fun)]);
print(?E(_L, call_thread, {InitialVal, Calls}), Str, Nl, Indent) ->
fmt(Str, "~s~n~s", Nl, Indent,
[print_single(InitialVal), print_thread_calls(Calls, Indent + 1)]);
print(?S(_L, list, Val), Str, Nl, Indent) ->
fmt(Str, "[~s]", Nl, Indent, [print_seq(Val)]);
print(?S(_L, map, {Var, KVs}), Str, Nl, Indent) ->
fmt(Str, "~s#{~s}", Nl, Indent, [print_single(Var), print_kvs(KVs)]);
print(?S(_L, map, KVs), Str, Nl, Indent) ->
fmt(Str, "{~s}", Nl, Indent, [print_kvs(KVs)]);
print(?LTag(_L, [?Atom(r), ?Atom(RecordName)], ?S(_MapLine, map, {Var, KVs})), Str, Nl, Indent) ->
fmt(Str, "#r.~p ~s#{~s}", Nl, Indent, [RecordName, print_single(Var), print_kvs(KVs)]);
print(?LTag(_L, [?Atom(r), ?Atom(RecordName)], ?S(_MapLine, map, KVs)), Str, Nl, Indent) ->
fmt(Str, "#r.~p {~s}", Nl, Indent, [RecordName, print_kvs(KVs)]);
print(?LTag(_L, [?Atom(c)], ?V(_StrLine, string, [Char])), Str, Nl, Indent) ->
fmt(Str, "#c \"~c\"", Nl, Indent, [Char]);
print(?LTag(_L, [?Atom(atom)], ?V(_StrLine, string, AtomStr)), Str, Nl, Indent) ->
fmt(Str, "#atom ~s", Nl, Indent, [escape_string(AtomStr)]);
print(?LTag(_L, Path, Val), Str, Nl, Indent) ->
fmt(Str, "#~s ~s", Nl, Indent, [print_path(Path), print_single(Val, Indent)]);
print(?T(_L, Path, Val), Str, Nl, Indent) ->
fmt(Str, "^~s ~s", Nl, Indent, [print_path(Path), print_single(Val, Indent)]);
print({kv, _L, Key, Val}, Str, Nl, Indent) ->
fmt(Str, "~s: ~s", Nl, Indent, [print_single(Key), print_single(Val)]);
print({kvmatch, _L, Key, Val}, Str, Nl, Indent) ->
fmt(Str, "~s = ~s", Nl, Indent, [print_single(Key), print_single(Val)]);
print(?S(_L, tuple, []), Str, Nl, Indent) ->
fmt(Str, "()", Nl, Indent, []);
print(?S(_L, tuple, [Item]), Str, Nl, Indent) ->
fmt(Str, "(~s,)", Nl, Indent, [print_single(Item)]);
print(?S(_L, tuple, Val), Str, Nl, Indent) ->
fmt(Str, "(~s)", Nl, Indent, [print_seq(Val)]);
print(?S(_L, cons, {H, T}), Str, Nl, Indent) ->
fmt(Str, "[~s::~s]", Nl, Indent, [print_single(H), print_single(T)]);
print(?V(_L, fn_ref, {[Mod, Fun], Arity}), Str, Nl, Indent) ->
fmt(Str, "fn ~s.~s:~s", Nl, Indent, [print_single(Mod), print_single(Fun), print_single(Arity)]);
print(?V(_L, fn_ref, {[Fun], Arity}), Str, Nl, Indent) ->
fmt(Str, "fn ~s:~s", Nl, Indent, [print_single(Fun), print_single(Arity)]);
print(?E(_L, 'when', Clauses), Str, _Nl, Indent) ->
fmt(Str, "when ~s~send", true, Indent, [print_wclauses(Clauses, Indent),
ind(Indent)]);
print({wcond, _L, Cond, Body}, Str, _Nl, Indent) ->
fmt(Str, "else ~s:~n~s", false, Indent, [print_guard(Cond), print_body(Body, Indent + 1)]);
print({welse, _L, Body}, Str, _Nl, Indent) ->
fmt(Str, "else:~n~s", false, Indent, [print_body(Body, Indent + 1)]);
print(?E(_L, 'for', {Qualifiers, Body}), Str, _Nl, Indent) ->
fmt(Str, "for~s:~n~s~send", true, Indent,
[print_qualifiers(Qualifiers, Indent + 1),
print_body(Body, Indent + 1), ind(Indent)]);
print(?E(_L, 'try', {Body, Catch, After}), Str, Nl, Indent) ->
fmt(Str, "try~n~s~s~s~send", true, Indent, [print_body(Body, Indent + 1),
print_catch(Catch, Nl, Indent),
print_after(After, Indent),
ind(Indent)]);
print(?E(_L, 'receive', {?E(_CLine, 'case', Clauses), After}), Str, Nl, Indent) ->
fmt(Str, "receive~n~s~s~send", true, Indent, [print_clauses(Clauses, Nl, Indent + 1),
print_after(After, Indent),
ind(Indent)]);
print(?E(_L, switch, {Value, ?E(_CaseLine, 'case', Clauses)}), Str, Nl, Indent) ->
fmt(Str, "match ~s:~n~s~send", true, Indent, [print_single(Value),
print_clauses(Clauses, Nl, Indent + 1),
ind(Indent)]);
print({cmatch, _L, {[], nowhen, Body}}, Str, Nl, Indent) ->
fmt(Str, "case:~n~s", false, Indent, [print(Body, "", Nl, Indent + 1)]);
print({cmatch, _L, {Conds, When, Body}}, Str, Nl, Indent) ->
fmt(Str, "case ~s~s:~n~s", false, Indent,
[print_conds(Conds), print_when(When), print(Body, "", Nl, Indent + 1)]);
print({celse, _L, Body}, Str, _Nl, Indent) ->
fmt(Str, "else:~n~s", false, Indent, [print_body(Body, Indent + 1)]);
print(?E(_L, 'begin', Body), Str, _Nl, Indent) ->
fmt(Str, "begin~n~s~send", true, Indent,
[print_body(Body, Indent + 1), ind(Indent)]);
print(?E(_L, fn, {Name, [], ?E(_CLine, 'case', Cases)}), Str, Nl, Indent) ->
fmt(Str, "fn ~s~n~s~send", true, Indent,
[print_single(Name), print_clauses(Cases, Nl, Indent + 1), ind(Indent)]);
print(?E(_L, fn, {Name, Attrs, ?E(_CLine, 'case', Cases)}), Str, Nl, Indent) ->
fmt(Str, "fn ~s~n~s~n~s~send", true, Indent,
[print_single(Name), print(Attrs, "", Nl, Indent + 1),
print_clauses(Cases, Nl, Indent + 1), ind(Indent)]);
print(?E(_L, fn, ?E(_CLine, 'case', Cases)), Str, Nl, Indent) ->
fmt(Str, "fn~n~s~send", true, Indent,
[print_clauses(Cases, Nl, Indent + 1), ind(Indent)]);
print(?E(_L, fn, {?V(_VLine, var, _RawName)=Name, ?E(_CLine, 'case', Cases)}), Str, Nl, Indent) ->
fmt(Str, "fn ~s~n~s~send", true, Indent,
[print_single(Name), print_clauses(Cases, Nl, Indent + 1), ind(Indent)]);
print(?E(_L, call, {[Mod, Fun], Args}), Str, Nl, Indent) ->
fmt(Str, "~s.~s(~s)", Nl, Indent,
[print_single(Mod), print_single(Fun), print_seq(Args)]);
print(?E(_L, call, {[Fun], Args}), Str, Nl, Indent) ->
fmt(Str, "~s(~s)", Nl, Indent, [print_single(Fun), print_seq(Args)]);
print(?O(_L, Op, Left, Right), Str, Nl, Indent) ->
WrapLeft = should_wrap(Op, left, Left),
WrapRight = should_wrap(Op, right, Right),
IndentRight = should_indent_right(Right),
RightStr = if IndentRight -> print_single(Right, Indent);
true -> print_single(Right)
end,
LeftFmt = if WrapLeft -> "(~s)"; true -> "~s" end,
RightFmt = if WrapRight -> "(~s)"; true -> "~s" end,
Format = LeftFmt ++ " ~s " ++ RightFmt,
fmt(Str, Format, Nl, Indent,
[print_single(Left), atom_to_list(Op), RightStr]);
print(?V(_L, atom, Val), Str, Nl, Indent) ->
AtomStr = atom_to_list(Val),
[FirstChar|_] = AtomStr,
IsSpecialChar = fun (C) when C >= $a, C =< $z; C >= $0, C =< $9;C == $_ -> false;
(_) -> true
end,
IsReserved = fn_lexer:is_reserved(AtomStr),
HasSpecialChar = ((FirstChar >= $0 andalso FirstChar =< $9) orelse
lists:any(IsSpecialChar, AtomStr)),
ShouldQuote = IsReserved orelse HasSpecialChar,
if ShouldQuote ->
fmt(Str, "`~s`", Nl, Indent, [AtomStr]);
true ->
fmt(Str, "~s", Nl, Indent, [AtomStr])
end;
print(?V(_L, integer, Val), Str, Nl, Indent) ->
fmt(Str, "~p", Nl, Indent, [Val]);
print(?V(_L, float, Val), Str, Nl, Indent) ->
fmt(Str, "~p", Nl, Indent, [Val]);
print(?V(_L, boolean, Val), Str, Nl, Indent) ->
fmt(Str, "~s", Nl, Indent, [atom_to_list(Val)]);
print(?V(_L, var, Val), Str, Nl, Indent) ->
fmt(Str, "~s", Nl, Indent, [atom_to_list(Val)]);
print(?V(_L, string, Val), Str, Nl, Indent) ->
fmt(Str, "~s", Nl, Indent, [escape_string(Val)]);
print(?V(_L, bstring, Val), Str, Nl, Indent) ->
fmt(Str, "~s", Nl, Indent, [escape_bstring(Val)]);
print(?UO(_L, Op, Val), Str, Nl, Indent) ->
Fmt = if Op == 'not' -> "~s ~s";
true -> "~s~s"
end,
fmt(Str, Fmt, Nl, Indent, [atom_to_list(Op), print_single(Val)]);
print(Ast, Str, Nl, Indent) ->
fmt(Str, "(INVALID: ~p)", Nl, Indent, [Ast]).
print([], Str, _Nl, _Indent, _Accum, _Sep) ->
Str;
print([H], Str, Nl, Indent, _Accum, _Sep) ->
print(H, Str, Nl, Indent);
print([H|T], Str, Nl, Indent, Accum, Sep) ->
NewStr = [print(H, Str, Nl, Indent), Sep],
print(T, NewStr, Nl, Indent, Accum, Sep).
print_kvs(Items) ->
print(Items, "", false, 0, [], ", ").
print_seq(Items) ->
print(Items, "", false, 0, [], ", ").
print_path(Items) ->
print(Items, "", false, 0, [], ".").
print_wclauses([{wcond, _L, Cond, Body}|Clauses], Indent) ->
[print_guard(Cond), ":\n", print_body(Body, Indent + 1), print(Clauses, "", true, Indent, [], "")].
print_catch(nocatch, _Nl, _Indent) -> "";
print_catch(?E(_CLine, 'case', Cases), Nl, Indent) ->
fmt("", "catch~n~s", false, Indent, [print_clauses(Cases, Nl, Indent + 1)]).
print_after(noafter, _Indent) -> "";
print_after({Timeout, Body}, Indent) ->
fmt("", "after ~s:~n~s", false, Indent, [print_single(Timeout),
print_body(Body, Indent + 1)]);
print_after(Body, Indent) ->
fmt("", "after:\n~s", false, Indent, [print_body(Body, Indent + 1)]).
print_clauses(Clauses, Nl, Indent) ->
print(Clauses, "", Nl, Indent, [], "").
print_conds(Conds) ->
print_seq(Conds).
print_body(Body, Indent) -> print(Body, "", true, Indent).
print_guard(Guard) ->
string:join(lists:map(fun print_seq/1, Guard), "; ").
print_when(nowhen) -> "";
print_when(When) ->
fmt("", " when ~s", false, 0, [print_guard(When)]).
print_thread_call({first, Call}, Indent) ->
fmt("", "-> ~s", true, Indent, [print_single(Call)]);
print_thread_call({last, Call}, Indent) ->
fmt("", "->> ~s", true, Indent, [print_single(Call)]).
print_thread_calls(Calls, Indent) ->
lists:map(fun (TCall) -> print_thread_call(TCall, Indent) end, Calls).
print_qualifier({filter, Filter}, Indent) ->
[ind(Indent), print_single(Filter)];
print_qualifier({generate, _Line, Left, Right}, Indent) ->
fmt("", "~s in ~s", false, Indent, [print_single(Left), print_single(Right)]);
print_qualifier({bgenerate, _Line, Left, Right}, Indent) ->
fmt("", "~s <- ~s", false, Indent, [print_single(Left), print_single(Right)]).
print_qualifiers(Qs, _Indent) ->
StrQs = lists:map(fun (Q) -> print_qualifier(Q, 0) end, Qs),
[" ", string:join(StrQs, "; ")].
precedence('~') -> {left, 300};
precedence('not') -> {left, 300};
precedence('andd') -> {left, 400};
precedence('&') -> {left, 400};
precedence('*') -> {left, 400};
precedence('/') -> {left, 400};
precedence('//') -> {left, 400};
precedence('%') -> {left, 400};
precedence('orr') -> {left, 500};
precedence('+') -> {left, 500};
precedence('-') -> {left, 500};
precedence('|') -> {left, 500};
precedence('^') -> {left, 500};
precedence('xor') -> {left, 500};
precedence('>>') -> {left, 500};
precedence('<<') -> {left, 500};
precedence('++') -> {right, 600};
precedence('--') -> {right, 600};
precedence('<') -> {left, 700};
precedence('<=') -> {left, 700};
precedence('>') -> {left, 700};
precedence('>=') -> {left, 700};
precedence('==') -> {left, 700};
precedence('is') -> {left, 700};
precedence('!=') -> {left, 700};
precedence('isnt') -> {left, 700};
precedence('and') -> {left, 800};
precedence('or') -> {left, 900};
precedence('!') -> {right, 1000};
precedence('=') -> {right, 1000}.
should_wrap(Op, Pos, ?O(_Line, SubOp, _L, _R)) ->
{OpAssoc, OpPrec} = precedence(Op),
{SubOpAssoc, SubOpPrec} = precedence(SubOp),
% TODO: this for sure is wrong
case {Pos, OpAssoc, SubOpAssoc} of
_ when OpPrec < SubOpPrec -> true;
{left, left, _} when OpPrec == SubOpPrec -> true;
{right, right, _} when OpPrec == SubOpPrec -> true;
_ -> false
end;
should_wrap(_Op, _Pos, _Sub) -> false.
should_indent_right(?E(_L, call, _)) -> false;
should_indent_right(?E(_L, call_do, _)) -> false;
should_indent_right(?E(_L, call_thread, _)) -> false;
should_indent_right(?E(_L, _, _)) -> true;
should_indent_right(_) -> false.
escape_string(Str) -> io_lib:write_string(Str, $").
escape_bstring(Str) -> io_lib:write_string(Str, $').