mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
V 0.0.12 open-source release
This commit is contained in:
223
builtin/array.v
Normal file
223
builtin/array.v
Normal file
@@ -0,0 +1,223 @@
|
||||
module builtin
|
||||
|
||||
struct array {
|
||||
// Using a void pointer allows to implement arrays without generics and without generating
|
||||
// extra code for every type.
|
||||
data voidptr
|
||||
pub:
|
||||
len int
|
||||
cap int
|
||||
element_size int
|
||||
}
|
||||
|
||||
// Private function, used by V (`nums := []int`)
|
||||
fn new_array(mylen int, 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 int, 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
|
||||
}
|
||||
|
||||
fn (a mut array) append_array(b array) {
|
||||
for i := 0; i < b.len; i++ {
|
||||
val := b[i]
|
||||
a._push(val)
|
||||
}
|
||||
}
|
||||
|
||||
fn (a mut array) sort_with_compare(compare voidptr) {
|
||||
C.qsort(a.data, a.len, a.element_size, compare)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
fn (a mut array) prepend(val voidptr) {
|
||||
a.insert(0, val)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fn (a array) first() voidptr {
|
||||
if a.len == 0 {
|
||||
panic('array.first: empty array')
|
||||
}
|
||||
return a.data + 0
|
||||
}
|
||||
|
||||
fn (a array) last() voidptr {
|
||||
if a.len == 0 {
|
||||
panic('array.last: empty array')
|
||||
}
|
||||
return a.data + (a.len - 1) * a.element_size
|
||||
}
|
||||
|
||||
fn (s array) left(n int) array {
|
||||
if n >= s.len {
|
||||
return s
|
||||
}
|
||||
return s.slice(0, n)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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++
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fn (a[]int) free() {
|
||||
// println('array free')
|
||||
C.free(a.data)
|
||||
}
|
||||
|
||||
// TODO generic
|
||||
// "[ 'a', 'b', 'c' ]"
|
||||
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)
|
||||
}
|
||||
|
||||
127
builtin/builtin.v
Normal file
127
builtin/builtin.v
Normal file
@@ -0,0 +1,127 @@
|
||||
module builtin
|
||||
|
||||
pub fn exit(reason string) {
|
||||
if reason == '' {
|
||||
panic('exit empty reason')
|
||||
}
|
||||
println2('exit(): $reason')
|
||||
C.exit(0)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
println2('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 {
|
||||
println2(s)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(s string) {
|
||||
C.printf('%.*s', s.len, s.str)
|
||||
}
|
||||
|
||||
fn println2(s string) {
|
||||
C.printf('%.*s\n', s.len, s.str)
|
||||
}
|
||||
|
||||
pub fn malloc(n int) byteptr {
|
||||
if n < 0 {
|
||||
panic('malloc(<0)')
|
||||
}
|
||||
#ifdef VPLAY
|
||||
if n > 10000 {
|
||||
panic('allocating more than 10 KB is not allowed in the playground')
|
||||
}
|
||||
#endif
|
||||
#ifdef DEBUG_ALLOC
|
||||
total := i64(0)
|
||||
# total_m += n;
|
||||
# total = total_m;
|
||||
println2('\n\n\nmalloc($n) total=$total')
|
||||
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(sizeof(float) * n, sizeof(float))
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// TODO this is not used anymore
|
||||
fn range_int(start, end int) []int {
|
||||
len := end - start
|
||||
mut res := [0; len]
|
||||
for i := 0; i < len; i++ {
|
||||
res[i] = start + i
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
163
builtin/int.v
Normal file
163
builtin/int.v
Normal file
@@ -0,0 +1,163 @@
|
||||
module builtin
|
||||
|
||||
fn (d double) str() string {
|
||||
buf := malloc(sizeof(double) * 5 + 1)// TODO
|
||||
C.sprintf(buf, '%f', d)
|
||||
return tos(buf, _strlen(buf))
|
||||
}
|
||||
|
||||
fn (d float) str() string {
|
||||
buf := malloc(sizeof(double) * 5 + 1)// TODO
|
||||
C.sprintf(buf, '%f', d)
|
||||
return tos(buf, _strlen(buf))
|
||||
}
|
||||
|
||||
fn (d f64) str() string {
|
||||
buf := malloc(sizeof(double) * 5 + 1)// TODO
|
||||
C.sprintf(buf, '%f', d)
|
||||
return tos(buf, _strlen(buf))
|
||||
}
|
||||
|
||||
fn (d f32) str() string {
|
||||
buf := malloc(sizeof(double) * 5 + 1)// TODO
|
||||
C.sprintf(buf, '%f', d)
|
||||
return tos(buf, _strlen(buf))
|
||||
}
|
||||
|
||||
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
|
||||
// }
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
fn (b bool) str() string {
|
||||
if b {
|
||||
return 'true'
|
||||
}
|
||||
return 'false'
|
||||
}
|
||||
|
||||
fn (n int) hex() string {
|
||||
s := n.str()
|
||||
hex := malloc(s.len + 2)
|
||||
C.sprintf(hex, '0x%x', n)
|
||||
return tos(hex, s.len + 2)
|
||||
}
|
||||
|
||||
fn (n i64) hex() string {
|
||||
s := n.str()
|
||||
hex := malloc(s.len + 2)
|
||||
C.sprintf(hex, '0x%x', n)
|
||||
return tos(hex, s.len + 2)
|
||||
}
|
||||
|
||||
fn (a[]byte) contains(val byte) bool {
|
||||
for aa in a {
|
||||
if aa == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/* TODO
|
||||
fn (c rune) str() string {
|
||||
}
|
||||
*/
|
||||
fn (c byte) str() string {
|
||||
mut str := string {
|
||||
len: 1
|
||||
str: malloc(2)
|
||||
}
|
||||
str.str[0] = c
|
||||
str.str[1] = `\0`
|
||||
return str
|
||||
}
|
||||
|
||||
159
builtin/map.v
Normal file
159
builtin/map.v
Normal file
@@ -0,0 +1,159 @@
|
||||
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
|
||||
}
|
||||
|
||||
fn new_map(cap int, 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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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('>>>>>>>>>>')
|
||||
}
|
||||
|
||||
fn (m map) free() {
|
||||
// C.free(m.table)
|
||||
// C.free(m.keys_table)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
8
builtin/option.v
Normal file
8
builtin/option.v
Normal file
@@ -0,0 +1,8 @@
|
||||
module builtin
|
||||
|
||||
struct Option {
|
||||
data voidptr
|
||||
error string
|
||||
ok bool
|
||||
}
|
||||
|
||||
97
builtin/smap.v
Normal file
97
builtin/smap.v
Normal file
@@ -0,0 +1,97 @@
|
||||
module builtin
|
||||
|
||||
struct Entry2 {
|
||||
key string
|
||||
val string
|
||||
}
|
||||
|
||||
struct smap {
|
||||
entries []Entry2
|
||||
is_sorted bool
|
||||
}
|
||||
|
||||
fn new_smap() smap {
|
||||
res := smap{}
|
||||
return res
|
||||
}
|
||||
|
||||
fn (m mut smap) set(key string, val string) {
|
||||
/*
|
||||
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
|
||||
}
|
||||
}
|
||||
*/
|
||||
e := Entry2{key: key, val: val}
|
||||
m.entries << e
|
||||
}
|
||||
|
||||
fn (m smap) get(key string) string {
|
||||
if m.is_sorted {
|
||||
return m.bs(key, 0, m.entries.len)
|
||||
}
|
||||
for i := 0; i < m.entries.len; i++ {
|
||||
entry := m.entries[i]
|
||||
if entry.key == key {
|
||||
return entry.val
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
fn (m smap) bs(query string, start, end int) string {
|
||||
mid := start + ((end - start) / 2)
|
||||
if end - start == 0 {
|
||||
last := m.entries[end]
|
||||
return last.val
|
||||
}
|
||||
if end - start == 1 {
|
||||
first := m.entries[start]
|
||||
return first.val
|
||||
}
|
||||
if mid >= m.entries.len {
|
||||
return ''
|
||||
}
|
||||
mid_msg := m.entries[mid]
|
||||
if query < mid_msg.key {
|
||||
return m.bs(query, start, mid)
|
||||
}
|
||||
return m.bs(query, mid, end)
|
||||
}
|
||||
|
||||
fn compare_smap(a, b *Entry2) int {
|
||||
if a.key < b.key {
|
||||
return -1
|
||||
}
|
||||
if a.key > b.key {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
fn (m mut smap) sort() {
|
||||
m.entries.sort_with_compare(compare_smap)
|
||||
m.is_sorted = true
|
||||
}
|
||||
|
||||
fn (m smap) free() {
|
||||
// m.entries.free()
|
||||
}
|
||||
|
||||
fn (m smap) str() string {
|
||||
if m.entries.len == 0 {
|
||||
return '{}'
|
||||
}
|
||||
// TODO use bytes buffer
|
||||
mut s := '{\n'
|
||||
for entry in m.entries {
|
||||
s += ' "$entry.key" => "$entry.val"\n'
|
||||
}
|
||||
s += '}'
|
||||
return s
|
||||
}
|
||||
|
||||
814
builtin/string.v
Normal file
814
builtin/string.v
Normal file
@@ -0,0 +1,814 @@
|
||||
module builtin
|
||||
|
||||
// V strings are not null-terminated.
|
||||
struct string {
|
||||
str byteptr
|
||||
pub:
|
||||
len int
|
||||
}
|
||||
|
||||
struct ustring {
|
||||
pub:
|
||||
s string
|
||||
runes []int
|
||||
len int
|
||||
}
|
||||
|
||||
// For C strings only
|
||||
fn C.strlen(s byteptr) int
|
||||
|
||||
// Converts a C string to a V string
|
||||
fn tos(s byteptr, len int) string {
|
||||
// This should never happen.
|
||||
if isnil(s) {
|
||||
panic('tos(): nil string')
|
||||
}
|
||||
return string {
|
||||
str: s
|
||||
len: len
|
||||
}
|
||||
}
|
||||
|
||||
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 itself. TODO bad name.
|
||||
fn tos2(s byteptr) string {
|
||||
if isnil(s) {
|
||||
panic('tos2: nil string')
|
||||
return string{}
|
||||
}
|
||||
len := C.strlen(s)
|
||||
res := tos(s, len)
|
||||
return res
|
||||
}
|
||||
|
||||
fn tos_no_len(s byteptr) string {
|
||||
return tos2(s)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 ''
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
// TODO `.int()` ?
|
||||
pub fn (s string) to_i() int {
|
||||
return C.atoi(s.str)
|
||||
}
|
||||
|
||||
// TODO `.f32()`
|
||||
fn (s string) to_float() float {
|
||||
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) ge(a string) bool {
|
||||
mut j := 0
|
||||
for i := 0; i < s.len; i++ {
|
||||
if i >= a.len {
|
||||
return true
|
||||
}
|
||||
if int(s[i]) < int(a[j]) {
|
||||
return false
|
||||
}
|
||||
else if int(s[i]) > int(a[j]) {
|
||||
return true
|
||||
}
|
||||
j++
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// s <= a
|
||||
fn (s string) le(a string) bool {
|
||||
return !s.ge(a) || s == a
|
||||
}
|
||||
|
||||
// s < a
|
||||
fn (s string) lt(a string) bool {
|
||||
return s.le(a) && s != a
|
||||
}
|
||||
|
||||
// s > a
|
||||
fn (s string) gt(a string) bool {
|
||||
return s.ge(a) && s != a
|
||||
}
|
||||
|
||||
// TODO `fn (s string) + (a string)` ? To be consistent with operator overloading syntax.
|
||||
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])
|
||||
// println2('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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
pub fn (s string) index(p string) int {
|
||||
if p.len > s.len {
|
||||
return -1
|
||||
}
|
||||
mut i := 0
|
||||
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) 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'
|
||||
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
|
||||
fn (ar[]string) contains(val string) bool {
|
||||
for s in ar {
|
||||
if s == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO generic
|
||||
fn (ar[]int) contains(val int) bool {
|
||||
for i, s in ar {
|
||||
if s == val {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
fn (s string) trim_right(cutset string) string {
|
||||
return s
|
||||
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.le(b) {
|
||||
return -1
|
||||
}
|
||||
if a.ge(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 := a.to_lower()
|
||||
return compare_strings(aa, bb)
|
||||
}
|
||||
|
||||
pub fn (s mut []string) sort() {
|
||||
s.sort_with_compare(compare_strings)
|
||||
}
|
||||
|
||||
fn (s mut []string) sort_ignore_case() {
|
||||
s.sort_with_compare(compare_lower_strings)
|
||||
}
|
||||
|
||||
fn (s mut []string) sort_by_len() {
|
||||
s.sort_with_compare(compare_strings_by_len)
|
||||
}
|
||||
|
||||
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 := 0
|
||||
# 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.
|
||||
# array_int g_ustring_runes;
|
||||
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]);
|
||||
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]
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fn (c byte) is_digit() bool {
|
||||
return c >= `0` && c <= `9`
|
||||
}
|
||||
|
||||
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'
|
||||
fn (s string) all_before(dot string) string {
|
||||
pos := s.index(dot)
|
||||
if pos == -1 {
|
||||
return s
|
||||
}
|
||||
return s.left(pos)
|
||||
}
|
||||
|
||||
fn (s string) all_before_last(dot string) string {
|
||||
pos := s.last_index(dot)
|
||||
if pos == -1 {
|
||||
return s
|
||||
}
|
||||
return s.left(pos)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fn (s[]string) join_lines() string {
|
||||
return s.join('\n')
|
||||
}
|
||||
|
||||
// 'hello'.limit(2) => 'he'
|
||||
// 'hi'.limit(10) => 'hi'
|
||||
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()
|
||||
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()
|
||||
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
|
||||
}
|
||||
|
||||
32
builtin/string_builder.v
Normal file
32
builtin/string_builder.v
Normal file
@@ -0,0 +1,32 @@
|
||||
module builtin
|
||||
|
||||
struct StringBuilder {
|
||||
buf []byte
|
||||
len int
|
||||
}
|
||||
|
||||
fn new_string_builder(initial_size int) StringBuilder {
|
||||
return StringBuilder {
|
||||
buf: new_array(0, initial_size, sizeof(byte))
|
||||
}
|
||||
}
|
||||
|
||||
fn (b mut StringBuilder) write(s string) {
|
||||
b.buf._push_many(s.str, s.len)
|
||||
b.len += s.len
|
||||
}
|
||||
|
||||
fn (b mut StringBuilder) writeln(s string) {
|
||||
b.buf._push_many(s.str, s.len)
|
||||
b.buf << `\n`
|
||||
b.len += s.len + 1
|
||||
}
|
||||
|
||||
fn (b StringBuilder) str() string {
|
||||
return tos(b.buf.data, b.len)
|
||||
}
|
||||
|
||||
fn (b StringBuilder) cut(n int) {
|
||||
b.len -= n
|
||||
}
|
||||
|
||||
338
builtin/utf8.v
Normal file
338
builtin/utf8.v
Normal file
@@ -0,0 +1,338 @@
|
||||
module builtin
|
||||
|
||||
fn (s string) is_utf8() int {
|
||||
faulty_bytes := 0
|
||||
len := s.len
|
||||
i := 0
|
||||
// # size_t i = 0;
|
||||
# byte * str = s.str;
|
||||
#
|
||||
# while (i < len) {
|
||||
# if (str[i] <= 0x7F) /* 00..7F */ {
|
||||
# i += 1;
|
||||
# }
|
||||
#else if (str[i] >= 0xC2 && str[i] <= 0xDF) /* C2..DF 80..BF */ {
|
||||
# if (i + 1 < len) /* Expect a 2nd byte */ {
|
||||
# if (str[i + 1] < 0x80 || str[i + 1] > 0xBF) {
|
||||
# printf( "After a first byte between C2 and DF, expecting a 2nd byte between 80 and BF");
|
||||
# faulty_bytes = 2;
|
||||
# goto end;
|
||||
# }
|
||||
# }
|
||||
#else {
|
||||
# printf( "After a first byte between C2 and DF, expecting a 2nd byte.");
|
||||
# faulty_bytes = 1;
|
||||
# goto end;
|
||||
# }
|
||||
# i += 2;
|
||||
# }
|
||||
#else if (str[i] == 0xE0) /* E0 A0..BF 80..BF */ {
|
||||
# if (i + 2 < len) /* Expect a 2nd and 3rd byte */ {
|
||||
# if (str[i + 1] < 0xA0 || str[i + 1] > 0xBF) {
|
||||
# printf( "After a first byte of E0, expecting a 2nd byte between A0 and BF.");
|
||||
# faulty_bytes = 2;
|
||||
# goto end;
|
||||
# }
|
||||
# if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) {
|
||||
# printf( "After a first byte of E0, expecting a 3nd byte between 80 and BF.");
|
||||
# faulty_bytes = 3;
|
||||
# goto end;
|
||||
# }
|
||||
# }
|
||||
#else {
|
||||
# printf( "After a first byte of E0, expecting two following bytes.");
|
||||
# faulty_bytes = 1;
|
||||
# goto end;
|
||||
# }
|
||||
# i += 3;
|
||||
# }
|
||||
#else if (str[i] >= 0xE1 && str[i] <= 0xEC) /* E1..EC 80..BF 80..BF */ {
|
||||
# if (i + 2 < len) /* Expect a 2nd and 3rd byte */ {
|
||||
# if (str[i + 1] < 0x80 || str[i + 1] > 0xBF) {
|
||||
# printf( "After a first byte between E1 and EC, expecting the 2nd byte between 80 and BF.");
|
||||
# faulty_bytes = 2;
|
||||
# goto end;
|
||||
# }
|
||||
# if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) {
|
||||
# printf( "After a first byte between E1 and EC, expecting the 3rd byte between 80 and BF.");
|
||||
# faulty_bytes = 3;
|
||||
# goto end;
|
||||
# }
|
||||
# }
|
||||
#else {
|
||||
# printf( "After a first byte between E1 and EC, expecting two following bytes.");
|
||||
# faulty_bytes = 1;
|
||||
# goto end;
|
||||
# }
|
||||
# i += 3;
|
||||
# }
|
||||
#else if (str[i] == 0xED) /* ED 80..9F 80..BF */ {
|
||||
# if (i + 2 < len) /* Expect a 2nd and 3rd byte */ {
|
||||
# if (str[i + 1] < 0x80 || str[i + 1] > 0x9F) {
|
||||
# printf( "After a first byte of ED, expecting 2nd byte between 80 and 9F.");
|
||||
# faulty_bytes = 2;
|
||||
# goto end;
|
||||
# }
|
||||
# if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) {
|
||||
# printf( "After a first byte of ED, expecting 3rd byte between 80 and BF.");
|
||||
# faulty_bytes = 3;
|
||||
# goto end;
|
||||
# }
|
||||
# }
|
||||
#else {
|
||||
# printf( "After a first byte of ED, expecting two following bytes.");
|
||||
# faulty_bytes = 1;
|
||||
# goto end;
|
||||
# }
|
||||
# i += 3;
|
||||
# }
|
||||
#else if (str[i] >= 0xEE && str[i] <= 0xEF) /* EE..EF 80..BF 80..BF */ {
|
||||
# if (i + 2 < len) /* Expect a 2nd and 3rd byte */ {
|
||||
# if (str[i + 1] < 0x80 || str[i + 1] > 0xBF) {
|
||||
# printf( "After a first byte between EE and EF, expecting 2nd byte between 80 and BF.");
|
||||
# faulty_bytes = 2;
|
||||
# goto end;
|
||||
# }
|
||||
# if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) {
|
||||
# printf( "After a first byte between EE and EF, expecting 3rd byte between 80 and BF.");
|
||||
# faulty_bytes = 3;
|
||||
# goto end;
|
||||
# }
|
||||
# }
|
||||
#else {
|
||||
# printf( "After a first byte between EE and EF, two following bytes.");
|
||||
# faulty_bytes = 1;
|
||||
# goto end;
|
||||
# }
|
||||
# i += 3;
|
||||
# }
|
||||
#else if (str[i] == 0xF0) /* F0 90..BF 80..BF 80..BF */ {
|
||||
# if (i + 3 < len) /* Expect a 2nd, 3rd 3th byte */ {
|
||||
# if (str[i + 1] < 0x90 || str[i + 1] > 0xBF) {
|
||||
# printf( "After a first byte of F0, expecting 2nd byte between 90 and BF.");
|
||||
# faulty_bytes = 2;
|
||||
# goto end;
|
||||
# }
|
||||
# if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) {
|
||||
# printf( "After a first byte of F0, expecting 3rd byte between 80 and BF.");
|
||||
# faulty_bytes = 3;
|
||||
# goto end;
|
||||
# }
|
||||
# if (str[i + 3] < 0x80 || str[i + 3] > 0xBF) {
|
||||
# printf( "After a first byte of F0, expecting 4th byte between 80 and BF.");
|
||||
# faulty_bytes = 4;
|
||||
# goto end;
|
||||
# }
|
||||
# }
|
||||
#else {
|
||||
# printf( "After a first byte of F0, expecting three following bytes.");
|
||||
# faulty_bytes = 1;
|
||||
# goto end;
|
||||
# }
|
||||
# i += 4;
|
||||
# }
|
||||
#else if (str[i] >= 0xF1 && str[i] <= 0xF3) /* F1..F3 80..BF 80..BF 80..BF */ {
|
||||
# if (i + 3 < len) /* Expect a 2nd, 3rd 3th byte */ {
|
||||
# if (str[i + 1] < 0x80 || str[i + 1] > 0xBF) {
|
||||
# printf( "After a first byte of F1, F2, or F3, expecting a 2nd byte between 80 and BF.");
|
||||
# faulty_bytes = 2;
|
||||
# goto end;
|
||||
# }
|
||||
# if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) {
|
||||
# printf( "After a first byte of F1, F2, or F3, expecting a 3rd byte between 80 and BF.");
|
||||
# faulty_bytes = 3;
|
||||
# goto end;
|
||||
# }
|
||||
# if (str[i + 3] < 0x80 || str[i + 3] > 0xBF) {
|
||||
# printf( "After a first byte of F1, F2, or F3, expecting a 4th byte between 80 and BF.");
|
||||
# faulty_bytes = 4;
|
||||
# goto end;
|
||||
# }
|
||||
# }
|
||||
#else {
|
||||
# printf( "After a first byte of F1, F2, or F3, expecting three following bytes.");
|
||||
# faulty_bytes = 1;
|
||||
# goto end;
|
||||
# }
|
||||
# i += 4;
|
||||
# }
|
||||
#else if (str[i] == 0xF4) /* F4 80..8F 80..BF 80..BF */ {
|
||||
# if (i + 3 < len) /* Expect a 2nd, 3rd 3th byte */ {
|
||||
# if (str[i + 1] < 0x80 || str[i + 1] > 0x8F) {
|
||||
# printf( "After a first byte of F4, expecting 2nd byte between 80 and 8F.");
|
||||
# faulty_bytes = 2;
|
||||
# goto end;
|
||||
# }
|
||||
# if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) {
|
||||
# printf( "After a first byte of F4, expecting 3rd byte between 80 and BF.");
|
||||
# faulty_bytes = 3;
|
||||
# goto end;
|
||||
# }
|
||||
# if (str[i + 3] < 0x80 || str[i + 3] > 0xBF) {
|
||||
# printf( "After a first byte of F4, expecting 4th byte between 80 and BF.");
|
||||
# faulty_bytes = 4;
|
||||
# goto end;
|
||||
# }
|
||||
# }
|
||||
#else {
|
||||
# printf( "After a first byte of F4, expecting three following bytes.");
|
||||
# faulty_bytes = 1;
|
||||
# goto end;
|
||||
# }
|
||||
# i += 4;
|
||||
# }
|
||||
#else {
|
||||
# printf( "i=%d Expecting bytes in the following ranges: 00..7F C2..F4.",
|
||||
# i);
|
||||
# faulty_bytes = 1;
|
||||
# goto end;
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# end: ;
|
||||
// println('faulty bytes=$faulty_bytes i=$i')
|
||||
// # printf("c='%c'\n", str[i]);
|
||||
ok := faulty_bytes == 0
|
||||
if ok {
|
||||
return -1
|
||||
}
|
||||
if !ok {
|
||||
println2('utf is bad dalen=$len KEK $s sdf')
|
||||
// s = s.left(i)
|
||||
}
|
||||
return i
|
||||
// return ok
|
||||
}
|
||||
|
||||
/*
|
||||
fn (s string) runes() []string {
|
||||
res2 := []string{}
|
||||
// res := new_empty_array_with_cap_string(s.len)
|
||||
res := []string{}
|
||||
if !s.is_utf8() {
|
||||
mys := s
|
||||
println2('string.me runes bad utf $mys HAHA')
|
||||
return res
|
||||
}
|
||||
for i := 0; i < s.len; i++ {
|
||||
char_len := 0
|
||||
# char_len =UTF8_CHAR_LEN(s.str[i]);
|
||||
switch char_len {
|
||||
case 1:
|
||||
// println('ONE')
|
||||
res <<(char2string(s[i]))
|
||||
case 2:
|
||||
// println('TWO')
|
||||
rune2 := s.substr(i, i + 2)
|
||||
res <<(rune2)
|
||||
i++
|
||||
case 3:
|
||||
// println('TWO')
|
||||
rune3 := s.substr(i, i + 3)
|
||||
res <<(rune3)
|
||||
i++
|
||||
i++
|
||||
case 4:
|
||||
// println('TWO')
|
||||
rune4 := s.substr(i, i + 4)
|
||||
res <<(rune4)
|
||||
i++
|
||||
i++
|
||||
i++
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
*/
|
||||
// Convert utf32 to utf8
|
||||
// utf32 == Codepoint
|
||||
fn utf32_to_str(code u32) string {
|
||||
// println('code = $code')
|
||||
buffer := malloc(5)
|
||||
# if (code <= 0x7F) {
|
||||
// println('!!!!!!!1')
|
||||
# buffer[0] = code;
|
||||
# return tos(buffer, 1);
|
||||
# }
|
||||
# if (code <= 0x7FF) {
|
||||
// println('!!!!!!!2')
|
||||
# buffer[0] = 0xC0 | (code >> 6); /* 110xxxxx */
|
||||
# buffer[1] = 0x80 | (code & 0x3F); /* 10xxxxxx */
|
||||
# return tos(buffer, 2);
|
||||
# }
|
||||
# if (code <= 0xFFFF) {
|
||||
// println('!!!!!!!3')
|
||||
# buffer[0] = 0xE0 | (code >> 12); /* 1110xxxx */
|
||||
# buffer[1] = 0x80 | ((code >> 6) & 0x3F); /* 10xxxxxx */
|
||||
# buffer[2] = 0x80 | (code & 0x3F); /* 10xxxxxx */
|
||||
# return tos(buffer, 3);
|
||||
# }
|
||||
# if (code <= 0x10FFFF) {
|
||||
# buffer[0] = 0xF0 | (code >> 18); /* 11110xxx */
|
||||
# buffer[1] = 0x80 | ((code >> 12) & 0x3F); /* 10xxxxxx */
|
||||
# buffer[2] = 0x80 | ((code >> 6) & 0x3F); /* 10xxxxxx */
|
||||
# buffer[3] = 0x80 | (code & 0x3F); /* 10xxxxxx */
|
||||
# return tos(buffer, 4);
|
||||
# }
|
||||
return ''
|
||||
}
|
||||
|
||||
// TODO copypasta
|
||||
fn utf32_to_str_no_malloc(code u32, buf voidptr) string {
|
||||
// println('code = $code')
|
||||
# char* buffer = buf;
|
||||
# if (code <= 0x7F) {
|
||||
// println('!!!!!!!1')
|
||||
# buffer[0] = code;
|
||||
# return tos(buffer, 1);
|
||||
# }
|
||||
# if (code <= 0x7FF) {
|
||||
// println('!!!!!!!2')
|
||||
# buffer[0] = 0xC0 | (code >> 6); /* 110xxxxx */
|
||||
# buffer[1] = 0x80 | (code & 0x3F); /* 10xxxxxx */
|
||||
# return tos(buffer, 2);
|
||||
# }
|
||||
# if (code <= 0xFFFF) {
|
||||
// println('!!!!!!!3')
|
||||
# buffer[0] = 0xE0 | (code >> 12); /* 1110xxxx */
|
||||
# buffer[1] = 0x80 | ((code >> 6) & 0x3F); /* 10xxxxxx */
|
||||
# buffer[2] = 0x80 | (code & 0x3F); /* 10xxxxxx */
|
||||
# return tos(buffer, 3);
|
||||
# }
|
||||
# if (code <= 0x10FFFF) {
|
||||
# buffer[0] = 0xF0 | (code >> 18); /* 11110xxx */
|
||||
# buffer[1] = 0x80 | ((code >> 12) & 0x3F); /* 10xxxxxx */
|
||||
# buffer[2] = 0x80 | ((code >> 6) & 0x3F); /* 10xxxxxx */
|
||||
# buffer[3] = 0x80 | (code & 0x3F); /* 10xxxxxx */
|
||||
# return tos(buffer, 4);
|
||||
# }
|
||||
return ''
|
||||
}
|
||||
|
||||
// Convert utf8 to utf32
|
||||
fn (_rune string) utf32_code() int {
|
||||
// println('utf 32 of $rune len=$rune.len')
|
||||
if _rune.len == 0 {
|
||||
return 0
|
||||
}
|
||||
// save ASC symbol as is
|
||||
if _rune.len == 1 {
|
||||
return int(_rune[0])
|
||||
}
|
||||
b := byte(int(_rune[0]))
|
||||
// TODO should be
|
||||
// res := int( rune[0] << rune.len)
|
||||
# b <<= _rune.len;
|
||||
res := int(b)
|
||||
mut shift := 6 - _rune.len
|
||||
for i := 1; i < _rune.len; i++ {
|
||||
// println('c=$res')
|
||||
c := int(_rune[i])
|
||||
# res <<= shift;
|
||||
# res |= c & 0x3f;
|
||||
shift = 6
|
||||
}
|
||||
// println('!!!!!!!! utf32 $rune res = $res')
|
||||
return res
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user