1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

move all vlib modules to vlib/

This commit is contained in:
Alexander Medvednikov
2019-06-29 11:54:29 +02:00
parent bdcbcb075b
commit 4594d78bd6
62 changed files with 6 additions and 5 deletions

227
vlib/builtin/array.v Normal file
View File

@@ -0,0 +1,227 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module builtin
struct array {
// Using a void pointer allows to implement arrays without generics and without generating
// extra code for every type.
pub:
data voidptr
len int
cap int
element_size int
}
// Private function, used by V (`nums := []int`)
fn new_array(mylen, cap, elm_size int) array {
arr := array {
len: mylen
cap: cap
element_size: elm_size
data: malloc(cap * elm_size)
}
return arr
}
// Private function, used by V (`nums := [1, 2, 3]`)
fn new_array_from_c_array(len, cap, elm_size int, c_array voidptr) array {
arr := array {
len: len
cap: cap
element_size: elm_size
data: malloc(cap * elm_size)
}
// TODO Write all memory functions (like memcpy) in V
C.memcpy(arr.data, c_array, len * elm_size)
return arr
}
// Private function, used by V (`nums := [1, 2, 3] !`)
fn new_array_from_c_array_no_alloc(len, cap, elm_size int, c_array voidptr) array {
arr := array {
len: len
cap: cap
element_size: elm_size
data: c_array
}
return arr
}
// Private function, used by V (`[0; 100]`)
fn array_repeat(val voidptr, nr_repeats, elm_size int) array {
arr := array {
len: nr_repeats
cap: nr_repeats
element_size: elm_size
data: malloc(nr_repeats * elm_size)
}
for i := 0; i < nr_repeats; i++ {
C.memcpy(arr.data + i * elm_size, val, elm_size)
}
return arr
}
pub fn (a mut array) append_array(b array) {
for i := 0; i < b.len; i++ {
val := b[i]
a._push(val)
}
}
pub fn (a mut array) sort_with_compare(compare voidptr) {
C.qsort(a.data, a.len, a.element_size, compare)
}
pub fn (a mut array) insert(i int, val voidptr) {
if i >= a.len {
panic('array.insert: index larger than length')
return
}
a._push(val)
size := a.element_size
C.memmove(a.data + (i + 1) * size, a.data + i * size, (a.len - i) * size)
a.set(i, val)
}
pub fn (a mut array) prepend(val voidptr) {
a.insert(0, val)
}
pub fn (a mut array) delete(idx int) {
size := a.element_size
C.memmove(a.data + idx * size, a.data + (idx + 1) * size, (a.len - idx) * size)
a.len--
a.cap--
}
fn (a array) _get(i int) voidptr {
if i < 0 || i >= a.len {
panic('array index out of range: $i/$a.len')
}
return a.data + i * a.element_size
}
pub fn (a array) first() voidptr {
if a.len == 0 {
panic('array.first: empty array')
}
return a.data + 0
}
pub fn (a array) last() voidptr {
if a.len == 0 {
panic('array.last: empty array')
}
return a.data + (a.len - 1) * a.element_size
}
pub fn (s array) left(n int) array {
if n >= s.len {
return s
}
return s.slice(0, n)
}
pub fn (s array) right(n int) array {
if n >= s.len {
return s
}
return s.slice(n, s.len)
}
pub fn (s array) slice(start, _end int) array {
mut end := _end
if start > end {
panic('invalid slice index: $start > $end')
}
if end >= s.len {
end = s.len
}
l := end - start
res := array {
element_size: s.element_size
data: s.data + start * s.element_size
len: l
cap: l
}
return res
}
pub fn (a mut array) set(idx int, val voidptr) {
if idx < 0 || idx >= a.len {
panic('array index out of range: $idx / $a.len')
}
C.memcpy(a.data + a.element_size * idx, val, a.element_size)
}
pub fn (arr mut array) _push(val voidptr) {
if arr.len >= arr.cap - 1 {
cap := (arr.len + 1) * 2
// println('_push: realloc, new cap=$cap')
if arr.cap == 0 {
arr.data = malloc(cap * arr.element_size)
}
else {
arr.data = C.realloc(arr.data, cap * arr.element_size)
}
arr.cap = cap
}
C.memcpy(arr.data + arr.element_size * arr.len, val, arr.element_size)
arr.len++
}
pub fn (arr mut array) _push_many(val voidptr, size int) {
if arr.len >= arr.cap - size {
cap := (arr.len + size) * 2
// println('_push: realloc, new cap=$cap')
if arr.cap == 0 {
arr.data = malloc(cap * arr.element_size)
}
else {
arr.data = C.realloc(arr.data, cap * arr.element_size)
}
arr.cap = cap
}
C.memcpy(arr.data + arr.element_size * arr.len, val, arr.element_size * size)
arr.len += size
}
pub fn (a[]int) str() string {
mut res := '['
for i := 0; i < a.len; i++ {
val := a[i]
res += '$val'
if i < a.len - 1 {
res += ', '
}
}
res += ']'
return res
}
pub fn (a[]int) free() {
// println('array free')
C.free(a.data)
}
// TODO generic
// "[ 'a', 'b', 'c' ]"
pub fn (a[]string) str() string {
mut res := '['
for i := 0; i < a.len; i++ {
val := a[i]
res += '"$val"'
if i < a.len - 1 {
res += ', '
}
}
res += ']'
return res
}
fn free(a voidptr) {
C.free(a)
}

