Skip to content

Commit db2f827

Browse files
committed
Update struct implementation to not use classes
1 parent 9e67c1a commit db2f827

File tree

10 files changed

+272
-65
lines changed

10 files changed

+272
-65
lines changed

lib/elixir_script/translator.ex

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,13 @@ defmodule ElixirScript.Translator do
219219
end
220220

221221
defp do_translate({:%, _, [alias_info, data]}, env) do
222-
{ Struct.new_struct(alias_info, data, env), env }
222+
module = case create_module_name(alias_info, env) do
223+
{module, _} ->
224+
module
225+
module ->
226+
module
227+
end
228+
Call.make_module_function_call(module, :__struct__, [data], env)
223229
end
224230

225231
defp do_translate({:%{}, _, [{:|, _, [map, data]}]}, env) do
@@ -541,12 +547,12 @@ defmodule ElixirScript.Translator do
541547
Def.process_delegate(name, params, options, env)
542548
end
543549

544-
defp do_translate({:defstruct, _, attributes}, env) do
545-
{ Struct.make_defstruct(attributes, env), env }
550+
defp do_translate({:defstruct, _, [attributes]}, env) do
551+
{ Struct.make_struct(attributes, env), env }
546552
end
547553

548-
defp do_translate({:defexception, _, attributes}, env) do
549-
{ Struct.make_defexception(attributes, env), env }
554+
defp do_translate({:defexception, _, [attributes]}, env) do
555+
{ Struct.make_struct(attributes ++ [__exception__: true], env), env }
550556
end
551557

552558
defp do_translate({:defmodule, _, [{:__aliases__, _, module_name_list}, [do: body]]}, env) do

lib/elixir_script/translator/kernel/defmodule.ex

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ defmodule ElixirScript.Translator.Defmodule do
5151

5252
{ exported_functions, private_functions } = process_functions(functions, env)
5353

54-
{structs, body} = extract_structs_from_body(body, env)
54+
struct_prop = if has_struct?(body) do
55+
[JS.property(Identifier.make_identifier("__struct__"), Identifier.make_identifier("__struct__"), :init, true)]
56+
else
57+
[]
58+
end
5559

