re-write map.v and update the compiler

pull/1143/head
Alexander Medvednikov 2019-07-14 11:01:32 +02:00
parent 9a3baf5609
commit f7dbbf5810
8 changed files with 226 additions and 138 deletions

View File

@ -13,6 +13,7 @@ const (
struct Fn { struct Fn {
// addr int // addr int
mut: mut:
name string
pkg string pkg string
local_vars []Var local_vars []Var
var_idx int var_idx int
@ -22,7 +23,6 @@ mut:
// idx int // idx int
scope_level int scope_level int
typ string // return type typ string // return type
name string
is_c bool is_c bool
receiver_typ string receiver_typ string
is_public bool is_public bool

View File

@ -356,8 +356,7 @@ string _STR_TMP(const char *fmt, ...) {
// Generate `main` which calls every single test function // Generate `main` which calls every single test function
else if v.pref.is_test { else if v.pref.is_test {
cgen.genln('int main() { init_consts();') cgen.genln('int main() { init_consts();')
for entry in v.table.fns.entries { for key, f in v.table.fns {
f := v.table.fns[entry.key]
if f.name.starts_with('test_') { if f.name.starts_with('test_') {
cgen.genln('$f.name();') 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 { if file.ends_with('_mac.v') && v.os != .mac {
lin_file := file.replace('_mac.v', '_lin.v') lin_file := file.replace('_mac.v', '_lin.v')
// println('lin_file="$lin_file"') // 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') { if os.file_exists('$dir/$lin_file') {
continue continue
} }
@ -1035,7 +1034,7 @@ fn run_repl() []string {
mut temp_line := line mut temp_line := line
mut temp_flag := false mut temp_flag := false
if !(line.contains(' ') || line.contains(':') || line.contains('=') || line.contains(',') ){ if !(line.contains(' ') || line.contains(':') || line.contains('=') || line.contains(',') ){
temp_line = 'println('+line+')' temp_line = 'println($line)'
temp_flag = true temp_flag = true
} }
temp_source_code := lines.join('\n') + '\n' + temp_line temp_source_code := lines.join('\n') + '\n' + temp_line
@ -1064,7 +1063,6 @@ fn run_repl() []string {
return lines return lines
} }
// This definitely needs to be better :)
const ( const (
HelpText = ' HelpText = '
Usage: v [options] [file | directory] Usage: v [options] [file | directory]
@ -1077,8 +1075,7 @@ Options:
-prod Build an optimized executable. -prod Build an optimized executable.
-o <file> Place output into <file>. -o <file> Place output into <file>.
-obf Obfuscate the resulting binary. -obf Obfuscate the resulting binary.
run Build and execute a V program. run Build and execute a V program. You can add arguments after file name.
You can add arguments after file name.
Files: Files:
<file>_test.v Test file. <file>_test.v Test file.

View File

@ -1200,7 +1200,7 @@ fn (p mut Parser) var_decl() {
fn (p mut Parser) bool_expression() string { fn (p mut Parser) bool_expression() string {
tok := p.tok tok := p.tok
typ := p.bterm() typ := p.bterm()
for p.tok == .and || p.tok == .ortok { for p.tok == .and || p.tok == .logical_or {
p.gen(' ${p.tok.str()} ') p.gen(' ${p.tok.str()} ')
p.check_space(p.tok) p.check_space(p.tok)
p.check_types(p.bterm(), typ) p.check_types(p.bterm(), typ)
@ -2916,10 +2916,21 @@ fn (p mut Parser) for_st() {
is_mut: true is_mut: true
} }
p.register_var(i_var) 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('for (int l = 0; l < $tmp . entries.len; l++) {')
p.genln('Entry entry = *((Entry*) (array__get($tmp .entries, l)));') p.genln('Entry entry = *((Entry*) (array__get($tmp .entries, l)));')
p.genln('string $i = entry.key;') p.genln('string $i = entry.key;')
p.genln('$var_typ $val; map_get($tmp, $i, & $val);') p.genln('$var_typ $val; map_get($tmp, $i, & $val);')
*/
} }
} }
// `for val in vals` // `for val in vals`

View File

@ -344,7 +344,7 @@ fn (s mut Scanner) scan() ScanRes {
case `|`: case `|`:
if nextc == `|` { if nextc == `|` {
s.pos++ s.pos++
return scan_res(.ortok, '') return scan_res(.logical_or, '')
} }
if nextc == `=` { if nextc == `=` {
s.pos++ s.pos++

View File

@ -565,8 +565,7 @@ fn (t &Table) is_interface(name string) bool {
// Do we have fn main()? // Do we have fn main()?
fn (t &Table) main_exists() bool { fn (t &Table) main_exists() bool {
for entry in t.fns.entries { for _, f in t.fns {
f := t.fns[entry.key]
if f.name == 'main' { if f.name == 'main' {
return true return true
} }
@ -696,11 +695,11 @@ fn (fit mut FileImportTable) register_import(mod string) {
} }
fn (fit mut FileImportTable) register_alias(alias string, mod string) { fn (fit mut FileImportTable) register_alias(alias string, mod string) {
if !fit.imports.exists(alias) { if fit.imports.exists(alias) {
fit.imports[alias] = mod panic('cannot import $mod as $alias: import name $alias already in use in "${fit.file_path}".')
} else { return
panic('Cannot import $mod as $alias: import name $alias already in use in "${fit.file_path}".')
} }
fit.imports[alias] = mod
} }
fn (fit &FileImportTable) known_alias(alias string) bool { fn (fit &FileImportTable) known_alias(alias string) bool {
@ -708,8 +707,8 @@ fn (fit &FileImportTable) known_alias(alias string) bool {
} }
fn (fit &FileImportTable) is_aliased(mod string) bool { fn (fit &FileImportTable) is_aliased(mod string) bool {
for i in fit.imports.keys() { for _, val in fit.imports {
if fit.imports[i] == mod { if val == mod {
return true return true
} }
} }

View File

@ -20,7 +20,7 @@ enum Token {
inc inc
dec dec
and and
ortok logical_or
not not
bit_not bit_not
question question
@ -67,51 +67,51 @@ enum Token {
dotdot dotdot
// keywords // keywords
keyword_beg keyword_beg
key_module key_as
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_assert key_assert
key_sizeof
key_in
key_atomic key_atomic
key_interface key_break
key_orelse key_case
key_const
key_continue
key_default
key_else
key_embed
key_enum
key_false
key_for
func
key_global 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_union
key_pub key_pub
key_goto
key_static key_static
key_as
keyword_end keyword_end
} }
// build_keys genereates a map with keywords' string values: // build_keys genereates a map with keywords' string values:
// Keywords['return'] == .key_return // Keywords['return'] == .key_return
fn build_keys() map_int { fn build_keys() map[string]int {
mut res := map[string]int{} mut res := map[string]int{}
for t := int(Token.keyword_beg) + 1; t < int(Token.keyword_end); t++ { for t := int(Token.keyword_beg) + 1; t < int(Token.keyword_end); t++ {
key := TOKENSTR[t] key := TokenStr[t]
res[key] = int(t) res[key] = int(t)
} }
return res return res
@ -140,7 +140,7 @@ fn build_token_str() []string {
s[Token.inc] = '++' s[Token.inc] = '++'
s[Token.dec] = '--' s[Token.dec] = '--'
s[Token.and] = '&&' s[Token.and] = '&&'
s[Token.ortok] = '||' s[Token.logical_or] = '||'
s[Token.not] = '!' s[Token.not] = '!'
s[Token.dot] = '.' s[Token.dot] = '.'
s[Token.dotdot] = '..' s[Token.dotdot] = '..'
@ -218,7 +218,7 @@ fn build_token_str() []string {
const ( const (
NrTokens = 140 NrTokens = 140
TOKENSTR = build_token_str() TokenStr = build_token_str()
KEYWORDS = build_keys() KEYWORDS = build_keys()
) )
@ -232,11 +232,13 @@ fn is_key(key string) bool {
} }
fn (t Token) str() string { fn (t Token) str() string {
return TOKENSTR[int(t)] return TokenStr[int(t)]
} }
fn (t Token) is_decl() bool { 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 || return t == .key_enum || t == .key_interface || t == .func ||
t == .key_struct || t == .typ || t == .key_struct || t == .typ ||
t == .key_const || t == .key_import_const || t == .key_pub || t == .eof t == .key_const || t == .key_import_const || t == .key_pub || t == .eof

View File

@ -5,60 +5,116 @@
module builtin module builtin
struct map { struct map {
// cap int
// keys []string
// table byteptr
// keys_table *string
// table *Entry
element_size int element_size int
// collisions []Entry root *Node
_keys []string // used by `keys()` TODO remove this from map struct,
key_i int // store in a separate var
pub: pub:
entries []Entry size int
is_sorted bool
} }
struct Entry { struct Node {
pub: left *Node
right *Node
key string key string
val voidptr val voidptr
// linked list for collisions
// next *Entry
} }
fn new_map(cap, elm_size int) map { fn new_map(cap, elm_size int) map {
res := map { res := map {
// len: len,
element_size: elm_size element_size: elm_size
// entries: root: 0
// keys: []string
} }
return res return res
} }
fn (m &map) new_entry(key string, val voidptr) Entry { fn new_node(key string, val voidptr, element_size int) *Node {
new_e := Entry { new_e := &Node {
key: key key: key
val: malloc(m.element_size) val: malloc(element_size)
// next: 0 left: 0
right: 0
} }
C.memcpy(new_e.val, val, m.element_size) C.memcpy(new_e.val, val, element_size)
return new_e return new_e
} }
fn (m mut map) _set(key string, val voidptr) { fn (m mut map) insert(n mut Node, key string, val voidptr) {
e := m.new_entry(key, val) if n.key == key {
for i := 0; i < m.entries.len; i++ { C.memcpy(n.val, val, m.element_size)
entry := m.entries[i]
if entry.key == key {
// e := Entry2{key: key, val: val}
m.entries[i] = e
return 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)
} }
m.entries << e// m.new_entry(key, val)
m.is_sorted = false
} }
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) {
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) { fn (m map) bs(query string, start, end int, out voidptr) {
// println('bs "$query" $start -> $end') // println('bs "$query" $start -> $end')
mid := start + ((end - start) / 2) 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) m.bs(query, mid, end, out)
} }
*/
fn compare_map(a, b *Entry) int { fn (m mut map) preorder_keys(node &Node) {
if a.key < b.key { m._keys[m.key_i] = node.key
return -1 m.key_i++
if !isnil(node.left) {
m.preorder_keys(node.left)
} }
if a.key > b.key { if !isnil(node.right) {
return 1 m.preorder_keys(node.right)
} }
return 0
} }
pub fn (m mut map) sort() { pub fn (m mut map) keys() []string {
m.entries.sort_with_compare(compare_map) m._keys = [''; m.size]
m.is_sorted = true m.key_i = 0
if isnil(m.root) {
return m._keys
} }
m.preorder_keys(m.root)
pub fn (m map) keys() []string { return m._keys
mut keys := []string{}
for i := 0; i < m.entries.len; i++ {
entry := m.entries[i]
keys << entry.key
}
return keys
} }
fn (m map) get(key string, out voidptr) bool { fn (m map) get(key string, out voidptr) bool {
if m.is_sorted { if isnil(m.root) {
// 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 return false
} }
return m.root.find(key, out, m.element_size)
}
pub fn (m map) exists(key string) bool { pub fn (m map) exists(key string) bool {
for i := 0; i < m.entries.len; i++ { return !isnil(m.root) && m.root.find2(key, m.element_size)
entry := m.entries[i]
if entry.key == key {
return true
}
}
return false
} }
pub fn (m map) print() { pub fn (m map) print() {
println('<<<<<<<<') println('<<<<<<<<')
for i := 0; i < m.entries.len; i++ { //for i := 0; i < m.entries.len; i++ {
// entry := m.entries[i] // entry := m.entries[i]
// println('$entry.key => $entry.val') // println('$entry.key => $entry.val')
} //}
/* /*
for i := 0; i < m.cap * m.element_size; i++ { for i := 0; i < m.cap * m.element_size; i++ {
b := m.table[i] b := m.table[i]
@ -158,15 +197,15 @@ pub fn (m map) free() {
pub fn (m map_string) str() string { pub fn (m map_string) str() string {
// return 'not impl' // return 'not impl'
if m.entries.len == 0 { if m.size == 0 {
return '{}' return '{}'
} }
// TODO use bytes buffer // TODO use bytes buffer
mut s := '{\n' mut s := '{\n'
for entry in m.entries { //for entry in m.entries {
val := m[entry.key] //val := m[entry.key]
s += ' "$entry.key" => "$val"\n' //s += ' "$entry.key" => "$val"\n'
} //}
s += '}' s += '}'
return s return s
} }

View File

@ -1,3 +1,5 @@
import time
struct User { struct User {
name string name string
} }
@ -13,13 +15,32 @@ fn (a mut A) set(key string, val int) {
fn test_map() { fn test_map() {
mut m := map[string]int{} mut m := map[string]int{}
assert m.size == 0
m['hi'] = 80 m['hi'] = 80
m['hello'] = 101
assert m['hi'] == 80 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{} mut users := map[string]User{}
users['1'] = User{'Peter'} users['1'] = User{'Peter'}
peter := users['1'] peter := users['1']
assert peter.name == 'Peter' assert peter.name == 'Peter'
println(peter.name)
mut a := A{ mut a := A{
m: map[string]int{} m: map[string]int{}
@ -33,3 +54,22 @@ fn test_map() {
assert a.m['one'] == 1 assert a.m['one'] == 1
assert a.m['two'] == 2 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)
}