js: array improvements (#11952)

pull/11981/head
playX 2021-09-26 07:33:53 +03:00 committed by GitHub
parent f3757a7cd1
commit 863dd0b23e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 469 additions and 193 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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)')
}

View File

@ -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) {')

View File

@ -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}) {')

View File

@ -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))'

View File

@ -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)')

View File

@ -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) {

View File

@ -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('`')

View File

@ -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]]
[[10, 10, 10], [10, 10, 10], [10, 10, 10]]