V 0.0.12 open-source release
commit
d32e538073
|
@ -0,0 +1,49 @@
|
||||||
|
// 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 base64
|
||||||
|
|
||||||
|
const (
|
||||||
|
Index = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
62, 63, 62, 62, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||||
|
17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 63, 0, 26, 27, 28, 29,
|
||||||
|
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
|
||||||
|
47, 48, 49, 50, 51]
|
||||||
|
)
|
||||||
|
|
||||||
|
fn decode(data string) string {
|
||||||
|
p := data.cstr()
|
||||||
|
len := data.len
|
||||||
|
mut pad := 0
|
||||||
|
if len > 0 && (len % 4 != 0 || p[len - 1] == `=`) {
|
||||||
|
pad = 1
|
||||||
|
}
|
||||||
|
L := ((len + 3) / 4 - pad) * 4
|
||||||
|
str_len := L / 4 * 3 + pad
|
||||||
|
mut str := malloc(str_len + 2)
|
||||||
|
mut j := 0
|
||||||
|
for i := 0; i < L; i += 4 {
|
||||||
|
n := (Index[p[i]] << 18) | (Index[p[i + 1]] << 12) |
|
||||||
|
(Index[p[i + 2]] << 6) | (Index[p[i + 3]])
|
||||||
|
str[j] = n >> 16
|
||||||
|
j++
|
||||||
|
str[j] = n >> 8 & 0xff
|
||||||
|
j++
|
||||||
|
str[j] = n & 0xff
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
if pad > 0 {
|
||||||
|
mut nn := (Index[p[L]] << 18) | (Index[p[L + 1]] << 12)
|
||||||
|
str[str_len - 1] = nn >> 16
|
||||||
|
if len > L + 2 && p[L + 2] != `=` {
|
||||||
|
nn = nn | (Index[p[L + 2]] << 6)
|
||||||
|
str[str_len] = nn >> 8 & 0xff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
str[str_len + 1] = `\0`
|
||||||
|
return string(str)
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
module builtin
|
||||||
|
|
||||||
|
struct Option {
|
||||||
|
data voidptr
|
||||||
|
error string
|
||||||
|
ok bool
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,290 @@
|
||||||
|
module main
|
||||||
|
|
||||||
|
struct CGen {
|
||||||
|
out os.File
|
||||||
|
out_path string
|
||||||
|
typedefs []string
|
||||||
|
type_aliases []string
|
||||||
|
includes []string
|
||||||
|
types []string
|
||||||
|
thread_args []string
|
||||||
|
thread_fns []string
|
||||||
|
consts []string
|
||||||
|
fns []string
|
||||||
|
so_fns []string
|
||||||
|
consts_init []string
|
||||||
|
// tmp_lines []string
|
||||||
|
// tmp_lines_pos int
|
||||||
|
lines []string
|
||||||
|
is_user bool
|
||||||
|
mut:
|
||||||
|
run Pass
|
||||||
|
nogen bool
|
||||||
|
tmp_line string
|
||||||
|
cur_line string
|
||||||
|
prev_line string
|
||||||
|
is_tmp bool
|
||||||
|
fn_main string
|
||||||
|
stash string
|
||||||
|
// st_start_pos int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_cgen(out_name_c string) *CGen {
|
||||||
|
// println('NEW CGENN($out_name_c)')
|
||||||
|
// println('$LANG_TMP/$out_name_c')
|
||||||
|
gen := &CGen {
|
||||||
|
out_path: '$TmpPath/$out_name_c'
|
||||||
|
out: os.create_file('$TmpPath/$out_name_c')
|
||||||
|
}
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
// gen.tmp_lines.push('')
|
||||||
|
}
|
||||||
|
return gen
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g mut CGen) genln(s string) {
|
||||||
|
if g.nogen || g.run == RUN_DECLS {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if g.is_tmp {
|
||||||
|
// if g.tmp_lines_pos > 0 {
|
||||||
|
g.tmp_line = '$g.tmp_line $s\n'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.cur_line = '$g.cur_line $s'
|
||||||
|
if g.cur_line != '' {
|
||||||
|
g.lines << g.cur_line
|
||||||
|
g.prev_line = g.cur_line
|
||||||
|
g.cur_line = ''
|
||||||
|
}
|
||||||
|
// g.lines << s
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g mut CGen) gen(s string) {
|
||||||
|
// if g.nogen || g.run == RunType.RUN_DECLS {
|
||||||
|
if g.nogen || g.run == RUN_DECLS {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if g.is_tmp {
|
||||||
|
// if g.tmp_lines_pos > 0 {
|
||||||
|
g.tmp_line = '$g.tmp_line $s'
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
g.cur_line = '$g.cur_line $s'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g mut CGen) save() {
|
||||||
|
s := g.lines.join('\n')
|
||||||
|
g.out.appendln(s)
|
||||||
|
g.out.close()
|
||||||
|
// os.system('clang-format -i $g.out_path')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g mut CGen) start_tmp() {
|
||||||
|
if g.is_tmp {
|
||||||
|
println(g.tmp_line)
|
||||||
|
os.exit('start_tmp() already started. cur_line="$g.cur_line"')
|
||||||
|
}
|
||||||
|
// kg.tmp_lines_pos++
|
||||||
|
g.tmp_line = ''
|
||||||
|
// g.tmp_lines[g.tmp_lines_pos] = ''
|
||||||
|
// g.tmp_lines.set(g.tmp_lines_pos, '')
|
||||||
|
g.is_tmp = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g mut CGen) end_tmp() string {
|
||||||
|
g.is_tmp = false
|
||||||
|
res := g.tmp_line
|
||||||
|
g.tmp_line = ''
|
||||||
|
// g.tmp_lines_pos--
|
||||||
|
// g.tmp_line = g.tmp_lines[g.tmp_lines_pos]
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g mut CGen) add_placeholder() int {
|
||||||
|
// g.genln('/*placeholder*/')
|
||||||
|
// g.genln('')
|
||||||
|
// return g.lines.len - 1
|
||||||
|
if g.is_tmp {
|
||||||
|
return g.tmp_line.len
|
||||||
|
}
|
||||||
|
return g.cur_line.len
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g mut CGen) set_placeholder(pos int, val string) {
|
||||||
|
if g.nogen {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// g.lines.set(pos, val)
|
||||||
|
if g.is_tmp {
|
||||||
|
left := g.tmp_line.left(pos)
|
||||||
|
right := g.tmp_line.right(pos)
|
||||||
|
g.tmp_line = '${left}${val}${right}'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
left := g.cur_line.left(pos)
|
||||||
|
right := g.cur_line.right(pos)
|
||||||
|
g.cur_line = '${left}${val}${right}'
|
||||||
|
// g.genln('')
|
||||||
|
}
|
||||||
|
|
||||||
|
// /////////////////////
|
||||||
|
fn (g mut CGen) add_placeholder2() int {
|
||||||
|
if g.is_tmp {
|
||||||
|
exit('tmp in addp2')
|
||||||
|
}
|
||||||
|
g.lines << ''
|
||||||
|
return g.lines.len - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g mut CGen) set_placeholder2(pos int, val string) {
|
||||||
|
if g.nogen {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if g.is_tmp {
|
||||||
|
exit('tmp in setp2')
|
||||||
|
}
|
||||||
|
g.lines[pos] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
// /////////////////
|
||||||
|
// fn (g mut CGen) cut_lines_after(pos int) string {
|
||||||
|
// end := g.lines.len
|
||||||
|
// lines := g.lines.slice_fast(pos, end)
|
||||||
|
// body := lines.join('\n')
|
||||||
|
// g.lines = g.lines.slice_fast(0, pos)
|
||||||
|
// return body
|
||||||
|
// }
|
||||||
|
// fn (g mut CGen) set_prev_line(val string) {
|
||||||
|
// g.lines.set(g.lines.len - 3, val)
|
||||||
|
// }
|
||||||
|
// ////fn (g mut CGen) go_back() {
|
||||||
|
// ////g.stash = g.prev_line + g.cur_line
|
||||||
|
// g.lines.set(g.lin
|
||||||
|
// ////}
|
||||||
|
// fn (g mut CGen) end_statement() {
|
||||||
|
// last_lines := g.lines.slice_fast(g.st_start_pos, g.lines.len - 1)
|
||||||
|
// mut merged := last_lines.join(' ')
|
||||||
|
// merged += '/* M $last_lines.len */'
|
||||||
|
// merged = merged.replace('\n', '')
|
||||||
|
// // zero last N lines instead of deleting them
|
||||||
|
// for i := g.st_start_pos; i < g.lines.len; i++ {
|
||||||
|
// g.lines.set(i, '')
|
||||||
|
// }
|
||||||
|
// g.lines.set(g.lines.len - 1, merged)
|
||||||
|
// // g.genln('')
|
||||||
|
// g.st_start_pos = g.lines.len - 1
|
||||||
|
// // os.exitkmerged)
|
||||||
|
// }
|
||||||
|
// fn (g mut CGen) prepend_type(typ string) {
|
||||||
|
// g.cur_line = typ.add(g.cur_line)
|
||||||
|
// g.cur_line='!!!'
|
||||||
|
// }
|
||||||
|
fn (g mut CGen) insert_before(val string) {
|
||||||
|
// g.cur_line = val.add(g.cur_line)
|
||||||
|
// return
|
||||||
|
// val += '/*inserted*/'
|
||||||
|
g.lines.insert(g.lines.len - 1, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn (g mut CGen) swap_last_lines() {
|
||||||
|
// return
|
||||||
|
// if g.run == RUN_DECLS {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// i := g.lines.len - 1
|
||||||
|
// j := i - 1
|
||||||
|
// tmp := g.lines[i]
|
||||||
|
// // println('lines i = $tmp')
|
||||||
|
// // println('lines j = ${g.lines[j]}')
|
||||||
|
// // // os.exit('')
|
||||||
|
// g.lines.set(i, g.lines[j])
|
||||||
|
// g.lines.set(j, tmp)
|
||||||
|
// }
|
||||||
|
fn (g mut CGen) register_thread_fn(wrapper_name, wrapper_text, struct_text string) {
|
||||||
|
for arg in g.thread_args {
|
||||||
|
if arg.contains(wrapper_name) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.thread_args << struct_text
|
||||||
|
g.thread_args << wrapper_text
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fn (g mut CGen) delete_all_after(pos int) {
|
||||||
|
if pos > g.cur_line.len - 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.cur_line = g.cur_line.substr(0, pos)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
fn (c mut V) prof_counters() string {
|
||||||
|
mut res := []string
|
||||||
|
// Global fns
|
||||||
|
for f in c.table.fns {
|
||||||
|
res << 'double ${c.table.cgen_name(f)}_time;'
|
||||||
|
// println(f.name)
|
||||||
|
}
|
||||||
|
// Methods
|
||||||
|
for typ in c.table.types {
|
||||||
|
// println('')
|
||||||
|
for f in typ.methods {
|
||||||
|
// res << f.cgen_name()
|
||||||
|
res << 'double ${c.table.cgen_name(f)}_time;'
|
||||||
|
// println(f.cgen_name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res.join(';\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) print_prof_counters() string {
|
||||||
|
mut res := []string
|
||||||
|
// Global fns
|
||||||
|
for f in p.table.fns {
|
||||||
|
counter := '${p.table.cgen_name(f)}_time'
|
||||||
|
res << 'if ($counter) printf("%%f : $f.name \\n", $counter);'
|
||||||
|
// println(f.name)
|
||||||
|
}
|
||||||
|
// Methods
|
||||||
|
for typ in p.table.types {
|
||||||
|
// println('')
|
||||||
|
for f in typ.methods {
|
||||||
|
counter := '${p.table.cgen_name(f)}_time'
|
||||||
|
res << 'if ($counter) printf("%%f : ${p.table.cgen_name(f)} \\n", $counter);'
|
||||||
|
// res << 'if ($counter) printf("$f.name : %%f\\n", $counter);'
|
||||||
|
// res << f.cgen_name()
|
||||||
|
// res << 'double ${f.cgen_name()}_time;'
|
||||||
|
// println(f.cgen_name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res.join(';\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) gen_type(s string) {
|
||||||
|
if !p.first_run() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.cgen.types << s
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) gen_typedef(s string) {
|
||||||
|
if !p.first_run() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.cgen.typedefs << s
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) gen_type_alias(s string) {
|
||||||
|
if !p.first_run() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.cgen.type_aliases << s
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g mut CGen) add_to_main(s string) {
|
||||||
|
println('add to main')
|
||||||
|
g.fn_main = g.fn_main + s
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,848 @@
|
||||||
|
module main
|
||||||
|
|
||||||
|
const (
|
||||||
|
MaxLocalVars = 50
|
||||||
|
)
|
||||||
|
|
||||||
|
struct Fn {
|
||||||
|
// addr int
|
||||||
|
mut:
|
||||||
|
pkg string
|
||||||
|
local_vars []Var
|
||||||
|
var_idx int
|
||||||
|
args []Var
|
||||||
|
is_interface bool
|
||||||
|
// called_fns []string
|
||||||
|
// idx int
|
||||||
|
scope_level int
|
||||||
|
typ string // return type
|
||||||
|
name string
|
||||||
|
is_c bool
|
||||||
|
receiver_typ string
|
||||||
|
is_private bool
|
||||||
|
is_method bool
|
||||||
|
returns_error bool
|
||||||
|
is_decl bool // type myfn fn(int, int)
|
||||||
|
defer string
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f &Fn) find_var(name string) Var {
|
||||||
|
for i in 0 .. f.var_idx {
|
||||||
|
if f.local_vars[i].name == name {
|
||||||
|
return f.local_vars[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Var{}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f mut Fn) open_scope() {
|
||||||
|
f.scope_level++
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f mut Fn) close_scope() {
|
||||||
|
// println('close_scope level=$f.scope_level var_idx=$f.var_idx')
|
||||||
|
// Move back `var_idx` (pointer to the end of the array) till we reach the previous scope level.
|
||||||
|
// This effectivly deletes (closes) current scope.
|
||||||
|
mut i := f.var_idx - 1
|
||||||
|
for; i >= 0; i-- {
|
||||||
|
v := f.local_vars[i]
|
||||||
|
if v.scope_level != f.scope_level {
|
||||||
|
// println('breaking. "$v.name" v.scope_level=$v.scope_level')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.var_idx = i + 1
|
||||||
|
// println('close_scope new var_idx=$f.var_idx\n')
|
||||||
|
f.scope_level--
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f &Fn) mark_var_used(v Var) {
|
||||||
|
for i, vv in f.local_vars {
|
||||||
|
if vv.name == v.name {
|
||||||
|
mut ptr := &f.local_vars[i]
|
||||||
|
ptr.is_used = true
|
||||||
|
// / f.local_vars[i].is_used = true
|
||||||
|
// return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f &Fn) known_var(name string) bool {
|
||||||
|
v := f.find_var(name)
|
||||||
|
return v.name.len > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f mut Fn) register_var(v Var) {
|
||||||
|
new_var := {v | scope_level: f.scope_level}
|
||||||
|
// Expand the array
|
||||||
|
if f.var_idx >= f.local_vars.len {
|
||||||
|
f.local_vars << new_var
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
f.local_vars[f.var_idx] = new_var
|
||||||
|
f.var_idx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// vlib header file?
|
||||||
|
fn (p mut Parser) is_sig() bool {
|
||||||
|
return (p.build_mode == DEFAULT_MODE || p.build_mode == BUILD) &&
|
||||||
|
(p.file_path.contains(TmpPath))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_fn(pkg string) *Fn {
|
||||||
|
mut f := &Fn {
|
||||||
|
pkg: pkg
|
||||||
|
local_vars: [Var{}
|
||||||
|
; MaxLocalVars]
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function signatures are added to the top of the .c file in the first run.
|
||||||
|
fn (p mut Parser) fn_decl() {
|
||||||
|
p.fgen('fn ')
|
||||||
|
is_pub := p.tok == PUB
|
||||||
|
if is_pub {
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
p.returns = false
|
||||||
|
p.next()
|
||||||
|
mut f := new_fn(p.pkg)
|
||||||
|
// Method receiver
|
||||||
|
mut receiver_typ := ''
|
||||||
|
if p.tok == LPAR {
|
||||||
|
f.is_method = true
|
||||||
|
p.check(LPAR)
|
||||||
|
receiver_name := p.check_name()
|
||||||
|
is_mut := p.tok == MUT
|
||||||
|
is_amp := p.tok == AMP
|
||||||
|
if is_mut || is_amp {
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
receiver_typ = p.get_type()
|
||||||
|
T := p.table.find_type(receiver_typ)
|
||||||
|
if T.is_interface {
|
||||||
|
p.error('invalid receiver type `$receiver_typ` (`$receiver_typ` is an interface)')
|
||||||
|
}
|
||||||
|
// Don't allow modifying types from a different module
|
||||||
|
if !p.first_run() && !p.builtin_pkg && T.pkg != p.pkg {
|
||||||
|
println('T.pkg=$T.pkg')
|
||||||
|
println('pkg=$p.pkg')
|
||||||
|
p.error('cannot define new methods on non-local type `$receiver_typ`')
|
||||||
|
}
|
||||||
|
// (a *Foo) instead of (a mut Foo) is a common mistake
|
||||||
|
if !p.builtin_pkg && receiver_typ.contains('*') {
|
||||||
|
t := receiver_typ.replace('*', '')
|
||||||
|
p.error('use `($receiver_name mut $t)` instead of `($receiver_name *$t)`')
|
||||||
|
}
|
||||||
|
f.receiver_typ = receiver_typ
|
||||||
|
if is_mut || is_amp {
|
||||||
|
receiver_typ += '*'
|
||||||
|
}
|
||||||
|
p.check(RPAR)
|
||||||
|
receiver := Var {
|
||||||
|
name: receiver_name
|
||||||
|
is_arg: true
|
||||||
|
typ: receiver_typ
|
||||||
|
is_mut: is_mut
|
||||||
|
ref: is_amp
|
||||||
|
ptr: is_mut
|
||||||
|
line_nr: p.scanner.line_nr
|
||||||
|
}
|
||||||
|
f.args << receiver
|
||||||
|
f.register_var(receiver)
|
||||||
|
}
|
||||||
|
if p.tok == PLUS || p.tok == MINUS || p.tok == MUL {
|
||||||
|
f.name = p.tok.str()
|
||||||
|
println('!!! $f.name')
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
f.name = p.check_name()
|
||||||
|
}
|
||||||
|
// C function header def? (fn C.NSMakeRect(int,int,int,int))
|
||||||
|
is_c := f.name == 'C' && p.tok == DOT
|
||||||
|
// Just fn signature? only builtin.v + default build mode
|
||||||
|
// is_sig := p.builtin_pkg && p.build_mode == DEFAULT_MODE
|
||||||
|
// is_sig := p.build_mode == DEFAULT_MODE && (p.builtin_pkg || p.file.contains(LANG_TMP))
|
||||||
|
is_sig := p.is_sig()
|
||||||
|
// println('\n\nfn decl !!is_sig=$is_sig name=$f.name $p.builtin_pkg')
|
||||||
|
if is_c {
|
||||||
|
p.check(DOT)
|
||||||
|
f.name = p.check_name()
|
||||||
|
f.is_c = true
|
||||||
|
}
|
||||||
|
else if !p.translated && !p.file_path.contains('view.v') {
|
||||||
|
if contains_capital(f.name) {
|
||||||
|
p.error('function names cannot contain uppercase letters, use snake_case instead')
|
||||||
|
}
|
||||||
|
if f.name.contains('__') {
|
||||||
|
p.error('function names cannot contain double underscores ("__"), use single underscores instead')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// simple_name := f.name
|
||||||
|
// println('!SIMPLE=$simple_name')
|
||||||
|
// user.register() => User_register()
|
||||||
|
has_receiver := receiver_typ.len > 0
|
||||||
|
if receiver_typ != '' {
|
||||||
|
// f.name = '${receiver_typ}_${f.name}'
|
||||||
|
}
|
||||||
|
// full pkg function name
|
||||||
|
// os.exit ==> os__exit()
|
||||||
|
if !is_c && !p.builtin_pkg && p.pkg != 'main' && receiver_typ.len == 0 {
|
||||||
|
f.name = p.prepend_pkg(f.name)
|
||||||
|
}
|
||||||
|
if p.first_run() && p.table.known_fn(f.name) && receiver_typ.len == 0 {
|
||||||
|
existing_fn := p.table.find_fn(f.name)
|
||||||
|
// This existing function could be defined as C decl before (no body), then we don't need to throw an erro
|
||||||
|
if !existing_fn.is_decl {
|
||||||
|
p.error('redefinition of `$f.name`')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Generic?
|
||||||
|
mut is_generic := false
|
||||||
|
if p.tok == LT {
|
||||||
|
p.next()
|
||||||
|
gen_type := p.check_name()
|
||||||
|
if gen_type != 'T' {
|
||||||
|
p.error('only `T` is allowed as a generic type for now')
|
||||||
|
}
|
||||||
|
p.check(GT)
|
||||||
|
is_generic = true
|
||||||
|
}
|
||||||
|
// Args (...)
|
||||||
|
p.fn_args(mut f)
|
||||||
|
// Returns an error?
|
||||||
|
if p.tok == NOT {
|
||||||
|
p.next()
|
||||||
|
f.returns_error = true
|
||||||
|
}
|
||||||
|
// Returns a type?
|
||||||
|
mut typ := 'void'
|
||||||
|
if p.tok == NAME || p.tok == MUL || p.tok == AMP || p.tok == LSBR ||
|
||||||
|
p.tok == QUESTION {
|
||||||
|
p.fgen(' ')
|
||||||
|
// TODO In
|
||||||
|
// if p.tok in [ NAME, MUL, AMP, LSBR ] {
|
||||||
|
typ = p.get_type()
|
||||||
|
}
|
||||||
|
// Translated C code can have empty functions (just definitions)
|
||||||
|
is_fn_header := !is_c && !is_sig && (p.translated || p.is_test) &&
|
||||||
|
(p.tok != LCBR)// || (p.tok == NAME && p.peek() != LCBR))
|
||||||
|
if is_fn_header {
|
||||||
|
f.is_decl = true
|
||||||
|
// println('GOT fn header $f.name')
|
||||||
|
}
|
||||||
|
// { required only in normal function declarations
|
||||||
|
if !is_c && !is_sig && !is_fn_header {
|
||||||
|
p.fgen(' ')
|
||||||
|
p.check(LCBR)
|
||||||
|
}
|
||||||
|
// Register option ? type
|
||||||
|
if typ.starts_with('Option_') {
|
||||||
|
p.cgen.typedefs << 'typedef Option $typ;'
|
||||||
|
}
|
||||||
|
// Register function
|
||||||
|
f.typ = typ
|
||||||
|
mut str_args := f.str_args(p.table)
|
||||||
|
// println('FN DECL $f.name typ=$f.typ str_args="$str_args"')
|
||||||
|
// Special case for main() args
|
||||||
|
if f.name == 'main' && !has_receiver {
|
||||||
|
if str_args != '' {
|
||||||
|
p.error('fn main must have no arguments and no return values')
|
||||||
|
}
|
||||||
|
typ = 'int'
|
||||||
|
str_args = 'int argc, char** argv'
|
||||||
|
}
|
||||||
|
// Only in C code generate User_register() instead of register()
|
||||||
|
// Internally it's still stored as "register" in type User
|
||||||
|
// mut fn_name_cgen := f.name
|
||||||
|
// if receiver_typ != '' {
|
||||||
|
// fn_name_cgen = '${receiver_typ}_$f.name'
|
||||||
|
// fn_name_cgen = fn_name_cgen.replace(' ', '')
|
||||||
|
// fn_name_cgen = fn_name_cgen.replace('*', '')
|
||||||
|
// }
|
||||||
|
mut fn_name_cgen := p.table.cgen_name(f)
|
||||||
|
// Start generation of the function body
|
||||||
|
is_live := p.is_live && f.name != 'main' && f.name != 'reload_so'
|
||||||
|
skip_main_in_test := f.name == 'main' && p.is_test
|
||||||
|
if !is_c && !is_live && !is_sig && !is_fn_header && !skip_main_in_test {
|
||||||
|
if p.obfuscate {
|
||||||
|
p.genln('; // ${f.name}')
|
||||||
|
}
|
||||||
|
p.genln('$typ $fn_name_cgen($str_args) {')
|
||||||
|
// if f.name == 'WinMain' {
|
||||||
|
// typ = 'int'
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
if is_fn_header {
|
||||||
|
p.genln('$typ $fn_name_cgen($str_args);')
|
||||||
|
p.fgenln('')
|
||||||
|
}
|
||||||
|
if is_c {
|
||||||
|
p.fgenln('\n')
|
||||||
|
}
|
||||||
|
p.cur_fn = f
|
||||||
|
// Register the method
|
||||||
|
if receiver_typ != '' {
|
||||||
|
mut receiver_T := p.table.find_type(receiver_typ)
|
||||||
|
// No such type yet? It could be defined later. Create a new type.
|
||||||
|
// struct declaration later will modify it instead of creating a new one.
|
||||||
|
if p.first_run() && receiver_T.name == '' {
|
||||||
|
// println('fn decl !!!!!!! REG PH $receiver_typ')
|
||||||
|
ttyp := Type {
|
||||||
|
name: receiver_typ.replace('*', '')
|
||||||
|
pkg: p.pkg
|
||||||
|
is_placeholder: true
|
||||||
|
}
|
||||||
|
p.table.register_type2(ttyp)
|
||||||
|
}
|
||||||
|
// f.idx = p.table.fn_cnt
|
||||||
|
receiver_T.add_method(f)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// println('register_fn typ=$typ isg=$is_generic')
|
||||||
|
p.table.register_fn(f)
|
||||||
|
}
|
||||||
|
if is_sig || p.first_run() || is_live || is_fn_header || skip_main_in_test {
|
||||||
|
// First pass? Skip the body for now [BIG]
|
||||||
|
if !is_sig && !is_fn_header {
|
||||||
|
for {
|
||||||
|
p.next()
|
||||||
|
if p.tok.is_decl() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Live code reloading? Load all fns from .so
|
||||||
|
if is_live && p.first_run() {
|
||||||
|
// p.cgen.consts_init.push('$fn_name_cgen = dlsym(lib, "$fn_name_cgen");')
|
||||||
|
p.cgen.so_fns << fn_name_cgen
|
||||||
|
fn_name_cgen = '(* $fn_name_cgen )'
|
||||||
|
}
|
||||||
|
// Actual fn declaration!
|
||||||
|
mut fn_decl := '$typ $fn_name_cgen($str_args)'
|
||||||
|
if p.obfuscate {
|
||||||
|
fn_decl += '; // ${f.name}'
|
||||||
|
}
|
||||||
|
// Add function definition to the top
|
||||||
|
if !is_c && f.name != 'main' && p.first_run() {
|
||||||
|
// TODO hack to make Volt compile without -embed_vlib
|
||||||
|
if f.name == 'darwin__nsstring' && p.build_mode == DEFAULT_MODE {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.cgen.fns << fn_decl + ';'
|
||||||
|
}
|
||||||
|
p.fgenln('\n')// TODO defer this instead of copy pasting
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if f.name == 'main' || f.name == 'WinMain' {
|
||||||
|
p.genln('init_consts();')
|
||||||
|
if p.table.imports.contains('os') {
|
||||||
|
if f.name == 'main' {
|
||||||
|
p.genln('os__init_os_args(argc, argv);')
|
||||||
|
}
|
||||||
|
else if f.name == 'WinMain' {
|
||||||
|
p.genln('os__parse_windows_cmd_line(pCmdLine);')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We are in live code reload mode, call the .so loader in bg
|
||||||
|
if p.is_live {
|
||||||
|
p.genln('
|
||||||
|
load_so("bounce.so");
|
||||||
|
pthread_t _thread_so;
|
||||||
|
pthread_create(&_thread_so , NULL, &reload_so, NULL); ')
|
||||||
|
}
|
||||||
|
if p.is_test && !p.scanner.file_path.contains('/volt') {
|
||||||
|
p.error('tests cannot have function `main`')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// println('is_c=$is_c name=$f.name')
|
||||||
|
if is_c || is_sig || is_fn_header {
|
||||||
|
// println('IS SIG RETURNING tok=${p.strtok()}')
|
||||||
|
p.fgenln('\n')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// We are in profile mode? Start counting at the beginning of the function (save current time).
|
||||||
|
if p.is_prof && f.name != 'main' && f.name != 'time__ticks' {
|
||||||
|
p.genln('double _PROF_START = time__ticks();//$f.name')
|
||||||
|
cgen_name := p.table.cgen_name(f)
|
||||||
|
f.defer = ' ${cgen_name}_time += time__ticks() - _PROF_START;'
|
||||||
|
}
|
||||||
|
p.statements_no_curly_end()
|
||||||
|
// Print counting result after all statements in main
|
||||||
|
if p.is_prof && f.name == 'main' {
|
||||||
|
p.genln(p.print_prof_counters())
|
||||||
|
}
|
||||||
|
// Counting or not, always need to add defer before the end
|
||||||
|
p.genln(f.defer)
|
||||||
|
if typ != 'void' && !p.returns && f.name != 'main' && f.name != 'WinMain' {
|
||||||
|
p.error('$f.name must return "$typ"')
|
||||||
|
}
|
||||||
|
// {} closed correctly? scope_level should be 0
|
||||||
|
if p.pkg == 'main' {
|
||||||
|
// println(p.cur_fn.scope_level)
|
||||||
|
}
|
||||||
|
if p.cur_fn.scope_level > 2 {
|
||||||
|
// p.error('unclosed {')
|
||||||
|
}
|
||||||
|
// Make sure all vars in this function are used (only in main for now)
|
||||||
|
// if p.builtin_pkg || p.pkg == 'os' ||p.pkg=='http'{
|
||||||
|
if p.pkg != 'main' {
|
||||||
|
p.genln('}')
|
||||||
|
p.fgenln('\n')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for var in f.local_vars {
|
||||||
|
if var.name == '' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !var.is_used && !var.is_arg && !p.translated && var.name != '_' {
|
||||||
|
p.scanner.line_nr = var.line_nr - 1
|
||||||
|
p.error('`$var.name` declared and not used')
|
||||||
|
}
|
||||||
|
// Very basic automatic memory management at the end of the function.
|
||||||
|
// This is inserted right before the final `}`, so if the object is being returned,
|
||||||
|
// the free method will not be called.
|
||||||
|
if p.is_test && var.typ.contains('array_') {
|
||||||
|
// p.genln('v_${var.typ}_free($var.name); // !!!! XAXA')
|
||||||
|
// p.genln('free(${var.name}.data); // !!!! XAXA')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// println('end of func decl')
|
||||||
|
// p.print_tok()
|
||||||
|
p.cur_fn = EmptyFn
|
||||||
|
p.fgenln('\n')
|
||||||
|
p.genln('}')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Important function with 5 args.
|
||||||
|
// user.say_hi() => "User_say_hi(user)"
|
||||||
|
// method_ph - where to insert "user_say_hi("
|
||||||
|
// receiver_var - "user" (needed for pthreads)
|
||||||
|
// receiver_type - "User"
|
||||||
|
fn (p mut Parser) async_fn_call(f Fn, method_ph int, receiver_var, receiver_type string) {
|
||||||
|
// println('\nfn_call $f.name is_method=$f.is_method receiver_type=$f.receiver_type')
|
||||||
|
// p.print_tok()
|
||||||
|
mut thread_name := ''
|
||||||
|
// Normal function => just its name, method => TYPE_FNNAME
|
||||||
|
mut fn_name := f.name
|
||||||
|
if f.is_method {
|
||||||
|
receiver_type = receiver_type.replace('*', '')
|
||||||
|
fn_name = '${receiver_type}_${f.name}'
|
||||||
|
}
|
||||||
|
// Generate tmp struct with args
|
||||||
|
arg_struct_name := 'thread_arg_$fn_name'
|
||||||
|
tmp_struct := p.get_tmp()
|
||||||
|
p.genln('$arg_struct_name * $tmp_struct = malloc(sizeof($arg_struct_name));')
|
||||||
|
mut arg_struct := 'typedef struct $arg_struct_name { '
|
||||||
|
p.next()
|
||||||
|
p.check(LPAR)
|
||||||
|
// str_args contains the args for the wrapper function:
|
||||||
|
// wrapper(arg_struct * arg) { fn("arg->a, arg->b"); }
|
||||||
|
mut str_args := ''
|
||||||
|
for i, arg in f.args {
|
||||||
|
arg_struct += '$arg.typ $arg.name ;'// Add another field (arg) to the tmp struct definition
|
||||||
|
str_args += 'arg->$arg.name'
|
||||||
|
if i == 0 && f.is_method {
|
||||||
|
p.genln('$tmp_struct -> $arg.name = $receiver_var ;')
|
||||||
|
if i < f.args.len - 1 {
|
||||||
|
str_args += ','
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Set the struct values (args)
|
||||||
|
p.genln('$tmp_struct -> $arg.name = ')
|
||||||
|
p.expression()
|
||||||
|
p.genln(';')
|
||||||
|
if i < f.args.len - 1 {
|
||||||
|
p.check(COMMA)
|
||||||
|
str_args += ','
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arg_struct += '} $arg_struct_name ;'
|
||||||
|
// Also register the wrapper, so we can use the original function without modifying it
|
||||||
|
fn_name = p.table.cgen_name(f)
|
||||||
|
wrapper_name := '${fn_name}_thread_wrapper'
|
||||||
|
wrapper_text := 'void* $wrapper_name($arg_struct_name * arg) {$fn_name( /*f*/$str_args ); }'
|
||||||
|
p.cgen.register_thread_fn(wrapper_name, wrapper_text, arg_struct)
|
||||||
|
// Create thread object
|
||||||
|
tmp_nr := p.get_tmp_counter()
|
||||||
|
thread_name = '_thread$tmp_nr'
|
||||||
|
if p.os != WINDOWS {
|
||||||
|
p.genln('pthread_t $thread_name;')
|
||||||
|
}
|
||||||
|
tmp2 := p.get_tmp()
|
||||||
|
mut parg := 'NULL'
|
||||||
|
if f.args.len > 0 {
|
||||||
|
parg = ' $tmp_struct'
|
||||||
|
}
|
||||||
|
// Call the wrapper
|
||||||
|
if p.os == WINDOWS {
|
||||||
|
p.genln(' CreateThread(0,0, $wrapper_name, $parg, 0,0);')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
p.genln('int $tmp2 = pthread_create(& $thread_name, NULL, $wrapper_name, $parg);')
|
||||||
|
}
|
||||||
|
p.check(RPAR)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type string) {
|
||||||
|
p.calling_c = f.is_c
|
||||||
|
is_print := p.is_prod &&// Hide prints only in prod
|
||||||
|
!p.is_test &&
|
||||||
|
!p.builtin_pkg &&// Allow prints in builtin pkgs
|
||||||
|
(f.name == 'println' || (f.is_c && f.name == 'printf'))
|
||||||
|
if !p.cgen.nogen {
|
||||||
|
p.cgen.nogen = is_print
|
||||||
|
}
|
||||||
|
cgen_name := p.table.cgen_name(f)
|
||||||
|
// if p.is_prof {
|
||||||
|
// p.cur_fn.called_fns << cgen_name
|
||||||
|
// }
|
||||||
|
// Normal function call
|
||||||
|
if !f.is_method {
|
||||||
|
p.gen(cgen_name)
|
||||||
|
p.gen('(')
|
||||||
|
// p.fgen(f.name)
|
||||||
|
}
|
||||||
|
// If we have a method placeholder,
|
||||||
|
// we need to preappend "method(receiver, ...)"
|
||||||
|
else {
|
||||||
|
// C only knows about functions "array_get", "array_set" etc
|
||||||
|
// TODO I don't need this?
|
||||||
|
// mut cgen_typ := receiver_type.replace('*', '')
|
||||||
|
// if cgen_typ.starts_with('array_') {
|
||||||
|
// cgen_typ = 'array'
|
||||||
|
// }
|
||||||
|
// println('METHOD fn_call name=$cgen_name')
|
||||||
|
// mut method_call := '${cgen_typ}_${cgen_name}('
|
||||||
|
mut method_call := '${cgen_name}('
|
||||||
|
// println('GGGG $f.name')
|
||||||
|
receiver := f.args.first()
|
||||||
|
if receiver.is_mut && !p.expr_var.is_mut {
|
||||||
|
println('$method_call recv=$receiver.name recv_mut=$receiver.is_mut')
|
||||||
|
p.error('`$p.expr_var.name` is immutable')
|
||||||
|
}
|
||||||
|
// if receiver is mutable or a ref (&), generate & for the first arg
|
||||||
|
if receiver.ref || (receiver.is_mut && !receiver_type.contains('*')) {
|
||||||
|
method_call += '& /* ? */'
|
||||||
|
}
|
||||||
|
// generate deref (TODO copy pasta later in fn_call_args)
|
||||||
|
if !receiver.is_mut && receiver_type.contains('*') {
|
||||||
|
method_call += '*'
|
||||||
|
}
|
||||||
|
mut cast = ''
|
||||||
|
// Method returns (void*) => cast it to int, string, user etc
|
||||||
|
// number := *(int*)numbers.first()
|
||||||
|
if f.typ == 'void*' {
|
||||||
|
// array_int => int
|
||||||
|
cast = receiver_type.all_after('_')
|
||||||
|
cast = '*($cast*) '
|
||||||
|
}
|
||||||
|
p.cgen.set_placeholder(method_ph, '$cast $method_call')
|
||||||
|
}
|
||||||
|
p.next()
|
||||||
|
// p.check(LPAR)
|
||||||
|
p.fn_call_args(f)
|
||||||
|
p.gen(')')
|
||||||
|
// p.check(RPAR)
|
||||||
|
p.calling_c = false
|
||||||
|
if is_print {
|
||||||
|
p.cgen.nogen = false
|
||||||
|
}
|
||||||
|
// println('end of fn call typ=$f.typ')
|
||||||
|
}
|
||||||
|
|
||||||
|
// for declaration
|
||||||
|
// return an updated Fn object with args[] field set
|
||||||
|
fn (p mut Parser) fn_args(f mut Fn) {
|
||||||
|
p.check(LPAR)
|
||||||
|
// TODO defer p.check(RPAR)
|
||||||
|
if f.is_interface {
|
||||||
|
int_arg := Var {
|
||||||
|
typ: f.receiver_typ
|
||||||
|
}
|
||||||
|
f.args << int_arg
|
||||||
|
}
|
||||||
|
// Just register fn arg types
|
||||||
|
types_only := p.tok == MUL || (p.peek() == COMMA && p.table.known_type(p.lit)) || p.peek() == RPAR// (int, string)
|
||||||
|
if types_only {
|
||||||
|
for p.tok != RPAR {
|
||||||
|
typ := p.get_type()
|
||||||
|
v := Var {
|
||||||
|
typ: typ
|
||||||
|
is_arg: true
|
||||||
|
// is_mut: is_mut
|
||||||
|
line_nr: p.scanner.line_nr
|
||||||
|
}
|
||||||
|
// f.register_var(v)
|
||||||
|
f.args << v
|
||||||
|
if p.tok == COMMA {
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// (a int, b,c string) syntax
|
||||||
|
for p.tok != RPAR {
|
||||||
|
mut names := [
|
||||||
|
p.check_name()
|
||||||
|
]
|
||||||
|
// a,b,c int syntax
|
||||||
|
for p.tok == COMMA {
|
||||||
|
p.check(COMMA)
|
||||||
|
p.fspace()
|
||||||
|
names << p.check_name()
|
||||||
|
}
|
||||||
|
p.fspace()
|
||||||
|
is_mut := p.tok == MUT
|
||||||
|
if is_mut {
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
mut typ2 := p.get_type()
|
||||||
|
for name in names {
|
||||||
|
if !p.first_run() && !p.table.known_type(typ2) {
|
||||||
|
p.error('fn_args: unknown type $typ2')
|
||||||
|
}
|
||||||
|
if is_mut {
|
||||||
|
// && !typ2.starts_with('array_') {
|
||||||
|
typ2 += '*'
|
||||||
|
}
|
||||||
|
v := Var {
|
||||||
|
name: name
|
||||||
|
typ: typ2
|
||||||
|
is_arg: true
|
||||||
|
is_mut: is_mut
|
||||||
|
ptr: is_mut
|
||||||
|
line_nr: p.scanner.line_nr
|
||||||
|
}
|
||||||
|
f.register_var(v)
|
||||||
|
f.args << v
|
||||||
|
}
|
||||||
|
if p.tok == COMMA {
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
if p.tok == DOTDOT {
|
||||||
|
f.args << Var {
|
||||||
|
name: '..'
|
||||||
|
}
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.check(RPAR)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) fn_call_args(f *Fn) *Fn {
|
||||||
|
// p.gen('(')
|
||||||
|
// println('fn_call_args() name=$f.name args.len=$f.args.len')
|
||||||
|
// C func. # of args is not known
|
||||||
|
// if f.name.starts_with('c_') {
|
||||||
|
p.check(LPAR)
|
||||||
|
if f.is_c {
|
||||||
|
for p.tok != RPAR {
|
||||||
|
// debug("LEX before EXP", p.tok)
|
||||||
|
p.bool_expression()
|
||||||
|
// debug("LEX AFTER EXP", p.tok)
|
||||||
|
if p.tok == COMMA {
|
||||||
|
p.gen(', ')
|
||||||
|
p.check(COMMA)
|
||||||
|
// debug("UUUUU C FUNC" + fnName)
|
||||||
|
// p.g.Gen("C FN " + fnName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.check(RPAR)
|
||||||
|
// p.gen(')')
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
// Receiver - first arg
|
||||||
|
for i, arg in f.args {
|
||||||
|
// println('$i) arg=$arg.name')
|
||||||
|
// Skip receiver, because it was already generated in the expression
|
||||||
|
if i == 0 && f.is_method {
|
||||||
|
if f.args.len > 1 {
|
||||||
|
p.gen(',')
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Reached the final vararg? Quit
|
||||||
|
if i == f.args.len - 1 && arg.name == '..' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
amp_ph := p.cgen.add_placeholder()
|
||||||
|
// ) here means not enough args were supplied
|
||||||
|
if p.tok == RPAR {
|
||||||
|
str_args := f.str_args(p.table)// TODO this is C args
|
||||||
|
p.error('not enough arguments in call to `$f.name ($str_args)`')
|
||||||
|
}
|
||||||
|
// If `arg` is mutable, the caller needs to provide MUT:
|
||||||
|
// `arr := [1,2,3]; reverse(mut arr);`
|
||||||
|
if arg.is_mut {
|
||||||
|
if p.tok != MUT {
|
||||||
|
p.error('`$arg.name` is a mutable argument, you need to provide `mut`: `$f.name(...mut a...)`')
|
||||||
|
}
|
||||||
|
if p.peek() != NAME {
|
||||||
|
p.error('`$arg.name` is a mutable argument, you need to provide a variable to modify: `$f.name(... mut a...)`')
|
||||||
|
}
|
||||||
|
p.check(MUT)
|
||||||
|
}
|
||||||
|
typ := p.bool_expression()
|
||||||
|
// TODO temporary hack to allow println(777)
|
||||||
|
if i == 0 && f.name == 'println' && typ != 'string'
|
||||||
|
&& typ != 'void' {
|
||||||
|
// If we dont check for void, then V will compile "println(procedure())"
|
||||||
|
if !p.is_prod {
|
||||||
|
T := p.table.find_type(typ)
|
||||||
|
if typ == 'u8' {
|
||||||
|
p.cgen.set_placeholder(amp_ph, 'u8_str(')
|
||||||
|
}
|
||||||
|
else if T.parent == 'int' {
|
||||||
|
p.cgen.set_placeholder(amp_ph, 'int_str(')
|
||||||
|
}
|
||||||
|
else if typ.ends_with('*') {
|
||||||
|
p.cgen.set_placeholder(amp_ph, 'ptr_str(')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Make sure this type has a `str()` method
|
||||||
|
if !T.has_method('str') {
|
||||||
|
p.error('`$typ` needs to have method `str() string` to be printable')
|
||||||
|
}
|
||||||
|
p.cgen.set_placeholder(amp_ph, '${typ}_str(')
|
||||||
|
}
|
||||||
|
p.gen(')')
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
got := typ
|
||||||
|
expected := arg.typ
|
||||||
|
// println('fn arg got="$got" exp="$expected"')
|
||||||
|
if !p.check_types_no_throw(got, expected) {
|
||||||
|
mut err := 'Fn "$f.name" wrong arg #${i+1}. '
|
||||||
|
err += 'Expected "$arg.typ" ($arg.name) but got "$typ"'
|
||||||
|
p.error(err)
|
||||||
|
}
|
||||||
|
is_interface := p.table.is_interface(arg.typ)
|
||||||
|
// Add & or * before arg?
|
||||||
|
if !is_interface {
|
||||||
|
// Dereference
|
||||||
|
if got.contains('*') && !expected.contains('*') {
|
||||||
|
p.cgen.set_placeholder(amp_ph, '*')
|
||||||
|
}
|
||||||
|
// Reference
|
||||||
|
// TODO ptr hacks. DOOM hacks, fix please.
|
||||||
|
if !got.contains('*') && expected.contains('*') && got != 'voidptr' {
|
||||||
|
// println('\ne:"$expected" got:"$got"')
|
||||||
|
if ! (expected == 'void*' && got == 'int') &&
|
||||||
|
! (expected == 'byte*' && got.contains(']byte')) &&
|
||||||
|
! (expected == 'byte*' && got == 'string') {
|
||||||
|
p.cgen.set_placeholder(amp_ph, '& /*11 EXP:"$expected" GOT:"$got" */')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// interface?
|
||||||
|
if is_interface {
|
||||||
|
if !got.contains('*') {
|
||||||
|
p.cgen.set_placeholder(amp_ph, '&')
|
||||||
|
}
|
||||||
|
// Pass all interface methods
|
||||||
|
interface_type := p.table.find_type(arg.typ)
|
||||||
|
for method in interface_type.methods {
|
||||||
|
p.gen(', ${typ}_${method.name} ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check for commas
|
||||||
|
if i < f.args.len - 1 {
|
||||||
|
// Handle 0 args passed to varargs
|
||||||
|
is_vararg := i == f.args.len - 2 && f.args[i + 1].name == '..'
|
||||||
|
if p.tok != COMMA && !is_vararg {
|
||||||
|
p.error('wrong number of arguments for $i,$arg.name fn `$f.name`: expected $f.args.len, but got less')
|
||||||
|
}
|
||||||
|
if p.tok == COMMA {
|
||||||
|
p.fgen(', ')
|
||||||
|
}
|
||||||
|
if !is_vararg {
|
||||||
|
p.next()
|
||||||
|
p.gen(',')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// varargs
|
||||||
|
if f.args.len > 0 {
|
||||||
|
last_arg := f.args.last()
|
||||||
|
if last_arg.name == '..' {
|
||||||
|
println('GOT VAR ARGS AFTER')
|
||||||
|
for p.tok != RPAR {
|
||||||
|
if p.tok == COMMA {
|
||||||
|
p.gen(',')
|
||||||
|
p.check(COMMA)
|
||||||
|
}
|
||||||
|
p.bool_expression()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.tok == COMMA {
|
||||||
|
p.error('wrong number of arguments for fn `$f.name`: expected $f.args.len, but got more')
|
||||||
|
}
|
||||||
|
p.check(RPAR)
|
||||||
|
// p.gen(')')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_capital(s string) bool {
|
||||||
|
// for c in s {
|
||||||
|
for i := 0; i < s.len; i++ {
|
||||||
|
c := s[i]
|
||||||
|
if c >= `A` && c <= `Z` {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// "fn (int, string) int"
|
||||||
|
fn (f Fn) typ_str() string {
|
||||||
|
mut sb := new_string_builder(50)
|
||||||
|
sb.write('fn (')
|
||||||
|
for i, arg in f.args {
|
||||||
|
sb.write(arg.typ)
|
||||||
|
if i < f.args.len - 1 {
|
||||||
|
sb.write(',')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.write(')')
|
||||||
|
if f.typ != 'void' {
|
||||||
|
sb.write(' $f.typ')
|
||||||
|
}
|
||||||
|
return sb.str()
|
||||||
|
}
|
||||||
|
|
||||||
|
// f.args => "int a, string b"
|
||||||
|
fn (f &Fn) str_args(table *Table) string {
|
||||||
|
mut s := ''
|
||||||
|
for i, arg in f.args {
|
||||||
|
// Interfaces are a special case. We need to pass the object + pointers
|
||||||
|
// to all methods:
|
||||||
|
// fn handle(r Runner) { =>
|
||||||
|
// void handle(void *r, void (*Runner_run)(void*)) {
|
||||||
|
if table.is_interface(arg.typ) {
|
||||||
|
// First the object (same name as the interface argument)
|
||||||
|
s += ' void* $arg.name'
|
||||||
|
// Now all methods
|
||||||
|
interface_type := table.find_type(arg.typ)
|
||||||
|
for method in interface_type.methods {
|
||||||
|
s += ', $method.typ (*${arg.typ}_${method.name})(void*) '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if arg.name == '..' {
|
||||||
|
s += '...'
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// s += '$arg.typ $arg.name'
|
||||||
|
s += table.cgen_name_type_pair(arg.name, arg.typ)// '$arg.typ $arg.name'
|
||||||
|
}
|
||||||
|
if i < f.args.len - 1 {
|
||||||
|
s += ', '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
// 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 main
|
||||||
|
|
||||||
|
// TODO replace with comptime code generation.
|
||||||
|
// TODO remove cJSON dependency.
|
||||||
|
// OLD: User decode_User(string js) {
|
||||||
|
// now it's
|
||||||
|
// User decode_User(cJSON* root) {
|
||||||
|
// User res;
|
||||||
|
// res.name = decode_string(js_get(root, "name"));
|
||||||
|
// res.profile = decode_Profile(js_get(root, "profile"));
|
||||||
|
// return res;
|
||||||
|
// }
|
||||||
|
// Codegen json_decode/encode funcs
|
||||||
|
fn (p mut Parser) gen_json_for_type(typ Type) {
|
||||||
|
mut dec := ''
|
||||||
|
mut enc := ''
|
||||||
|
t := typ.name
|
||||||
|
if t == 'int' || t == 'string' || t == 'bool' {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if p.first_run() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// println('gen_json_for_type( $typ.name )')
|
||||||
|
// Register decoder fn
|
||||||
|
mut dec_fn := Fn {
|
||||||
|
pkg: p.pkg
|
||||||
|
typ: 'Option_$typ.name'
|
||||||
|
name: js_dec_name(t)
|
||||||
|
}
|
||||||
|
// Already registered? Skip.
|
||||||
|
if p.table.known_fn(dec_fn.name) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// decode_TYPE funcs receive an actual cJSON* object to decode
|
||||||
|
// cJSON_Parse(str) call is added by the compiler
|
||||||
|
arg := Var {
|
||||||
|
typ: 'cJSON*'
|
||||||
|
}
|
||||||
|
dec_fn.args << arg
|
||||||
|
p.table.register_fn(dec_fn)
|
||||||
|
// Register encoder fn
|
||||||
|
mut enc_fn := Fn {
|
||||||
|
pkg: p.pkg
|
||||||
|
typ: 'cJSON*'
|
||||||
|
name: js_enc_name(t)
|
||||||
|
}
|
||||||
|
// encode_TYPE funcs receive an object to encode
|
||||||
|
enc_arg := Var {
|
||||||
|
typ: t
|
||||||
|
}
|
||||||
|
enc_fn.args << enc_arg
|
||||||
|
p.table.register_fn(enc_fn)
|
||||||
|
// Code gen decoder
|
||||||
|
dec += '
|
||||||
|
//$t $dec_fn.name(cJSON* root) {
|
||||||
|
Option $dec_fn.name(cJSON* root, $t* res) {
|
||||||
|
// $t res;
|
||||||
|
if (!root) {
|
||||||
|
const char *error_ptr = cJSON_GetErrorPtr();
|
||||||
|
if (error_ptr != NULL) {
|
||||||
|
fprintf(stderr, "Error in decode() for $t error_ptr=: %%s\\n", error_ptr);
|
||||||
|
// printf("\\nbad js=%%s\\n", js.str);
|
||||||
|
return b_error(tos2(error_ptr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'
|
||||||
|
// Code gen encoder
|
||||||
|
enc += '
|
||||||
|
cJSON* $enc_fn.name($t val) {
|
||||||
|
cJSON *o = cJSON_CreateObject();
|
||||||
|
string res = tos2("");
|
||||||
|
'
|
||||||
|
// Handle arrays
|
||||||
|
if t.starts_with('array_') {
|
||||||
|
dec += p.decode_array(t)
|
||||||
|
enc += p.encode_array(t)
|
||||||
|
}
|
||||||
|
// Range through fields
|
||||||
|
for field in typ.fields {
|
||||||
|
if field.attr == 'skip' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
field_type := p.table.find_type(field.typ)
|
||||||
|
// Now generate decoders for all field types in this struct
|
||||||
|
// need to do it here so that these functions are generated first
|
||||||
|
p.gen_json_for_type(field_type)
|
||||||
|
name := field.name
|
||||||
|
_typ := field.typ.replace('*', '')
|
||||||
|
enc_name := js_enc_name(_typ)
|
||||||
|
dec_name := js_dec_name(_typ)
|
||||||
|
if is_js_prim(_typ) {
|
||||||
|
dec += ' /*prim*/ res->$name = $dec_name(js_get(root, "$field.name"))'
|
||||||
|
// dec += '.data'
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dec += ' /*!!*/ $dec_name(js_get(root, "$field.name"), & (res->$name))'
|
||||||
|
}
|
||||||
|
dec += ';\n'
|
||||||
|
enc += ' cJSON_AddItemToObject(o, "$name", $enc_name(val.$name)); \n'
|
||||||
|
}
|
||||||
|
// cJSON_delete
|
||||||
|
p.cgen.fns << '$dec return opt_ok(res); \n}'
|
||||||
|
p.cgen.fns << '/*enc start*/ $enc return o;}'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_js_prim(typ string) bool {
|
||||||
|
return typ == 'int' || typ == 'string' || typ == 'bool'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) decode_array(typ string) string {
|
||||||
|
typ = typ.replace('array_', '')
|
||||||
|
t := p.table.find_type(typ)
|
||||||
|
fn_name := js_dec_name(typ)
|
||||||
|
// If we have `[]Profile`, have to register a Profile en(de)coder first
|
||||||
|
p.gen_json_for_type(t)
|
||||||
|
mut s := ''
|
||||||
|
if is_js_prim(typ) {
|
||||||
|
s = '$typ val= $fn_name(jsval); '
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
s = ' $typ val; $fn_name(jsval, &val); '
|
||||||
|
}
|
||||||
|
return '
|
||||||
|
*res = new_array(0, 0, sizeof($typ));
|
||||||
|
const cJSON *jsval = NULL;
|
||||||
|
cJSON_ArrayForEach(jsval, root)
|
||||||
|
{
|
||||||
|
$s
|
||||||
|
array__push(res, &val);
|
||||||
|
}
|
||||||
|
'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn js_enc_name(typ string) string {
|
||||||
|
name := 'json__jsencode_$typ'
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn js_dec_name(typ string) string {
|
||||||
|
name := 'json__jsdecode_$typ'
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p &Parser) encode_array(typ string) string {
|
||||||
|
typ = typ.replace('array_', '')
|
||||||
|
fn_name := js_enc_name(typ)
|
||||||
|
return '
|
||||||
|
o = cJSON_CreateArray();
|
||||||
|
for (int i = 0; i < val.len; i++){
|
||||||
|
cJSON_AddItemToArray(o, $fn_name( (($typ*)val.data)[i] ));
|
||||||
|
}
|
||||||
|
'
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,845 @@
|
||||||
|
module main
|
||||||
|
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
const (
|
||||||
|
Version = '0.0.12'
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO no caps
|
||||||
|
enum BuildMode {
|
||||||
|
// `v program.v'
|
||||||
|
// Build user code only, and add pre-compiled vlib (`cc program.o builtin.o os.o...`)
|
||||||
|
DEFAULT_MODE
|
||||||
|
// `v -embed_vlib program.v`
|
||||||
|
// vlib + user code in one file (slower compilation, but easier when working on vlib and cross-compiling)
|
||||||
|
EMBED_VLIB
|
||||||
|
// `v -lib ~/v/os`
|
||||||
|
// build any module (generate os.o + os.vh)
|
||||||
|
BUILD // TODO a better name would be smth like `.build_module` I think
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vtmp_path() string {
|
||||||
|
$if windows {
|
||||||
|
return os.home_dir() + '/.vlang$Version/'
|
||||||
|
}
|
||||||
|
return '/var/tmp/vlang$Version/'
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
SupportedPlatforms = ['windows', 'mac', 'linux']
|
||||||
|
TmpPath = vtmp_path()
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO V was re-written in V before enums were implemented. Lots of consts need to be replaced with
|
||||||
|
// enums.
|
||||||
|
const (
|
||||||
|
MAC = 0
|
||||||
|
LINUX = 1
|
||||||
|
WINDOWS = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
enum Pass {
|
||||||
|
// A very short pass that only looks at imports in the begginning of each file
|
||||||
|
RUN_IMPORTS
|
||||||
|
// First pass, only parses and saves declarations (fn signatures, consts, types).
|
||||||
|
// Skips function bodies.
|
||||||
|
// We need this because in V things can be used before they are declared.
|
||||||
|
RUN_DECLS
|
||||||
|
// Second pass, parses function bodies and generates C or machine code.
|
||||||
|
RUN_MAIN
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// TODO rename to:
|
||||||
|
enum Pass {
|
||||||
|
imports
|
||||||
|
decls
|
||||||
|
main
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
struct V {
|
||||||
|
mut:
|
||||||
|
build_mode BuildMode
|
||||||
|
os int // the OS to build for
|
||||||
|
nofmt bool // disable vfmt
|
||||||
|
out_name_c string // name of the temporary C file
|
||||||
|
files []string // all V files that need to be parsed and compiled
|
||||||
|
dir string // directory (or file) being compiled (TODO rename to path?)
|
||||||
|
table *Table // table with types, vars, functions etc
|
||||||
|
cgen *CGen // C code generator
|
||||||
|
is_test bool // `v test string_test.v`
|
||||||
|
is_script bool // single file mode (`v program.v`), `fn main(){}` can be skipped
|
||||||
|
is_so bool
|
||||||
|
is_live bool // for hot code reloading
|
||||||
|
is_prof bool // benchmark every function
|
||||||
|
translated bool // `v translated doom.v` are we running V code translated from C? allow globals, ++ expressions, etc
|
||||||
|
obfuscate bool // `v -obf program.v`, renames functions to "f_XXX"
|
||||||
|
lang_dir string // "~/code/v"
|
||||||
|
is_verbose bool // print extra information with `v.log()`
|
||||||
|
is_run bool // `v run program.v`
|
||||||
|
is_play bool // playground mode
|
||||||
|
show_c_cmd bool // `v -show_c_cmd` prints the C command to build program.v.c
|
||||||
|
sanitize bool // use Clang's new "-fsanitize" option
|
||||||
|
out_name string // "program.exe"
|
||||||
|
is_prod bool // use "-O2" and skip printlns (TODO I don't thik many people want printlns to disappear in prod buidls)
|
||||||
|
is_repl bool
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// There's no `flags` module yet, so args have to be parsed manually
|
||||||
|
args := os.args
|
||||||
|
// Print the version and exit.
|
||||||
|
if 'version' in args {
|
||||||
|
println2('V $Version')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if '-h' in args || '--help' in args || 'help' in args {
|
||||||
|
println(HelpText)
|
||||||
|
}
|
||||||
|
// u := os.file_last_mod_unix('/var/tmp/alex')
|
||||||
|
// t := time.unixn(u)
|
||||||
|
// println(t.clean())
|
||||||
|
// If there's not tmp path with current version yet, the user must be using a pre-built package
|
||||||
|
// Copy the `vlib` directory to the tmp path.
|
||||||
|
if !os.file_exists(TmpPath) && os.file_exists('vlib') {
|
||||||
|
os.mkdir(TmpPath)
|
||||||
|
os.system2('cp -rf vlib $TmpPath/')
|
||||||
|
// os.system2('cp -rf json $TmpPath/')
|
||||||
|
}
|
||||||
|
// Just fmt and exit
|
||||||
|
if args.contains('fmt') {
|
||||||
|
file := args.last()
|
||||||
|
if !os.file_exists(file) {
|
||||||
|
os.exit1('"$file" does not exist')
|
||||||
|
}
|
||||||
|
if !file.ends_with('.v') {
|
||||||
|
os.exit1('v fmt can only be used on .v files')
|
||||||
|
}
|
||||||
|
println2('vfmt is temporarily disabled')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// V with no args? REPL
|
||||||
|
if args.len < 2 {
|
||||||
|
run_repl()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Construct the V object from command line arguments
|
||||||
|
mut c := new_v(args)
|
||||||
|
if c.is_verbose {
|
||||||
|
println(args)
|
||||||
|
}
|
||||||
|
// Generate the docs and exit
|
||||||
|
if args.contains('doc') {
|
||||||
|
// c.gen_doc_html_for_module(args.last())
|
||||||
|
os.exit('')
|
||||||
|
}
|
||||||
|
c.compile()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c mut V) compile() {
|
||||||
|
mut cgen := c.cgen
|
||||||
|
cgen.genln('// Generated by V')
|
||||||
|
// Add user files to compile
|
||||||
|
c.add_user_v_files()
|
||||||
|
if c.is_verbose {
|
||||||
|
println('all .v files:')
|
||||||
|
println(c.files)
|
||||||
|
}
|
||||||
|
// First pass (declarations)
|
||||||
|
for file in c.files {
|
||||||
|
mut p := c.new_parser(file, RUN_DECLS)
|
||||||
|
p.parse()
|
||||||
|
}
|
||||||
|
// Main pass
|
||||||
|
cgen.run = RUN_MAIN
|
||||||
|
if c.os == MAC {
|
||||||
|
cgen.genln('#define mac (1) ')
|
||||||
|
// cgen.genln('#include <pthread.h>')
|
||||||
|
}
|
||||||
|
if c.os == LINUX {
|
||||||
|
cgen.genln('#define linux (1) ')
|
||||||
|
cgen.genln('#include <pthread.h>')
|
||||||
|
}
|
||||||
|
if c.os == WINDOWS {
|
||||||
|
cgen.genln('#define windows (1) ')
|
||||||
|
// cgen.genln('#include <WinSock2.h>')
|
||||||
|
cgen.genln('#include <windows.h> ')
|
||||||
|
}
|
||||||
|
if c.is_play {
|
||||||
|
cgen.genln('#define VPLAY (1) ')
|
||||||
|
}
|
||||||
|
cgen.genln('
|
||||||
|
#include <stdio.h> // TODO remove all these includes, define all function signatures and types manually
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdarg.h> // for va_list
|
||||||
|
#include <inttypes.h> // int64_t etc
|
||||||
|
|
||||||
|
//================================== TYPEDEFS ================================*/
|
||||||
|
|
||||||
|
typedef unsigned char byte;
|
||||||
|
typedef unsigned int uint;
|
||||||
|
typedef int64_t i64;
|
||||||
|
typedef int32_t i32;
|
||||||
|
typedef int16_t i16;
|
||||||
|
typedef int8_t i8;
|
||||||
|
typedef uint64_t u64;
|
||||||
|
typedef uint32_t u32;
|
||||||
|
typedef uint16_t u16;
|
||||||
|
typedef uint8_t u8;
|
||||||
|
typedef uint32_t rune;
|
||||||
|
typedef float f32;
|
||||||
|
typedef double f64;
|
||||||
|
typedef unsigned char* byteptr;
|
||||||
|
typedef int* intptr;
|
||||||
|
typedef void* voidptr;
|
||||||
|
typedef struct array array;
|
||||||
|
typedef struct map map;
|
||||||
|
typedef array array_string;
|
||||||
|
typedef array array_int;
|
||||||
|
typedef array array_byte;
|
||||||
|
typedef array array_uint;
|
||||||
|
typedef array array_float;
|
||||||
|
typedef map map_int;
|
||||||
|
typedef map map_string;
|
||||||
|
#ifndef bool
|
||||||
|
typedef int bool;
|
||||||
|
#define true 1
|
||||||
|
#define false 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//============================== HELPER C MACROS =============================*/
|
||||||
|
|
||||||
|
#define _PUSH(arr, val, tmp, tmp_typ) {tmp_typ tmp = (val); array__push(arr, &tmp);}
|
||||||
|
#define _IN(typ, val, arr) array_##typ##_contains(arr, val)
|
||||||
|
#define ALLOC_INIT(type, ...) (type *)memdup((type[]){ __VA_ARGS__ }, sizeof(type))
|
||||||
|
#define UTF8_CHAR_LEN( byte ) (( 0xE5000000 >> (( byte >> 3 ) & 0x1e )) & 3 ) + 1
|
||||||
|
|
||||||
|
//================================== GLOBALS =================================*/
|
||||||
|
//int V_ZERO = 0;
|
||||||
|
byteptr g_str_buf;
|
||||||
|
int load_so(byteptr);
|
||||||
|
void reload_so();
|
||||||
|
void init_consts();')
|
||||||
|
imports_json := c.table.imports.contains('json')
|
||||||
|
// TODO remove global UI hack
|
||||||
|
if c.os == MAC && ((c.build_mode == EMBED_VLIB && c.table.imports.contains('ui')) ||
|
||||||
|
(c.build_mode == BUILD && c.dir.contains('/ui'))) {
|
||||||
|
cgen.genln('id defaultFont = 0; // main.v')
|
||||||
|
}
|
||||||
|
// TODO remove ugly .c include once V has its own json parser
|
||||||
|
// Embed cjson either in embedvlib or in json.o
|
||||||
|
if imports_json && c.build_mode == EMBED_VLIB ||
|
||||||
|
(c.build_mode == BUILD && c.out_name.contains('json.o')) {
|
||||||
|
cgen.genln('#include "json/cJSON/cJSON.c" ')
|
||||||
|
}
|
||||||
|
// We need the cjson header for all the json decoding user will do in default mode
|
||||||
|
if c.build_mode == DEFAULT_MODE {
|
||||||
|
if imports_json {
|
||||||
|
cgen.genln('#include "json/cJSON/cJSON.h"')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.build_mode == EMBED_VLIB || c.build_mode == DEFAULT_MODE {
|
||||||
|
// If we declare these for all modes, then when running `v a.v` we'll get
|
||||||
|
// `/usr/bin/ld: multiple definition of 'total_m'`
|
||||||
|
cgen.genln('i64 total_m = 0; // For counting total RAM allocated')
|
||||||
|
cgen.genln('int g_test_ok = 1; ')
|
||||||
|
if c.table.imports.contains('json') {
|
||||||
|
cgen.genln('
|
||||||
|
#define js_get(object, key) cJSON_GetObjectItemCaseSensitive((object), (key))
|
||||||
|
')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if os.args.contains('-debug_alloc') {
|
||||||
|
cgen.genln('#define DEBUG_ALLOC 1')
|
||||||
|
}
|
||||||
|
cgen.genln('/*================================== FNS =================================*/')
|
||||||
|
cgen.genln('this line will be replaced with definitions')
|
||||||
|
defs_pos := cgen.lines.len - 1
|
||||||
|
for file in c.files {
|
||||||
|
mut p := c.new_parser(file, RUN_MAIN)
|
||||||
|
p.parse()
|
||||||
|
// p.g.gen_x64()
|
||||||
|
// Format all files (don't format automatically generated vlib headers)
|
||||||
|
if !c.nofmt && !file.contains('/vlib/') {
|
||||||
|
// new vfmt is not ready yet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.log('Done parsing.')
|
||||||
|
// Write everything
|
||||||
|
mut d := new_string_builder(10000)// Just to avoid some unnecessary allocations
|
||||||
|
d.writeln(cgen.includes.join_lines())
|
||||||
|
d.writeln(cgen.typedefs.join_lines())
|
||||||
|
d.writeln(cgen.types.join_lines())
|
||||||
|
d.writeln('\nstring _STR(const char*, ...);\n')
|
||||||
|
d.writeln('\nstring _STR_TMP(const char*, ...);\n')
|
||||||
|
d.writeln(cgen.fns.join_lines())
|
||||||
|
d.writeln(cgen.consts.join_lines())
|
||||||
|
d.writeln(cgen.thread_args.join_lines())
|
||||||
|
if c.is_prof {
|
||||||
|
d.writeln('; // Prof counters:')
|
||||||
|
d.writeln(c.prof_counters())
|
||||||
|
}
|
||||||
|
dd := d.str()
|
||||||
|
cgen.lines.set(defs_pos, dd)// TODO `def.str()` doesn't compile
|
||||||
|
// if c.build_mode in [.default, .embed_vlib] {
|
||||||
|
if c.build_mode == DEFAULT_MODE || c.build_mode == EMBED_VLIB {
|
||||||
|
// vlib can't have `init_consts()`
|
||||||
|
cgen.genln('void init_consts() { g_str_buf=malloc(1000); ${cgen.consts_init.join_lines()} }')
|
||||||
|
// _STR function can't be defined in vlib
|
||||||
|
cgen.genln('
|
||||||
|
string _STR(const char *fmt, ...) {
|
||||||
|
va_list argptr;
|
||||||
|
va_start(argptr, fmt);
|
||||||
|
size_t len = vsnprintf(0, 0, fmt, argptr) + 1;
|
||||||
|
va_end(argptr);
|
||||||
|
byte* buf = malloc(len);
|
||||||
|
va_start(argptr, fmt);
|
||||||
|
vsprintf(buf, fmt, argptr);
|
||||||
|
va_end(argptr);
|
||||||
|
#ifdef DEBUG_ALLOC
|
||||||
|
puts("_STR:");
|
||||||
|
puts(buf);
|
||||||
|
#endif
|
||||||
|
return tos2(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
string _STR_TMP(const char *fmt, ...) {
|
||||||
|
va_list argptr;
|
||||||
|
va_start(argptr, fmt);
|
||||||
|
size_t len = vsnprintf(0, 0, fmt, argptr) + 1;
|
||||||
|
va_end(argptr);
|
||||||
|
va_start(argptr, fmt);
|
||||||
|
vsprintf(g_str_buf, fmt, argptr);
|
||||||
|
va_end(argptr);
|
||||||
|
#ifdef DEBUG_ALLOC
|
||||||
|
//puts("_STR_TMP:");
|
||||||
|
//puts(g_str_buf);
|
||||||
|
#endif
|
||||||
|
return tos2(g_str_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
')
|
||||||
|
}
|
||||||
|
// Make sure the main function exists
|
||||||
|
// Obviously we don't need it in libraries
|
||||||
|
if c.build_mode != BUILD {
|
||||||
|
if !c.table.main_exists() && !c.is_test {
|
||||||
|
// It can be skipped in single file programs
|
||||||
|
if c.is_script {
|
||||||
|
println('Generating main()...')
|
||||||
|
cgen.genln('int main() { $cgen.fn_main; return 0; }')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
println('panic: function `main` is undeclared in the main module')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Generate `main` which calls every single test function
|
||||||
|
else if c.is_test {
|
||||||
|
cgen.genln('int main() { init_consts();')
|
||||||
|
for v in c.table.fns {
|
||||||
|
if v.name.starts_with('test_') {
|
||||||
|
cgen.genln('$v.name();')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cgen.genln('return g_test_ok == 0; }')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.is_live {
|
||||||
|
cgen.genln(' int load_so(byteptr path) {
|
||||||
|
printf("load_so %s\\n", path); dlclose(live_lib); live_lib = dlopen(path, RTLD_LAZY);
|
||||||
|
if (!live_lib) {puts("open failed"); exit(1); return 0;}
|
||||||
|
')
|
||||||
|
for so_fn in cgen.so_fns {
|
||||||
|
cgen.genln('$so_fn = dlsym(live_lib, "$so_fn"); ')
|
||||||
|
}
|
||||||
|
cgen.genln('return 1; }')
|
||||||
|
}
|
||||||
|
cgen.save()
|
||||||
|
c.log('flags=')
|
||||||
|
if c.is_verbose {
|
||||||
|
println(c.table.flags)
|
||||||
|
}
|
||||||
|
c.cc()
|
||||||
|
if c.is_test || c.is_run {
|
||||||
|
if true || c.is_verbose {
|
||||||
|
println('============running $c.out_name==============================')
|
||||||
|
}
|
||||||
|
cmd := if c.out_name.starts_with('/') {
|
||||||
|
c.out_name
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
'./' + c.out_name
|
||||||
|
}
|
||||||
|
ret := os.system2(cmd)
|
||||||
|
if ret != 0 {
|
||||||
|
s := os.system(cmd)
|
||||||
|
println2(s)
|
||||||
|
os.exit1('ret not 0, exiting')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c mut V) cc() {
|
||||||
|
linux_host := os.user_os() == 'linux'
|
||||||
|
c.log('cc() isprod=$c.is_prod outname=$c.out_name')
|
||||||
|
mut a := ['-w']// arguments for the C compiler
|
||||||
|
flags := c.table.flags.join(' ')
|
||||||
|
/*
|
||||||
|
mut shared := ''
|
||||||
|
if c.is_so {
|
||||||
|
a << '-shared'// -Wl,-z,defs'
|
||||||
|
c.out_name = c.out_name + '.so'
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if c.is_prod {
|
||||||
|
a << '-O2'
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
a << '-g'
|
||||||
|
}
|
||||||
|
mut libs := ''// builtin.o os.o http.o etc
|
||||||
|
if c.build_mode == BUILD {
|
||||||
|
a << '-c'
|
||||||
|
}
|
||||||
|
else if c.build_mode == EMBED_VLIB {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
else if c.build_mode == DEFAULT_MODE {
|
||||||
|
libs = '$TmpPath/vlib/builtin.o'
|
||||||
|
if !os.file_exists(libs) {
|
||||||
|
println2('`builtin.o` not found')
|
||||||
|
exit('')
|
||||||
|
}
|
||||||
|
for imp in c.table.imports {
|
||||||
|
if imp == 'webview' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
libs += ' $TmpPath/vlib/${imp}.o'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// -I flags
|
||||||
|
/*
|
||||||
|
mut args := ''
|
||||||
|
for flag in c.table.flags {
|
||||||
|
if !flag.starts_with('-l') {
|
||||||
|
args += flag
|
||||||
|
args += ' '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if c.sanitize {
|
||||||
|
a << '-fsanitize=leak'
|
||||||
|
}
|
||||||
|
// Cross compiling linux
|
||||||
|
sysroot := '/Users/alex/tmp/lld/linuxroot/'
|
||||||
|
if c.os == LINUX && !linux_host {
|
||||||
|
// Build file.o
|
||||||
|
a << '-c --sysroot=$sysroot -target x86_64-linux-gnu'
|
||||||
|
// Right now `out_name` can be `file`, not `file.o`
|
||||||
|
if !c.out_name.ends_with('.o') {
|
||||||
|
c.out_name = c.out_name + '.o'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Cross compiling windows
|
||||||
|
// sysroot := '/Users/alex/tmp/lld/linuxroot/'
|
||||||
|
// Output executable name
|
||||||
|
// else {
|
||||||
|
a << '-o $c.out_name'
|
||||||
|
// }
|
||||||
|
// Min macos version is mandatory I think?
|
||||||
|
if c.os == MAC {
|
||||||
|
a << '-mmacosx-version-min=10.7'
|
||||||
|
}
|
||||||
|
a << flags
|
||||||
|
a << libs
|
||||||
|
// macOS code can include objective C TODO remove once objective C is replaced with C
|
||||||
|
if c.os == MAC {
|
||||||
|
a << '-x objective-c'
|
||||||
|
}
|
||||||
|
// The C file we are compiling
|
||||||
|
a << '$TmpPath/$c.out_name_c'
|
||||||
|
// Without these libs compilation will fail on Linux
|
||||||
|
if c.os == LINUX && c.build_mode != BUILD {
|
||||||
|
a << '-lm -ldl -lpthread'
|
||||||
|
}
|
||||||
|
// Find clang executable
|
||||||
|
fast_clang := '/usr/local/Cellar/llvm/8.0.0/bin/clang'
|
||||||
|
args := a.join(' ')
|
||||||
|
cmd := if os.file_exists(fast_clang) {
|
||||||
|
'$fast_clang -I. $args'
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
'clang -I. $args'
|
||||||
|
}
|
||||||
|
// Print the C command
|
||||||
|
if c.show_c_cmd || c.is_verbose {
|
||||||
|
println('\n==========\n$cmd\n=========\n')
|
||||||
|
}
|
||||||
|
// Run
|
||||||
|
res := os.system(cmd)
|
||||||
|
// println('C OUTPUT:')
|
||||||
|
if res.contains('error: ') {
|
||||||
|
println2(res)
|
||||||
|
panic('clang error')
|
||||||
|
}
|
||||||
|
// Link it if we are cross compiling and need an executable
|
||||||
|
if c.os == LINUX && !linux_host && c.build_mode != BUILD {
|
||||||
|
c.out_name = c.out_name.replace('.o', '')
|
||||||
|
obj_file := c.out_name + '.o'
|
||||||
|
println('linux obj_file=$obj_file out_name=$c.out_name')
|
||||||
|
ress := os.system('/usr/local/Cellar/llvm/8.0.0/bin/ld.lld --sysroot=$sysroot ' +
|
||||||
|
'-v -o $c.out_name ' +
|
||||||
|
'-m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 ' +
|
||||||
|
'/usr/lib/x86_64-linux-gnu/crt1.o ' +
|
||||||
|
'$sysroot/lib/x86_64-linux-gnu/libm-2.28.a ' +
|
||||||
|
'/usr/lib/x86_64-linux-gnu/crti.o ' +
|
||||||
|
obj_file +
|
||||||
|
' /usr/lib/x86_64-linux-gnu/libc.so ' +
|
||||||
|
'/usr/lib/x86_64-linux-gnu/crtn.o')
|
||||||
|
println(ress)
|
||||||
|
if ress.contains('error:') {
|
||||||
|
os.exit1('')
|
||||||
|
}
|
||||||
|
println('linux cross compilation done. resulting binary: "$c.out_name"')
|
||||||
|
}
|
||||||
|
// print_time('after gcc')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c &V) v_files_from_dir(dir string) []string {
|
||||||
|
mut res := []string
|
||||||
|
mut files := os.ls(dir)
|
||||||
|
if !os.file_exists(dir) {
|
||||||
|
panic('$dir doesnt exist')
|
||||||
|
}
|
||||||
|
if c.is_verbose {
|
||||||
|
println('v_files_from_dir ("$dir")')
|
||||||
|
}
|
||||||
|
// println(files.len)
|
||||||
|
// println(files)
|
||||||
|
files.sort()
|
||||||
|
for file in files {
|
||||||
|
c.log('F=$file')
|
||||||
|
if !file.ends_with('.v') && !file.ends_with('.vh') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if file.ends_with('_test.v') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if file.ends_with('_win.v') && c.os != WINDOWS {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if file.ends_with('_lin.v') && c.os != LINUX {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if file.ends_with('_mac.v') && c.os != MAC {
|
||||||
|
lin_file := file.replace('_mac.v', '_lin.v')
|
||||||
|
// println('lin_file="$lin_file"')
|
||||||
|
// If there are both _mac.v and _lin.v, don't use _mac.v
|
||||||
|
if os.file_exists('$dir/$lin_file') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
else if c.os == WINDOWS {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// If there's only _mac.v, then it can be used on Linux too
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res << '$dir/$file'
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses imports, adds necessary libs, and then user files
|
||||||
|
fn (c mut V) add_user_v_files() {
|
||||||
|
mut dir := c.dir
|
||||||
|
c.log('add_v_files($dir)')
|
||||||
|
// Need to store user files separately, because they have to be added after libs, but we dont know
|
||||||
|
// which libs need to be added yet
|
||||||
|
mut user_files := []string
|
||||||
|
// v volt/slack_test.v: compile all .v files to get the environment
|
||||||
|
// I need to implement user packages! TODO
|
||||||
|
is_test_with_imports := dir.ends_with('_test.v') &&
|
||||||
|
(dir.contains('/volt') || dir.contains('/c2volt'))// TODO
|
||||||
|
if is_test_with_imports {
|
||||||
|
user_files << dir
|
||||||
|
pos := dir.last_index('/')
|
||||||
|
dir = dir.left(pos) + '/'// TODO WHY IS THIS NEEDED?
|
||||||
|
}
|
||||||
|
if dir.ends_with('.v') {
|
||||||
|
// Just compile one file and get parent dir
|
||||||
|
user_files << dir
|
||||||
|
dir = dir.all_before('/')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Add files from the dir user is compiling (only .v files)
|
||||||
|
files := c.v_files_from_dir(dir)
|
||||||
|
for file in files {
|
||||||
|
user_files << file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if user_files.len == 0 {
|
||||||
|
exit('No input .v files')
|
||||||
|
}
|
||||||
|
if c.is_verbose {
|
||||||
|
c.log('user_files:')
|
||||||
|
println(user_files)
|
||||||
|
}
|
||||||
|
// Parse user imports
|
||||||
|
for file in user_files {
|
||||||
|
mut p := c.new_parser(file, RUN_IMPORTS)
|
||||||
|
p.parse()
|
||||||
|
}
|
||||||
|
// Parse lib imports
|
||||||
|
if c.build_mode == DEFAULT_MODE {
|
||||||
|
for i := 0; i < c.table.imports.len; i++ {
|
||||||
|
pkg := c.table.imports[i]
|
||||||
|
vfiles := c.v_files_from_dir('$TmpPath/vlib/$pkg')
|
||||||
|
// Add all imports referenced by these libs
|
||||||
|
for file in vfiles {
|
||||||
|
mut p := c.new_parser(file, RUN_IMPORTS)
|
||||||
|
p.parse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO this used to crash compiler?
|
||||||
|
// for pkg in c.table.imports {
|
||||||
|
for i := 0; i < c.table.imports.len; i++ {
|
||||||
|
pkg := c.table.imports[i]
|
||||||
|
// mut import_path := '$c.lang_dir/$pkg'
|
||||||
|
vfiles := c.v_files_from_dir('$c.lang_dir/$pkg')
|
||||||
|
// Add all imports referenced by these libs
|
||||||
|
for file in vfiles {
|
||||||
|
mut p := c.new_parser(file, RUN_IMPORTS)
|
||||||
|
p.parse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.is_verbose {
|
||||||
|
c.log('imports:')
|
||||||
|
println(c.table.imports)
|
||||||
|
}
|
||||||
|
// Only now add all combined lib files
|
||||||
|
for pkg in c.table.imports {
|
||||||
|
mut module_path := '$c.lang_dir/$pkg'
|
||||||
|
// If we are in default mode, we don't parse vlib .v files, but header .vh files in
|
||||||
|
// TmpPath/vlib
|
||||||
|
// These were generated by vfmt
|
||||||
|
if c.build_mode == DEFAULT_MODE || c.build_mode == BUILD {
|
||||||
|
module_path = '$TmpPath/vlib/$pkg'
|
||||||
|
}
|
||||||
|
vfiles := c.v_files_from_dir(module_path)
|
||||||
|
for vfile in vfiles {
|
||||||
|
c.files << vfile
|
||||||
|
}
|
||||||
|
// TODO c.files.append_array(vfiles)
|
||||||
|
}
|
||||||
|
// Add user code last
|
||||||
|
for file in user_files {
|
||||||
|
c.files << file
|
||||||
|
}
|
||||||
|
// c.files.append_array(user_files)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_arg(joined_args, arg, def string) string {
|
||||||
|
key := '-$arg '
|
||||||
|
mut pos := joined_args.index(key)
|
||||||
|
if pos == -1 {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
pos += key.len
|
||||||
|
mut space := joined_args.index_after(' ', pos)
|
||||||
|
if space == -1 {
|
||||||
|
space = joined_args.len
|
||||||
|
}
|
||||||
|
res := joined_args.substr(pos, space)
|
||||||
|
// println('get_arg($arg) = "$res"')
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c &V) log(s string) {
|
||||||
|
if !c.is_verbose {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
println(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_v(args[]string) *V {
|
||||||
|
mut dir := args.last()
|
||||||
|
// println('new compiler "$dir"')
|
||||||
|
if args.len < 2 {
|
||||||
|
dir = ''
|
||||||
|
}
|
||||||
|
joined_args := args.join(' ')
|
||||||
|
target_os := get_arg(joined_args, 'os', '')
|
||||||
|
mut out_name := get_arg(joined_args, 'o', 'a.out')
|
||||||
|
// build mode
|
||||||
|
mut build_mode := DEFAULT_MODE
|
||||||
|
if args.contains('-lib') {
|
||||||
|
build_mode = BUILD
|
||||||
|
// v -lib ~/v/os => os.o
|
||||||
|
base := dir.all_after('/')
|
||||||
|
println('Building module ${base}...')
|
||||||
|
out_name = '$TmpPath/vlib/${base}.o'
|
||||||
|
// Cross compiling? Use separate dirs for each os
|
||||||
|
if target_os != os.user_os() {
|
||||||
|
os.mkdir('$TmpPath/vlib/$target_os')
|
||||||
|
out_name = '$TmpPath/vlib/$target_os/${base}.o'
|
||||||
|
println('Cross compiling $out_name')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO embed_vlib is temporarily the default mode. It's much slower.
|
||||||
|
else if !args.contains('-embed_vlib') {
|
||||||
|
build_mode = EMBED_VLIB
|
||||||
|
}
|
||||||
|
//
|
||||||
|
is_test := dir.ends_with('_test.v')
|
||||||
|
is_script := dir.ends_with('.v')
|
||||||
|
if is_script && !os.file_exists(dir) {
|
||||||
|
exit('`$dir` does not exist')
|
||||||
|
}
|
||||||
|
// No -o provided? foo.v => foo
|
||||||
|
if out_name == 'a.out' && dir.ends_with('.v') {
|
||||||
|
out_name = dir.left(dir.len - 2)
|
||||||
|
}
|
||||||
|
// if we are in `/foo` and run `v .`, the executable should be `foo`
|
||||||
|
if dir == '.' && out_name == 'a.out' {
|
||||||
|
base := os.getwd().all_after('/')
|
||||||
|
out_name = base.trim_space()
|
||||||
|
}
|
||||||
|
mut _os := MAC
|
||||||
|
// No OS specifed? Use current system
|
||||||
|
if target_os == '' {
|
||||||
|
$if linux {
|
||||||
|
_os = LINUX
|
||||||
|
}
|
||||||
|
$if mac {
|
||||||
|
_os = MAC
|
||||||
|
}
|
||||||
|
$if windows {
|
||||||
|
_os = WINDOWS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch target_os {
|
||||||
|
case 'linux': _os = LINUX
|
||||||
|
case 'windows': _os = WINDOWS
|
||||||
|
case 'mac': _os = MAC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builtins := [
|
||||||
|
'array.v',
|
||||||
|
'string.v',
|
||||||
|
'builtin.v',
|
||||||
|
'int.v',
|
||||||
|
'utf8.v',
|
||||||
|
'map.v',
|
||||||
|
'smap.v',
|
||||||
|
'option.v',
|
||||||
|
'string_builder.v',
|
||||||
|
]
|
||||||
|
// Location of all vlib files TODO allow custom location
|
||||||
|
mut lang_dir = os.home_dir() + '/code/v/'
|
||||||
|
out_name_c := out_name.all_after('/') + '.c'
|
||||||
|
mut files := []string
|
||||||
|
// Add builtin files
|
||||||
|
if !out_name.contains('builtin.o') {
|
||||||
|
for builtin in builtins {
|
||||||
|
mut f := '$lang_dir/builtin/$builtin'
|
||||||
|
// In default mode we use precompiled vlib.o, point to .vh files with signatures
|
||||||
|
if build_mode == DEFAULT_MODE || build_mode == BUILD {
|
||||||
|
f = '$TmpPath/vlib/builtin/${builtin}h'
|
||||||
|
}
|
||||||
|
files << f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obfuscate := args.contains('-obf')
|
||||||
|
return &V {
|
||||||
|
os: _os
|
||||||
|
out_name: out_name
|
||||||
|
files: files
|
||||||
|
dir: dir
|
||||||
|
lang_dir: lang_dir
|
||||||
|
table: new_table(obfuscate)
|
||||||
|
out_name: out_name
|
||||||
|
out_name_c: out_name_c
|
||||||
|
is_test: is_test
|
||||||
|
is_script: is_script
|
||||||
|
is_so: args.contains('-shared')
|
||||||
|
is_play: args.contains('play')
|
||||||
|
is_prod: args.contains('-prod')
|
||||||
|
is_verbose: args.contains('-verbose')
|
||||||
|
obfuscate: obfuscate
|
||||||
|
is_prof: args.contains('-prof')
|
||||||
|
is_live: args.contains('-live')
|
||||||
|
sanitize: args.contains('-sanitize')
|
||||||
|
nofmt: args.contains('-nofmt')
|
||||||
|
show_c_cmd: args.contains('-show_c_cmd')
|
||||||
|
translated: args.contains('translated')
|
||||||
|
cgen: new_cgen(out_name_c)
|
||||||
|
build_mode: build_mode
|
||||||
|
is_run: args.contains('run')
|
||||||
|
is_repl: args.contains('-repl')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_repl() []string {
|
||||||
|
println2('V $Version')
|
||||||
|
println2('Use Ctrl-D to exit')
|
||||||
|
println2('For now you have to use println() to print values, this will be fixed soon\n')
|
||||||
|
file := TmpPath + '/vrepl.v'
|
||||||
|
mut lines := []string
|
||||||
|
for {
|
||||||
|
print('>>> ')
|
||||||
|
mut line := os.get_line().trim_space()
|
||||||
|
if line == '' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Save the source only if the user is printing something,
|
||||||
|
// but don't add this print call to the `lines` array,
|
||||||
|
// so that it doesn't get called during the next print.
|
||||||
|
if line.starts_with('print') {
|
||||||
|
// TODO remove this once files without main compile correctly
|
||||||
|
source_code := 'fn main(){' + lines.join('\n') + '\n' + line + '}'
|
||||||
|
os.write_file(file, source_code)
|
||||||
|
mut v := new_v( ['v', '-repl', file])
|
||||||
|
v.compile()
|
||||||
|
s := os.system(TmpPath + '/vrepl')
|
||||||
|
println2(s)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lines << line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
// This definitely needs to be better :)
|
||||||
|
const (
|
||||||
|
HelpText = '
|
||||||
|
- To build a V program:
|
||||||
|
v file.v
|
||||||
|
|
||||||
|
- To get current V version:
|
||||||
|
v version
|
||||||
|
|
||||||
|
- To build an optimized executable:
|
||||||
|
v -prod file.v
|
||||||
|
|
||||||
|
- To specify the executable\'s name:
|
||||||
|
v -o program file.v
|
||||||
|
'
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
- To disable automatic formatting:
|
||||||
|
v -nofmt file.v
|
||||||
|
|
||||||
|
- To build a program with an embedded vlib (use this if you do not have prebuilt vlib libraries or if you
|
||||||
|
are working on vlib)
|
||||||
|
v -embed_vlib file.v
|
||||||
|
*/
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,630 @@
|
||||||
|
module main
|
||||||
|
|
||||||
|
struct Scanner {
|
||||||
|
mut:
|
||||||
|
file_path string
|
||||||
|
text string
|
||||||
|
pos int
|
||||||
|
line_nr int
|
||||||
|
inside_string bool
|
||||||
|
dollar_start bool // for hacky string interpolation TODO simplify
|
||||||
|
dollar_end bool
|
||||||
|
debug bool
|
||||||
|
line_comment string
|
||||||
|
started bool
|
||||||
|
is_fmt bool
|
||||||
|
// vfmt fields
|
||||||
|
fmt_out StringBuilder
|
||||||
|
fmt_indent int
|
||||||
|
fmt_line_empty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
SINGLE_QUOTE = `\'`
|
||||||
|
QUOTE = `"`
|
||||||
|
)
|
||||||
|
|
||||||
|
fn new_scanner(file_path string) *Scanner {
|
||||||
|
if !os.file_exists(file_path) {
|
||||||
|
panic('"$file_path" doesnt exist')
|
||||||
|
}
|
||||||
|
scanner := &Scanner {
|
||||||
|
file_path: file_path
|
||||||
|
text: os.read_file(file_path)
|
||||||
|
fmt_out: new_string_builder(1000)
|
||||||
|
}
|
||||||
|
// println('new scanner "$file_path" txt.len=$scanner.text.len')
|
||||||
|
return scanner
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO remove once multiple return values are implemented
|
||||||
|
struct ScanRes {
|
||||||
|
tok Token
|
||||||
|
lit string
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scan_res(tok Token, lit string) ScanRes {
|
||||||
|
return ScanRes{tok, lit}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_white(c byte) bool {
|
||||||
|
return c.is_white()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_nl(c byte) bool {
|
||||||
|
i := int(c)
|
||||||
|
return i == 12 || i == 10
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (s mut Scanner) ident_name() string {
|
||||||
|
start := s.pos
|
||||||
|
for {
|
||||||
|
s.pos++
|
||||||
|
c := s.text[s.pos]
|
||||||
|
if !is_name_char(c) && !c.is_digit() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
name := s.text.substr(start, s.pos)
|
||||||
|
s.pos--
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (s mut Scanner) ident_number() string {
|
||||||
|
start := s.pos
|
||||||
|
is_hex := s.text[s.pos] == `0` && s.text[s.pos + 1] == `x`
|
||||||
|
is_oct := !is_hex && s.text[s.pos] == `0`
|
||||||
|
mut is_float := false
|
||||||
|
for {
|
||||||
|
s.pos++
|
||||||
|
c := s.text[s.pos]
|
||||||
|
if c == `.` {
|
||||||
|
is_float = true
|
||||||
|
}
|
||||||
|
is_good_hex := is_hex && (c == `x` || c == `u` || (c >= `a` && c <= `f`))
|
||||||
|
// 1e+3, 1e-3, 1e3
|
||||||
|
if !is_hex && c == `e` {
|
||||||
|
next := s.text[s.pos + 1]
|
||||||
|
if next == `+` || next == `-` || next.is_digit() {
|
||||||
|
s.pos++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !c.is_digit() && c != `.` && !is_good_hex {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// 1..9
|
||||||
|
if c == `.` && s.text[s.pos + 1] == `.` {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if is_oct && c >= `8` && !is_float {
|
||||||
|
s.error('malformed octal constant')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
number := s.text.substr(start, s.pos)
|
||||||
|
s.pos--
|
||||||
|
return number
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (s mut Scanner) skip_whitespace() {
|
||||||
|
for s.pos < s.text.len && is_white(s.text[s.pos]) {
|
||||||
|
if is_nl(s.text[s.pos]) {
|
||||||
|
s.line_nr++
|
||||||
|
if s.is_fmt {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.pos++
|
||||||
|
}
|
||||||
|
// if s.pos == s.text.len {
|
||||||
|
// return scan_res(EOF, '')
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (s mut Scanner) scan() ScanRes {
|
||||||
|
// if s.file_path == 'd.v' {
|
||||||
|
// println('\nscan()')
|
||||||
|
// }
|
||||||
|
// if s.started {
|
||||||
|
if s.pos > 0 {
|
||||||
|
// || (s.pos == 0 && s.text.len > 0 && s.text[s.pos] == `\n`) {
|
||||||
|
s.pos++
|
||||||
|
}
|
||||||
|
s.started = true
|
||||||
|
if s.pos >= s.text.len {
|
||||||
|
return scan_res(EOF, '')
|
||||||
|
}
|
||||||
|
// skip whitespace
|
||||||
|
if !s.inside_string {
|
||||||
|
s.skip_whitespace()
|
||||||
|
}
|
||||||
|
if s.is_fmt && s.text[s.pos] == `\n` {
|
||||||
|
return scan_res(NL, '')
|
||||||
|
}
|
||||||
|
// End of $var, start next string
|
||||||
|
if !s.is_fmt && s.dollar_end {
|
||||||
|
// fmt.Println("end of $var, get string", s.pos, string(s.text[s.pos]))
|
||||||
|
if s.text[s.pos] == SINGLE_QUOTE {
|
||||||
|
// fmt.Println("ENDDD")
|
||||||
|
s.dollar_end = false
|
||||||
|
return scan_res(STRING, '')
|
||||||
|
}
|
||||||
|
s.dollar_end = false
|
||||||
|
return scan_res(STRING, s.ident_string())
|
||||||
|
}
|
||||||
|
s.skip_whitespace()
|
||||||
|
// println('ws skipped')
|
||||||
|
// end of file
|
||||||
|
if s.pos >= s.text.len {
|
||||||
|
// println('scan(): returning EOF (pos >= len)')
|
||||||
|
return scan_res(EOF, '')
|
||||||
|
}
|
||||||
|
// println('!!!!! HANDLE CHAR pos=$s.pos')
|
||||||
|
// handle each char
|
||||||
|
c := s.text[s.pos]
|
||||||
|
mut nextc := `\0`
|
||||||
|
if s.pos + 1 < s.text.len {
|
||||||
|
nextc = s.text[s.pos + 1]
|
||||||
|
}
|
||||||
|
// name or keyword
|
||||||
|
if is_name_char(c) {
|
||||||
|
name := s.ident_name()
|
||||||
|
next_char := s.text[s.pos + 1]// tmp hack to detect . in ${}
|
||||||
|
// println('!!! got name=$name next_char=$next_char')
|
||||||
|
if is_key(name) {
|
||||||
|
// println('IS KEY')
|
||||||
|
// tok := (key_to_token(name))
|
||||||
|
// println(tok.str())
|
||||||
|
return scan_res(key_to_token(name), '')
|
||||||
|
}
|
||||||
|
// 'asdf $b' => "b" is the last name in the string, dont start parsing string
|
||||||
|
// at the next ', skip it
|
||||||
|
if s.inside_string {
|
||||||
|
// println('is_letter inside string! nextc=${nextc.str()}')
|
||||||
|
if s.text[s.pos + 1] == SINGLE_QUOTE {
|
||||||
|
// println('var is last before QUOTE')
|
||||||
|
s.pos++
|
||||||
|
s.dollar_start = false
|
||||||
|
s.inside_string = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.dollar_start && next_char != `.` {
|
||||||
|
// println('INSIDE STRING .dollar var=$name')
|
||||||
|
s.dollar_end = true
|
||||||
|
s.dollar_start = false
|
||||||
|
}
|
||||||
|
return scan_res(NAME, name)
|
||||||
|
}
|
||||||
|
// number, `.123`
|
||||||
|
else if c.is_digit() || c == `.` && nextc.is_digit() {
|
||||||
|
num := s.ident_number()
|
||||||
|
return scan_res(INT, num)
|
||||||
|
}
|
||||||
|
// all other tokens
|
||||||
|
switch c {
|
||||||
|
case `+`:
|
||||||
|
if nextc == `+` {
|
||||||
|
s.pos++
|
||||||
|
return scan_res(INC, '')
|
||||||
|
}
|
||||||
|
else if nextc == `=` {
|
||||||
|
s.pos++
|
||||||
|
return scan_res(PLUS_ASSIGN, '')
|
||||||
|
}
|
||||||
|
return scan_res(PLUS, '')
|
||||||
|
case `-`:
|
||||||
|
if nextc == `-` {
|
||||||
|
s.pos++
|
||||||
|
return scan_res(DEC, '')
|
||||||
|
}
|
||||||
|
else if nextc == `=` {
|
||||||
|
s.pos++
|
||||||
|
return scan_res(MINUS_ASSIGN, '')
|
||||||
|
}
|
||||||
|
return scan_res(MINUS, '')
|
||||||
|
case `*`:
|
||||||
|
if nextc == `=` {
|
||||||
|
s.pos++
|
||||||
|
return scan_res(MULT_ASSIGN, '')
|
||||||
|
}
|
||||||
|
return scan_res(MUL, '')
|
||||||
|
case `^`:
|
||||||
|
if nextc == `=` {
|
||||||
|
s.pos++
|
||||||
|
return scan_res(XOR_ASSIGN, '')
|
||||||
|
}
|
||||||
|
return scan_res(XOR, '')
|
||||||
|
case `%`:
|
||||||
|
if nextc == `=` {
|
||||||
|
s.pos++
|
||||||
|
return scan_res(MOD_ASSIGN, '')
|
||||||
|
}
|
||||||
|
return scan_res(MOD, '')
|
||||||
|
case `?`:
|
||||||
|
return scan_res(QUESTION, '')
|
||||||
|
case SINGLE_QUOTE:
|
||||||
|
return scan_res(STRING, s.ident_string())
|
||||||
|
// TODO allow double quotes
|
||||||
|
// case QUOTE:
|
||||||
|
// return scan_res(STRING, s.ident_string())
|
||||||
|
case `\``:
|
||||||
|
return scan_res(CHAR, s.ident_char())
|
||||||
|
case `(`:
|
||||||
|
return scan_res(LPAR, '')
|
||||||
|
case `)`:
|
||||||
|
return scan_res(RPAR, '')
|
||||||
|
case `[`:
|
||||||
|
return scan_res(LSBR, '')
|
||||||
|
case `]`:
|
||||||
|
return scan_res(RSBR, '')
|
||||||
|
case `{`:
|
||||||
|
// Skip { in ${ in strings
|
||||||
|
if s.inside_string {
|
||||||
|
return s.scan()
|
||||||
|
}
|
||||||
|
return scan_res(LCBR, '')
|
||||||
|
case `$`:
|
||||||
|
return scan_res(DOLLAR, '')
|
||||||
|
case `}`:
|
||||||
|
// s = `hello $name kek`
|
||||||
|
// s = `hello ${name} kek`
|
||||||
|
if s.inside_string {
|
||||||
|
s.pos++
|
||||||
|
// TODO UNNEEDED?
|
||||||
|
if s.text[s.pos] == SINGLE_QUOTE {
|
||||||
|
s.inside_string = false
|
||||||
|
return scan_res(STRING, '')
|
||||||
|
}
|
||||||
|
return scan_res(STRING, s.ident_string())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return scan_res(RCBR, '')
|
||||||
|
}
|
||||||
|
case `&`:
|
||||||
|
if nextc == `=` {
|
||||||
|
s.pos++
|
||||||
|
return scan_res(AND_ASSIGN, '')
|
||||||
|
}
|
||||||
|
if s.text[s.pos + 1] == `&` {
|
||||||
|
s.pos++
|
||||||
|
return scan_res(AND, '')
|
||||||
|
}
|
||||||
|
return scan_res(AMP, '')
|
||||||
|
case `|`:
|
||||||
|
if s.text[s.pos + 1] == `|` {
|
||||||
|
s.pos++
|
||||||
|
return scan_res(OR, '')
|
||||||
|
}
|
||||||
|
if nextc == `=` {
|
||||||
|
s.pos++
|
||||||
|
return scan_res(OR_ASSIGN, '')
|
||||||
|
}
|
||||||
|
return scan_res(PIPE, '')
|
||||||
|
case `,`:
|
||||||
|
return scan_res(COMMA, '')
|
||||||
|
case `\n`:
|
||||||
|
return scan_res(NL, '')
|
||||||
|
case `.`:
|
||||||
|
if s.text[s.pos + 1] == `.` {
|
||||||
|
s.pos++
|
||||||
|
return scan_res(DOTDOT, '')
|
||||||
|
}
|
||||||
|
return scan_res(DOT, '')
|
||||||
|
case `#`:
|
||||||
|
start := s.pos + 1
|
||||||
|
for s.text[s.pos] != `\n` {
|
||||||
|
s.pos++
|
||||||
|
}
|
||||||
|
s.line_nr++
|
||||||
|
hash := s.text.substr(start, s.pos)
|
||||||
|
if s.is_fmt {
|
||||||
|
// fmt needs NL after #
|
||||||
|
s.pos--
|
||||||
|
}
|
||||||
|
return scan_res(HASH, hash.trim_space())
|
||||||
|
case `@`:
|
||||||
|
start := s.pos + 1
|
||||||
|
for s.text[s.pos] != `\n` {
|
||||||
|
s.pos++
|
||||||
|
}
|
||||||
|
s.line_nr++
|
||||||
|
at := s.text.substr(start, s.pos)
|
||||||
|
return scan_res(AT, at.trim_space())
|
||||||
|
case `>`:
|
||||||
|
if s.text[s.pos + 1] == `=` {
|
||||||
|
s.pos++
|
||||||
|
return scan_res(GE, '')
|
||||||
|
}
|
||||||
|
else if s.text[s.pos + 1] == `>` {
|
||||||
|
if s.text[s.pos + 2] == `=` {
|
||||||
|
s.pos += 2
|
||||||
|
return scan_res(RIGHT_SHIFT_ASSIGN, '')
|
||||||
|
}
|
||||||
|
s.pos++
|
||||||
|
return scan_res(RIGHT_SHIFT, '')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return scan_res(GT, '')
|
||||||
|
}
|
||||||
|
case `<`:
|
||||||
|
if s.text[s.pos + 1] == `=` {
|
||||||
|
s.pos++
|
||||||
|
return scan_res(LE, '')
|
||||||
|
}
|
||||||
|
else if s.text[s.pos + 1] == `<` {
|
||||||
|
if s.text[s.pos + 2] == `=` {
|
||||||
|
s.pos += 2
|
||||||
|
return scan_res(LEFT_SHIFT_ASSIGN, '')
|
||||||
|
}
|
||||||
|
s.pos++
|
||||||
|
return scan_res(LEFT_SHIFT, '')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return scan_res(LT, '')
|
||||||
|
}
|
||||||
|
case `=`:
|
||||||
|
if s.text[s.pos + 1] == `=` {
|
||||||
|
s.pos++
|
||||||
|
return scan_res(EQ, '')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return scan_res(ASSIGN, '')
|
||||||
|
}
|
||||||
|
case `:`:
|
||||||
|
if s.text[s.pos + 1] == `=` {
|
||||||
|
s.pos++
|
||||||
|
return scan_res(DECL_ASSIGN, '')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return scan_res(COLON, '')
|
||||||
|
}
|
||||||
|
case `;`:
|
||||||
|
return scan_res(SEMICOLON, '')
|
||||||
|
case `!`:
|
||||||
|
if s.text[s.pos + 1] == `=` {
|
||||||
|
s.pos++
|
||||||
|
return scan_res(NE, '')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return scan_res(NOT, '')
|
||||||
|
}
|
||||||
|
case `~`:
|
||||||
|
return scan_res(BIT_NOT, '')
|
||||||
|
case `/`:
|
||||||
|
if nextc == `=` {
|
||||||
|
s.pos++
|
||||||
|
return scan_res(DIV_ASSIGN, '')
|
||||||
|
}
|
||||||
|
if s.text[s.pos + 1] == `/` {
|
||||||
|
// debug("!!!!!!GOT LINE COM")
|
||||||
|
start := s.pos + 1
|
||||||
|
for s.text[s.pos] != `\n` {
|
||||||
|
s.pos++
|
||||||
|
}
|
||||||
|
s.line_nr++
|
||||||
|
s.line_comment = s.text.substr(start + 1, s.pos)
|
||||||
|
s.line_comment = s.line_comment.trim_space()
|
||||||
|
s.fgenln('// $s.line_comment')
|
||||||
|
if s.is_fmt {
|
||||||
|
// fmt needs NL after comment
|
||||||
|
s.pos--
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Skip comment
|
||||||
|
return s.scan()
|
||||||
|
}
|
||||||
|
return scan_res(LINE_COM, s.line_comment)
|
||||||
|
}
|
||||||
|
// Multiline comments
|
||||||
|
if s.text[s.pos + 1] == `*` {
|
||||||
|
start := s.pos
|
||||||
|
// Skip comment
|
||||||
|
for ! (s.text[s.pos] == `*` && s.text[s.pos + 1] == `/`) {
|
||||||
|
s.pos++
|
||||||
|
if s.pos >= s.text.len {
|
||||||
|
s.line_nr--
|
||||||
|
s.error('comment not terminated')
|
||||||
|
}
|
||||||
|
if s.text[s.pos] == `\n` {
|
||||||
|
s.line_nr++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.pos++
|
||||||
|
end := s.pos + 1
|
||||||
|
comm := s.text.substr(start, end)
|
||||||
|
s.fgenln(comm)
|
||||||
|
if s.is_fmt {
|
||||||
|
return scan_res(MLINE_COM, comm)
|
||||||
|
}
|
||||||
|
// Skip if not in fmt mode
|
||||||
|
return s.scan()
|
||||||
|
}
|
||||||
|
return scan_res(DIV, '')
|
||||||
|
}
|
||||||
|
println2('(char code=$c) pos=$s.pos len=$s.text.len')
|
||||||
|
s.error('invalid character `${c.str()}`')
|
||||||
|
return scan_res(EOF, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (s &Scanner) error(msg string) {
|
||||||
|
// println('!! SCANNER ERROR: $msg')
|
||||||
|
file := s.file_path.all_after('/')
|
||||||
|
println2('panic: $file:${s.line_nr + 1}')
|
||||||
|
println2(msg)
|
||||||
|
// os.print_backtrace()
|
||||||
|
// println(file)
|
||||||
|
// println(s.file_path)
|
||||||
|
os.exit1(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
// println2('array out of bounds $idx len=$a.len')
|
||||||
|
// This is really bad. It needs a major clean up
|
||||||
|
fn (s mut Scanner) ident_string() string {
|
||||||
|
// println("\nidentString() at char=", string(s.text[s.pos]),
|
||||||
|
// "chard=", s.text[s.pos], " pos=", s.pos, "txt=", s.text[s.pos:s.pos+7])
|
||||||
|
debug := s.file_path.contains('test_test')
|
||||||
|
if debug {
|
||||||
|
println('identStr() $s.file_path line=$s.line_nr pos=$s.pos')
|
||||||
|
}
|
||||||
|
mut start := s.pos
|
||||||
|
s.inside_string = false
|
||||||
|
slash := `\\`
|
||||||
|
for {
|
||||||
|
s.pos++
|
||||||
|
if s.pos >= s.text.len {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
c := s.text[s.pos]
|
||||||
|
if debug {
|
||||||
|
println(c.str())
|
||||||
|
}
|
||||||
|
prevc := s.text[s.pos - 1]
|
||||||
|
// end of string
|
||||||
|
if c == SINGLE_QUOTE && (prevc != slash || (prevc == slash && s.text[s.pos - 2] == slash)) {
|
||||||
|
// handle '123\\' slash at the end
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if c == `\n` {
|
||||||
|
s.line_nr++
|
||||||
|
}
|
||||||
|
// Don't allow \0
|
||||||
|
if c == `0` && s.pos > 2 && s.text[s.pos - 1] == `\\` {
|
||||||
|
s.error('0 character in a string literal')
|
||||||
|
}
|
||||||
|
// Don't allow \x00
|
||||||
|
if c == `0` && s.pos > 5 && s.text[s.pos - 1] == `0` && s.text[s.pos - 2] == `x` &&
|
||||||
|
s.text[s.pos - 3] == `\\` {
|
||||||
|
s.error('0 character in a string literal')
|
||||||
|
}
|
||||||
|
// ${var}
|
||||||
|
if !s.is_fmt && c == `{` && prevc == `$` {
|
||||||
|
s.inside_string = true
|
||||||
|
// fmt.Println("breaking out of is()")
|
||||||
|
// so that s.pos points to $ at the next step
|
||||||
|
s.pos -= 2
|
||||||
|
// fmt.Println("break pos=", s.pos, "c=", string(s.text[s.pos]), "d=", s.text[s.pos])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// $var
|
||||||
|
// if !s.is_fmt && c != `{` && c != ` ` && ! (c >= `0` && c <= `9`) && prevc == `$` {
|
||||||
|
if !s.is_fmt && (c.is_letter() || c == `_`) && prevc == `$` {
|
||||||
|
s.inside_string = true
|
||||||
|
s.dollar_start = true
|
||||||
|
// println('setting s.dollar=true pos=$s.pos')
|
||||||
|
s.pos -= 2
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mut lit := ''
|
||||||
|
if s.text[start] == SINGLE_QUOTE {
|
||||||
|
start++
|
||||||
|
}
|
||||||
|
mut end := s.pos
|
||||||
|
if s.inside_string {
|
||||||
|
end++
|
||||||
|
}
|
||||||
|
if start > s.pos{}
|
||||||
|
else {
|
||||||
|
lit = s.text.substr(start, end)
|
||||||
|
}
|
||||||
|
// if lit.contains('\n') {
|
||||||
|
// println('\nstring lit="$lit" pos=$s.pos line=$s.line_nr')
|
||||||
|
// }
|
||||||
|
/*
|
||||||
|
for c in lit {
|
||||||
|
if s.file_path.contains('range_test') {
|
||||||
|
println('!')
|
||||||
|
println(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return lit
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (s mut Scanner) ident_char() string {
|
||||||
|
start := s.pos
|
||||||
|
slash := `\\`
|
||||||
|
mut len := 0
|
||||||
|
for {
|
||||||
|
s.pos++
|
||||||
|
if s.pos >= s.text.len {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if s.text[s.pos] != slash {
|
||||||
|
len++
|
||||||
|
}
|
||||||
|
double_slash := s.text[s.pos - 1] == slash && s.text[s.pos - 2] == slash
|
||||||
|
if s.text[s.pos] == `\`` && (s.text[s.pos - 1] != slash || double_slash) {
|
||||||
|
if double_slash {
|
||||||
|
len++
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
len--
|
||||||
|
c := s.text.substr(start + 1, s.pos)
|
||||||
|
if len != 1 {
|
||||||
|
s.error('invalid character literal (more than one character: $len)')
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) peek() Token {
|
||||||
|
for {
|
||||||
|
tok := p.scanner.peek()
|
||||||
|
if tok != NL {
|
||||||
|
return tok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (s mut Scanner) peek() Token {
|
||||||
|
pos := s.pos
|
||||||
|
line := s.line_nr
|
||||||
|
inside_string := s.inside_string
|
||||||
|
dollar_start := s.dollar_start
|
||||||
|
dollar_end := s.dollar_end
|
||||||
|
// /////
|
||||||
|
res := s.scan()
|
||||||
|
tok := res.tok
|
||||||
|
s.pos = pos
|
||||||
|
s.line_nr = line
|
||||||
|
s.inside_string = inside_string
|
||||||
|
s.dollar_start = dollar_start
|
||||||
|
s.dollar_end = dollar_end
|
||||||
|
return tok
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (s mut Scanner) debug_tokens() {
|
||||||
|
s.pos = 0
|
||||||
|
fname := s.file_path.all_after('/')
|
||||||
|
println('\n===DEBUG TOKENS $fname ============')
|
||||||
|
// allToks := ''
|
||||||
|
s.debug = true
|
||||||
|
for {
|
||||||
|
res := s.scan()
|
||||||
|
tok := res.tok
|
||||||
|
lit := res.lit
|
||||||
|
// printiln(tok)
|
||||||
|
print(tok.str())
|
||||||
|
// allToks += tok.String()
|
||||||
|
if lit != '' {
|
||||||
|
println(' `$lit`')
|
||||||
|
// allToks += " `" + lit + "`"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
println('')
|
||||||
|
}
|
||||||
|
// allToks += "\n"
|
||||||
|
if tok == EOF {
|
||||||
|
println('============ END OF DEBUG TOKENS ==================')
|
||||||
|
// fmt.Println("========"+s.file+"========\n", allToks)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_name_char(c byte) bool {
|
||||||
|
return c.is_letter() || c == `_`
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,644 @@
|
||||||
|
module main
|
||||||
|
|
||||||
|
struct Table {
|
||||||
|
mut:
|
||||||
|
types []Type
|
||||||
|
consts []Var
|
||||||
|
fns []Fn
|
||||||
|
obf_ids map_int // obf_ids 'myfunction'] == 23
|
||||||
|
packages []string // List of all modules registered by the application
|
||||||
|
imports []string // List of all imports
|
||||||
|
flags []string // ['-framework Cocoa', '-lglfw3']
|
||||||
|
fn_cnt int atomic
|
||||||
|
obfuscate bool
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AccessMod {
|
||||||
|
PRIVATE // private immutable
|
||||||
|
PRIVET_MUT // private mutable
|
||||||
|
PUBLIC // public immmutable (readonly)
|
||||||
|
PUBLIC_MUT // public, but mutable only in this module
|
||||||
|
PUBLIC_MUT_MUT // public and mutable both inside and outside (not recommended to use, that's why it's so verbose)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TypeCategory {
|
||||||
|
TYPE_STRUCT
|
||||||
|
T_CAT_FN
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Type {
|
||||||
|
mut:
|
||||||
|
pkg string
|
||||||
|
name string
|
||||||
|
fields []Var
|
||||||
|
methods []Fn
|
||||||
|
parent string
|
||||||
|
cat TypeCategory
|
||||||
|
gen_types []string
|
||||||
|
func Fn // For cat == FN (type kek fn())
|
||||||
|
is_c bool // C.FILE
|
||||||
|
is_interface bool
|
||||||
|
is_enum bool
|
||||||
|
// This field is used for types that are not defined yet but are known to exist.
|
||||||
|
// It allows having things like `fn (f Foo) bar()` before `Foo` is defined.
|
||||||
|
// This information is needed in the first pass.
|
||||||
|
is_placeholder bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// For debugging types
|
||||||
|
fn (t Type) str() string {
|
||||||
|
mut s := 'type "$t.name" {'
|
||||||
|
if t.fields.len > 0 {
|
||||||
|
// s += '\n $t.fields.len fields:\n'
|
||||||
|
for field in t.fields {
|
||||||
|
s += '\n $field.name $field.typ'
|
||||||
|
}
|
||||||
|
s += '\n'
|
||||||
|
}
|
||||||
|
if t.methods.len > 0 {
|
||||||
|
// s += '\n $t.methods.len methods:\n'
|
||||||
|
for method in t.methods {
|
||||||
|
s += '\n ${method.str()}'
|
||||||
|
}
|
||||||
|
s += '\n'
|
||||||
|
}
|
||||||
|
s += '}\n'
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
CReserved = [
|
||||||
|
'exit',
|
||||||
|
'unix',
|
||||||
|
'print',
|
||||||
|
// 'ok',
|
||||||
|
'error',
|
||||||
|
'malloc',
|
||||||
|
'calloc',
|
||||||
|
'char',
|
||||||
|
'free',
|
||||||
|
'panic',
|
||||||
|
'register'
|
||||||
|
]
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is used in generated C code
|
||||||
|
fn (f Fn) str() string {
|
||||||
|
t := Table{}
|
||||||
|
str_args := f.str_args(t)
|
||||||
|
return '$f.name($str_args) $f.typ'
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn (types array_Type) print_to_file(f string) {
|
||||||
|
// }
|
||||||
|
const (
|
||||||
|
NUMBER_TYPES = ['number', 'int', 'i8', 'u8', 'i16', 'u16', 'i32', 'u32', 'byte', 'i64', 'u64', 'long', 'double', 'float', 'f32', 'f64']
|
||||||
|
FLOAT_TYPES = ['double', 'float', 'f32', 'f64']
|
||||||
|
)
|
||||||
|
|
||||||
|
fn is_number_type(typ string) bool {
|
||||||
|
return NUMBER_TYPES.contains(typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_float_type(typ string) bool {
|
||||||
|
return FLOAT_TYPES.contains(typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_table(obfuscate bool) *Table {
|
||||||
|
mut t := &Table {
|
||||||
|
obf_ids: map[string]int{}
|
||||||
|
obfuscate: obfuscate
|
||||||
|
}
|
||||||
|
t.register_type('int')
|
||||||
|
t.register_type('size_t')
|
||||||
|
t.register_type_with_parent('i8', 'int')
|
||||||
|
t.register_type_with_parent('u8', 'int')
|
||||||
|
t.register_type_with_parent('i16', 'int')
|
||||||
|
t.register_type_with_parent('u16', 'int')
|
||||||
|
t.register_type_with_parent('i32', 'int')
|
||||||
|
t.register_type_with_parent('u32', 'int')
|
||||||
|
t.register_type_with_parent('byte', 'int')
|
||||||
|
// t.register_type_with_parent('i64', 'int')
|
||||||
|
t.register_type('i64')
|
||||||
|
t.register_type_with_parent('u64', 'int')
|
||||||
|
t.register_type('long')
|
||||||
|
t.register_type('byteptr')
|
||||||
|
t.register_type('intptr')
|
||||||
|
t.register_type('double')// TODO remove
|
||||||
|
t.register_type('float')// TODO remove
|
||||||
|
t.register_type('f32')
|
||||||
|
t.register_type('f64')
|
||||||
|
t.register_type('rune')
|
||||||
|
t.register_type('bool')
|
||||||
|
t.register_type('void')
|
||||||
|
t.register_type('voidptr')
|
||||||
|
t.register_type('va_list')
|
||||||
|
t.register_const('stdin', 'int', 'main', false)
|
||||||
|
t.register_const('stderr', 'int', 'main', false)
|
||||||
|
t.register_type_with_parent('map_string', 'map')
|
||||||
|
t.register_type_with_parent('map_int', 'map')
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// If `name` is a reserved C keyword, returns `v_name` instead.
|
||||||
|
fn (t mut Table) var_cgen_name(name string) string {
|
||||||
|
if CReserved.contains(name) {
|
||||||
|
return 'v_$name'
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t mut Table) register_package(pkg string) {
|
||||||
|
if t.packages.contains(pkg) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.packages << pkg
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (table &Table) known_pkg(pkg string) bool {
|
||||||
|
return pkg in table.packages
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t mut Table) register_const(name, typ string, pkg string, is_imported bool) {
|
||||||
|
t.consts << Var {
|
||||||
|
name: name
|
||||||
|
typ: typ
|
||||||
|
is_const: true
|
||||||
|
is_import_const: is_imported
|
||||||
|
pkg: pkg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only for translated code
|
||||||
|
fn (p mut Parser) register_global(name, typ string) {
|
||||||
|
p.table.consts << Var {
|
||||||
|
name: name
|
||||||
|
typ: typ
|
||||||
|
is_const: true
|
||||||
|
is_global: true
|
||||||
|
pkg: p.pkg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO PERF O(N) this slows down the comiler a lot!
|
||||||
|
fn (t mut Table) register_fn(f Fn) {
|
||||||
|
// Avoid duplicate fn names TODO why? the name should already be unique?
|
||||||
|
for ff in t.fns {
|
||||||
|
if ff.name == f.name {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.fns << f
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (table &Table) known_type(typ string) bool {
|
||||||
|
// 'byte*' => look up 'byte', but don't mess up fns
|
||||||
|
if typ.ends_with('*') && !typ.contains(' ') {
|
||||||
|
typ = typ.left(typ.len - 1)
|
||||||
|
}
|
||||||
|
for t in table.types {
|
||||||
|
if t.name == typ && !t.is_placeholder {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO PERF O(N) this slows down the comiler a lot!
|
||||||
|
fn (t &Table) find_fn(name string) Fn {
|
||||||
|
for f in t.fns {
|
||||||
|
if f.name == name {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Fn{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO PERF O(N) this slows down the comiler a lot!
|
||||||
|
fn (t &Table) known_fn(name string) bool {
|
||||||
|
for f in t.fns {
|
||||||
|
if f.name == name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t &Table) known_const(name string) bool {
|
||||||
|
v := t.find_const(name)
|
||||||
|
// TODO use optional
|
||||||
|
return v.name.len > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t mut Table) register_type(typ string) {
|
||||||
|
if typ.len == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// println('REGISTER TYPE $typ')
|
||||||
|
for typ2 in t.types {
|
||||||
|
if typ2.name == typ {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if t.types.filter( _.name == typ.name).len > 0 {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
datyp := Type {
|
||||||
|
name: typ
|
||||||
|
}
|
||||||
|
t.types << datyp
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) register_type_with_parent(strtyp, parent string) {
|
||||||
|
typ := Type {
|
||||||
|
name: strtyp
|
||||||
|
parent: parent
|
||||||
|
pkg: p.pkg
|
||||||
|
}
|
||||||
|
p.table.register_type2(typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t mut Table) register_type_with_parent(typ, parent string) {
|
||||||
|
if typ.len == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// if t.types.filter(_.name == typ) > 0
|
||||||
|
for typ2 in t.types {
|
||||||
|
if typ2.name == typ {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
mut pkg := ''
|
||||||
|
if parent == 'array' {
|
||||||
|
pkg = 'builtin'
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
datyp := Type {
|
||||||
|
name: typ
|
||||||
|
parent: parent
|
||||||
|
}
|
||||||
|
t.types << datyp
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t mut Table) register_type2(typ Type) {
|
||||||
|
if typ.name.len == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// println('register type2 $typ.name')
|
||||||
|
for typ2 in t.types {
|
||||||
|
if typ2.name == typ.name {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.types << typ
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t mut Type) add_field(name, typ string, is_mut bool, attr string, access_mod AccessMod) {
|
||||||
|
// if t.name == 'Parser' {
|
||||||
|
// println('adding field $name')
|
||||||
|
// }
|
||||||
|
v := Var {
|
||||||
|
name: name
|
||||||
|
typ: typ
|
||||||
|
is_mut: is_mut
|
||||||
|
attr: attr
|
||||||
|
access_mod: access_mod
|
||||||
|
}
|
||||||
|
t.fields << v
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t &Type) has_field(name string) bool {
|
||||||
|
field := t.find_field(name)
|
||||||
|
return (field.name != '')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t &Type) find_field(name string) Var {
|
||||||
|
for field in t.fields {
|
||||||
|
if field.name == name {
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Var{}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (table &Table) type_has_field(typ &Type, name string) bool {
|
||||||
|
field := table.find_field(typ, name)
|
||||||
|
return (field.name != '')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (table &Table) find_field(typ &Type, name string) Var {
|
||||||
|
field := typ.find_field(name)
|
||||||
|
if field.name.len == 0 && typ.parent.len > 0 {
|
||||||
|
parent := table.find_type(typ.parent)
|
||||||
|
return parent.find_field(name)
|
||||||
|
}
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t mut Type) add_method(f Fn) {
|
||||||
|
// if t.name.contains('Parser') {
|
||||||
|
// println('!!!add_method() $f.name to $t.name len=$t.methods.len cap=$t.methods.cap')
|
||||||
|
// }
|
||||||
|
t.methods << f
|
||||||
|
// println('end add_method()')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t &Type) has_method(name string) bool {
|
||||||
|
method := t.find_method(name)
|
||||||
|
return (method.name != '')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (table &Table) type_has_method(typ &Type, name string) bool {
|
||||||
|
method := table.find_method(typ, name)
|
||||||
|
return (method.name != '')
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO use `?Fn`
|
||||||
|
fn (table &Table) find_method(typ &Type, name string) Fn {
|
||||||
|
// println('TYPE HAS METHOD $name')
|
||||||
|
method := typ.find_method(name)
|
||||||
|
if method.name.len == 0 && typ.parent.len > 0 {
|
||||||
|
parent := table.find_type(typ.parent)
|
||||||
|
return parent.find_method(name)
|
||||||
|
// println('parent = $parent.name $res')
|
||||||
|
// return res
|
||||||
|
}
|
||||||
|
return method
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t &Type) find_method(name string) Fn {
|
||||||
|
// println('$t.name find_method($name) methods.len=$t.methods.len')
|
||||||
|
for method in t.methods {
|
||||||
|
// println('method=$method.name')
|
||||||
|
if method.name == name {
|
||||||
|
return method
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Fn{}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t mut Type) add_gen_type(type_name string) {
|
||||||
|
// println('add_gen_type($s)')
|
||||||
|
if t.gen_types.contains(type_name) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.gen_types << type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p &Parser) find_type(name string) *Type {
|
||||||
|
typ := p.table.find_type(name)
|
||||||
|
if typ.name.len == 0 {
|
||||||
|
return p.table.find_type(p.prepend_pkg(name))
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t &Table) find_type(name string) *Type {
|
||||||
|
if name.ends_with('*') && !name.contains(' ') {
|
||||||
|
name = name.left(name.len - 1)
|
||||||
|
}
|
||||||
|
// TODO PERF use map
|
||||||
|
for i, typ in t.types {
|
||||||
|
if typ.name == name {
|
||||||
|
return &t.types[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &Type{}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) _check_types(got, expected string, throw bool) bool {
|
||||||
|
p.log('check types got="$got" exp="$expected" ')
|
||||||
|
if p.translated {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Allow ints to be used as floats
|
||||||
|
if got.eq('int') && expected.eq('float') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if got.eq('int') && expected.eq('f64') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if got == 'f64' && expected == 'float' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if got == 'float' && expected == 'f64' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Allow ints to be used as longs
|
||||||
|
if got.eq('int') && expected.eq('long') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if got == 'void*' && expected.starts_with('fn ') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if got.starts_with('[') && expected == 'byte*' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Todo void* allows everything right now
|
||||||
|
if got.eq('void*') || expected.eq('void*') {
|
||||||
|
// if !p.builtin_pkg {
|
||||||
|
if p.is_play {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// TODO only allow numeric consts to be assigned to bytes, and
|
||||||
|
// throw an error if they are bigger than 255
|
||||||
|
if got.eq('int') && expected.eq('byte') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if got.eq('int') && expected.eq('byte*') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// byteptr += int
|
||||||
|
if got.eq('int') && expected.eq('byteptr') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if got == 'Option' && expected.starts_with('Option_') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// lines := new_array
|
||||||
|
if got == 'array' && expected.starts_with('array_') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Expected type "Option_os__File", got "os__File"
|
||||||
|
if expected.starts_with('Option_') && expected.ends_with(got) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// NsColor* return 0
|
||||||
|
if !p.is_play {
|
||||||
|
if expected.ends_with('*') && got == 'int' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// if got == 'T' || got.contains('<T>') {
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
// if expected == 'T' || expected.contains('<T>') {
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
// Allow pointer arithmetic
|
||||||
|
if expected.eq('void*') && got.eq('int') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expected = expected.replace('*', '')
|
||||||
|
got = got.replace('*', '')
|
||||||
|
if got != expected {
|
||||||
|
// Interface check
|
||||||
|
if expected.ends_with('er') {
|
||||||
|
if p.satisfies_interface(expected, got, throw) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !throw {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
p.error('expected type `$expected`, but got `$got`')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// throw by default
|
||||||
|
fn (p mut Parser) check_types(got, expected string) bool {
|
||||||
|
return p._check_types(got, expected, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) check_types_no_throw(got, expected string) bool {
|
||||||
|
return p._check_types(got, expected, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p mut Parser) satisfies_interface(interface_name, _typ string, throw bool) bool {
|
||||||
|
int_typ := p.table.find_type(interface_name)
|
||||||
|
typ := p.table.find_type(_typ)
|
||||||
|
for method in int_typ.methods {
|
||||||
|
if !typ.has_method(method.name) {
|
||||||
|
// if throw {
|
||||||
|
p.error('Type "$_typ" doesnt satisfy interface "$interface_name" (method "$method.name" is not implemented)')
|
||||||
|
// }
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn type_default(typ string) string {
|
||||||
|
if typ.starts_with('array_') {
|
||||||
|
typ = typ.right(6)
|
||||||
|
return 'new_array(0, 1, sizeof($typ))'
|
||||||
|
}
|
||||||
|
// Always set pointers to 0
|
||||||
|
if typ.ends_with('*') {
|
||||||
|
return '0'
|
||||||
|
}
|
||||||
|
// ?
|
||||||
|
if typ.contains('__') {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
// Default values for other types are not needed because of mandatory initialization
|
||||||
|
switch typ {
|
||||||
|
case 'int': return '0'
|
||||||
|
case 'string': return 'tos("", 0)'
|
||||||
|
case 'void*': return '0'
|
||||||
|
case 'byte*': return '0'
|
||||||
|
case 'bool': return '0'
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO PERF O(n)
|
||||||
|
fn (t &Table) is_interface(name string) bool {
|
||||||
|
for typ in t.types {
|
||||||
|
if typ.is_interface && typ.name == name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we have fn main()?
|
||||||
|
fn (t &Table) main_exists() bool {
|
||||||
|
for f in t.fns {
|
||||||
|
if f.name == 'main' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO use `?Var`
|
||||||
|
fn (t &Table) find_const(name string) Var {
|
||||||
|
for c in t.consts {
|
||||||
|
if c.name == name {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Var{}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (table mut Table) cgen_name(f &Fn) string {
|
||||||
|
mut name := f.name
|
||||||
|
if f.is_method {
|
||||||
|
name = '${f.receiver_typ}_$f.name'
|
||||||
|
name = name.replace(' ', '')
|
||||||
|
name = name.replace('*', '')
|
||||||
|
name = name.replace('+', 'plus')
|
||||||
|
name = name.replace('-', 'minus')
|
||||||
|
}
|
||||||
|
// Avoid name conflicts (with things like abs(), print() etc).
|
||||||
|
// Generate b_abs(), b_print()
|
||||||
|
// TODO duplicate functionality
|
||||||
|
if f.pkg == 'builtin' && CReserved.contains(f.name) {
|
||||||
|
return 'v_$name'
|
||||||
|
}
|
||||||
|
// Obfuscate but skip certain names
|
||||||
|
// TODO ugly, fix
|
||||||
|
if table.obfuscate && f.name != 'main' && f.name != 'WinMain' && f.pkg != 'builtin' && !f.is_c &&
|
||||||
|
f.pkg != 'darwin' && f.pkg != 'os' && !f.name.contains('window_proc') && f.name != 'gg__vec2' &&
|
||||||
|
f.name != 'build_token_str' && f.name != 'build_keys' && f.pkg != 'json' &&
|
||||||
|
!name.ends_with('_str') && !name.contains('contains') {
|
||||||
|
mut idx := table.obf_ids[name]
|
||||||
|
// No such function yet, register it
|
||||||
|
if idx == 0 {
|
||||||
|
table.fn_cnt++
|
||||||
|
table.obf_ids[name] = table.fn_cnt
|
||||||
|
idx = table.fn_cnt
|
||||||
|
}
|
||||||
|
old := name
|
||||||
|
name = 'f_$idx'
|
||||||
|
println2('$old ==> $name')
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
// ('s', 'string') => 'string s'
|
||||||
|
// ('nums', '[20]byte') => 'byte nums[20]'
|
||||||
|
// ('myfn', 'fn(int) string') => 'string (*myfn)(int)'
|
||||||
|
fn (table &Table) cgen_name_type_pair(name, typ string) string {
|
||||||
|
// Special case for [10]int
|
||||||
|
if typ.len > 0 && typ[0] == `[` {
|
||||||
|
tmp := typ.all_after(']')
|
||||||
|
size := typ.all_before(']')
|
||||||
|
return '$tmp $name $size ]'
|
||||||
|
}
|
||||||
|
// fn()
|
||||||
|
else if typ.starts_with('fn (') {
|
||||||
|
T := table.find_type(typ)
|
||||||
|
if T.name == '' {
|
||||||
|
os.exit1('this should never happen')
|
||||||
|
}
|
||||||
|
str_args := T.func.str_args(table)
|
||||||
|
return '$T.func.typ (*$name)( $str_args /*FFF*/ )'
|
||||||
|
}
|
||||||
|
// TODO tm hack, do this for all C struct args
|
||||||
|
else if typ == 'tm' {
|
||||||
|
return 'struct tm $name'
|
||||||
|
}
|
||||||
|
return '$typ $name'
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,265 @@
|
||||||
|
module main
|
||||||
|
|
||||||
|
enum Token {
|
||||||
|
EOF
|
||||||
|
NAME
|
||||||
|
INT
|
||||||
|
STRING
|
||||||
|
CHAR
|
||||||
|
FLOAT
|
||||||
|
PLUS
|
||||||
|
MINUS
|
||||||
|
MUL
|
||||||
|
DIV
|
||||||
|
MOD
|
||||||
|
XOR
|
||||||
|
PIPE
|
||||||
|
INC
|
||||||
|
DEC
|
||||||
|
AND
|
||||||
|
OR
|
||||||
|
NOT
|
||||||
|
BIT_NOT
|
||||||
|
QUESTION
|
||||||
|
COMMA
|
||||||
|
SEMICOLON
|
||||||
|
COLON
|
||||||
|
AMP
|
||||||
|
HASH
|
||||||
|
AT
|
||||||
|
DOLLAR
|
||||||
|
LEFT_SHIFT
|
||||||
|
RIGHT_SHIFT
|
||||||
|
// = := += -=
|
||||||
|
ASSIGN
|
||||||
|
DECL_ASSIGN
|
||||||
|
PLUS_ASSIGN
|
||||||
|
MINUS_ASSIGN
|
||||||
|
DIV_ASSIGN
|
||||||
|
MULT_ASSIGN
|
||||||
|
XOR_ASSIGN
|
||||||
|
MOD_ASSIGN
|
||||||
|
OR_ASSIGN
|
||||||
|
AND_ASSIGN
|
||||||
|
RIGHT_SHIFT_ASSIGN
|
||||||
|
LEFT_SHIFT_ASSIGN
|
||||||
|
// {} () []
|
||||||
|
LCBR
|
||||||
|
RCBR
|
||||||
|
LPAR
|
||||||
|
RPAR
|
||||||
|
LSBR
|
||||||
|
RSBR
|
||||||
|
// == != <= < >= >
|
||||||
|
EQ
|
||||||
|
NE
|
||||||
|
GT
|
||||||
|
LT
|
||||||
|
GE
|
||||||
|
LE
|
||||||
|
// comments
|
||||||
|
LINE_COM
|
||||||
|
MLINE_COM
|
||||||
|
NL
|
||||||
|
DOT
|
||||||
|
DOTDOT
|
||||||
|
// keywords
|
||||||
|
keyword_beg
|
||||||
|
PACKAGE
|
||||||
|
// MODULE
|
||||||
|
STRUCT
|
||||||
|
IF
|
||||||
|
ELSE
|
||||||
|
RETURN
|
||||||
|
GO
|
||||||
|
CONST
|
||||||
|
IMPORT_CONST
|
||||||
|
MUT
|
||||||
|
TIP
|
||||||
|
ENUM
|
||||||
|
FOR
|
||||||
|
SWITCH
|
||||||
|
MATCH
|
||||||
|
CASE
|
||||||
|
FUNC
|
||||||
|
TRUE
|
||||||
|
FALSE
|
||||||
|
CONTINUE
|
||||||
|
BREAK
|
||||||
|
EMBED
|
||||||
|
IMPORT
|
||||||
|
TYPEOF
|
||||||
|
DEFAULT
|
||||||
|
ENDIF
|
||||||
|
ASSERT
|
||||||
|
SIZEOF
|
||||||
|
IN
|
||||||
|
ATOMIC
|
||||||
|
INTERFACE
|
||||||
|
OR_ELSE
|
||||||
|
GLOBAL
|
||||||
|
UNION
|
||||||
|
PUB
|
||||||
|
GOTO
|
||||||
|
STATIC
|
||||||
|
keyword_end
|
||||||
|
}
|
||||||
|
|
||||||
|
// build_keys genereates a map with keywords' string values:
|
||||||
|
// Keywords['return'] == .return
|
||||||
|
fn build_keys() map_int {
|
||||||
|
mut res := map[string]int{}
|
||||||
|
for t := int(keyword_beg) + 1; t < int(keyword_end); t++ {
|
||||||
|
key := TOKENSTR[t]
|
||||||
|
res[key] = int(t)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_token_str() []string {
|
||||||
|
mut s := [''; 140]// TODO define a const
|
||||||
|
s[keyword_beg] = ''
|
||||||
|
s[keyword_end] = ''
|
||||||
|
s[EOF] = 'EOF'
|
||||||
|
s[NAME] = 'NAME'
|
||||||
|
s[INT] = 'INT'
|
||||||
|
s[STRING] = 'STR'
|
||||||
|
s[CHAR] = 'CHAR'
|
||||||
|
s[FLOAT] = 'FLOAT'
|
||||||
|
s[PLUS] = '+'
|
||||||
|
s[MINUS] = '-'
|
||||||
|
s[MUL] = '*'
|
||||||
|
s[DIV] = '/'
|
||||||
|
s[MOD] = '%'
|
||||||
|
s[XOR] = '^'
|
||||||
|
s[BIT_NOT] = '~'
|
||||||
|
s[PIPE] = '|'
|
||||||
|
s[HASH] = '#'
|
||||||
|
s[AMP] = '&'
|
||||||
|
s[AT] = '@'
|
||||||
|
s[INC] = '++'
|
||||||
|
s[DEC] = '--'
|
||||||
|
s[AND] = '&&'
|
||||||
|
s[OR] = '||'
|
||||||
|
s[NOT] = '!'
|
||||||
|
s[DOT] = '.'
|
||||||
|
s[DOTDOT] = '..'
|
||||||
|
s[COMMA] = ','
|
||||||
|
s[SEMICOLON] = ';'
|
||||||
|
s[COLON] = ':'
|
||||||
|
s[ASSIGN] = '='
|
||||||
|
s[DECL_ASSIGN] = ':='
|
||||||
|
s[PLUS_ASSIGN] = '+='
|
||||||
|
s[MINUS_ASSIGN] = '-='
|
||||||
|
s[MULT_ASSIGN] = '*='
|
||||||
|
s[DIV_ASSIGN] = '/='
|
||||||
|
s[XOR_ASSIGN] = '^='
|
||||||
|
s[MOD_ASSIGN] = '%='
|
||||||
|
s[OR_ASSIGN] = '|='
|
||||||
|
s[AND_ASSIGN] = '&='
|
||||||
|
s[RIGHT_SHIFT_ASSIGN] = '>>='
|
||||||
|
s[LEFT_SHIFT_ASSIGN] = '<<='
|
||||||
|
s[LCBR] = '{'
|
||||||
|
s[RCBR] = '}'
|
||||||
|
s[LPAR] = '('
|
||||||
|
s[RPAR] = ')'
|
||||||
|
s[LSBR] = '['
|
||||||
|
s[RSBR] = ']'
|
||||||
|
s[EQ] = '=='
|
||||||
|
s[NE] = '!='
|
||||||
|
s[GT] = '>'
|
||||||
|
s[LT] = '<'
|
||||||
|
s[GE] = '>='
|
||||||
|
s[LE] = '<='
|
||||||
|
s[QUESTION] = '?'
|
||||||
|
s[LEFT_SHIFT] = '<<'
|
||||||
|
s[RIGHT_SHIFT] = '>>'
|
||||||
|
s[LINE_COM] = '//'
|
||||||
|
s[NL] = 'NLL'
|
||||||
|
s[DOLLAR] = '$'
|
||||||
|
s[ASSERT] = 'assert'
|
||||||
|
s[STRUCT] = 'struct'
|
||||||
|
s[IF] = 'if'
|
||||||
|
s[ELSE] = 'else'
|
||||||
|
s[RETURN] = 'return'
|
||||||
|
s[PACKAGE] = 'module'
|
||||||
|
s[SIZEOF] = 'sizeof'
|
||||||
|
s[GO] = 'go'
|
||||||
|
s[GOTO] = 'goto'
|
||||||
|
s[CONST] = 'const'
|
||||||
|
s[MUT] = 'mut'
|
||||||
|
s[TIP] = 'type'
|
||||||
|
s[FOR] = 'for'
|
||||||
|
s[SWITCH] = 'switch'
|
||||||
|
s[MATCH] = 'match'
|
||||||
|
s[CASE] = 'case'
|
||||||
|
s[FUNC] = 'fn'
|
||||||
|
s[TRUE] = 'true'
|
||||||
|
s[FALSE] = 'false'
|
||||||
|
s[CONTINUE] = 'continue'
|
||||||
|
s[BREAK] = 'break'
|
||||||
|
s[IMPORT] = 'import'
|
||||||
|
s[EMBED] = 'embed'
|
||||||
|
s[TYPEOF] = 'typeof'
|
||||||
|
s[DEFAULT] = 'default'
|
||||||
|
s[ENDIF] = 'endif'
|
||||||
|
s[ENUM] = 'enum'
|
||||||
|
s[INTERFACE] = 'interface'
|
||||||
|
s[PUB] = 'pub'
|
||||||
|
s[IMPORT_CONST] = 'import_const'
|
||||||
|
s[IN] = 'in'
|
||||||
|
s[ATOMIC] = 'atomic'
|
||||||
|
s[OR_ELSE] = 'or'
|
||||||
|
s[GLOBAL] = '__global'
|
||||||
|
s[UNION] = 'union'
|
||||||
|
s[STATIC] = 'static'
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
TOKENSTR = build_token_str()
|
||||||
|
KEYWORDS = build_keys()
|
||||||
|
)
|
||||||
|
|
||||||
|
fn key_to_token(key string) Token {
|
||||||
|
a := Token(KEYWORDS[key])
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_key(key string) bool {
|
||||||
|
return int(key_to_token(key)) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t Token) str() string {
|
||||||
|
return TOKENSTR[int(t)]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t Token) is_decl() bool {
|
||||||
|
// TODO return t in [FUNC ,TIP, CONST, IMPORT_CONST ,AT ,EOF]
|
||||||
|
return t == ENUM || t == INTERFACE || t == FUNC || t == STRUCT || t == TIP ||
|
||||||
|
t == CONST || t == IMPORT_CONST || t == AT || t == EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
AssignTokens = [
|
||||||
|
ASSIGN, PLUS_ASSIGN, MINUS_ASSIGN,
|
||||||
|
MULT_ASSIGN, DIV_ASSIGN, XOR_ASSIGN, MOD_ASSIGN,
|
||||||
|
OR_ASSIGN, AND_ASSIGN, RIGHT_SHIFT_ASSIGN,
|
||||||
|
LEFT_SHIFT_ASSIGN
|
||||||
|
]
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
fn (t Token) is_assign() bool {
|
||||||
|
return t in AssignTokens
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t[]Token) contains(val Token) bool {
|
||||||
|
for tt in t {
|
||||||
|
if tt == val {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Please share your thoughts, suggestions, questions, etc here:
|
||||||
|
// https://github.com/vlang-io/V/issues/3
|
||||||
|
// I'm very interested in your feedback.
|
||||||
|
import http
|
||||||
|
import json
|
||||||
|
import runtime
|
||||||
|
|
||||||
|
struct Story {
|
||||||
|
title string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetches top HN stories in 8 coroutines
|
||||||
|
fn main() {
|
||||||
|
resp := http.get('https://hacker-news.firebaseio.com/v0/topstories.json')?
|
||||||
|
ids := json.decode([]int, resp.body)?
|
||||||
|
mut cursor := 0
|
||||||
|
for _ in 0..8 {
|
||||||
|
go fn() {
|
||||||
|
for {
|
||||||
|
lock { // Without this lock the program will not compile
|
||||||
|
if cursor >= ids.len {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
id := ids[cursor]
|
||||||
|
cursor++
|
||||||
|
}
|
||||||
|
url := 'https://hacker-news.firebaseio.com/v0/item/$id.json'
|
||||||
|
resp := http.get(url)?
|
||||||
|
story := json.decode(Story, resp.body)?
|
||||||
|
println(story.title)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
runtime.wait() // Waits for all coroutines to finish
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import http
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
html := http.get('https://news.ycombinator.com')
|
||||||
|
mut pos := 0
|
||||||
|
for {
|
||||||
|
pos = html.index_after('https://', pos + 1)
|
||||||
|
if pos == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
end := html.index_after('"', pos)
|
||||||
|
println(html.substr(pos, end))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,339 @@
|
||||||
|
import rand
|
||||||
|
import time
|
||||||
|
import gx
|
||||||
|
import gl
|
||||||
|
import gg
|
||||||
|
import glfw
|
||||||
|
import math
|
||||||
|
|
||||||
|
const (
|
||||||
|
BlockSize = 20 // pixels
|
||||||
|
FieldHeight = 20 // # of blocks
|
||||||
|
FieldWidth = 10
|
||||||
|
TetroSize = 4
|
||||||
|
WinWidth = BlockSize * FieldWidth
|
||||||
|
WinHeight = BlockSize * FieldHeight
|
||||||
|
TimerPeriod = 250 // ms
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Tetros' 4 possible states are encoded in binaries
|
||||||
|
BTetros = [
|
||||||
|
// 0000 0
|
||||||
|
// 0000 0
|
||||||
|
// 0110 6
|
||||||
|
// 0110 6
|
||||||
|
[66, 66, 66, 66],
|
||||||
|
// 0000 0
|
||||||
|
// 0000 0
|
||||||
|
// 0010 2
|
||||||
|
// 0111 7
|
||||||
|
[27, 131, 72, 232],
|
||||||
|
// 0000 0
|
||||||
|
// 0000 0
|
||||||
|
// 0011 3
|
||||||
|
// 0110 6
|
||||||
|
[36, 231, 36, 231],
|
||||||
|
// 0000 0
|
||||||
|
// 0000 0
|
||||||
|
// 0110 6
|
||||||
|
// 0011 3
|
||||||
|
[63, 132, 63, 132],
|
||||||
|
// 0000 0
|
||||||
|
// 0011 3
|
||||||
|
// 0001 1
|
||||||
|
// 0001 1
|
||||||
|
[311, 17, 223, 74],
|
||||||
|
// 0000 0
|
||||||
|
// 0011 3
|
||||||
|
// 0010 2
|
||||||
|
// 0010 2
|
||||||
|
[322, 71, 113, 47],
|
||||||
|
// Special case since 15 can't be used
|
||||||
|
// 1111
|
||||||
|
[1111, 9, 1111, 9],
|
||||||
|
]
|
||||||
|
// Each tetro has its unique color
|
||||||
|
Colors = [
|
||||||
|
gx.rgb(0, 0, 0),
|
||||||
|
gx.rgb(253, 32, 47),
|
||||||
|
gx.rgb(0, 110, 194),
|
||||||
|
gx.rgb(34, 169, 16),
|
||||||
|
gx.rgb(170, 0, 170),
|
||||||
|
gx.rgb(0, 0, 170),
|
||||||
|
gx.rgb(0, 170, 0),
|
||||||
|
gx.rgb(170, 85, 0),
|
||||||
|
gx.rgb(0, 170, 170),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: type Tetro [TetroSize]struct{ x, y int }
|
||||||
|
struct Block {
|
||||||
|
mut:
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Game {
|
||||||
|
mut:
|
||||||
|
// Position of the current tetro
|
||||||
|
pos_x int
|
||||||
|
pos_y int
|
||||||
|
// field[y][x] contains the color of the block with (x,y) coordinates
|
||||||
|
// "-1" border is to avoid bounds checking.
|
||||||
|
// -1 -1 -1 -1
|
||||||
|
// -1 0 0 -1
|
||||||
|
// -1 0 0 -1
|
||||||
|
// -1 -1 -1 -1
|
||||||
|
// TODO: field [][]int
|
||||||
|
field array_array_int
|
||||||
|
// TODO: tetro Tetro
|
||||||
|
tetro []Block
|
||||||
|
// TODO: tetros_cache []Tetro
|
||||||
|
tetros_cache []Block
|
||||||
|
// Index of the current tetro. Refers to its color.
|
||||||
|
tetro_idx int
|
||||||
|
// Index of the rotation (0-3)
|
||||||
|
rotation_idx int
|
||||||
|
// gg context for drawing
|
||||||
|
gg *gg.GG
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
glfw.init()
|
||||||
|
mut game := &Game{gg: 0} // TODO
|
||||||
|
game.parse_tetros()
|
||||||
|
game.init_game()
|
||||||
|
mut window := glfw.create_window(glfw.WinCfg {
|
||||||
|
width: WinWidth
|
||||||
|
height: WinHeight
|
||||||
|
title: 'V Tetris'
|
||||||
|
ptr: game // glfw user pointer
|
||||||
|
})
|
||||||
|
window.make_context_current()
|
||||||
|
window.onkeydown(key_down)
|
||||||
|
gg.init()
|
||||||
|
game.gg = gg.new_context(gg.Cfg {
|
||||||
|
width: WinWidth
|
||||||
|
height: WinHeight
|
||||||
|
use_ortho: true // This is needed for 2D drawing
|
||||||
|
})
|
||||||
|
go game.run() // Run the game loop in a new thread
|
||||||
|
gl.clear() // For some reason this is necessary to avoid an intial flickering
|
||||||
|
gl.clear_color(255, 255, 255, 255)
|
||||||
|
for {
|
||||||
|
gl.clear()
|
||||||
|
gl.clear_color(255, 255, 255, 255)
|
||||||
|
game.draw_scene()
|
||||||
|
window.swap_buffers()
|
||||||
|
glfw.wait_events()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g mut Game) init_game() {
|
||||||
|
rand.seed()
|
||||||
|
g.generate_tetro()
|
||||||
|
g.field = []array_int // TODO: g.field = [][]int
|
||||||
|
// Generate the field, fill it with 0's, add -1's on each edge
|
||||||
|
for i := 0; i < FieldHeight + 2; i++ {
|
||||||
|
mut row := [0; FieldWidth + 2]
|
||||||
|
row[0] = - 1
|
||||||
|
row[FieldWidth + 1] = - 1
|
||||||
|
g.field << row
|
||||||
|
}
|
||||||
|
mut first_row := g.field[0]
|
||||||
|
mut last_row := g.field[FieldHeight + 1]
|
||||||
|
for j := 0; j < FieldWidth + 2; j++ {
|
||||||
|
first_row[j] = - 1
|
||||||
|
last_row[j] = - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g mut Game) parse_tetros() {
|
||||||
|
for b_tetros in BTetros {
|
||||||
|
for b_tetro in b_tetros {
|
||||||
|
for t in parse_binary_tetro(b_tetro) {
|
||||||
|
g.tetros_cache << t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g mut Game) run() {
|
||||||
|
for {
|
||||||
|
g.move_tetro()
|
||||||
|
g.delete_completed_lines()
|
||||||
|
glfw.post_empty_event() // force window redraw
|
||||||
|
time.sleep_ms(TimerPeriod)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g mut Game) move_tetro() {
|
||||||
|
// Check each block in current tetro
|
||||||
|
for block in g.tetro {
|
||||||
|
y := block.y + g.pos_y + 1
|
||||||
|
x := block.x + g.pos_x
|
||||||
|
// Reached the bottom of the screen or another block?
|
||||||
|
// TODO: if g.field[y][x] != 0
|
||||||
|
row := g.field[y]
|
||||||
|
if row[x] != 0 {
|
||||||
|
// The new tetro has no space to drop => end of the game
|
||||||
|
if g.pos_y < 2 {
|
||||||
|
g.init_game()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Drop it and generate a new one
|
||||||
|
g.drop_tetro()
|
||||||
|
g.generate_tetro()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.pos_y++
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g mut Game) move_right(dx int) {
|
||||||
|
// Reached left/right edge or another tetro?
|
||||||
|
for i := 0; i < TetroSize; i++ {
|
||||||
|
tetro := g.tetro[i]
|
||||||
|
y := tetro.y + g.pos_y
|
||||||
|
x := tetro.x + g.pos_x + dx
|
||||||
|
row := g.field[y]
|
||||||
|
if row[x] != 0 {
|
||||||
|
// Do not move
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.pos_x += dx
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g mut Game) delete_completed_lines() {
|
||||||
|
for y := FieldHeight; y >= 1; y-- {
|
||||||
|
g.delete_completed_line(y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g mut Game) delete_completed_line(y int) {
|
||||||
|
for x := 1; x <= FieldWidth; x++ {
|
||||||
|
f := g.field[y]
|
||||||
|
if f[x] == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Move everything down by 1 position
|
||||||
|
for yy := y - 1; yy >= 1; yy-- {
|
||||||
|
for x := 1; x <= FieldWidth; x++ {
|
||||||
|
mut a := g.field[yy + 1]
|
||||||
|
mut b := g.field[yy]
|
||||||
|
a[x] = b[x]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Place a new tetro on top
|
||||||
|
fn (g mut Game) generate_tetro() {
|
||||||
|
g.pos_y = 0
|
||||||
|
g.pos_x = FieldWidth / 2 - TetroSize / 2
|
||||||
|
g.tetro_idx = rand.next(BTetros.len)
|
||||||
|
g.rotation_idx = 0
|
||||||
|
g.get_tetro()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the right tetro from cache
|
||||||
|
fn (g mut Game) get_tetro() {
|
||||||
|
idx := g.tetro_idx * TetroSize * TetroSize + g.rotation_idx * TetroSize
|
||||||
|
g.tetro = g.tetros_cache.slice(idx, idx + TetroSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g mut Game) drop_tetro() {
|
||||||
|
for i := 0; i < TetroSize; i++ {
|
||||||
|
tetro := g.tetro[i]
|
||||||
|
x := tetro.x + g.pos_x
|
||||||
|
y := tetro.y + g.pos_y
|
||||||
|
// Remember the color of each block
|
||||||
|
// TODO: g.field[y][x] = g.tetro_idx + 1
|
||||||
|
mut row := g.field[y]
|
||||||
|
row[x] = g.tetro_idx + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g &Game) draw_tetro() {
|
||||||
|
for i := 0; i < TetroSize; i++ {
|
||||||
|
tetro := g.tetro[i]
|
||||||
|
g.draw_block(g.pos_y + tetro.y, g.pos_x + tetro.x, g.tetro_idx + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g &Game) draw_block(i, j int, color_idx int) {
|
||||||
|
g.gg.draw_rect((j - 1) * BlockSize, (i - 1) * BlockSize,
|
||||||
|
BlockSize - 1, BlockSize - 1, Colors[color_idx])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g &Game) draw_field() {
|
||||||
|
for i := 1; i < FieldHeight + 1; i++ {
|
||||||
|
for j := 1; j < FieldWidth + 1; j++ {
|
||||||
|
f := g.field[i]
|
||||||
|
if f[j] > 0 {
|
||||||
|
g.draw_block(i, j, f[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g &Game) draw_scene() {
|
||||||
|
g.draw_tetro()
|
||||||
|
g.draw_field()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_binary_tetro(t int) []Block {
|
||||||
|
res := [Block{} ; 4]
|
||||||
|
mut cnt := 0
|
||||||
|
horizontal := t == 9// special case for the horizontal line
|
||||||
|
for i := 0; i <= 3; i++ {
|
||||||
|
// Get ith digit of t
|
||||||
|
p := int(math.pow(10, 3 - i))
|
||||||
|
mut digit := int(t / p)
|
||||||
|
t %= p
|
||||||
|
// Convert the digit to binary
|
||||||
|
for j := 3; j >= 0; j-- {
|
||||||
|
bin := digit % 2
|
||||||
|
digit /= 2
|
||||||
|
if bin == 1 || (horizontal && i == TetroSize - 1) {
|
||||||
|
// TODO: res[cnt].x = j
|
||||||
|
// res[cnt].y = i
|
||||||
|
mut point := &res[cnt]
|
||||||
|
point.x = j
|
||||||
|
point.y = i
|
||||||
|
cnt++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this exposes the unsafe C interface, clean up
|
||||||
|
fn key_down(wnd voidptr, key int, code int, action, mods int) {
|
||||||
|
if action != 2 && action != 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Fetch the game object stored in the user pointer
|
||||||
|
mut game := &Game(glfw.get_window_user_pointer(wnd))
|
||||||
|
switch key {
|
||||||
|
case glfw.KeyUp:
|
||||||
|
// Rotate the tetro
|
||||||
|
game.rotation_idx++
|
||||||
|
if game.rotation_idx == TetroSize {
|
||||||
|
game.rotation_idx = 0
|
||||||
|
}
|
||||||
|
game.get_tetro()
|
||||||
|
if game.pos_x < 0 {
|
||||||
|
game.pos_x = 1
|
||||||
|
}
|
||||||
|
case glfw.KeyLeft:
|
||||||
|
game.move_right(-1)
|
||||||
|
case glfw.KeyRight:
|
||||||
|
game.move_right(1)
|
||||||
|
case glfw.KeyDown:
|
||||||
|
game.move_tetro() // drop faster when the player presses <down>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
mut path = 'cinderella.txt'
|
||||||
|
if os.args.len != 2 {
|
||||||
|
println('usage: word_counter [text_file]')
|
||||||
|
println('using $path')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
path = os.args[1]
|
||||||
|
}
|
||||||
|
lines := os.read_file_lines(path.trim_space())
|
||||||
|
mut m := map[string]int{}
|
||||||
|
for line in lines {
|
||||||
|
words := line.to_lower().split(' ')
|
||||||
|
for word in words {
|
||||||
|
key := filter_word(word)
|
||||||
|
if key == '' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m[key] = m[key] + 1// TODO m[key]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Sort the keys
|
||||||
|
mut keys := []string
|
||||||
|
for e in m.entries {
|
||||||
|
keys << e.key
|
||||||
|
}
|
||||||
|
keys.sort()
|
||||||
|
// Print the map
|
||||||
|
for key in keys {
|
||||||
|
val := m[key]
|
||||||
|
println('$key => $val')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes punctuation
|
||||||
|
fn filter_word(word string) string {
|
||||||
|
if word == '' || word == ' ' {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
mut i := 0
|
||||||
|
for i < word.len && !is_letter(word[i]) {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
start := i
|
||||||
|
for i < word.len && is_letter(word[i]) {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
end := i
|
||||||
|
return word.substr(start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO remove once it's possible to call word[i].is_letter()
|
||||||
|
fn is_letter(c byte) bool {
|
||||||
|
return c.is_letter()
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,712 @@
|
||||||
|
module gg
|
||||||
|
|
||||||
|
import stbi
|
||||||
|
import glm
|
||||||
|
import gl
|
||||||
|
|
||||||
|
#flag darwin -I/usr/local/Cellar/freetype/2.10.0/include/freetype2
|
||||||
|
#flag -lfreetype
|
||||||
|
#flag linux -I/usr/include/freetype2
|
||||||
|
#flag linux -I.
|
||||||
|
#include "ft2build.h"
|
||||||
|
#include FT_FREETYPE_H
|
||||||
|
#include "glad.h"
|
||||||
|
struct Vec2 {
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
}
|
||||||
|
|
||||||
|
import const (
|
||||||
|
GL_STATIC_DRAW
|
||||||
|
GL_FLOAT
|
||||||
|
GL_FALSE
|
||||||
|
GL_UNSIGNED_INT
|
||||||
|
GL_INT
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DEFAULT_FONT_SIZE = 12
|
||||||
|
)
|
||||||
|
|
||||||
|
pub fn vec2(x, y int) Vec2 {
|
||||||
|
res := Vec2 {
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Character {
|
||||||
|
texture_id u32
|
||||||
|
size Vec2
|
||||||
|
bearing Vec2
|
||||||
|
advance u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init() {
|
||||||
|
println(gl.TEXT_VERT)
|
||||||
|
gl.init_glad()
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Face {
|
||||||
|
cobj voidptr
|
||||||
|
kek int
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Cfg {
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
use_ortho int
|
||||||
|
retina bool
|
||||||
|
font_size int
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GG {
|
||||||
|
shader gl.Shader
|
||||||
|
// use_ortho bool
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
VAO u32
|
||||||
|
rect_vao u32
|
||||||
|
rect_vbo u32
|
||||||
|
line_vao u32
|
||||||
|
line_vbo u32
|
||||||
|
VBO u32
|
||||||
|
chars []gg.Character
|
||||||
|
utf_runes []string
|
||||||
|
utf_chars []gg.Character
|
||||||
|
text_ctx *GG
|
||||||
|
face Face
|
||||||
|
scale int // retina = 2 , normal = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn new_context(width, height int, use_ortho bool, font_size int) *GG {
|
||||||
|
pub fn new_context(cfg Cfg) *GG {
|
||||||
|
// println('new context orhto=$cfg.use_ortho')
|
||||||
|
// # glScissor(0,0,300,300);
|
||||||
|
shader := gl.new_shader('simple')
|
||||||
|
shader.use()
|
||||||
|
if cfg.use_ortho > 0 {
|
||||||
|
projection := glm.ortho(0, cfg.width, cfg.height, 0)
|
||||||
|
/*
|
||||||
|
// for debugging broken tetris in gg.o
|
||||||
|
# projection.data[0]=0.010000;
|
||||||
|
# projection.data[1]=0.000000;
|
||||||
|
# projection.data[2]=0.000000;
|
||||||
|
# projection.data[3]=0.000000;
|
||||||
|
# projection.data[4]=0.000000;
|
||||||
|
# projection.data[5]=-0.005000;
|
||||||
|
# projection.data[6]=0.000000;
|
||||||
|
# projection.data[7]=0.000000;
|
||||||
|
# projection.data[8]=0.000000;
|
||||||
|
# projection.data[9]=0.000000;
|
||||||
|
# projection.data[10]=1.000000;
|
||||||
|
# projection.data[11]=0.000000;
|
||||||
|
# projection.data[12]=-1.000000;
|
||||||
|
# projection.data[13]=1.000000;
|
||||||
|
# projection.data[14]=0.000000;
|
||||||
|
# projection.data[15]=1.000000;
|
||||||
|
*/
|
||||||
|
// projection_new := ortho(0, width, height, 0)
|
||||||
|
// println('\nORTHO OLD=')
|
||||||
|
# for (int i=0;i<16;i++) printf("%d=%f ",i, projection.data[i]);
|
||||||
|
// println('\n\n!ORTHO NEW=')
|
||||||
|
// # for (int i=0;i<16;i++) printf("%d=%f ",i, projection_new[i]);
|
||||||
|
// println('\n\n')
|
||||||
|
println('setting o')
|
||||||
|
shader.set_mat4('projection', projection)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO move to function (allow volt functions to return arrrays without allocations)
|
||||||
|
// i := glm.identity3()
|
||||||
|
shader.set_mat4('projection', glm.identity())
|
||||||
|
}
|
||||||
|
VAO := gl.gen_vertex_array()
|
||||||
|
println('new gg context VAO=$VAO')
|
||||||
|
VBO := gl.gen_buffer()
|
||||||
|
mut scale := 1
|
||||||
|
if cfg.retina {
|
||||||
|
scale = 2
|
||||||
|
}
|
||||||
|
mut ctx := &GG {
|
||||||
|
shader: shader,
|
||||||
|
width: cfg.width,
|
||||||
|
height: cfg.height,
|
||||||
|
VAO: VAO,
|
||||||
|
VBO: VBO,
|
||||||
|
// /line_vao: gl.gen_vertex_array()
|
||||||
|
// /line_vbo: gl.gen_buffer()
|
||||||
|
text_ctx: new_context_text(cfg, scale),
|
||||||
|
scale: scale
|
||||||
|
// use_ortho: use_ortho
|
||||||
|
}
|
||||||
|
// ctx.init_rect_vao()
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (ctx &GG) draw_triangle(x1, y1, x2, y2, x3, y3 float, c gx.Color) {
|
||||||
|
// println('draw_triangle $x1,$y1 $x2,$y2 $x3,$y3')
|
||||||
|
ctx.shader.use()
|
||||||
|
ctx.shader.set_color('color', c)
|
||||||
|
vertices := [
|
||||||
|
x1, y1, 0,
|
||||||
|
x2, y2, 0,
|
||||||
|
x3, y3, 0,
|
||||||
|
] !
|
||||||
|
// bind the Vertex Array Object first, then bind and set vertex buffer(s),
|
||||||
|
// and then configure vertex attributes(s).
|
||||||
|
gl.bind_vao(ctx.VAO)
|
||||||
|
gl.set_vbo(ctx.VBO, vertices, GL_STATIC_DRAW)
|
||||||
|
gl.vertex_attrib_pointer(0, 3, GL_FLOAT, false, 3, 0)
|
||||||
|
gl.enable_vertex_attrib_array(0)
|
||||||
|
// gl.bind_buffer(GL_ARRAY_BUFFER, uint(0))
|
||||||
|
// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO,
|
||||||
|
// but this rarely happens. Modifying other
|
||||||
|
// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs
|
||||||
|
// (nor VBOs) when it's not directly necessary.
|
||||||
|
// gl.bind_vertex_array(uint(0))
|
||||||
|
// gl.bind_vertex_array(ctx.VAO)
|
||||||
|
gl.draw_arrays(GL_TRIANGLES, 0, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (ctx &GG) draw_triangle_tex(x1, y1, x2, y2, x3, y3 float, c gx.Color) {
|
||||||
|
ctx.shader.use()
|
||||||
|
ctx.shader.set_color('color', c)
|
||||||
|
ctx.shader.set_int('has_texture', 1)
|
||||||
|
vertices := [
|
||||||
|
x1, y1, 0, 0, 0, 0, 1, 1,
|
||||||
|
x2, y2, 0, 0, 0, 0, 1, 0,
|
||||||
|
x3, y3, 0, 0, 0, 0, 0, 0,
|
||||||
|
] !
|
||||||
|
gl.bind_vao(ctx.VAO)
|
||||||
|
gl.set_vbo(ctx.VBO, vertices, GL_STATIC_DRAW)
|
||||||
|
// position attribute
|
||||||
|
gl.vertex_attrib_pointer(0, 3, GL_FLOAT, false, 3, 0)
|
||||||
|
gl.enable_vertex_attrib_array(0)
|
||||||
|
// color attribute
|
||||||
|
gl.vertex_attrib_pointer(1, 3, GL_FLOAT, false, 8, 3)
|
||||||
|
gl.enable_vertex_attrib_array(1)
|
||||||
|
// texture attribute
|
||||||
|
gl.vertex_attrib_pointer(2, 2, GL_FLOAT, false, 8, 6)
|
||||||
|
gl.enable_vertex_attrib_array(2)
|
||||||
|
// /
|
||||||
|
// gl.draw_arrays(GL_TRIANGLES, 0, 3)
|
||||||
|
gl.draw_elements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (ctx &GG) draw_rect(x, y, w, h float, c gx.Color) {
|
||||||
|
// println('gg.draw_rect($x,$y,$w,$h)')
|
||||||
|
// wrong order
|
||||||
|
// // ctx.draw_triangle(x, y, x + w, y, x + w, y + h, c)
|
||||||
|
// // ctx.draw_triangle(x, y, x, y + h, x + w, y + h, c)
|
||||||
|
// good order. counter clock wise
|
||||||
|
// ctx.draw_triangle(x, y, x, y + h, x + w, y + h, c)
|
||||||
|
// ctx.draw_triangle(x, y, x + w, y + h, x + w, y, c)
|
||||||
|
ctx.draw_rect2(x, y, w, h, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fn (ctx mut GG) init_rect_vao() {
|
||||||
|
|
||||||
|
ctx.rect_vao = gl.gen_vertex_array()
|
||||||
|
ctx.rect_vbo = gl.gen_buffer()
|
||||||
|
vertices := [
|
||||||
|
x + w, y, 0,
|
||||||
|
x + w, y + h, 0,
|
||||||
|
x, y + h, 0,
|
||||||
|
x, y, 0,
|
||||||
|
] !
|
||||||
|
indices := [
|
||||||
|
0, 1, 3,// first triangle
|
||||||
|
1, 2, 3// second triangle
|
||||||
|
] !
|
||||||
|
gl.bind_vao(ctx.rect_vao)
|
||||||
|
gl.set_vbo(ctx.rect_vbo, vertices, GL_STATIC_DRAW)
|
||||||
|
ebo := gl.gen_buffer()
|
||||||
|
// ///////
|
||||||
|
gl.set_ebo(ebo, indices, GL_STATIC_DRAW)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
fn (ctx &GG) draw_rect2(x, y, w, h float, c gx.Color) {
|
||||||
|
C.glDeleteBuffers(1, &ctx.VAO)
|
||||||
|
C.glDeleteBuffers(1, &ctx.VBO)
|
||||||
|
ctx.shader.use()
|
||||||
|
ctx.shader.set_color('color', c)
|
||||||
|
ctx.shader.set_int('has_texture', 0)
|
||||||
|
// 4--1
|
||||||
|
// 3--2
|
||||||
|
#ifdef linux
|
||||||
|
// y += h
|
||||||
|
#endif
|
||||||
|
vertices := [
|
||||||
|
x + w, y, 0,
|
||||||
|
x + w, y + h, 0,
|
||||||
|
x, y + h, 0,
|
||||||
|
x, y, 0,
|
||||||
|
] !
|
||||||
|
indices := [
|
||||||
|
0, 1, 3,// first triangle
|
||||||
|
1, 2, 3// second triangle
|
||||||
|
] !
|
||||||
|
gl.bind_vao(ctx.VAO)
|
||||||
|
gl.set_vbo(ctx.VBO, vertices, GL_STATIC_DRAW)
|
||||||
|
ebo := gl.gen_buffer()
|
||||||
|
// ///////
|
||||||
|
gl.set_ebo(ebo, indices, GL_STATIC_DRAW)// !!! LEAKS
|
||||||
|
// /////
|
||||||
|
gl.vertex_attrib_pointer(0, 3, GL_FLOAT, false, 3, 0)
|
||||||
|
gl.enable_vertex_attrib_array(0)
|
||||||
|
// gl.bind_vao(ctx.rect_vao)
|
||||||
|
gl.bind_vao(ctx.VAO)
|
||||||
|
gl.draw_elements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0)
|
||||||
|
C.glDeleteBuffers(1, &ebo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// jfn ft_load_char(face FT_Face, code FT_ULong) Character {
|
||||||
|
// fn ft_load_char(_face voidptr, _code voidptr) Character {
|
||||||
|
fn ft_load_char(_face Face, code long) Character {
|
||||||
|
// #FT_Face face = *(FT_Face*)(_face); FT_ULong code = *(FT_ULong*)(code);
|
||||||
|
# FT_Face face = *((FT_Face*)_face.cobj);
|
||||||
|
# if (FT_Load_Char(face, code, FT_LOAD_RENDER))
|
||||||
|
{
|
||||||
|
os.exit('ERROR::FREETYTPE: Failed to load Glyph')
|
||||||
|
}
|
||||||
|
// Generate texture
|
||||||
|
# GLuint texture;
|
||||||
|
# glGenTextures(1, &texture);
|
||||||
|
# glBindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
# glTexImage2D(
|
||||||
|
# GL_TEXTURE_2D,
|
||||||
|
# 0,
|
||||||
|
# GL_RED,
|
||||||
|
# face->glyph->bitmap.width,
|
||||||
|
# face->glyph->bitmap.rows,
|
||||||
|
# 0,
|
||||||
|
# GL_RED,
|
||||||
|
# GL_UNSIGNED_BYTE,
|
||||||
|
# face->glyph->bitmap.buffer
|
||||||
|
# );
|
||||||
|
// Set texture options
|
||||||
|
# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
// Now store character for later use
|
||||||
|
ch := Character{}
|
||||||
|
# ch.texture_id=texture ;
|
||||||
|
# ch.size = gg__vec2(face->glyph->bitmap.width, face->glyph->bitmap.rows);
|
||||||
|
# ch.bearing = gg__vec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
|
||||||
|
# ch.advance = face->glyph->advance.x;
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_context_text(cfg Cfg, scale int) *GG {
|
||||||
|
// Can only have text in ortho mode
|
||||||
|
if !cfg.use_ortho {
|
||||||
|
return &GG{text_ctx: 0}
|
||||||
|
}
|
||||||
|
mut width := cfg.width * scale
|
||||||
|
mut height := cfg.height * scale
|
||||||
|
font_size := cfg.font_size * scale
|
||||||
|
// exit('fs=$font_size')
|
||||||
|
// if false {
|
||||||
|
// retina
|
||||||
|
// width = width * 2// scale// 2
|
||||||
|
// height = height * 2// scale// 2
|
||||||
|
// font_size *= scale// 2
|
||||||
|
// }
|
||||||
|
/*
|
||||||
|
gl.viewport(0, 0, width, height)
|
||||||
|
*/
|
||||||
|
// gl.enable(GL_CULL_FACE) // TODO NEED CULL? MEANS SHIT IS BROKEN?
|
||||||
|
gl.enable(GL_BLEND)
|
||||||
|
// return &GG{}
|
||||||
|
# glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
shader := gl.new_shader('text')
|
||||||
|
shader.use()
|
||||||
|
projection := glm.ortho(0, width, 0, height)// 0 at BOT
|
||||||
|
// projection_new := ortho(0, width, 0, height)// 0 at BOT
|
||||||
|
// projection := gl.ortho(0, width,height,0) // 0 at TOP
|
||||||
|
shader.set_mat4('projection', projection)
|
||||||
|
// FREETYPE
|
||||||
|
# FT_Library ft;
|
||||||
|
// All functions return a value different than 0 whenever an error occurred
|
||||||
|
# if (FT_Init_FreeType(&ft))
|
||||||
|
println('ERROR::FREETYPE: Could not init FreeType Library')
|
||||||
|
// Load font as face
|
||||||
|
// face := FT_Face{}
|
||||||
|
mut font_path := 'RobotoMono-Regular.ttf'
|
||||||
|
if !os.file_exists(font_path) {
|
||||||
|
font_path = '/var/tmp/RobotoMono-Regular.ttf'
|
||||||
|
}
|
||||||
|
if !os.file_exists(font_path) {
|
||||||
|
println2('failed to load RobotoMono-Regular.ttf')
|
||||||
|
exit('')
|
||||||
|
}
|
||||||
|
# FT_Face face;
|
||||||
|
# if (FT_New_Face(ft, font_path.str, 0, &face))
|
||||||
|
// # if (FT_New_Face(ft, "/Library/Fonts/Courier New.ttf", 0, &face))
|
||||||
|
// # if (FT_New_Face(ft, "/System/Library/Fonts/Apple Color Emoji.ttc", 0, &face))
|
||||||
|
{
|
||||||
|
exit('ERROR::FREETYPE: Failed to load font')
|
||||||
|
}
|
||||||
|
// Set size to load glyphs as
|
||||||
|
# FT_Set_Pixel_Sizes(face, 0, font_size) ;
|
||||||
|
// Disable byte-alignment restriction
|
||||||
|
# glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
|
// Gen texture
|
||||||
|
// Load first 128 characters of ASCII set
|
||||||
|
mut chars := []gg.Character{}
|
||||||
|
f := Face {
|
||||||
|
cobj: 0
|
||||||
|
kek: 0
|
||||||
|
}
|
||||||
|
# f.cobj = &face;
|
||||||
|
// # for (GLubyte c = 0; c < 128; c++)
|
||||||
|
for c := 0; c < 128; c++ {
|
||||||
|
// ch := Character{}
|
||||||
|
// ch:=ft_load_char(face, c)
|
||||||
|
// # ch =gg__ft_load_char(&face, &c);
|
||||||
|
// ////////////////////////////////
|
||||||
|
mut ch := ft_load_char(f, long(c))
|
||||||
|
// s := utf32_to_str(uint(0x043f))
|
||||||
|
// s := 'п'
|
||||||
|
// ch = ft_load_char(f, s.utf32_code())
|
||||||
|
// # ch = gg__ft_load_char(f, 0x043f); // RUS P
|
||||||
|
// # unsigned long c = FT_Get_Char_Index(face, 0x043f );
|
||||||
|
// # printf("!!!!!!!!! %lu\n", c);
|
||||||
|
// # c = FT_Get_Char_Index(face, 0xd0bf );
|
||||||
|
// # printf("!!!!!!!!! %lu\n", c);
|
||||||
|
// # ch = gg__ft_load_char(f, 0xd0bf) ; // UTF 8
|
||||||
|
chars << ch
|
||||||
|
}
|
||||||
|
ch := Character{}
|
||||||
|
// # ch = gg__ft_load_char(f, 0x0000043f);
|
||||||
|
// # ch = gg__ft_load_char(f, 128169);
|
||||||
|
// chars.push(ch)
|
||||||
|
// Configure VAO
|
||||||
|
VAO := gl.gen_vertex_array()
|
||||||
|
println('new gg text context VAO=$VAO')
|
||||||
|
VBO := gl.gen_buffer()
|
||||||
|
gl.bind_vao(VAO)
|
||||||
|
gl.bind_buffer(GL_ARRAY_BUFFER, VBO)
|
||||||
|
// # glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
|
||||||
|
gl.enable_vertex_attrib_array(0)
|
||||||
|
gl.vertex_attrib_pointer(0, 4, GL_FLOAT, false, 4, 0)
|
||||||
|
// # glVertexAttribPointer(0, 4, GL_FLOAT,false, 4 * sizeof(GLfloat), 0);
|
||||||
|
// gl.bind_buffer(GL_ARRAY_BUFFER, uint(0))
|
||||||
|
// # glBindVertexArray(0);
|
||||||
|
mut ctx := &GG {
|
||||||
|
shader: shader,
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
scale: scale
|
||||||
|
VAO: VAO,
|
||||||
|
VBO: VBO,
|
||||||
|
chars: chars,
|
||||||
|
face: f
|
||||||
|
text_ctx: 0
|
||||||
|
}
|
||||||
|
ctx.init_utf8_runes()
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// A dirty hack to implement rendering of cyrillic letters.
|
||||||
|
// All UTF-8 must be supported.
|
||||||
|
fn (ctx mut GG) init_utf8_runes() {
|
||||||
|
s := '≈йцукенгшщзхъфывапролджэячсмитьбюЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮ'
|
||||||
|
println(s)
|
||||||
|
us := s.ustring()
|
||||||
|
for i := 0; i < us.len; i++ {
|
||||||
|
_rune := us.at(i)
|
||||||
|
ch := ft_load_char(ctx.face, _rune.utf32_code())
|
||||||
|
// ctx.utf_rune_map.set(rune, ch)
|
||||||
|
ctx.utf_runes << _rune
|
||||||
|
ctx.utf_chars << ch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn (ctx &GG) render_text(text string, x, y, scale float, color gx.Color) {
|
||||||
|
pub fn (ctx &GG) draw_text(_x, _y int, text string, cfg gx.TextCfg) {
|
||||||
|
// dont draw non ascii for now
|
||||||
|
/*
|
||||||
|
for i := 0; i < text.len; i++ {
|
||||||
|
c := text[i]
|
||||||
|
if int(c) > 128 {
|
||||||
|
// ctx.text_ctx._draw_text(_x, _y, '[NON ASCII]', cfg)
|
||||||
|
// return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// # glScissor(0,0,300,300);
|
||||||
|
utext := text.ustring_tmp()
|
||||||
|
// utext := text.ustring()
|
||||||
|
ctx.text_ctx._draw_text(_x, _y, utext, cfg)
|
||||||
|
// utext.free()
|
||||||
|
// # glScissor(0,0,ctx->width*2,ctx->height*2);
|
||||||
|
// gl.disable(GL_SCISSOR_TEST)// TODO
|
||||||
|
// #free(text.str);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (ctx &GG) draw_text_fast(_x, _y int, text ustring, cfg gx.TextCfg) {
|
||||||
|
ctx.text_ctx._draw_text(_x, _y, text, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO HACK with second text context
|
||||||
|
// fn (ctx &GG) _draw_text(_x, _y int, text string, cfg gx.TextCfg) {
|
||||||
|
fn (ctx &GG) _draw_text(_x, _y int, utext ustring, cfg gx.TextCfg) {
|
||||||
|
/*
|
||||||
|
if utext.s.contains('on_seg') {
|
||||||
|
println('\nat(0)')
|
||||||
|
println(utext.runes)
|
||||||
|
firstc := utext.at(0)
|
||||||
|
println('drawtext "$utext.s" len=$utext.s.len ulen=$utext.len x=$_x firstc=$firstc')
|
||||||
|
if firstc != ' ' {
|
||||||
|
exit('')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// println('scale=$ctx.scale size=$cfg.size')
|
||||||
|
if cfg.align == gx.ALIGN_RIGHT {
|
||||||
|
width := utext.len * 7
|
||||||
|
_x -= width + 10
|
||||||
|
}
|
||||||
|
x := float(_x) * ctx.scale// float(2)
|
||||||
|
// println('y=$_y height=$ctx.height')
|
||||||
|
// _y = _y * int(ctx.scale) //+ 26
|
||||||
|
_y = _y * int(ctx.scale) + ((cfg.size * ctx.scale) / 2) + 5 * ctx.scale
|
||||||
|
y := float(ctx.height - _y)
|
||||||
|
color := cfg.color
|
||||||
|
// Activate corresponding render state
|
||||||
|
ctx.shader.use()
|
||||||
|
ctx.shader.set_color('textColor', color)
|
||||||
|
# glActiveTexture(GL_TEXTURE0);
|
||||||
|
gl.bind_vao(ctx.VAO)
|
||||||
|
// Iterate through all characters
|
||||||
|
// utext := text.ustring()
|
||||||
|
for i := 0; i < utext.len; i++ {
|
||||||
|
_rune := utext.at(i)
|
||||||
|
// println('$i => $_rune')
|
||||||
|
mut ch := Character{}
|
||||||
|
if _rune.len == 1 {
|
||||||
|
idx := _rune[0]
|
||||||
|
if idx < 0 || idx >= ctx.chars.len {
|
||||||
|
println('BADE RUNE $_rune')
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ch = ctx.chars[_rune[0]]
|
||||||
|
}
|
||||||
|
else if _rune.len > 1 {
|
||||||
|
// TODO O(1) use map
|
||||||
|
for j := 0; j < ctx.utf_runes.len; j++ {
|
||||||
|
rune_j := ctx.utf_runes[j]
|
||||||
|
// if string_eq(ctx.utf_runes[j], rune) {
|
||||||
|
if rune_j.eq(_rune) {
|
||||||
|
ch = ctx.utf_chars[j]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ch.size.x == 0 {
|
||||||
|
// continue
|
||||||
|
}
|
||||||
|
// mut c := int(text[i])
|
||||||
|
// c = 128
|
||||||
|
// s := 'A'
|
||||||
|
// c := int(s[0])
|
||||||
|
// ch := ctx.chars[c]
|
||||||
|
xpos := x + float(ch.bearing.x) * 1
|
||||||
|
ypos := y - float(ch.size.y - ch.bearing.y) * 1
|
||||||
|
w := float(ch.size.x) * 1
|
||||||
|
h := float(ch.size.y) * 1
|
||||||
|
// Update VBO for each character
|
||||||
|
# GLfloat vertices[6][4] = {
|
||||||
|
# { xpos, ypos + h, 0.0, 0.0 },
|
||||||
|
# { xpos, ypos, 0.0, 1.0 },
|
||||||
|
# { xpos + w, ypos, 1.0, 1.0 },
|
||||||
|
# { xpos, ypos + h, 0.0, 0.0 },
|
||||||
|
# { xpos + w, ypos, 1.0, 1.0 },
|
||||||
|
# { xpos + w, ypos + h, 1.0, 0.0 }
|
||||||
|
# };
|
||||||
|
// t := glfw.get_time()
|
||||||
|
// Render glyph texture over quad
|
||||||
|
// t1 := glfw.get_time()
|
||||||
|
# glBindTexture(GL_TEXTURE_2D, ch.texture_id);
|
||||||
|
// Update content of VBO memory
|
||||||
|
gl.bind_buffer(GL_ARRAY_BUFFER, ctx.VBO)
|
||||||
|
// t2 := glfw.get_time()
|
||||||
|
// # glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // Be sure to use glBufferSubData and not glBufferData
|
||||||
|
# glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
|
||||||
|
// t3 := glfw.get_time()
|
||||||
|
// gl.bind_buffer(GL_ARRAY_BUFFER, uint(0))
|
||||||
|
// t4 := glfw.get_time()
|
||||||
|
// Render quad
|
||||||
|
gl.draw_arrays(GL_TRIANGLES, 0, 6)
|
||||||
|
// t5 := glfw.get_time()
|
||||||
|
// # if (glfw__get_time() - t > 0.001)
|
||||||
|
// {
|
||||||
|
// # printf("do_text = %f '%s' \n", glfw__get_time() - t, text.str);
|
||||||
|
// # printf("t1=%f, t2=%f, t3=%f, t4=%f, t5=%f\n\n\n", t1-t, t2-t1, t3-t2, t4-t3, t5-t4);
|
||||||
|
// }
|
||||||
|
// Now advance cursors for next glyph (note that advance is number of 1/64 pixels)
|
||||||
|
// Bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th pixels by 64 to get amount of pixels))
|
||||||
|
# x += (ch.advance >> 6) * 1;
|
||||||
|
}
|
||||||
|
gl.bind_vao(u32(0))
|
||||||
|
# glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
// runes.free()
|
||||||
|
// #free(runes.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (ctx &GG) draw_text_def(x, y int, text string) {
|
||||||
|
cfg := gx.TextCfg {
|
||||||
|
color: gx.BLACK,
|
||||||
|
size: DEFAULT_FONT_SIZE,
|
||||||
|
align: gx.ALIGN_LEFT,
|
||||||
|
}
|
||||||
|
ctx.draw_text(x, y, text, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update() {
|
||||||
|
// # ui__post_empty_event();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (c GG) circle(x, y, r int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c GG) fill_color(color gx.Color) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c GG) fill() {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c GG) move_to(x, y int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c GG) line_to(x, y int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c GG) stroke_width(size int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c GG) stroke_color(color gx.Color) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c GG) stroke() {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c GG) save() {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c GG) restore() {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c GG) intersect_scissor(x, y, w, h int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c GG) translate(x, y int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c GG) create_font(name, file string) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c GG) text(x, y int, text string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c GG) text_box(x, y, max int, text string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c GG) font_face(f string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c GG) font_size(size int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (c GG) text_align(a int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_image(file string) u32 {
|
||||||
|
println('gg create image "$file"')
|
||||||
|
if file.contains('twitch') {
|
||||||
|
return u32(0)// TODO
|
||||||
|
}
|
||||||
|
if !os.file_exists(file) {
|
||||||
|
println('gg create image no such file "$file"')
|
||||||
|
return u32(0)
|
||||||
|
}
|
||||||
|
texture := gl.gen_texture()
|
||||||
|
img := stbi.load(file)
|
||||||
|
gl.bind_2d_texture(texture)
|
||||||
|
img.tex_image_2d()
|
||||||
|
gl.generate_mipmap(GL_TEXTURE_2D)
|
||||||
|
img.free()
|
||||||
|
// println('gg end')
|
||||||
|
return texture
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (ctx &GG) draw_line_c(x, y, x2, y2 int, color gx.Color) {
|
||||||
|
C.glDeleteBuffers(1, &ctx.VAO)
|
||||||
|
C.glDeleteBuffers(1, &ctx.VBO)
|
||||||
|
ctx.shader.use()
|
||||||
|
ctx.shader.set_color('color', color)
|
||||||
|
vertices := [float(x), float(y), float(x2), float(y2)] !
|
||||||
|
gl.bind_vao(ctx.VAO)
|
||||||
|
gl.set_vbo(ctx.VBO, vertices, GL_STATIC_DRAW)
|
||||||
|
gl.vertex_attrib_pointer(0, 2, GL_FLOAT, false, 2, 0)
|
||||||
|
gl.enable_vertex_attrib_array(0)
|
||||||
|
gl.bind_vao(ctx.VAO)
|
||||||
|
gl.draw_arrays(GL_LINES, 0, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (c &GG) draw_line(x, y, x2, y2 int) {
|
||||||
|
c.draw_line_c(x, y, x2, y2, gx.GRAY)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (c &GG) draw_vertical(x, y, height int) {
|
||||||
|
c.draw_line(x, y, x, y + height)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn (ctx &GG) draw_image(x, y, w, h float, img stbi.Image) {
|
||||||
|
pub fn (ctx &GG) draw_image(x, y, w, h float, tex_id u32) {
|
||||||
|
// println('DRAW IMAGE $x $y $w $h $tex_id')
|
||||||
|
ctx.shader.use()
|
||||||
|
// ctx.shader.set_color('color', c)
|
||||||
|
ctx.shader.set_int('has_texture', 1)
|
||||||
|
// 4--1
|
||||||
|
// | |
|
||||||
|
// 3--2
|
||||||
|
vertices := [
|
||||||
|
x + w, y, 0, 1, 0, 0, 1, 1,
|
||||||
|
x + w, y + h, 0, 0, 1, 0, 1, 0,
|
||||||
|
x, y + h, 0, 0, 0, 1, 0, 0,
|
||||||
|
x, y, 0, 1, 1, 0, 0, 1,
|
||||||
|
] !
|
||||||
|
indices := [
|
||||||
|
0, 1, 3,// first triangle
|
||||||
|
1, 2, 3// second triangle
|
||||||
|
] !
|
||||||
|
// VAO := gl.gen_vertex_array()
|
||||||
|
// VBO := gl.gen_buffer()
|
||||||
|
gl.bind_vao(ctx.VAO)
|
||||||
|
gl.set_vbo(ctx.VBO, vertices, GL_STATIC_DRAW)
|
||||||
|
ebo := gl.gen_buffer()
|
||||||
|
gl.set_ebo(ebo, indices, GL_STATIC_DRAW)
|
||||||
|
gl.vertex_attrib_pointer(0, 3, GL_FLOAT, false, 8, 0)
|
||||||
|
gl.enable_vertex_attrib_array(0)
|
||||||
|
gl.vertex_attrib_pointer(1, 3, GL_FLOAT, false, 8, 3)
|
||||||
|
gl.enable_vertex_attrib_array(1)
|
||||||
|
gl.vertex_attrib_pointer(2, 2, GL_FLOAT, false, 8, 6)
|
||||||
|
gl.enable_vertex_attrib_array(2)
|
||||||
|
gl.bind_2d_texture(u32(tex_id))
|
||||||
|
gl.bind_vao(ctx.VAO)
|
||||||
|
gl.draw_elements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (c &GG) draw_empty_rect(x, y, w, h int, color gx.Color) {
|
||||||
|
c.draw_line_c(x, y, x + w, y, color)
|
||||||
|
c.draw_line_c(x, y, x, y + h, color)
|
||||||
|
c.draw_line_c(x, y + h, x + w, y + h, color)
|
||||||
|
c.draw_line_c(x + w, y, x + w, y + h, color)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,303 @@
|
||||||
|
module glfw
|
||||||
|
|
||||||
|
// Debugging a custom build
|
||||||
|
#flag darwin -L/var/tmp/glfw/src/
|
||||||
|
#flag darwin -lglfw
|
||||||
|
#flag linux -lglfw
|
||||||
|
#flag windows -I/usr/local/Cellar/glfw/3.2.1/include/
|
||||||
|
// #include <glad/glad.h> // !gen.go include GLFW.v
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
// #flag darwin -framework Carbon
|
||||||
|
// #flag darwin -framework Cocoa
|
||||||
|
// #flag darwin -framework CoreVideo
|
||||||
|
// #flag darwin -framework IOKit
|
||||||
|
// struct C.GL
|
||||||
|
// @GLFWwindow* C.glfwCreateWindow
|
||||||
|
// #int gconst_init = 0;
|
||||||
|
const (
|
||||||
|
RESIZABLE = 1
|
||||||
|
DECORATED = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
import const (
|
||||||
|
GLFW_RESIZABLE
|
||||||
|
GLFW_DECORATED
|
||||||
|
)
|
||||||
|
|
||||||
|
import const (
|
||||||
|
GLFW_KEY_ENTER
|
||||||
|
GLFW_KEY_A
|
||||||
|
GLFW_KEY_B
|
||||||
|
GLFW_KEY_P
|
||||||
|
GLFW_KEY_F
|
||||||
|
GLFW_KEY_M
|
||||||
|
GLFW_KEY_L
|
||||||
|
GLFW_KEY_V
|
||||||
|
GLFW_KEY_R
|
||||||
|
GLFW_KEY_D
|
||||||
|
GLFW_KEY_7
|
||||||
|
GLFW_KEY_Z
|
||||||
|
GLFW_KEY_UP
|
||||||
|
GLFW_KEY_DOWN
|
||||||
|
GLFW_KEY_UP
|
||||||
|
GLFW_KEY_LEFT
|
||||||
|
GLFW_KEY_RIGHT
|
||||||
|
GLFW_KEY_BACKSPACE
|
||||||
|
GLFW_KEY_ENTER
|
||||||
|
GLFW_KEY_ESCAPE
|
||||||
|
GLFW_KEY_N
|
||||||
|
GLFW_KEY_PERIOD
|
||||||
|
GLFW_KEY_SLASH
|
||||||
|
GLFW_KEY_F5
|
||||||
|
GLFW_KEY_F6
|
||||||
|
GLFW_KEY_MINUS
|
||||||
|
GLFW_KEY_EQUAL
|
||||||
|
GLFW_KEY_C
|
||||||
|
GLFW_KEY_G
|
||||||
|
GLFW_KEY_I
|
||||||
|
GLFW_KEY_J
|
||||||
|
GLFW_KEY_E
|
||||||
|
GLFW_KEY_K
|
||||||
|
GLFW_KEY_O
|
||||||
|
GLFW_KEY_T
|
||||||
|
GLFW_KEY_H
|
||||||
|
GLFW_KEY_L
|
||||||
|
GLFW_KEY_N
|
||||||
|
GLFW_KEY_U
|
||||||
|
GLFW_KEY_X
|
||||||
|
GLFW_KEY_W
|
||||||
|
GLFW_KEY_Y
|
||||||
|
GLFW_KEY_Q
|
||||||
|
GLFW_KEY_RIGHT_BRACKET
|
||||||
|
GLFW_KEY_LEFT_BRACKET
|
||||||
|
GLFW_KEY_8
|
||||||
|
GLFW_KEY_TAB
|
||||||
|
GLFW_KEY_COMMA
|
||||||
|
GLFW_KEY_QUESTION
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
KEY_ESCAPE = 256
|
||||||
|
KEY_LEFT_SUPER = 343
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
KeyUp = 265
|
||||||
|
KeyLeft = 263
|
||||||
|
KeyRight = 262
|
||||||
|
KeyDown = 264
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO COPY PASTA
|
||||||
|
struct WinCfg {
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
title string
|
||||||
|
ptr voidptr
|
||||||
|
borderless bool
|
||||||
|
is_modal int
|
||||||
|
is_browser bool
|
||||||
|
url string
|
||||||
|
}
|
||||||
|
|
||||||
|
// data *C.GLFWwindow
|
||||||
|
// TODO change data to cobj
|
||||||
|
struct Window {
|
||||||
|
data voidptr
|
||||||
|
title string
|
||||||
|
mx int
|
||||||
|
my int
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Size {
|
||||||
|
pub:
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Pos {
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
}
|
||||||
|
|
||||||
|
// type clickfn fn (window * GLFWwindow, button, action, mods int)
|
||||||
|
type clickfn fn (window voidptr, button, action, mods int)
|
||||||
|
|
||||||
|
fn init() {
|
||||||
|
C.glfwInit()
|
||||||
|
# glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
|
# glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||||
|
# glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||||
|
# glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn mouse_move(w * GLFWwindow, x, y double) {
|
||||||
|
fn mouse_move(w voidptr, x, y double) {
|
||||||
|
// #printf("%f : %f => %d \n", x,y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn create_window(title string, w, h int) * Window {
|
||||||
|
fn window_hint(key, val int) {
|
||||||
|
C.glfwWindowHint(key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_window(c WinCfg) *Window {
|
||||||
|
// TODO why i need this in stdlib? extern?
|
||||||
|
// # if (!gconst_init) { init_consts(); gconst_init = 1; }
|
||||||
|
// ChatsRepo
|
||||||
|
if c.borderless {
|
||||||
|
window_hint(GLFW_RESIZABLE, 0)
|
||||||
|
window_hint(GLFW_DECORATED, 0)
|
||||||
|
}
|
||||||
|
cwindow := C.glfwCreateWindow(c.width, c.height, c.title.str, 0, 0)
|
||||||
|
# if (!cwindow)
|
||||||
|
// if cwindow == 0
|
||||||
|
{
|
||||||
|
println('failed to credate glfw window')
|
||||||
|
C.glfwTerminate()
|
||||||
|
}
|
||||||
|
// # glfwSetCursorPosCallback(cwindow, glfw__mouse_move) ;
|
||||||
|
// C.glfwSetCursorPosCallback(cwindow, mouse_move)
|
||||||
|
C.printf('create window wnd=%p ptr==%p\n', cwindow, c.ptr)
|
||||||
|
C.glfwSetWindowUserPointer(cwindow, c.ptr)
|
||||||
|
// # void *a =glfwGetWindowUserPointer(cwindow);
|
||||||
|
// # printf("aaaaaa=%p d=%d\n", a,a);
|
||||||
|
window := &Window {
|
||||||
|
data: cwindow,
|
||||||
|
title: c.title,
|
||||||
|
}
|
||||||
|
// user_ptr: ptr,
|
||||||
|
// repo: repo,
|
||||||
|
// for !C.glfwWindowShouldClose(cwindow) {
|
||||||
|
// C.glfwPollEvents()
|
||||||
|
// wait_events()
|
||||||
|
// }
|
||||||
|
// C.glfwTerminate()
|
||||||
|
return window
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (w &Window) set_title(title string) {
|
||||||
|
C.glfwSetWindowTitle(w.data, title.str)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (w &Window) make_context_current() {
|
||||||
|
// ChatsRepo
|
||||||
|
kkk := 0
|
||||||
|
// println('making context current' )
|
||||||
|
C.glfwMakeContextCurrent(w.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn swap_interval(interval int) {
|
||||||
|
C.glfwSwapInterval(interval)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait_events() {
|
||||||
|
C.glfwWaitEvents()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_events() {
|
||||||
|
C.glfwPollEvents()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (w &Window) should_close() bool {
|
||||||
|
// ChatsRepo
|
||||||
|
return C.glfwWindowShouldClose(w.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (w &Window) swap_buffers() {
|
||||||
|
C.glfwSwapBuffers(w.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (w mut Window) onmousemove(cb voidptr) {
|
||||||
|
C.glfwSetCursorPosCallback(w.data, cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (w mut Window) set_mouse_button_callback(cb voidptr) {
|
||||||
|
C.glfwSetMouseButtonCallback(w.data, cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (w mut Window) on_click(cb voidptr) {
|
||||||
|
C.glfwSetMouseButtonCallback(w.data, cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (w &Window) set_scroll_callback(cb voidptr) {
|
||||||
|
C.glfwSetScrollCallback(w.data, cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (w &Window) on_scroll(cb voidptr) {
|
||||||
|
C.glfwSetScrollCallback(w.data, cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_empty_event() {
|
||||||
|
C.glfwPostEmptyEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (w mut Window) onkeydown(cb voidptr) {
|
||||||
|
C.glfwSetKeyCallback(w.data, cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (w mut Window) onchar(cb voidptr) {
|
||||||
|
C.glfwSetCharModsCallback(w.data, cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_time() double {
|
||||||
|
return C.glfwGetTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn key_pressed(wnd voidptr, key int) bool {
|
||||||
|
# return glfwGetKey(wnd, key) == GLFW_PRESS;
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO not mut
|
||||||
|
fn (w mut Window) get_clipboard_text() string {
|
||||||
|
return tos2(C.glfwGetClipboardString(w.data))
|
||||||
|
// # char *c = glfwGetClipboardString(w->data);
|
||||||
|
// # return tos_no_len(c);
|
||||||
|
// return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (w &Window) set_clipboard_text(s string) {
|
||||||
|
C.glfwSetClipboardString(w.data, s.str)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (w &Window) get_cursor_pos() Pos {
|
||||||
|
x := double(0)
|
||||||
|
y := double(0)
|
||||||
|
C.glfwGetCursorPos(w.data, &x, &y)
|
||||||
|
return Pos {
|
||||||
|
x: int(x)
|
||||||
|
y: int(y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (w &Window) user_ptr() voidptr {
|
||||||
|
return C.glfwGetWindowUserPointer(w.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (w &Window) set_user_ptr(ptr voidptr) {
|
||||||
|
C.glfwSetWindowUserPointer(w.data, ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn C.glfwGetVideoMode() C.GLFWvideoMode
|
||||||
|
|
||||||
|
fn get_monitor_size() Size {
|
||||||
|
# GLFWvidmode* mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||||||
|
// window_width = mode->width;
|
||||||
|
// window_height = mode->height;
|
||||||
|
// monitor := C.glfwGetPrimaryMonitor()
|
||||||
|
res := Size{}
|
||||||
|
# res.width=mode->width;
|
||||||
|
# res.height=mode->height;
|
||||||
|
// C.glfwGetMonitorPhysicalSize(monitor, &res.width, &res.height)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (size Size) str() string {
|
||||||
|
return '{$size.width, $size.height}'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_window_user_pointer(gwnd voidptr) voidptr {
|
||||||
|
return C.glfwGetWindowUserPointer(gwnd)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,314 @@
|
||||||
|
module glm
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
|
/*
|
||||||
|
#flag -lmyglm
|
||||||
|
# float* myglm_ortho(float, float, float, float);
|
||||||
|
# float* myglm_translate(float, float, float);
|
||||||
|
*/
|
||||||
|
// # float* myglm_rotate(float *m, float angle, float, float, float);
|
||||||
|
// # float* myglm_perspective(float, float, float, float);
|
||||||
|
// # float* myglm_look_at(glm__Vec3, glm__Vec3, glm__Vec3);
|
||||||
|
// # glm__Vec3 myglm_mult(glm__Vec3, glm__Vec3);
|
||||||
|
// # glm__Vec3 myglm_cross(glm__Vec3, glm__Vec3);
|
||||||
|
// # glm__Vec3 myglm_normalize(glm__Vec3);
|
||||||
|
struct Mat4 {
|
||||||
|
pub:
|
||||||
|
data *float
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Vec2 {
|
||||||
|
x float
|
||||||
|
y float
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Vec3 {
|
||||||
|
x float
|
||||||
|
y float
|
||||||
|
z float
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vec3(x, y, z float) Vec3 {
|
||||||
|
res := Vec3 {
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
z: z,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mat4(f *float) Mat4 {
|
||||||
|
res := Mat4 {
|
||||||
|
data: f
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (v Vec3) str() string {
|
||||||
|
return 'Vec3{ $v.x, $v.y, $v.z }'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (v Vec2) str() string {
|
||||||
|
return 'Vec3{ $v.x, $v.y }'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (m Mat4) str() string {
|
||||||
|
mut s := '[ '
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
if i != 0 {
|
||||||
|
s += ' '
|
||||||
|
}
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
val := m.data[i * 4 + j]
|
||||||
|
s += '${val:.2f} '
|
||||||
|
}
|
||||||
|
if i != 3 {
|
||||||
|
s += '\n'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s += ']'
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vec2(x, y int) Vec2 {
|
||||||
|
res := Vec2 {
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (a Vec3) add(b Vec3) Vec3 {
|
||||||
|
res := Vec3 {
|
||||||
|
x: a.x + b.x,
|
||||||
|
y: a.y + b.y,
|
||||||
|
z: a.z + b.z,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (a Vec3) sub(b Vec3) Vec3 {
|
||||||
|
res := Vec3 {
|
||||||
|
x: a.x - b.x,
|
||||||
|
y: a.y - b.y,
|
||||||
|
z: a.z - b.z,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn (a Vec3) mult(b Vec3) Vec3 {
|
||||||
|
// # return myglm_mult(a,b);
|
||||||
|
// }
|
||||||
|
fn (a Vec3) mult_scalar(b float) Vec3 {
|
||||||
|
res := Vec3 {
|
||||||
|
x: a.x * b,
|
||||||
|
y: a.y * b,
|
||||||
|
z: a.z * b,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (a Vec3) print() {
|
||||||
|
x := a.x
|
||||||
|
y := a.y
|
||||||
|
z := a.z
|
||||||
|
# printf("vec3{%f,%f,%f}\n",x,y,z);
|
||||||
|
// println('vec3{$x,$y,$z}')
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fn rotate(m Mat4, angle float, vec Vec3) Mat4 {
|
||||||
|
// # t_mat4 m;
|
||||||
|
// println('rotate done')
|
||||||
|
# return glm__mat4( myglm_rotate(m.data, angle, vec.x,vec.y,vec.z) );
|
||||||
|
return Mat4{}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// fn translate(vec Vec3) *float {
|
||||||
|
fn translate(m Mat4, v Vec3) Mat4 {
|
||||||
|
// # return glm__mat4(myglm_translate(vec.x,vec.y,vec.z) );
|
||||||
|
a := m.data
|
||||||
|
mut out := float_calloc(16)
|
||||||
|
x := v.x
|
||||||
|
y := v.y
|
||||||
|
z := v.z
|
||||||
|
a00 := a[0]a01 := a[1]a02 := a[2]a03 := a[3]
|
||||||
|
a10 := a[4]a11 := a[5]a12 := a[6]a13 := a[7]
|
||||||
|
a20 := a[8]a21 := a[9]a22 := a[10]a23 := a[11]
|
||||||
|
out[0] = a00 out[1] = a01 out[2] = a02 out[3] = a03
|
||||||
|
out[4] = a10 out[5] = a11 out[6] = a12 out[7] = a13
|
||||||
|
out[8] = a20 out[9] = a21 out[10] = a22 out[11] = a23
|
||||||
|
out[12] = a00 * x + a10 * y + a20 * z + a[12]
|
||||||
|
out[13] = a01 * x + a11 * y + a21 * z + a[13]
|
||||||
|
out[14] = a02 * x + a12 * y + a22 * z + a[14]
|
||||||
|
out[15] = a03 * x + a13 * y + a23 * z + a[15]
|
||||||
|
return mat4(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fn normalize(vec Vec3) Vec3 {
|
||||||
|
# return myglm_normalize(vec);
|
||||||
|
return Vec3{}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// https://github.com/g-truc/glm/blob/0ceb2b755fb155d593854aefe3e45d416ce153a4/glm/ext/matrix_clip_space.inl
|
||||||
|
fn ortho(left, right, bottom, top float) Mat4 {
|
||||||
|
println('glm ortho($left, $right, $bottom, $top)')
|
||||||
|
// mat<4, 4, T, defaultp> Result(static_cast<T>(1));
|
||||||
|
n := 16
|
||||||
|
mut res := float_calloc(n)
|
||||||
|
# res[0] = 2 / (right - left) ;
|
||||||
|
# res[5] = 2.0 / (top - bottom);
|
||||||
|
# res[10] = (1);
|
||||||
|
# res[12] = - (right + left) / (right - left);
|
||||||
|
# res[13] = - (top + bottom) / (top - bottom);
|
||||||
|
res[15] = 1
|
||||||
|
return mat4(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn scale(a *float, v Vec3) *float {
|
||||||
|
fn scale(m Mat4, v Vec3) Mat4 {
|
||||||
|
a := m.data
|
||||||
|
mut out := float_calloc(16)
|
||||||
|
x := v.x
|
||||||
|
y := v.y
|
||||||
|
z := v.z
|
||||||
|
out[0] = a[0] * v.x
|
||||||
|
out[1] = a[1] * x
|
||||||
|
out[2] = a[2] * x
|
||||||
|
out[3] = a[3] * x
|
||||||
|
out[4] = a[4] * y
|
||||||
|
out[5] = a[5] * y
|
||||||
|
out[6] = a[6] * y
|
||||||
|
out[7] = a[7] * y
|
||||||
|
out[8] = a[8] * z
|
||||||
|
out[9] = a[9] * z
|
||||||
|
out[10] = a[10] * z
|
||||||
|
out[11] = a[11] * z
|
||||||
|
out[12] = a[12]
|
||||||
|
out[13] = a[13]
|
||||||
|
out[14] = a[14]
|
||||||
|
out[15] = a[15]
|
||||||
|
return mat4(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn rotate_z(a *float, rad float) *float {
|
||||||
|
fn rotate_z(m Mat4, rad float) Mat4 {
|
||||||
|
a := m.data
|
||||||
|
mut out := float_calloc(16)
|
||||||
|
s := math.sin(rad)
|
||||||
|
c := math.cos(rad)
|
||||||
|
a00 := a[0]
|
||||||
|
a01 := a[1]
|
||||||
|
a02 := a[2]
|
||||||
|
a03 := a[3]
|
||||||
|
a10 := a[4]
|
||||||
|
a11 := a[5]
|
||||||
|
a12 := a[6]
|
||||||
|
a13 := a[7]
|
||||||
|
out[8] = a[8]
|
||||||
|
out[9] = a[9]
|
||||||
|
out[10] = a[10]
|
||||||
|
out[11] = a[11]
|
||||||
|
out[12] = a[12]
|
||||||
|
out[13] = a[13]
|
||||||
|
out[14] = a[14]
|
||||||
|
out[15] = a[15]
|
||||||
|
// Perform axis-specific matrix multiplication
|
||||||
|
out[0] = a00 * c + a10 * s
|
||||||
|
out[1] = a01 * c + a11 * s
|
||||||
|
out[2] = a02 * c + a12 * s
|
||||||
|
out[3] = a03 * c + a13 * s
|
||||||
|
out[4] = a10 * c - a00 * s
|
||||||
|
out[5] = a11 * c - a01 * s
|
||||||
|
out[6] = a12 * c - a02 * s
|
||||||
|
out[7] = a13 * c - a03 * s
|
||||||
|
return mat4(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn identity() Mat4 {
|
||||||
|
// 1 0 0 0
|
||||||
|
// 0 1 0 0
|
||||||
|
// 0 0 1 0
|
||||||
|
// 0 0 0 1
|
||||||
|
n := 16
|
||||||
|
mut res := float_calloc(sizeof(float) * n)
|
||||||
|
res[0] = 1
|
||||||
|
res[5] = 1
|
||||||
|
res[10] = 1
|
||||||
|
res[15] = 1
|
||||||
|
return mat4(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns *float without allocation
|
||||||
|
fn identity2(res *float) {
|
||||||
|
res[0] = 1
|
||||||
|
res[5] = 1
|
||||||
|
res[10] = 1
|
||||||
|
res[15] = 1
|
||||||
|
// # float f[16]={0};// for (int i =0;i<16;i++)
|
||||||
|
// # printf("!!%d\n", f[0]);
|
||||||
|
// # glm__identity2(&f);
|
||||||
|
// # gl__Shader_set_mat4(shader, tos2("projection"), f) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn identity3() []float {
|
||||||
|
res := [1.0, 0, 0, 0,
|
||||||
|
0, 1, 0, 0,
|
||||||
|
0, 0, 1, 0,
|
||||||
|
0, 0, 0, 1,
|
||||||
|
] !
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/toji/gl-matrix/blob/1549cf21dfa14a2bc845993485343d519cf064fe/src/gl-matrix/mat4.js
|
||||||
|
fn ortho_js(left, right, bottom, top float) *float {
|
||||||
|
mynear := 1
|
||||||
|
myfar := 1
|
||||||
|
lr := 1.0 / (left - right)
|
||||||
|
bt := 1.0 / (bottom - top)
|
||||||
|
nf := 1.0 / 1.0// (mynear -myfar)
|
||||||
|
# float* out = malloc (sizeof(float) * 16);
|
||||||
|
# out[0] = -2 * lr;
|
||||||
|
# out[1] = 0;
|
||||||
|
# out[2] = 0;
|
||||||
|
# out[3] = 0;
|
||||||
|
# out[4] = 0;
|
||||||
|
# out[5] = -2 * bt;
|
||||||
|
# out[6] = 0;
|
||||||
|
# out[7] = 0;
|
||||||
|
# out[8] = 0;
|
||||||
|
# out[9] = 0;
|
||||||
|
# out[10] = 2 * nf;
|
||||||
|
# out[11] = 0;
|
||||||
|
# out[12] = (left + right) * lr;
|
||||||
|
# out[13] = (top + bottom) * bt;
|
||||||
|
# out[14] = 1 * nf;//(far + near) * nf;
|
||||||
|
# out[15] = 1;
|
||||||
|
# return out;
|
||||||
|
f := 0.0
|
||||||
|
return &f
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn ortho_old(a, b, c, d float) *float {
|
||||||
|
// # return myglm_ortho(a,b,c,d);
|
||||||
|
// }
|
||||||
|
fn cross(a, b Vec3) Vec3 {
|
||||||
|
// # return myglm_cross(a,b);
|
||||||
|
return Vec3{}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fn perspective(degrees float, ratio float, a, b float) Mat4 {
|
||||||
|
// println('lang per degrees=$degrees ratio=$ratio a=$a b=$b')
|
||||||
|
// # printf("lang pers degrees=%f ratio=%f a=%f b=%f\n", degrees, ratio, a,b);
|
||||||
|
# return glm__mat4( myglm_perspective(degrees, ratio, a,b) ) ;
|
||||||
|
return Mat4{}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn look_at(eye, center, up Vec3) Mat4 {
|
||||||
|
# return glm__mat4( myglm_look_at(eye, center, up) ) ;
|
||||||
|
return Mat4{}
|
||||||
|
}
|
||||||
|
*/
|
|
@ -0,0 +1,77 @@
|
||||||
|
module gx
|
||||||
|
|
||||||
|
struct Color {
|
||||||
|
pub:
|
||||||
|
r int
|
||||||
|
g int
|
||||||
|
b int
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
BLUE = Color { r: 60, g: 126, b: 197 }
|
||||||
|
Blue = Color { r: 60, g: 126, b: 197 }
|
||||||
|
BlueLite = Color { r: 226, g: 233, b: 241 }
|
||||||
|
BLACK = Color { r: 0, g: 0, b: 0 }
|
||||||
|
WHITE = Color { r: 255, g: 255, b: 255 }
|
||||||
|
GRAY = Color { r: 223, g: 223, b: 223 }
|
||||||
|
GRAY_DARK = Color { r: 150, g: 150, b: 150 }
|
||||||
|
GRAY_LITE = Color { r: 245, g: 245, b: 245 }
|
||||||
|
BLUE_LITE = Color { r: 226, g: 233, b: 241 }
|
||||||
|
ORANGE = Color { r: 255, g: 140, b: 0 }
|
||||||
|
GREEN = Color { r: 0, g: 140, b: 0 }
|
||||||
|
RED = Color { r: 140, g: 0, b: 0 }
|
||||||
|
YELLOW = Color { r: 255, g: 255, b: 0 }
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ALIGN_LEFT = 1
|
||||||
|
ALIGN_RIGHT = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
struct TextCfg {
|
||||||
|
pub:
|
||||||
|
color Color
|
||||||
|
size int
|
||||||
|
align int
|
||||||
|
max_width int
|
||||||
|
family string
|
||||||
|
bold bool
|
||||||
|
mono bool
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Image {
|
||||||
|
mut:
|
||||||
|
obj voidptr
|
||||||
|
pub:
|
||||||
|
id int
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (img Image) is_empty() bool {
|
||||||
|
return isnil(img.obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (c Color) str() string {
|
||||||
|
return '{$c.r, $c.g, $c.b}'
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (a Color) eq(b Color) bool {
|
||||||
|
return a.r == b.r &&
|
||||||
|
a.g == b.g &&
|
||||||
|
a.b == b.b
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rgb(r, g, b int) Color {
|
||||||
|
res := Color {
|
||||||
|
r: r,
|
||||||
|
g: g,
|
||||||
|
b: b,
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn text_width_char(c char) int {
|
||||||
|
// return text_width(char2string(c))
|
||||||
|
// // return C.text_width_char(c)
|
||||||
|
// }
|
|
@ -0,0 +1,59 @@
|
||||||
|
module http
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
@size_t kek
|
||||||
|
type downloadfn fn (written int)
|
||||||
|
|
||||||
|
struct DownloadStruct {
|
||||||
|
stream voidptr
|
||||||
|
written int
|
||||||
|
cb downloadfn
|
||||||
|
}
|
||||||
|
|
||||||
|
fn download_cb(ptr voidptr, size, nmemb size_t, userp voidptr) int {
|
||||||
|
// # struct http__MemoryStruct *mem = (struct http__MemoryStruct *)userp;
|
||||||
|
data := &DownloadStruct(userp)
|
||||||
|
# size_t written = fwrite(ptr, size, nmemb, (FILE*)(data->stream));
|
||||||
|
// # printf("!!!%d\n", written);
|
||||||
|
# data->written += written;
|
||||||
|
if !isnil(data.cb) {
|
||||||
|
# data->cb(data->written);
|
||||||
|
}
|
||||||
|
# return written;
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn download_file_with_progress(url, out string, cb, cb_finished voidptr) {
|
||||||
|
curl := C.curl_easy_init()
|
||||||
|
if isnil(curl) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
# FILE* fp = fopen(out.str,"wb");
|
||||||
|
# curl_easy_setopt(curl, CURLOPT_URL, url.str);
|
||||||
|
C.curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, download_cb)
|
||||||
|
// # curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http__download_cb);
|
||||||
|
data := &DownloadStruct {
|
||||||
|
// stream:fp
|
||||||
|
cb: cb
|
||||||
|
}
|
||||||
|
# data->stream = fp;
|
||||||
|
# curl_easy_setopt(curl, CURLOPT_WRITEDATA, data);
|
||||||
|
# double d = 0;
|
||||||
|
# curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d);
|
||||||
|
# CURLcode res = curl_easy_perform(curl);
|
||||||
|
println('DONE!')
|
||||||
|
# curl_easy_cleanup(curl);
|
||||||
|
# fclose(fp);
|
||||||
|
# void (*finished)() =cb_finished; finished();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn download_file(url, out string) {
|
||||||
|
// println('\nDOWNLOAD FILE $out url=$url')
|
||||||
|
// -L follow redirects
|
||||||
|
// println('curl -L -o "$out" "$url"')
|
||||||
|
res := os.system('curl -s -L -o "$out" "$url"')
|
||||||
|
// res := os.system('curl -s -L -o "$out" "$url"')
|
||||||
|
// println(res)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
module http
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
struct LUEL {
|
||||||
|
age int
|
||||||
|
}
|
||||||
|
|
||||||
|
type downloadfn fn (written int)
|
||||||
|
|
||||||
|
struct DownloadStruct {
|
||||||
|
stream voidptr
|
||||||
|
written int
|
||||||
|
cb downloadfn
|
||||||
|
}
|
||||||
|
|
||||||
|
fn download_cb(ptr voidptr, size, nmemb size_t, userp voidptr) int {
|
||||||
|
// # struct http__MemoryStruct *mem = (struct http__MemoryStruct *)userp;
|
||||||
|
data := &DownloadStruct(userp)
|
||||||
|
# size_t written = fwrite(ptr, size, nmemb, (FILE*)(data->stream));
|
||||||
|
// # printf("!!!%d\n", written);
|
||||||
|
# data->written += written;
|
||||||
|
if !isnil(data.cb) {
|
||||||
|
# data->cb(data->written);
|
||||||
|
}
|
||||||
|
# return written;
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn download_file_with_progress(url, out string, cb, cb_finished voidptr) {
|
||||||
|
/*
|
||||||
|
curl := C.curl_easy_init()
|
||||||
|
if isnil(curl) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
# FILE* fp = fopen(out.str,"wb");
|
||||||
|
# curl_easy_setopt(curl, CURLOPT_URL, url.str);
|
||||||
|
C.curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, download_cb)
|
||||||
|
// # curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http__download_cb);
|
||||||
|
data := &DownloadStruct {
|
||||||
|
// stream:fp
|
||||||
|
cb: cb
|
||||||
|
}
|
||||||
|
# data->stream = fp;
|
||||||
|
# curl_easy_setopt(curl, CURLOPT_WRITEDATA, data);
|
||||||
|
# double d = 0;
|
||||||
|
# curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d);
|
||||||
|
# CURLcode res = curl_easy_perform(curl);
|
||||||
|
println('DONE!')
|
||||||
|
# curl_easy_cleanup(curl);
|
||||||
|
# fclose(fp);
|
||||||
|
# void (*finished)() =cb_finished; finished();
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
fn download_file(url, out string) {
|
||||||
|
// println('\nDOWNLOAD FILE $out url=$url')
|
||||||
|
// -L follow redirects
|
||||||
|
// println('curl -L -o "$out" "$url"')
|
||||||
|
os.system2('curl -s -L -o "$out" "$url"')
|
||||||
|
// res := os.system('curl -s -L -o "$out" "$url"')
|
||||||
|
// println(res)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
module http
|
||||||
|
|
||||||
|
fn download_file_with_progress(url, out string, cb, cb_finished voidptr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn download_file(url, out string) {
|
||||||
|
# HRESULT res = URLDownloadToFile(NULL, url.str, out.str, 0, NULL);
|
||||||
|
# if(res == S_OK) {
|
||||||
|
println('Download Ok')
|
||||||
|
# } else if(res == E_OUTOFMEMORY) {
|
||||||
|
println('Buffer length invalid, or insufficient memory')
|
||||||
|
# } else if(res == INET_E_DOWNLOAD_FAILURE) {
|
||||||
|
println('URL is invalid')
|
||||||
|
# } else {
|
||||||
|
# printf("Download error: %d\n", res);
|
||||||
|
# }
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
module http
|
||||||
|
|
||||||
|
struct Request {
|
||||||
|
pub:
|
||||||
|
// headers []string
|
||||||
|
headers map_string
|
||||||
|
method string
|
||||||
|
// cookies map[string]string
|
||||||
|
h string
|
||||||
|
cmd string
|
||||||
|
typ string // GET POST
|
||||||
|
data string
|
||||||
|
url string
|
||||||
|
ws_func voidptr
|
||||||
|
user_ptr voidptr
|
||||||
|
verbose bool
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Response {
|
||||||
|
pub:
|
||||||
|
body string
|
||||||
|
headers map_string
|
||||||
|
status_code int
|
||||||
|
}
|
||||||
|
|
||||||
|
// embed 'http'
|
||||||
|
fn get(url string) string {
|
||||||
|
if url == '' {
|
||||||
|
println2('http: empty get url')
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
mut req := new_request('GET', url, '')
|
||||||
|
resp := req.do()
|
||||||
|
return resp.body
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get2(url string) string {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post(url, data string) string {
|
||||||
|
req := new_request('POST', url, data)
|
||||||
|
resp := req.do()
|
||||||
|
return resp.body
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_request(typ, _url, _data string) *Request {
|
||||||
|
mut url := _url
|
||||||
|
mut data := _data
|
||||||
|
// req.headers['User-Agent'] = 'V $VERSION'
|
||||||
|
if typ == 'GET' && !url.contains('?') && data != '' {
|
||||||
|
println('zeroing data, to url')
|
||||||
|
url = '$url?$data'
|
||||||
|
data = ''
|
||||||
|
}
|
||||||
|
// req.headers = new_map(0, sizeof(string))// []string{}
|
||||||
|
return &Request {
|
||||||
|
typ: typ
|
||||||
|
url: _url
|
||||||
|
data: _data
|
||||||
|
ws_func: 0
|
||||||
|
user_ptr: 0
|
||||||
|
headers: new_map(0, sizeof(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fn (req &Request) do() Response {
|
||||||
|
mut resp := Response{}
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
fn (req mut Request) free() {
|
||||||
|
req.headers.free()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (resp mut Response) free() {
|
||||||
|
resp.headers.free()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (req mut Request) add_header(key, val string) {
|
||||||
|
// println('start add header')
|
||||||
|
// println('add header "$key" "$val"')
|
||||||
|
// println(key)
|
||||||
|
// println(val)
|
||||||
|
// h := '$key: $val'
|
||||||
|
// println('SET H')
|
||||||
|
// req.headers << h
|
||||||
|
req.headers[key] = val
|
||||||
|
// mut h := req.h
|
||||||
|
// h += ' -H "${key}: ${val}" '
|
||||||
|
// req.h = h
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,202 @@
|
||||||
|
module http
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#flag windows -I/usr/local/opt/curl/include
|
||||||
|
#flag darwin -lcurl
|
||||||
|
#flag windows -lcurl
|
||||||
|
#flag linux -lcurl
|
||||||
|
@size_t kek
|
||||||
|
@CURL* curl_easy_init
|
||||||
|
type wsfn fn (s string, ptr voidptr)
|
||||||
|
|
||||||
|
struct MemoryStruct {
|
||||||
|
size size_t
|
||||||
|
ws_func wsfn
|
||||||
|
user_ptr voidptr // for wsfn
|
||||||
|
strings []string
|
||||||
|
}
|
||||||
|
|
||||||
|
import const (
|
||||||
|
CURLOPT_WRITEFUNCTION
|
||||||
|
CURLOPT_SSL_VERIFYPEER
|
||||||
|
CURLOPT_HEADERFUNCTION
|
||||||
|
CURLOPT_WRITEDATA
|
||||||
|
CURLOPT_HEADERDATA
|
||||||
|
CURLOPT_FOLLOWLOCATION
|
||||||
|
CURLOPT_URL
|
||||||
|
CURLOPT_VERBOSE
|
||||||
|
CURLOPT_HTTP_VERSION
|
||||||
|
CURL_HTTP_VERSION_1_1
|
||||||
|
CURLOPT_HTTPHEADER
|
||||||
|
CURLOPT_POSTFIELDS
|
||||||
|
CURLOPT_CUSTOMREQUEST
|
||||||
|
CURLOPT_TCP_KEEPALIVE
|
||||||
|
CURLE_OK
|
||||||
|
)
|
||||||
|
|
||||||
|
// type C.CURLcode {
|
||||||
|
// }
|
||||||
|
fn C.curl_easy_strerror(curl voidptr) byteptr
|
||||||
|
|
||||||
|
fn C.curl_easy_perform(curl voidptr) C.CURLcode
|
||||||
|
|
||||||
|
fn write_fn(contents byteptr, size, nmemb int, _mem *MemoryStruct) int {
|
||||||
|
mut mem = _mem
|
||||||
|
// # printf("size =%d nmemb=%d contents=%s\n", size, nmemb, contents);
|
||||||
|
realsize := size * nmemb// TODO size_t ?
|
||||||
|
// if !isnil(mem.ws_func) {
|
||||||
|
# if (mem->ws_func)
|
||||||
|
{
|
||||||
|
C.printf('\n\nhttp_mac.m: GOT WS FUNC. size=%d\n', realsize)
|
||||||
|
// Skip negative and 0 junk chars in the WS string
|
||||||
|
mut start := 0
|
||||||
|
for i := 0; i < realsize; i++ {
|
||||||
|
// printf("char=%d %c\n", s[i], s[i]);
|
||||||
|
if contents[i] == 0 && start == 0 {
|
||||||
|
start = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contents += start + 1
|
||||||
|
// printf("GOOD CONTEnTS=%s\n", contents);
|
||||||
|
s := tos_no_len(contents)
|
||||||
|
// mem.ws_func('kek', 0)
|
||||||
|
# mem->ws_func(s, mem->user_ptr);
|
||||||
|
}
|
||||||
|
mut c := tos_no_len(contents)
|
||||||
|
c = c.trim_space()
|
||||||
|
// Need to clone because libcurl reuses this memory
|
||||||
|
mem.strings << c.clone()
|
||||||
|
return realsize
|
||||||
|
}
|
||||||
|
|
||||||
|
struct C.curl_slist { }
|
||||||
|
|
||||||
|
fn (req &Request) do() Response {
|
||||||
|
println('req.do() mac/linux url="$req.url" data="$req.data"')
|
||||||
|
// println('req.do() url="$req.url"')
|
||||||
|
/*
|
||||||
|
mut resp := Response {
|
||||||
|
headers: map[string]string{}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
mut headers := map[string]string{}
|
||||||
|
// no data at this point
|
||||||
|
chunk := MemoryStruct {
|
||||||
|
ws_func: req.ws_func
|
||||||
|
user_ptr: req.user_ptr
|
||||||
|
}
|
||||||
|
// header chunk
|
||||||
|
hchunk := MemoryStruct {
|
||||||
|
ws_func: 0
|
||||||
|
user_ptr: 0
|
||||||
|
}
|
||||||
|
// init curl
|
||||||
|
curl := C.curl_easy_init()
|
||||||
|
if isnil(curl) {
|
||||||
|
println2('curl init failed')
|
||||||
|
return Response{}
|
||||||
|
}
|
||||||
|
// options
|
||||||
|
// url2 := req.url.clone()
|
||||||
|
C.curl_easy_setopt(curl, CURLOPT_URL, req.url.cstr())// ..clone())
|
||||||
|
// C.curl_easy_setopt(curl, CURLOPT_URL, 'http://example.com')
|
||||||
|
// return resp
|
||||||
|
// curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
|
||||||
|
$if windows {
|
||||||
|
C.curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0)
|
||||||
|
}
|
||||||
|
C.curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_fn)
|
||||||
|
C.curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, write_fn)
|
||||||
|
C.curl_easy_setopt(curl, CURLOPT_WRITEDATA, &chunk)
|
||||||
|
C.curl_easy_setopt(curl, CURLOPT_HEADERDATA, &hchunk)
|
||||||
|
C.curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1)
|
||||||
|
if req.typ == 'POST' {
|
||||||
|
C.curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req.data.cstr())
|
||||||
|
C.curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, 'POST')
|
||||||
|
// req.headers << 'Content-Type: application/x-www-form-urlencoded'
|
||||||
|
}
|
||||||
|
// Add request headers
|
||||||
|
mut hlist := &C.curl_slist{!}
|
||||||
|
// for i, h := range req.headers {
|
||||||
|
for entry in req.headers.entries {
|
||||||
|
key := entry.key
|
||||||
|
val := req.headers[key]
|
||||||
|
h := '$key: $val'
|
||||||
|
hlist = C.curl_slist_append(hlist, h.cstr())
|
||||||
|
}
|
||||||
|
// curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, // (long)CURL_HTTP_VERSION_2TLS);ô`CÒÊ€9À
|
||||||
|
C.curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1)
|
||||||
|
if req.verbose {
|
||||||
|
C.curl_easy_setopt(curl, CURLOPT_VERBOSE, 1)
|
||||||
|
}
|
||||||
|
C.curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hlist)
|
||||||
|
C.curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1)
|
||||||
|
C.curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1)
|
||||||
|
println('bef easy()')
|
||||||
|
res := C.curl_easy_perform(curl)
|
||||||
|
println('after easy()')
|
||||||
|
# if (res != CURLE_OK )
|
||||||
|
{
|
||||||
|
err := C.curl_easy_strerror(res)
|
||||||
|
println('curl_easy_perform() failed: $err')
|
||||||
|
}
|
||||||
|
body := chunk.strings.join('')// tos_no_len(chunk.memory)
|
||||||
|
// chunk.strings.free()
|
||||||
|
// resp.headers = hchunk.strings
|
||||||
|
if hchunk.strings.len == 0 {
|
||||||
|
return Response{}
|
||||||
|
}
|
||||||
|
first_header := hchunk.strings.first()
|
||||||
|
mut status_code := 0
|
||||||
|
if first_header.contains('HTTP/') {
|
||||||
|
val := first_header.find_between(' ', ' ')
|
||||||
|
status_code = val.to_i()
|
||||||
|
}
|
||||||
|
// Build resp headers map
|
||||||
|
// println('building resp headers hchunk.strings.len')
|
||||||
|
for h in hchunk.strings {
|
||||||
|
// break
|
||||||
|
// println(h)
|
||||||
|
vals := h.split(':')
|
||||||
|
pos := h.index(':')
|
||||||
|
if pos == -1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if h.contains('Content-Type') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key := h.left(pos)
|
||||||
|
val := h.right(pos + 1)
|
||||||
|
// println('"$key" *** "$val"')
|
||||||
|
// val2 := val.trim_space()
|
||||||
|
// println('val2="$val2"')
|
||||||
|
headers[key] = val// val.trim_space()
|
||||||
|
}
|
||||||
|
// println('done')
|
||||||
|
// j.println(resp.status_code)
|
||||||
|
// println('body=')
|
||||||
|
// j.println(resp.body)
|
||||||
|
// j.println('headers=')
|
||||||
|
// j.println(hchunk.strings)
|
||||||
|
C.curl_easy_cleanup(curl)
|
||||||
|
println('end of req.do() url="$req.url"')
|
||||||
|
return Response {
|
||||||
|
body: body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unescape(s string) string {
|
||||||
|
return tos2(C.curl_unescape(s.cstr(), s.len))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn escape(s string) string {
|
||||||
|
return tos2(C.curl_escape(s.cstr(), s.len))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ////////////////
|
||||||
|
fn (req &Request) do2() Response {
|
||||||
|
mut resp := Response{}
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,203 @@
|
||||||
|
module http
|
||||||
|
|
||||||
|
#flag -lwininet
|
||||||
|
#flag -lurlmon
|
||||||
|
// #include <WinInet.h>
|
||||||
|
#include "urlmon.h"
|
||||||
|
#include <shlwapi.h>
|
||||||
|
// #LPWSTR winstring(string s);
|
||||||
|
// # bool ok = InternetReadFile(request, buf, BUF_MAX, &nr_read);
|
||||||
|
import const (
|
||||||
|
INTERNET_OPEN_TYPE_PRECONFIG
|
||||||
|
INTERNET_DEFAULT_HTTP_PORT
|
||||||
|
INTERNET_DEFAULT_HTTPS_PORT
|
||||||
|
INTERNET_SERVICE_HTTP
|
||||||
|
)
|
||||||
|
|
||||||
|
fn (req &Request) do() Response {
|
||||||
|
mut s := ''
|
||||||
|
emptyresp := Response{}
|
||||||
|
mut url := req.url
|
||||||
|
println('\n\nhttp.do() WIN URL="$url" TYP=$req.typ data="$req.data" headers.len=req.headers.len"')
|
||||||
|
println(req.headers)
|
||||||
|
is_ssl := req.url.starts_with('https://')
|
||||||
|
println('is ssl=$is_ssl')
|
||||||
|
mut pos := url.index('/')
|
||||||
|
url = url.right(pos + 2)
|
||||||
|
mut host := url
|
||||||
|
mut path := '/'
|
||||||
|
pos = url.index('/')
|
||||||
|
if pos > -1 {
|
||||||
|
host = url.left(pos)
|
||||||
|
host = host.clone()
|
||||||
|
path = url.right(pos)
|
||||||
|
}
|
||||||
|
// println('HOST="$host"')
|
||||||
|
// println('PATH="$path"')
|
||||||
|
mut headers := ''
|
||||||
|
mut resp_headers := ''
|
||||||
|
// for header in req.headers {
|
||||||
|
for entry in req.headers.entries {
|
||||||
|
// headers += '$header\r\n'
|
||||||
|
key := entry.key
|
||||||
|
val := req.headers[key]
|
||||||
|
headers += '$key: $val\r\n'
|
||||||
|
}
|
||||||
|
if req.typ == 'POST' {
|
||||||
|
headers += 'Content-Type: application/x-www-form-urlencoded'
|
||||||
|
}
|
||||||
|
// headers = headers.trim_space()
|
||||||
|
// println('!!! OLO REQ HEADERS WIN="$headers"')
|
||||||
|
data := req.data
|
||||||
|
// Retrieve default http user agent
|
||||||
|
// char httpUseragent[512];
|
||||||
|
// # char httpUseragent []= "";
|
||||||
|
user_agent := ''
|
||||||
|
// DWORD szhttpUserAgent = sizeof(httpUseragent);
|
||||||
|
// ObtainUserAgentString(0, httpUseragent, &szhttpUserAgent);
|
||||||
|
// # HINTERNET internet = InternetOpenA(httpUseragent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
|
||||||
|
internet := C.InternetOpenA(user_agent.str, INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, 0)
|
||||||
|
// # if (!internet)
|
||||||
|
if isnil(internet) {
|
||||||
|
println('InternetOpen() failed')
|
||||||
|
return emptyresp
|
||||||
|
}
|
||||||
|
// # INTERNET_PORT port = INTERNET_DEFAULT_HTTP_PORT;
|
||||||
|
port := int(if is_ssl{INTERNET_DEFAULT_HTTPS_PORT} else { INTERNET_DEFAULT_HTTP_PORT})
|
||||||
|
// if is_ssl {
|
||||||
|
// # port = INTERNET_DEFAULT_HTTPS_PORT;
|
||||||
|
// }
|
||||||
|
connect := C.InternetConnectA(internet, host.str, port, 0, 0, INTERNET_SERVICE_HTTP, 0, 0)
|
||||||
|
// # HINTERNET connect = InternetConnectA(internet, host.str, port, NULL, NULL,
|
||||||
|
// # INTERNET_SERVICE_HTTP, 0, 0);
|
||||||
|
# if (!connect)
|
||||||
|
if isnil(connect) {
|
||||||
|
e := C.GetLastError()
|
||||||
|
println('[windows] InternetConnect() failed')
|
||||||
|
C.printf('err=%d\n', e)
|
||||||
|
return emptyresp
|
||||||
|
}
|
||||||
|
flags := 0
|
||||||
|
// # DWORD flags =
|
||||||
|
#flags =
|
||||||
|
# INTERNET_FLAG_HYPERLINK | INTERNET_FLAG_IGNORE_CERT_CN_INVALID |
|
||||||
|
# INTERNET_FLAG_IGNORE_CERT_DATE_INVALID |
|
||||||
|
# INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP |
|
||||||
|
# INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS | INTERNET_FLAG_NO_AUTH |
|
||||||
|
# INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_UI |
|
||||||
|
# INTERNET_FLAG_NO_COOKIES | // FUCK YOU MICROSOFT
|
||||||
|
# INTERNET_FLAG_KEEP_CONNECTION |
|
||||||
|
# INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_RELOAD ;
|
||||||
|
if is_ssl {
|
||||||
|
#flags = flags | INTERNET_FLAG_SECURE;
|
||||||
|
}
|
||||||
|
request := C.HttpOpenRequest(connect, req.typ.str, path.str, 'HTTP/1.1', 0, 0, flags, 0)
|
||||||
|
// request := C.InternetOpenUrl(connect, req.typ.str, path.str, 'HTTP/1.1', 0, 0, flags, 0)
|
||||||
|
// # HINTERNET request = HttpOpenRequest(connect, req->typ.str, path.str, "HTTP/1.1",
|
||||||
|
// # NULL, NULL, flags, NULL);
|
||||||
|
// # if (!request)
|
||||||
|
if isnil(request) {
|
||||||
|
println('HttpOpenRequest() failed')
|
||||||
|
return emptyresp
|
||||||
|
}
|
||||||
|
// println('LEN BEFORE SEND=$headers.len ; $headers')
|
||||||
|
# bool ret =HttpSendRequest(request, headers.str, -1, data.str, data.len);
|
||||||
|
# printf("RET=%d\n", ret);
|
||||||
|
# int e = GetLastError();
|
||||||
|
# printf("e=%d\n", e);
|
||||||
|
// Get response headers
|
||||||
|
// Todo call twice to get len
|
||||||
|
# LPSTR h_buf = malloc(1024);
|
||||||
|
# DWORD dwSize = 1024;
|
||||||
|
// LPVOID lpOutBuffer=malloc(dwSize);
|
||||||
|
# HttpQueryInfo(request, HTTP_QUERY_RAW_HEADERS_CRLF,
|
||||||
|
# h_buf,&dwSize,NULL);
|
||||||
|
# printf(" resp HEADERS %s\n", h_buf);
|
||||||
|
// Get response body
|
||||||
|
// # const int BUF_MAX = 1024;
|
||||||
|
// # TCHAR buf[BUF_MAX + 1];
|
||||||
|
mut buf := [1025]byte
|
||||||
|
mut nr_read := 0
|
||||||
|
BUF_MAX := 1024
|
||||||
|
// ok := C.InternetReadFile(request, buf, BUF_MAX, &nr_read)
|
||||||
|
// # DWORD dwRead = 0;
|
||||||
|
// /println('calling InternetReadFile()')
|
||||||
|
// # bool ok = InternetReadFile(request, buf, BUF_MAX, &nr_read);
|
||||||
|
// # if (!ok)
|
||||||
|
// {
|
||||||
|
// println('read not ok')
|
||||||
|
// # int e = GetLastError();
|
||||||
|
// # printf("%d\n", e);
|
||||||
|
// }
|
||||||
|
// # printf("dwread=%d\n", dwRead);
|
||||||
|
// # while ((InternetReadFile(request, buf, BUF_MAX, &nr_read)) && nr_read > 0)
|
||||||
|
for
|
||||||
|
{
|
||||||
|
println('111')
|
||||||
|
ok := C.InternetReadFile(request, buf, BUF_MAX, &nr_read)
|
||||||
|
println('222')
|
||||||
|
if !ok {
|
||||||
|
println('InternetReadFile() not ok ')
|
||||||
|
}
|
||||||
|
if ok && nr_read == 0 {
|
||||||
|
println('ok && nr read == 0, breaking')
|
||||||
|
C.printf('buf broken="%s"\n', buf)
|
||||||
|
if req.url.contains('websocket') {
|
||||||
|
println('win sleeping 2')
|
||||||
|
time.sleep(2)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
println('ireadfile()')
|
||||||
|
buf[nr_read] = 0
|
||||||
|
C.printf('buf="%s"\n', buf)
|
||||||
|
s += tos2(buf)// TODO perf
|
||||||
|
nr_read = 0
|
||||||
|
}
|
||||||
|
C.InternetCloseHandle(request)
|
||||||
|
C.InternetCloseHandle(connect)
|
||||||
|
C.InternetCloseHandle(internet)
|
||||||
|
# resp_headers = tos2(h_buf);
|
||||||
|
hh := resp_headers.split('\n')
|
||||||
|
mut resp := Response {
|
||||||
|
body: s
|
||||||
|
headers: map[string]string{}
|
||||||
|
// headers: resp_headers
|
||||||
|
}
|
||||||
|
// println('gen hh')
|
||||||
|
for h in hh {
|
||||||
|
// println('\n!')
|
||||||
|
// println(h)
|
||||||
|
vals := h.split(':')
|
||||||
|
pos := h.index(':')
|
||||||
|
if pos == -1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key := h.left(pos)
|
||||||
|
val := h.right(pos + 1)
|
||||||
|
// println('$key => $val')
|
||||||
|
resp.headers[key] = val.trim_space()
|
||||||
|
}
|
||||||
|
println('END OF WIN req.do($req.url)')
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
fn escape(s string) string {
|
||||||
|
# DWORD size=1;
|
||||||
|
# char *escaped = NULL;
|
||||||
|
# char *empty_string = NULL;
|
||||||
|
# HRESULT res = UrlEscapeA(s.str, empty_string, &size, URL_ESCAPE_PERCENT | URL_ESCAPE_SEGMENT_ONLY);
|
||||||
|
# if (res == E_POINTER)
|
||||||
|
{
|
||||||
|
# escaped = HeapAlloc(GetProcessHeap(), 0, size);
|
||||||
|
# if (!escaped)
|
||||||
|
# return s;
|
||||||
|
# UrlEscapeA(s.str, escaped, &size, URL_ESCAPE_PERCENT | URL_ESCAPE_SEGMENT_ONLY);
|
||||||
|
# return tos2(escaped);
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
fn C.InternetReadFile(voidptr, voidptr, int, intptr) bool
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
module json
|
||||||
|
|
||||||
|
// #include "json/cJSON/cJSON.c"
|
||||||
|
#include "json/cJSON/cJSON.h"
|
||||||
|
struct C.cJSON {
|
||||||
|
valueint int
|
||||||
|
valuestring byteptr
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jsdecode_int(root *C.cJSON) int {
|
||||||
|
if isnil(root) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return root.valueint
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jsdecode_string(root *C.cJSON) string {
|
||||||
|
if isnil(root) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
if isnil(root.valuestring) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
// println('jsdecode string valuestring="$root.valuestring"')
|
||||||
|
// return tos(root.valuestring, _strlen(root.valuestring))
|
||||||
|
return tos_clone(root.valuestring)// , _strlen(root.valuestring))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jsdecode_bool(root *C.cJSON) bool {
|
||||||
|
if isnil(root) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return C.cJSON_IsTrue(root)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ///////////////////
|
||||||
|
fn jsencode_int(val int) *C.cJSON {
|
||||||
|
return C.cJSON_CreateNumber(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jsencode_bool(val bool) *C.cJSON {
|
||||||
|
return C.cJSON_CreateBool(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jsencode_string(val string) *C.cJSON {
|
||||||
|
clone := val.clone()
|
||||||
|
return C.cJSON_CreateString(clone.str)
|
||||||
|
// return C.cJSON_CreateString2(val.str, val.len)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ///////////////////////
|
||||||
|
// user := decode_User(json_parse(js_string_var))
|
||||||
|
fn json_parse(s string) *C.cJSON {
|
||||||
|
return C.cJSON_Parse(s.str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// json_string := json_print(encode_User(user))
|
||||||
|
fn json_print(json *C.cJSON) string {
|
||||||
|
s := C.cJSON_PrintUnformatted(json)
|
||||||
|
return tos(s, _strlen(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// / cjson wrappers
|
||||||
|
// fn json_array_for_each(val, root *C.cJSON) {
|
||||||
|
// #cJSON_ArrayForEach (val ,root)
|
||||||
|
// }
|
|
@ -0,0 +1,55 @@
|
||||||
|
// 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 math
|
||||||
|
|
||||||
|
const (
|
||||||
|
PI = 3.14159265358979323846264338327950288419716939937510582097494459
|
||||||
|
)
|
||||||
|
|
||||||
|
fn abs(a f64) f64 {
|
||||||
|
if a < 0 {
|
||||||
|
return -a
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cos(a f64) f64 {
|
||||||
|
return C.cos(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn max(a, b f64) f64 {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
fn min(a, b f64) f64 {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pow(a, b f64) f64 {
|
||||||
|
return C.pow(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn radians(degrees f64) f64 {
|
||||||
|
return degrees * (PI / 180.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn round(f f64) f64 {
|
||||||
|
return C.round(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sin(a f64) f64 {
|
||||||
|
return C.sin(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sqrt(a f64) f64 {
|
||||||
|
return C.sqrt(a)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,490 @@
|
||||||
|
module os
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
const (
|
||||||
|
args = []string
|
||||||
|
)
|
||||||
|
|
||||||
|
struct FILE {
|
||||||
|
}
|
||||||
|
|
||||||
|
struct File {
|
||||||
|
cfile *FILE
|
||||||
|
}
|
||||||
|
|
||||||
|
import const (
|
||||||
|
SEEK_SET
|
||||||
|
SEEK_END
|
||||||
|
)
|
||||||
|
|
||||||
|
fn init_os_args(argc int, c voidptr) []string {
|
||||||
|
mut args := []string
|
||||||
|
# char** argv = (char**) c;
|
||||||
|
for i := 0; i < argc; i++ {
|
||||||
|
// # printf("ARG %d = '%s'\n", i, argv[i]);
|
||||||
|
arg := ''
|
||||||
|
# arg = tos(argv[i], strlen(argv[i]));
|
||||||
|
args << arg
|
||||||
|
}
|
||||||
|
# os__args = args;
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_windows_cmd_line(cmd byteptr) {
|
||||||
|
s := tos2(cmd)
|
||||||
|
vals := s.split(' ')
|
||||||
|
println(vals)
|
||||||
|
# os__args = vals;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read_file reads the file in `path` and returns the contents.
|
||||||
|
// TODO return `?string`
|
||||||
|
pub fn read_file(path string) string {
|
||||||
|
res := ''
|
||||||
|
# FILE *f = fopen(path.str, "r");
|
||||||
|
# if (!f) return tos("", 0);
|
||||||
|
# fseek(f, 0, SEEK_END);
|
||||||
|
# long fsize = ftell(f);
|
||||||
|
// # fseek(f, 0, SEEK_SET); //same as rewind(f);
|
||||||
|
# rewind(f);
|
||||||
|
# char *string = malloc(fsize + 1);
|
||||||
|
# fread(string, fsize, 1, f);
|
||||||
|
# fclose(f);
|
||||||
|
# string[fsize] = 0;
|
||||||
|
// # printf("RFILE= %s\n", string);
|
||||||
|
# res = tos(string, fsize);
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f File) read_rune() string {
|
||||||
|
# if (!f.cfile) return tos("", 0);
|
||||||
|
c := malloc(1)
|
||||||
|
C.fread(c, 1, 1, f.cfile)
|
||||||
|
return tos(c, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// `file_size` returns the size of the file located in `path`.
|
||||||
|
pub fn file_size(path string) int {
|
||||||
|
# struct stat s;
|
||||||
|
# stat(path.str, &s);
|
||||||
|
// # if (S_ISLNK(s.st_mode)) return -1;
|
||||||
|
# return s.st_size;
|
||||||
|
// //////////////////////
|
||||||
|
# FILE *f = fopen(path.str, "r");
|
||||||
|
# if (!f) return 0;
|
||||||
|
# fseek(f, 0, SEEK_END);
|
||||||
|
# long fsize = ftell(f);
|
||||||
|
// # fseek(f, 0, SEEK_SET); //same as rewind(f);
|
||||||
|
# rewind(f);
|
||||||
|
# return fsize;
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn file_last_mod_unix(path string) int {
|
||||||
|
# struct stat attr;
|
||||||
|
# stat(path.str, &attr);
|
||||||
|
# return attr.st_mtime ;
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub fn file_last_mod_time(path string) time.Time {
|
||||||
|
return time.now()
|
||||||
|
q := C.tm{}
|
||||||
|
# struct stat attr;
|
||||||
|
# stat(path.str, &attr);
|
||||||
|
// # q = attr.st_mtime;
|
||||||
|
# struct tm * now = localtime(&attr.st_mtime);
|
||||||
|
# q = *now;
|
||||||
|
# printf("Last modified time: %s", ctime(&attr.st_mtime));
|
||||||
|
return time.convert_ctime(q)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// `read_lines` reads the file in `path` into an array of lines.
|
||||||
|
pub fn read_lines(path string) []string {
|
||||||
|
return read_file_lines(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_file_into_lines(path string) []string {
|
||||||
|
return read_file_lines(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_file_into_ulines(path string) []ustring {
|
||||||
|
lines := read_file_into_lines(path)
|
||||||
|
// mut ulines := new_array(0, lines.len, sizeof(ustring))
|
||||||
|
mut ulines := []ustring
|
||||||
|
for myline in lines {
|
||||||
|
// ulines[i] = ustr
|
||||||
|
ulines << myline.ustring()
|
||||||
|
}
|
||||||
|
return ulines
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
BUF_SIZE = 5000
|
||||||
|
)
|
||||||
|
|
||||||
|
fn read_file_lines(path string) []string {
|
||||||
|
// println('read file $path into lines')
|
||||||
|
mut res := []string
|
||||||
|
# char buf[os__BUF_SIZE];
|
||||||
|
# FILE *fp = fopen(path.str, "rb");
|
||||||
|
# if (!fp)
|
||||||
|
{
|
||||||
|
println('failed to open file "$path"')
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
# while (fgets(buf, os__BUF_SIZE, fp) != NULL)
|
||||||
|
{
|
||||||
|
mut val := ''
|
||||||
|
# buf[strlen(buf) - 1] = '\0'; // eat the newline fgets() stores
|
||||||
|
#ifdef windows
|
||||||
|
# if (buf[strlen(buf)-2] == 13)
|
||||||
|
# buf[strlen(buf) - 2] = '\0'; // eat the newline fgets() stores
|
||||||
|
#endif
|
||||||
|
// # printf("%s\n", buf);
|
||||||
|
# val=tos_clone(buf) ;
|
||||||
|
// for i := 0; i < val.len; i++ {
|
||||||
|
// C.printf('%d) %c %d\n', i, val.str[i], val.str[i])
|
||||||
|
// }
|
||||||
|
#ifdef windows
|
||||||
|
// if val.str[val.len - 1] == 13 {
|
||||||
|
if val[val.len - 1] == 13 {
|
||||||
|
// TODO
|
||||||
|
// val.len--
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// println('QQQ read line="$val"')
|
||||||
|
res << val
|
||||||
|
}
|
||||||
|
# fclose(fp);
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append_to_file(file, s string) {
|
||||||
|
# FILE* fp = fopen(file.str, "a");
|
||||||
|
# fputs(s.str, fp);
|
||||||
|
# fputs("\n", fp);
|
||||||
|
# fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Reader {
|
||||||
|
fp *FILE
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FileInfo {
|
||||||
|
name string
|
||||||
|
size int
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn open(file string) File? {
|
||||||
|
// return open_file(file)
|
||||||
|
// }
|
||||||
|
pub fn open(path string) File {
|
||||||
|
return open_file(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_file(file string) File {
|
||||||
|
return create_file2(file, 'r')
|
||||||
|
}
|
||||||
|
|
||||||
|
// `create` creates a file at a specified location and returns a writable `File` object.
|
||||||
|
pub fn create(path string) File {
|
||||||
|
return create_file(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_append(path string) File {
|
||||||
|
return create_file(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_file(file string) File {
|
||||||
|
return create_file2(file, 'w')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_file_a(file string) File {
|
||||||
|
return create_file2(file, 'a')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open_file_a(file string) File {
|
||||||
|
return create_file2(file, 'a')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_file2(file string, mode string) File {
|
||||||
|
res := File {
|
||||||
|
cfile: C.fopen(file.cstr(), mode.cstr())
|
||||||
|
}
|
||||||
|
if isnil(res.cfile) {
|
||||||
|
println('coudlnt create file "$file"')
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f File) append(s string) {
|
||||||
|
ss := s.clone()
|
||||||
|
C.fputs(ss.cstr(), f.cfile)
|
||||||
|
// ss.free()
|
||||||
|
// C.fwrite(s.str, 1, s.len, f.cfile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert any value to []byte (LittleEndian) and write it
|
||||||
|
// for example if we have write(7, 4), "07 00 00 00" gets written
|
||||||
|
// write(0x1234, 2) => "34 12"
|
||||||
|
fn (f File) write(data voidptr, size int) {
|
||||||
|
C.fwrite(data, 1, size, f.cfile)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f File) write_at(data voidptr, size, pos int) {
|
||||||
|
C.fseek(f.cfile, pos, SEEK_SET)
|
||||||
|
C.fwrite(data, 1, size, f.cfile)
|
||||||
|
C.fseek(f.cfile, 0, SEEK_END)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f File) appendln(s string) {
|
||||||
|
// C.fwrite(s.str, 1, s.len, f.cfile)
|
||||||
|
// ss := s.clone()
|
||||||
|
// TODO perf
|
||||||
|
C.fputs(s.cstr(), f.cfile)
|
||||||
|
// ss.free()
|
||||||
|
C.fputs('\n', f.cfile)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (f File) close() {
|
||||||
|
C.fclose(f.cfile)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close_file(fp *FILE) {
|
||||||
|
$if windows {
|
||||||
|
}
|
||||||
|
# if (fp)
|
||||||
|
C.fclose(fp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// `system2` starts the specified command, waits for it to complete, and returns its code.
|
||||||
|
pub fn system2(cmd string) int {
|
||||||
|
cstr := cmd.clone()
|
||||||
|
ret := int(C.system(cstr.cstr()))
|
||||||
|
// println(' system2 ret=$ret cmd="$s"')
|
||||||
|
if ret == -1 {
|
||||||
|
os.print_c_errno()
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn popen(path string) *FILE {
|
||||||
|
cpath := path.cstr()
|
||||||
|
$if windows {
|
||||||
|
return C._popen(cpath, 'r')
|
||||||
|
}
|
||||||
|
$else {
|
||||||
|
return C.popen(cpath, 'r')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO rename to run or exec (system doesnt return a string)
|
||||||
|
// `system` starts the specified command, waits for it to complete, and returns its output.
|
||||||
|
// TODO merge the two functions.
|
||||||
|
pub fn system(cmd string) string {
|
||||||
|
// println('OS SYSTEM($s)')
|
||||||
|
res := ''
|
||||||
|
ss := '$cmd 2>&1'
|
||||||
|
_ := 0// TODO DOLLAR TOKEN
|
||||||
|
f := popen(ss)// cmd)
|
||||||
|
// # if (!f)
|
||||||
|
if isnil(f) {
|
||||||
|
println('popen $cmd failed')
|
||||||
|
}
|
||||||
|
#define MAX 1000
|
||||||
|
# char buf[MAX];
|
||||||
|
// # char* buf = malloc(MAX);
|
||||||
|
// j# sleep(1);
|
||||||
|
// # if (!fgets(buf, MAX, f)) {
|
||||||
|
// jprintln('first get failed')
|
||||||
|
// jos.print_c_errno()
|
||||||
|
// j# }
|
||||||
|
# while (fgets(buf, MAX, f) != NULL) {
|
||||||
|
// # printf("popen buf=%s\n", buf);
|
||||||
|
# res = string_add(res, tos(buf, strlen(buf)));
|
||||||
|
# }
|
||||||
|
// println(res)
|
||||||
|
return res.trim_space()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn system_into_lines(s string) []string {
|
||||||
|
mut res := []string
|
||||||
|
cmd := '$s 2>&1'
|
||||||
|
#ifdef windows
|
||||||
|
# FILE* f = _popen(cmd.str, "r");
|
||||||
|
#else
|
||||||
|
# FILE* f = popen(cmd.str, "r");
|
||||||
|
#endif
|
||||||
|
#define MAX 5000
|
||||||
|
// # char buf[MAX];
|
||||||
|
# char * buf = malloc(sizeof(char) * MAX);
|
||||||
|
# while (fgets(buf, MAX, f) != NULL)
|
||||||
|
{
|
||||||
|
val := ''
|
||||||
|
# buf[strlen(buf) - 1] = '\0'; // eat the newline fgets() stores
|
||||||
|
# val=tos_clone(buf);
|
||||||
|
res << val
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// `getenv` returns the value of the environment variable named by the key.
|
||||||
|
pub fn getenv(key string) string {
|
||||||
|
s := C.getenv(key.cstr())
|
||||||
|
if isnil(s) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
return tos2(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit(reason string) {
|
||||||
|
println2('exit(): $reason')
|
||||||
|
log(reason)
|
||||||
|
C.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit1(reason string) {
|
||||||
|
println2('exit(): $reason')
|
||||||
|
C.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// `file_exists` returns true if `path` exists.
|
||||||
|
pub fn file_exists(path string) bool {
|
||||||
|
// # return access( path.str, F_OK ) != -1 ;
|
||||||
|
res := false
|
||||||
|
#ifdef windows
|
||||||
|
# res = _access( path.str, 0 ) != -1 ;
|
||||||
|
#else
|
||||||
|
# res = access( path.str, 0 ) != -1 ;
|
||||||
|
#endif
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// `mkdir` creates a new directory with the specified path.
|
||||||
|
pub fn mkdir(path string) {
|
||||||
|
$if windows {
|
||||||
|
path = path.replace('/', '\\')
|
||||||
|
C.CreateDirectory(path.cstr(), 0)
|
||||||
|
}
|
||||||
|
$else {
|
||||||
|
println('AAAAAAAA $$ "$path"')
|
||||||
|
C.mkdir(path.cstr(), 511)// S_IRWXU | S_IRWXG | S_IRWXO
|
||||||
|
// os.system2('mkdir -p $path')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// `rm` removes file in `path`.
|
||||||
|
pub fn rm(path string) {
|
||||||
|
$if windows {
|
||||||
|
// os.system2('del /f $path')
|
||||||
|
}
|
||||||
|
$else {
|
||||||
|
C.remove(path.cstr())
|
||||||
|
}
|
||||||
|
// C.unlink(path.cstr())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rmdir(path string, guard string) {
|
||||||
|
if !path.contains(guard) {
|
||||||
|
println('rmdir canceled because the path doesnt contain $guard')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
println2('rmdir "$path"')
|
||||||
|
#ifndef windows
|
||||||
|
os.system('rm -rf "$path"')
|
||||||
|
#else
|
||||||
|
os.system('rmdir /s /q "$path"')
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unzip(path, out string) {
|
||||||
|
$if windows {
|
||||||
|
// TODO native string
|
||||||
|
// TODO handle older Windows
|
||||||
|
// The only way to unzip a file without installing dependencies is to use PowerShell + .NET
|
||||||
|
# char *s="powershell.exe -nologo -noprofile -command \"& { Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::ExtractToDirectory('PATH', 'OUT'); }\" ";
|
||||||
|
mut cmd := ''
|
||||||
|
# cmd = tos(s, strlen(s));
|
||||||
|
cmd = cmd.replace('PATH', path)
|
||||||
|
cmd = cmd.replace('OUT', out)
|
||||||
|
os.system(cmd)
|
||||||
|
}
|
||||||
|
$else {
|
||||||
|
os.system('unzip -o -d "$out" "$path"')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_c_errno() {
|
||||||
|
# printf("errno=%d err='%s'\n", errno, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn basedir(path string) string {
|
||||||
|
pos := path.last_index('/')
|
||||||
|
if pos == -1 {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
return path.left(pos + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn filename(path string) string {
|
||||||
|
return path.all_after('/')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn C.getline(voidptr, voidptr, voidptr) int
|
||||||
|
|
||||||
|
pub fn get_line() string {
|
||||||
|
max := 256
|
||||||
|
buf := malloc(max)
|
||||||
|
nr_chars := C.getline(&buf, &max, stdin)
|
||||||
|
if nr_chars == 0 {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
return tos(buf, nr_chars - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn user_os() string {
|
||||||
|
$if linux {
|
||||||
|
return 'linux'
|
||||||
|
}
|
||||||
|
$if mac {
|
||||||
|
return 'mac'
|
||||||
|
}
|
||||||
|
$if windows {
|
||||||
|
return 'windows'
|
||||||
|
}
|
||||||
|
return 'unknown'
|
||||||
|
}
|
||||||
|
|
||||||
|
// `home_dir` returns path to user's home directory.
|
||||||
|
pub fn home_dir() string {
|
||||||
|
mut home := os.getenv('HOME')
|
||||||
|
$if windows {
|
||||||
|
home = os.getenv('HOMEDRIVE')
|
||||||
|
home += os.getenv('HOMEPATH')
|
||||||
|
}
|
||||||
|
home += '/'
|
||||||
|
return home
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_file(path, text string) {
|
||||||
|
f := os.create(path)
|
||||||
|
f.appendln(text)
|
||||||
|
f.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_segfault(f voidptr) {
|
||||||
|
#ifdef windows
|
||||||
|
return
|
||||||
|
#endif
|
||||||
|
#ifdef mac
|
||||||
|
# struct sigaction sa;
|
||||||
|
# memset(&sa, 0, sizeof(struct sigaction));
|
||||||
|
# sigemptyset(&sa.sa_mask);
|
||||||
|
# sa.sa_sigaction = f;
|
||||||
|
# sa.sa_flags = SA_SIGINFO;
|
||||||
|
# sigaction(SIGSEGV, &sa, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
module os
|
||||||
|
|
||||||
|
#include <execinfo.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
// import darwin
|
||||||
|
fn log(s string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_dir(path string) bool {
|
||||||
|
# struct stat statbuf;
|
||||||
|
cstr := path.cstr()
|
||||||
|
# if (stat(cstr, &statbuf) != 0)
|
||||||
|
{
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
# return S_ISDIR(statbuf.st_mode);
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chdir(path string) {
|
||||||
|
C.chdir(path.cstr())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getwd() string {
|
||||||
|
cwd := malloc(1024)
|
||||||
|
# if (getcwd(cwd, 1024)) return tos2(cwd);
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ls(path string) []string {
|
||||||
|
mut res := []string
|
||||||
|
# DIR *dir;
|
||||||
|
# struct dirent *ent;
|
||||||
|
# if ((dir = opendir (path.str)) == NULL)
|
||||||
|
{
|
||||||
|
println('ls() couldnt open dir "$path"')
|
||||||
|
print_c_errno()
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
// print all the files and directories within directory */
|
||||||
|
# while ((ent = readdir (dir)) != NULL) {
|
||||||
|
name := ''
|
||||||
|
# name = tos_clone(ent->d_name);//, strlen(ent->d_name));
|
||||||
|
// # printf ("printf ls() %s\n", ent->d_name);
|
||||||
|
// println(name)
|
||||||
|
if name != '.' && name != '..' && name != '' {
|
||||||
|
res << name
|
||||||
|
}
|
||||||
|
# }
|
||||||
|
# closedir (dir);
|
||||||
|
// res.sort()
|
||||||
|
// println('sorted res')
|
||||||
|
// print_strings(res)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_backtrace() {
|
||||||
|
# void *buffer[100];
|
||||||
|
nptrs := 0
|
||||||
|
# nptrs = backtrace(buffer, 100);
|
||||||
|
# printf("%d!!\n", nptrs);
|
||||||
|
# backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO) ;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
module os
|
||||||
|
|
||||||
|
fn ls(path string) []string {
|
||||||
|
mut res := []string
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
FILE_ATTRIBUTE_DIRECTORY = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
fn is_dir(path string) bool {
|
||||||
|
val := int(C.GetFileAttributes(path.cstr()))
|
||||||
|
return val &FILE_ATTRIBUTE_DIRECTORY > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chdir(path string) {
|
||||||
|
C._chdir(path.cstr())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getwd() string {
|
||||||
|
panic('getwd() not impl')
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(s string) {
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
module rand
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
// #include <stdlib.h>
|
||||||
|
fn seed() {
|
||||||
|
# time_t t;
|
||||||
|
# srand((unsigned) time(&t));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(max int) int {
|
||||||
|
# return rand() % max;
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
module stbi
|
||||||
|
|
||||||
|
#include "glad.h"
|
||||||
|
import gl
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include <stb_image.h>
|
||||||
|
struct Image {
|
||||||
|
mut:
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
nr_channels int
|
||||||
|
ok bool
|
||||||
|
data voidptr
|
||||||
|
ext string
|
||||||
|
}
|
||||||
|
|
||||||
|
import const (
|
||||||
|
GL_RGBA
|
||||||
|
GL_RGB
|
||||||
|
GL_UNSIGNED_BYTE
|
||||||
|
GL_TEXTURE_2D
|
||||||
|
STBI_rgb_alpha
|
||||||
|
)
|
||||||
|
|
||||||
|
fn load(path string) Image {
|
||||||
|
ext := path.all_after('.')
|
||||||
|
mut res := Image {
|
||||||
|
ok: true
|
||||||
|
ext: ext
|
||||||
|
data: 0
|
||||||
|
}
|
||||||
|
if ext == 'png' {
|
||||||
|
res.data = C.stbi_load(path.str, &res.width, &res.height, &res.nr_channels, STBI_rgb_alpha)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.data = C.stbi_load(path.str, &res.width, &res.height, &res.nr_channels, 0)
|
||||||
|
}
|
||||||
|
if isnil(res.data) {
|
||||||
|
exit('stbi cant load')
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (img Image) free() {
|
||||||
|
C.stbi_image_free(img.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (img Image) tex_image_2d() {
|
||||||
|
mut rgb_flag := GL_RGB
|
||||||
|
if img.ext == 'png' {
|
||||||
|
rgb_flag = GL_RGBA
|
||||||
|
}
|
||||||
|
C.glTexImage2D(GL_TEXTURE_2D, 0, rgb_flag, img.width, img.height, 0, rgb_flag, GL_UNSIGNED_BYTE,
|
||||||
|
img.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_flip_vertically_on_load(val bool) {
|
||||||
|
C.stbi_set_flip_vertically_on_load(val)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,330 @@
|
||||||
|
module time
|
||||||
|
|
||||||
|
import rand
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
struct Time {
|
||||||
|
pub:
|
||||||
|
year int
|
||||||
|
day int
|
||||||
|
month int
|
||||||
|
hour int
|
||||||
|
minute int
|
||||||
|
second int
|
||||||
|
uni int // TODO it's safe to use "unix" now
|
||||||
|
}
|
||||||
|
|
||||||
|
fn asfd() {
|
||||||
|
}
|
||||||
|
|
||||||
|
struct C.tm {
|
||||||
|
tm_year int
|
||||||
|
tm_mon int
|
||||||
|
tm_mday int
|
||||||
|
tm_hour int
|
||||||
|
tm_min int
|
||||||
|
tm_sec int
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn now() Time {
|
||||||
|
# time_t t = time(0);
|
||||||
|
// t := C.time(0)
|
||||||
|
# struct tm * now = localtime(&t);
|
||||||
|
res := Time{}
|
||||||
|
# res.year = now->tm_year + 1900;
|
||||||
|
# res.month = now->tm_mon + 1;
|
||||||
|
# res.day = now->tm_mday;
|
||||||
|
# res.hour = now->tm_hour;
|
||||||
|
# res.minute = now->tm_min;
|
||||||
|
# res.second = now->tm_sec;
|
||||||
|
# res.uni = (int)t;
|
||||||
|
// # res.ms = now->tm_msec;
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn now() Time {
|
||||||
|
// t := C.time(0)
|
||||||
|
// now := localtime(&t)
|
||||||
|
// return Time{
|
||||||
|
// year: now.tm_year + 1900
|
||||||
|
// month : now.tm_mon + 1
|
||||||
|
// day : now.tm_mday
|
||||||
|
// hour : now.tm_hour
|
||||||
|
// minute : now.tm_min
|
||||||
|
// second : now.tm_sec
|
||||||
|
// uni : int(t)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
pub fn random() Time {
|
||||||
|
return Time {
|
||||||
|
year: rand.next(2) + 201
|
||||||
|
month: rand.next(12) + 1
|
||||||
|
day: rand.next(30) + 1
|
||||||
|
hour: rand.next(24)
|
||||||
|
minute: rand.next(60)
|
||||||
|
second: rand.next(60)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unix(u string) Time {
|
||||||
|
// println('unix time($u)')
|
||||||
|
// # int aa = atoi(u.str);
|
||||||
|
// #printf("!!!! %d\n", aa);
|
||||||
|
# int uni = atoi(u.str);
|
||||||
|
# time_t t = (time_t)uni;
|
||||||
|
# struct tm * now = localtime(&t);
|
||||||
|
// println('got tm')
|
||||||
|
// TODO COPY PASTA
|
||||||
|
res := Time{}
|
||||||
|
# res.year = now->tm_year + 1900;
|
||||||
|
# res.month = now->tm_mon + 1;
|
||||||
|
# res.day = now->tm_mday;
|
||||||
|
# res.hour = now->tm_hour;
|
||||||
|
# res.minute = now->tm_min;
|
||||||
|
# res.second = now->tm_sec;
|
||||||
|
# res.uni = uni;
|
||||||
|
// println('end unix')
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn convert_ctime(t tm) Time {
|
||||||
|
return Time {
|
||||||
|
year: t.tm_year + 1900
|
||||||
|
month: t.tm_mon + 1
|
||||||
|
day: t.tm_mday
|
||||||
|
hour: t.tm_hour
|
||||||
|
minute: t.tm_min
|
||||||
|
second: t.tm_sec
|
||||||
|
}
|
||||||
|
// uni = uni;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unixn(uni int) Time {
|
||||||
|
// println('unix time($u)')
|
||||||
|
// # int aa = atoi(u.str);
|
||||||
|
// #printf("!!!! %d\n", aa);
|
||||||
|
# time_t t = (time_t)uni;
|
||||||
|
# struct tm * now = localtime(&t);
|
||||||
|
// println('got tm')
|
||||||
|
// TODO COPY PASTA
|
||||||
|
res := Time{}
|
||||||
|
# res.year = now->tm_year + 1900;
|
||||||
|
# res.month = now->tm_mon + 1;
|
||||||
|
# res.day = now->tm_mday;
|
||||||
|
# res.hour = now->tm_hour;
|
||||||
|
# res.minute = now->tm_min;
|
||||||
|
# res.second = now->tm_sec;
|
||||||
|
# res.uni = uni;
|
||||||
|
// println('end unix')
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t Time) format_ss() string {
|
||||||
|
return '${t.year}-${t.month:02d}-${t.day:02d} ${t.hour:02d}:${t.minute:02d}:${t.second:02d}'
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (t Time) format() string {
|
||||||
|
return '${t.year}-${t.month:02d}-${t.day:02d} ${t.hour:02d}:${t.minute:02d}'
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
Months = 'JanFebMarAprMayJunJulAugSepOctNovDec'
|
||||||
|
)
|
||||||
|
|
||||||
|
pub fn (t Time) smonth() string {
|
||||||
|
i := t.month - 1
|
||||||
|
return Months.substr(i * 3, (i + 1) * 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 21:04
|
||||||
|
pub fn (t Time) hhmm() string {
|
||||||
|
return '${t.hour:02d}:${t.minute:02d}'
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t Time) hhmm_tmp() string {
|
||||||
|
return '${t.hour:02d}:${t.minute:02d}'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 21:04
|
||||||
|
pub fn (t Time) hhmm12() string {
|
||||||
|
mut am := 'am'
|
||||||
|
mut hour = t.hour
|
||||||
|
if t.hour > 11 {
|
||||||
|
am = 'pm'
|
||||||
|
}
|
||||||
|
if t.hour > 12 {
|
||||||
|
hour = hour - 12
|
||||||
|
}
|
||||||
|
if t.hour == 0 {
|
||||||
|
hour = 12
|
||||||
|
}
|
||||||
|
return '$hour:${t.minute:02d} $am'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 21:04:03
|
||||||
|
fn (t Time) hhmmss() string {
|
||||||
|
return '${t.hour:02d}:${t.minute:02d}:${t.second:02d}'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2012-01-05
|
||||||
|
fn (t Time) ymmdd() string {
|
||||||
|
return '${t.year}-${t.month:02d}-${t.day:02d}'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jul 3
|
||||||
|
fn (t Time) md() string {
|
||||||
|
// jl := t.smonth()
|
||||||
|
s := '${t.smonth()} $t.day'
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t Time) clean() string {
|
||||||
|
nowe := time.now()
|
||||||
|
// if amtime {
|
||||||
|
// hm = t.Format("3:04 pm")
|
||||||
|
// }
|
||||||
|
// Today
|
||||||
|
if t.month == nowe.month && t.year == nowe.year && t.day == nowe.day {
|
||||||
|
return t.hhmm()
|
||||||
|
}
|
||||||
|
// This week
|
||||||
|
// if time.Since(t) < 24*7*time.Hour {
|
||||||
|
// return t.Weekday().String()[:3] + " " + hm
|
||||||
|
// }
|
||||||
|
// This year
|
||||||
|
if t.year == nowe.year {
|
||||||
|
return '${t.smonth()} ${t.day} ${t.hhmm()}'
|
||||||
|
}
|
||||||
|
return t.format()
|
||||||
|
// return fmt.Sprintf("%4d/%02d/%02d", t.Year(), t.Month(), t.Day()) + " " + hm
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t Time) clean12() string {
|
||||||
|
nowe := time.now()
|
||||||
|
// if amtime {
|
||||||
|
// hm = t.Format("3:04 pm")
|
||||||
|
// }
|
||||||
|
// Today
|
||||||
|
if t.month == nowe.month && t.year == nowe.year && t.day == nowe.day {
|
||||||
|
return t.hhmm12()
|
||||||
|
}
|
||||||
|
// This week
|
||||||
|
// if time.Since(t) < 24*7*time.Hour {
|
||||||
|
// return t.Weekday().String()[:3] + " " + hm
|
||||||
|
// }
|
||||||
|
// This year
|
||||||
|
if t.year == nowe.year {
|
||||||
|
return '${t.smonth()} ${t.day} ${t.hhmm12()}'
|
||||||
|
}
|
||||||
|
return t.format()
|
||||||
|
// return fmt.Sprintf("%4d/%02d/%02d", t.Year(), t.Month(), t.Day()) + " " + hm
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// in ms
|
||||||
|
fn ticks() double {
|
||||||
|
# struct timeval tv;
|
||||||
|
# gettimeofday(&tv, NULL);
|
||||||
|
# double time_in_mill = (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000 ; // convert tv_sec & tv_usec to millisecond
|
||||||
|
// # printf("!!!%f\n", time_in_mill);
|
||||||
|
// # return (int)time_in_mill;
|
||||||
|
// # return (int)(time_in_mill - 1521561736529);
|
||||||
|
# return (long)(time_in_mill - 1523777913000);
|
||||||
|
return double(0)
|
||||||
|
// return int64(0)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// `parse` parses time in the following format: "2018-01-27 12:48:34"
|
||||||
|
pub fn parse(s string) Time {
|
||||||
|
// println('parse="$s"')
|
||||||
|
pos := s.index(' ')
|
||||||
|
if pos <= 0 {
|
||||||
|
println('bad time format')
|
||||||
|
return now()
|
||||||
|
}
|
||||||
|
symd := s.left(pos)
|
||||||
|
ymd := symd.split('-')
|
||||||
|
if ymd.len != 3 {
|
||||||
|
println('bad time format')
|
||||||
|
return now()
|
||||||
|
}
|
||||||
|
shms := s.right(pos)
|
||||||
|
hms := shms.split(':')
|
||||||
|
hour := hms[0]
|
||||||
|
minute := hms[1]
|
||||||
|
second := hms[2]
|
||||||
|
// //////////
|
||||||
|
return new_time(Time {
|
||||||
|
year: ymd[0].to_i()
|
||||||
|
month: ymd[1].to_i()
|
||||||
|
day: ymd[2].to_i()
|
||||||
|
hour: hour.to_i()
|
||||||
|
minute: minute.to_i()
|
||||||
|
second: second.to_i()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_time(t Time) Time {
|
||||||
|
return{t | uni: t.calc_unix()}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (t &Time) calc_unix() int {
|
||||||
|
# struct tm lDate;
|
||||||
|
# lDate.tm_sec = t->second;
|
||||||
|
# lDate.tm_min = t->minute;
|
||||||
|
# lDate.tm_hour = t->hour;
|
||||||
|
# lDate.tm_mday = t->day;
|
||||||
|
# lDate.tm_mon = t->month-1;
|
||||||
|
# lDate.tm_year = t->year - 1900;
|
||||||
|
# time_t kek = mktime(&lDate);
|
||||||
|
// # t->uni = (int)kek;
|
||||||
|
# return (int)kek;
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO add(d time.Duration)
|
||||||
|
pub fn (t Time) add_seconds(seconds int) Time {
|
||||||
|
return unixn(t.uni + seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO use time.Duration instead of seconds
|
||||||
|
fn since(t Time) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (t Time) relative() string {
|
||||||
|
now := time.now()
|
||||||
|
secs := now.uni - t.uni
|
||||||
|
if secs <= 30 {
|
||||||
|
// right now or in the future
|
||||||
|
// TODO handle time in the future
|
||||||
|
return 'now'
|
||||||
|
}
|
||||||
|
if secs < 60 {
|
||||||
|
return '1m'
|
||||||
|
}
|
||||||
|
if secs < 3600 {
|
||||||
|
return '${secs/60}m'
|
||||||
|
}
|
||||||
|
if secs < 3600 * 24 {
|
||||||
|
return '${secs/3600}h'
|
||||||
|
}
|
||||||
|
if secs < 3600 * 24 * 5 {
|
||||||
|
return '${secs/3600/24}d'
|
||||||
|
}
|
||||||
|
if secs > 3600 * 24 * 10000 {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
return t.md()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn day_of_week(y, m, d int) int {
|
||||||
|
// TODO please no
|
||||||
|
# return (d += m < 3 ? y-- : y - 2, 23*m/9 + d + 4 + y/4- y/100 + y/400)%7;
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (t Time) day_of_week() int {
|
||||||
|
return day_of_week(t.year, t.month, t.day)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
module time
|
||||||
|
|
||||||
|
// in ms
|
||||||
|
fn ticks() double {
|
||||||
|
return double(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sleep(seconds int) {
|
||||||
|
C.sleep(seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sleep_ms(seconds int) {
|
||||||
|
C.usleep(seconds * 1000)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
module time
|
||||||
|
|
||||||
|
#flag -framework CoreServices
|
||||||
|
#include <CoreServices/CoreServices.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <mach/mach_time.h>
|
||||||
|
// in ms
|
||||||
|
fn ticks() double {
|
||||||
|
// #return glfwGetTime() * 1000.0;
|
||||||
|
// return glfw.get_time() * double(1000.0)
|
||||||
|
t := i64(C.mach_absolute_time())
|
||||||
|
# Nanoseconds elapsedNano = AbsoluteToNanoseconds( *(AbsoluteTime *) &t );
|
||||||
|
# return (double)(* (uint64_t *) &elapsedNano) / 1000000;
|
||||||
|
return double(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sleep(seconds int) {
|
||||||
|
C.sleep(seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usleep(seconds int) {
|
||||||
|
C.usleep(seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sleep_ms(seconds int) {
|
||||||
|
C.usleep(seconds * 1000)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
module time
|
||||||
|
|
||||||
|
// in ms
|
||||||
|
fn ticks() double {
|
||||||
|
return C.GetTickCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sleep(seconds int) {
|
||||||
|
C._sleep(seconds * 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usleep(seconds int) {
|
||||||
|
panic('usleep not impl')
|
||||||
|
// C._usleep(seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sleep_ms(n int) {
|
||||||
|
C.Sleep(n)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue