Skip to content

Commit 848a660

Browse files
committed
Add equals function to better calculate equality
1 parent 95c7513 commit 848a660

7 files changed

Lines changed: 176 additions & 11 deletions

File tree

lib/elixir_script/passes/translate/form.ex

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,19 @@ defmodule ElixirScript.Translate.Form do
253253
{ast, state}
254254
end
255255

256-
def compile({{:., _, [:erlang, op]}, _, [left, right]}, state) when op in [:+, :-, :*, :/, :==, :>, :<, :>=] do
256+
def compile({{:., _, [:erlang, op]}, _, [left, right]}, state) when op in [:==, :===] do
257+
ast = Helpers.call(
258+
J.member_expression(
259+
Helpers.core_module("erlang"),
260+
J.identifier("equals")
261+
),
262+
[compile!(left, state), compile!(right, state)]
263+
)
264+
265+
{ast, state}
266+
end
267+
268+
def compile({{:., _, [:erlang, op]}, _, [left, right]}, state) when op in [:+, :-, :*, :/, :>, :<, :>=] do
257269
ast = J.binary_expression(
258270
op,
259271
compile!(left, state),

src/javascript/lib/core.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export default {
3939
Tuple: ErlangTypes.Tuple,
4040
PID: ErlangTypes.PID,
4141
BitString: ErlangTypes.BitString,
42+
Reference: ErlangTypes.Reference,
4243
Patterns,
4344
Integer,
4445
Float,

src/javascript/lib/core/erlang_compat/erlang.js

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,103 @@ function list_subtraction(list1, list2) {
6666
return list;
6767
}
6868

69+
function arrayEquals(left, right) {
70+
if (!Array.isArray(right)) {
71+
return false;
72+
}
73+
74+
if (left.length !== right.length) {
75+
return false;
76+
}
77+
78+
for (let i = 0; i < left.length; i++) {
79+
if (equals(left[i], right[i]) === false) {
80+
return false;
81+
}
82+
}
83+
84+
return true;
85+
}
86+
87+
function tupleEquals(left, right) {
88+
if (right instanceof ErlangTypes.Tuple === false) {
89+
return false;
90+
}
91+
92+
if (left.length !== right.length) {
93+
return false;
94+
}
95+
96+
return arrayEquals(left.values, right.values);
97+
}
98+
99+
function bitstringEquals(left, right) {
100+
if (right instanceof ErlangTypes.BitString === false) {
101+
return false;
102+
}
103+
104+
if (left.length !== right.length) {
105+
return false;
106+
}
107+
108+
return arrayEquals(left.value, right.value);
109+
}
110+
111+
function pidEquals(left, right) {
112+
if (right instanceof ErlangTypes.PID === false) {
113+
return false;
114+
}
115+
116+
return left.id === right.id;
117+
}
118+
119+
function referenceEquals(left, right) {
120+
if (right instanceof ErlangTypes.Reference === false) {
121+
return false;
122+
}
123+
124+
return left.id === right.id;
125+
}
126+
127+
function mapEquals(left, right) {
128+
if (right instanceof Map === false) {
129+
return false;
130+
}
131+
132+
const leftEntries = Array.from(left.entries());
133+
const rightEntries = Array.from(right.entries());
134+
135+
return arrayEquals(leftEntries, rightEntries);
136+
}
137+
138+
function equals(left, right) {
139+
if (Array.isArray(left)) {
140+
return arrayEquals(left, right);
141+
}
142+
143+
if (left instanceof ErlangTypes.Tuple) {
144+
return tupleEquals(left, right);
145+
}
146+
147+
if (left instanceof ErlangTypes.PID) {
148+
return pidEquals(left, right);
149+
}
150+
151+
if (left instanceof ErlangTypes.BitString) {
152+
return bitstringEquals(left, right);
153+
}
154+
155+
if (left instanceof ErlangTypes.Reference) {
156+
return referenceEquals(left, right);
157+
}
158+
159+
if (left instanceof Map) {
160+
return mapEquals(left, right);
161+
}
162+
163+
return left === right;
164+
}
165+
69166
function div(left, right) {
70167
return left / right;
71168
}
@@ -473,4 +570,5 @@ export default {
473570
list_to_binary,
474571
nodes,
475572
function_exported,
573+
equals,
476574
};

src/javascript/tests/core/erlang_compat/erlang_spec.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,37 @@ test('nodes/1', (t) => {
9898

9999
t.deepEqual(Core.erlang.nodes([Symbol.for('connected')]), []);
100100
});
101+
102+
test('equals', (t) => {
103+
t.is(Core.erlang.equals(1, 1), true);
104+
t.is(Core.erlang.equals(1, 'a'), false);
105+
t.is(Core.erlang.equals('a', 'a'), true);
106+
t.is(Core.erlang.equals('a', 'b'), false);
107+
t.is(Core.erlang.equals(Symbol.for('this'), Symbol.for('this')), true);
108+
t.is(Core.erlang.equals([], []), true);
109+
t.is(Core.erlang.equals([1], []), false);
110+
t.is(
111+
Core.erlang.equals(
112+
new Map([[Symbol.for('nest1'), 'valuenest1']]),
113+
new Map([[Symbol.for('nest2'), 'valuenest2']]),
114+
),
115+
false,
116+
);
117+
t.is(
118+
Core.erlang.equals(
119+
new Map([[Symbol.for('nest1'), 'valuenest1']]),
120+
new Map([[Symbol.for('nest1'), 'valuenest1']]),
121+
),
122+
true,
123+
);
124+
t.is(Core.erlang.equals(new Core.Tuple('abc'), new Core.Tuple('abc')), true);
125+
t.is(Core.erlang.equals(new Core.Tuple('abc'), new Core.Tuple('abc', 's')), false);
126+
127+
const pid = new Core.PID();
128+
t.is(Core.erlang.equals(pid, pid), true);
129+
t.is(Core.erlang.equals(pid, new Core.PID()), false);
130+
131+
const ref = new Core.Reference();
132+
t.is(Core.erlang.equals(ref, ref), true);
133+
t.is(Core.erlang.equals(ref, new Core.Reference()), false);
134+
});

src/javascript/tests/core/functions.spec.js

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ test('object_to_map/1', (t) => {
4545
let result = Functions.object_to_map(obj);
4646
t.deepEqual(result, new Map());
4747

48-
obj = {key: 'value'};
48+
obj = { key: 'value' };
4949
result = Functions.object_to_map(obj);
5050
t.deepEqual(result, new Map([['key', 'value']]));
5151

@@ -60,17 +60,26 @@ test('object_to_map/2', (t) => {
6060
let result = Functions.object_to_map(obj, []);
6161
t.deepEqual(result, new Map());
6262

63-
obj = {key: 'value'};
63+
obj = { key: 'value' };
6464
result = Functions.object_to_map(obj, [new Core.Tuple(Symbol.for('keys'), Symbol.for('atom'))]);
6565
t.deepEqual(result, new Map([[Symbol.for('key'), 'value']]));
6666

6767
obj = {};
68-
obj[Symbol.for('key')] = [{nest1: 'valuenest1'},{nest2: 'valuenest2'}];
68+
obj[Symbol.for('key')] = [{ nest1: 'valuenest1' }, { nest2: 'valuenest2' }];
6969
result = Functions.object_to_map(obj, [
70-
new Core.Tuple(Symbol.for('keys'), Symbol.for('atom')),
71-
new Core.Tuple(Symbol.for('recurse_array'), true)]);
72-
t.deepEqual(result, new Map([[Symbol.for('key'), [
73-
new Map([[Symbol.for('nest1'), 'valuenest1']]),
74-
new Map([[Symbol.for('nest2'), 'valuenest2']])
75-
]]]));
76-
});
70+
new Core.Tuple(Symbol.for('keys'), Symbol.for('atom')),
71+
new Core.Tuple(Symbol.for('recurse_array'), true),
72+
]);
73+
t.deepEqual(
74+
result,
75+
new Map([
76+
[
77+
Symbol.for('key'),
78+
[
79+
new Map([[Symbol.for('nest1'), 'valuenest1']]),
80+
new Map([[Symbol.for('nest2'), 'valuenest2']]),
81+
],
82+
],
83+
]),
84+
);
85+
});

test/integration/integration_test.exs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,9 @@ defmodule ElixirScript.Integration.Test do
1919
[:option, %{value: "[email protected]"}, "[email protected]"]
2020
]
2121
end
22+
23+
test "map equals" do
24+
val = call_compiled_function Integration, :map_equals, []
25+
assert val == true
26+
end
2227
end

test/support/integration.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,10 @@ defmodule Integration do
1010
options = Enum.reduce(orders, [],
1111
&(&2 ++ [ [:option, %{value: &1.email}, &1.email] ]))
1212
end
13+
14+
def map_equals do
15+
map1 = %{test: "map"}
16+
map2 = %{test: "map"}
17+
map1 == map2
18+
end
1319
end

0 commit comments

Comments
 (0)