Skip to content

Commit 5b01f81

Browse files
committed
Add module state to track module references
1 parent 55e2618 commit 5b01f81

File tree

8 files changed

+82
-47
lines changed

8 files changed

+82
-47
lines changed

lib/elixir_script/experimental/backend.ex

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
11
defmodule ElixirScript.Experimental.Backend do
22
alias ElixirScript.Experimental.Module
33
alias ESTree.Tools.Generator
4+
alias ElixirScript.Experimental.ModuleState
45

56
def compile(line, file, module, attrs, defs, unreachable, opts) do
6-
77
# Print all arguments
8-
IO.inspect binding()
8+
#IO.inspect binding()
99

1010
# Compile module to JavaScript AST
1111
js_ast = Module.compile(line, file, module, attrs, defs, unreachable, opts)
1212

1313
# Generate JavaScript code string
1414
js_code = Generator.generate(js_ast)
1515

16-
IO.puts js_code
16+
#IO.puts js_code
1717

1818
write_js(module, js_code)
19-
2019
# Invoke the default backend - it returns the compiled beam binary
2120
:elixir_erl.compile(line, file, module, attrs, defs, unreachable, opts)
2221
end

lib/elixir_script/experimental/example.ex renamed to lib/elixir_script/experimental/examples/example.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ defmodule Example do
44
defstruct [:name]
55

66
def new() do
7+
JS.Map.new()
78
Hello.hi()
89
end
910

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
defprotocol Example.Size do
2+
@compile {:undocumented_elixir_backend_option, ElixirScript.Experimental.Backend}
3+
def size(data)
4+
end
5+
6+
defimpl Example.Size, for: BitString do
7+
@compile {:undocumented_elixir_backend_option, ElixirScript.Experimental.Backend}
8+
9+
def size(string), do: byte_size(string)
10+
end
11+
12+
defimpl Example.Size, for: Map do
13+
@compile {:undocumented_elixir_backend_option, ElixirScript.Experimental.Backend}
14+
15+
def size(map), do: map_size(map)
16+
end
17+
18+
defimpl Example.Size, for: Tuple do
19+
@compile {:undocumented_elixir_backend_option, ElixirScript.Experimental.Backend}
20+
21+
def size(tuple), do: tuple_size(tuple)
22+
end

lib/elixir_script/experimental/forms/call.ex

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,35 @@ defmodule ElixirScript.Experimental.Forms.Call do
22
alias ESTree.Tools.Builder, as: J
33
alias ElixirScript.Experimental.Form
44
alias ElixirScript.Translator.Identifier
5+
alias ElixirScript.Experimental.ModuleState
56

67
def compile({{:., _, [module, function]}, _, params}) do
8+
function_name = if ElixirScript.Experimental.Module.is_js_module(module) do
9+
ElixirScript.Translator.Identifier.make_extern_function_name(function)
10+
else
11+
ElixirScript.Translator.Identifier.make_function_name(function, length(params))
12+
end
13+
714
J.call_expression(
815
J.member_expression(
916
process_module_name(module),
10-
ElixirScript.Translator.Identifier.make_function_name(function, length(params))
17+
function_name
1118
),
1219
Enum.map(params, &Form.compile(&1))
1320
)
1421
end
1522

1623
defp process_module_name(module) when is_atom(module) do
17-
if ElixirScript.Experimental.Module.is_elixir_module(module) do
18-
members = ["Elixir"] ++ Module.split(module)
19-
J.identifier(Enum.join(members, "_"))
20-
else
21-
ElixirScript.Translator.Identifier.make_identifier(module)
24+
cond do
25+
ElixirScript.Experimental.Module.is_js_module(module) ->
26+
members = tl(Module.split(module))
27+
Identifier.make_namespace_members(members)
28+
ElixirScript.Experimental.Module.is_elixir_module(module) ->
29+
ModuleState.put_module_ref(module)
30+
members = ["Elixir"] ++ Module.split(module)
31+
J.identifier(Enum.join(members, "_"))
32+
true ->
33+
ElixirScript.Translator.Identifier.make_identifier(module)
2234
end
2335
end
2436

lib/elixir_script/experimental/module.ex

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ defmodule ElixirScript.Experimental.Module do
22
alias ESTree.Tools.Builder, as: J
33
alias ElixirScript.Experimental.Function
44
alias ElixirScript.Translator.Identifier
5+
alias ElixirScript.Experimental.ModuleState
56

