From 836ac54d12a3c292beeb2dc0342bc819a32bde38 Mon Sep 17 00:00:00 2001 From: playX Date: Sun, 1 Aug 2021 14:08:49 +0300 Subject: [PATCH] v.gen.js: add more array tests and fixes (#11014) --- vlib/builtin/js/array.js.v | 17 +- vlib/builtin/js/int.js.v | 8 + vlib/builtin/js/string.js.v | 2 +- vlib/v/gen/js/fast_deep_equal.js | 9 +- vlib/v/gen/js/js.v | 6 +- vlib/v/gen/js/temp_fast_deep_equal.v | 9 +- vlib/v/gen/js/tests/testdata/array.out | 67 +++++++ vlib/v/gen/js/tests/testdata/array.v | 253 ++++++++++++++++++++++++- 8 files changed, 356 insertions(+), 15 deletions(-) create mode 100644 vlib/builtin/js/int.js.v diff --git a/vlib/builtin/js/array.js.v b/vlib/builtin/js/array.js.v index f98cfe8f25..a741059414 100644 --- a/vlib/builtin/js/array.js.v +++ b/vlib/builtin/js/array.js.v @@ -87,8 +87,21 @@ pub fn (mut a array) sort_with_compare(compare voidptr) { #a.arr.sort(compare) } +#function $sortComparator(a, b) +#{ +#"use strict"; +#a = a.$toJS(); +#b = b.$toJS(); +# +#if (a > b) return 1; +#if (a < b) return -1; +#return 0; +# +# +#} + pub fn (mut a array) sort() { - #a.arr.sort() + #a.arr.sort($sortComparator) } pub fn (a array) index(v string) int { @@ -137,7 +150,7 @@ pub fn (a array) str() string { #array.prototype[Symbol.iterator] = function () { return this.arr[Symbol.iterator](); } #array.prototype.entries = function () { return this.arr.entries(); } -#array.prototype.map = function(callback) { return this.arr.map(callback); } +#array.prototype.map = function(callback) { return new builtin.array(this.arr.map(callback)); } #array.prototype.filter = function(callback) { return new array(this.arr.filter( function (it) { return (+callback(it)) != 0; } )); } #Object.defineProperty(array.prototype,'cap',{ get: function () { return this.len; } }) // delete deletes array element at index `i`. diff --git a/vlib/builtin/js/int.js.v b/vlib/builtin/js/int.js.v new file mode 100644 index 0000000000..08e52a912d --- /dev/null +++ b/vlib/builtin/js/int.js.v @@ -0,0 +1,8 @@ +module builtin + +pub fn (i int) str() string { + mut res := '' + #res = new builtin.string( i ) + + return res +} diff --git a/vlib/builtin/js/string.js.v b/vlib/builtin/js/string.js.v index b43b2c894f..e7d270800d 100644 --- a/vlib/builtin/js/string.js.v +++ b/vlib/builtin/js/string.js.v @@ -3,7 +3,7 @@ module builtin pub struct string { pub: str JS.String - len u32 + len int } pub fn (s string) slice(a int, b int) string { diff --git a/vlib/v/gen/js/fast_deep_equal.js b/vlib/v/gen/js/fast_deep_equal.js index 0b0ae6a221..50d37e88c8 100644 --- a/vlib/v/gen/js/fast_deep_equal.js +++ b/vlib/v/gen/js/fast_deep_equal.js @@ -5,8 +5,13 @@ function vEq(a, b) { if (a && b && typeof a == 'object' && typeof b == 'object') { if (a.constructor !== b.constructor) return false; - a = a.$toJS(); - b = b.$toJS(); + // we want to convert all V types to JS for comparison. + if ('$toJS' in a) + a = a.$toJS(); + + if ('$toJS' in b) + b = b.$toJS(); + var length, i, keys; if (Array.isArray(a)) { length = a.length; diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 9f5ae5a68c..6ec623e2eb 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -619,7 +619,8 @@ fn (mut g JsGen) expr(node ast.Expr) { g.gen_type_cast_expr(node) } ast.CharLiteral { - g.write("'$node.val'") + // todo(playX): char type? + g.write("new builtin.string('$node.val')") } ast.Comment {} ast.ConcatExpr { @@ -1312,8 +1313,9 @@ fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) { } g.writeln('}`') g.dec_indent() - g.writeln('}') + g.writeln('},') } + g.writeln('\$toJS() { return this; }') g.dec_indent() g.writeln('};\n') if node.is_pub { diff --git a/vlib/v/gen/js/temp_fast_deep_equal.v b/vlib/v/gen/js/temp_fast_deep_equal.v index 9015331a4c..bca6e6175d 100644 --- a/vlib/v/gen/js/temp_fast_deep_equal.v +++ b/vlib/v/gen/js/temp_fast_deep_equal.v @@ -9,8 +9,13 @@ function vEq(a, b) { if (a && b && typeof a == 'object' && typeof b == 'object') { if (a.constructor !== b.constructor) return false; - a = a.\$toJS(); - b = b.\$toJS(); + // we want to convert all V types to JS for comparison. + if ('\$toJS' in a) + a = a.\$toJS(); + + if ('\$toJS' in b) + b = b.\$toJS(); + var length, i, keys; if (Array.isArray(a)) { length = a.length; diff --git a/vlib/v/gen/js/tests/testdata/array.out b/vlib/v/gen/js/tests/testdata/array.out index 0822ce3c74..b375fcb7cf 100644 --- a/vlib/v/gen/js/tests/testdata/array.out +++ b/vlib/v/gen/js/tests/testdata/array.out @@ -162,3 +162,70 @@ true [2,3,4,6,8,9,10] [4,5,6] [5,10] +[2,4] +[2,4] +[1,2,3,4,5,6] +["v","is","awesome"] +[0,0,0,0,0,0] +0 +[10,20,30,40,50,60] +[1,4,9,16,25,36] +["1","2","3","4","5","6"] +[false,true,false,true,false,true] +["V","IS","AWESOME"] +[false,false,true] +[true,true,false] +[7,7,7] +[1,4,9,16,25,36] +[3,4,5,6,7,8] +[3,9,4,6,12,7] +[] +[true,true,true,true,true,true] +["1a","2a","3a","4a","5a","6a"] +[2,3,4,5,6,7] +[2,3,8] +["1v","2is","3awesome"] +[1,4,9,16,25,36] +[1,25,100] +[1,2,3,4,5,6] +["v","is","awesome"] +[2,3,4] +[2,3,4] +[3,4,5] +[2,3,4] +["1","2","3"] +["1","2","3"] +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +["1","3","5","hi"] +[-3,7,42,67,108] +["a","b","c","d","e","f"] +0 +1 +79 diff --git a/vlib/v/gen/js/tests/testdata/array.v b/vlib/v/gen/js/tests/testdata/array.v index 53e9c1b409..8ef56bc19f 100644 --- a/vlib/v/gen/js/tests/testdata/array.v +++ b/vlib/v/gen/js/tests/testdata/array.v @@ -10,6 +10,23 @@ const ( c_n = 5 ) +struct User { + age int + name string +} + +fn map_test_helper_1(i int) int { + return i * i +} + +fn map_test_helper_2(i int, b string) int { + return i + b.len +} + +fn map_test_helper_3(i int, b []string) int { + return i + b.map(it.len)[i % b.len] +} + fn filter_test_helper_1(a int) bool { return a > 3 } @@ -510,21 +527,245 @@ fn main() { println([1, 5, 10].filter(filter_test_helper_1)) } { - // todo(playX): RUNTIME ERROR: Invalid memory access - /* // test anon fn filter filter_num := fn (i int) bool { return i % 2 == 0 } - assert [1, 2, 3, 4, 5].filter(filter_num) == [2, 4] - */ + println([1, 2, 3, 4, 5].filter(filter_num)) } { - /* a := [1, 2, 3, 4].filter(fn (i int) bool { return i % 2 == 0 }) - assert a == [2, 4] + println(a) + } + { + // test map + nums := [1, 2, 3, 4, 5, 6] + strs := ['v', 'is', 'awesome'] + // assert nums.map() == + // assert nums.map(it, 'excessive') == + // identity + println(nums.map(it)) + println(strs.map(it)) + println(nums.map(it - it)) + println(nums.map(it - it)[0]) + // type switch + println(nums.map(it * 10)) + println(nums.map(it * it)) + println(nums.map('$it')) + println(nums.map(it % 2 == 0)) + println(strs.map(it.to_upper())) + println(strs.map(it == 'awesome')) + println(strs.map(it.len in nums)) + println(strs.map(int(7))) + // external func + println(nums.map(map_test_helper_1(it))) + println(nums.map(map_test_helper_2(it, 'bb'))) + println(nums.map(map_test_helper_3(it, strs))) + // empty array as input + println([]int{len: 0}.map(it * 2)) + // nested maps (where it is of same type) + println(nums.map(strs.map(int(7)) == [7, 7, 7])) + println(nums.map('$it' + strs.map('a')[0])) + // assert nums.map(it + strs.map(int(7))[0]) == [8, 9, 10, 11, 12, 13] + println(nums.map(it + strs.map(it.len)[0])) + println(strs.map(it.len + strs.map(it.len)[0])) + // nested (different it types) + // todo(playX): this one produces invalid JS code. + // assert strs.map(it[nums.map(it - it)[0]]) == [byte(`v`), `i`, `a`] + println(nums[0..3].map('$it' + strs.map(it)[it - 1])) + println(nums.map(map_test_helper_1)) + println([1, 5, 10].map(map_test_helper_1)) + println(nums) + println(strs) + } + { + // test anon fn map + add_num := fn (i int) int { + return i + 1 + } + println([1, 2, 3].map(add_num)) + } + { + // test multi anon fn map + a := [1, 2, 3].map(fn (i int) int { + return i + 1 + }) + b := [1, 2, 3].map(fn (i int) int { + return i + 2 + }) + println(a) + println(b) + } + { + // test anon fn arg map + a := [1, 2, 3].map(fn (i int) int { + return i + 1 + }) + println(a) + } + { + // test anon fn arg different type map + i_to_str := fn (i int) string { + return i.str() + } + a := [1, 2, 3].map(i_to_str) + println(a) + } + { + // test anon fn inline different type map + a := [1, 2, 3].map(fn (i int) string { + return i.str() + }) + println(a) + } + { + // test array str + // todo(playX): JS array formatting should match what default builtin impl has. + /* + numbers := [1, 2, 3] + assert numbers == [1, 2, 3] + numbers2 := [numbers, [4, 5, 6]] // dup str() bug + println(numbers2) + assert true + assert numbers.str() == '[1, 2, 3]' */ } + { + // test eq + println([5, 6, 7] != [6, 7]) + println([`a`, `b`] == [`a`, `b`]) + println([User{ + age: 22 + name: 'bob' + }] == [User{ + age: 22 + name: 'bob' + }]) + // todo(playX): map cmp does not work yet + /* + assert [map{ + 'bob': 22 + }, map{ + 'tom': 33 + }] == [map{ + 'bob': 22 + }, map{ + 'tom': 33 + }]*/ + println([[1, 2, 3], [4]] == [[1, 2, 3], [4]]) + } + { + // test fixed array eq + a1 := [1, 2, 3]! + println(a1 == [1, 2, 3]!) + println(a1 != [2, 3, 4]!) + + a2 := [[1, 2]!, [3, 4]!]! + println(a2 == [[1, 2]!, [3, 4]!]!) + println(a2 != [[3, 4]!, [1, 2]!]!) + + a3 := [[1, 2], [3, 4]]! + println(a3 == [[1, 2], [3, 4]]!) + println(a3 != [[1, 1], [2, 2]]!) + + a4 := [[`a`, `b`], [`c`, `d`]]! + println(a4 == [[`a`, `b`], [`c`, `d`]]!) + println(a4 != [[`c`, `a`], [`a`, `b`]]!) + + a5 := [['aaa', 'bbb'], ['ccc', 'ddd']]! + println(a5 == [['aaa', 'bbb'], ['ccc', 'ddd']]!) + println(a5 != [['abc', 'def'], ['ccc', 'ddd']]!) + + a6 := [['aaa', 'bbb']!, ['ccc', 'ddd']!]! + println(a6 == [['aaa', 'bbb']!, ['ccc', 'ddd']!]!) + println(a6 != [['aaa', 'bbb']!, ['aaa', 'ddd']!]!) + + a7 := [[1, 2]!, [3, 4]!] + println(a7 == [[1, 2]!, [3, 4]!]) + println(a7 != [[2, 3]!, [1, 2]!]) + + a8 := [['aaa', 'bbb']!, ['ccc', 'ddd']!] + println(a8 == [['aaa', 'bbb']!, ['ccc', 'ddd']!]) + println(a8 != [['bbb', 'aaa']!, ['cccc', 'dddd']!]) + } + { + // test fixed array literal eq + println([1, 2, 3]! == [1, 2, 3]!) + println([1, 1, 1]! != [1, 2, 3]!) + + println([[1, 2], [3, 4]]! == [[1, 2], [3, 4]]!) + println([[1, 1], [2, 2]]! != [[1, 2], [3, 4]]!) + + println([[1, 1]!, [2, 2]!]! == [[1, 1]!, [2, 2]!]!) + println([[1, 1]!, [2, 2]!]! != [[1, 2]!, [2, 3]!]!) + + println([[1, 1]!, [2, 2]!] == [[1, 1]!, [2, 2]!]) + println([[1, 1]!, [2, 2]!] != [[1, 2]!, [2, 3]!]) + } + { + // test sort + mut a := ['hi', '1', '5', '3'] + a.sort() + println(a) + + mut nums := [67, -3, 108, 42, 7] + nums.sort() + println(nums) + assert nums[0] == -3 + assert nums[1] == 7 + assert nums[2] == 42 + assert nums[3] == 67 + assert nums[4] == 108 + // todo(playX): add codegen for comparator fn passed + /* + nums.sort(a < b) + assert nums[0] == -3 + assert nums[1] == 7 + assert nums[2] == 42 + assert nums[3] == 67 + assert nums[4] == 108 + + mut users := [User{22, 'Peter'}, User{20, 'Bob'}, User{25, 'Alice'}] + users.sort(a.age < b.age) + assert users[0].age == 20 + assert users[1].age == 22 + assert users[2].age == 25 + assert users[0].name == 'Bob' + assert users[1].name == 'Peter' + assert users[2].name == 'Alice' + + users.sort(a.age > b.age) + assert users[0].age == 25 + assert users[1].age == 22 + assert users[2].age == 20 + + users.sort(a.name < b.name) // Test sorting by string fields + */ + } + { + // test rune sort + mut bs := [`f`, `e`, `d`, `b`, `c`, `a`] + bs.sort() + println(bs) + + /* + bs.sort(a > b) + println(bs) + assert '$bs' == '[`f`, `e`, `d`, `c`, `b`, `a`]' + + bs.sort(a < b) + println(bs) + assert '$bs' == '[`a`, `b`, `c`, `d`, `e`, `f`]' + */ + } + { + // test f32 sort + mut f := [f32(50.0), 15, 1, 79, 38, 0, 27] + f.sort() + println(f[0]) + println(f[1]) + println(f[6]) + } }