From 6824e6e7db477c89a29138c2c4c974cdbfed54ee Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Thu, 27 Jun 2019 19:02:47 +0200 Subject: [PATCH] Replace all remaining C code with V in the compiler and vlib (hoorah!) --- builtin/builtin.v | 7 +- builtin/string.v | 21 ++- builtin/utf8.v | 244 +--------------------------- compiler/fn.v | 10 -- compiler/main.v | 3 +- compiler/parser.v | 49 +++--- compiler/table.v | 2 +- examples/game_of_life/life.v | 2 +- examples/news_fetcher.v | 2 +- examples/tetris/tetris.v | 4 +- os/os.v | 21 ++- os/os_mac.v | 49 +++--- rand/rand.v | 10 +- time/info.v | 17 -- time/time.v | 307 +++++++++++++++++++++++++++-------- time/time_lin.v | 82 +--------- time/time_mac.v | 74 +++------ time/time_win.v | 62 ++----- time/util.v | 66 -------- 19 files changed, 369 insertions(+), 663 deletions(-) delete mode 100644 time/info.v delete mode 100644 time/util.v diff --git a/builtin/builtin.v b/builtin/builtin.v index f17fcc7466074f..5107d178c36bd7 100644 --- a/builtin/builtin.v +++ b/builtin/builtin.v @@ -57,6 +57,7 @@ pub fn print(s string) { C.printf('%.*s', s.len, s.str) } +__global total_m i64 = 0 pub fn malloc(n int) byteptr { if n < 0 { panic('malloc(<0)') @@ -67,10 +68,8 @@ pub fn malloc(n int) byteptr { } #endif #ifdef DEBUG_ALLOC - total := i64(0) - # total_m += n; - # total = total_m; - println('\n\n\nmalloc($n) total=$total') + total_m += n + println('\n\n\nmalloc($n) total=$total_m') print_backtrace() #endif ptr := C.malloc(n) diff --git a/builtin/string.v b/builtin/string.v index 96573aa751e096..0ef6c091188188 100644 --- a/builtin/string.v +++ b/builtin/string.v @@ -496,15 +496,17 @@ pub fn (ar[]int) contains(val int) bool { return false } +/* pub fn (a[]string) to_c() voidptr { - # char ** res = malloc(sizeof(char*) * a.len); + char ** res = malloc(sizeof(char*) * a.len); for i := 0; i < a.len; i++ { val := a[i] # res[i] = val.str; } - # return res; + return res; return 0 } +*/ fn is_space(c byte) bool { return C.isspace(c) @@ -619,8 +621,8 @@ pub fn (s string) ustring() ustring { 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]); + char_len := utf8_char_len(s.str[i]) + //# char_len =UTF8_CHAR_LEN(s.str[i]); // println('cl=$char_len') res.runes << i i += char_len - 1 @@ -632,18 +634,19 @@ pub fn (s string) ustring() ustring { // 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; +__global g_ustring_runes []int pub fn (s string) ustring_tmp() ustring { mut res := ustring { s: s runes: 0 } - # res.runes = g_ustring_runes ; - # res.runes.len = s.len ; + res.runes = g_ustring_runes + res.runes.len = s.len mut j := 0 for i := 0; i < s.len; i++ { - char_len := 0 - # char_len =UTF8_CHAR_LEN(s.str[i]); + //char_len := 0 + //# char_len =UTF8_CHAR_LEN(s.str[i]); + char_len := utf8_char_len(s.str[i]) res.runes[j] = i j++ i += char_len - 1 diff --git a/builtin/utf8.v b/builtin/utf8.v index 21d135ac2a1100..b87d328ff10fbf 100644 --- a/builtin/utf8.v +++ b/builtin/utf8.v @@ -4,250 +4,10 @@ module builtin -pub 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 { - println('utf is bad dalen=$len KEK $s sdf') - // s = s.left(i) - } - return i - // return ok +pub fn utf8_char_len(b byte) int { + return (( 0xe5000000 >> (( b >> 3 ) & 0x1e )) & 3 ) + 1 } -/* -fn (s string) runes() []string { - res2 := []string{} - // res := new_empty_array_with_cap_string(s.len) - res := []string{} - if !s.is_utf8() { - mys := s - println('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 pub fn utf32_to_str(code u32) string { diff --git a/compiler/fn.v b/compiler/fn.v index 5381dac55412e7..da86dc7dae988c 100644 --- a/compiler/fn.v +++ b/compiler/fn.v @@ -517,14 +517,6 @@ fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type strin // 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}(' receiver := f.args.first() if receiver.is_mut && !p.expr_var.is_mut { @@ -550,10 +542,8 @@ fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type strin 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 diff --git a/compiler/main.v b/compiler/main.v index 5174f662213770..433316d13d453c 100644 --- a/compiler/main.v +++ b/compiler/main.v @@ -257,7 +257,8 @@ void init_consts();') 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') + // TODO + //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(' diff --git a/compiler/parser.v b/compiler/parser.v index c49a5e3cc802a0..02daf87375b5b8 100644 --- a/compiler/parser.v +++ b/compiler/parser.v @@ -72,6 +72,7 @@ mut: returns bool vroot string is_c_struct_init bool + can_chash bool } const ( @@ -141,6 +142,7 @@ fn (p mut Parser) parse() { } p.fgenln('\n') p.builtin_pkg = p.pkg == 'builtin' + p.can_chash = p.pkg == 'gg' || p.pkg == 'glm' || p.pkg == 'gl' || p.pkg == 'glfw' // TODO tmp remove // Import pass - the first and the smallest pass that only analyzes imports p.table.register_package(p.pkg) if p.run == RUN_IMPORTS { @@ -203,7 +205,7 @@ fn (p mut Parser) parse() { // $if, $else p.comp_time() case GLOBAL: - if !p.translated { + if !p.translated && !p.builtin_pkg && !p.building_v() { p.error('__global is only allowed in translated code') } p.next() @@ -419,7 +421,7 @@ fn (p mut Parser) struct_decl() { if !is_c { kind := if is_union{'union'} else { 'struct'} p.gen_typedef('typedef $kind $name $name;') - p.gen_type('$kind $name {') + p.gen_type('$kind /*kind*/ $name {') } } // V used to have 'type Foo struct', many Go users might use this syntax @@ -1010,15 +1012,8 @@ fn (p mut Parser) statement(add_semi bool) string { // this can be `user = ...` or `user.field = ...`, in both cases `v` is `user` fn (p mut Parser) assign_statement(v Var, ph int, is_map bool) { p.log('assign_statement() name=$v.name tok=') - // p.print_tok() - // p.gen(name) - // p.assigned_var = name tok := p.tok - if !v.is_mut && !v.is_arg && !p.translated { - p.error('`$v.name` is immutable') - } - if !v.is_mut && p.is_play && !p.builtin_pkg && !p.translated { - // no mutable args in play + if !v.is_mut && !v.is_arg && !p.translated && !v.is_global{ p.error('`$v.name` is immutable') } is_str := v.typ == 'string' @@ -1232,7 +1227,7 @@ fn (p mut Parser) name_expr() string { // ////////////////////////// // module ? // Allow shadowing (gg = gg.newcontext(); gg.draw_triangle()) - if p.table.known_pkg(name) && !p.cur_fn.known_var(name) { + if p.table.known_pkg(name) && !p.cur_fn.known_var(name) && !is_c { // println('"$name" is a known pkg') pkg := name p.next() @@ -2344,21 +2339,25 @@ fn (p mut Parser) register_array(typ string) { } } -// name == 'User' fn (p mut Parser) struct_init(is_c_struct_init bool) string { p.is_struct_init = true mut typ := p.get_type() p.scanner.fmt_out.cut(typ.len) ptr := typ.contains('*') + // TODO tm struct struct bug + if typ == 'tm' { + p.cgen.lines[p.cgen.lines.len-1] = '' + p.cgen.lines[p.cgen.lines.len-2] = '' + } p.check(LCBR) // tmp := p.get_tmp() if !ptr { if p.is_c_struct_init { - p.gen('(struct $typ){') + p.gen('(struct $typ) {') p.is_c_struct_init = false } else { - p.gen('($typ){') + p.gen('($typ) {') } } else { @@ -2370,12 +2369,6 @@ fn (p mut Parser) struct_init(is_c_struct_init bool) string { p.check(RCBR) return typ } - mut type_gen := typ.replace('*', '') - // All V types are typedef'ed, C structs aren't, so we need to prepend "struct " - if is_c_struct_init { - type_gen = 'struct $type_gen' - } - // p.gen('malloc(sizeof($type_gen)); \n') no_star := typ.replace('*', '') p.gen('ALLOC_INIT($no_star, {') } @@ -2463,7 +2456,6 @@ fn (p mut Parser) struct_init(is_c_struct_init bool) string { } p.check(RCBR) p.is_struct_init = false - // println('struct init typ=$typ') return typ } @@ -2668,13 +2660,15 @@ fn (p mut Parser) chash() { } } else { + if !p.can_chash { + p.error('bad token `#` (embedding C code is no longer supported)') + } + //println('HASH!!! $p.file_name $p.scanner.line_nr') if p.cur_fn.name == '' { // p.error('# outside of fn') } p.genln(hash) } - // p.cgen.nogen = false - // println('HASH=$hash') } fn is_c_pre(hash string) bool { @@ -3134,6 +3128,14 @@ fn is_compile_time_const(s string) bool { return true } +fn (p &Parser) building_v() bool { + cur_dir := os.getwd() + return p.file_path.contains('v/compiler') || cur_dir.contains('v/compiler') +} + + +/////////////////////////////////////////////////////////////////////////////// + // fmt helpers fn (scanner mut Scanner) fgen(s string) { /* @@ -3167,3 +3169,4 @@ fn (p mut Parser) fgenln(s string) { //p.scanner.fgenln(s) } + diff --git a/compiler/table.v b/compiler/table.v index 1f5c6df73969b4..1b70b6a3847e85 100644 --- a/compiler/table.v +++ b/compiler/table.v @@ -644,7 +644,7 @@ fn (table &Table) cgen_name_type_pair(name, typ string) string { } // TODO tm hack, do this for all C struct args else if typ == 'tm' { - return 'struct tm $name' + return 'struct /*TM*/ tm $name' } return '$typ $name' } diff --git a/examples/game_of_life/life.v b/examples/game_of_life/life.v index 79e79f3ff5fa52..7796a69883bf4d 100644 --- a/examples/game_of_life/life.v +++ b/examples/game_of_life/life.v @@ -73,7 +73,7 @@ fn main() { } field = new_field print_field(field) - time.sleep(time.milliseconds(100)) + time.sleep_ms(100) } } diff --git a/examples/news_fetcher.v b/examples/news_fetcher.v index 61f1a2f0a05bcd..3cb1a94107046e 100644 --- a/examples/news_fetcher.v +++ b/examples/news_fetcher.v @@ -53,6 +53,6 @@ fn main() { go fetcher.fetch() } println(fetcher.ids) - time.sleep(time.seconds(5)) + time.sleep(5) } diff --git a/examples/tetris/tetris.v b/examples/tetris/tetris.v index f03cd5606a8ac8..6eb728a3282534 100644 --- a/examples/tetris/tetris.v +++ b/examples/tetris/tetris.v @@ -17,7 +17,7 @@ const ( TetroSize = 4 WinWidth = BlockSize * FieldWidth WinHeight = BlockSize * FieldHeight - TimerPeriod = time.milliseconds(250) // 250ms + TimerPeriod = 250 // ms ) const ( @@ -168,7 +168,7 @@ fn (g mut Game) run() { g.move_tetro() g.delete_completed_lines() glfw.post_empty_event() // force window redraw - time.sleep(TimerPeriod) + time.sleep_ms(TimerPeriod) } } diff --git a/os/os.v b/os/os.v index a9eeceff20c1be..594129fe4a5971 100644 --- a/os/os.v +++ b/os/os.v @@ -26,17 +26,30 @@ import const ( SEEK_END SA_SIGINFO SIGSEGV + +S_IFMT +S_IFDIR ) struct C.stat { st_size int + st_mode int +} + +struct C.DIR { + +} + +struct C.dirent { + d_name byteptr + } struct C.sigaction { - mut: - sa_mask int - sa_sigaction int - sa_flags int +mut: + sa_mask int + sa_sigaction int + sa_flags int } fn C.getline(voidptr, voidptr, voidptr) int diff --git a/os/os_mac.v b/os/os_mac.v index 7a67d4b22ee139..b3990c688e3370 100644 --- a/os/os_mac.v +++ b/os/os_mac.v @@ -13,15 +13,14 @@ module os fn log(s string) { } -fn is_dir(path string) bool { - # struct stat statbuf; + +pub fn is_dir(path string) bool { + statbuf := C.stat{} cstr := path.cstr() - # if (stat(cstr, &statbuf) != 0) - { + if C.stat(cstr, &statbuf) != 0 { return false } - # return S_ISDIR(statbuf.st_mode); - return false + return statbuf.st_mode & S_IFMT == S_IFDIR } fn chdir(path string) { @@ -29,35 +28,33 @@ fn chdir(path string) { } pub fn getwd() string { - cwd := malloc(1024) - # if (getcwd(cwd, 1024)) return tos2(cwd); - return '' + cwd := malloc(512) + if C.getcwd(cwd, 512) == 0 { + return '' + } + return string(cwd) } pub fn ls(path string) []string { mut res := []string - # DIR *dir; - # struct dirent *ent; - # if ((dir = opendir (path.str)) == NULL) - { + dir := C.opendir(path.str) + if isnil(dir) { 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 + mut ent := &C.dirent{!} + for { + ent = C.readdir(dir) + if isnil(ent) { + break + } + name := tos_clone(ent.d_name) + if name != '.' && name != '..' && name != '' { + res << name + } } - # } - # closedir (dir); - // res.sort() - // println('sorted res') - // print_strings(res) + C.closedir(dir) return res } diff --git a/rand/rand.v b/rand/rand.v index 0d1f1f44646662..e6b8efa2a58be6 100644 --- a/rand/rand.v +++ b/rand/rand.v @@ -4,19 +4,15 @@ module rand -#include +import time pub fn seed() { - # time_t t; - # srand((unsigned) time(&t)); + C.srand(time.now().uni) } pub fn next(max int) int { - r := 0 - # r = rand(); // TODO parser bug `rand` module name conflict - return r % max + return C.rand() % max } -struct C.time_t{} fn C.rand() int diff --git a/time/info.v b/time/info.v deleted file mode 100644 index dac64dcca974ac..00000000000000 --- a/time/info.v +++ /dev/null @@ -1,17 +0,0 @@ -// 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 time - -struct Info { -pub: - year int - month int - day int - hour int - minute int - second int - yday int - wday int -} diff --git a/time/time.v b/time/time.v index dc02c318931494..9288bce6be40cc 100644 --- a/time/time.v +++ b/time/time.v @@ -4,104 +4,277 @@ module time -#include +import rand +#include struct Time { - sec i64 - nsec i32 - mono i64 +pub: + year int + month int + day int + hour int + minute int + second int + uni int // TODO it's safe to use "unix" now } -fn (a Time) + (b Time) Time { - sec := a.sec + b.sec - nsec := a.nsec + b.nsec - return Time{ - sec: sec + i64(nsec)/i64(1000000000) - nsec: nsec % i32(1000000000) - } +fn C.localtime(int) *C.tm + +fn remove_me_when_c_bug_is_fixed() { // TODO } -fn (a Time) - (b Time) Time { - sec := a.sec - b.sec - i64(1) - nsec := a.nsec - b.nsec + i32(1000000000) - t := Time{ - sec: sec + i64(nsec)/i64(1000000000) - nsec: nsec % i32(1000000000) - } - if a.mono <= i64(0) || b.mono <= i64(0) { - return t +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 { + t := C.time(0) + mut now := &C.tm{!} + now = C.localtime(&t) + return convert_ctime(now) +} + +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 int) Time { + mut t := &C.tm{!} + t = C.localtime(&u) + return convert_ctime(t) +} - mono := a.mono - b.mono - if mono > i64(0) && t.sec >= i64(0) && t.nsec >= i32(0) { - return t +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 } - if mono < i64(0) && t.sec <= i64(0) && t.nsec <= i32(0) { - return t + // uni = uni; +} + +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' + Days = 'MonTueWedThuFriSatSun' +) + +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}' +} +*/ + +// 9:04pm +pub fn (t Time) hhmm12() string { + mut am := 'am' + mut hour = t.hour + if t.hour > 11 { + am = 'pm' } - if mono == i64(0) { - return Time{} + if t.hour > 12 { + hour = hour - 12 } - return Time{ - sec: mono / i64(1000000000) - nsec: i32(mono%i64(1000000000)) + if t.hour == 0 { + hour = 12 } + return '$hour:${t.minute:02d} $am' } -fn (t Time) days() f64 { - return f64(t.sec)/86400.0 +// 21:04:03 +fn (t Time) hhmmss() string { + return '${t.hour:02d}:${t.minute:02d}:${t.second:02d}' } -fn (t Time) hours() f64 { - return f64(t.sec)/3600.0 +// 2012-01-05 +fn (t Time) ymmdd() string { + return '${t.year}-${t.month:02d}-${t.day:02d}' } -fn (t Time) minutes() f64 { - return f64(t.sec)/60.0 +// Jul 3 +fn (t Time) md() string { + // jl := t.smonth() + s := '${t.smonth()} $t.day' + return s } -fn (t Time) seconds() f64 { - return f64(t.sec) + f64(t.nsec)/1000000000.0 +pub 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) milliseconds() f64 { - return 1000.0*f64(t.sec) + f64(t.nsec)/1000000.0 +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 } -fn (t Time) microseconds() f64 { - return 1000000.0*f64(t.sec) + f64(t.nsec)/1000.0 +/* +// in ms +fn ticks() double { + return 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].int() + month: ymd[1].int() + day: ymd[2].int() + hour: hour.int() + minute: minute.int() + second: second.int() + }) } -fn (t Time) nanoseconds() f64 { - return 1000000000.0*f64(t.sec) + f64(t.nsec) +fn new_time(t Time) Time { + return{t | uni: t.calc_unix()} } -fn (t Time) str() string { - if t.sec == i64(0) { - if t.nsec == i32(0) { - return '0s' - } - if t.nsec < i32(1000) && t.nsec > i32(-1000) { - return '${t.nsec}ns' - } - if t.nsec < i32(1000000) && t.nsec > i32(-1000000) { - return '${f64(t.nsec)/1000.0:.1f}µs' - } - if t.nsec < i32(1000000000) && t.nsec > i32(-1000000000) { - return '${f64(t.nsec)/1000000.0:.1f}ms' - } +fn (t &Time) calc_unix() int { + if t.uni != 0 { + return t.uni } - if t.sec < i64(60) && t.sec > i64(-60) { - return '${f64(t.sec)+f64(t.nsec)/1000000000.0:.1f}s' + tt := C.tm{ + tm_sec : t.second + tm_min : t.minute + tm_hour : t.hour + tm_mday : t.day + tm_mon : t.month-1 + tm_year : t.year - 1900 } - if t.sec < i64(3600) && t.sec > i64(-3600) { - return '${f64(t.sec)/60.0:.1f}m' + return C.mktime(&tt) +} + +// TODO add(d time.Duration) +pub fn (t Time) add_seconds(seconds int) Time { + return unix(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 t.sec < i64(86400) && t.sec > i64(-86400) { - return '${f64(t.sec)/3600.0:.1f}h' + if secs < 3600 { + return '${secs/60}m' } - if t.sec < i64(864000) && t.sec > i64(-864000) { - return '${f64(t.sec)/86400.0:.1f}d' + if secs < 3600 * 24 { + return '${secs/3600}h' } - return '${f64(t.sec)/86400.0:.0f}d' + 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) +} + +// weekday_str() returns the current day in string (upto 3 characters) +pub fn (t Time) weekday_str() string { + i := t.day_of_week() - 1 + return Days.substr(i * 3, (i + 1) * 3) } diff --git a/time/time_lin.v b/time/time_lin.v index 8e365daaefc0b3..e3291c26dde0d4 100644 --- a/time/time_lin.v +++ b/time/time_lin.v @@ -4,84 +4,16 @@ module time -pub fn now() Time { - # struct timespec t = {0, 0}; - # struct timespec m = {0, 0}; - # clock_gettime(CLOCK_REALTIME, &t); - # clock_gettime(CLOCK_MONOTONIC_RAW, &m); - - res := Time{} - # res.sec = t.tv_sec; - # res.nsec = t.tv_nsec; - # res.mono = 1000000000*m.tv_sec + m.tv_nsec; - return res -} - -pub fn sleep(t Time) { - if t.sec > i64(0) { - C.sleep(t.sec) - } - if t.nsec > i32(0) { - C.usleep((t.nsec+i32(999))/i32(1000)) - } +// in ms +pub fn ticks() f64 { + return f64(0) } -fn (t Time) local() Info { - info := Info{} - # struct tm tm; - # localtime_r(&t.sec, &tm); - # info.year = tm.tm_year + 1900; - # info.month = tm.tm_mon + 1; - # info.day = tm.tm_mday; - # info.hour = tm.tm_hour; - # info.minute = tm.tm_min; - # info.second = tm.tm_sec; - # info.yday = tm.tm_yday; - # info.wday = tm.tm_wday; - return info +pub fn sleep(seconds int) { + C.sleep(seconds) } -fn (t Time) utc() Info { - info := Info{} - # struct tm tm; - # gmtime_r(&t.sec, &tm); - # info.year = tm.tm_year + 1900; - # info.month = tm.tm_mon + 1; - # info.day = tm.tm_mday; - # info.hour = tm.tm_hour; - # info.minute = tm.tm_min; - # info.second = tm.tm_sec; - # info.yday = tm.tm_yday; - # info.wday = tm.tm_wday; - return info +pub fn sleep_ms(seconds int) { + C.usleep(seconds * 1000) } -fn (t Time) format(fmt string) string { - res := '' - cfmt := fmt.cstr() - # char buf[1024]; - # struct tm tm; - # localtime_r(&t.sec, &tm); - # strftime(buf, 1024, cfmt, &tm); - # res = tos2(buf); - return res -} - - -pub fn parse(s, fmt string) ?Time { - t := Time{} - cs := s.cstr() - cfmt := fmt.cstr() - ok := 0 - # struct tm tm; - # memset(&tm, 0, sizeof(struct tm)); - # ok = strptime(cs, cfmt, &tm); - if ok == 0 { - return error('time.parse: invalid time string') - } - # t.sec = mktime(&tm); - if t.sec < i64(0) { - return error('time.parse: invalid time string') - } - return t -} diff --git a/time/time_mac.v b/time/time_mac.v index 32bd49afc07165..77efba96639500 100644 --- a/time/time_mac.v +++ b/time/time_mac.v @@ -4,66 +4,30 @@ module time -// TODO: nanosecond and monotonic -pub fn now() Time { - # time_t t = time(0); - - res := Time{} - # res.sec = t; - return res -} - -pub fn sleep(t Time) { - if t.sec > i64(0) { - C.sleep(t.sec) - } - if t.nsec > i32(0) { - C.usleep((t.nsec+i32(999))/i32(1000)) - } +//#flag -framework CoreServices +//#include +//#include + +// in ms +pub fn ticks() f64 { + panic('not implemented') +/* + t := i64(C.mach_absolute_time()) + # Nanoseconds elapsedNano = AbsoluteToNanoseconds( *(AbsoluteTime *) &t ); + # return (double)(* (uint64_t *) &elapsedNano) / 1000000; +*/ + return f64(0) } -// TODO: Thread safety -fn (t Time) local() Info { - info := Info{} - # struct tm *tm = localtime(&t.sec); - # info.year = tm->tm_year + 1900; - # info.month = tm->tm_mon + 1; - # info.day = tm->tm_mday; - # info.hour = tm->tm_hour; - # info.minute = tm->tm_min; - # info.second = tm->tm_sec; - # info.yday = tm->tm_yday; - # info.wday = tm->tm_wday; - return info +pub fn sleep(seconds int) { + C.sleep(seconds) } -// TODO: Thread safety -fn (t Time) utc() Info { - info := Info{} - # struct tm *tm = gmtime(&t.sec); - # info.year = tm->tm_year + 1900; - # info.month = tm->tm_mon + 1; - # info.day = tm->tm_mday; - # info.hour = tm->tm_hour; - # info.minute = tm->tm_min; - # info.second = tm->tm_sec; - # info.yday = tm->tm_yday; - # info.wday = tm->tm_wday; - return info +pub fn usleep(seconds int) { + C.usleep(seconds) } -// TODO: Thread safety -fn (t Time) format(fmt string) string { - res := '' - cfmt := fmt.cstr() - # char buf[1024]; - # struct tm *tm = localtime(&t.sec); - # strftime(buf, 1024, cfmt, tm); - # res = tos2(buf); - return res +pub fn sleep_ms(seconds int) { + C.usleep(seconds * 1000) } -// TODO: Not implemented yet -pub fn parse(s string) ?Time { - return Time{} -} diff --git a/time/time_win.v b/time/time_win.v index cc46b25b9d6f4f..fb0b1511124731 100644 --- a/time/time_win.v +++ b/time/time_win.v @@ -4,63 +4,21 @@ module time -// TODO: nanosecond and monotonic -pub fn now() Time { - # time_t t = time(0); - - res := Time{} - # res.sec = t; - return res -} - -pub fn sleep(t Time) { - if t.sec > i64(0) || t.nsec > i32(0) { - C.Sleep(i64(1000)*t.sec+(i64(t.nsec)+i64(999999))/i64(1000000)) - } +// in ms +fn ticks() double { + return C.GetTickCount() } -// TODO: Thread safety -fn (t Time) local() Info { - info := Info{} - # struct tm *tm = localtime(&t.sec); - # info.year = tm->tm_year + 1900; - # info.month = tm->tm_mon + 1; - # info.day = tm->tm_mday; - # info.hour = tm->tm_hour; - # info.minute = tm->tm_min; - # info.second = tm->tm_sec; - # info.yday = tm->tm_yday; - # info.wday = tm->tm_wday; - return info +fn sleep(seconds int) { + C._sleep(seconds * 1000) } -// TODO: Thread safety -fn (t Time) utc() Info { - info := Info{} - # struct tm *tm = gmtime(&t.sec); - # info.year = tm->tm_year + 1900; - # info.month = tm->tm_mon + 1; - # info.day = tm->tm_mday; - # info.hour = tm->tm_hour; - # info.minute = tm->tm_min; - # info.second = tm->tm_sec; - # info.yday = tm->tm_yday; - # info.wday = tm->tm_wday; - return info +fn usleep(seconds int) { + panic('usleep not impl') + // C._usleep(seconds) } -// TODO: Thread safety -fn (t Time) format(fmt string) string { - res := '' - cfmt := fmt.cstr() - # char buf[1024]; - # struct tm *tm = localtime(&t.sec); - # strftime(buf, 1024, cfmt, tm); - # res = tos2(buf); - return res +fn sleep_ms(n int) { + C.Sleep(n) } -// TODO: Not implemented yet -pub fn parse(s string) ?Time { - return Time{} -} diff --git a/time/util.v b/time/util.v deleted file mode 100644 index 317cd24e8bdc6b..00000000000000 --- a/time/util.v +++ /dev/null @@ -1,66 +0,0 @@ -// 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 time - -pub fn make(i Info) ?Time { - t := Time{} - # struct tm tm; - # tm.tm_year = i.year - 1900; - # tm.tm_mon = i.month - 1; - # tm.tm_mday = i.day; - # tm.tm_hour = i.hour; - # tm.tm_min = i.minute; - # tm.tm_sec = i.second; - # tm.tm_yday = i.yday; - # tm.tm_wday = i.wday; - # tm.tm_isdst = 0; - # t.sec = mktime(&tm); - if t.sec < i64(0) { - return error('time.make: invalid time infomation') - } - return t -} - -pub fn days(n int) Time { - return Time{ - sec: 86400*n - } -} - -pub fn hours(n int) Time { - return Time{ - sec: 3600*n - } -} - -pub fn minutes(n int) Time { - return Time{ - sec: 60*n - } -} - -pub fn seconds(n int) Time { - return Time{ - sec: n - } -} - -pub fn milliseconds(n int) Time { - return Time{ - nsec: 1000000*n - } -} - -pub fn microseconds(n int) Time { - return Time{ - nsec: 1000*n - } -} - -pub fn nanoseconds(n int) Time { - return Time{ - nsec: n - } -}