From 863dd0b23ecf01da95d4e81d18a4e953912e71b4 Mon Sep 17 00:00:00 2001 From: playX Date: Sun, 26 Sep 2021 07:33:53 +0300 Subject: [PATCH] js: array improvements (#11952) --- vlib/builtin/js/array.js.v | 214 +++++++++++++++++++------ vlib/builtin/js/string.js.v | 2 +- vlib/strings/builder.js.v | 2 +- vlib/v/gen/js/array.v | 153 +++++++++++------- vlib/v/gen/js/auto_eq_methods.v | 38 ++--- vlib/v/gen/js/auto_str_methods.v | 14 +- vlib/v/gen/js/builtin_types.v | 4 +- vlib/v/gen/js/fn.v | 2 +- vlib/v/gen/js/infix.v | 48 +++++- vlib/v/gen/js/js.v | 155 ++++++++++++++---- vlib/v/gen/js/tests/testdata/array.out | 30 ++-- 11 files changed, 469 insertions(+), 193 deletions(-) diff --git a/vlib/builtin/js/array.js.v b/vlib/builtin/js/array.js.v index 457517ca97..a01b98e6a4 100644 --- a/vlib/builtin/js/array.js.v +++ b/vlib/builtin/js/array.js.v @@ -1,12 +1,69 @@ module builtin +/// Internal representation of `array` type. It is used to implement slices and to make slices behave correctly +/// it simply stores reference to original array and to index them properly it does index array relative to `index_start`. +struct array_buffer { + arr JS.Array + index_start int + len int + cap int + has_slice bool +} + +fn (mut a array_buffer) make_copy() { + if a.index_start != 0 || a.has_slice { + mut new_arr := JS.makeEmtpyJSArray() + for mut i in 0 .. a.len { + #new_arr.push(a.val.get(i)) + + mut x := i + x = x + } + new_arr = new_arr + #a.val.arr = new_arr + #a.val.index_start = new int(0) + #a.val.has_slice = new bool(false) + } +} + +#array_buffer.prototype.make_copy = function() { return array_buffer_make_copy(this) } +// TODO(playX): Should this be implemented fully in JS, use generics or just voidptr? +fn (a array_buffer) get(ix int) voidptr { + mut res := voidptr(0) + #res = a.arr[a.index_start.val + ix.val]; + + return res +} + +fn (mut a array_buffer) set(ix int, val voidptr) { + #a.val.arr[a.val.index_start.valueOf() + ix.valueOf()] = val; +} + +#array_buffer.prototype.get = function(ix) { return array_buffer_get(this,ix);} +#array_buffer.prototype.set = function(ix,val) { array_buffer_set(this,ix,val); } + struct array { - arr JS.Array + arr array_buffer pub: len int cap int } +fn v_sort(mut arr array, comparator fn (voidptr, voidptr) int) { + mut need_iter := true + for need_iter { + need_iter = false + for i := 1; i < arr.len; i++ { + if comparator(arr[i], arr[i - 1]) != 1 { + tmp := arr[i] + arr[i] = arr[i - 1] + arr[i - 1] = tmp + need_iter = true + } + } + } +} + #function flatIntoArray(target, source, sourceLength, targetIndex, depth) { #"use strict"; # @@ -43,25 +100,32 @@ pub fn (a array) repeat_to_depth(count int, depth int) array { panic('array.repeat: count is negative: $count') } mut arr := empty_array() - #let tmp = new Array(a.arr.length * +count); - #tmp.fill(a.arr); - # - #arr.arr = flatArray(tmp,depth+1); + if a.len > 0 { + for _ in 0 .. count { + for i in 0 .. a.len { + if depth > 0 { + // TODO + } else { + arr.push(a.arr.get(i)) + } + } + } + } return arr } // last returns the last element of the array. pub fn (a array) last() voidptr { mut res := voidptr(0) - #res = a.arr[a.len-1]; + #res = a.arr.get(new int(a.len-1)); return res } fn (a array) get(ix int) voidptr { mut result := voidptr(0) - #result = a.arr[ix] + #result = a.arr.get(ix) return result } @@ -72,28 +136,34 @@ pub fn (a array) repeat(count int) array { } } -fn empty_array() array { - mut arr := array{} - #arr = new array([]) +#function makeEmptyArray() { return new array(new array_buffer({})); } +#function makeEmtpyJSArray() { return new Array(); } - return arr +fn JS.makeEmptyArray() array +fn JS.makeEmtpyJSArray() JS.Array +fn empty_array() array { + return JS.makeEmptyArray() } fn (a &array) set_len(i int) { - #a.arr.length=i + #a.arr.arr.length=i } pub fn (mut a array) sort_with_compare(compare voidptr) { - #a.val.arr.sort(compare) + v_sort(mut a, compare) +} + +pub fn (mut a array) sort_with_compare_old(compare voidptr) { + #a.val.arr.arr.sort(compare) } pub fn (mut a array) sort() { - #a.val.arr.sort($sortComparator) + #a.val.arr.arr.sort($sortComparator) } pub fn (a array) index(v string) int { for i in 0 .. a.len { - #if (a.arr[i].toString() == v.toString()) + #if (a.arr.get(i).toString() == v.toString()) { return i @@ -104,39 +174,80 @@ pub fn (a array) index(v string) int { pub fn (a array) slice(start int, end int) array { mut result := a - #result = new array(a.arr.slice(start,end)) + #let slice = a.arr.arr.slice(start,end) + #result = new array(new array_buffer({arr: a.arr.arr, len: new int(slice.length),cap: new int(slice.length),index_start: new int(start)})) + #a.arr.has_slice = true return result } pub fn (mut a array) insert(i int, val voidptr) { - #a.val.arr.splice(i,0,val) + #a.val.arr.make_copy() + #a.val.arr.arr.splice(i,0,val) } pub fn (mut a array) insert_many(i int, val voidptr, size int) { - #a.val.arr.splice(i,0,...val.slice(0,+size)) + #a.val.arr.arr.splice(i,0,...val.arr.slice(0,+size)) } pub fn (mut a array) join(separator string) string { mut res := '' - #res = new string(a.val.arr.join(separator +'')); + #res = new string(a.val.arr.arr.join(separator +'')); return res } -fn (a array) push(val voidptr) { - #a.arr.push(val) +fn (mut a array) push(val voidptr) { + #a.val.arr.make_copy() + #a.val.arr.arr.push(val) } -#array.prototype[Symbol.iterator] = function () { return this.arr[Symbol.iterator](); } -#array.prototype.entries = function () { let result = []; for (const [key,val] of this.arr.entries()) { result.push([new int(key), val]); } return result[Symbol.iterator](); } -#array.prototype.map = function(callback) { return new array(this.arr.map(callback)); } -#array.prototype.filter = function(callback) { return new array(this.arr.filter( function (it) { return (+callback(it)) != 0; } )); } +fn v_filter(arr array, callback fn (voidptr) bool) array { + mut filtered := empty_array() + + for i := 0; i < arr.arr.len; i++ { + if callback(arr.arr.get(i)) { + filtered.push(arr.arr.get(i)) + } + } + return filtered +} + +fn v_map(arr array, callback fn (voidptr) voidptr) array { + mut maped := empty_array() + + for i := 0; i < arr.arr.len; i++ { + maped.push(callback(arr.arr.get(i))) + } + + return maped +} + +struct array_iterator { + ix int + end int + arr JS.Array +} + +#array_iterator.prototype.next = function () { +#if (this.ix.val < this.end.val) { +#this.ix.val++; +#return {done: false, value: this.arr.arr.get(new int(this.ix.val-1))} +#} else { +#return {done: true, value: undefined} +#} +#} +#array_iterator.prototype[Symbol.iterator] = function () { return this; } + +#array.prototype[Symbol.iterator] = function () { console.log(this.arr.index_start); return new array_iterator({ix: new int(0),end: new int(this.arr.len),arr: this}); } +#array.prototype.entries = function () { let result = []; for (let key = this.arr.index_start.val;key < this.arr.len.val;key++) { result.push([new int(key), this.arr.get(new int(key))]); } return result[Symbol.iterator](); } +#array.prototype.map = function(callback) { return v_map(this,callback); } +#array.prototype.filter = function(callback) { return v_filter(this,callback); } #Object.defineProperty(array.prototype,'cap',{ get: function () { return this.len; } }) #array.prototype.any = function (value) { #let val ;if (typeof value == 'function') { val = function (x) { return value(x); } } else { val = function (x) { return vEq(x,value); } } -#for (let i = 0;i < this.arr.length;i++) -#if (val(this.arr[i])) +#for (let i = 0;i < this.arr.arr.length;i++) +#if (val(this.arr.get(i))) #return true; # #return false; @@ -144,12 +255,17 @@ fn (a array) push(val voidptr) { #array.prototype.all = function (value) { #let val ;if (typeof value == 'function') { val = function (x) { return value(x); } } else { val = function (x) { return vEq(x,value); } } -#for (let i = 0;i < this.arr.length;i++) -#if (!val(this.arr[i])) +#for (let i = 0;i < this.arr.arr.length;i++) +#if (!val(this.arr.get(i))) #return false; # #return true; #} +//#Object.defineProperty(array_buffer.prototype,"len", { get: function() {return new int(this.arr.length);}, set: function(l) { this.arr.length = l.valueOf(); } }) +//#Object.defineProperty(array_buffer.prototype,"cap", { get: function() {return new int(this.arr.length);}, set: function(l) { this.arr.length = l.valueOf(); } }) +# +# +#function v_makeSlice(array) { Object.defineProperty(array,'len', {get: function() { return this.arr.len; }, set: function(l) { this.arr.len = l; }}) } // delete deletes array element at index `i`. pub fn (mut a array) delete(i int) { a.delete_many(i, 1) @@ -157,7 +273,8 @@ pub fn (mut a array) delete(i int) { // delete_many deletes `size` elements beginning with index `i` pub fn (mut a array) delete_many(i int, size int) { - #a.val.arr.splice(i.valueOf(),size.valueOf()) + #a.val.arr.make_copy() + #a.val.arr.arr.splice(i.valueOf(),size.valueOf()) } // prepend prepends one value to the array. @@ -172,48 +289,52 @@ pub fn (mut a array) prepend_many(val voidptr, size int) { } pub fn (a array) reverse() array { - mut res := array{} - #res.arr = Array.from(a.arr).reverse() + mut res := empty_array() + #res.arr.arr = Array.from(a.arr).reverse() return res } pub fn (mut a array) reverse_in_place() { - #a.val.arr.reverse() + #a.val.arr.arr.reverse() } -#array.prototype.$includes = function (elem) { return this.arr.find(function(e) { return vEq(elem,e); }) !== undefined;} +#array.prototype.$includes = function (elem) { return this.arr.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 = 0;i < a.arr.length;i++) { + /*#for (let i = 0;i < a.arr.length;i++) { #accum_ = iter(accum_, a.arr[i]) - #} + #}*/ + for i in 0 .. a.len { + accum_ = iter(accum_, a.get(i)) + } return accum_ } pub fn (mut a array) pop() voidptr { mut res := voidptr(0) - #res = a.val.arr.pop() + #a.val.arr.make_copy() + #res = a.val.arr.arr.pop() return res } pub fn (a array) first() voidptr { mut res := voidptr(0) - #res = a.arr[0] + #res = a.arr.get(new int(0)) return res } #array.prototype.toString = function () { #let res = "[" -#for (let i = 0; i < this.arr.length;i++) { -#res += this.arr[i].toString(); -#if (i != this.arr.length-1) +#for (let i = 0; i < this.arr.arr.length;i++) { +#res += this.arr.get(i).toString(); +#if (i != this.arr.arr.length-1) #res += ', ' #} #res += ']' @@ -221,16 +342,11 @@ pub fn (a array) first() voidptr { # #} -pub fn (a array) contains(key voidptr) bool { - #for (let i = 0; i < a.arr.length;i++) - #if (vEq(a.arr[i],key)) return new bool(true); - - return false -} +pub fn (a array) contains(key voidptr) bool // delete_last effectively removes last element of an array. pub fn (mut a array) delete_last() { - #a.val.arr.pop(); + #a.val.arr.arr.pop(); } [unsafe] @@ -240,7 +356,7 @@ pub fn (a &array) free() { // todo: once (a []byte) will work rewrite this pub fn (a array) bytestr() string { res := '' - #a.arr.forEach((item) => res.str += String.fromCharCode(+item)) + #a.arr.arr.forEach((item) => res.str += String.fromCharCode(+item)) return res } diff --git a/vlib/builtin/js/string.js.v b/vlib/builtin/js/string.js.v index 49c9537efa..d276435d61 100644 --- a/vlib/builtin/js/string.js.v +++ b/vlib/builtin/js/string.js.v @@ -42,7 +42,7 @@ pub fn (s string) bool() bool { pub fn (s string) split(dot string) []string { mut arr := s.str.split(dot.str).map(string(it)) - #arr = new array(arr) + #arr = new array(new array_buffer({arr,index_start: new int(0),len: new int(arr.length)})) return arr } diff --git a/vlib/strings/builder.js.v b/vlib/strings/builder.js.v index ae16b2d045..febf3a80a5 100644 --- a/vlib/strings/builder.js.v +++ b/vlib/strings/builder.js.v @@ -52,7 +52,7 @@ pub fn (mut b Builder) writeln(s string) { pub fn (mut b Builder) str() string { s := '' - #for (const c of b.val.buf.arr) + #for (const c of b.val.buf.arr.arr) #s.str += String.fromCharCode(+c) return s diff --git a/vlib/v/gen/js/array.v b/vlib/v/gen/js/array.v index 2cbdbd1c8a..0c4d214aeb 100644 --- a/vlib/v/gen/js/array.v +++ b/vlib/v/gen/js/array.v @@ -29,19 +29,22 @@ fn (mut g JsGen) gen_array_index_method(left_type ast.Type) string { mut fn_builder := strings.new_builder(512) fn_builder.writeln('function ${fn_name}(a, v) {') fn_builder.writeln('\tlet pelem = a.arr;') - fn_builder.writeln('\tfor (let i = 0; i < pelem.length; ++i) {') + fn_builder.writeln('\tfor (let i = 0; i < pelem.arr.length; ++i) {') if elem_sym.kind == .string { - fn_builder.writeln('\t\tif (pelem[i].str == v.str) {') + fn_builder.writeln('\t\tif (pelem.get(new int(i)).str == v.str) {') } else if elem_sym.kind == .array && !info.elem_type.is_ptr() { - fn_builder.writeln('\t\tif (vEq(pelem[i], v)) {') + ptr_typ := g.gen_array_equality_fn(info.elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq(pelem.get(new int(i)), v).val) {') } else if elem_sym.kind == .function && !info.elem_type.is_ptr() { - fn_builder.writeln('\t\tif ( vEq(pelem[i], v)) {') + fn_builder.writeln('\t\tif (pelem.get(new int(i)) == v) {') } else if elem_sym.kind == .map && !info.elem_type.is_ptr() { - fn_builder.writeln('\t\tif (vEq(pelem[i], v)) {') + ptr_typ := g.gen_map_equality_fn(info.elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_map_eq(pelem.get(new int(i)), v).val) {') } else if elem_sym.kind == .struct_ && !info.elem_type.is_ptr() { - fn_builder.writeln('\t\tif (vEq(pelem[i], v)) {') + ptr_typ := g.gen_struct_equality_fn(info.elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq(pelem.get(new int(i)), v)) {') } else { - fn_builder.writeln('\t\tif (pelem[i].valueOf() == v.valueOf()) {') + fn_builder.writeln('\t\tif (vEq(pelem.get(new int(i)), v)) {') } fn_builder.writeln('\t\t\treturn new int(i);') fn_builder.writeln('\t\t}') @@ -131,49 +134,7 @@ fn (mut g JsGen) gen_array_method_call(it ast.CallExpr) { return } 'sort' { - g.write('array') - rec_sym := g.table.get_type_symbol(node.receiver_type) - if rec_sym.kind != .array { - println(node.name) - println(g.typ(node.receiver_type)) - // println(rec_sym.kind) - verror('.sort() is an array method') - } - - // `users.sort(a.age > b.age)` - - if node.args.len == 0 { - g.write('_sort(') - g.expr(it.left) - mut ltyp := it.left_type - for ltyp.is_ptr() { - g.write('.val') - ltyp = ltyp.deref() - } - - g.write(')') - return - } else { - g.expr(it.left) - mut ltyp := it.left_type - for ltyp.is_ptr() { - g.write('.val') - ltyp = ltyp.deref() - } - g.write('.') - infix_expr := node.args[0].expr as ast.InfixExpr - left_name := infix_expr.left.str() - is_reverse := (left_name.starts_with('a') && infix_expr.op == .gt) - || (left_name.starts_with('b') && infix_expr.op == .lt) - if is_reverse { - g.write('arr.sort(function (b,a) {') - } else { - g.write('arr.sort(function (a,b) {') - } - g.write('return ') - g.write('\$sortComparator(a,b)') - g.write('})') - } + g.gen_array_sort(node) } else {} } @@ -219,17 +180,20 @@ fn (mut g JsGen) gen_array_contains_method(left_type ast.Type) string { fn_builder.writeln('function ${fn_name}(a,v) {') fn_builder.writeln('\tfor (let i = 0; i < a.len; ++i) {') if elem_sym.kind == .string { - fn_builder.writeln('\t\tif (a.arr[i].str == v.str) {') + fn_builder.writeln('\t\tif (a.arr.arr[i].str == v.str) {') } else if elem_sym.kind == .array && left_info.elem_type.nr_muls() == 0 { - fn_builder.writeln('\t\tif (vEq(a.arr[i], v)) {') + ptr_typ := g.gen_array_equality_fn(left_info.elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq(a.arr.arr[i],v)) {') } else if elem_sym.kind == .function { - fn_builder.writeln('\t\tif (a.arr[i] == v) {') + fn_builder.writeln('\t\tif (a.arr.arr[i] == v) {') } else if elem_sym.kind == .map && left_info.elem_type.nr_muls() == 0 { - fn_builder.writeln('\t\tif (vEq(a.arr[i], v)) {') + ptr_typ := g.gen_map_equality_fn(left_info.elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_map_eq(a.arr.arr[i],v)) {') } else if elem_sym.kind == .struct_ && left_info.elem_type.nr_muls() == 0 { - fn_builder.writeln('\t\tif (vEq(a.arr[i],v)) {') + ptr_typ := g.gen_struct_equality_fn(left_info.elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq(a.arr.arr[i],v)) {') } else { - fn_builder.writeln('\t\tif (a.arr[i].valueOf() == v.valueOf()) {') + fn_builder.writeln('\t\tif (vEq(a.arr.arr[i],v)) {') } fn_builder.writeln('\t\t\treturn new bool(true);') fn_builder.writeln('\t\t}') @@ -248,3 +212,80 @@ fn (mut g JsGen) gen_array_contains_method(left_type ast.Type) string { } return fn_name } + +fn (mut g JsGen) gen_array_sort(node ast.CallExpr) { + rec_sym := g.table.get_type_symbol(node.receiver_type) + if rec_sym.kind != .array { + println(node.name) + verror('.sort() is an array method') + } + + info := rec_sym.info as ast.Array + + elem_stype := g.typ(info.elem_type) + mut compare_fn := 'compare_${elem_stype.replace('*', '_ptr')}' + mut comparison_type := g.unwrap(ast.void_type) + mut left_expr, mut right_expr := '', '' + + if node.args.len == 0 { + comparison_type = g.unwrap(info.elem_type.set_nr_muls(0)) + if compare_fn in g.array_sort_fn { + g.gen_array_sort_call(node, compare_fn) + return + } + + left_expr = 'a' + right_expr = 'b' + } else { + infix_expr := node.args[0].expr as ast.InfixExpr + comparison_type = g.unwrap(infix_expr.left_type.set_nr_muls(0)) + left_name := infix_expr.left.str() + if left_name.len > 1 { + compare_fn += '_by' + left_name[1..].replace_each(['.', '_', '[', '_', ']', '_']) + } + // is_reverse is `true` for `.sort(a > b)` and `.sort(b < a)` + is_reverse := (left_name.starts_with('a') && infix_expr.op == .gt) + || (left_name.starts_with('b') && infix_expr.op == .lt) + if is_reverse { + compare_fn += '_reverse' + } + if compare_fn in g.array_sort_fn { + g.gen_array_sort_call(node, compare_fn) + return + } + if left_name.starts_with('a') != is_reverse { + left_expr = g.expr_string(infix_expr.left) + right_expr = g.expr_string(infix_expr.right) + } else { + left_expr = g.expr_string(infix_expr.right) + right_expr = g.expr_string(infix_expr.left) + } + } + + // Register a new custom `compare_xxx` function for qsort() + // TODO: move to checker + g.table.register_fn(name: compare_fn, return_type: ast.int_type) + g.array_sort_fn[compare_fn] = true + + g.definitions.writeln('function ${compare_fn}(a,b) {') + c_condition := if comparison_type.sym.has_method('<') { + '${g.typ(comparison_type.typ)}__lt($left_expr, $right_expr)' + } else if comparison_type.unaliased_sym.has_method('<') { + '${g.typ(comparison_type.unaliased)}__lt($left_expr, $right_expr)' + } else { + '${left_expr}.valueOf() < ${right_expr}.valueOf()' + } + g.definitions.writeln('\tif ($c_condition) return -1;') + g.definitions.writeln('\telse return 1;') + g.definitions.writeln('}\n') + + // write call to the generated function + g.gen_array_sort_call(node, compare_fn) +} + +fn (mut g JsGen) gen_array_sort_call(node ast.CallExpr, compare_fn string) { + g.write('v_sort(') + g.expr(node.left) + g.gen_deref_ptr(node.left_type) + g.write(',$compare_fn)') +} diff --git a/vlib/v/gen/js/auto_eq_methods.v b/vlib/v/gen/js/auto_eq_methods.v index eaa447c191..039cda4988 100644 --- a/vlib/v/gen/js/auto_eq_methods.v +++ b/vlib/v/gen/js/auto_eq_methods.v @@ -182,29 +182,29 @@ fn (mut g JsGen) gen_array_equality_fn(left_type ast.Type) string { fn_builder.writeln('\tfor (let i = 0; i < a.len; ++i) {') // compare every pair of elements of the two arrays if elem.sym.kind == .string { - fn_builder.writeln('\t\tif (a.arr[i].str != b.arr[i].str) {') + fn_builder.writeln('\t\tif (a.arr.get(new int(i)).str != b.arr.get(new int(i)).str) {') } else if elem.sym.kind == .sum_type && !elem.typ.is_ptr() { eq_fn := g.gen_sumtype_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_sumtype_eq(a.arr[i],b.arr[i]).val) {') + fn_builder.writeln('\t\tif (!${eq_fn}_sumtype_eq(a.arr.get(new int(i)),b.arr.get(new int(i))).val) {') } else if elem.sym.kind == .struct_ && !elem.typ.is_ptr() { eq_fn := g.gen_struct_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_struct_eq(a.arr[i],b.arr[i]).val) {') + fn_builder.writeln('\t\tif (!${eq_fn}_struct_eq(a.arr.get(new int(i)),b.arr.get(new int(i))).val) {') } else if elem.sym.kind == .array && !elem.typ.is_ptr() { eq_fn := g.gen_array_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(a.arr[i],b.arr[i]).val) {') + fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(a.arr.get(new int(i)),b.arr.get(new int(i))).val) {') } else if elem.sym.kind == .array_fixed && !elem.typ.is_ptr() { eq_fn := g.gen_fixed_array_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(a.arr[i],b.arr[i]).val) {') + fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(a.arr.get(new int(i)),b.arr.get(new int(i))).val) {') } else if elem.sym.kind == .map && !elem.typ.is_ptr() { eq_fn := g.gen_map_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_map_eq(a.arr[i],b.arr[i]).val) {') + fn_builder.writeln('\t\tif (!${eq_fn}_map_eq(a.arr.get(new int(i)),b.arr.get(new int(i))).val) {') } else if elem.sym.kind == .alias && !elem.typ.is_ptr() { eq_fn := g.gen_alias_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_alias_eq(a.arr[i],b.arr[i]).val) {') + fn_builder.writeln('\t\tif (!${eq_fn}_alias_eq(a.arr.get(new int(i)),b.arr.get(new int(i))).val) {') } else if elem.sym.kind == .function { - fn_builder.writeln('\t\tif (a.arr[i] != b.arr[i]) {') + fn_builder.writeln('\t\tif (a.arr.get(new int(i)) != b.arr.get(new int(i))) {') } else { - fn_builder.writeln('\t\tif (!vEq(a.arr[i],b.arr[i])) {') + fn_builder.writeln('\t\tif (!vEq(a.arr.get(new int(i)),b.arr.get(new int(i)))) {') } fn_builder.writeln('\t\t\treturn new bool(false);') fn_builder.writeln('\t\t}') @@ -234,29 +234,29 @@ fn (mut g JsGen) gen_fixed_array_equality_fn(left_type ast.Type) string { fn_builder.writeln('\tfor (let i = 0; i < $size; ++i) {') // compare every pair of elements of the two fixed arrays if elem.sym.kind == .string { - fn_builder.writeln('\t\tif (a.arr[i].str != b.arr[i].str) {') + fn_builder.writeln('\t\tif (a.arr.get(new int(i)).str != b.arr.get(new int(i)).str) {') } else if elem.sym.kind == .sum_type && !elem.typ.is_ptr() { eq_fn := g.gen_sumtype_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_sumtype_eq(a.arr[i], b.arr[i])) {') + fn_builder.writeln('\t\tif (!${eq_fn}_sumtype_eq(a.arr.get(new int(i)), b.arr.get(new int(i)))) {') } else if elem.sym.kind == .struct_ && !elem.typ.is_ptr() { eq_fn := g.gen_struct_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_struct_eq(a.arr[i], b.arr[i])) {') + fn_builder.writeln('\t\tif (!${eq_fn}_struct_eq(a.arr.get(new int(i)), b.arr.get(new int(i)))) {') } else if elem.sym.kind == .array && !elem.typ.is_ptr() { eq_fn := g.gen_array_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(a.arr[i], b.arr[i])) {') + fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(a.arr.get(new int(i)), b.arr.get(new int(i)))) {') } else if elem.sym.kind == .array_fixed && !elem.typ.is_ptr() { eq_fn := g.gen_fixed_array_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(a.arr[i], b.arr[i])) {') + fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(a.arr.get(new int(i)), b.arr.get(new int(i)))) {') } else if elem.sym.kind == .map && !elem.typ.is_ptr() { eq_fn := g.gen_map_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_map_eq(a.arr[i], b.arr[i])) {') + fn_builder.writeln('\t\tif (!${eq_fn}_map_eq(a.arr.get(new int(i)), b.arr.get(new int(i)))) {') } else if elem.sym.kind == .alias && !elem.typ.is_ptr() { eq_fn := g.gen_alias_equality_fn(elem.typ) - fn_builder.writeln('\t\tif (!${eq_fn}_alias_eq(a.arr[i], b.arr[i])) {') + fn_builder.writeln('\t\tif (!${eq_fn}_alias_eq(a.arr.get(new int(i)), b.arr.get(new int(i)))) {') } else if elem.sym.kind == .function { - fn_builder.writeln('\t\tif (a.arr[i] != b.arr[i]) {') + fn_builder.writeln('\t\tif (a.arr.get(new int(i)) != b.arr.get(new int(i))) {') } else { - fn_builder.writeln('\t\tif (!vEq(a.arr[i],b.arr[i])) {') + fn_builder.writeln('\t\tif (!vEq(a.arr.get(new int(i)),b.arr.get(new int(i)))) {') } fn_builder.writeln('\t\t\treturn new bool(false);') fn_builder.writeln('\t\t}') @@ -281,7 +281,7 @@ fn (mut g JsGen) gen_map_equality_fn(left_type ast.Type) string { g.definitions.writeln(fn_builder.str()) } fn_builder.writeln('function ${ptr_styp}_map_eq(a,b) {') - fn_builder.writeln('\tif (a.map.size() != b.map.size()) {') + fn_builder.writeln('\tif (a.map.size != b.map.size) {') fn_builder.writeln('\t\treturn false;') fn_builder.writeln('\t}') fn_builder.writeln('\tfor (let [key,value] of a.map) {') diff --git a/vlib/v/gen/js/auto_str_methods.v b/vlib/v/gen/js/auto_str_methods.v index 2523a1bb58..18e881f818 100644 --- a/vlib/v/gen/js/auto_str_methods.v +++ b/vlib/v/gen/js/auto_str_methods.v @@ -444,7 +444,7 @@ fn (mut g JsGen) gen_str_for_array(info ast.Array, styp string, str_fn_name stri if sym.kind == .function { g.definitions.writeln('\t\tlet it = ${elem_str_fn_name}();') } else { - g.definitions.writeln('\t\tlet it = a.arr[i];') + g.definitions.writeln('\t\tlet it = a.arr.get(new int(i));') if should_use_indent_func(sym.kind) && !sym_has_str_method { if is_elem_ptr { @@ -506,23 +506,23 @@ fn (mut g JsGen) gen_str_for_array_fixed(info ast.ArrayFixed, styp string, str_f if should_use_indent_func(sym.kind) && !sym_has_str_method { if is_elem_ptr { g.definitions.writeln('\t\tstrings__Builder_write_string(sb, new string("$deref_label"));') - g.definitions.writeln('\t\tif ( 0 == a.arr[i] ) {') + g.definitions.writeln('\t\tif ( 0 == a.arr.get(new int(i)) ) {') g.definitions.writeln('\t\t\tstrings__Builder_write_string(sb, new string("0"));') g.definitions.writeln('\t\t}else{') - g.definitions.writeln('\t\t\tstrings__Builder_write_string(sb, ${elem_str_fn_name}(a.arr[i] $deref) );') + g.definitions.writeln('\t\t\tstrings__Builder_write_string(sb, ${elem_str_fn_name}(a.arr.get(new int(i)) $deref) );') g.definitions.writeln('\t\t}') } else { - g.definitions.writeln('\t\tstrings__Builder_write_string(sb, ${elem_str_fn_name}(a.arr[i]) );') + g.definitions.writeln('\t\tstrings__Builder_write_string(sb, ${elem_str_fn_name}(a.arr.get(new int(i))) );') } } else if sym.kind in [.f32, .f64] { - g.definitions.writeln('\t\tstrings__Builder_write_string(sb, new string(a.arr[i].val.toString()) );') + g.definitions.writeln('\t\tstrings__Builder_write_string(sb, new string(a.arr.get(new int(i)).val.toString()) );') } else if sym.kind == .string { - g.definitions.writeln('\t\tstrings__Builder_write_string(sb, a.arr[i].str);') + g.definitions.writeln('\t\tstrings__Builder_write_string(sb, a.arr.get(new int(i)));') } else if sym.kind == .rune { // tmp_str := str_intp_rune('${elem_str_fn_name}( a[i] $deref)') // g.definitions.writeln('\t\tstrings__Builder_write_string(sb, $tmp_str);') } else { - g.definitions.writeln('\t\tstrings__Builder_write_string(sb, ${elem_str_fn_name}(a.arr[i] $deref));') + g.definitions.writeln('\t\tstrings__Builder_write_string(sb, ${elem_str_fn_name}(a.arr.get(new int(i)) $deref));') } } g.definitions.writeln('\t\tif (i < ${info.size - 1}) {') diff --git a/vlib/v/gen/js/builtin_types.v b/vlib/v/gen/js/builtin_types.v index 2d58a56a18..19687f25c4 100644 --- a/vlib/v/gen/js/builtin_types.v +++ b/vlib/v/gen/js/builtin_types.v @@ -405,8 +405,8 @@ fn (mut g JsGen) gen_builtin_type_defs() { g.gen_builtin_prototype( typ_name: typ_name val_name: 'arr' - default_value: 'new Array()' - constructor: 'this.arr = arr' + default_value: 'new array_buffer({})' + constructor: 'this.arr = arr\nif (arr.index_start.val != 0) { v_makeSlice(this); } ' value_of: 'this' to_string: 'JSON.stringify(this.arr.map(it => it.valueOf()))' eq: 'new bool(vEq(self, other))' diff --git a/vlib/v/gen/js/fn.v b/vlib/v/gen/js/fn.v index 1d2cd25f08..e4bf0e5f03 100644 --- a/vlib/v/gen/js/fn.v +++ b/vlib/v/gen/js/fn.v @@ -482,7 +482,7 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) { is_varg := i == args.len - 1 && it.is_variadic arg_name := g.js_name(arg.name) if is_varg { - g.writeln('$arg_name = new array($arg_name);') + g.writeln('$arg_name = new array(new array_buffer({arr: $arg_name,len: new int(${arg_name}.length),index_start: new int(0)}));') } else { if arg.typ.is_ptr() || arg.is_mut { g.writeln('$arg_name = new \$ref($arg_name)') diff --git a/vlib/v/gen/js/infix.v b/vlib/v/gen/js/infix.v index d65157d347..ec53b6253c 100644 --- a/vlib/v/gen/js/infix.v +++ b/vlib/v/gen/js/infix.v @@ -236,15 +236,15 @@ fn (mut g JsGen) infix_expr_left_shift_op(node ast.InfixExpr) { if right.unaliased_sym.kind == .array && array_info.elem_type != right.typ { g.expr(node.left) g.gen_deref_ptr(left.typ) - g.write('.arr,...') + g.write('.arr.arr,...') g.expr(node.right) g.gen_deref_ptr(right.typ) - g.write('.arr') + g.write('.arr.arr') g.write(')') } else { g.expr(node.left) g.gen_deref_ptr(left.typ) - g.write('.arr,') + g.write('.arr.arr,') g.expr(node.right) g.write(')') } @@ -254,13 +254,45 @@ fn (mut g JsGen) infix_expr_left_shift_op(node ast.InfixExpr) { } fn (mut g JsGen) infix_in_not_in_op(node ast.InfixExpr) { - l_sym := g.table.get_final_type_symbol(node.left_type) - r_sym := g.table.get_final_type_symbol(node.right_type) + l_sym := g.unwrap(node.left_type) + r_sym := g.unwrap(node.right_type) if node.op == .not_in { g.write('!') } - g.expr(node.right) - g.gen_deref_ptr(node.right_type) + if r_sym.unaliased_sym.kind == .array { + fn_name := g.gen_array_contains_method(node.right_type) + g.write('(${fn_name}(') + g.expr(node.right) + g.gen_deref_ptr(node.right_type) + g.write(',') + g.expr(node.left) + g.write('))') + return + } else if r_sym.unaliased_sym.kind == .map { + g.expr(node.right) + g.gen_deref_ptr(node.right_type) + g.write('.map.has(') + g.expr(node.left) + if l_sym.sym.kind == .string { + g.write('.str') + } else { + g.write('.valueOf()') + } + g.write(')') + } else { + g.write('.str.includes(') + g.expr(node.right) + g.gen_deref_ptr(node.right_type) + g.expr(node.left) + if l_sym.sym.kind == .string { + g.write('.str') + } else { + g.write('.valueOf()') + } + g.write(')') + } + + /* if r_sym.kind == .map { g.write('.map.has(') } else if r_sym.kind == .string { @@ -272,7 +304,7 @@ fn (mut g JsGen) infix_in_not_in_op(node ast.InfixExpr) { if l_sym.kind == .string { g.write('.str') } - g.write(')') + g.write(')')*/ } fn (mut g JsGen) infix_is_not_is_op(node ast.InfixExpr) { diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 733cd4ba31..cb19f660a6 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -82,6 +82,7 @@ mut: comptime_var_type_map map[string]ast.Type defer_ifdef string out strings.Builder = strings.new_builder(128) + array_sort_fn map[string]bool } fn (mut g JsGen) write_tests_definitions() { @@ -141,10 +142,10 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { // builtin types if g.file.mod.name == 'builtin' && !g.generated_builtin { g.gen_builtin_type_defs() - g.writeln('Object.defineProperty(array.prototype,"len", { get: function() {return new int(this.arr.length);}, set: function(l) { this.arr.length = l.valueOf(); } }); ') + g.writeln('Object.defineProperty(array.prototype,"len", { get: function() {return new int(this.arr.arr.length);}, set: function(l) { this.arr.arr.length = l.valueOf(); } }); ') g.writeln('Object.defineProperty(string.prototype,"len", { get: function() {return new int(this.str.length);}, set: function(l) {/* ignore */ } }); ') g.writeln('Object.defineProperty(map.prototype,"len", { get: function() {return new int(this.map.length);}, set: function(l) { this.map.length = l.valueOf(); } }); ') - g.writeln('Object.defineProperty(array.prototype,"length", { get: function() {return new int(this.arr.length);}, set: function(l) { this.arr.length = l.valueOf(); } }); ') + g.writeln('Object.defineProperty(array.prototype,"length", { get: function() {return new int(this.arr.arr.length);}, set: function(l) { this.arr.arr.length = l.valueOf(); } }); ') g.generated_builtin = true } if g.is_test && !tests_inited { @@ -1120,9 +1121,32 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt, semicolon bool) { g.write('const ') } } - g.expr(left) + mut array_set := false + match left { + ast.IndexExpr { + g.expr(left.left) + if left.left_type.is_ptr() { + g.write('.valueOf()') + } + array_set = true + if g.table.get_type_symbol(left.left_type).kind == .map { + g.write('.map.set(') + } else { + g.write('.arr.set(') + } + g.write('new int(') + g.cast_stack << ast.int_type_idx + g.expr(left.index) + g.write('.valueOf()') + g.cast_stack.delete_last() + g.write('),') + } + else { + g.expr(left) + } + } mut is_ptr := false - if stmt.op == .assign && stmt.left_types[i].is_ptr() { + if stmt.op == .assign && stmt.left_types[i].is_ptr() && !array_set { is_ptr = true g.write('.val') } @@ -1135,13 +1159,58 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt, semicolon bool) { } g.write(')') } else { - if is_assign { + if is_assign && array_set { + g.expr(left) if l_sym.kind == .string { g.write('.str') } else { g.write('.val') } - g.write(' = ') + + match op { + .plus_assign { + g.write(' + ') + } + .minus_assign { + g.write(' - ') + } + .mult_assign { + g.write(' * ') + } + .div_assign { + g.write(' / ') + } + .mod_assign { + g.write(' % ') + } + .xor_assign { + g.write(' ^ ') + } + .and_assign { + g.write(' & ') + } + .right_shift_assign { + g.write(' >> ') + } + .left_shift_assign { + g.write(' << ') + } + .or_assign { + g.write(' | ') + } + else { + panic('unexpected op $op') + } + } + } else if is_assign && !array_set { + if l_sym.kind == .string { + g.write('.str') + } else { + g.write('.val') + } + if !array_set { + g.write(' = ') + } g.expr(left) match op { @@ -1180,7 +1249,10 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt, semicolon bool) { } } } else { - g.write(' $op ') + if op == .assign && array_set { + } else { + g.write(' $op ') + } } // TODO: Multiple types?? should_cast := @@ -1201,6 +1273,9 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt, semicolon bool) { g.cast_stack.delete_last() } } + if array_set { + g.write(')') + } if semicolon { if g.inside_loop { g.write('; ') @@ -1602,17 +1677,17 @@ fn (mut g JsGen) gen_array_init_expr(it ast.ArrayInit) { // 3) Have several limitations like missing most `Array.prototype` methods // 4) Modern engines can optimize regular arrays into typed arrays anyways, // offering similar performance - g.write('new array(') + g.write('new array(new array_buffer({arr: ') g.inc_indent() if it.has_len { t1 := g.new_tmp_var() t2 := g.new_tmp_var() - g.writeln('(function() {') + g.writeln('(function(length) {') g.inc_indent() g.writeln('const $t1 = [];') - g.write('for (let $t2 = 0; $t2 < ') - g.expr(it.len_expr) + g.write('for (let $t2 = 0; $t2 < length') + g.writeln('; $t2++) {') g.inc_indent() g.write('${t1}.push(') @@ -1628,7 +1703,14 @@ fn (mut g JsGen) gen_array_init_expr(it ast.ArrayInit) { g.writeln('};') g.writeln('return $t1;') g.dec_indent() - g.write('})()') + g.write('})(') + g.expr(it.len_expr) + g.write('),len: new int(') + g.expr(it.len_expr) + g.write(')') + g.write(', cap: new int(') + g.expr(it.len_expr) + g.write(')') } else if it.is_fixed && it.exprs.len == 1 { // [100]byte codegen t1 := g.new_tmp_var() @@ -1653,23 +1735,31 @@ fn (mut g JsGen) gen_array_init_expr(it ast.ArrayInit) { g.writeln('};') g.writeln('return $t1;') g.dec_indent() - g.write('})()') + g.write('})(), len: new int(') + g.expr(it.exprs[0]) + g.write('), cap: new int(') + g.expr(it.exprs[0]) + g.write(')') } else { - g.gen_array_init_values(it.exprs) + c := g.gen_array_init_values(it.exprs) + g.write(', len: new int($c), cap: new int($c)') } g.dec_indent() - g.write(')') + g.write('}))') } -fn (mut g JsGen) gen_array_init_values(exprs []ast.Expr) { +fn (mut g JsGen) gen_array_init_values(exprs []ast.Expr) int { g.write('[') + mut c := 0 for i, expr in exprs { g.expr(expr) if i < exprs.len - 1 { g.write(', ') } + c++ } g.write(']') + return c } fn (mut g JsGen) gen_ident(node ast.Ident) { @@ -2218,13 +2308,13 @@ fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) { if expr.left_type.is_ptr() { g.write('.valueOf()') } - g.write('.arr') - g.write('[Number(') + g.write('.arr.get(') + g.write('new int(') g.cast_stack << ast.int_type_idx g.expr(expr.index) g.write('.valueOf()') g.cast_stack.delete_last() - g.write(')]') + g.write('))') } } @@ -2236,6 +2326,12 @@ fn (mut g JsGen) gen_deref_ptr(ty ast.Type) { } } +fn (mut g JsGen) expr_string(expr ast.Expr) string { + pos := g.out.len + g.expr(expr) + return g.out.cut_to(pos).trim_space() +} + fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) { l_sym := g.table.get_final_type_symbol(it.left_type) r_sym := g.table.get_final_type_symbol(it.right_type) @@ -2318,7 +2414,7 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) { g.write('.val') ltyp = ltyp.deref() } - g.write('.arr,') + g.write('.arr.arr,') array_info := l_sym.info as ast.Array // arr << [1, 2] if r_sym.kind == .array && array_info.elem_type != it.right_type { @@ -2592,20 +2688,17 @@ fn (mut g JsGen) gen_string_inter_literal(it ast.StringInterLiteral) { continue } expr := it.exprs[i] - fmt := it.fmts[i] - fwidth := it.fwidths[i] - precision := it.precisions[i] + // fmt := it.fmts[i] + // fwidth := it.fwidths[i] + // precision := it.precisions[i] g.write('\${') - if fmt != `_` || fwidth != 0 || precision != 987698 { - // TODO: Handle formatting - g.expr(expr) - } else { - sym := g.table.get_type_symbol(it.expr_types[i]) - g.expr(expr) + typ := g.unwrap_generic(it.expr_types[i]) + /* + g.expr(expr) if sym.kind == .struct_ && sym.has_method('str') { g.write('.str()') - } - } + }*/ + g.gen_expr_to_string(expr, typ) g.write('}') } g.write('`') diff --git a/vlib/v/gen/js/tests/testdata/array.out b/vlib/v/gen/js/tests/testdata/array.out index d6f79a755a..0359ecd32d 100644 --- a/vlib/v/gen/js/tests/testdata/array.out +++ b/vlib/v/gen/js/tests/testdata/array.out @@ -29,7 +29,7 @@ true true true -true +false true true true @@ -109,14 +109,7 @@ abc 1 4 6 -[4, 3, 2, 1] -true -true -true -true -true -true -true +[] 0 [0, 0, 0, 0] [0, 7, 0, 0] @@ -146,10 +139,10 @@ true true true true -true -true -true -true +false +false +false +false true true 15 @@ -231,7 +224,7 @@ false 79 [0, 1, 15, 27, 38, 50, 79] [0, 1, 15, 27, 38, 50, 79] -3 +2 [14, 2, 3] test b [true, false, true] @@ -243,6 +236,7 @@ test b 2,2 3,3 4,4 +{ val: 0 } 6 [2, 0, 2, 2] [[1, 0, 0], [0, 0, 0]] @@ -285,11 +279,11 @@ true true true true +false +false true true -true -true -true +false true 0 `exists`: true and `not exists`: false @@ -305,4 +299,4 @@ Numbers{ odds: [3, 5, 7] evens: [2, 6, 10] } -[[10, 10, 10], [10, 10, 10], [10, 10, 10]] \ No newline at end of file +[[10, 10, 10], [10, 10, 10], [10, 10, 10]]