5660
body = Enum.map(body, fn(x) ->
5761
case x do
@@ -65,7 +69,7 @@ defmodule ElixirScript.Translator.Defmodule do
6569
body = Group.inflate_groups(body)
6670

6771
exported_object = JS.object_expression(
68-
make_defstruct_property(module, structs) ++
72+
struct_prop ++
6973
Enum.map(exported_functions, fn({key, _value}) ->
7074
JS.property(Identifier.make_identifier(key), Identifier.make_identifier(key), :init, true)
7175
end)
@@ -74,7 +78,7 @@ defmodule ElixirScript.Translator.Defmodule do
7478
exported_functions = Enum.map(exported_functions, fn({_key, value}) -> value end)
7579
private_functions = Enum.map(private_functions, fn({_key, value}) -> value end)
7680

77-
body = structs ++ private_functions ++ exported_functions ++ body
81+
body = private_functions ++ exported_functions ++ body
7882
{body, exported_object}
7983
end
8084

@@ -161,17 +165,17 @@ defmodule ElixirScript.Translator.Defmodule do
161165
end)
162166
end
163167

164-
def extract_structs_from_body(body, env) do
165-
module_js_name = Utils.name_to_js_name(env.module)
166-
167-
Enum.partition(body, fn(x) ->
168+
def has_struct?(body) do
169+
val = Enum.find(body, fn(x) ->
168170
case x do
169-
%ESTree.VariableDeclaration{declarations: [%ESTree.VariableDeclarator{id: %ESTree.Identifier{name: ^module_js_name} } ] } ->
171+
%ESTree.VariableDeclaration{declarations: [%ESTree.VariableDeclarator{id: %ESTree.Identifier{name: "__struct__"} } ] } ->
170172
true
171173
_ ->
172174
false
173175
end
174176
end)
177+
178+
val != nil
175179
end
176180

177181
defp make_defstruct_property(_, []) do

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,22 @@ defmodule ElixirScript.Translator.Map do
6767

6868
js_ast = JS.call_expression(
6969
JS.member_expression(
70-
Primitive.special_forms(),
71-
JS.identifier("map_update")
70+
JS.identifier("Object"),
71+
JS.identifier("freeze")
7272
),
73-
[map, data]
73+
[
74+
JS.call_expression(
75+
JS.member_expression(
76+
JS.identifier("Object"),
77+
JS.identifier("assign")
78+
),
79+
[
80+
JS.object_expression([]),
81+
map,
82+
data
83+
]
84+
)
85+
]
7486
)
7587

7688
{ js_ast, env }

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

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,111 @@ defmodule ElixirScript.Translator.Struct do
139139
JS.variable_declaration([ref_declarator], :const)
140140
end
141141

142+
def make_struct(attributes, env) do
143+
struct_name = Map.make_property(Translator.translate!(:__struct__, env), Translator.translate!({:__MODULE__, [], []}, env))
144+
145+
defaults = Enum.map(attributes, fn
146+
({x, y}) ->
147+
Map.make_property(
148+
Translator.translate!(x, env),
149+
Translator.translate!(y, env)
150+
)
151+
(x) ->
152+
Map.make_property(
153+
Translator.translate!(x, env),
154+
Translator.translate!(nil, env)
155+
)
156+
end)
157+
158+
keys = Enum.map(attributes, fn
159+
({x, _}) ->
160+
Translator.translate!(x, env)
161+
(x) ->
162+
Translator.translate!(x, env)
163+
end)
164+
165+
keys = JS.array_expression(keys)
166+
defaults = JS.object_expression([struct_name] ++ defaults)
167+
168+
allowed_keys = JS.variable_declaration([JS.variable_declarator(
169+
JS.identifier("allowed_keys"),
170+
keys
171+
)], :const)
172+
173+
value_keys = JS.variable_declaration([JS.variable_declarator(
174+
JS.identifier("value_keys"),
175+
JS.call_expression(
176+
JS.member_expression(
177+
JS.identifier("Object"),
178+
JS.identifier("keys")
179+
),
180+
[JS.identifier("values")]
181+
)
182+
)], :const)
183+
184+
every_call = JS.call_expression(
185+
JS.member_expression(
186+
JS.identifier("value_keys"),
187+
JS.identifier("every")
188+
),
189+
[
190+
JS.function_expression([JS.identifier("key")], [], JS.block_statement([
191+
JS.return_statement(
192+
JS.call_expression(
193+
JS.member_expression(
194+
JS.identifier("allowed_keys"),
195+
JS.identifier("includes")
196+
),
197+
[JS.identifier("key")]
198+
)
199+
)
200+
]))
201+
]
202+
)
203+
204+
every_call_result = JS.variable_declaration([JS.variable_declarator(
205+
JS.identifier("every_call_result"),
206+
every_call
207+
)], :const)
208+
209+
bottom = JS.if_statement(
210+
JS.identifier("every_call_result"),
211+
JS.block_statement([
212+
JS.return_statement(
213+
JS.call_expression(
214+
JS.member_expression(
215+
JS.identifier("Object"),
216+
JS.identifier("assign")
217+
),
218+
[JS.object_expression([]), defaults, JS.identifier("values")]
219+
)
220+
)
221+
]),
222+
JS.block_statement([
223+
JS.throw_statement(
224+
JS.literal("Unallowed key found")
225+
)
226+
])
227+
)
228+
229+
func = JS.function_expression([
230+
%ESTree.AssignmentPattern{
231+
left: JS.identifier("values"),
232+
right: JS.object_expression([])
233+
}
234+
],
235+
[],
236+
JS.block_statement([
237+
allowed_keys,
238+
value_keys,
239+
every_call_result,
240+
bottom
241+
]))
242+
243+
JS.variable_declaration([JS.variable_declarator(
244+
JS.identifier("__struct__"),
245+
func
246+
)], :const)
247+
end
248+
142249
end

priv/std_lib/map.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ defmodule ElixirScript.Map do
7070
end
7171

7272
def merge(map1, map2) do
73-
Bootstrap.Core.SpecialForms.map_update(map1, map2)
73+
JS.Object.assign(%{}, map1, map2)
74+
|> JS.Object.freeze()
7475
end
7576

7677
def split(map, keys) do

test/prelude/kernel_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ defmodule ElixirScript.Lib.Elixir.Kernel.Test do
99

1010
js_code = """
1111
12-
Elixir.ElixirScript.Range.__load(Elixir).Elixir$ElixirScript$Range.create(Object.freeze({
12+
Elixir.ElixirScript.Range.__load(Elixir).__struct__(Object.freeze({
1313
[Symbol.for('first')]: 1,
1414
[Symbol.for('last')]: 4
1515
}))

test/translator/defmodule_test.exs

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,24 @@ defmodule ElixirScript.Translator.Defmodule.Test do
6868
end
6969

7070
js_code = """
71-
const Elixir$Animals$Elephant = Bootstrap.Core.Functions.defstruct({
72-
[Symbol.for('__struct__')]: Symbol.for('Elixir.Animals.Elephant'), [Symbol.for('trunk')]: true
73-
});
71+
const __struct__ = function(values = {}) {
72+
const allowed_keys = [Symbol.for('trunk')]
73+
74+
const value_keys = Object.keys(values)
75+
76+
const every_call_result = value_keys.every(function(key) {
77+
return allowed_keys.includes(key);
78+
})
79+
80+
if (every_call_result) {
81+
return Object.assign({}, {
82+
[Symbol.for('__struct__')]: Symbol.for('Elixir.Animals.Elephant'),
83+
[Symbol.for('trunk')]: true
84+
}, values);
85+
} else {
86+
throw 'Unallowed key found';
87+
}
88+
};
7489
"""
7590

7691
assert_translation(ex_ast, js_code)
@@ -101,10 +116,24 @@ defmodule ElixirScript.Translator.Defmodule.Test do
101116
end
102117

103118
js_code = """
104-
const Elixir$Animals$Elephant$Bear = Bootstrap.Core.Functions.defstruct({
105-
[Symbol.for('__struct__')]: Symbol.for('Elixir.Animals.Elephant.Bear'),
106-
[Symbol.for('trunk')]: true
107-
});
119+
const __struct__ = function(values = {}) {
120+
const allowed_keys = [Symbol.for('trunk')]
121+
122+
const value_keys = Object.keys(values)
123+
124+
const every_call_result = value_keys.every(function(key) {
125+
return allowed_keys.includes(key);
126+
})
127+
128+
if (every_call_result) {
129+
return Object.assign({}, {
130+
[Symbol.for('__struct__')]: Symbol.for('Elixir.Animals.Elephant.Bear'),
131+
[Symbol.for('trunk')]: true
132+
}, values);
133+
} else {
134+
throw 'Unallowed key found';
135+
}
136+
};
108137
"""
109138

110139
assert_translation(ex_ast, js_code)

test/translator/map_test.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ defmodule ElixirScript.Translator.Map.Test do
4949
end
5050

5151
js_code = """
52-
Bootstrap.Core.SpecialForms.map_update(map,Object.freeze({
52+
Object.freeze(Object.assign({}, map, Object.freeze({
5353
[Symbol.for('value')]: 1
54-
}))
54+
})))
5555
"""
5656

5757
assert_translation(ex_ast, js_code)

0 commit comments

Comments
 (0)