diff --git a/vlib/builtin/js/array.js.v b/vlib/builtin/js/array.js.v
index f9124bdaf0..834ae617d3 100644
--- a/vlib/builtin/js/array.js.v
+++ b/vlib/builtin/js/array.js.v
@@ -194,13 +194,6 @@ pub fn (mut a array) insert_many(i int, val voidptr, size int) {
#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.arr.join(separator +''));
-
- return res
-}
-
fn (mut a array) push(val voidptr) {
#a.val.arr.make_copy()
#if (arguments[2] && arguments[2].valueOf()) {a.val.arr.arr.push(...val)} else {
@@ -246,7 +239,7 @@ struct array_iterator {
#}
#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[Symbol.iterator] = function () { 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); }
diff --git a/vlib/builtin/js/byte.js.v b/vlib/builtin/js/byte.js.v
index 4a46e7852f..78b3f6fb2e 100644
--- a/vlib/builtin/js/byte.js.v
+++ b/vlib/builtin/js/byte.js.v
@@ -21,3 +21,19 @@ pub fn (c byte) str() string {
return res
}
+
+pub fn (c byte) ascii_str() string {
+ res := ''
+ #res.str = String.fromCharCode(c.val)
+
+ return res
+}
+
+pub fn (c byte) repeat(count int) string {
+ mut res := ''
+ for _ in 0 .. count {
+ res += c.ascii_str()
+ }
+
+ return res
+}
diff --git a/vlib/builtin/js/int.js.v b/vlib/builtin/js/int.js.v
index 1220fb5033..dd67017078 100644
--- a/vlib/builtin/js/int.js.v
+++ b/vlib/builtin/js/int.js.v
@@ -1,5 +1,26 @@
module builtin
+pub fn (i i8) str() string {
+ mut res := ''
+ #res.str = i.val.toString()
+
+ return res
+}
+
+pub fn (i i16) str() string {
+ mut res := ''
+ #res.str = i.val.toString()
+
+ return res
+}
+
+pub fn (i u16) str() string {
+ mut res := ''
+ #res.str = i.val.toString()
+
+ return res
+}
+
pub fn (i int) str() string {
mut res := ''
#res = new string( i )
@@ -48,3 +69,82 @@ pub fn (i int_literal) str() string {
return res
}
+
+pub fn (x u64) hex() string {
+ res := ''
+ #res.str = x.val.toString(16)
+
+ return res
+}
+
+pub fn (x i64) hex() string {
+ res := ''
+ #res.str = x.val.toString(16)
+
+ return res
+}
+
+pub fn (x u32) hex() string {
+ res := ''
+ #res.str = x.val.toString(16)
+
+ return res
+}
+
+pub fn (x u16) hex() string {
+ res := ''
+ #res.str = x.val.toString(16)
+
+ return res
+}
+
+pub fn (x i8) hex() string {
+ res := ''
+ #res.str = x.val.toString(16)
+
+ return res
+}
+
+pub fn (x i16) hex() string {
+ res := ''
+ #res.str = x.val.toString(16)
+
+ return res
+}
+
+pub fn (x int) hex() string {
+ res := ''
+ #res.str = x.val.toString(16)
+
+ return res
+}
+
+pub fn (x int_literal) hex() string {
+ res := ''
+ #res.str = x.val.toString(16)
+
+ return res
+}
+
+pub fn (x byte) hex() string {
+ res := ''
+ #res.str = x.val.toString(16)
+
+ return res
+}
+
+// hex returns a string with the hexadecimal representation
+// of the byte elements of the array.
+pub fn (b []byte) hex() string {
+ mut hex := ''
+ for i in b {
+ mut z := i
+ z = z
+ #let n0 = i.val >> 4
+ #hex.str += n0 < 10 ? String.fromCharCode(n0) : String.fromCharCode(n0 + 87)
+
+ #let n1 = i.val & 0xF
+ #hex.str += n1 < 10 ? String.fromCharCode(n1) : String.fromCharCode(n1 + 87)
+ }
+ return hex
+}
diff --git a/vlib/builtin/js/int_test.js.v b/vlib/builtin/js/int_test.js.v
new file mode 100644
index 0000000000..f760d463f6
--- /dev/null
+++ b/vlib/builtin/js/int_test.js.v
@@ -0,0 +1,244 @@
+const (
+ a = 3
+ u = u64(1)
+)
+
+fn test_const() {
+ b := (true && true) || false
+ assert b == true
+ assert a == 3
+ assert u == u64(1)
+ assert u == 1 // make sure this works without the cast
+}
+
+fn test_str_methods() {
+ assert i8(1).str() == '1'
+ assert i8(-1).str() == '-1'
+ assert i16(1).str() == '1'
+ assert i16(-1).str() == '-1'
+ assert int(1).str() == '1'
+ assert int(-1).str() == '-1'
+ assert int(2147483647).str() == '2147483647'
+ // todo: overflow check for integers
+ // assert int(2147483648).str() == '-2147483648'
+ // assert int(-2147483648).str() == '-2147483648'
+ assert i64(1).str() == '1'
+ assert i64(-1).str() == '-1'
+ assert u16(1).str() == '1'
+ // assert u16(-1).str() == '65535'
+ assert u32(1).str() == '1'
+ // assert u32(-1).str() == '4294967295'
+ assert u64(1).str() == '1'
+ // assert u64(-1).str() == '18446744073709551615'
+}
+
+fn test_and_precendence() {
+ assert (2 & 0 == 0) == ((2 & 0) == 0)
+ assert (2 & 0 != 0) == ((2 & 0) != 0)
+ assert (0 & 0 >= 0) == ((0 & 0) >= 0)
+ assert (0 & 0 <= 0) == ((0 & 0) <= 0)
+ assert (0 & 0 < 1) == ((0 & 0) < 1)
+ assert (1 & 2 > 0) == ((1 & 2) > 0)
+}
+
+fn test_or_precendence() {
+ assert (1 | 0 == 0) == ((1 | 0) == 0)
+ assert (1 | 0 != 1) == ((1 | 0) != 1)
+ assert (1 | 0 >= 2) == ((1 | 0) >= 2)
+ assert (1 | 0 <= 0) == ((1 | 0) <= 0)
+ assert (1 | 0 < 0) == ((1 | 0) < 0)
+ assert (1 | 0 > 1) == ((1 | 0) > 1)
+}
+
+fn test_xor_precendence() {
+ assert (1 ^ 0 == 2) == ((1 ^ 0) == 2)
+ assert (1 ^ 0 != 2) == ((1 ^ 0) != 2)
+ assert (1 ^ 0 >= 0) == ((1 ^ 0) >= 0)
+ assert (1 ^ 0 <= 1) == ((1 ^ 0) <= 1)
+ assert (1 ^ 0 < 0) == ((1 ^ 0) < 0)
+ assert (1 ^ 0 > 1) == ((1 ^ 0) > 1)
+}
+
+fn test_left_shift_precendence() {
+ assert (2 << 4 | 3) == ((2 << 4) | 3)
+ assert (2 << 4 | 3) != (2 << (4 | 3))
+}
+
+fn test_right_shift_precendence() {
+ assert (256 >> 4 | 3) == ((256 >> 4) | 3)
+ assert (256 >> 4 | 3) != (256 >> (4 | 3))
+}
+
+fn test_i8_print() {
+ b := i8(0)
+ println(b)
+ c := i16(7)
+ println(c)
+ d := u16(6)
+ println(d)
+ assert true
+}
+
+/*
+fn test_cmp() {
+ assert 1 ≠ 2
+ assert 1 ⩽ 2
+ assert 1 ⩾ 0
+}
+*/
+type MyInt = int
+
+fn test_int_alias() {
+ i := MyInt(2)
+ assert i + 10 == 12
+}
+
+fn test_hex() {
+ x := u64(10)
+ assert x.hex() == 'a'
+ b := 1234
+ assert b.hex() == '4d2'
+ b1 := -1
+ // assert b1.hex() == 'ffffffff'
+ // unsigned tests
+ // assert u8(12).hex() == '0c'
+ // assert u8(255).hex() == 'ff'
+ assert u16(65535).hex() == 'ffff'
+ // assert u32(-1).hex() == 'ffffffff'
+ // assert u64(-1).hex() == 'ffffffffffffffff'
+ // signed tests
+ // assert i8(-1).hex() == 'ff'
+ assert i8(12).hex() == 'c'
+ assert i16(32767).hex() == '7fff'
+ assert int(2147483647).hex() == '7fffffff'
+ assert i64(9223372036854775807).hex() == '7fffffffffffffff'
+}
+
+fn test_bin() {
+ x1 := 0b10
+ assert x1 == 2
+ x2 := 0b10101010
+ assert x2 == 0xAA
+ x3 := -0b0000001
+ assert x3 == -1
+ x4 := 0b11111111
+ assert x4 == 255
+ x5 := byte(0b11111111)
+ assert x5 == 255
+ x6 := char(0b11111111)
+ assert int(x6) == -1
+ x7 := 0b0
+ assert x7 == 0
+ x8 := -0b0
+ assert x8 == 0
+}
+
+fn test_oct() {
+ x1 := 0o12
+ assert x1 == 10
+ x2 := 0o350
+ assert x2 == 232
+ x3 := 0o00073
+ assert x3 == 59
+ x4 := 0
+ assert x4 == 0
+ x5 := 195
+ assert x5 == 195
+ x6 := -0o744
+ assert x6 == -484
+ x7 := -0o000042
+ assert x7 == -34
+ x8 := -112
+ assert x8 == -112
+ x9 := -0
+ assert x9 == 0
+}
+
+fn test_num_separator() {
+ // int
+ assert 100_000_0 == 1000000
+ assert -2_23_4_6 == -22346
+
+ // bin
+ assert 0b0_11 == 3
+ assert -0b0_100 == -4
+
+ // oct
+ assert 0o1_73 == 123
+ assert -0o17_5 == -125
+ assert -0o175 == -125
+
+ // hex
+ assert 0xFF == 255
+ assert 0xF_F == 255
+
+ // f32 or f64
+ assert 312_2.55 == 3122.55
+ assert 312_2.55 == 3122.55
+}
+
+fn test_int_decl() {
+ x1 := 0
+ x2 := 1333
+ x3 := -88955
+ x4 := 2000000000
+ x5 := -1999999999
+ assert typeof(x1).name == 'int'
+ assert typeof(x2).name == 'int'
+ assert typeof(x3).name == 'int'
+ assert typeof(x4).name == 'int'
+ assert typeof(x5).name == 'int'
+ x7 := u64(-321314588900011)
+ assert typeof(x7).name == 'u64'
+}
+
+fn test_int_to_hex() {
+ // array hex
+ /*
+ st := [byte(`V`), `L`, `A`, `N`, `G`]
+ assert st.hex() == '564c414e47'
+ assert st.hex().len == 10
+ st1 := [byte(0x41)].repeat(100)
+ assert st1.hex() == '41'.repeat(100)*/
+ // --- int to hex tests
+ c0 := 12
+ // 8Bit
+ assert byte(0).hex() == '0'
+ assert byte(c0).hex() == 'c'
+ assert i8(c0).hex() == 'c'
+ assert byte(127).hex() == '7f'
+ assert i8(127).hex() == '7f'
+ assert byte(255).hex() == 'ff'
+ // assert byte(-1).hex() == 'ff'
+ // 16bit
+ assert u16(0).hex() == '0'
+ assert i16(c0).hex() == 'c'
+ assert u16(c0).hex() == 'c'
+ assert i16(32767).hex() == '7fff'
+ assert u16(32767).hex() == '7fff'
+ // assert i16(-1).hex() == 'ffff'
+ assert u16(65535).hex() == 'ffff'
+ // 32bit
+ assert u32(0).hex() == '0'
+ assert c0.hex() == 'c'
+ assert u32(c0).hex() == 'c'
+ assert 2147483647.hex() == '7fffffff'
+ assert u32(2147483647).hex() == '7fffffff'
+ // assert (-1).hex() == 'ffffffffffffffff'
+ assert u32(4294967295).hex() == 'ffffffff'
+ // 64 bit
+ assert u64(0).hex() == '0'
+ assert i64(c0).hex() == 'c'
+ assert u64(c0).hex() == 'c'
+ assert i64(9223372036854775807).hex() == '7fffffffffffffff'
+ assert u64(9223372036854775807).hex() == '7fffffffffffffff'
+ // assert i64(-1).hex() == 'ffffffffffffffff'
+ assert u64(18446744073709551615).hex() == 'ffffffffffffffff'
+}
+
+fn test_repeat() {
+ b := byte(`V`)
+ assert b.repeat(5) == 'VVVVV'
+ assert b.repeat(1) == b.ascii_str()
+ assert b.repeat(0) == ''
+}
diff --git a/vlib/builtin/js/map.js.v b/vlib/builtin/js/map.js.v
index 1b913674b6..5bcd65fb55 100644
--- a/vlib/builtin/js/map.js.v
+++ b/vlib/builtin/js/map.js.v
@@ -25,3 +25,5 @@ pub fn (m &map) free() {}
#res += '}'
#return res;
#}
+
+#map.prototype.getOrSet = function (key, init) { if (this.map.has(key)) { return this.map.get(key); } else { this.map.set(key,init); return init; } }
diff --git a/vlib/builtin/js/map_test.js.v b/vlib/builtin/js/map_test.js.v
new file mode 100644
index 0000000000..76b79ed47b
--- /dev/null
+++ b/vlib/builtin/js/map_test.js.v
@@ -0,0 +1,949 @@
+import rand
+
+const (
+ strings = unique_strings(200, 10)
+)
+
+fn unique_strings(arr_len int, str_len int) []string {
+ mut arr := []string{cap: arr_len}
+ for arr.len < arr_len {
+ str := rand.string(str_len)
+ if str !in arr {
+ arr << str
+ }
+ }
+ return arr
+}
+
+fn test_get_and_set_many() {
+ mut m := map[string]int{}
+ for i, s in strings {
+ m[s] = i
+ assert m[s] == i
+ assert m.len == i + 1
+ }
+ for i, s in strings {
+ assert m[s] == i
+ }
+ assert m.len == strings.len
+}
+
+fn test_for_in_many() {
+ mut m := map[string]int{}
+ for i, s in strings {
+ m[s] = i
+ }
+ for k, v in m {
+ assert m[k] == v
+ }
+}
+
+fn test_keys_many() {
+ mut m := map[string]int{}
+ for i, s in strings {
+ m[s] = i
+ }
+ keys := m.keys()
+ assert keys.len == strings.len
+ assert keys.len == m.len
+ assert keys == strings
+}
+
+fn test_deletes_many() {
+ mut m := map[string]int{}
+ for i, s in strings {
+ m[s] = i
+ }
+ for i, s in strings {
+ m.delete(s)
+ assert m[s] == 0
+ assert m.len == strings.len - (i + 1)
+ }
+ assert m.len == 0
+ assert m.keys().len == 0
+}
+
+struct User {
+mut:
+ name string
+}
+
+struct Aaa {
+mut:
+ m map[string]int
+ users map[string]User
+}
+
+fn (mut a Aaa) set(key string, val int) {
+ a.m[key] = val
+}
+
+fn test_map() {
+ mut m := map[string]int{}
+ assert m.len == 0
+ m['hi'] = 80
+ m['hello'] = 101
+ assert m['hi'] == 80
+ assert m['hello'] == 101
+ assert m.len == 2
+ assert 'hi' in m
+ mut sum := 0
+ // Test `for in`
+ for _, val in m {
+ sum += val
+ }
+ assert sum == 80 + 101
+ // Test `.keys()`
+ keys := m.keys()
+ assert keys.len == 2
+ assert 'hi' in keys
+ assert 'hello' in keys
+ m.delete('hi')
+ assert m.len == 1
+ m.delete('aloha')
+ assert m.len == 1
+ assert m['hi'] == 0
+ assert m.keys().len == 1
+ assert m.keys()[0] == 'hello'
+ // //
+ mut users := map[string]User{}
+ users['1'] = User{'Peter'}
+ peter := users['1']
+ assert peter.name == 'Peter'
+ mut a := Aaa{
+ m: map[string]int{}
+ users: map[string]User{}
+ }
+ a.users['Bob'] = User{'Bob'}
+ q := a.users['Bob']
+ assert q.name == 'Bob'
+ // test struct field change
+ a.users['Bob'].name = 'bob'
+ q2 := a.users['Bob']
+ assert q2.name == 'bob'
+ a.m['one'] = 1
+ a.set('two', 2)
+ assert a.m['one'] == 1
+ assert a.m['two'] == 2
+}
+
+fn test_map_init() {
+ one := 'one'
+ three := 'three'
+ m := {
+ one: 1
+ 'two': 2
+ three: 1 + 2
+ }
+ assert m['one'] == 1
+ assert m['two'] == 2
+ assert m['three'] == 3
+ assert m['unknown'] == 0
+}
+
+fn test_string_map() {
+ // m := map[string]Fn
+}
+
+fn test_large_map() {
+ // ticks := time.ticks()
+ mut nums := map[string]int{}
+ n := 30 * 1000
+ for i in 0 .. n {
+ key := i.str()
+ nums[key] = i
+ }
+ assert nums['1'] == 1
+ assert nums['999'] == 999
+ assert nums['1000000'] == 0
+ // println(time.ticks() - ticks)
+}
+
+fn test_various_map_value() {
+ mut m1 := map[string]int{}
+ m1['test'] = 1
+ assert m1['test'] == 1
+ mut m2 := map[string]string{}
+ m2['test'] = 'test'
+ assert m2['test'] == 'test'
+ mut m3 := map[string]i8{}
+ m3['test'] = i8(0)
+ assert m3['test'] == i8(0)
+ mut m4 := map[string]i16{}
+ m4['test'] = i16(0)
+ assert m4['test'] == i16(0)
+ mut m7 := map[string]u16{}
+ m7['test'] = u16(0)
+ assert m7['test'] == u16(0)
+ mut m8 := map[string]u32{}
+ m8['test'] = u32(0)
+ assert m8['test'] == u32(0)
+ mut m9 := map[string]bool{}
+ m9['test'] = true
+ assert m9['test'] == true
+ mut m10 := map[string]byte{}
+ m10['test'] = byte(0)
+ assert m10['test'] == byte(0)
+ mut m11 := map[string]f32{}
+ m11['test'] = f32(0.0)
+ assert m11['test'] == f32(0.0)
+ mut m12 := map[string]f64{}
+ m12['test'] = f64(0.0)
+ assert m12['test'] == f64(0.0)
+ // mut m13 := map[string]rune
+ // m13['test'] = rune(0)
+ // assert m13['test'] == rune(0)
+ // todo(playX): pointer equality does not work yet
+ /*
+ mut m14 := map[string]voidptr{}
+ m14['test'] = voidptr(0)
+ assert m14['test'] == voidptr(0)
+ mut m15 := map[string]&byte{}
+ m15['test'] = &byte(0)
+ assert m15['test'] == &byte(0)
+ mut m16 := map[string]i64{}
+ m16['test'] = i64(0)
+ assert m16['test'] == i64(0)
+ mut m17 := map[string]u64{}
+ m17['test'] = u64(0)
+ assert m17['test'] == u64(0)
+ mut m18 := map[string]&int{}
+ m18['test'] = &int(0)
+ assert m18['test'] == &int(0)*/
+}
+
+fn test_string_arr() {
+ mut m := map[string][]string{}
+ m['a'] = ['one', 'two']
+ assert m['a'].len == 2
+ assert m['a'][0] == 'one'
+ assert m['a'][1] == 'two'
+}
+
+fn mut_map(mut m map[string]int) {
+ m['a'] = 10
+}
+
+fn test_mut_arg() {
+ mut m := map[string]int{}
+ mut_map(mut m)
+ a := m['a']
+ assert a == 10
+}
+
+fn test_delete() {
+ mut m := map[string]int{}
+ m['one'] = 1
+ m['two'] = 2
+ println(m['two']) // => "2"
+ m.delete('two')
+ println(m['two'].str()) // => 0
+ assert ('two' in m) == false
+ println('two' in m) // => true, on Linux and Windows <-- wrong !
+}
+
+fn test_delete_size() {
+ arr := ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
+ mut m := map[string]int{}
+ for _ in 0 .. 10 {
+ for i in 0 .. 10 {
+ m[arr[i]] = i
+ }
+ assert m.len == 10
+ println(m.len)
+ for i in 0 .. 10 {
+ m.delete(arr[i])
+ }
+ }
+}
+
+fn test_nested_for_in() {
+ mut m := map[string]int{}
+ for i in 0 .. 1000 {
+ m[i.str()] = i
+ }
+ mut i := 0
+ for key1, _ in m {
+ assert key1 == i.str()
+ i++
+ mut j := 0
+ for key2, _ in m {
+ assert key2 == j.str()
+ j++
+ }
+ }
+}
+
+fn test_delete_in_for_in() {
+ mut m := map[string]string{}
+ for i in 0 .. 1000 {
+ m[i.str()] = i.str()
+ }
+ mut i := 0
+ for key, _ in m {
+ assert key == i.str()
+ m.delete(key)
+ i++
+ }
+ assert m.str() == '{}'
+ assert m.len == 0
+}
+
+fn test_set_in_for_in() {
+ mut m := map[string]string{}
+ for i in 0 .. 10 {
+ m[i.str()] = i.str()
+ }
+ mut last_key := ''
+ mut i := 0
+ for key, _ in m {
+ m['10'] = '10'
+ assert key == i.str()
+ last_key = key
+ i++
+ }
+ assert last_key == '10'
+}
+
+fn test_delete_and_set_in_for_in() {
+ mut m := map[string]string{}
+ for i in 0 .. 1000 {
+ m[i.str()] = i.str()
+ }
+ mut i := 0
+ for key, _ in m {
+ assert key == i.str()
+ m.delete(key)
+ m[key] = i.str()
+ if i == 999 {
+ break
+ }
+ i++
+ }
+ assert m.len == 1000
+ i = 0
+ for key, _ in m {
+ assert m[key] == i.str()
+ i++
+ }
+ assert i == 1000
+}
+
+struct Mstruct1 {
+pub mut:
+ mymap map[string]int
+}
+
+struct Mstruct2 {
+pub mut:
+ mymap map[string]f64
+}
+
+struct Mstruct3 {
+pub mut:
+ mymap map[string]u16
+}
+
+fn test_map_assign() {
+ mut a := map[string]f64{}
+ mut b := map[string]int{}
+ mut c := map[string]u16{}
+ a = {
+ 'x': 12.4
+ 'y': 3
+ }
+ b = {
+ 'u': -13
+ 'v': 12
+ }
+ c = {
+ 's': u16(5)
+ 't': 3
+ }
+ _ := Mstruct1{{
+ 'p': 12
+ }}
+ _ := Mstruct2{{
+ 'q': 1.7
+ }}
+ _ := Mstruct3{{
+ 'r': u16(6)
+ 's': 5
+ }}
+}
+
+fn test_postfix_op_directly() {
+ mut a := map[string]int{}
+ a['aaa']++
+ assert a['aaa'] == 1
+ a['aaa']++
+ assert a['aaa'] == 2
+ a['bbb']--
+ assert a['bbb'] == -1
+ a['bbb']--
+ assert a['bbb'] == -2
+}
+
+fn test_map_push_directly() {
+ mut a := map[string][]string{}
+ a['aaa'] << ['a', 'b', 'c']
+ assert a['aaa'].len == 3
+ assert a['aaa'] == ['a', 'b', 'c']
+}
+
+fn test_assign_directly() {
+ mut a := map[string]int{}
+ a['aaa'] += 4
+ assert a['aaa'] == 4
+ a['aaa'] -= 2
+ assert a['aaa'] == 2
+}
+
+fn test_map_in_directly() {
+ for k, v in {
+ 'aa': 1
+ } {
+ assert k == 'aa'
+ assert v == 1
+ }
+}
+
+fn test_plus_assign_string() {
+ mut m := {
+ 'one': ''
+ }
+ m['one'] += '1'
+ assert m.len == 1
+ assert m['one'] == '1'
+}
+
+fn test_map_keys_to_array() {
+ m := {
+ 'a': 'b'
+ 'c': 'd'
+ }
+ mut arr := []string{}
+ for k, _ in m {
+ arr << k
+ }
+ sarr := arr.str()
+ println(sarr)
+ assert sarr == "['a', 'c']"
+}
+
+fn map_in_mut(mut m map[string]int) {
+ if 'one' in m {
+ m['one'] = 2
+ }
+}
+
+fn test_map_in_mut() {
+ mut m := {
+ 'one': 1
+ }
+ map_in_mut(mut m)
+ assert m['one'] == 2
+}
+
+fn test_map_in() {
+ m := {
+ 'Foo': 'bar'
+ }
+ if 'foo'.capitalize() in m {
+ println('ok')
+ } else {
+ assert false
+ }
+}
+
+fn mut_map_with_relation_op_in_fn(mut m map[string]int) {
+ if m['one'] == 1 {
+ m['three'] = 3
+ }
+ if m['two'] != 1 {
+ m['four'] = 4
+ }
+ if m['one'] > 0 {
+ m['five'] = 5
+ }
+ if m['one'] < 2 {
+ m['six'] = 6
+ }
+ if m['two'] >= 2 {
+ m['seven'] = 7
+ }
+ if m['two'] <= 2 {
+ m['eight'] = 8
+ }
+}
+
+fn test_mut_map_with_relation_op_in_fn() {
+ mut m := {
+ 'one': 1
+ 'two': 2
+ }
+ mut_map_with_relation_op_in_fn(mut m)
+ assert 'three' in m
+ assert 'four' in m
+ assert 'five' in m
+ assert 'six' in m
+ assert 'seven' in m
+ assert 'eight' in m
+}
+
+fn test_map_str_after_delete() {
+ mut m := {
+ 'first': 1
+ 'second': 2
+ 'third': 3
+ }
+ osm := '$m'
+ m.delete('second')
+ nsm := '$m'
+ println('m: $m')
+ assert osm == "{'first': 1, 'second': 2, 'third': 3}"
+ assert nsm == "{'first': 1, 'third': 3}"
+}
+
+fn test_modify_map_value() {
+ mut m1 := {
+ 'foo': 3
+ 'bar': -7
+ }
+ m1['foo'] += 5
+ m1['bar'] *= -2
+ assert m1['foo'] == 8
+ assert m1['bar'] == 14
+}
+
+fn test_map_clone() {
+ mut nums := {
+ 'foo': 1
+ 'bar': 2
+ }
+ mut nums2 := nums.clone()
+ nums2['foo']++
+ nums2['bar'] *= 4
+ assert nums['foo'] == 1
+ assert nums['bar'] == 2
+ assert nums2['foo'] == 2
+ assert nums2['bar'] == 8
+}
+
+struct MValue {
+ name string
+ misc map[string]string
+}
+
+fn test_map_default_zero() {
+ m := map[string]MValue{}
+ v := m['unknown']
+ x := v.misc['x']
+ println(x)
+ assert x == ''
+}
+
+fn test_map_or() {
+ m := {
+ 'first': 1
+ 'second': 2
+ 'third': 3
+ }
+ _ = m
+ // num := m['first'] or { return }
+}
+
+fn test_int_keys() {
+ mut m := map[int]int{}
+ m[3] = 9
+ m[4] = 16
+ assert m.len == 2
+ assert m[3] == 9
+ assert m[4] == 16
+ m[5] += 24
+ m[5]++
+ assert m[5] == 25
+ mut m2 := {
+ 3: 9
+ 4: 16
+ 5: 25
+ }
+
+ four := 4
+ m2.delete(3)
+ m2.delete(four)
+ m2.delete(5)
+ assert m2.len == 0
+ assert m2[3] == 0
+ assert m2[4] == 0
+ assert m2[5] == 0
+ assert m2.keys() == []
+
+ m2 = {
+ 3: 9
+ 4: 16
+ 5: 25
+ }
+
+ assert m2.len == 3
+ // clone
+ mc := m.clone()
+ same := mc == m
+ assert same
+ assert mc.len == 3
+ assert mc.keys() == [3, 4, 5]
+ mut all := []int{}
+ for k, v in mc {
+ assert m[k] == v
+ all << k
+ all << v
+ }
+ assert all == [3, 9, 4, 16, 5, 25]
+
+ mut m3 := {
+ 1: 'one'
+ 2: 'two'
+ }
+ assert m3[1] == 'one'
+ m3.delete(1)
+}
+
+enum Color {
+ red
+ green
+ blue
+}
+
+type ColorAlias = Color
+
+fn test_alias_enum() {
+ mut m := map[ColorAlias]string{}
+ m[Color.red] = 'hi'
+ assert m[Color.red] == 'hi'
+}
+
+fn test_enum_in_map() {
+ mut m := map[Color]string{}
+ m[Color.red] = 'hi'
+ assert Color.red in m
+ assert Color.green !in m
+ assert Color.blue !in m
+}
+
+fn test_voidptr_keys() {
+ mut m := map[voidptr]string{}
+ v := 5
+ m[&v] = 'var'
+ m[&m] = 'map'
+ assert m[&v] == 'var'
+ assert m[&m] == 'map'
+ assert m.len == 2
+}
+
+fn test_rune_keys() {
+ mut m := {
+ `!`: 2
+ `%`: 3
+ }
+ assert typeof(m).name == 'map[rune]int'
+ assert m[`!`] == 2
+ m[`@`] = 7
+ assert m.len == 3
+ println(m)
+ assert '$m' == '{`!`: 2, `%`: 3, `@`: 7}'
+
+ mut a := []rune{}
+ for k, v in m {
+ a << k
+ a << rune(v) + `0`
+ }
+ assert a == [`!`, `2`, `%`, `3`, `@`, `7`]
+}
+
+fn test_eq() {
+ a := {
+ 'a': 1
+ 'b': 2
+ }
+ assert a == {
+ 'a': 1
+ 'b': 2
+ }
+ b := {
+ 'a': [[1]]
+ 'b': [[2]]
+ }
+ assert b == {
+ 'a': [[1]]
+ 'b': [[2]]
+ }
+ c := {
+ 'a': {
+ '11': 1
+ }
+ 'b': {
+ '22': 2
+ }
+ }
+ assert c == {
+ 'a': {
+ '11': 1
+ }
+ 'b': {
+ '22': 2
+ }
+ }
+ d := {
+ 'a': MValue{
+ name: 'aa'
+ misc: {
+ '11': '1'
+ }
+ }
+ 'b': MValue{
+ name: 'bb'
+ misc: {
+ '22': '2'
+ }
+ }
+ }
+ assert d == {
+ 'a': MValue{
+ name: 'aa'
+ misc: {
+ '11': '1'
+ }
+ }
+ 'b': MValue{
+ name: 'bb'
+ misc: {
+ '22': '2'
+ }
+ }
+ }
+}
+
+fn test_non_string_key_map_str() {
+ assert {
+ 23: 4
+ }.str() == '{23: 4}'
+ assert {
+ `a`: 12
+ `b`: 13
+ }.str() == '{`a`: 12, `b`: 13}'
+ assert {
+ 23: 'foo'
+ 25: 'bar'
+ }.str() == "{23: 'foo', 25: 'bar'}"
+}
+
+fn test_map_assign_empty_map_init() {
+ mut a := {
+ 'one': 1
+ }
+ a = {}
+ println(a)
+ assert a == map[string]int{}
+ assert '$a' == '{}'
+}
+
+fn test_in_map_literal() {
+ assert 1 in {
+ 1: 'one'
+ }
+}
+
+fn test_byte_keys() {
+ mut m := map[byte]byte{}
+ byte_max := byte(255)
+ for i in byte(0) .. byte_max {
+ m[i] = i
+ assert m[i] == i
+ }
+ for k, v in m {
+ assert k == v
+ }
+ for i in byte(0) .. 100 {
+ m[i]++
+ assert m[i] == i + 1
+ }
+ assert m.len == byte_max
+ keys := m.keys()
+ for i in byte(0) .. byte_max {
+ assert keys[i] == i
+ }
+ for i in byte(0) .. byte_max {
+ m.delete(i)
+ assert m[i] == 0
+ }
+ assert m.len == 0
+}
+
+fn test_i16_keys() {
+ mut m := map[i16]i16{}
+ end := i16(1000)
+ for i in i16(0) .. end {
+ m[i] = i
+ assert m[i] == i
+ }
+ for k, v in m {
+ assert k == v
+ }
+ for i in i16(0) .. 500 {
+ m[i]++
+ assert m[i] == i + 1
+ }
+ assert m.len == end
+ keys := m.keys()
+ for i in i16(0) .. end {
+ assert keys[i] == i
+ }
+ for i in i16(0) .. end {
+ m.delete(i)
+ assert m[i] == 0
+ }
+ assert m.len == 0
+}
+
+fn test_u16_keys() {
+ mut m := map[u16]u16{}
+ end := u16(1000)
+ for i in u16(0) .. end {
+ m[i] = i
+ assert m[i] == i
+ }
+ for k, v in m {
+ assert k == v
+ }
+ for i in u16(0) .. 500 {
+ m[i]++
+ assert m[i] == i + 1
+ }
+ assert m.len == end
+ keys := m.keys()
+ for i in u16(0) .. end {
+ assert keys[i] == i
+ }
+ for i in u16(0) .. end {
+ m.delete(i)
+ assert m[i] == 0
+ }
+ assert m.len == 0
+}
+
+fn test_u32_keys() {
+ mut m := map[u32]u32{}
+ end := u32(1000)
+ for i in u32(0) .. end {
+ m[i] = i
+ assert m[i] == i
+ }
+ for k, v in m {
+ assert k == v
+ }
+ for i in u32(0) .. 500 {
+ m[i]++
+ assert m[i] == i + 1
+ }
+ assert m.len == end
+ keys := m.keys()
+ for i in u32(0) .. end {
+ assert keys[i] == i
+ }
+ for i in u32(0) .. end {
+ m.delete(i)
+ assert m[i] == 0
+ }
+ assert m.len == 0
+}
+
+fn test_int_keys2() {
+ mut m := map[int]int{}
+ end := 1000
+ for i in int(0) .. end {
+ m[i] = i
+ assert m[i] == i
+ }
+ for k, v in m {
+ assert k == v
+ }
+ for i in int(0) .. 500 {
+ m[i]++
+ assert m[i] == i + 1
+ }
+ assert m.len == end
+ keys := m.keys()
+ for i in int(0) .. end {
+ assert keys[i] == i
+ }
+ for i in int(0) .. end {
+ m.delete(i)
+ assert m[i] == 0
+ }
+ assert m.len == 0
+}
+
+fn test_i64_keys() {
+ mut m := map[i64]i64{}
+ end := i64(1000)
+ for i in i64(0) .. end {
+ m[i] = i
+ assert m[i] == i
+ }
+ for k, v in m {
+ assert k == v
+ }
+ for i in i64(0) .. 500 {
+ m[i]++
+ assert m[i] == i + 1
+ }
+ assert m.len == end
+ keys := m.keys()
+ for i in i64(0) .. end {
+ assert keys[i] == i
+ }
+ for i in i64(0) .. end {
+ m.delete(i)
+ assert m[i] == 0
+ }
+ assert m.len == 0
+}
+
+fn test_u64_keys() {
+ mut m := map[u64]u64{}
+ end := u64(1000)
+ for i in u64(0) .. end {
+ m[i] = i
+ assert m[i] == i
+ }
+ for k, v in m {
+ assert k == v
+ }
+ for i in u64(0) .. 500 {
+ m[i]++
+ assert m[i] == i + 1
+ }
+ assert m.len == end
+ keys := m.keys()
+ for i in u64(0) .. end {
+ assert keys[i] == i
+ }
+ for i in u64(0) .. end {
+ m.delete(i)
+ assert m[i] == 0
+ }
+ assert m.len == 0
+}
+
+fn test_map_set_fixed_array_variable() {
+ mut m := map[string][2]f64{}
+ m['A'] = [1.1, 2.2]!
+ println(m)
+ assert '$m' == "{'A': [1.1, 2.2]}"
+
+ mut m2 := map[string][2]f64{}
+ arr := [1.1, 2.2]!
+ m2['A'] = arr
+ println(m2)
+ assert '$m2' == "{'A': [1.1, 2.2]}"
+}
diff --git a/vlib/builtin/js/string.js.v b/vlib/builtin/js/string.js.v
index d276435d61..1420f5cfee 100644
--- a/vlib/builtin/js/string.js.v
+++ b/vlib/builtin/js/string.js.v
@@ -10,6 +10,10 @@ pub fn (s string) slice(a int, b int) string {
return string(s.str.slice(a, b))
}
+pub fn (s string) substr(start int, end int) string {
+ return s.slice(start, end)
+}
+
pub fn (s string) after(dot string) string {
return string(s.str.slice(s.str.lastIndexOf(dot.str) + 1, int(s.str.length)))
}
@@ -20,20 +24,37 @@ pub fn (s string) after_char(dot byte) string {
}
pub fn (s string) all_after(dot string) string {
- return string(s.str.slice(s.str.indexOf(dot.str) + 1, int(s.str.length)))
+ pos := if dot.len == 0 { -1 } else { s.str.indexOf(dot.str) }
+ if pos == -1 {
+ return s.clone()
+ }
+ return s[pos + dot.len..]
}
// why does this exist?
pub fn (s string) all_after_last(dot string) string {
- return s.after(dot)
+ pos := if dot.len == 0 { -1 } else { s.str.lastIndexOf(dot.str) }
+ if pos == -1 {
+ return s.clone()
+ }
+ return s[pos + dot.len..]
}
pub fn (s string) all_before(dot string) string {
- return string(s.str.slice(0, s.str.indexOf(dot.str)))
+ pos := if dot.len == 0 { -1 } else { s.str.indexOf(dot.str) }
+ if pos == -1 {
+ return s.clone()
+ }
+ return s[..pos]
+ // return string(s.str.slice(0, s.str.indexOf(dot.str)))
}
pub fn (s string) all_before_last(dot string) string {
- return string(s.str.slice(0, s.str.lastIndexOf(dot.str)))
+ pos := if dot.len == 0 { -1 } else { s.str.lastIndexOf(dot.str) }
+ if pos == -1 {
+ return s.clone()
+ }
+ return s[..pos]
}
pub fn (s string) bool() bool {
@@ -79,6 +100,9 @@ pub fn (s string) contains_any(chars string) bool {
}
pub fn (s string) contains_any_substr(chars []string) bool {
+ if chars.len == 0 {
+ return true
+ }
for x in chars {
if s.str.includes(x.str) {
return true
@@ -726,3 +750,131 @@ pub fn (s string) replace_once(rep string, with_ string) string {
return s2
}
+
+pub fn (s string) title() string {
+ words := s.split(' ')
+ mut tit := []string{}
+ for word in words {
+ tit << word.capitalize()
+ }
+
+ title := tit.join(' ')
+ return title
+}
+
+// index_any returns the position of any of the characters in the input string - if found.
+pub fn (s string) index_any(chars string) int {
+ for i, ss in s {
+ for c in chars {
+ if c == ss {
+ return i
+ }
+ }
+ }
+ return -1
+}
+
+/*
+// limit returns a portion of the string, starting at `0` and extending for a given number of characters afterward.
+// 'hello'.limit(2) => 'he'
+// 'hi'.limit(10) => 'hi'
+pub fn (s string) limit(max int) string {
+ u := s.runes()
+ if u.len <= max {
+ return s.clone()
+ }
+ return u[0..max].string()
+}
+*/
+// is_title returns true if all words of the string is capitalized.
+// Example: assert 'Hello V Developer'.is_title() == true
+pub fn (s string) is_title() bool {
+ words := s.split(' ')
+ for word in words {
+ if !word.is_capital() {
+ return false
+ }
+ }
+ return true
+}
+
+// is_capital returns `true` if the first character in the string is a capital letter.
+// Example: assert 'Hello'.is_capital() == true
+[direct_array_access]
+pub fn (s string) is_capital() bool {
+ if s.len == 0 || !(s[0] >= `A` && s[0] <= `Z`) {
+ return false
+ }
+ for i in 1 .. s.len {
+ if s[i] >= `A` && s[i] <= `Z` {
+ return false
+ }
+ }
+ return true
+}
+
+// is_upper returns `true` if all characters in the string is uppercase.
+// Example: assert 'HELLO V'.is_upper() == true
+pub fn (s string) is_upper() bool {
+ res := false
+ #res.val = s.str == s.str.toUpperCase() && s.str != s.str.toLowerCase()
+
+ return res
+}
+
+// is_upper returns `true` if all characters in the string is uppercase.
+// Example: assert 'HELLO V'.is_upper() == true
+pub fn (s string) is_lower() bool {
+ res := false
+ #res.val = s.str == s.str.toLowerCase() && s.str != s.str.toUpperCase()
+
+ return res
+}
+
+pub fn (s string) reverse() string {
+ res := ''
+ #res.str = [...s.str].reverse().join('')
+
+ return res
+}
+
+pub fn (s string) trim(cutset string) string {
+ if s.len < 1 || cutset.len < 1 {
+ return s.clone()
+ }
+ mut pos_left := 0
+ mut pos_right := s.len - 1
+ mut cs_match := true
+ for pos_left <= s.len && pos_right >= -1 && cs_match {
+ cs_match = false
+ for cs in cutset {
+ if s[pos_left] == cs {
+ pos_left++
+ cs_match = true
+ break
+ }
+ }
+ for cs in cutset {
+ if s[pos_right] == cs {
+ pos_right--
+ cs_match = true
+ break
+ }
+ }
+ if pos_left > pos_right {
+ return ''
+ }
+ }
+ return s.substr(pos_left, pos_right + 1)
+}
+
+pub fn (s []string) join(sep string) string {
+ mut res := ''
+ for i, str in s {
+ res += str
+ if i != s.len - 1 {
+ res += sep
+ }
+ }
+ return res
+}
diff --git a/vlib/builtin/js/string_test.js.v b/vlib/builtin/js/string_test.js.v
new file mode 100644
index 0000000000..21387f29d0
--- /dev/null
+++ b/vlib/builtin/js/string_test.js.v
@@ -0,0 +1,897 @@
+import strings
+
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license
+// that can be found in the LICENSE file.
+
+struct Foo {
+ bar int
+mut:
+ str string
+}
+
+fn test_add() {
+ mut a := 'a'
+ a += 'b'
+ assert a == ('ab')
+ a = 'a'
+ for i := 1; i < 1000; i++ {
+ a += 'b'
+ }
+ assert a.len == 1000
+ assert a.ends_with('bbbbb')
+ a += '123'
+ assert a.ends_with('3')
+}
+
+fn test_ends_with() {
+ a := 'browser.v'
+ assert a.ends_with('.v')
+
+ s := 'V Programming Language'
+ assert s.ends_with('guage') == true
+ assert s.ends_with('Language') == true
+ assert s.ends_with('Programming Language') == true
+ assert s.ends_with('V') == false
+}
+
+fn test_between() {
+ s := 'hello [man] how you doing'
+ assert s.find_between('[', ']') == 'man'
+}
+
+fn test_compare() {
+ a := 'Music'
+ b := 'src'
+ assert b >= a
+}
+
+fn test_lt() {
+ a := ''
+ b := 'a'
+ c := 'a'
+ d := 'b'
+ e := 'aa'
+ f := 'ab'
+ assert a < b
+ assert !(b < c)
+ assert c < d
+ assert !(d < e)
+ assert c < e
+ assert e < f
+}
+
+fn test_ge() {
+ a := 'aa'
+ b := 'aa'
+ c := 'ab'
+ d := 'abc'
+ e := 'aaa'
+ assert b >= a
+ assert c >= b
+ assert d >= c
+ assert !(c >= d)
+ assert e >= a
+}
+
+fn test_compare_strings() {
+ a := 'aa'
+ b := 'aa'
+ c := 'ab'
+ d := 'abc'
+ e := 'aaa'
+ assert compare_strings(a, b) == 0
+ assert compare_strings(b, c) == -1
+ assert compare_strings(c, d) == -1
+ assert compare_strings(d, e) == 1
+ assert compare_strings(a, e) == -1
+ assert compare_strings(e, a) == 1
+}
+
+fn test_sort() {
+ mut vals := [
+ 'arr',
+ 'an',
+ 'a',
+ 'any',
+ ]
+ len := vals.len
+ vals.sort()
+ assert len == vals.len
+ assert vals[0] == 'a'
+ assert vals[1] == 'an'
+ assert vals[2] == 'any'
+ assert vals[3] == 'arr'
+}
+
+fn test_sort_reverse() {
+ mut vals := [
+ 'arr',
+ 'an',
+ 'a',
+ 'any',
+ ]
+ len := vals.len
+ vals.sort(b > a)
+ assert len == vals.len
+ assert vals[0] == 'a'
+ assert vals[1] == 'an'
+ assert vals[2] == 'any'
+ assert vals[3] == 'arr'
+}
+
+fn test_split_nth() {
+ a := '1,2,3'
+ assert a.split(',').len == 3
+ assert a.split_nth(',', -1).len == 3
+ assert a.split_nth(',', 0).len == 3
+ assert a.split_nth(',', 1).len == 1
+ assert a.split_nth(',', 2).len == 2
+ assert a.split_nth(',', 10).len == 3
+ b := '1::2::3'
+ assert b.split('::').len == 3
+ assert b.split_nth('::', -1).len == 3
+ assert b.split_nth('::', 0).len == 3
+ assert b.split_nth('::', 1).len == 1
+ assert b.split_nth('::', 2).len == 2
+ assert b.split_nth('::', 10).len == 3
+ c := 'ABCDEF'
+ println(c.split('').len)
+ assert c.split('').len == 6
+ assert c.split_nth('', 3).len == 3
+ assert c.split_nth('BC', -1).len == 2
+ d := ','
+ assert d.split(',').len == 2
+ assert d.split_nth('', 3).len == 1
+ assert d.split_nth(',', -1).len == 2
+ assert d.split_nth(',', 3).len == 2
+ e := ',,,0,,,,,a,,b,'
+ assert e.split(',,').len == 5
+ assert e.split_nth(',,', 3).len == 3
+ assert e.split_nth(',', -1).len == 12
+ assert e.split_nth(',', 3).len == 3
+}
+
+fn test_split_nth_values() {
+ line := 'CMD=eprintln(phase=1)'
+
+ a0 := line.split_nth('=', 0)
+ assert a0.len == 3
+ assert a0[0] == 'CMD'
+ assert a0[1] == 'eprintln(phase'
+ assert a0[2] == '1)'
+
+ a1 := line.split_nth('=', 1)
+ assert a1.len == 1
+ assert a1[0] == 'CMD=eprintln(phase=1)'
+
+ a2 := line.split_nth('=', 2)
+ assert a2.len == 2
+ assert a2[0] == 'CMD'
+ assert a2[1] == 'eprintln(phase=1)'
+
+ a3 := line.split_nth('=', 3)
+ assert a3.len == 3
+ assert a3[0] == 'CMD'
+ assert a3[1] == 'eprintln(phase'
+ assert a3[2] == '1)'
+
+ a4 := line.split_nth('=', 4)
+ assert a4.len == 3
+ assert a4[0] == 'CMD'
+ assert a4[1] == 'eprintln(phase'
+ assert a4[2] == '1)'
+}
+
+fn test_split() {
+ mut s := 'volt/twitch.v:34'
+ mut vals := s.split(':')
+ assert vals.len == 2
+ assert vals[0] == 'volt/twitch.v'
+ assert vals[1] == '34'
+ // /////////
+ s = '2018-01-01z13:01:02'
+ vals = s.split('z')
+ assert vals.len == 2
+ assert vals[0] == '2018-01-01'
+ assert vals[1] == '13:01:02'
+ // //////////
+ s = '4627a862c3dec29fb3182a06b8965e0025759e18___1530207969___blue'
+ vals = s.split('___')
+ assert vals.len == 3
+ assert vals[0] == '4627a862c3dec29fb3182a06b8965e0025759e18'
+ assert vals[1] == '1530207969'
+ assert vals[2] == 'blue'
+ // /////////
+ s = 'lalala'
+ vals = s.split('a')
+ assert vals.len == 4
+ assert vals[0] == 'l'
+ assert vals[1] == 'l'
+ assert vals[2] == 'l'
+ assert vals[3] == ''
+ // /////////
+ s = 'awesome'
+ a := s.split('')
+ assert a.len == 7
+ assert a[0] == 'a'
+ assert a[1] == 'w'
+ assert a[2] == 'e'
+ assert a[3] == 's'
+ assert a[4] == 'o'
+ assert a[5] == 'm'
+ assert a[6] == 'e'
+ // /////////
+ s = 'wavy turquoise bags'
+ vals = s.split(' bags')
+ assert vals.len == 2
+ assert vals[0] == 'wavy turquoise'
+ assert vals[1] == ''
+}
+
+/*
+fn test_trim_space() {
+ a := ' a '
+ assert a.trim_space() == 'a'
+ code := '
+
+fn main() {
+ println(2)
+}
+
+'
+ code_clean := 'fn main() {
+ println(2)
+}'
+ assert code.trim_space() == code_clean
+}*/
+
+/*
+fn test_join() {
+ mut strings := ['a', 'b', 'c']
+ mut s := strings.join(' ')
+ assert s == 'a b c'
+ strings = [
+ 'one
+two ',
+ 'three!
+four!',
+ ]
+ s = strings.join(' ')
+ assert s.contains('one') && s.contains('two ') && s.contains('four')
+ empty := []string{len: 0}
+ assert empty.join('A') == ''
+}*/
+
+fn test_clone() {
+ mut a := 'a'
+ a += 'a'
+ a += 'a'
+ b := a
+ c := a.clone()
+ assert c == a
+ assert c == 'aaa'
+ assert b == 'aaa'
+}
+
+fn test_replace() {
+ a := 'hello man!'
+ mut b := a.replace('man', 'world')
+ assert b == ('hello world!')
+ b = b.replace('!', '')
+ assert b == ('hello world')
+ b = b.replace('h', 'H')
+ assert b == ('Hello world')
+ b = b.replace('foo', 'bar')
+ assert b == ('Hello world')
+ s := 'hey man how are you'
+ assert s.replace('man ', '') == 'hey how are you'
+ lol := 'lol lol lol'
+ assert lol.replace('lol', 'LOL') == 'LOL LOL LOL'
+ b = 'oneBtwoBBthree'
+ assert b.replace('B', '') == 'onetwothree'
+ b = '*charptr'
+ assert b.replace('charptr', 'byteptr') == '*byteptr'
+ c := 'abc'
+ println(c.replace('', '-'))
+ // assert c.replace('', '-') == c
+ v := 'a b c d'
+ assert v.replace(' ', ' ') == 'a b c d'
+}
+
+fn test_replace_each() {
+ s := 'hello man man :)'
+ q := s.replace_each([
+ 'man',
+ 'dude',
+ 'hello',
+ 'hey',
+ ])
+ assert q == 'hey dude dude :)'
+ bb := '[b]bold[/b] [code]code[/code]'
+ assert bb.replace_each([
+ '[b]',
+ '',
+ '[/b]',
+ '',
+ '[code]',
+ '',
+ '[/code]',
+ '
',
+ ]) == 'bold code
'
+ bb2 := '[b]cool[/b]'
+ assert bb2.replace_each([
+ '[b]',
+ '',
+ '[/b]',
+ '',
+ ]) == 'cool'
+ t := 'aaaaaaaa'
+ y := t.replace_each([
+ 'aa',
+ 'b',
+ ])
+ assert y == 'bbbb'
+ s2 := 'hello_world hello'
+ assert s2.replace_each(['hello_world', 'aaa', 'hello', 'bbb']) == 'aaa bbb'
+}
+
+fn test_itoa() {
+ num := 777
+ assert num.str() == '777'
+ big := 7779998
+ assert big.str() == '7779998'
+ a := 3
+ assert a.str() == '3'
+ b := 5555
+ assert b.str() == '5555'
+ zero := 0
+ assert zero.str() == '0'
+ neg := -7
+ assert neg.str() == '-7'
+}
+
+fn test_reassign() {
+ a := 'hi'
+ mut b := a
+ b += '!'
+ assert a == 'hi'
+ assert b == 'hi!'
+}
+
+/*
+fn test_runes() {
+ s := 'привет'
+ assert s.len == 12
+ s2 := 'privet'
+ assert s2.len == 6
+ u := s.runes()
+ assert u.len == 6
+ assert s2.substr(1, 4).len == 3
+ assert s2.substr(1, 4) == 'riv'
+ assert s2[1..4].len == 3
+ assert s2[1..4] == 'riv'
+ assert s2[..4].len == 4
+ assert s2[..4] == 'priv'
+ assert s2[2..].len == 4
+ assert s2[2..] == 'ivet'
+ assert u[1..4].string().len == 6
+ assert u[1..4].string() == 'рив'
+ assert s2.substr(1, 2) == 'r'
+ assert u[1..2].string() == 'р'
+ assert s2.runes()[1] == `r`
+ assert u[1] == `р`
+ first := u[0]
+ last := u[u.len - 1]
+ assert first.str().len == 2
+ assert last.str().len == 2
+}*/
+
+fn test_contains() {
+ s := 'view.v'
+ assert s.contains('vi')
+ assert !s.contains('random')
+ assert ''.contains('')
+ assert 'abc'.contains('')
+}
+
+fn test_contains_any() {
+ assert !'team'.contains_any('i')
+ assert 'fail'.contains_any('ui')
+ assert 'ure'.contains_any('ui')
+ assert 'failure'.contains_any('ui')
+ assert !'foo'.contains_any('')
+ assert !''.contains_any('')
+}
+
+fn test_contains_any_substr() {
+ s := 'Some random text'
+ assert s.contains_any_substr(['false', 'not', 'rand'])
+ assert !s.contains_any_substr(['ABC', 'invalid'])
+ assert ''.contains_any_substr([])
+ assert 'abc'.contains_any_substr([''])
+}
+
+fn test_arr_contains() {
+ a := ['a', 'b', 'c']
+ assert a.contains('b')
+ ints := [1, 2, 3]
+ assert ints.contains(2)
+}
+
+/*
+fn test_to_num() {
+ s := '7'
+ assert s.int() == 7
+ assert s.byte() == 7
+ assert s.u64() == 7
+ f := '71.5 hasdf'
+ // QTODO
+ assert f.f32() == 71.5
+ vals := ['9']
+ assert vals[0].int() == 9
+ big := '93993993939322'
+ assert big.u64() == 93993993939322
+ assert big.i64() == 93993993939322
+}*/
+
+/*
+fn test_inter_format_string() {
+ float_num := 1.52345
+ float_num_string := '-${float_num:.3f}-'
+ //assert float_num_string == '-1.523-'
+ int_num := 7
+ int_num_string := '-${int_num:03d}-'
+ //assert int_num_string == '-007-'
+ ch := `a`
+ ch_string := '-${ch:c}-'
+ //assert ch_string == '-a-'
+ hex_n := 192
+ hex_n_string := '-${hex_n:x}-'
+ assert hex_n_string == '-c0-'
+ oct_n := 192
+ oct_n_string := '-${oct_n:o}-'
+ assert oct_n_string == '-300-'
+ str := 'abc'
+ str_string := '-${str:s}-'
+ assert str_string == '-abc-'
+}*/
+
+/*
+fn test_hash() {
+ s := '10000'
+ assert s.hash() == 46730161
+ s2 := '24640'
+ assert s2.hash() == 47778736
+ s3 := 'Content-Type'
+ assert s3.hash() == 949037134
+ s4 := 'bad_key'
+ assert s4.hash() == -346636507
+ s5 := '24640'
+ // From a map collision test
+ assert s5.hash() % ((1 << 20) - 1) == s.hash() % ((1 << 20) - 1)
+ assert s5.hash() % ((1 << 20) - 1) == 592861
+}*/
+
+fn test_trim() {
+ assert 'banana'.trim('bna') == ''
+ assert 'abc'.trim('ac') == 'b'
+ assert 'aaabccc'.trim('ac') == 'b'
+}
+
+fn test_trim_left() {
+ mut s := 'module main'
+ assert s.trim_left(' ') == 'module main'
+ s = ' module main'
+ assert s.trim_left(' ') == 'module main'
+ // test cutset
+ s = 'banana'
+ assert s.trim_left('ba') == 'nana'
+ assert s.trim_left('ban') == ''
+}
+
+fn test_trim_right() {
+ mut s := 'module main'
+ assert s.trim_right(' ') == 'module main'
+ s = 'module main '
+ assert s.trim_right(' ') == 'module main'
+ // test cutset
+ s = 'banana'
+ assert s.trim_right('na') == 'b'
+ assert s.trim_right('ban') == ''
+}
+
+fn test_all_before() {
+ s := 'fn hello fn'
+ assert s.all_before(' ') == 'fn'
+ assert s.all_before('2') == s
+ assert s.all_before('') == s
+}
+
+fn test_all_before_last() {
+ s := 'fn hello fn'
+ assert s.all_before_last(' ') == 'fn hello'
+ assert s.all_before_last('2') == s
+ assert s.all_before_last('') == s
+}
+
+fn test_all_after() {
+ s := 'fn hello'
+ assert s.all_after('fn ') == 'hello'
+ assert s.all_after('test') == s
+ assert s.all_after('') == s
+ assert s.after('e') == 'llo'
+ x := s.after('e')
+ assert x == 'llo'
+}
+
+fn test_reverse() {
+ println('hello'.reverse())
+ assert 'hello'.reverse() == 'olleh'
+ assert ''.reverse() == ''
+ assert 'a'.reverse() == 'a'
+}
+
+fn test_count() {
+ assert ''.count('') == 0
+ assert ''.count('a') == 0
+ assert 'a'.count('') == 0
+ assert 'aa'.count('a') == 2
+ assert 'aa'.count('aa') == 1
+ assert 'aabbaa'.count('aa') == 2
+ assert 'bbaabb'.count('aa') == 1
+}
+
+fn test_lower() {
+ mut s := 'A'
+ assert !s.is_lower()
+ assert s.to_lower() == 'a'
+ assert s.to_lower().len == 1
+ s = 'HELLO'
+ assert !s.is_lower()
+ assert s.to_lower() == 'hello'
+ assert s.to_lower().len == 5
+ s = 'Aloha'
+ assert !s.is_lower()
+ assert s.to_lower() == 'aloha'
+ s = 'Have A nice Day!'
+ assert !s.is_lower()
+ assert s.to_lower() == 'have a nice day!'
+ s = 'hi'
+ assert s.is_lower()
+ assert s.to_lower() == 'hi'
+ assert 'aloha!'[0] == `a`
+ assert 'aloha!'[5] == `!`
+}
+
+fn test_upper() {
+ mut s := 'a'
+ assert !s.is_upper()
+ assert s.to_upper() == 'A'
+ assert s.to_upper().len == 1
+ s = 'hello'
+ assert !s.is_upper()
+ assert s.to_upper() == 'HELLO'
+ assert s.to_upper().len == 5
+ s = 'Aloha'
+ assert !s.is_upper()
+ assert s.to_upper() == 'ALOHA'
+ s = 'have a nice day!'
+ assert !s.is_upper()
+ assert s.to_upper() == 'HAVE A NICE DAY!'
+ s = 'HI'
+ assert s.is_upper()
+ assert s.to_upper() == 'HI'
+}
+
+fn test_capitalize() {
+ mut s := 'hello'
+ assert !s.is_capital()
+ assert s.capitalize() == 'Hello'
+ s = 'test'
+ assert !s.is_capital()
+ assert s.capitalize() == 'Test'
+ s = 'i am ray'
+ assert !s.is_capital()
+ assert s.capitalize() == 'I am ray'
+ s = ''
+ assert !s.is_capital()
+ assert s.capitalize() == ''
+ s = 'TEST IT'
+ assert !s.is_capital()
+ assert s.capitalize() == 'TEST IT'
+ s = 'Test it'
+ assert s.is_capital()
+ assert s.capitalize() == 'Test it'
+ assert 'GameMission_t'.capitalize() == 'GameMission_t'
+}
+
+fn test_title() {
+ mut s := 'hello world'
+ assert !s.is_title()
+ assert s.title() == 'Hello World'
+ s = 'HELLO WORLD'
+ assert !s.is_title()
+ assert s.title() == 'HELLO WORLD'
+ s = 'Hello World'
+ assert s.is_title()
+ assert s.title() == 'Hello World'
+}
+
+fn test_for_loop() {
+ mut i := 0
+ s := 'abcd'
+
+ for c in s {
+ assert c == s[i]
+ i++
+ }
+}
+
+fn test_for_loop_two() {
+ s := 'abcd'
+
+ for i, c in s {
+ assert c == s[i]
+ }
+}
+
+fn test_quote() {
+ a := `'`
+ println('testing double quotes')
+ b := 'hi'
+ assert b == 'hi'
+ // assert a.str() == "'"
+}
+
+/*
+fn test_limit() {
+ s := 'hello'
+ assert s.limit(2) == 'he'
+ assert s.limit(9) == s
+ assert s.limit(0) == ''
+ // assert s.limit(-1) == ''
+}*/
+
+fn test_repeat() {
+ s1 := 'V! '
+ assert s1.repeat(5) == 'V! V! V! V! V! '
+ assert s1.repeat(1) == s1
+ assert s1.repeat(0) == ''
+ s2 := ''
+ assert s2.repeat(5) == s2
+ assert s2.repeat(1) == s2
+ assert s2.repeat(0) == s2
+ // TODO Add test for negative values
+}
+
+fn test_starts_with() {
+ s := 'V Programming Language'
+ assert s.starts_with('V') == true
+ assert s.starts_with('V Programming') == true
+ assert s.starts_with('Language') == false
+}
+
+fn test_trim_prefix() {
+ s := 'V Programming Language'
+ assert s.trim_prefix('V ') == 'Programming Language'
+ assert s.trim_prefix('V Programming ') == 'Language'
+ assert s.trim_prefix('Language') == s
+
+ s2 := 'TestTestTest'
+ assert s2.trim_prefix('Test') == 'TestTest'
+ assert s2.trim_prefix('TestTest') == 'Test'
+
+ s3 := '123Test123Test'
+ assert s3.trim_prefix('123') == 'Test123Test'
+ assert s3.trim_prefix('123Test') == '123Test'
+}
+
+fn test_trim_suffix() {
+ s := 'V Programming Language'
+ assert s.trim_suffix(' Language') == 'V Programming'
+ assert s.trim_suffix(' Programming Language') == 'V'
+ assert s.trim_suffix('V') == s
+
+ s2 := 'TestTestTest'
+ assert s2.trim_suffix('Test') == 'TestTest'
+ assert s2.trim_suffix('TestTest') == 'Test'
+
+ s3 := '123Test123Test'
+ assert s3.trim_suffix('123') == s3
+ assert s3.trim_suffix('123Test') == '123Test'
+}
+
+fn test_raw() {
+ raw := r'raw\nstring'
+ lines := raw.split('\n')
+ println(lines)
+ assert lines.len == 1
+ println('raw string: "$raw"')
+
+ raw2 := r'Hello V\0'
+ assert raw2[7] == `\\`
+ assert raw2[8] == `0`
+
+ raw3 := r'Hello V\x00'
+ assert raw3[7] == `\\`
+ assert raw3[8] == `x`
+ assert raw3[9] == `0`
+ assert raw3[10] == `0`
+}
+
+fn test_raw_with_quotes() {
+ raw := r"some'" + r'"thing' // " should be escaped in the generated C code
+ // assert raw[0] == `s`
+ // assert raw[5] == `"`
+ // assert raw[6] == `t`
+}
+
+fn test_escape() {
+ a := 10
+ println("\"$a")
+ // assert "\"$a" == '"10'
+}
+
+fn test_atoi() {
+ assert '234232'.int() == 234232
+ assert '-9009'.int() == -9009
+ assert '0'.int() == 0
+ for n in -10000 .. 100000 {
+ s := n.str()
+ assert s.int() == n
+ }
+}
+
+fn test_raw_inter() {
+ world := 'world'
+ println(world)
+ s := r'hello\n$world'
+ assert s == r'hello\n$world'
+ assert s.contains('$')
+}
+
+fn test_c_r() {
+ // This used to break because of r'' and c''
+ c := 42
+ println('$c')
+ r := 50
+ println('$r')
+}
+
+fn test_inter_before_comp_if() {
+ s := '123'
+ // This used to break ('123 $....')
+ $if linux {
+ println(s)
+ }
+ assert s == '123'
+}
+
+fn test_double_quote_inter() {
+ a := 1
+ b := 2
+ println('$a $b')
+ assert '$a $b' == '1 2'
+ assert '$a $b' == '1 2'
+}
+
+fn foo(b byte) byte {
+ return b - 10
+}
+
+fn filter(b byte) bool {
+ return b != `a`
+}
+
+/*
+fn test_split_into_lines() {
+ line_content := 'Line'
+ text_crlf := '$line_content\r\n$line_content\r\n$line_content'
+ lines_crlf := text_crlf.split_into_lines()
+
+ assert lines_crlf.len == 3
+ for line in lines_crlf {
+ assert line == line_content
+ }
+
+ text_lf := '$line_content\n$line_content\n$line_content'
+ lines_lf := text_lf.split_into_lines()
+
+ assert lines_lf.len == 3
+ for line in lines_lf {
+ assert line == line_content
+ }
+}
+*/
+fn test_string_literal_with_backslash() {
+ a := 'HelloWorld'
+ assert a == 'HelloWorld'
+
+ b := 'OneTwoThree'
+ assert b == 'OneTwoThree'
+}
+
+/*
+type MyString = string
+
+fn test_string_alias() {
+ s := MyString('hi')
+ ss := s + '!'
+}
+*/
+
+// sort an array of structs, by their string field values
+
+struct Ka {
+ s string
+ i int
+}
+
+fn test_sorter() {
+ mut arr := [
+ Ka{
+ s: 'bbb'
+ i: 100
+ },
+ Ka{
+ s: 'aaa'
+ i: 101
+ },
+ Ka{
+ s: 'ccc'
+ i: 102
+ },
+ ]
+ cmp := fn (a &Ka, b &Ka) int {
+ return compare_strings(a.s, b.s)
+ }
+ arr.sort_with_compare(cmp)
+ assert arr[0].s == 'aaa'
+ assert arr[0].i == 101
+ assert arr[1].s == 'bbb'
+ assert arr[1].i == 100
+ assert arr[2].s == 'ccc'
+ assert arr[2].i == 102
+}
+
+fn test_fields() {
+ assert 'a bcde'.fields() == ['a', 'bcde']
+ assert ' sss \t ssss '.fields() == ['sss', 'ssss']
+ assert '\n xyz \t abc def'.fields() == ['xyz', 'abc', 'def']
+ assert 'hello'.fields() == ['hello']
+ assert ''.fields() == []
+}
+
+/*
+fn test_interpolation_after_quoted_variable_still_works() {
+ rr := 'abc'
+ tt := 'xyz'
+
+ // Basic interpolation, no internal quotes
+ yy := 'Replacing $rr with $tt'
+ assert yy == 'Replacing abc with xyz'
+
+ // Interpolation after quoted variable ending with 'r'quote
+ // that may be mistaken with the start of a raw string,
+ // ensure that it is not.
+ ss := 'Replacing "$rr" with "$tt"'
+ assert ss == 'Replacing "abc" with "xyz"'
+ zz := "Replacing '$rr' with '$tt'"
+ assert zz == "Replacing 'abc' with 'xyz'"
+
+ // Interpolation after quoted variable ending with 'c'quote
+ // may be mistaken with the start of a c string, so
+ // check it is not.
+ cc := 'abc'
+ ccc := "Replacing '$cc' with '$tt'"
+ assert ccc == "Replacing 'abc' with 'xyz'"
+ cccq := 'Replacing "$cc" with "$tt"'
+ assert cccq == 'Replacing "abc" with "xyz"'
+}
+*/
+fn test_index_any() {
+ x := 'abcdefghij'
+ assert x.index_any('ef') == 4
+ assert x.index_any('fe') == 4
+}
diff --git a/vlib/v/gen/js/infix.v b/vlib/v/gen/js/infix.v
index 767ce56a72..e6d55fc2f2 100644
--- a/vlib/v/gen/js/infix.v
+++ b/vlib/v/gen/js/infix.v
@@ -287,7 +287,11 @@ fn (mut g JsGen) infix_in_not_in_op(node ast.InfixExpr) {
g.gen_deref_ptr(node.right_type)
g.write(',')
g.expr(node.left)
- g.write('))')
+ g.write(')')
+ if node.op == .not_in {
+ g.write('.valueOf()')
+ }
+ g.write(')')
return
} else if r_sym.unaliased_sym.kind == .map {
g.expr(node.right)
diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v
index be0f8cf132..c87eb5e232 100644
--- a/vlib/v/gen/js/js.v
+++ b/vlib/v/gen/js/js.v
@@ -145,7 +145,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
g.gen_builtin_type_defs()
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(map.prototype,"len", { get: function() {return new int(this.map.size);}, set: function(l) { this.map.size = 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
}
@@ -1182,7 +1182,7 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt, semicolon bool) {
g.write('.val')
}
- if g.inside_map_set && op == .assign {
+ if false && g.inside_map_set && op == .assign {
g.inside_map_set = false
g.write(', ')
g.expr(val)
@@ -2338,15 +2338,24 @@ fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) {
if expr.is_setter {
g.inside_map_set = true
- g.write('.map.set(')
+ g.write('.getOrSet(')
} else {
g.write('.map.get(')
}
g.expr(expr.index)
g.write('.\$toJS()')
- if !expr.is_setter {
- g.write(')')
+ if expr.is_setter {
+ // g.write(', ${g.to_js_typ_val(left_typ.)')
+ match left_typ.info {
+ ast.Map {
+ g.write(', ${g.to_js_typ_val(left_typ.info.value_type)}')
+ }
+ else {
+ verror('unreachable')
+ }
+ }
}
+ g.write(')')
} else if left_typ.kind == .string {
if expr.is_setter {
// TODO: What's the best way to do this?