v.gen.js: port string methods and fix booleans (#10824)

pull/10851/head
playX 2021-07-18 09:00:20 +03:00 committed by GitHub
parent 8b8f496762
commit d5e0fa6d1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 332 additions and 12 deletions

View File

@ -74,7 +74,7 @@ fn (a &array) set_len(i int) {
#a.arr.length=i #a.arr.length=i
} }
pub fn (mut a array) sort_with_comparator(compare voidptr) { pub fn (mut a array) sort_with_compare(compare voidptr) {
#a.arr.sort(compare) #a.arr.sort(compare)
} }

View File

@ -0,0 +1,8 @@
module builtin
pub fn (b byte) is_space() bool {
mut result := false
#result = /^\s*$/.test(String.fromCharCode(b))
return result
}

View File

@ -121,5 +121,5 @@ fn (s JS.String) toUpperCase() JS.String
fn (s JS.String) toLowerCase() JS.String fn (s JS.String) toLowerCase() JS.String
fn (s JS.String) concat(a JS.String) JS.String fn (s JS.String) concat(a JS.String) JS.String
fn (s JS.String) includes(substr JS.String) bool fn (s JS.String) includes(substr JS.String) bool
fn (s JS.String) ends_with(substr JS.String) bool fn (s JS.String) endsWith(substr JS.String) bool
fn (s JS.String) starts_with(substr JS.String) bool fn (s JS.String) startsWith(substr JS.String) bool

View File

