diff --git a/vlib/builtin/js/array.js.v b/vlib/builtin/js/array.js.v index 651e366b92..f98cfe8f25 100644 --- a/vlib/builtin/js/array.js.v +++ b/vlib/builtin/js/array.js.v @@ -138,7 +138,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.filter = function(callback) { return this.arr.filter(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`. pub fn (mut a array) delete(i int) { @@ -160,3 +160,23 @@ pub fn (mut a array) prepend(val voidptr) { pub fn (mut a array) prepend_many(val voidptr, size int) { unsafe { a.insert_many(0, val, size) } } + +pub fn (a array) reverse() array { + mut res := array{} + #res.arr = Array.from(a.arr).reverse() + + return res +} + +#array.prototype.$includes = function (elem) { return this.arr.find(function(e) { return vEq(elem,e); }) !== undefined;} + +// reduce executes a given reducer function on each element of the array, +// resulting in a single output value. +pub fn (a array) reduce(iter fn (int, int) int, accum_start int) int { + mut accum_ := accum_start + #for (let i of a) { + #accum_ = iter(accum_, a.arr[i]) + #} + + return accum_ +} diff --git a/vlib/v/gen/js/builtin_types.v b/vlib/v/gen/js/builtin_types.v index 6fb9115f0e..1bd1c15d47 100644 --- a/vlib/v/gen/js/builtin_types.v +++ b/vlib/v/gen/js/builtin_types.v @@ -263,6 +263,7 @@ struct BuiltinPrototypeConfig { value_of string = 'this.val' to_string string = 'this.val.toString()' eq string = 'this.val === other.val' + to_jsval string = 'this' extras string has_strfn bool } @@ -284,6 +285,7 @@ fn (mut g JsGen) gen_builtin_prototype(c BuiltinPrototypeConfig) { g.writeln('valueOf() { return $c.value_of },') g.writeln('toString() { return $c.to_string },') g.writeln('eq(other) { return $c.eq },') + g.writeln('\$toJS() { return $c.to_jsval }, ') if c.has_strfn { g.writeln('str() { return new string(this.toString()) }') } @@ -306,6 +308,7 @@ fn (mut g JsGen) gen_builtin_type_defs() { value_of: 'this.val | 0' to_string: 'this.valueOf().toString()' eq: 'this.valueOf() === other.valueOf()' + to_jsval: '+this' ) } 'byte' { @@ -316,18 +319,22 @@ fn (mut g JsGen) gen_builtin_type_defs() { value_of: 'this.val | 0' to_string: 'new string(this.val + "")' eq: 'this.valueOf() === other.valueOf()' + to_jsval: '+this' ) } 'f32', 'f64', 'float_literal' { g.gen_builtin_prototype( typ_name: typ_name default_value: 'new Number(0)' + to_jsval: '+this' ) } 'bool' { g.gen_builtin_prototype( + constructor: 'this.val = +val !== 0' typ_name: typ_name default_value: 'new Boolean(false)' + to_jsval: '+this != 0' ) } 'string' { @@ -340,6 +347,7 @@ fn (mut g JsGen) gen_builtin_type_defs() { to_string: 'this.str' eq: 'this.str === other.str' has_strfn: false + to_jsval: 'this.str' ) } 'map' { @@ -348,9 +356,10 @@ fn (mut g JsGen) gen_builtin_type_defs() { val_name: 'map' default_value: 'new Map()' constructor: 'this.map = map' - value_of: 'this.map' + value_of: 'this' to_string: 'this.map.toString()' eq: 'vEq(this, other)' + to_jsval: 'this.map' ) } 'array' { @@ -359,9 +368,10 @@ fn (mut g JsGen) gen_builtin_type_defs() { val_name: 'arr' default_value: 'new Array()' constructor: 'this.arr = arr' - value_of: 'this.arr' + value_of: 'this' to_string: 'JSON.stringify(this.arr.map(it => it.valueOf()))' eq: 'vEq(this, other)' + to_jsval: 'this.arr' ) } 'any' { @@ -373,6 +383,7 @@ fn (mut g JsGen) gen_builtin_type_defs() { value_of: 'this.val' to_string: '"&" + this.val' eq: 'this == other' // compare by ptr + to_jsval: 'this.val.\$toJS()' ) } else {} diff --git a/vlib/v/gen/js/fast_deep_equal.js b/vlib/v/gen/js/fast_deep_equal.js index 9022d08444..0b0ae6a221 100644 --- a/vlib/v/gen/js/fast_deep_equal.js +++ b/vlib/v/gen/js/fast_deep_equal.js @@ -5,8 +5,8 @@ function vEq(a, b) { if (a && b && typeof a == 'object' && typeof b == 'object') { if (a.constructor !== b.constructor) return false; - a = a.valueOf(); - b = b.valueOf(); + a = a.$toJS(); + 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 0119224e71..5bc985aff8 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -1079,6 +1079,7 @@ fn (mut g JsGen) gen_for_c_stmt(it ast.ForCStmt) { g.write('; ') } if it.has_cond { + g.write('+') // convert to number or boolean g.expr(it.cond) } g.write('; ') @@ -1174,6 +1175,7 @@ fn (mut g JsGen) gen_for_stmt(it ast.ForStmt) { if it.is_inf { g.write('true') } else { + g.write('+') // convert expr to number or boolean g.expr(it.cond) } g.writeln(') {') @@ -1325,6 +1327,7 @@ fn (mut g JsGen) gen_array_init_expr(it ast.ArrayInit) { // offering similar performance g.write('new array(') g.inc_indent() + if it.has_len { t1 := g.new_tmp_var() t2 := g.new_tmp_var() @@ -1349,6 +1352,31 @@ fn (mut g JsGen) gen_array_init_expr(it ast.ArrayInit) { g.writeln('return $t1;') g.dec_indent() g.write('})()') + } else if it.is_fixed && it.exprs.len == 1 { + // [100]byte codegen + t1 := g.new_tmp_var() + t2 := g.new_tmp_var() + g.writeln('(function() {') + g.inc_indent() + g.writeln('const $t1 = [];') + g.write('for (let $t2 = 0; $t2 < ') + g.expr(it.exprs[0]) + g.writeln('; $t2++) {') + g.inc_indent() + g.write('${t1}.push(') + if it.has_default { + g.expr(it.default_expr) + } else { + // Fill the array with the default values for its type + t := g.to_js_typ_val(it.elem_type) + g.write(t) + } + g.writeln(');') + g.dec_indent() + g.writeln('};') + g.writeln('return $t1;') + g.dec_indent() + g.write('})()') } else { g.gen_array_init_values(it.exprs) } @@ -1549,7 +1577,9 @@ fn (mut g JsGen) gen_if_expr(node ast.IfExpr) { g.write(' : ') } if i < node.branches.len - 1 || !node.has_else { + g.write('(') g.expr(branch.cond) + g.write(')') g.write('.valueOf()') g.write(' ? ') } @@ -1570,7 +1600,9 @@ fn (mut g JsGen) gen_if_expr(node ast.IfExpr) { if '$branch.cond' == 'js' { g.write('true') } else { + g.write('(') g.expr(branch.cond) + g.write(')') g.write('.valueOf()') } g.writeln(') {') @@ -1578,7 +1610,9 @@ fn (mut g JsGen) gen_if_expr(node ast.IfExpr) { } } else if i < node.branches.len - 1 || !node.has_else { g.write('} else if (') + g.write('(') g.expr(branch.cond) + g.write(')') g.write('.valueOf()') g.writeln(') {') } else if i == node.branches.len - 1 && node.has_else { @@ -1608,7 +1642,7 @@ fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) { if expr.index is ast.RangeExpr { g.expr(expr.left) if expr.left_type.is_ptr() { - g.write('.val') + g.write('.valueOf()') } g.write('.slice(') if expr.index.has_low { @@ -1622,7 +1656,7 @@ fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) { } else { g.expr(expr.left) if expr.left_type.is_ptr() { - g.write('.val') + g.write('.valueOf()') } g.write('.length') } @@ -1649,7 +1683,7 @@ fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) { g.write('new byte(') g.expr(expr.left) if expr.left_type.is_ptr() { - g.write('.val') + g.write('.valueOf()') } g.write('.str.charCodeAt(') g.expr(expr.index) @@ -1659,10 +1693,10 @@ fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) { // TODO Does this cover all cases? g.expr(expr.left) if expr.left_type.is_ptr() { - g.write('.val') + g.write('.valueOf()') } g.write('.arr') - g.write('[') + g.write('[+') g.cast_stack << ast.int_type_idx g.expr(expr.index) g.cast_stack.delete_last() @@ -1682,7 +1716,10 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) { if it.op == .eq || it.op == .ne { // Shallow equatables if l_sym.kind in js.shallow_equatables && r_sym.kind in js.shallow_equatables { + // wrap left expr in parens so binary operations will work correctly. + g.write('(') g.expr(it.left) + g.write(')') g.write('.eq(') g.cast_stack << int(l_sym.kind) g.expr(it.right) @@ -1713,7 +1750,7 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) { } else if r_sym.kind == .string { g.write('.str.includes(') } else { - g.write('.arr.includes(') + g.write('.\$includes(') } g.expr(it.left) if l_sym.kind == .string { @@ -1728,14 +1765,15 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) { is_arithmetic := it.op in [token.Kind.plus, .minus, .mul, .div, .mod] mut needs_cast := is_arithmetic && it.left_type != it.right_type mut greater_typ := 0 - if needs_cast { + // todo(playX): looks like this cast is always required to perform .eq operation on types. + if true || needs_cast { greater_typ = g.greater_typ(it.left_type, it.right_type) if g.cast_stack.len > 0 { needs_cast = g.cast_stack.last() != greater_typ } } - if needs_cast { + if true || needs_cast { if g.ns.name == 'builtin' { g.write('new ') } @@ -1746,7 +1784,7 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) { g.write(' $it.op ') g.expr(it.right) - if needs_cast { + if true || needs_cast { g.cast_stack.delete_last() g.write(')') } diff --git a/vlib/v/gen/js/temp_fast_deep_equal.v b/vlib/v/gen/js/temp_fast_deep_equal.v index dac4f24cda..9015331a4c 100644 --- a/vlib/v/gen/js/temp_fast_deep_equal.v +++ b/vlib/v/gen/js/temp_fast_deep_equal.v @@ -9,8 +9,8 @@ function vEq(a, b) { if (a && b && typeof a == 'object' && typeof b == 'object') { if (a.constructor !== b.constructor) return false; - a = a.valueOf(); - b = b.valueOf(); + a = a.\$toJS(); + 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 ffeaeacc6a..0822ce3c74 100644 --- a/vlib/v/gen/js/tests/testdata/array.out +++ b/vlib/v/gen/js/tests/testdata/array.out @@ -108,4 +108,57 @@ abc 6 1 4 -6 \ No newline at end of file +6 +[4,3,2,1] +true +true +true +true +true +true +true +0 +[0,0,0,0] +[0,7,0,0] +0 +[2,4,6,8,10,12,14,16,18,20] +[2,4,6,8,10,12,14,16,18,20] +[2,4,6,8,10] +2 +[1,2] +0 +1 +-1 +0 +3 +-1 +0 +2 +-1 +1 +2 +-1 +2 +3 +1 +3 +6 +true +true +true +true +true +true +true +true +true +0 +0 +0 +0 +0 +[2,4,6] +["is","awesome"] +[2,3,4,6,8,9,10] +[4,5,6] +[5,10] diff --git a/vlib/v/gen/js/tests/testdata/array.v b/vlib/v/gen/js/tests/testdata/array.v index 3278bd46ce..53e9c1b409 100644 --- a/vlib/v/gen/js/tests/testdata/array.v +++ b/vlib/v/gen/js/tests/testdata/array.v @@ -6,6 +6,42 @@ struct Kkk { q []Chunk } +const ( + c_n = 5 +) + +fn filter_test_helper_1(a int) bool { + return a > 3 +} + +fn sum(prev int, curr int) int { + return prev + curr +} + +fn sub(prev int, curr int) int { + return prev - curr +} + +struct Foooj { + a [5]int // c_n +} + +fn double_up(mut a []int) { + for i := 0; i < a.len; i++ { + a[i] = a[i] * 2 + } +} + +fn double_up_v2(mut a []int) { + for i, _ in a { + a[i] = a[i] * 2 // or val*2, doesn't matter + } +} + +fn modify(mut numbers []int) { + numbers[0] = 777 +} + fn main() { { // test pointer @@ -262,6 +298,7 @@ fn main() { } } { + // todo(playX): deep repeat does not yet work. /* // test deep repeat mut a3 := [[[1, 1], [2, 2], [3, 3]], [[4, 4], [5, 5], [6, 6]]] @@ -319,4 +356,175 @@ fn main() { println(a[3]) println(a[5]) } + { + // test reverse + a := [1, 2, 3, 4] + b := ['test', 'array', 'reverse'] + c := a.reverse() + println(c) + d := b.reverse() + for i, _ in c { + println(c[i] == a[a.len - i - 1]) + } + for i, _ in d { + println(d[i] == b[b.len - i - 1]) + } + e := []int{} + f := e.reverse() + println(f.len) + } + { + // test fixed + mut nums := [4]int{} + // x := nums[1..3] + // assert x.len == 2 + + println(nums) + nums[1] = 7 + println(nums) + nums2 := [5]int{} // c_n + println(nums2[c_n - 1]) + } + { + // test mut slice + /* + todo(playX): slices do not work yet. We have to implement custom wrapper for them. + mut n := [1, 2, 3] + // modify(mut n) + modify(mut n[..2]) + assert n[0] == 777 + modify(mut n[2..]) + assert n[2] == 777 + println(n) + */ + } + { + // test mut arg + mut arr := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + double_up(mut arr) + println(arr.str()) + arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + double_up_v2(mut arr) + println(arr.str()) + } + { + // test doubling + mut nums := [1, 2, 3, 4, 5] + for i in 0 .. nums.len { + nums[i] *= 2 + } + println(nums.str()) + } + { + // test single element + + mut a := [1] + a << 2 + println(a.len) + assert a[0] == 1 + assert a[1] == 2 + println(a) + } + { + // test find index + + // string + a := ['v', 'is', 'great'] + println(a.index('v')) + println(a.index('is')) + println(a.index('gre')) + // int + b := [1, 2, 3, 4] + println(b.index(1)) + println(b.index(4)) + println(b.index(5)) + // byte + c := [0x22, 0x33, 0x55] + println(c.index(0x22)) + println(c.index(0x55)) + println(c.index(0x99)) + // char + d := [`a`, `b`, `c`] + println(d.index(`b`)) + println(d.index(`c`)) + println(d.index(`u`)) + } + { + // test multi + + a := [[1, 2, 3], [4, 5, 6]] + println(a.len) + println(a[0].len) + println(a[0][0]) + println(a[0][2]) + println(a[1][2]) + } + { + // test in + a := [1, 2, 3] + println(1 in a) + println(2 in a) + println(3 in a) + println(!(4 in a)) + println(!(0 in a)) + println(0 !in a) + println(4 !in a) + b := [1, 4, 0] + c := [3, 6, 2, 0] + println(0 in b) + println(0 in c) + } + { + // test reduce + a := [1, 2, 3, 4, 5] + b := a.reduce(sum, 0) + c := a.reduce(sum, 5) + d := a.reduce(sum, -1) + println(b) + println(c) + println(d) + e := [1, 2, 3] + f := e.reduce(sub, 0) + g := e.reduce(sub, -1) + println(f) + println(g) + } + { + a := [1, 2, 3, 4, 5, 6] + b := a.filter(it % 2 == 0) + println(b) + + c := ['v', 'is', 'awesome'] + d := c.filter(it.len > 1) + println(d) + assert d[0] == 'is' + assert d[1] == 'awesome' + //////// + arr := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + println(arr.filter(it % 2 == 0 || it % 3 == 0)) + + mut mut_arr := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + mut_arr = mut_arr.filter(it < 4) + assert mut_arr.len == 3 + println(a.filter(filter_test_helper_1)) + 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] + */ + } + { + /* + a := [1, 2, 3, 4].filter(fn (i int) bool { + return i % 2 == 0 + }) + assert a == [2, 4] + */ + } }