Skip to content

Commit 8fa0fce

Browse files
committed
Got compilation working with new version of elixir
1 parent fe909ac commit 8fa0fce

25 files changed

Lines changed: 2480 additions & 2 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
defmodule ElixirScript.Experimental.Backend do
2+
alias ElixirScript.Experimental.Module
3+
alias ESTree.Tools.Generator
4+
alias ElixirScript.Experimental.ModuleState
5+
6+
def compile(line, file, module, attrs, defs, unreachable, opts) do
7+
# Print all arguments
8+
#IO.inspect binding()
9+
10+
# Compile module to JavaScript AST
11+
js_ast = Module.compile(line, file, module, attrs, defs, unreachable, opts)
12+
13+
# Generate JavaScript code string
14+
js_code = Generator.generate(js_ast)
15+
16+
#IO.puts js_code
17+
18+
write_js(module, js_code)
19+
# Invoke the default backend - it returns the compiled beam binary
20+
:elixir_erl.compile(line, file, module, attrs, defs, unreachable, opts)
21+
end
22+
23+
defp write_js(module, js_code) do
24+
output_dir = Path.join([Mix.Project.build_path(), "_javascript"])
25+
26+
if !File.exists?(output_dir) do
27+
File.mkdir_p!(output_dir)
28+
end
29+
30+
output_path = Path.join([output_dir, "#{module}.js"])
31+
File.write!(output_path, js_code)
32+
end
33+
end
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
defmodule ElixirScript.Experimental.Clause do
2+
alias ESTree.Tools.Builder, as: J
3+
alias ElixirScript.Experimental.Form
4+
alias ElixirScript.Experimental.Forms.Pattern
5+
6+
@moduledoc """
7+
Handles translation of all of the clause ASTs
8+
"""
9+
10+
@patterns J.member_expression(
11+
J.member_expression(
12+
J.identifier("Bootstrap"),
13+
J.identifier("Core")
14+
),
15+
J.identifier("Patterns")
16+
)
17+
18+
def compile({ _, args, guards, body}, state) do
19+
{patterns, params} = Pattern.compile(args, state)
20+
guard = compile_guard(params, guards, state)
21+
22+
body = case body do
23+
nil ->
24+
J.identifier("null")
25+
{:__block__, _, block_body} ->
26+
Enum.map(block_body, &Form.compile(&1, state))
27+
|> List.flatten
28+
_ ->
29+
Form.compile(body, state)
30+
end
31+
32+
body = return_last_statement(body)
33+
34+
J.call_expression(
35+
J.member_expression(
36+
@patterns,
37+
J.identifier("clause")
38+
),
39+
[
40+
J.array_expression(patterns),
41+
J.function_expression(
42+
params,
43+
[],
44+
J.block_statement(body)
45+
),
46+
guard
47+
]
48+
)
49+
end
50+
51+
def compile({:->, _, [[{:when, _, params}], body ]}, state) do
52+
guards = List.last(params)
53+
params = params |> Enum.reverse |> tl |> Enum.reverse
54+
55+
compile({[], params, guards, body}, state)
56+
end
57+
58+
def compile({:->, _, [params, body]}, state) do
59+
compile({[], params, [], body}, state)
60+
end
61+
62+
def return_last_statement(body) do
63+
body
64+
|> List.wrap
65+
|> Enum.reverse
66+
|> do_return_last_statement
67+
|> Enum.reverse
68+
end
69+
70+
defp do_return_last_statement([%ESTree.ThrowStatement{} = ast]) do
71+
[ast]
72+
end
73+
74+
defp do_return_last_statement([head]) do
75+
[J.return_statement(head)]
76+
end
77+
78+
defp do_return_last_statement([%ESTree.ThrowStatement{} = head | tail]) do
79+
[head] ++ tail
80+
end
81+
82+
defp do_return_last_statement([head | tail]) do
83+
[J.return_statement(head)] ++ tail
84+
end
85+
86+
defp compile_guard(params, guards, state) do
87+
88+
guards = guards
89+
|> List.wrap
90+
|> Enum.reverse
91+
|> process_guards
92+
|> Form.compile(state)
93+
94+
J.function_expression(
95+
params,
96+
[],
97+
J.block_statement([
98+
J.return_statement(guards)
99+
])
100+
)
101+
102+
end
103+
104+
defp process_guards([]) do
105+
true
106+
end
107+
108+
defp process_guards([guard]) do
109+
guard
110+
end
111+
112+
defp process_guards([head | tail]) do
113+
{{:., [], [:erlang, :orelse]}, [], [process_guards(tail), head]}
114+
end
115+
end
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
defmodule Example do
2+
@compile {:undocumented_elixir_backend_option, ElixirScript.Experimental.Backend}
3+
4+
defstruct [:name]
5+
6+
def new() do
7+
JS.Map.new()
8+
Hello.hi()
9+
end
10+
11+
end
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
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
defmodule ElixirScript.Experimental.Form do
2+
alias ESTree.Tools.Builder, as: J
3+
alias ElixirScript.Experimental.Forms.{Map, Bitstring, Match, Call, Try, For, Struct}
4+
alias ElixirScript.Experimental.Functions.{Erlang, Lists, Maps}
5+
alias ElixirScript.Translator.Identifier
6+
alias ElixirScript.Experimental.Clause
7+
8+
@moduledoc """
9+
Handles translation of all forms that are not functions or clauses
10+
"""
11+
12+
def compile(nil, _) do
13+
J.identifier("null")
14+
end
15+
16+
def compile(form, _) when is_boolean(form) when is_integer(form) when is_float(form) when is_binary(form) do
17+
J.literal(form)
18+
end
19+
20+
def compile(form, state) when is_list(form) do
21+
J.array_expression(
22+
Enum.map(form, &compile(&1, state))
23+
)
24+
end
25+
26+
def compile(form, state) when is_atom(form) do
27+
if ElixirScript.Experimental.Module.is_elixir_module(form) do
28+
members = ["Elixir"] ++ Module.split(form)
29+
J.identifier(Enum.join(members, "_"))
30+
else
31+
J.call_expression(
32+
J.member_expression(
33+
J.identifier("Symbol"),
34+
J.identifier("for")
35+
),
36+
[J.literal(form)]
37+
)
38+
end
39+
end
40+
41+
def compile({a, b}, state) do
42+
compile({:{}, [], [a, b]}, state)
43+
end
44+
45+
def compile({:{}, _, elements}, state) do
46+
J.call_expression(
47+
J.member_expression(
48+
J.member_expression(
49+
J.identifier("Bootstrap"),
50+
J.identifier("Core")
51+
),
52+
J.identifier("Tuple")
53+
),
54+
Enum.map(elements, &compile(&1, state))
55+
)
56+
end
57+
58+
def compile({:%{}, _, _} = map, state) do
59+
Map.compile(map, state)
60+
end
61+
62+
def compile({:<<>>, _, _} = bitstring, state) do
63+
Bitstring.compile(bitstring, state)
64+
end
65+
66+
def compile({:=, _, [left, right]} = match, state) do
67+
Match.compile(match, state)
68+
end
69+
70+
def compile({:%, _, [_, _]} = ast, state) do
71+
Struct.compile(ast, state)
72+
end
73+
74+
def compile({:for, _, _} = ast, state) do
75+
For.compile(ast, state)
76+
end
77+
78+
def compile({:case, _, [condition, [do: clauses]]}, state) do
79+
func = J.call_expression(
80+
J.member_expression(
81+
ElixirScript.Experimental.Function.patterns_ast(),
82+
J.identifier("defmatch")
83+
),
84+
Enum.map(clauses, &Clause.compile(&1, state))
85+
)
86+
87+
J.call_expression(
88+
J.member_expression( func, J.identifier("call")),
89+
[J.identifier(:this), compile(condition, state)]
90+
)
91+
end
92+
93+
def compile({:cond, _, [[do: clauses]]}, state) do
94+
processed_clauses = Enum.map(clauses, fn({:->, _, [clause, clause_body]}) ->
95+
translated_body = Enum.map(List.wrap(clause_body), &compile(&1, state))
96+
|> Clause.return_last_statement
97+
translated_body = J.function_expression([], [], J.block_statement(translated_body))
98+
99+
translated_clause = compile(hd(clause), state)
100+
101+
102+
J.array_expression([translated_clause, translated_body])
103+
end)
104+
105+
106+
cond_function = J.member_expression(
107+
J.member_expression(
108+
J.identifier("Bootstrap"),
109+
J.member_expression(
110+
J.identifier("Core"),
111+
J.identifier("SpecialForms")
112+
)
113+
),
114+
J.identifier("cond")
115+
)
116+
117+
J.call_expression(
118+
cond_function,
119+
processed_clauses
120+
)
121+
end
122+
123+
def compile({:receive, context, _}, state) do
124+
line = Keyword.get(context, :line, 1)
125+
raise ElixirScriptCompileError, message: "Line: #{line} receive not supported"
126+
end
127+
128+
def compile({:try, _, [blocks]}, state) do
129+
Try.compile(blocks, state)
130+
end
131+
132+
def compile({:fn, _, clauses}, state) do
133+
J.call_expression(
134+
J.member_expression(
135+
ElixirScript.Experimental.Function.patterns_ast(),
136+
J.identifier("defmatch")
137+
),
138+
Enum.map(clauses, &Clause.compile(&1, state))
139+
)
140+
end
141+
142+
def compile({{:., _, [:erlang, _]}, _, _} = ast, state) do
143+
Erlang.rewrite(ast, state)
144+
end
145+
146+
def compile({{:., _, [:lists, _]}, _, _} = ast, state) do
147+
Lists.rewrite(ast, state)
148+
end
149+
150+
def compile({{:., _, [:maps, _]}, _, _} = ast, state) do
151+
Maps.rewrite(ast, state)
152+
end
153+
154+
def compile({{:., _, [_, _]}, _, _} = ast, state) do
155+
Call.compile(ast, state)
156+
end
157+
158+
def compile({:super, context, params}, state) do
159+
{function_name, _} = Keyword.fetch!(context, :function)
160+
IO.inspect {"HERE!!!!", function_name}
161+
162+
J.call_expression(
163+
ElixirScript.Translator.Identifier.make_function_name(function_name, length(params)),
164+
Enum.map(params, &compile(&1, state))
165+
)
166+
end
167+
168+
def compile({function_name, _, params}, state) when is_list(params) do
169+
case function_name do
170+
a when is_atom(a) ->
171+
J.call_expression(
172+
ElixirScript.Translator.Identifier.make_function_name(function_name, length(params)),
173+
Enum.map(params, &compile(&1, state))
174+
)
175+
_ ->
176+
J.call_expression(
177+
compile(function_name, state),
178+
Enum.map(params, &compile(&1, state))
179+
)
180+
end
181+
end
182+
183+
def compile({var, _, _}, state) do
184+
ElixirScript.Translator.Identifier.make_identifier(var)
185+
end
186+
187+
end

0 commit comments

Comments
 (0)