Skip to content

Commit aae50d6

Browse files
committed
Support reraise, Add tests and fixes
1 parent d651e33 commit aae50d6

File tree

3 files changed

+205
-36
lines changed

3 files changed

+205
-36
lines changed

lib/gradient/elixir_expr.ex

Lines changed: 59 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,7 @@ defmodule Gradient.ElixirExpr do
6161
inspect(l)
6262

6363
:error ->
64-
items =
65-
pp_cons(cons)
66-
|> Enum.join(", ")
64+
items = pp_cons(cons)
6765

6866
"[" <> items <> "]"
6967
end
@@ -73,9 +71,9 @@ defmodule Gradient.ElixirExpr do
7371
"&#{name}/#{arity}"
7472
end
7573

76-
def pp_expr({:fun, _, {:function, module, name, arity}}) do
74+
def pp_expr({:fun, _, {:function, {:atom, _, module}, {:atom, _, name}, arity}}) do
7775
module = ElixirFmt.parse_module(module)
78-
name = pp_expr(name)
76+
name = Atom.to_string(name)
7977
arity = pp_expr(arity)
8078
"&#{module}#{name}/#{arity}"
8179
end
@@ -90,14 +88,28 @@ defmodule Gradient.ElixirExpr do
9088
"throw " <> pp_expr(arg)
9189
end
9290

91+
def pp_expr(
92+
{:call, _, {:remote, _, {:atom, _, :erlang}, {:atom, _, :error}},
93+
[
94+
{:call, _, {:remote, _, {:atom, _, :erlang}, {:atom, _, :raise}},
95+
[
96+
{:atom, _, :error},
97+
{:call, _, {:remote, _, {:atom, _, Kernel.Utils}, {:atom, _, :raise}}, [var]},
98+
var_stacktrace
99+
]}
100+
]}
101+
) do
102+
"reraise " <> pp_expr(var) <> ", " <> pp_expr(var_stacktrace)
103+
end
104+
93105
def pp_expr({:call, _, {:remote, _, {:atom, _, :erlang}, {:atom, _, :error}}, [arg]}) do
94106
"raise " <> pp_raise_args(arg)
95107
end
96108

97109
def pp_expr({:call, _, name, args}) do
98110
args =
99111
Enum.map(args, &pp_expr/1)
100-
|> Enum.join(" ,")
112+
|> Enum.join(", ")
101113

102114
pp_name(name) <> "(" <> args <> ")"
103115
end
@@ -166,12 +178,16 @@ defmodule Gradient.ElixirExpr do
166178

167179
def pp_expr({t, _, expr0, quantifiers}) when t in [:bc, :lc] do
168180
expr0 = pp_expr(expr0)
169-
"for #{quantifiers}, do: #{expr0}"
181+
pquantifiers = pp_expr(quantifiers)
182+
"for #{pquantifiers}, do: #{expr0}"
170183
end
171184

172185
# Quantifiers
173-
def pp_expr({type, _, pattern, expr}) when type in [:generate, :b_generate] do
174-
pp_expr(pattern) <> " <- " <> pp_expr(expr)
186+
def pp_expr({:b_generate, _, pattern, expr}) do
187+
# drop >> to insert quantifier before
188+
ppatern = String.slice(pp_expr(pattern), 0..-3)
189+
# add a space before >> for a case whan expr is a bin
190+
ppatern <> " <- " <> pp_expr(expr) <> " >>"
175191
end
176192

177193
def pp_expr({:case, _, condition, clauses} = case_expr) do
@@ -190,13 +206,14 @@ defmodule Gradient.ElixirExpr do
190206
end
191207

192208
def pp_expr({:receive, _, clauses}) do
193-
"receive" <> pp_clauses(clauses) <> "end"
209+
"receive do " <> pp_clauses(clauses) <> " end"
194210
end
195211

196-
def pp_expr({:receive, _, clauses, after_value, _after_body}) do
212+
def pp_expr({:receive, _, clauses, after_value, after_body}) do
197213
pclauses = pp_clauses(clauses)
198214
pvalue = pp_expr(after_value)
199-
"receive " <> pclauses <> "after " <> pvalue <> " -> ... end"
215+
pafter_body = pp_expr(after_body)
216+
"receive do " <> pclauses <> " after " <> pvalue <> " -> " <> pafter_body <> " end"
200217
end
201218

202219
def pp_expr({:try, _, body, else_block, catchers, after_block}) do
@@ -254,26 +271,30 @@ defmodule Gradient.ElixirExpr do
254271

255272
defp pp_catch_clause({:clause, _, [{:tuple, _, [type, var, _stacktrace]}], guards, body}) do
256273
# rescue/catch clause
257-
# FIXME support guards, support stacktrace?
258-
259-
case get_error_type(guards) do
260-
{:ok, error_type} ->
261-
# rescue
274+
case {elem(type, 2), get_error_struct(guards)} do
275+
{:error, {:ok, error_struct}} ->
276+
# rescue when error is struct
262277
{var2, body2} = get_error_var(var, body)
263278

264279
pp_expr(type) <>
265280
", %" <>
266-
pp_expr(error_type) <>
281+
pp_expr(error_struct) <>
267282
"{} = " <> pp_expr(var2) <> " -> " <> pp_expr(body2)
268283

269-
:not_found ->
284+
{:error, :not_found} ->
285+
# rescue
286+
{var2, body2} = get_error_var(var, body)
287+
288+
pp_expr(type) <>
289+
", " <> pp_expr(var2) <> " -> " <> pp_expr(body2)
290+
291+
{:throw, :not_found} ->
270292
# throw
271293
pp_expr(type) <> ", " <> pp_expr(var) <> " -> " <> pp_expr(body)
272294
end
273295
end
274296

275297
defp pp_case_clause({:clause, _, patterns, guards, body}) do
276-
# FIXME support guards
277298
patterns =
278299
patterns
279300
|> Enum.map(&pp_expr/1)
@@ -339,21 +360,28 @@ defmodule Gradient.ElixirExpr do
339360
end
340361

341362
defp maybe_try_after(res, else_block) do
342-
res <> "; after " <> pp_clauses(else_block)
363+
res <> "; after " <> pp_expr(else_block)
343364
end
344365

345-
def get_error_type([[{:op, _, :andalso, {:op, _, :==, _, error_type}, _}]]) do
346-
{:ok, error_type}
366+
def get_error_struct([[{:op, _, :andalso, {:op, _, :==, _, error_struct}, _}]]) do
367+
{:ok, error_struct}
347368
end
348369

349-
def get_error_type(_) do
370+
def get_error_struct(_) do
350371
:not_found
351372
end
352373

353374
def get_error_var({:var, _, v}, [{:match, _, user_var, {:var, _, v}} | body_tail]) do
354375
{user_var, body_tail}
355376
end
356377

378+
def get_error_var({:var, _, v}, [
379+
{:match, _, user_var, {:call, _, _, [_, {:var, _, v} | _]}} | body_tail
380+
]) do
381+
# Extract variable from Exception.normalize (used in reraise)
382+
{user_var, body_tail}
383+
end
384+
357385
def get_error_var(var, body) do
358386
{var, body}
359387
end
@@ -380,7 +408,6 @@ defmodule Gradient.ElixirExpr do
380408
defp bin_set_tsl(:default), do: ""
381409
defp bin_set_tsl([:integer]), do: ""
382410
defp bin_set_tsl([tsl]), do: Atom.to_string(tsl)
383-
defp bin_set_tsl(tsl), do: Atom.to_string(tsl)
384411

385412
def format_map_elements(elems) do
386413
atom_keys = all_keys_atoms?(elems)
@@ -429,6 +456,10 @@ defmodule Gradient.ElixirExpr do
429456
end
430457
end
431458

459+
defp pp_raise_args({:call, _, {:remote, _, error_type, {:atom, _, :exception}}, [{nil, _}]}) do
460+
pp_expr(error_type)
461+
end
462+
432463
defp pp_raise_args(
433464
{:call, _, {:remote, _, {:atom, _, RuntimeError}, {:atom, _, :exception}}, [arg]}
434465
) do
@@ -439,17 +470,13 @@ defmodule Gradient.ElixirExpr do
439470
pp_expr(error_type) <> ", " <> pp_expr(arg)
440471
end
441472

442-
defp pp_raise_args(arg) do
443-
pp_expr(arg)
444-
end
445-
446473
defp try_int_list_({nil, _}), do: []
447474
defp try_int_list_({:cons, _, {:integer, _, val}, t}), do: [val | try_int_list_(t)]
448475
defp try_int_list_(_), do: throw(nil)
449476

450-
defp pp_cons({nil, _}), do: []
451-
defp pp_cons({:var, _, _} = v), do: [pp_expr(v)]
452-
defp pp_cons({:cons, _, h, t}), do: [pp_expr(h) | pp_cons(t)]
477+
defp pp_cons({:cons, _, h, {nil, _}}), do: pp_expr(h)
478+
defp pp_cons({:cons, _, h, {:var, _, _} = v}), do: pp_expr(h) <> " | " <> pp_expr(v)
479+
defp pp_cons({:cons, _, h, t}), do: pp_expr(h) <> ", " <> pp_cons(t)
453480

454481
defp pp_name({:remote, _, {:atom, _, m}, {:atom, _, n}}),
455482
do: ElixirFmt.parse_module(m) <> to_string(n)

test/gradient/elixir_expr_test.exs

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,130 @@ defmodule Gradient.ElixirExprTest do
3232
end
3333

3434
describe "complex pretty print" do
35+
test "lambda" do
36+
actual =
37+
elixir_to_ast do
38+
fn
39+
{:ok, v} ->
40+
v
41+
42+
{:error, _} ->
43+
:error
44+
end
45+
end
46+
|> ElixirExpr.pp_expr()
47+
48+
assert "fn {:ok, v} -> v; {:error, _} -> :error end" == actual
49+
end
50+
51+
test "binary comprehension" do
52+
actual =
53+
elixir_to_ast do
54+
pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
55+
for <<r::8, g::8, b::8 <- pixels>>, do: {r, g, b}
56+
end
57+
|> ElixirExpr.pp_expr()
58+
59+
assert "pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>; for <<r::8, g::8, b::8 <- pixels >>, do: {r, g, b}" ==
60+
actual
61+
end
62+
63+
test "binary comprehension 2" do
64+
actual =
65+
elixir_to_ast do
66+
for <<one, (_rest::binary-size(3) <- <<1, 2, 3, 4>>)>>, do: one
67+
end
68+
|> ElixirExpr.pp_expr()
69+
70+
assert "for <<one, _rest::binary-size(3) <- <<1, 2, 3, 4>> >>, do: one" == actual
71+
end
72+
73+
test "receive" do
74+
actual =
75+
elixir_to_ast do
76+
receive do
77+
{:hello, msg} -> msg
78+
end
79+
end
80+
|> ElixirExpr.pp_expr()
81+
82+
assert "receive do {:hello, msg} -> msg end" == actual
83+
end
84+
85+
test "receive after" do
86+
actual =
87+
elixir_to_ast do
88+
receive do
89+
{:hello, msg} -> msg
90+
after
91+
1_000 -> "nothing happened"
92+
end
93+
end
94+
|> ElixirExpr.pp_expr()
95+
96+
assert "receive do {:hello, msg} -> msg after 1000 -> \"nothing happened\" end" == actual
97+
end
98+
99+
test "try reraise" do
100+
actual =
101+
elixir_to_ast do
102+
try do
103+
raise "ok"
104+
rescue
105+
e ->
106+
IO.puts(Exception.format(:error, e, __STACKTRACE__))
107+
reraise e, __STACKTRACE__
108+
end
109+
end
110+
|> ElixirExpr.pp_expr()
111+
112+
assert "try do raise \"ok\"; catch :error, e -> IO.puts(Exception.format(:error, e, __STACKTRACE__)); reraise e, __STACKTRACE__ end" ==
113+
actual
114+
end
115+
116+
test "try rescue without error var" do
117+
actual =
118+
elixir_to_ast do
119+
try do
120+
raise "oops"
121+
rescue
122+
RuntimeError -> "Error!"
123+
end
124+
end
125+
|> ElixirExpr.pp_expr()
126+
127+
assert "try do raise \"oops\"; catch :error, %RuntimeError{} = _ -> \"Error!\" end" ==
128+
actual
129+
end
130+
131+
test "simple rescue try" do
132+
actual =
133+
elixir_to_ast do
134+
try do
135+
:ok
136+
rescue
137+
_ -> :ok
138+
end
139+
end
140+
|> ElixirExpr.pp_expr()
141+
142+
assert "try do :ok; catch :error, _ -> :ok end" == actual
143+
end
144+
145+
test "simple after try" do
146+
actual =
147+
elixir_to_ast do
148+
try do
149+
:ok
150+
after
151+
:ok
152+
end
153+
end
154+
|> ElixirExpr.pp_expr()
155+
156+
assert "try do :ok; after :ok end" == actual
157+
end
158+
35159
test "try guard" do
36160
actual =
37161
elixir_to_ast do
@@ -54,11 +178,13 @@ defmodule Gradient.ElixirExprTest do
54178

55179
_ ->
56180
0
181+
after
182+
IO.puts("Cleaning!")
57183
end
58184
end
59185
|> ElixirExpr.pp_expr()
60186

61-
assert "try do throw \"good\"; :ok; else v when v == :ok -> :ok; v -> :nok; catch :error, %RuntimeError{} = e -> 11; e; :throw, val -> val; :throw, _ -> 0 end" ==
187+
assert "try do throw \"good\"; :ok; else v when v == :ok -> :ok; v -> :nok; catch :error, %RuntimeError{} = e -> 11; e; :throw, val -> val; :throw, _ -> 0; after IO.puts(\"Cleaning!\") end" ==
62188
actual
63189
end
64190

0 commit comments

Comments
 (0)