67
@moduledoc """
78
Upper level module that handles compilation
89
"""
910

1011
def compile(_line, _file, module, attrs, defs, unreachable, opts) do
12+
ModuleState.start_link(module)
13+
1114
reachable_defs = Enum.filter(defs, fn
1215
{ name, _, _, _} -> not(name in unreachable)
1316
{ _, type, _, _} when type in [:defmacro, :defmacrop] -> false
@@ -17,9 +20,10 @@ defmodule ElixirScript.Experimental.Module do
1720
compiled_functions = reachable_defs
1821
|> Enum.map(&Function.compile(&1))
1922

20-
imports = make_imports(reachable_defs)
23+
imports = make_imports()
2124
exports = make_exports(reachable_defs)
2225

26+
ModuleState.stop()
2327
J.program(imports ++ compiled_functions ++ [J.export_default_declaration(exports)])
2428
end
2529

@@ -35,39 +39,8 @@ defmodule ElixirScript.Experimental.Module do
3539
J.object_expression(exports)
3640
end
3741

38-
defp make_imports(reachable_defs) do
39-
imports = Enum.reduce(reachable_defs, [], fn
40-
{{name, arity}, type, _, clauses}, list when type in [:def, :defp] ->
41-
imports = search_for_imports(clauses)
42-
list ++ imports
43-
_, list ->
44-
list
45-
end)
46-
47-
imports
48-
end
49-
50-
defp search_for_imports(clauses) do
51-
imports = Enum.map(clauses, fn(clause) ->
52-
53-
# Walk the AST and try to find module references
54-
# We will turn these into imports
55-
{ast, list} = Macro.postwalk(clause, [], fn
56-
{_, _, _, {{:., _, [module, _]}, _, _}} = ast, list ->
57-
if is_elixir_module(module) do
58-
{ast, list ++ [module_to_import(module)]}
59-
else
60-
{ast, list}
61-
end
62-
ast, list ->
63-
{ ast, list }
64-
end)
65-
66-
list
67-
68-
end)
69-
70-
List.flatten(imports) |> Enum.uniq
42+
defp make_imports() do
43+
Enum.map(ModuleState.get_module_refs(), fn(x) -> module_to_import(x) end)
7144
end
7245

7346
defp module_to_import(module) do
@@ -94,4 +67,8 @@ defmodule ElixirScript.Experimental.Module do
9467
false
9568
end
9669

70+
def is_js_module(module) do
71+
is_elixir_module(module) and hd(Module.split(module)) == "JS"
72+
end
73+
9774
end
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
defmodule ElixirScript.Experimental.ModuleState do
2+
def start_link(module) do
3+
Agent.start_link(fn -> %{ module: module, refs: [] } end, name: __MODULE__)
4+
end
5+
6+
def stop() do
7+
Agent.stop(__MODULE__)
8+
end
9+
10+
def put_module_ref(module) do
11+
Agent.update(__MODULE__, fn(x) ->
12+
%{x | refs: Enum.uniq([module | x.refs]) }
13+
end)
14+
end
15+
16+
def get_module_refs() do
17+
Agent.get(__MODULE__, fn(x) ->
18+
x.refs
19+
end)
20+
end
21+
end

lib/elixir_script/translator/kernel/special_forms/identifier.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ defmodule ElixirScript.Translator.Identifier do
5151
|> JS.identifier
5252
end
5353

54+
def make_extern_function_name(name) when is_atom(name) do
55+
JS.identifier("#{name}")
56+
end
57+
5458
def make_function_name(name, arity) when is_atom(name) do
5559
name = filter_name(name)
5660
JS.identifier("#{name}#{arity}")

test/experimental/form_test.exs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@ defmodule ElixirScript.Experimental.Form.Test do
2323
assert generate_js(result) == "Symbol.for('atom')"
2424
end
2525

26-
test "upper case atom do
27-
end" do
26+
test "upper case atom" do
2827
result = Form.compile(Atom)
29-
assert generate_js(result) == "Symbol.for('Elixir.Atom')"
28+
assert generate_js(result) == "Elixir_Atom"
3029
end
3130

3231
test "float" do

0 commit comments

Comments
 (0)