@ -94,15 +94,42 @@ pub fn (s string) count(substr string) int {
} }
pub fn (s string) ends_with(p string) bool { pub fn (s string) ends_with(p string) bool {
return s.str.ends_with(p.str) return s.str.endsWith(p.str)
} }
pub fn (s string) starts_with(p string) bool { pub fn (s string) starts_with(p string) bool {
return s.str.starts_with(p.str) return s.str.startsWith(p.str)
} }
pub fn (s string) fields() []string { pub fn (s string) fields() []string {
return [] // s.str.split() mut res := []string{}
mut word_start := 0
mut word_len := 0
mut is_in_word := false
mut is_space := false
for i, c in s {
is_space = c in [32, 9, 10]
if !is_space {
word_len++
}
if !is_in_word && !is_space {
word_start = i
is_in_word = true
continue
}
if is_space && is_in_word {
res << s[word_start..word_start + word_len]
is_in_word = false
word_len = 0
word_start = 0
continue
}
}
if is_in_word && word_len > 0 {
// collect the remainder word at the end
res << s[word_start..s.len]
}
return res
} }
pub fn (s string) find_between(start string, end string) string { pub fn (s string) find_between(start string, end string) string {
@ -167,3 +194,265 @@ pub fn (s string) u32() u32 {
pub fn (s string) u64() u64 { pub fn (s string) u64() u64 {
return u64(JS.parseInt(s)) return u64(JS.parseInt(s))
} }
// trim_right strips any of the characters given in `cutset` from the right of the string.
// Example: assert ' Hello V d'.trim_right(' d') == ' Hello V'
pub fn (s string) trim_right(cutset string) string {
if s.len < 1 || cutset.len < 1 {
return s.clone()
}
mut pos := s.len - 1
for pos >= 0 {
mut found := false
for cs in cutset {
if s[pos] == cs {
found = true
}
}
if !found {
break
}
pos--
}
if pos < 0 {
return ''
}
return s[..pos + 1]
}
// trim_left strips any of the characters given in `cutset` from the left of the string.
// Example: assert 'd Hello V developer'.trim_left(' d') == 'Hello V developer'
[direct_array_access]
pub fn (s string) trim_left(cutset string) string {
if s.len < 1 || cutset.len < 1 {
return s.clone()
}
mut pos := 0
for pos < s.len {
mut found := false
for cs in cutset {
if s[pos] == cs {
found = true
break
}
}
if !found {
break
}
pos++
}
return s[pos..]
}
// trim_prefix strips `str` from the start of the string.
// Example: assert 'WorldHello V'.trim_prefix('World') == 'Hello V'
pub fn (s string) trim_prefix(str string) string {
if s.starts_with(str) {
return s[str.len..]
}
return s.clone()
}
// trim_suffix strips `str` from the end of the string.
// Example: assert 'Hello VWorld'.trim_suffix('World') == 'Hello V'
pub fn (s string) trim_suffix(str string) string {
if s.ends_with(str) {
return s[..s.len - str.len]
}
return s.clone()
}
// compare_strings returns `-1` if `a < b`, `1` if `a > b` else `0`.
pub fn compare_strings(a &string, b &string) int {
if a < b {
return -1
}
if a > b {
return 1
}
return 0
}
// compare_strings_reverse returns `1` if `a < b`, `-1` if `a > b` else `0`.
fn compare_strings_reverse(a &string, b &string) int {
if a < b {
return 1
}
if a > b {
return -1
}
return 0
}
// compare_strings_by_len returns `-1` if `a.len < b.len`, `1` if `a.len > b.len` else `0`.
fn compare_strings_by_len(a &string, b &string) int {
if a.len < b.len {
return -1
}
if a.len > b.len {
return 1
}
return 0
}
// compare_lower_strings returns the same as compare_strings but converts `a` and `b` to lower case before comparing.
fn compare_lower_strings(a &string, b &string) int {
aa := a.to_lower()
bb := b.to_lower()
return compare_strings(&aa, &bb)
}
// at returns the byte at index `idx`.
// Example: assert 'ABC'.at(1) == byte(`B`)
fn (s string) at(idx int) byte {
mut result := byte(0)
#result = new byte(s.str.charCodeAt(result))
return result
}
pub fn (s string) to_lower() string {
mut result := ''
#let str = s.str.toLowerCase()
#result = new string(str)
return result
}
pub fn (s string) to_upper() string {
mut result := ''
#let str = s.str.toUpperCase()
#result = new string(str)
return result
}
// sort sorts the string array.
pub fn (mut s []string) sort() {
s.sort_with_compare(compare_strings)
}
// sort_ignore_case sorts the string array using case insesitive comparing.
pub fn (mut s []string) sort_ignore_case() {
s.sort_with_compare(compare_lower_strings)
}
// sort_by_len sorts the the string array by each string's `.len` length.
pub fn (mut s []string) sort_by_len() {
s.sort_with_compare(compare_strings_by_len)
}
// str returns a copy of the string
pub fn (s string) str() string {
return s.clone()
}
pub fn (s string) repeat(count int) string {
mut result := ''
#result = new string(s.str.repeat(count))
return result
}
// TODO(playX): Use this iterator instead of using .split('').map(c => byte(c))
#function string_iterator(string) { this.stringIteratorFieldIndex = 0; this.stringIteratorIteratedString = string.str; }
#string_iterator.prototype.next = function next() {
#var done = true;
#var value = undefined;
#var position = this.stringIteratorFieldIndex;
#if (position !== -1) {
#var string = this.stringIteratorIteratedString;
#var length = string.length >>> 0;
#if (position >= length) {
#this.stringIteratorFieldIndex = -1;
#} else {
#done = false;
#var first = string.charCodeAt(position);
#if (first < 0xD800 || first > 0xDBFF || position + 1 === length)
#value = new byte(string[position]);
#else {
#value = new byte(string[position]+string[position+1])
#}
#this.stringIteratorFieldIndex = position + value.length;
#}
#}
#return {
#value, done
#}
#}
#string.prototype[Symbol.iterator] = function () { return new string_iterator(this) }
// TODO: Make these functions actually work.
// strip_margin allows multi-line strings to be formatted in a way that removes white-space
// before a delimeter. by default `|` is used.
// Note: the delimiter has to be a byte at this time. That means surrounding
// the value in ``.
//
// Example:
// st := 'Hello there,
// |this is a string,
// | Everything before the first | is removed'.strip_margin()
// Returns:
// Hello there,
// this is a string,
// Everything before the first | is removed
pub fn (s string) strip_margin() string {
return s.strip_margin_custom(`|`)
}
// strip_margin_custom does the same as `strip_margin` but will use `del` as delimiter instead of `|`
[direct_array_access]
pub fn (s string) strip_margin_custom(del byte) string {
mut sep := del
if sep.is_space() {
eprintln('Warning: `strip_margin` cannot use white-space as a delimiter')
eprintln(' Defaulting to `|`')
sep = `|`
}
// don't know how much space the resulting string will be, but the max it
// can be is this big
mut ret := []byte{}
#ret = new array()
mut count := 0
for i := 0; i < s.len; i++ {
if s[i] in [10, 13] {
unsafe {
ret[count] = s[i]
}
count++
// CRLF
if s[i] == 13 && i < s.len - 1 && s[i + 1] == 10 {
unsafe {
ret[count] = s[i + 1]
}
count++
i++
}
for s[i] != sep {
i++
if i >= s.len {
break
}
}
} else {
unsafe {
ret[count] = s[i]
}
count++
}
}
/*
unsafe {
ret[count] = 0
return ret.vstring_with_len(count)
}*/
mut result := ''
#for (let x of ret.arr) result.str += String.fromCharCode(x.val)
return result
}

View File

@ -359,6 +359,11 @@ pub fn (typ Type) is_string() bool {
return typ.idx() in ast.string_type_idxs return typ.idx() in ast.string_type_idxs
} }
[inline]
pub fn (typ Type) is_bool() bool {
return typ.idx() == ast.bool_type_idx
}
pub const ( pub const (
void_type_idx = 1 void_type_idx = 1
voidptr_type_idx = 2 voidptr_type_idx = 2

View File

@ -308,7 +308,7 @@ fn (mut g JsGen) gen_builtin_type_defs() {
default_value: 'new Number(0)' default_value: 'new Number(0)'
constructor: 'this.val = typeof(val) == "string" ? val.charCodeAt() : (val | 0)' constructor: 'this.val = typeof(val) == "string" ? val.charCodeAt() : (val | 0)'
value_of: 'this.val | 0' value_of: 'this.val | 0'
to_string: 'String.fromCharCode(this.val)' to_string: 'new string(this.val + "")'
eq: 'this.valueOf() === other.valueOf()' eq: 'this.valueOf() === other.valueOf()'
) )
} }

View File

@ -674,6 +674,7 @@ fn (mut g JsGen) expr(node ast.Expr) {
} else { } else {
g.write(node.op.str()) g.write(node.op.str())
g.expr(node.right) g.expr(node.right)
g.write('.valueOf()')
} }
} }
ast.RangeExpr { ast.RangeExpr {
@ -1454,6 +1455,7 @@ fn (mut g JsGen) gen_if_expr(node ast.IfExpr) {
} }
if i < node.branches.len - 1 || !node.has_else { if i < node.branches.len - 1 || !node.has_else {
g.expr(branch.cond) g.expr(branch.cond)
g.write('.valueOf()')
g.write(' ? ') g.write(' ? ')
} }
g.stmts(branch.stmts) g.stmts(branch.stmts)
@ -1474,6 +1476,7 @@ fn (mut g JsGen) gen_if_expr(node ast.IfExpr) {
g.write('true') g.write('true')
} else { } else {
g.expr(branch.cond) g.expr(branch.cond)
g.write('.valueOf()')
} }
g.writeln(') {') g.writeln(') {')
} }
@ -1481,6 +1484,7 @@ fn (mut g JsGen) gen_if_expr(node ast.IfExpr) {
} else if i < node.branches.len - 1 || !node.has_else { } else if i < node.branches.len - 1 || !node.has_else {
g.write('} else if (') g.write('} else if (')
g.expr(branch.cond) g.expr(branch.cond)
g.write('.valueOf()')
g.writeln(') {') g.writeln(') {')
} else if i == node.branches.len - 1 && node.has_else { } else if i == node.branches.len - 1 && node.has_else {
/* /*
@ -1540,10 +1544,12 @@ fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) {
// TODO: What's the best way to do this? // TODO: What's the best way to do this?
// 'string'[3] = `o` // 'string'[3] = `o`
} else { } else {
// TODO: Maybe use u16 there? JS String returns values up to 2^16-1
g.write('new byte(')
g.expr(expr.left) g.expr(expr.left)
g.write('.str.charCodeAt(') g.write('.str.charCodeAt(')
g.expr(expr.index) g.expr(expr.index)
g.write(')') g.write('))')
} }
} else { } else {
// TODO Does this cover all cases? // TODO Does this cover all cases?
@ -1768,14 +1774,14 @@ fn (mut g JsGen) gen_string_inter_literal(it ast.StringInterLiteral) {
fn (mut g JsGen) gen_string_literal(it ast.StringLiteral) { fn (mut g JsGen) gen_string_literal(it ast.StringLiteral) {
text := it.val.replace("'", "\\'") text := it.val.replace("'", "\\'")
should_cast := !(g.cast_stack.len > 0 && g.cast_stack.last() == ast.string_type_idx) should_cast := !(g.cast_stack.len > 0 && g.cast_stack.last() == ast.string_type_idx)
if should_cast { if true || should_cast {
if g.file.mod.name == 'builtin' { if g.file.mod.name == 'builtin' {
g.write('new ') g.write('new ')
} }
g.write('string(') g.write('string(')
} }
g.write("'$text'") g.write("'$text'")
if should_cast { if true || should_cast {
g.write(')') g.write(')')
} }
} }

View File

@ -0,0 +1,2 @@
true
false

View File

@ -0,0 +1,4 @@
x := ' x'
println(x[0].is_space())
println(x[1].is_space())

View File

@ -0,0 +1,3 @@
Hello V developer
Hello V
Hello V

View File

@ -0,0 +1,3 @@
println('d Hello V developer'.trim_left(' d'))
println(' Hello V d'.trim_right(' d'))
println('WorldHello V'.trim_prefix('World'))