138
vlib/builtin/array_test.v Normal file
View File

@@ -0,0 +1,138 @@
import os
const (
q = [1, 2, 3]
A = 8
)
fn test_ints() {
mut a := [1, 5, 2, 3]
assert a.len == 4
assert a[0] == 1
assert a[2] == 2
assert a.last() == 3
println(a)
a << 4
println(a)
assert a.len == 5
assert a[4] == 4
assert a.last() == 4
// assert a.contains(4) == true
// assert a.contains(777) == false
// assert a.str() == '51234'
mut s := a.str()
assert s == '[1, 5, 2, 3, 4]'
// Insert
// val := 5
// a.insert(1, val)// 1 5 2 3 4
s = a.str()
assert a[1] == 5
assert a.last() == 4
// a.sort()
s = a.str()
assert s == '[1, 5, 2, 3, 4]'
// Delete
a.delete(0)
assert a.str() == '[5, 2, 3, 4]'
a.delete(1)
assert a.str() == '[5, 3, 4]'
}
fn test_short() {
a := [1, 2, 3]
assert a.len == 3
assert a.cap == 3
assert a[0] == 1
assert a[1] == 2
assert a[2] == 3
}
fn test_large() {
mut a := [0; 0]
for i := 0; i < 10000; i++ {
a << i
}
assert a.len == 10000
assert a[234] == 234
}
struct Chunk {
val string
}
struct K {
q []Chunk
}
fn test_empty() {
mut chunks := []Chunk{}
a := Chunk{}
assert chunks.len == 0
chunks << a
assert chunks.len == 1
chunks = []Chunk{}
assert chunks.len == 0
chunks << a
assert chunks.len == 1
}
fn test_push() {
mut a := []int
a << 1
a << 3
assert a[1] == 3
assert a.str() == '[1, 3]'
}
fn test_strings() {
s := 'hi'
if s.contains('i') {
// println('$s')
}
a := ['a', 'b', 'c']
assert a.str() == '["a", "b", "c"]'
// println(a)
}
fn test_repeat() {
a := [0; 5]
// a := [0 x 5]
assert a.len == 5
assert a[0] == 0 && a[1] == 0 && a[2] == 0 && a[3] == 0 && a[4] == 0
}
fn test_right() {
a := [1, 2, 3, 4]
b := a.right(1)
assert b[0] == 2
assert b[1] == 3
}
fn test_left() {
a := [1, 2, 3]
b := a.left(2)
assert b[0] == 1
assert b[1] == 2
}
fn test_slice() {
a := [1, 2, 3, 4]
b := a.slice(2, 4)
for val in b {
println(val)
}
assert a.slice(1, 2).len == 1
println(a.slice(2, 4))
}
fn test_push_many() {
mut a := [1, 2, 3]
b := [4, 5, 6]
a._push_many(b.data, b.len)
assert a.len == 6
assert a[0] == 1
assert a[3] == 4
assert a[5] == 6
println(a)
}

116
vlib/builtin/builtin.v Normal file
View File

@@ -0,0 +1,116 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module builtin
pub fn exit(code int) {
C.exit(code)
}
// isnil returns true if an object is nil (only for C objects).
pub fn isnil(v voidptr) bool {
return v == 0
}
fn on_panic(f fn (int) int) {
// TODO
}
pub fn print_backtrace() {
return
$if mac {
buffer := [100]voidptr
nr_ptrs := C.backtrace(buffer, 100)
C.backtrace_symbols_fd(buffer, nr_ptrs, 1)
}
}
pub fn panic(s string) {
println('V panic: $s')
print_backtrace()
C.exit(1)
}
pub fn println(s string) {
// Should never happen
if isnil(s.str) {
panic('println(NIL)')
}
C.printf('%.*s\n', s.len, s.str)
}
pub fn eprintln(s string) {
if isnil(s.str) {
panic('eprintln(NIL)')
}
$if mac {
C.fprintf(stderr, '%.*s\n', s.len, s.str)
}
// TODO issues with stderr and cross compiling for Linux
$else {
println(s)
}
}
pub fn print(s string) {
C.printf('%.*s', s.len, s.str)
}
__global total_m i64 = 0
pub fn malloc(n int) byteptr {
if n < 0 {
panic('malloc(<0)')
}
/*
TODO
#ifdef VPLAY
if n > 10000 {
panic('allocating more than 10 KB is not allowed in the playground')
}
#endif
#ifdef DEBUG_ALLOC
total_m += n
println('\n\n\nmalloc($n) total=$total_m')
print_backtrace()
#endif
*/
ptr := C.malloc(n)
if isnil(ptr) {
panic('malloc($n) failed')
}
return ptr
}
pub fn calloc(n int) byteptr {
if n < 0 {
panic('calloc(<0)')
}
return C.calloc(n, 1)
}
fn _strlen(s byteptr) int {
// TODO don't call a C function, implement it in V
return C.strlen(s)
}
// `fn foo() ?Foo { return foo }` => `fn foo() ?Foo { return opt_ok(foo); }`
fn opt_ok(data voidptr) Option {
return Option {
data: data
ok: true
}
}
fn memdup(src voidptr, sz int) voidptr {
mem := malloc(sz)
return C.memcpy(mem, src, sz)
}
pub fn error(s string) Option {
return Option {
error: s
}
}

161
vlib/builtin/int.v Normal file
View File

@@ -0,0 +1,161 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module builtin
pub fn (d double) str() string {
buf := malloc(sizeof(double) * 5 + 1)// TODO
C.sprintf(buf, '%f', d)
return tos(buf, _strlen(buf))
}
pub fn (d f64) str() string {
buf := malloc(sizeof(double) * 5 + 1)// TODO
C.sprintf(buf, '%f', d)
return tos(buf, _strlen(buf))
}
pub fn (d f32) str() string {
buf := malloc(sizeof(double) * 5 + 1)// TODO
C.sprintf(buf, '%f', d)
return tos(buf, _strlen(buf))
}
pub fn ptr_str(ptr voidptr) string {
buf := malloc(sizeof(double) * 5 + 1)// TODO
C.sprintf(buf, '%p', ptr)
return tos(buf, _strlen(buf))
}
// fn (nn i32) str() string {
// return i
// }
pub fn (nn int) str() string {
mut n := nn
if n == 0 {
return '0'
}
max := 16
mut buf := malloc(max)
mut len := 0
mut is_neg := false
if n < 0 {
n = -n
is_neg = true
}
// Fill the string from the end
for n > 0 {
d := n % 10
buf[max - len - 1] = d + int(`0`)
len++
n = n / 10
}
// Prepend - if it's negative
if is_neg {
buf[max - len - 1] = `-`
len++
}
return tos(buf + max - len, len)
}
pub fn (nn u8) str() string {
mut n := nn
if n == u8(0) {
return '0'
}
max := 5
mut buf := malloc(max)
mut len := 0
mut is_neg := false
if n < u8(0) {
n = -n
is_neg = true
}
// Fill the string from the end
for n > u8(0) {
d := n % u8(10)
buf[max - len - 1] = d + u8(`0`)
len++
n = n / u8(10)
}
// Prepend - if it's negative
if is_neg {
buf[max - len - 1] = `-`
len++
}
return tos(buf + max - len, len)
}
pub fn (nn i64) str() string {
mut n := nn
if n == i64(0) {
return '0'
}
max := 32
mut buf := malloc(max)
mut len := 0
mut is_neg := false
if n < i64(0) {
n = -n
is_neg = true
}
// Fill the string from the end
for n > i64(0) {
d := int(n % i64(10))
buf[max - len - 1] = d + int(`0`)
len++
n = n / i64(10)
}
// Prepend - if it's negative
if is_neg {
buf[max - len - 1] = `-`
len++
}
return tos(buf + max - len, len)
}
pub fn (b bool) str() string {
if b {
return 'true'
}
return 'false'
}
pub fn (n int) hex() string {
s := n.str()
hex := malloc(s.len + 3) // 0x + \n
C.sprintf(hex, '0x%x', n)
return tos(hex, s.len + 3)
}
pub fn (n i64) hex() string {
s := n.str()
hex := malloc(s.len + 3)
C.sprintf(hex, '0x%x', n)
return tos(hex, s.len + 3)
}
pub fn (a[]byte) contains(val byte) bool {
for aa in a {
if aa == val {
return true
}
}
return false
}
/* TODO
fn (c rune) str() string {
}
*/
pub fn (c byte) str() string {
mut str := string {
len: 1
str: malloc(2)
}
str.str[0] = c
str.str[1] = `\0`
return str
}

181
vlib/builtin/map.v Normal file
View File

@@ -0,0 +1,181 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module builtin
struct map {
// cap int
// keys []string
// table byteptr
// keys_table *string
// table *Entry
element_size int
// collisions []Entry
pub:
entries []Entry
is_sorted bool
}
struct Entry {
pub:
key string
val voidptr
// linked list for collisions
// next *Entry
}
pub fn new_map(cap, elm_size int) map {
res := map {
// len: len,
element_size: elm_size
// entries:
// keys: []string
}
return res
}
fn (m &map) new_entry(key string, val voidptr) Entry {
new_e := Entry {
key: key
val: malloc(m.element_size)
// next: 0
}
C.memcpy(new_e.val, val, m.element_size)
return new_e
}
fn (m mut map) _set(key string, val voidptr) {
e := m.new_entry(key, val)
for i := 0; i < m.entries.len; i++ {
entry := m.entries[i]
if entry.key == key {
// e := Entry2{key: key, val: val}
m.entries[i] = e
return
}
}
m.entries << e// m.new_entry(key, val)
m.is_sorted = false
}
fn volt_abs(n int) int {
// println('volt_abs($n)')
if n < 0 {
// println('< 0: -($n)')
return -n
}
return n
}
fn (m map) bs(query string, start, end int, out voidptr) {
// println('bs "$query" $start -> $end')
mid := start + ((end - start) / 2)
if end - start == 0 {
last := m.entries[end]
C.memcpy(out, last.val, m.element_size)
return
}
if end - start == 1 {
first := m.entries[start]
C.memcpy(out, first.val, m.element_size)
return
}
if mid >= m.entries.len {
return
}
mid_msg := m.entries[mid]
// println('mid.key=$mid_msg.key')
if query < mid_msg.key {
m.bs(query, start, mid, out)
return
}
m.bs(query, mid, end, out)
}
fn compare_map(a, b *Entry) int {
if a.key < b.key {
return -1
}
if a.key > b.key {
return 1
}
return 0
}
pub fn (m mut map) sort() {
m.entries.sort_with_compare(compare_map)
m.is_sorted = true
}
pub fn (m map) keys() []string {
mut keys := []string{}
for i := 0; i < m.entries.len; i++ {
entry := m.entries[i]
keys << entry.key
}
return keys
}
fn (m map) get(key string, out voidptr) bool {
if m.is_sorted {
// println('\n\nget "$key" sorted')
m.bs(key, 0, m.entries.len, out)
return true
}
for i := 0; i < m.entries.len; i++ {
entry := m.entries[i]
if entry.key == key {
C.memcpy(out, entry.val, m.element_size)
return true
}
}
return false
}
pub fn (m map) exists(key string) bool {
for i := 0; i < m.entries.len; i++ {
entry := m.entries[i]
if entry.key == key {
return true
}
}
return false
}
pub fn (m map) print() {
println('<<<<<<<<')
for i := 0; i < m.entries.len; i++ {
// entry := m.entries[i]
// println('$entry.key => $entry.val')
}
/*
for i := 0; i < m.cap * m.element_size; i++ {
b := m.table[i]
print('$i: ')
C.printf('%02x', b)
println('')
}
*/
println('>>>>>>>>>>')
}
pub fn (m map) free() {
// C.free(m.table)
// C.free(m.keys_table)
}
pub fn (m map_string) str() string {
// return 'not impl'
if m.entries.len == 0 {
return '{}'
}
// TODO use bytes buffer
mut s := '{\n'
for entry in m.entries {
val := m[entry.key]
s += ' "$entry.key" => "$val"\n'
}
s += '}'
return s
}

12
vlib/builtin/option.v Normal file
View File

@@ -0,0 +1,12 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module builtin
struct Option {
data voidptr
error string
ok bool
}

840
vlib/builtin/string.v Normal file
View File

@@ -0,0 +1,840 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module builtin
// V strings are not null-terminated.
struct string {
pub:
str byteptr
len int
}
struct ustring {
pub:
s string
runes []int
len int
}
// For C strings only
fn C.strlen(s byteptr) int
fn todo() { }
// Converts a C string to a V string
pub fn tos(s byteptr, len int) string {
// This should never happen.
if isnil(s) {
panic('tos(): nil string')
}
return string {
str: s
len: len
}
}
pub fn tos_clone(s byteptr) string {
if isnil(s) {
panic('tos: nil string')
return string{}
}
len := strlen(s)
res := tos(s, len)
return res.clone()
}
// Same as `tos`, but calculates the length. Called by `string(bytes)` casts.
fn tos2(s byteptr) string {
if isnil(s) {
panic('tos2: nil string')
return string{}
}
len := C.strlen(s)
res := tos(s, len)
return res
}
pub fn (a string) clone() string {
mut b := string {
len: a.len
str: malloc(a.len + 1)
}
for i := 0; i < a.len; i++ {
b[i] = a[i]
}
b[a.len] = `\0`
return b
}
pub fn (s string) cstr() byteptr {
clone := s.clone()
return clone.str
}
pub fn (s string) replace(rep, with string) string {
if s.len == 0 || rep.len == 0 {
return s
}
if !s.contains(rep) {
return s
}
// println('"$s" replace "$rep" with "$with" rep.len=$rep.len')
// TODO PERF Allocating ints is expensive. Should be a stack array
// Get locations of all reps within this string
mut idxs := []int{}
// idxs := []int {
// 2, 8, 14
// }
for i := 0; i < s.len; i++ {
// Do we have the string we are looking for (rep) starting at i?
// Go thru all chars in rep and compare.
mut rep_i := 0
mut j := i
for rep_i < rep.len && j < s.len && s[j] == rep[rep_i] {
rep_i++
j++
}
if rep_i == rep.len {
idxs << i
}
}
// Dont change the string if there's nothing to replace
if idxs.len == 0 {
return s
}
// Now we know the number of replacements we need to do and we can calc the len of the new string
new_len := s.len + idxs.len * (with.len - rep.len)
mut b := malloc(new_len + 1)// add a newline just in case
// Fill the new string
mut idx_pos := 0
mut cur_idx := idxs[idx_pos]
mut b_i := 0
for i := 0; i < s.len; i++ {
// Reached the location of rep, replace it with "with"
if i == cur_idx {
for j := 0; j < with.len; j++ {
b[b_i] = with[j]
b_i++
}
// Skip the length of rep, since we just replaced it with "with"
i += rep.len - 1
// Go to the next index
idx_pos++
if idx_pos < idxs.len {
cur_idx = idxs[idx_pos]
}
}
// Rep doesnt start here, just copy
else {
b[b_i] = s[i]
b_i++
}
}
b[new_len] = `\0`
return tos(b, new_len)
}
pub fn (s string) int() int {
return C.atoi(s.str)
}
pub fn (s string) f32() f32 {
return C.atof(s.str)
}
// ==
fn (s string) eq(a string) bool {
if isnil(s.str) {
panic('string.eq(): nil string')
}
if s.len != a.len {
return false
}
for i := 0; i < s.len; i++ {
if s[i] != a[i] {
return false
}
}
return true
}
// !=
fn (s string) ne(a string) bool {
return !s.eq(a)
}
// s < a
fn (s string) lt(a string) bool {
for i := 0; i < s.len; i++ {
if i >= a.len || s[i] > a[i] {
return false
}
else if s[i] < a[i] {
return true
}
}
if s.len < a.len {
return true
}
return false
}
// s <= a
fn (s string) le(a string) bool {
return s.lt(a) || s.eq(a)
}
// s > a
fn (s string) gt(a string) bool {
return !s.le(a)
}
// s >= a
fn (s string) ge(a string) bool {
return !s.lt(a)
}
// TODO `fn (s string) + (a string)` ? To be consistent with operator overloading syntax.
pub fn (s string) add(a string) string {
new_len := a.len + s.len
mut res := string {
len: new_len
str: malloc(new_len + 1)
}
for j := 0; j < s.len; j++ {
res[j] = s[j]
}
for j := 0; j < a.len; j++ {
res[s.len + j] = a[j]
}
res[new_len] = `\0`// V strings are not null terminated, but just in case
return res
}
pub fn (s string) split(delim string) []string {
// println('string split delim="$delim" s="$s"')
mut res := []string
if delim.len == 0 {
res << s
return res
}
if delim.len == 1 {
return s.split_single(delim[0])
// println('split 1 only')
// os.exit()
}
mut i := 0
mut start := 0// - 1
for i < s.len {
// printiln(i)
mut a := s[i] == delim[0]
mut j := 1
for j < delim.len && a {
a = a && s[i + j] == delim[j]
j++
}
last := i == s.len - 1
if a || last {
if last {
i++
}
mut val := s.substr(start, i)
// println('got it "$val" start=$start i=$i delim="$delim"')
if val.len > 0 {
// todo perf
// val now is '___VAL'. remove '___' from the start
if val.starts_with(delim) {
// println('!!')
val = val.right(delim.len)
}
res << val.trim_space()
}
start = i
}
i++
}
return res
}
pub fn (s string) split_single(delim byte) []string {
mut res := []string
if int(delim) == 0 {
res << s
return res
}
mut i := 0
mut start := 0
for i < s.len {
a := s[i] == delim
b := i == s.len - 1
if a || b {
if i == s.len - 1 {
i++
}
val := s.substr(start, i)
if val.len > 0 {
res << val.trim_space()
}
start = i + 1
}
i++
}
return res
}
pub fn (s string) split_into_lines() []string {
mut res := []string
if s.len == 0 {
return res
}
mut start := 0
for i := 0; i < s.len; i++ {
last := i == s.len - 1
if int(s[i]) == 10 || last {
if last {
i++
}
line := s.substr(start, i)
res << line
start = i + 1
}
}
return res
}
// 'hello'.left(2) => 'he'
pub fn (s string) left(n int) string {
if n >= s.len {
return s
}
return s.substr(0, n)
}
pub fn (s string) right(n int) string {
if n >= s.len {
return ''
}
return s.substr(n, s.len)
}
// Because the string is immutable, it is safe for multiple strings to share
// the same storage, so slicing s results in a new 2-word structure with a
// potentially different pointer and length that still refers to the same byte
// sequence. This means that slicing can be done without allocation or copying,
// making string slices as efficient as passing around explicit indexes.
// substr without allocations. Reuses memory and works great. BUT. This substring does not have
// a \0 at the end, and it's not possible to add it. So if we have s = 'privet'
// and substr := s.substr_fast(1, 4) ('riv')
// puts(substr.str) will print 'rivet'
// Avoid using C functions with these substrs!
pub fn (s string) substr(start, end int) string {
/*
if start > end || start >= s.len || end > s.len || start < 0 || end < 0 {
panic('substr($start, $end) out of bounds (len=$s.len)')
return ''
}
*/
if start >= s.len {
return ''
}
len := end - start
res := string {
str: s.str + start
len: len
}
return res
}
// KMP search
pub fn (s string) index(p string) int {
if p.len > s.len {
return -1
}
mut prefix := [0]
mut j := 0
for i := 1; i < p.len; i++ {
for p[j] != p[i] && j > 0 {
j = prefix[j - 1]
}
if p[j] == p[i] {
j++
}
prefix << j
}
j = 0
for i := 0; i < s.len; i++ {
for p[j] != s[i] && j > 0 {
j = prefix[j - 1]
}
if p[j] == s[i] {
j++
}
if j == p.len {
prefix.free()
return i - p.len + 1
}
}
prefix.free()
return -1
}
pub fn (s string) last_index(p string) int {
if p.len > s.len {
return -1
}
mut i := s.len - p.len
for i >= 0 {
mut j := 0
for j < p.len && s[i + j] == p[j] {
j++
}
if j == p.len {
return i
}
i--
}
return -1
}
pub fn (s string) index_after(p string, start int) int {
if p.len > s.len {
return -1
}
mut strt := start
if start < 0 {
strt = 0
}
if start >= s.len {
return -1
}
mut i := strt
for i < s.len {
mut j := 0
mut ii := i
for j < p.len && s[ii] == p[j] {
j++
ii++
}
if j == p.len {
return i
}
i++
}
return -1
}
pub fn (s string) contains(p string) bool {
res := s.index(p) > 0 - 1
return res
}
pub fn (s string) starts_with(p string) bool {
res := s.index(p) == 0
return res
}
pub fn (s string) ends_with(p string) bool {
if p.len > s.len {
return false
}
res := s.last_index(p) == s.len - p.len
return res
}
// TODO only works with ASCII
pub fn (s string) to_lower() string {
mut b := malloc(s.len)// TODO + 1 ??
for i := 0; i < s.len; i++ {
b[i] = C.tolower(s.str[i])
}
return tos(b, s.len)
}
pub fn (s string) to_upper() string {
mut b := malloc(s.len)// TODO + 1 ??
for i := 0; i < s.len; i++ {
b[i] = C.toupper(s.str[i])
}
return tos(b, s.len)
}
// 'hey [man] how you doin'
// find_between('[', ']') == 'man'
pub fn (s string) find_between(start, end string) string {
start_pos := s.index(start)
if start_pos == -1 {
return ''
}
// First get everything to the right of 'start'
val := s.right(start_pos + start.len)
end_pos := val.index(end)
if end_pos == -1 {
return val
}
return val.left(end_pos)
}
// TODO generic
pub fn (ar[]string) contains(val string) bool {
for s in ar {
if s == val {
return true
}
}
return false
}
// TODO generic
pub fn (ar[]int) contains(val int) bool {
for i, s in ar {
if s == val {
return true
}
}
return false
}
/*
pub fn (a[]string) to_c() voidptr {
char ** res = malloc(sizeof(char*) * a.len);
for i := 0; i < a.len; i++ {
val := a[i]
# res[i] = val.str;
}
return res;
return 0
}
*/
fn is_space(c byte) bool {
return C.isspace(c)
}
pub fn (c byte) is_space() bool {
return is_space(c)
}
pub fn (s string) trim_space() string {
if s == '' {
return ''
}
// println('TRIM SPACE "$s"')
mut i := 0
for i < s.len && is_space(s[i]) {
i++
}
mut res := s.right(i)
mut end := res.len - 1
for end >= 0 && is_space(res[end]) {
// C.printf('end=%d c=%d %c\n', end, res.str[end])
end--
}
res = res.left(end + 1)
// println('after SPACE "$res"')
return res
}
pub fn (s string) trim(c byte) string {
if s == '' {
return ''
}
mut i := 0
for i < s.len && c == s[i] {
i++
}
mut res := s.right(i)
mut end := res.len - 1
for end >= 0 && c == res[end] {
end--
}
res = res.left(end + 1)
return res
}
pub fn (s string) trim_left(cutset string) string {
mut start := s.index(cutset)
if start != 0 {
return s
}
for start < s.len - 1 && s[start] == cutset[0] {
start++
}
return s.right(start)
}
pub fn (s string) trim_right(cutset string) string {
pos := s.last_index(cutset)
if pos == -1 {
return s
}
return s.left(pos)
}
// fn print_cur_thread() {
// //C.printf("tid = %08x \n", pthread_self());
// }
fn compare_strings(a, b *string) int {
if a.lt(b) {
return -1
}
if a.gt(b) {
return 1
}
return 0
}
fn compare_strings_by_len(a, b *string) int {
if a.len < b.len {
return -1
}
if a.len > b.len {
return 1
}
return 0
}
fn compare_lower_strings(a, b *string) int {
aa := a.to_lower()
bb := b.to_lower()
return compare_strings(aa, bb)
}
pub fn (s mut []string) sort() {
s.sort_with_compare(compare_strings)
}
pub fn (s mut []string) sort_ignore_case() {
s.sort_with_compare(compare_lower_strings)
}
pub fn (s mut []string) sort_by_len() {
s.sort_with_compare(compare_strings_by_len)
}
pub fn (s string) ustring() ustring {
mut res := ustring {
s: s
// runes will have at least s.len elements, save reallocations
// TODO use VLA for small strings?
runes: new_array(0, s.len, sizeof(int))
}
for i := 0; i < s.len; i++ {
char_len := utf8_char_len(s.str[i])
//# char_len =UTF8_CHAR_LEN(s.str[i]);
// println('cl=$char_len')
res.runes << i
i += char_len - 1
res.len++
}
return res
}
// A hack that allows to create ustring without allocations.
// It's called from functions like draw_text() where we know that the string is going to be freed
// right away. Uses global buffer for storing runes []int array.
__global g_ustring_runes []int
pub fn (s string) ustring_tmp() ustring {
mut res := ustring {
s: s
runes: 0
}
res.runes = g_ustring_runes
res.runes.len = s.len
mut j := 0
for i := 0; i < s.len; i++ {
//char_len := 0
//# char_len =UTF8_CHAR_LEN(s.str[i]);
char_len := utf8_char_len(s.str[i])
res.runes[j] = i
j++
i += char_len - 1
res.len++
}
return res
}
fn (u ustring) substr(start, end int) string {
// println('substr($start, $end)')
// println('runes=')
// println(u.runes)
start = u.runes[start]
// handle last char
if end >= u.runes.len {
end = u.s.len
}
else {
end = u.runes[end]
}
// println('fast $start, $end')
return u.s.substr(start, end)
}
fn (u ustring) left(pos int) string {
return u.substr(0, pos)
}
fn (u ustring) right(pos int) string {
return u.substr(pos, u.len)
}
fn (s string) at(idx int) byte {
if idx < 0 || idx >= s.len {
panic('string index out of range: $idx / $s.len')
}
return s.str[idx]
}
pub fn (u ustring) at(idx int) string {
return u.substr(idx, idx + 1)
}
fn (u ustring) free() {
u.runes.free()
}
fn abs(a int) int {
if a >= 0 {
return a
}
return -a
}
pub fn (c byte) is_digit() bool {
return c >= `0` && c <= `9`
}
pub fn (c byte) is_letter() bool {
return (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`)
}
pub fn (s string) free() {
C.free(s.str)
}
fn (arr[]string) free() {
for s in arr {
s.free()
}
C.free(arr.data)
}
// all_before('23:34:45.234', '.') == '23:34:45'
pub fn (s string) all_before(dot string) string {
pos := s.index(dot)
if pos == -1 {
return s
}
return s.left(pos)
}
pub fn (s string) all_before_last(dot string) string {
pos := s.last_index(dot)
if pos == -1 {
return s
}
return s.left(pos)
}
pub fn (s string) all_after(dot string) string {
pos := s.last_index(dot)
if pos == -1 {
return s
}
return s.right(pos + dot.len)
}
// fn (s []string) substr(a, b int) string {
// return join_strings(s.slice_fast(a, b))
// }
pub fn (a[]string) join(del string) string {
if a.len == 0 {
return ''
}
mut len := 0
for i, val in a {
len += val.len + del.len
}
len -= del.len
// Allocate enough memory
mut res := ''
res.len = len
res.str = malloc(res.len + 1)
mut idx := 0
// Go thru every string and copy its every char one by one
for i, val in a {
for j := 0; j < val.len; j++ {
c := val[j]
res.str[idx] = val.str[j]
idx++
}
// Add del if it's not last
if i != a.len - 1 {
for k := 0; k < del.len; k++ {
res.str[idx] = del.str[k]
idx++
}
}
}
res.str[res.len] = `\0`
return res
}
pub fn (s[]string) join_lines() string {
return s.join('\n')
}
pub fn (s string) reverse() string {
mut res := string {
len: s.len
str: malloc(s.len + 1)
}
for i := s.len - 1; i >= 0; i-- {
res[s.len-i-1] = s[i]
}
return res
}
// 'hello'.limit(2) => 'he'
// 'hi'.limit(10) => 'hi'
pub fn (s string) limit(max int) string {
u := s.ustring()
if u.len <= max {
return s
}
return u.substr(0, max)
}
// TODO is_white_space()
pub fn (c byte) is_white() bool {
i := int(c)
return i == 10 || i == 32 || i == 9 || i == 13 || c == `\r`
}
// TODO move this to strings.repeat()
pub fn repeat_char(c byte, n int) string {
if n <= 0 {
return ''
}
mut arr := malloc(n + 1)
for i := 0; i < n; i++ {
arr[i] = c
}
arr[n] = `\0`
return tos(arr, n)
}
pub fn (s string) hash() int {
mut hash := int(0)
for i := 0; i < s.len; i++ {
// if key == 'Content-Type' {
// println('$i) $hash')
// }
hash = hash * int(31) + int(s.str[i])
}
return hash
}

View File

@@ -0,0 +1,39 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module builtin
struct StringBuilder {
buf []byte
len int
}
pub fn new_string_builder(initial_size int) StringBuilder {
return StringBuilder {
buf: new_array(0, initial_size, sizeof(byte))
}
}
pub fn (b mut StringBuilder) write(s string) {
b.buf._push_many(s.str, s.len)
b.len += s.len
}
pub fn (b mut StringBuilder) writeln(s string) {
b.buf._push_many(s.str, s.len)
b.buf << `\n`
b.len += s.len + 1
}
pub fn (b StringBuilder) str() string {
return tos(b.buf.data, b.len)
}
pub fn (b StringBuilder) cut(n int) {
b.len -= n
}
pub fn (b mut StringBuilder) free() {
C.free(b.buf.data)
}

295
vlib/builtin/string_test.v Normal file
View File

@@ -0,0 +1,295 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
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')
}
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_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'
}
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')
}
fn test_clone() {
mut a := 'a'
a += 'a'
a += 'a'
mut b := a
mut 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('kek', 'lul')
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', 'kek') == 'kek kek kek'
b = 'oneBtwoBBthree'
assert b.replace('B', '') == 'onetwothree'
b = '**char'
assert b.replace('*char', 'byteptr') == '*byteptr'
mut c :='abc'
assert c.replace('','-') == c
}
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.ustring()
assert u.len == 6
assert s2.substr(1, 4).len == 3
assert s2.substr(1, 4) == 'riv'
assert u.substr(1, 4).len == 6
assert u.substr(1, 4) == 'рив'
assert s2.substr(1, 2) == 'r'
assert u.substr(1, 2) == 'р'
assert s2.ustring().at(1) == 'r'
assert u.at(1) == 'р'
// ///////
first := u.at(0)
last := u.at(u.len - 1)
assert first.len == 2
assert last.len == 2
}
fn test_lower() {
mut s := 'A'
assert s.to_lower() == 'a'
assert s.to_lower().len == 1
s = 'HELLO'
assert s.to_lower() == 'hello'
assert s.to_lower().len == 5
s = 'Aloha'
assert s.to_lower() == 'aloha'
s = 'Have A nice Day!'
assert s.to_lower() == 'have a nice day!'
s = 'hi'
assert s.to_lower() == 'hi'
}
fn test_left_right() {
s := 'ALOHA'
assert s.left(3) == 'ALO'
assert s.right(3) == 'HA'
u := s.ustring()
assert u.left(3) == 'ALO'
assert u.right(3) == 'HA'
}
fn test_contains() {
s := 'view.v'
assert s.contains('vi')
assert !s.contains('random')
}
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
f := '71.5 hasdf'
assert f.f32() == 71.5
b := 1.52345
mut a := '${b:.03f}'
assert a == '1.523'
num := 7
a = '${num:03d}'
vals := ['9']
assert vals[0].int() == 9
}
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_left() {
mut s := 'module main'
assert s.trim_left(' ') == 'module main'
s = ' module main'
assert s.trim_left(' ') == 'module main'
}
fn test_all_after() {
s := 'fn hello'
q := s.all_after('fn ')
assert q == 'hello'
}

93
vlib/builtin/utf8.v Normal file
View File

@@ -0,0 +1,93 @@
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module builtin
pub fn utf8_char_len(b byte) int {
return (( 0xe5000000 >> (( b >> 3 ) & 0x1e )) & 3 ) + 1
}
// Convert utf32 to utf8
// utf32 == Codepoint
pub fn utf32_to_str(code u32) string {
icode := int(code) //Prevents doing casts everywhere
mut buffer := malloc(5)
if icode <= 127 /* 0x7F */ {
buffer[0] = icode
return tos(buffer, 1)
}
if (icode <= 2047 /* 0x7FF */) {
buffer[0] = 192 /*0xC0*/ | (icode >> 6) /* 110xxxxx */
buffer[1] = 128 /*0x80*/ | (icode & 63 /*0x3F*/) /* 10xxxxxx */
return tos(buffer, 2)
}
if (icode <= 65535 /* 0xFFFF */) {
buffer[0] = 224 /*0xE0*/ | (icode >> 12) /* 1110xxxx */
buffer[1] = 128 /*0x80*/ | ((icode >> 6) & 63 /*0x3F*/) /* 10xxxxxx */
buffer[2] = 128 /*0x80*/ | (icode & 63 /*0x3F*/) /* 10xxxxxx */
return tos(buffer, 3)
}
if (icode <= 1114111 /* 0x10FFFF */) {
buffer[0] = 240 /*0xF0*/ | (icode >> 18) /* 11110xxx */
buffer[1] = 128 /*0x80*/ | ((icode >> 12) & 63 /*0x3F*/) /* 10xxxxxx */
buffer[2] = 128 /*0x80*/ | ((icode >> 6) & 63 /*0x3F*/) /* 10xxxxxx */
buffer[3] = 128 /*0x80*/ | (icode & 63 /*0x3F*/) /* 10xxxxxx */
return tos(buffer, 4)
}
return ''
}
// TODO copypasta
pub fn utf32_to_str_no_malloc(code u32, buf voidptr) string {
icode := int(code) //Prevents doing casts everywhere
mut buffer := byteptr(buf)
if icode <= 127 /* 0x7F */ {
buffer[0] = icode
return tos(buffer, 1)
}
if (icode <= 2047 /* 0x7FF */) {
buffer[0] = 192 /*0xC0*/ | (icode >> 6) /* 110xxxxx */
buffer[1] = 128 /*0x80*/ | (icode & 63 /*0x3F*/) /* 10xxxxxx */
return tos(buffer, 2)
}
if (icode <= 65535 /* 0xFFFF */) {
buffer[0] = 224 /*0xE0*/ | (icode >> 12) /* 1110xxxx */
buffer[1] = 128 /*0x80*/ | ((icode >> 6) & 63 /*0x3F*/) /* 10xxxxxx */
buffer[2] = 128 /*0x80*/ | (icode & 63 /*0x3F*/) /* 10xxxxxx */
return tos(buffer, 3)
}
if (icode <= 1114111 /* 0x10FFFF */) {
buffer[0] = 240 /*0xF0*/ | (icode >> 18) /* 11110xxx */
buffer[1] = 128 /*0x80*/ | ((icode >> 12) & 63 /*0x3F*/) /* 10xxxxxx */
buffer[2] = 128 /*0x80*/ | ((icode >> 6) & 63 /*0x3F*/) /* 10xxxxxx */
buffer[3] = 128 /*0x80*/ | (icode & 63 /*0x3F*/) /* 10xxxxxx */
return tos(buffer, 4)
}
return ''
}
// Convert utf8 to utf32
pub fn (_rune string) utf32_code() int {
if _rune.len == 0 {
return 0
}
// save ASC symbol as is
if _rune.len == 1 {
return int(_rune[0])
}
mut b := byte(int(_rune[0]))
// TODO should be
// res := int( rune[0] << rune.len)
b = b << _rune.len
mut res := int(b)
mut shift := 6 - _rune.len
for i := 1; i < _rune.len; i++ {
c := int(_rune[i])
res = res << shift
res |= c & 63 /* 0x3f */
shift = 6
}
return res
}