builtins: ustring comparisons, concatenation and other functions
							parent
							
								
									fd68c44dfa
								
							
						
					
					
						commit
						ffcff9ebd4
					
				|  | @ -861,12 +861,18 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn { | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		p.expected_type = arg.typ | 		p.expected_type = arg.typ | ||||||
| 		typ := p.bool_expression() | 		mut typ := p.bool_expression() | ||||||
| 		// Optimize `println`: replace it with `printf` to avoid extra allocations and
 | 		// Optimize `println`: replace it with `printf` to avoid extra allocations and
 | ||||||
| 		// function calls.
 | 		// function calls.
 | ||||||
| 		// `println(777)` => `printf("%d\n", 777)`
 | 		// `println(777)` => `printf("%d\n", 777)`
 | ||||||
| 		// (If we don't check for void, then V will compile `println(func())`)
 | 		// (If we don't check for void, then V will compile `println(func())`)
 | ||||||
| 		if i == 0 && (f.name == 'println' || f.name == 'print')  && typ != 'string' && typ != 'void' { | 		if i == 0 && (f.name == 'println' || f.name == 'print') && typ == 'ustring' { | ||||||
|  | 			if typ == 'ustring' { | ||||||
|  | 				p.gen('.s') | ||||||
|  | 			} | ||||||
|  | 			typ = 'string' | ||||||
|  | 		} | ||||||
|  | 		if i == 0 && (f.name == 'println' || f.name == 'print')  && typ != 'string' && typ != 'ustring' && typ != 'void' { | ||||||
| 			T := p.table.find_type(typ) | 			T := p.table.find_type(typ) | ||||||
| 			$if !windows { | 			$if !windows { | ||||||
| 			$if !js { | 			$if !js { | ||||||
|  |  | ||||||
|  | @ -1262,6 +1262,7 @@ fn ($v.name mut $v.typ) $p.cur_fn.name (...) { | ||||||
| 		p.mark_var_changed(v) | 		p.mark_var_changed(v) | ||||||
| 	} | 	} | ||||||
| 	is_str := v.typ == 'string' | 	is_str := v.typ == 'string' | ||||||
|  | 	is_ustr := v.typ == 'ustring' | ||||||
| 	switch tok { | 	switch tok { | ||||||
| 	case Token.assign: | 	case Token.assign: | ||||||
| 		if !is_map && !p.is_empty_c_struct_init { | 		if !is_map && !p.is_empty_c_struct_init { | ||||||
|  | @ -1271,6 +1272,9 @@ fn ($v.name mut $v.typ) $p.cur_fn.name (...) { | ||||||
| 		if is_str && !p.is_js { | 		if is_str && !p.is_js { | ||||||
| 			p.gen('= string_add($v.name, ')// TODO can't do `foo.bar += '!'`
 | 			p.gen('= string_add($v.name, ')// TODO can't do `foo.bar += '!'`
 | ||||||
| 		} | 		} | ||||||
|  | 		else if is_ustr { | ||||||
|  | 			p.gen('= ustring_add($v.name, ') | ||||||
|  | 		} | ||||||
| 		else { | 		else { | ||||||
| 			p.gen(' += ') | 			p.gen(' += ') | ||||||
| 		} | 		} | ||||||
|  | @ -1297,7 +1301,7 @@ fn ($v.name mut $v.typ) $p.cur_fn.name (...) { | ||||||
| 		p.scanner.line_nr-- | 		p.scanner.line_nr-- | ||||||
| 		p.error('cannot use type `$expr_type` as type `$p.assigned_type` in assignment') | 		p.error('cannot use type `$expr_type` as type `$p.assigned_type` in assignment') | ||||||
| 	} | 	} | ||||||
| 	if is_str && tok == .plus_assign && !p.is_js { | 	if (is_str || is_ustr) && tok == .plus_assign && !p.is_js { | ||||||
| 		p.gen(')') | 		p.gen(')') | ||||||
| 	} | 	} | ||||||
| 	// p.assigned_var = ''
 | 	// p.assigned_var = ''
 | ||||||
|  | @ -1393,11 +1397,12 @@ fn (p mut Parser) bterm() string { | ||||||
| 	mut typ := p.expression() | 	mut typ := p.expression() | ||||||
| 	p.expected_type = typ | 	p.expected_type = typ | ||||||
| 	is_str := typ=='string'  &&   !p.is_sql | 	is_str := typ=='string'  &&   !p.is_sql | ||||||
|  | 	is_ustr := typ=='ustring' | ||||||
| 	tok := p.tok | 	tok := p.tok | ||||||
| 	// if tok in [ .eq, .gt, .lt, .le, .ge, .ne] {
 | 	// if tok in [ .eq, .gt, .lt, .le, .ge, .ne] {
 | ||||||
| 	if tok == .eq || tok == .gt || tok == .lt || tok == .le || tok == .ge || tok == .ne { | 	if tok == .eq || tok == .gt || tok == .lt || tok == .le || tok == .ge || tok == .ne { | ||||||
| 		p.fgen(' ${p.tok.str()} ') | 		p.fgen(' ${p.tok.str()} ') | ||||||
| 		if is_str && !p.is_js { | 		if (is_str || is_ustr) && !p.is_js { | ||||||
| 			p.gen(',') | 			p.gen(',') | ||||||
| 		} | 		} | ||||||
| 		else if p.is_sql && tok == .eq { | 		else if p.is_sql && tok == .eq { | ||||||
|  | @ -1440,6 +1445,17 @@ fn (p mut Parser) bterm() string { | ||||||
| 			 Token.lt => p.cgen.set_placeholder(ph, 'string_lt(') | 			 Token.lt => p.cgen.set_placeholder(ph, 'string_lt(') | ||||||
| */ | */ | ||||||
| 		} | 		} | ||||||
|  | 		if is_ustr { | ||||||
|  | 			p.gen(')') | ||||||
|  | 			switch tok { | ||||||
|  | 			case Token.eq: p.cgen.set_placeholder(ph, 'ustring_eq(') | ||||||
|  | 			case Token.ne: p.cgen.set_placeholder(ph, 'ustring_ne(') | ||||||
|  | 			case Token.le: p.cgen.set_placeholder(ph, 'ustring_le(') | ||||||
|  | 			case Token.ge: p.cgen.set_placeholder(ph, 'ustring_ge(') | ||||||
|  | 			case Token.gt: p.cgen.set_placeholder(ph, 'ustring_gt(') | ||||||
|  | 			case Token.lt: p.cgen.set_placeholder(ph, 'ustring_lt(') | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	return typ | 	return typ | ||||||
| } | } | ||||||
|  | @ -2072,6 +2088,7 @@ fn (p mut Parser) expression() string { | ||||||
| 	ph := p.cgen.add_placeholder() | 	ph := p.cgen.add_placeholder() | ||||||
| 	mut typ := p.term() | 	mut typ := p.term() | ||||||
| 	is_str := typ=='string' | 	is_str := typ=='string' | ||||||
|  | 	is_ustr := typ=='ustring' | ||||||
| 	// `a << b` ==> `array_push(&a, b)`
 | 	// `a << b` ==> `array_push(&a, b)`
 | ||||||
| 	if p.tok == .left_shift { | 	if p.tok == .left_shift { | ||||||
| 		if typ.contains('array_') { | 		if typ.contains('array_') { | ||||||
|  | @ -2153,6 +2170,10 @@ fn (p mut Parser) expression() string { | ||||||
| 			p.cgen.set_placeholder(ph, 'string_add(') | 			p.cgen.set_placeholder(ph, 'string_add(') | ||||||
| 			p.gen(',') | 			p.gen(',') | ||||||
| 		} | 		} | ||||||
|  | 		else if is_ustr && tok_op == .plus { | ||||||
|  | 			p.cgen.set_placeholder(ph, 'ustring_add(') | ||||||
|  | 			p.gen(',') | ||||||
|  | 		} | ||||||
| 		// 3 + 4
 | 		// 3 + 4
 | ||||||
| 		else if is_num || p.is_js { | 		else if is_num || p.is_js { | ||||||
| 			if typ == 'void*' { | 			if typ == 'void*' { | ||||||
|  | @ -2172,11 +2193,11 @@ fn (p mut Parser) expression() string { | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		p.check_types(p.term(), typ) | 		p.check_types(p.term(), typ) | ||||||
| 		if is_str && tok_op == .plus && !p.is_js { | 		if (is_str || is_ustr) && tok_op == .plus && !p.is_js { | ||||||
| 			p.gen(')') | 			p.gen(')') | ||||||
| 		} | 		} | ||||||
| 		// Make sure operators are used with correct types
 | 		// Make sure operators are used with correct types
 | ||||||
| 		if !p.pref.translated && !is_str && !is_num { | 		if !p.pref.translated && !is_str && !is_ustr && !is_num { | ||||||
| 			T := p.table.find_type(typ) | 			T := p.table.find_type(typ) | ||||||
| 			if tok_op == .plus { | 			if tok_op == .plus { | ||||||
| 				if T.has_method('+') { | 				if T.has_method('+') { | ||||||
|  |  | ||||||
|  | @ -725,22 +725,128 @@ pub fn (s string) ustring_tmp() ustring { | ||||||
| 	return res | 	return res | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | fn (u ustring) eq(a ustring) bool { | ||||||
|  | 	if u.len != a.len || u.s != a.s { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn (u ustring) ne(a ustring) bool { | ||||||
|  | 	return !u.eq(a) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn (u ustring) lt(a ustring) bool { | ||||||
|  | 	return u.s < a.s | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn (u ustring) le(a ustring) bool { | ||||||
|  | 	return u.lt(a) || u.eq(a) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn (u ustring) gt(a ustring) bool { | ||||||
|  | 	return !u.le(a) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn (u ustring) ge(a ustring) bool { | ||||||
|  | 	return !u.lt(a) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn (u ustring) add(a ustring) ustring { | ||||||
|  | 	mut res := ustring { | ||||||
|  | 		s: u.s + a.s | ||||||
|  | 		runes: new_array(0, u.s.len + a.s.len, sizeof(int)) | ||||||
|  | 	} | ||||||
|  | 	mut j := 0 | ||||||
|  | 	for i := 0; i < u.s.len; i++ { | ||||||
|  | 		char_len := utf8_char_len(u.s.str[i]) | ||||||
|  | 		res.runes << j | ||||||
|  | 		i += char_len - 1 | ||||||
|  | 		j += char_len | ||||||
|  | 		res.len++ | ||||||
|  | 	} | ||||||
|  | 	for i := 0; i < a.s.len; i++ { | ||||||
|  | 		char_len := utf8_char_len(a.s.str[i]) | ||||||
|  | 		res.runes << j | ||||||
|  | 		i += char_len - 1 | ||||||
|  | 		j += char_len | ||||||
|  | 		res.len++ | ||||||
|  | 	} | ||||||
|  | 	return res | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn (u ustring) index_after(p ustring, start int) int { | ||||||
|  | 	if p.len > u.len { | ||||||
|  | 		return -1 | ||||||
|  | 	} | ||||||
|  | 	mut strt := start | ||||||
|  | 	if start < 0 { | ||||||
|  | 		strt = 0 | ||||||
|  | 	} | ||||||
|  | 	if start > u.len { | ||||||
|  | 		return -1 | ||||||
|  | 	} | ||||||
|  | 	mut i := strt | ||||||
|  | 	for i < u.len { | ||||||
|  | 		mut j := 0 | ||||||
|  | 		mut ii := i | ||||||
|  | 		for j < p.len && u.at(ii) == p.at(j) { | ||||||
|  | 			j++ | ||||||
|  | 			ii++ | ||||||
|  | 		} | ||||||
|  | 		if j == p.len { | ||||||
|  | 			return i | ||||||
|  | 		} | ||||||
|  | 		i++ | ||||||
|  | 	} | ||||||
|  | 	return -1 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // counts occurrences of substr in s
 | ||||||
|  | pub fn (u ustring) count(substr ustring) int { | ||||||
|  | 	if u.len == 0 || substr.len == 0 { | ||||||
|  | 		return 0 | ||||||
|  | 	} | ||||||
|  | 	if substr.len > u.len { | ||||||
|  | 		return 0 | ||||||
|  | 	} | ||||||
|  | 	mut n := 0 | ||||||
|  | 	mut i := 0 | ||||||
|  | 	for { | ||||||
|  | 		i = u.index_after(substr, i) | ||||||
|  | 		if i == -1 { | ||||||
|  | 			return n | ||||||
|  | 		} | ||||||
|  | 		i += substr.len | ||||||
|  | 		n++ | ||||||
|  | 	} | ||||||
|  | 	return 0 // TODO can never get here - v doesn't know that
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
| pub fn (u ustring) substr(_start, _end int) string { | pub fn (u ustring) substr(_start, _end int) string { | ||||||
| 	start := u.runes[_start] | 	if _start > _end || _start > u.len || _end > u.len || _start < 0 || _end < 0 { | ||||||
| 	end := if _end >= u.runes.len { | 		panic('substr($_start, $_end) out of bounds (len=$u.len)') | ||||||
|  | 	} | ||||||
|  | 	end := if _end >= u.len { | ||||||
| 		u.s.len | 		u.s.len | ||||||
| 	} | 	} | ||||||
| 	else { | 	else { | ||||||
| 		u.runes[_end] | 		u.runes[_end] | ||||||
| 	} | 	} | ||||||
| 	return u.s.substr(start, end) | 	return u.s.substr(u.runes[_start], end) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn (u ustring) left(pos int) string { | pub fn (u ustring) left(pos int) string { | ||||||
|  | 	if pos >= u.len { | ||||||
|  | 		return u.s | ||||||
|  | 	} | ||||||
| 	return u.substr(0, pos) | 	return u.substr(0, pos) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn (u ustring) right(pos int) string { | pub fn (u ustring) right(pos int) string { | ||||||
|  | 	if pos >= u.len { | ||||||
|  | 		return '' | ||||||
|  | 	} | ||||||
| 	return u.substr(pos, u.len) | 	return u.substr(pos, u.len) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -752,6 +858,9 @@ fn (s string) at(idx int) byte { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn (u ustring) at(idx int) string { | pub fn (u ustring) at(idx int) string { | ||||||
|  | 	if idx < 0 || idx >= u.len { | ||||||
|  | 		panic('string index out of range: $idx / $u.runes.len') | ||||||
|  | 	} | ||||||
| 	return u.substr(idx, idx + 1) | 	return u.substr(idx, idx + 1) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -423,3 +423,36 @@ fn test_quote() { | ||||||
| 	a := `'` | 	a := `'` | ||||||
| 	assert a.str() == '\'' | 	assert a.str() == '\'' | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | fn test_ustring_comparisons() { | ||||||
|  | 	assert ('h€llô !'.ustring() == 'h€llô !'.ustring()) == true | ||||||
|  | 	assert ('h€llô !'.ustring() == 'h€llô'.ustring()) == false | ||||||
|  | 	assert ('h€llô !'.ustring() == 'h€llo !'.ustring()) == false | ||||||
|  | 
 | ||||||
|  | 	assert ('h€llô !'.ustring() != 'h€llô !'.ustring()) == false | ||||||
|  | 	assert ('h€llô !'.ustring() != 'h€llô'.ustring()) == true | ||||||
|  | 
 | ||||||
|  | 	assert ('h€llô'.ustring() < 'h€llô!'.ustring()) == true | ||||||
|  | 	assert ('h€llô'.ustring() < 'h€llo'.ustring()) == false | ||||||
|  | 	assert ('h€llo'.ustring() < 'h€llô'.ustring()) == true | ||||||
|  | 
 | ||||||
|  | 	assert ('h€llô'.ustring() <= 'h€llô!'.ustring()) == true | ||||||
|  | 	assert ('h€llô'.ustring() <= 'h€llô'.ustring()) == true | ||||||
|  | 	assert ('h€llô!'.ustring() <= 'h€llô'.ustring()) == false | ||||||
|  | 
 | ||||||
|  | 	assert ('h€llô!'.ustring() > 'h€llô'.ustring()) == true | ||||||
|  | 	assert ('h€llô'.ustring() > 'h€llô'.ustring()) == false | ||||||
|  | 
 | ||||||
|  | 	assert ('h€llô!'.ustring() >= 'h€llô'.ustring()) == true | ||||||
|  | 	assert ('h€llô'.ustring() >= 'h€llô'.ustring()) == true | ||||||
|  | 	assert ('h€llô'.ustring() >= 'h€llô!'.ustring()) == false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn test_ustring_count() { | ||||||
|  | 	a := 'h€llôﷰ h€llô ﷰ'.ustring() | ||||||
|  | 	assert (a.count('l'.ustring())) == 4 | ||||||
|  | 	assert (a.count('€'.ustring())) == 2 | ||||||
|  | 	assert (a.count('h€llô'.ustring())) == 2 | ||||||
|  | 	assert (a.count('ﷰ'.ustring())) == 2 | ||||||
|  | 	assert (a.count('a'.ustring())) == 0 | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue