Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module.exports = {
'no-restricted-syntax': 'off',
'no-underscore-dangle': 'off',
'import/extensions': 'off',
'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
},
extends: 'airbnb-base',
plugins: ['import'],
Expand Down
14 changes: 13 additions & 1 deletion lib/elixir_script/passes/translate/form.ex
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,19 @@ defmodule ElixirScript.Translate.Form do
{ast, state}
end

def compile({{:., _, [:erlang, op]}, _, [left, right]}, state) when op in [:+, :-, :*, :/, :==, :>, :<, :>=] do
def compile({{:., _, [:erlang, op]}, _, [left, right]}, state) when op in [:==, :===] do

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functions should have a @SPEC type specification.

ast = Helpers.call(
J.member_expression(
Helpers.core_module("erlang"),
J.identifier("equals")
),
[compile!(left, state), compile!(right, state)]
)

{ast, state}
end

def compile({{:., _, [:erlang, op]}, _, [left, right]}, state) when op in [:+, :-, :*, :/, :>, :<, :>=] do

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functions should have a @SPEC type specification.

ast = J.binary_expression(
op,
compile!(left, state),
Expand Down
1 change: 1 addition & 0 deletions src/javascript/lib/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export default {
Tuple: ErlangTypes.Tuple,
PID: ErlangTypes.PID,
BitString: ErlangTypes.BitString,
Reference: ErlangTypes.Reference,
Patterns,
Integer,
Float,
Expand Down
27 changes: 13 additions & 14 deletions src/javascript/lib/core/erlang_compat/binary.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ function copy(subject, n = 1) {
}

function first(subject) {
if (subject.length == 0) {
throw new Error(`Binary is of length 0`);
if (subject.length === 0) {
throw new Error('Binary is of length 0');
}
return at(subject, 0);
}

function last(subject) {
if (subject.length == 0) {
throw new Error(`Binary is of length 0`);
if (subject.length === 0) {
throw new Error('Binary is of length 0');
}
return subject.slice(-1);
}
Expand All @@ -29,20 +29,19 @@ function list_to_bin(bytelist) {

function part(subject, posOrTuple, len = null) {
if (len === null) {
var pos;
[pos, len] = posOrTuple.values;
return subject.substr(pos, len);
} else {
return subject.substr(posOrTuple, len);
const [pos, theLen] = posOrTuple.values;
return subject.substr(pos, theLen);
}

return subject.substr(posOrTuple, len);
}

//TODO: Support more options
//TODO: pattern cannot be list of strings
// TODO: Support more options

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO found

// TODO: pattern cannot be list of strings

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO found

function replace(subject, pattern, replacement, options = []) {
const opt_global = proplists.get_value(Symbol.for('global'), options);

var regex;
let regex;
if (opt_global !== Symbol.for('undefined')) {
regex = new RegExp(pattern, 'g');
} else {
Expand All @@ -52,8 +51,8 @@ function replace(subject, pattern, replacement, options = []) {
return subject.replace(regex, replacement);
}

//TODO: Support more options, global is implied
//TODO: pattern cannot be list of strings
// TODO: Support more options, global is implied

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO found

// TODO: pattern cannot be list of strings

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO found

function split(subject, pattern, options = []) {
return subject.split(pattern);
}
Expand Down
98 changes: 98 additions & 0 deletions src/javascript/lib/core/erlang_compat/erlang.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,103 @@ function list_subtraction(list1, list2) {
return list;
}

function arrayEquals(left, right) {
if (!Array.isArray(right)) {
return false;
}

if (left.length !== right.length) {
return false;
}

for (let i = 0; i < left.length; i++) {
if (equals(left[i], right[i]) === false) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'equals' was used before it was defined.

return false;
}
}

return true;
}

function tupleEquals(left, right) {
if (right instanceof ErlangTypes.Tuple === false) {
return false;
}

if (left.length !== right.length) {
return false;
}

return arrayEquals(left.values, right.values);
}

function bitstringEquals(left, right) {
if (right instanceof ErlangTypes.BitString === false) {
return false;
}

if (left.length !== right.length) {
return false;
}

return arrayEquals(left.value, right.value);
}

function pidEquals(left, right) {
if (right instanceof ErlangTypes.PID === false) {
return false;
}

return left.id === right.id;
}

function referenceEquals(left, right) {
if (right instanceof ErlangTypes.Reference === false) {
return false;
}

return left.id === right.id;
}

function mapEquals(left, right) {
if (right instanceof Map === false) {
return false;
}

const leftEntries = Array.from(left.entries());
const rightEntries = Array.from(right.entries());

return arrayEquals(leftEntries, rightEntries);
}

function equals(left, right) {
if (Array.isArray(left)) {
return arrayEquals(left, right);
}

if (left instanceof ErlangTypes.Tuple) {
return tupleEquals(left, right);
}

if (left instanceof ErlangTypes.PID) {
return pidEquals(left, right);
}

if (left instanceof ErlangTypes.BitString) {
return bitstringEquals(left, right);
}

if (left instanceof ErlangTypes.Reference) {
return referenceEquals(left, right);
}

if (left instanceof Map) {
return mapEquals(left, right);
}

return left === right;
}

function div(left, right) {
return left / right;
}
Expand Down Expand Up @@ -473,4 +570,5 @@ export default {
list_to_binary,
nodes,
function_exported,
equals,
};
4 changes: 3 additions & 1 deletion src/javascript/tests/core/erlang_compat/binary_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ test('replace/3', (t) => {
});

test('replace/4', (t) => {
const result = Core.binary.replace('abcb', 'b', 'c', [new Core.Tuple(Symbol.for('global'), true)]);
const result = Core.binary.replace('abcb', 'b', 'c', [
new Core.Tuple(Symbol.for('global'), true),
]);
t.deepEqual(result, 'accc');
});

Expand Down
34 changes: 34 additions & 0 deletions src/javascript/tests/core/erlang_compat/erlang_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,37 @@ test('nodes/1', (t) => {

t.deepEqual(Core.erlang.nodes([Symbol.for('connected')]), []);
});

test('equals', (t) => {
t.is(Core.erlang.equals(1, 1), true);
t.is(Core.erlang.equals(1, 'a'), false);
t.is(Core.erlang.equals('a', 'a'), true);
t.is(Core.erlang.equals('a', 'b'), false);
t.is(Core.erlang.equals(Symbol.for('this'), Symbol.for('this')), true);
t.is(Core.erlang.equals([], []), true);
t.is(Core.erlang.equals([1], []), false);
t.is(
Core.erlang.equals(
new Map([[Symbol.for('nest1'), 'valuenest1']]),
new Map([[Symbol.for('nest2'), 'valuenest2']]),
),
false,
);
t.is(
Core.erlang.equals(
new Map([[Symbol.for('nest1'), 'valuenest1']]),
new Map([[Symbol.for('nest1'), 'valuenest1']]),
),
true,
);
t.is(Core.erlang.equals(new Core.Tuple('abc'), new Core.Tuple('abc')), true);
t.is(Core.erlang.equals(new Core.Tuple('abc'), new Core.Tuple('abc', 's')), false);

const pid = new Core.PID();
t.is(Core.erlang.equals(pid, pid), true);
t.is(Core.erlang.equals(pid, new Core.PID()), false);

const ref = new Core.Reference();
t.is(Core.erlang.equals(ref, ref), true);
t.is(Core.erlang.equals(ref, new Core.Reference()), false);
});
29 changes: 19 additions & 10 deletions src/javascript/tests/core/functions.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ test('object_to_map/1', (t) => {
let result = Functions.object_to_map(obj);
t.deepEqual(result, new Map());

obj = {key: 'value'};
obj = { key: 'value' };
result = Functions.object_to_map(obj);
t.deepEqual(result, new Map([['key', 'value']]));

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

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

obj = {};
obj[Symbol.for('key')] = [{nest1: 'valuenest1'},{nest2: 'valuenest2'}];
obj[Symbol.for('key')] = [{ nest1: 'valuenest1' }, { nest2: 'valuenest2' }];
result = Functions.object_to_map(obj, [
new Core.Tuple(Symbol.for('keys'), Symbol.for('atom')),
new Core.Tuple(Symbol.for('recurse_array'), true)]);
t.deepEqual(result, new Map([[Symbol.for('key'), [
new Map([[Symbol.for('nest1'), 'valuenest1']]),
new Map([[Symbol.for('nest2'), 'valuenest2']])
]]]));
});
new Core.Tuple(Symbol.for('keys'), Symbol.for('atom')),
new Core.Tuple(Symbol.for('recurse_array'), true),
]);
t.deepEqual(
result,
new Map([
[
Symbol.for('key'),
[
new Map([[Symbol.for('nest1'), 'valuenest1']]),
new Map([[Symbol.for('nest2'), 'valuenest2']]),
],
],
]),
);
});
5 changes: 5 additions & 0 deletions test/integration/integration_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ defmodule ElixirScript.Integration.Test do
]
end

test "map equals" do
val = call_compiled_function Integration, :map_equals, []
assert val == true
end

test "multi-remote call" do
val = call_compiled_function Integration, :multi_field_call, []
assert val == "5,000,000"
Expand Down
6 changes: 6 additions & 0 deletions test/support/integration.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ defmodule Integration do
&(&2 ++ [ [:option, %{value: &1.email}, &1.email] ]))
end

def map_equals do
map1 = %{test: "map"}
map2 = %{test: "map"}
map1 == map2
end

def multi_field_call do
map = %{token_count: 5000000}
map.token_count.toLocaleString()
Expand Down