diff --git a/vlib/builtin/js/map.js.v b/vlib/builtin/js/map.js.v index 894d614cf0..033a5fde9e 100644 --- a/vlib/builtin/js/map.js.v +++ b/vlib/builtin/js/map.js.v @@ -14,3 +14,13 @@ pub fn (mut m map) delete(key voidptr) { pub fn (m &map) free() {} #map.prototype[Symbol.iterator] = function () { return this.map[Symbol.iterator](); } + +#map.prototype.toString = function () { +#function fmtKey(key) { return typeof key == 'string' ? '\'' + key + '\'' : key} +#let res = '{' +#for (const entry of this) { +#res += fmtKey(entry[0]) + ': ' + entry[0]; +#} +#res += '}' +#return res; +#} diff --git a/vlib/builtin/js/string.js.v b/vlib/builtin/js/string.js.v index e7d270800d..f209fb8d6d 100644 --- a/vlib/builtin/js/string.js.v +++ b/vlib/builtin/js/string.js.v @@ -94,7 +94,10 @@ pub fn (s string) count(substr string) int { } pub fn (s string) ends_with(p string) bool { - return s.str.endsWith(p.str) + mut res := false + #res.val = s.str.endsWith(p.str) + + return res } pub fn (s string) starts_with(p string) bool { @@ -133,7 +136,7 @@ pub fn (s string) fields() []string { } pub fn (s string) find_between(start string, end string) string { - return string(s.str.slice(s.str.indexOf(start.str), s.str.indexOf(end.str) + 1)) + return string(s.str.slice(s.str.indexOf(start.str) + 1, s.str.indexOf(end.str))) } // unnecessary in the JS backend, implemented for api parity. @@ -464,3 +467,77 @@ pub fn (s string) strip_margin_custom(del byte) string { return result } + +// split_nth splits the string based on the passed `delim` substring. +// It returns the first Nth parts. When N=0, return all the splits. +// The last returned element has the remainder of the string, even if +// the remainder contains more `delim` substrings. +[direct_array_access] +pub fn (s string) split_nth(delim string, nth int) []string { + mut res := []string{} + mut i := 0 + + match delim.len { + 0 { + i = 1 + for ch in s { + if nth > 0 && i >= nth { + res << s[i..] + break + } + res << ch.str() + i++ + } + return res + } + 1 { + mut start := 0 + delim_byte := delim[0] + + for i < s.len { + if s[i] == delim_byte { + was_last := nth > 0 && res.len == nth - 1 + if was_last { + break + } + val := s[start..i] //.substr(start, i) + res << val + start = i + delim.len + i = start + } else { + i++ + } + } + + // Then the remaining right part of the string + if nth < 1 || res.len < nth { + res << s[start..] + } + return res + } + else { + mut start := 0 + // Take the left part for each delimiter occurence + for i <= s.len { + is_delim := i + delim.len <= s.len && s[i..i + delim.len] == delim + if is_delim { + was_last := nth > 0 && res.len == nth - 1 + if was_last { + break + } + val := s[start..i] // .substr(start, i) + res << val + start = i + delim.len + i = start + } else { + i++ + } + } + // Then the remaining right part of the string + if nth < 1 || res.len < nth { + res << s[start..] + } + return res + } + } +} diff --git a/vlib/v/gen/js/builtin_types.v b/vlib/v/gen/js/builtin_types.v index 0b16fc006c..69158dd568 100644 --- a/vlib/v/gen/js/builtin_types.v +++ b/vlib/v/gen/js/builtin_types.v @@ -30,7 +30,7 @@ fn (mut g JsGen) to_js_typ_val(t ast.Type) string { styp = '$prefix${g.sym_to_js_typ(sym)}("")' } .map { - styp = 'new Map()' + styp = 'new map(new Map())' } .array { styp = '$prefix${g.sym_to_js_typ(sym)}()' @@ -335,6 +335,7 @@ fn (mut g JsGen) gen_builtin_type_defs() { typ_name: typ_name default_value: 'new Boolean(false)' to_jsval: '+this != 0' + eq: 'this.val === other.valueOf()' ) } 'string' { @@ -354,7 +355,7 @@ fn (mut g JsGen) gen_builtin_type_defs() { g.gen_builtin_prototype( typ_name: typ_name val_name: 'map' - default_value: 'new Map()' + default_value: 'new map(new Map())' constructor: 'this.map = map' value_of: 'this' to_string: 'this.map.toString()' diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index aabca09738..9778b44944 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -1831,17 +1831,16 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) { } else { is_arithmetic := it.op in [token.Kind.plus, .minus, .mul, .div, .mod] - mut needs_cast := is_arithmetic && it.left_type != it.right_type mut greater_typ := 0 // todo(playX): looks like this cast is always required to perform .eq operation on types. - if true || needs_cast { + if is_arithmetic { greater_typ = g.greater_typ(it.left_type, it.right_type) if g.cast_stack.len > 0 { - needs_cast = g.cast_stack.last() != greater_typ + // needs_cast = g.cast_stack.last() != greater_typ } } - if true || needs_cast { + if is_arithmetic { if g.ns.name == 'builtin' { g.write('new ') } @@ -1855,7 +1854,7 @@ fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) { g.expr(it.right) - if true || needs_cast { + if is_arithmetic { g.cast_stack.delete_last() g.write(')') } diff --git a/vlib/v/gen/js/tests/testdata/array.out b/vlib/v/gen/js/tests/testdata/array.out index 3ab514deaf..c152476d81 100644 --- a/vlib/v/gen/js/tests/testdata/array.out +++ b/vlib/v/gen/js/tests/testdata/array.out @@ -195,6 +195,7 @@ true [2, 3, 4] [1, 2, 3] [1, 2, 3] +[[1, 2, 3], [4, 5, 6]] true true true @@ -292,5 +293,11 @@ true true true 0 +`exists`: true and `not exists`: false [[], [], [], []] [[], [], [123], []] +[{}, {}, {}, {}] +[{}, {}, {'123': 123}, {}] +Numbers { odds: [1, 3, 5] , evens: [2, 4] } +Numbers { odds: [3, 5, 7] , evens: [2, 6, 10] } +[[10, 10, 10], [10, 10, 10], [10, 10, 10]] diff --git a/vlib/v/gen/js/tests/testdata/array.v b/vlib/v/gen/js/tests/testdata/array.v index 2465669f26..de669b3374 100644 --- a/vlib/v/gen/js/tests/testdata/array.v +++ b/vlib/v/gen/js/tests/testdata/array.v @@ -16,6 +16,11 @@ struct Coord { z int } +struct Numbers { + odds []int + evens []int +} + struct Person { name string nums []int @@ -672,15 +677,13 @@ fn main() { } { // test array str - // todo(playX): JS array formatting should match what default builtin impl has. - /* + numbers := [1, 2, 3] assert numbers == [1, 2, 3] numbers2 := [numbers, [4, 5, 6]] // dup str() bug println(numbers2) assert true assert numbers.str() == '[1, 2, 3]' - */ } { // test eq @@ -1110,8 +1113,7 @@ fn main() { } { // test array struct contains - /* - todo: does not work + mut coords := []Coord{} coord_1 := Coord{ x: 1 @@ -1124,7 +1126,6 @@ fn main() { println('`exists`: $exists and `not exists`: $not_exists') assert exists == true assert not_exists == false - */ } { // test array of array append @@ -1133,4 +1134,47 @@ fn main() { x[2] << 123 // RTE println(x) } + { + // test array of map insert + mut x := []map[string]int{len: 4} + println(x) // OK + x[2]['123'] = 123 // RTE + println(x) + } + { + // todo(playX): does not work + /* + // test multi fixed array init + a := [3][3]int{} + println(a) + */ + } + { + // test array of multi filter + arr := [1, 2, 3, 4, 5] + nums := Numbers{ + odds: arr.filter(it % 2 == 1) + evens: arr.filter(it % 2 == 0) + } + println(nums) + assert nums.odds == [1, 3, 5] + assert nums.evens == [2, 4] + } + { + // test array of multi map + arr := [1, 3, 5] + nums := Numbers{ + odds: arr.map(it + 2) + evens: arr.map(it * 2) + } + println(nums) + assert nums.odds == [3, 5, 7] + assert nums.evens == [2, 6, 10] + } + { + // test multi fixed array with default init + a := [3][3]int{init: [3]int{init: 10}} + println(a) + assert a == [[10, 10, 10]!, [10, 10, 10]!, [10, 10, 10]!]! + } } diff --git a/vlib/v/gen/js/tests/testdata/string.out b/vlib/v/gen/js/tests/testdata/string.out new file mode 100644 index 0000000000..4070999769 --- /dev/null +++ b/vlib/v/gen/js/tests/testdata/string.out @@ -0,0 +1,7 @@ +ab +1000 +true +true +true +man +true diff --git a/vlib/v/gen/js/tests/testdata/string.v b/vlib/v/gen/js/tests/testdata/string.v new file mode 100644 index 0000000000..d8ecb5e434 --- /dev/null +++ b/vlib/v/gen/js/tests/testdata/string.v @@ -0,0 +1,200 @@ +struct Foo { + bar int +mut: + str string +} + +fn main() { + { + // test add + mut a := 'a' + a += 'b' + println(a) + a = 'a' + for i := 1; i < 1000; i++ { + a += 'b' + } + println(a.len) + println(a.ends_with('bbbbb')) + a += '123' + println(a.ends_with('3')) + } + { + // test ends with + a := 'browser.v' + println(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 + } + { + // test between + s := 'hello [man] how you doing' + println(s.find_between('[', ']')) + } + { + // test compare + a := 'Music' + b := 'src' + println(b >= a) + } + { + // 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 + } + { + // 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 + } + { + // 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 + } + { + // 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' + } + { + // todo(playX): sort codegen + /*// 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'*/ + } + { + // todo: 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 + */ + } + { + // 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] == '' + } +}