diff --git a/compiler/fn.v b/compiler/fn.v index 2134094d2b..50618bc3da 100644 --- a/compiler/fn.v +++ b/compiler/fn.v @@ -13,6 +13,7 @@ const ( struct Fn { // addr int mut: + name string pkg string local_vars []Var var_idx int @@ -22,7 +23,6 @@ mut: // idx int scope_level int typ string // return type - name string is_c bool receiver_typ string is_public bool diff --git a/compiler/main.v b/compiler/main.v index 7fdec70056..e9d647da62 100644 --- a/compiler/main.v +++ b/compiler/main.v @@ -356,8 +356,7 @@ string _STR_TMP(const char *fmt, ...) { // Generate `main` which calls every single test function else if v.pref.is_test { cgen.genln('int main() { init_consts();') - for entry in v.table.fns.entries { - f := v.table.fns[entry.key] + for key, f in v.table.fns { if f.name.starts_with('test_') { cgen.genln('$f.name();') } @@ -680,7 +679,7 @@ fn (v &V) v_files_from_dir(dir string) []string { if file.ends_with('_mac.v') && v.os != .mac { lin_file := file.replace('_mac.v', '_lin.v') // println('lin_file="$lin_file"') - // If there are both _mav.v and _lin.v, don't use _mav.v + // If there are both _mac.v and _lin.v, don't use _mav.v if os.file_exists('$dir/$lin_file') { continue } @@ -1035,7 +1034,7 @@ fn run_repl() []string { mut temp_line := line mut temp_flag := false if !(line.contains(' ') || line.contains(':') || line.contains('=') || line.contains(',') ){ - temp_line = 'println('+line+')' + temp_line = 'println($line)' temp_flag = true } temp_source_code := lines.join('\n') + '\n' + temp_line @@ -1064,7 +1063,6 @@ fn run_repl() []string { return lines } -// This definitely needs to be better :) const ( HelpText = ' Usage: v [options] [file | directory] @@ -1077,8 +1075,7 @@ Options: -prod Build an optimized executable. -o Place output into . -obf Obfuscate the resulting binary. - run Build and execute a V program. - You can add arguments after file name. + run Build and execute a V program. You can add arguments after file name. Files: _test.v Test file. diff --git a/compiler/parser.v b/compiler/parser.v index 345b43348f..1de611014f 100644 --- a/compiler/parser.v +++ b/compiler/parser.v @@ -1200,7 +1200,7 @@ fn (p mut Parser) var_decl() { fn (p mut Parser) bool_expression() string { tok := p.tok typ := p.bterm() - for p.tok == .and || p.tok == .ortok { + for p.tok == .and || p.tok == .logical_or { p.gen(' ${p.tok.str()} ') p.check_space(p.tok) p.check_types(p.bterm(), typ) @@ -2916,10 +2916,21 @@ fn (p mut Parser) for_st() { is_mut: true } p.register_var(i_var) + p.genln('array_string keys_$tmp = map_keys(& $tmp ); ') + p.genln('for (int l = 0; l < keys_$tmp .len; l++) {') + p.genln(' string $i = ((string*)keys_$tmp .data)[l];') + //p.genln(' string $i = *(string*) ( array__get(keys_$tmp, l) );') + def := type_default(var_typ) + // TODO don't call map_get() for each key, fetch values while traversing + // the tree (replace `map_keys()` above with `map_key_vals()`) + p.genln('$var_typ $val = $def; map_get($tmp, $i, & $val);') + + /* p.genln('for (int l = 0; l < $tmp . entries.len; l++) {') p.genln('Entry entry = *((Entry*) (array__get($tmp .entries, l)));') p.genln('string $i = entry.key;') p.genln('$var_typ $val; map_get($tmp, $i, & $val);') + */ } } // `for val in vals` diff --git a/compiler/scanner.v b/compiler/scanner.v index b9255109a9..d583d18000 100644 --- a/compiler/scanner.v +++ b/compiler/scanner.v @@ -344,7 +344,7 @@ fn (s mut Scanner) scan() ScanRes { case `|`: if nextc == `|` { s.pos++ - return scan_res(.ortok, '') + return scan_res(.logical_or, '') } if nextc == `=` { s.pos++ diff --git a/compiler/table.v b/compiler/table.v index 9ec7a1a255..2f35bfa8ec 100644 --- a/compiler/table.v +++ b/compiler/table.v @@ -565,8 +565,7 @@ fn (t &Table) is_interface(name string) bool { // Do we have fn main()? fn (t &Table) main_exists() bool { - for entry in t.fns.entries { - f := t.fns[entry.key] + for _, f in t.fns { if f.name == 'main' { return true } @@ -696,11 +695,11 @@ fn (fit mut FileImportTable) register_import(mod string) { } fn (fit mut FileImportTable) register_alias(alias string, mod string) { - if !fit.imports.exists(alias) { - fit.imports[alias] = mod - } else { - panic('Cannot import $mod as $alias: import name $alias already in use in "${fit.file_path}".') - } + if fit.imports.exists(alias) { + panic('cannot import $mod as $alias: import name $alias already in use in "${fit.file_path}".') + return + } + fit.imports[alias] = mod } fn (fit &FileImportTable) known_alias(alias string) bool { @@ -708,10 +707,10 @@ fn (fit &FileImportTable) known_alias(alias string) bool { } fn (fit &FileImportTable) is_aliased(mod string) bool { - for i in fit.imports.keys() { - if fit.imports[i] == mod { - return true - } + for _, val in fit.imports { + if val == mod { + return true + } } return false } diff --git a/compiler/token.v b/compiler/token.v index 7802c63a97..c1c9e56400 100644 --- a/compiler/token.v +++ b/compiler/token.v @@ -20,7 +20,7 @@ enum Token { inc dec and - ortok + logical_or not bit_not question @@ -67,51 +67,51 @@ enum Token { dotdot // keywords keyword_beg - key_module - key_struct - key_if - key_else - key_return - key_go - key_const - key_import_const - key_mut - typ - key_enum - key_for - key_switch - MATCH - key_case - func - key_true - key_false - key_continue - key_break - key_embed - key_import - //typeof - key_default + key_as key_assert - key_sizeof - key_in key_atomic - key_interface - key_orelse + key_break + key_case + key_const + key_continue + key_default + key_else + key_embed + key_enum + key_false + key_for + func key_global + key_go + key_goto + key_if + key_import + key_import_const + key_in + key_interface + MATCH + key_module + key_mut + key_return + key_sizeof + key_struct + key_switch + key_true + typ + //typeof + key_orelse key_union key_pub - key_goto key_static - key_as keyword_end } // build_keys genereates a map with keywords' string values: // Keywords['return'] == .key_return -fn build_keys() map_int { +fn build_keys() map[string]int { mut res := map[string]int{} for t := int(Token.keyword_beg) + 1; t < int(Token.keyword_end); t++ { - key := TOKENSTR[t] + key := TokenStr[t] res[key] = int(t) } return res @@ -140,7 +140,7 @@ fn build_token_str() []string { s[Token.inc] = '++' s[Token.dec] = '--' s[Token.and] = '&&' - s[Token.ortok] = '||' + s[Token.logical_or] = '||' s[Token.not] = '!' s[Token.dot] = '.' s[Token.dotdot] = '..' @@ -218,7 +218,7 @@ fn build_token_str() []string { const ( NrTokens = 140 - TOKENSTR = build_token_str() + TokenStr = build_token_str() KEYWORDS = build_keys() ) @@ -232,11 +232,13 @@ fn is_key(key string) bool { } fn (t Token) str() string { - return TOKENSTR[int(t)] + return TokenStr[int(t)] } fn (t Token) is_decl() bool { - // TODO return t in [.func ,.typ, .key_const, .key_import_.key_const ,AT ,.eof] + // TODO i + //return t in [.key_enum, .key_interface, .func, .typ, .key_const, + //.key_import_const, .key_struct, .key_pub, .eof] return t == .key_enum || t == .key_interface || t == .func || t == .key_struct || t == .typ || t == .key_const || t == .key_import_const || t == .key_pub || t == .eof diff --git a/vlib/builtin/map.v b/vlib/builtin/map.v index af89fb91d1..bbc55a1916 100644 --- a/vlib/builtin/map.v +++ b/vlib/builtin/map.v @@ -5,60 +5,116 @@ 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 + root *Node + _keys []string // used by `keys()` TODO remove this from map struct, + key_i int // store in a separate var +pub: + size int } -struct Entry { -pub: +struct Node { + left *Node + right *Node key string val voidptr - // linked list for collisions - // next *Entry } fn new_map(cap, elm_size int) map { res := map { - // len: len, element_size: elm_size - // entries: - // keys: []string + root: 0 } return res } -fn (m &map) new_entry(key string, val voidptr) Entry { - new_e := Entry { +fn new_node(key string, val voidptr, element_size int) *Node { + new_e := &Node { key: key - val: malloc(m.element_size) - // next: 0 + val: malloc(element_size) + left: 0 + right: 0 } - C.memcpy(new_e.val, val, m.element_size) + C.memcpy(new_e.val, val, element_size) return new_e } +fn (m mut map) insert(n mut Node, key string, val voidptr) { + if n.key == key { + C.memcpy(n.val, val, m.element_size) + return + } + if n.key > key { + if isnil(n.left) { + n.left = new_node(key, val, m.element_size) + m.size++ + } else { + m.insert(mut n.left, key, val) + } + return + } + if isnil(n.right) { + n.right = new_node(key, val, m.element_size) + m.size++ + } else { + m.insert(mut n.right, key, val) + } +} + +fn (n & Node) find(key string, out voidptr, element_size int) bool{ + if n.key == key { + C.memcpy(out, n.val, element_size) + return true + } + else if n.key > key { + if isnil(n.left) { + return false + } else { + return n.left.find(key, out, element_size) + } + } + else { + if isnil(n.right) { + return false + } else { + return n.right.find(key, out, element_size) + } + } + return false +} + +// same as `find`, but doesn't return a value. Used by `exists` +fn (n & Node) find2(key string, element_size int) bool{ + if n.key == key { + return true + } + else if n.key > key { + if isnil(n.left) { + return false + } else { + return n.left.find2(key, element_size) + } + } + else { + if isnil(n.right) { + return false + } else { + return n.right.find2(key, element_size) + } + } + return false +} + 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 + if isnil(m.root) { + m.root = new_node(key, val, m.element_size) + m.size++ + return + } + m.insert(mut m.root, key, val) } +/* fn (m map) bs(query string, start, end int, out voidptr) { // println('bs "$query" $start -> $end') mid := start + ((end - start) / 2) @@ -83,63 +139,46 @@ fn (m map) bs(query string, start, end int, out voidptr) { } 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 -} +fn (m mut map) preorder_keys(node &Node) { + m._keys[m.key_i] = node.key + m.key_i++ + if !isnil(node.left) { + m.preorder_keys(node.left) + } + if !isnil(node.right) { + m.preorder_keys(node.right) + } +} -pub fn (m mut map) sort() { - m.entries.sort_with_compare(compare_map) - m.is_sorted = true -} - -pub fn (m map) keys() []string { - mut keys := []string{} - for i := 0; i < m.entries.len; i++ { - entry := m.entries[i] - keys << entry.key - } - return keys +pub fn (m mut map) keys() []string { + m._keys = [''; m.size] + m.key_i = 0 + if isnil(m.root) { + return m._keys + } + m.preorder_keys(m.root) + return m._keys } fn (m map) get(key string, out voidptr) bool { - if m.is_sorted { - // println('\n\nget "$key" sorted') - m.bs(key, 0, m.entries.len, out) - return true - } - for i := 0; i < m.entries.len; i++ { - entry := m.entries[i] - if entry.key == key { - C.memcpy(out, entry.val, m.element_size) - return true - } - } - return false + if isnil(m.root) { + return false + } + return m.root.find(key, out, m.element_size) } pub fn (m map) exists(key string) bool { - for i := 0; i < m.entries.len; i++ { - entry := m.entries[i] - if entry.key == key { - return true - } - } - return false + return !isnil(m.root) && m.root.find2(key, m.element_size) } pub fn (m map) print() { println('<<<<<<<<') - for i := 0; i < m.entries.len; i++ { + //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] @@ -158,15 +197,15 @@ pub fn (m map) free() { pub fn (m map_string) str() string { // return 'not impl' - if m.entries.len == 0 { + if m.size == 0 { return '{}' } // TODO use bytes buffer mut s := '{\n' - for entry in m.entries { - val := m[entry.key] - s += ' "$entry.key" => "$val"\n' - } + //for entry in m.entries { + //val := m[entry.key] + //s += ' "$entry.key" => "$val"\n' + //} s += '}' return s } diff --git a/vlib/builtin/map_test.v b/vlib/builtin/map_test.v index 7758f71e3e..4f41cdcbeb 100644 --- a/vlib/builtin/map_test.v +++ b/vlib/builtin/map_test.v @@ -1,3 +1,5 @@ +import time + struct User { name string } @@ -13,13 +15,32 @@ fn (a mut A) set(key string, val int) { fn test_map() { mut m := map[string]int{} + assert m.size == 0 m['hi'] = 80 + m['hello'] = 101 assert m['hi'] == 80 + assert m['hello'] == 101 + assert m.size == 2 + mut sum := 0 + mut key_sum := '' + // Test `for in` + for key, val in m { + sum += val + key_sum += key + } + assert sum == 80 + 101 + assert key_sum == 'hihello' + // Test `.keys()` + keys := m.keys() + assert keys.len == 2 + assert keys[0] == 'hi' + assert keys[1] == 'hello' //// mut users := map[string]User{} users['1'] = User{'Peter'} peter := users['1'] assert peter.name == 'Peter' + println(peter.name) mut a := A{ m: map[string]int{} @@ -33,3 +54,22 @@ fn test_map() { assert a.m['one'] == 1 assert a.m['two'] == 2 } + +fn test_string_map() { + //m := map[string]Fn +} + +fn test_large_map() { + //ticks := time.ticks() + mut nums := map[string]int{} + N := 30 * 1000 + for i := 0; i < N; i++ { + key := i.str() + nums[key] = i + } + assert nums['1'] == 1 + assert nums['999'] == 999 + assert nums['1000000'] == 0 + //println(time.ticks() - ticks) +} +