Skip to content

Commit 82824a8

Browse files
committed
Add tail call tail call optimization
This works by using a trampoline function defined in ElixirScript.Core.Functions JavaScript module.
1 parent 3938cc9 commit 82824a8

File tree

6 files changed

+129
-2568
lines changed

6 files changed

+129
-2568
lines changed

lib/elixir_script/passes/translate/function.ex

Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ defmodule ElixirScript.Translate.Function do
3131
:let
3232
)
3333

34-
function_dec = J.arrow_function_expression(
34+
function_recur_dec = J.function_declaration(
35+
J.identifier("recur"),
3536
[J.rest_element(J.identifier("__function_args__"))],
3637
[],
3738
J.block_statement([
@@ -49,6 +50,17 @@ defmodule ElixirScript.Translate.Function do
4950
])
5051
)
5152

53+
function_dec = J.arrow_function_expression(
54+
[J.rest_element(J.identifier("__function_args__"))],
55+
[],
56+
J.block_statement([
57+
function_recur_dec,
58+
J.return_statement(
59+
trampoline()
60+
)
61+
])
62+
)
63+
5264
{ function_dec, state }
5365
end
5466

@@ -66,8 +78,8 @@ defmodule ElixirScript.Translate.Function do
6678
:let
6779
)
6880

69-
function_dec = J.function_declaration(
70-
ElixirScript.Translate.Identifier.make_function_name(name),
81+
function_recur_dec = J.function_declaration(
82+
J.identifier("recur"),
7183
[J.rest_element(J.identifier("__function_args__"))],
7284
[],
7385
J.block_statement([
@@ -85,6 +97,18 @@ defmodule ElixirScript.Translate.Function do
8597
])
8698
)
8799

100+
function_dec = J.function_declaration(
101+
ElixirScript.Translate.Identifier.make_function_name(name),
102+
[J.rest_element(J.identifier("__function_args__"))],
103+
[],
104+
J.block_statement([
105+
function_recur_dec,
106+
J.return_statement(
107+
trampoline()
108+
)
109+
])
110+
)
111+
88112
{ function_dec, state }
89113
end
90114

@@ -132,6 +156,7 @@ defmodule ElixirScript.Translate.Function do
132156

133157
body = body
134158
|> Clause.return_last_statement
159+
|> update_last_call(state)
135160

136161
declarator = J.variable_declarator(
137162
J.array_expression(params),
@@ -168,4 +193,70 @@ defmodule ElixirScript.Translate.Function do
168193

169194
{ast, state}
170195
end
196+
197+
defp update_last_call(clause_body, %{function: {name, _}}) do
198+
last_item = List.last(clause_body)
199+
function_name = ElixirScript.Translate.Identifier.make_function_name(name)
200+
201+
case last_item do
202+
%ESTree.ReturnStatement{ argument: %ESTree.CallExpression{ callee: ^function_name, arguments: arguments } } ->
203+
new_last_item = J.return_statement(
204+
recurse(
205+
recur_bind(arguments)
206+
)
207+
)
208+
209+
List.replace_at(clause_body, length(clause_body) - 1, new_last_item)
210+
_ ->
211+
clause_body
212+
end
213+
end
214+
215+
defp recur_bind(args) do
216+
J.call_expression(
217+
J.member_expression(
218+
J.identifier("recur"),
219+
J.identifier("bind")
220+
),
221+
[J.identifier("null")] ++ args
222+
)
223+
end
224+
225+
defp recurse(func) do
226+
J.new_expression(
227+
J.member_expression(
228+
J.member_expression(
229+
J.identifier("ElixirScript"),
230+
J.member_expression(
231+
J.identifier("Core"),
232+
J.identifier("Functions")
233+
)
234+
),
235+
J.identifier("Recurse")
236+
),
237+
[
238+
func
239+
]
240+
)
241+
end
242+
243+
defp trampoline() do
244+
J.call_expression(
245+
J.member_expression(
246+
J.member_expression(
247+
J.identifier("ElixirScript"),
248+
J.member_expression(
249+
J.identifier("Core"),
250+
J.identifier("Functions")
251+
)
252+
),
253+
J.identifier("trampoline")
254+
),
255+
[
256+
recurse(
257+
recur_bind([J.rest_element(J.identifier("__function_args__"))])
258+
)
259+
]
260+
)
261+
end
171262
end

0 commit comments

Comments
 (0)