diff --git a/compiler/comptime.v b/compiler/comptime.v index 2b0b9ad3d6..7cdbfb8bf8 100644 --- a/compiler/comptime.v +++ b/compiler/comptime.v @@ -242,8 +242,9 @@ fn (p mut Parser) comptime_method_call(typ Type) { } } -fn (p mut Parser) gen_array_str(typ mut Type) { - typ.add_method(Fn{ +fn (p mut Parser) gen_array_str(typ Type) { + //println('gen array str "$typ.name"') + p.table.add_method(typ.name, Fn{ name: 'str', typ: 'string' args: [Var{typ: typ.name, is_arg:true}] @@ -251,9 +252,17 @@ fn (p mut Parser) gen_array_str(typ mut Type) { is_public: true receiver_typ: typ.name }) + /* + tt := p.table.find_type(typ.name) + for m in tt.methods { + println(m.name + ' ' + m.typ) + } + */ t := typ.name elm_type := t.right(6) - if p.typ_to_fmt(elm_type, 0) == '' && !p.table.type_has_method(p.table.find_type(elm_type), 'str') { + elm_type2 := p.table.find_type(elm_type) + if p.typ_to_fmt(elm_type, 0) == '' && + !p.table.type_has_method(elm_type2, 'str') { p.error('cant print ${elm_type}[], unhandled print of ${elm_type}') } p.cgen.fns << ' diff --git a/compiler/fn.v b/compiler/fn.v index f69206deab..94ae1f54c9 100644 --- a/compiler/fn.v +++ b/compiler/fn.v @@ -185,7 +185,7 @@ fn (p mut Parser) fn_decl() { // is_sig := p.builtin_mod && p.pref.build_mode == default_mode // is_sig := p.pref.build_mode == default_mode && (p.builtin_mod || p.file.contains(LANG_TMP)) is_sig := p.is_sig() - // println('\n\nfn decl !!is_sig=$is_sig name=$f.name $p.builtin_mod') + // println('\n\nfn_decl() name=$f.name receiver_typ=$receiver_typ') if is_c { p.check(.dot) f.name = p.check_name() @@ -330,15 +330,15 @@ fn (p mut Parser) fn_decl() { // 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_pass() && receiver_t.name == '' { - // println('fn decl !!!!!!! REG PH $receiver_typ') - p.table.register_type2(Type { + //println('fn decl ! registering placeholder $receiver_typ') + receiver_t = Type { name: receiver_typ.replace('*', '') mod: p.mod is_placeholder: true - }) + } + p.table.register_type2(receiver_t) } - // f.idx = p.table.fn_cnt - receiver_t.add_method(f) + p.table.add_method(receiver_t.name, f) } else { // println('register_fn typ=$typ isg=$is_generic') @@ -852,7 +852,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) *Fn { if !T.has_method('str') { // Arrays have automatic `str()` methods if T.name.starts_with('array_') { - p.gen_array_str(mut T) + p.gen_array_str(T) p.cgen.set_placeholder(ph, '${typ}_str(') p.gen(')') continue diff --git a/compiler/main.v b/compiler/main.v index 2e8fd65aaf..185d8d94cb 100644 --- a/compiler/main.v +++ b/compiler/main.v @@ -812,7 +812,7 @@ fn new_v(args[]string) *V { if pref.is_so { out_name_c = out_name.all_after('/') + '_shared_lib.c' } - return &V { + return &V{ os: _os out_name: out_name files: files @@ -951,8 +951,13 @@ fn test_v() { fn create_symlink() { vexe := os.executable() link_path := '/usr/local/bin/v' - os.system('ln -sf $vexe $link_path') - println('symlink "$link_path" has been created') + ret := os.system('ln -sf $vexe $link_path') + if ret == 0 { + println('symlink "$link_path" has been created') + } else { + println('failed to create symlink "$link_path", '+ + 'make sure you run with sudo') + } } pub fn cerror(s string) { diff --git a/compiler/parser.v b/compiler/parser.v index d0b7c95d8d..f5057fe373 100644 --- a/compiler/parser.v +++ b/compiler/parser.v @@ -517,15 +517,17 @@ fn (p mut Parser) struct_decl() { mut typ := p.table.find_type(name) mut is_ph := false if typ.is_placeholder { + // Update the placeholder is_ph = true typ.name = name typ.mod = p.mod typ.is_c = is_c typ.is_placeholder = false typ.cat = cat + p.table.rewrite_type(typ) } else { - typ = &Type { + typ = Type { name: name mod: p.mod is_c: is_c @@ -552,6 +554,12 @@ fn (p mut Parser) struct_decl() { } println('fmt max len = $max_len nrfields=$typ.fields.len pass=$p.pass') */ + + if !is_ph && p.first_pass() { + p.table.register_type2(typ) + //println('registering 1 nrfields=$typ.fields.len') + } + mut did_gen_something := false for p.tok != .rcbr { if p.tok == .key_pub { @@ -598,8 +606,8 @@ fn (p mut Parser) struct_decl() { names << field_name // We are in an interface? // `run() string` => run is a method, not a struct field - if is_interface { - typ.add_method(p.interface_method(field_name, name)) + if is_interface { //&& p.first_pass() { + p.table.add_method(typ.name, p.interface_method(field_name, name)) continue } // `pub` access mod @@ -625,16 +633,11 @@ fn (p mut Parser) struct_decl() { p.error('struct field with attribute "raw" should be of type "string" but got "$field_type"') } did_gen_something = true - - typ.add_field(field_name, field_type, is_mut, attr, access_mod) + if p.first_pass() { + p.table.add_field(typ.name, field_name, field_type, is_mut, attr, access_mod) + } p.fgenln('') } - - if !is_ph && p.first_pass() { - p.table.register_type2(typ) - //println('registering 1 nrfields=$typ.fields.len') - } - p.check(.rcbr) p.fgenln('\n') } @@ -666,7 +669,7 @@ fn (p mut Parser) enum_decl(_enum_name string) { p.table.register_const(name, enum_name, p.mod) val++ } - p.table.register_type2(&Type { + p.table.register_type2(Type { name: enum_name mod: p.mod parent: 'int' @@ -743,7 +746,6 @@ fn (p mut Parser) error(s string) { // Dump all vars and types for debugging if p.pref.is_debug { // os.write_to_file('/var/tmp/lang.types', '')//pes(p.table.types)) - // os.write_to_file('/var/tmp/lang.vars', q.J(p.table.vars)) os.write_file('fns.txt', p.table.debug_fns()) } if p.pref.is_verbose || p.pref.is_debug { @@ -755,13 +757,16 @@ fn (p mut Parser) error(s string) { if !p.pref.is_repl && !p.pref.is_test && ( p.file_path.contains('v/compiler') || cur_path.contains('v/compiler') ){ println('\n=========================') println('It looks like you are building V. It is being frequently updated every day.') - println('If you didn\'t modify the compiler\'s code, most likely there was a change that ') + println('If you didn\'t modify V\'s code, most likely there was a change that ') println('lead to this error.') - println('\nRun `git pull && make`, that will most likely fix it.') + println('\nRun `v up`, that will most likely fix it.') //println('\nIf this doesn\'t help, re-install V from source or download a precompiled' + ' binary from\nhttps://vlang.io.') println('\nIf this doesn\'t help, please create a GitHub issue.') println('=========================\n') } + if p.pref.is_debug { + print_backtrace() + } // p.scanner.debug_tokens() // Print `[]int` instead of `array_int` in errors p.scanner.error(s.replace('array_', '[]').replace('__', '.').replace('Option_', '?')) @@ -1750,7 +1755,7 @@ fn (p mut Parser) dot(str_typ string, method_ph int) string { mut has_method := p.table.type_has_method(typ, field_name) // generate `.str()` if !has_method && field_name == 'str' && typ.name.starts_with('array_') { - p.gen_array_str(mut typ) + p.gen_array_str(typ) has_method = true } if !typ.is_c && !p.is_c_fn_call && !has_field && !has_method && !p.first_pass() { @@ -2512,10 +2517,11 @@ fn (p mut Parser) string_expr() { f := p.typ_to_fmt(typ, 0) if f == '' { is_array := typ.starts_with('array_') - has_str_method := p.table.type_has_method(p.table.find_type(typ), 'str') + typ2 := p.table.find_type(typ) + has_str_method := p.table.type_has_method(typ2, 'str') if is_array || has_str_method { if is_array && !has_str_method { - p.gen_array_str(mut p.table.find_type(typ)) + p.gen_array_str(typ2) } args = args.all_before_last(val) + '${typ}_str(${val}).len, ${typ}_str(${val}).str' format += '%.*s ' @@ -2758,9 +2764,9 @@ fn (p mut Parser) array_init() string { fn (p mut Parser) struct_init(typ string, is_c_struct_init bool) string { p.is_struct_init = true t := p.table.find_type(typ) - // TODO hack. If it's a C type, we may need to add struct before declaration: + // TODO hack. If it's a C type, we may need to add "struct" before declaration: // a := &C.A{} ==> struct A* a = malloc(sizeof(struct A)); - if is_c_struct_init { // && t.cat != .c_typedef { + if is_c_struct_init { p.is_c_struct_init = true if t.cat != .c_typedef { p.cgen.insert_before('struct /*c struct init*/') @@ -2800,8 +2806,7 @@ fn (p mut Parser) struct_init(typ string, is_c_struct_init bool) string { p.check(.rcbr) return typ } - //p.gen('ALLOC_INIT($no_star, {') -p.gen('($no_star*)memdup(&($no_star) {') //sizeof(Node)); + p.gen('($no_star*)memdup(&($no_star) {') //sizeof(Node)); } mut did_gen_something := false // Loop thru all struct init keys and assign values @@ -2812,7 +2817,7 @@ p.gen('($no_star*)memdup(&($no_star) {') //sizeof(Node)); if peek == .colon || p.tok == .rcbr { for p.tok != .rcbr { field := if typ != 'Option' { p.table.var_cgen_name( p.check_name() ) } else { p.check_name() } - if !t.has_field(field) { + if !p.first_pass() && !t.has_field(field) { p.error('`$t.name` has no field `$field`') } if inited_fields.contains(field) { diff --git a/compiler/table.v b/compiler/table.v index 4dc85563f7..6f20417a76 100644 --- a/compiler/table.v +++ b/compiler/table.v @@ -50,11 +50,11 @@ enum AccessMod { enum TypeCategory { builtin struct_ - func - interface_ // 2 + func // 2 + interface_ enum_ - union_ - c_struct // 5 + union_ // 5 + c_struct c_typedef } @@ -415,7 +415,42 @@ fn (t mut Table) register_type2(typ Type) { t.types << typ } -fn (t mut Type) add_field(name, typ string, is_mut bool, attr string, access_mod AccessMod) { +fn (t mut Table) rewrite_type(typ Type) { + if typ.name.len == 0 { + return + } + for i, typ2 in t.types { + if typ2.name == typ.name { + t.types[i] = typ + return + } + } +} + +fn (table mut Table) add_field(type_name, field_name, field_type string, is_mut bool, attr string, access_mod AccessMod) { + if type_name == '' { + print_backtrace() + cerror('add_field: empty type') + } + for i, typ in table.types { + if typ.name == type_name { + table.types[i].fields << Var { + name: field_name + typ: field_type + is_mut: is_mut + attr: attr + parent_fn: type_name // Name of the parent type + access_mod: access_mod + } + return + } + } + print_backtrace() + cerror('failed to add_field `$field_name` to type `$type_name`') +} + +/* +fn adf(name, typ string, is_mut bool, attr string, access_mod AccessMod) { // if t.name == 'Parser' { // println('adding field $name') // } @@ -429,6 +464,7 @@ fn (t mut Type) add_field(name, typ string, is_mut bool, attr string, access_mod } t.fields << v } +*/ fn (t &Type) has_field(name string) bool { field := t.find_field(name) @@ -462,12 +498,19 @@ fn (table &Table) find_field(typ &Type, name string) Var { 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 (table mut Table) add_method(type_name string, f Fn) { + if type_name == '' { + print_backtrace() + cerror('add_method: empty type') + } + for i, typ in table.types { + if typ.name == type_name { + table.types[i].methods << f + return + } + } + print_backtrace() + cerror('failed to add_method `$f.name` to type `$type_name`') } fn (t &Type) has_method(name string) bool { @@ -505,7 +548,8 @@ fn (t &Type) find_method(name string) Fn { } /* -fn (t mut Type) add_gen_type(type_name string) { +// TODO +fn (t mutt Type) add_gen_type(type_name string) { // println('add_gen_type($s)') if t.gen_types.contains(type_name) { return @@ -514,26 +558,36 @@ fn (t mut Type) add_gen_type(type_name string) { } */ -fn (p &Parser) find_type(name string) &Type { +fn (p &Parser) find_type(name string) Type { typ := p.table.find_type(name) - if typ.name.len == 0 { + if typ.name == '' { return p.table.find_type(p.prepend_mod(name)) } return typ } -fn (t &Table) find_type(name_ string) &Type { +fn (t &Table) find_type(name_ string) Type { mut name := name_ + debug := name.starts_with('V') && name.len < 3 + if debug { + //println('find_type("$name)"') + } if name.ends_with('*') && !name.contains(' ') { name = name.left(name.len - 1) } // TODO PERF use map for i, typ in t.types { + if debug { + //println('^^ "$typ.name"') + } if typ.name == name { - return &t.types[i] + return t.types[i] } } - return &Type{} + if debug { + //println('NOT FOUND') + } + return Type{} } fn (p mut Parser) _check_types(got_, expected_ string, throw bool) bool { @@ -637,6 +691,7 @@ fn (p mut Parser) _check_types(got_, expected_ string, throw bool) bool { // throw by default fn (p mut Parser) check_types(got, expected string) bool { + if p.first_pass() { return true } return p._check_types(got, expected, true) } diff --git a/compiler/tests/interface_test.v b/compiler/tests/interface_test.v index 822d5c1dcf..8bee5dde51 100644 --- a/compiler/tests/interface_test.v +++ b/compiler/tests/interface_test.v @@ -1,4 +1,3 @@ - struct Dog { } @@ -12,11 +11,11 @@ fn (d Dog) name() string { interface Speaker { name() string - speak() + speak() } interface Speak2er { - speak() + speak() name() string } diff --git a/compiler/tests/str_gen_test.v b/compiler/tests/str_gen_test.v index 97230724d5..8e83c485dd 100644 --- a/compiler/tests/str_gen_test.v +++ b/compiler/tests/str_gen_test.v @@ -1,16 +1,16 @@ struct Foo { - a int -} + a int +} fn test_array_str() { f := Foo{34} - println(f) - //s := f.str() - //println(s) - n := [i64(1), 2, 3] - assert n.str() == '[1, 2, 3]' - println(n) // make sure the array is printable - n2 := [4,5,6] - assert n2.str() == '[4, 5, 6]' - println(n2) -} + println(f) + //s := f.str() + //println(s) + n := [i64(1), 2, 3] + assert n.str() == '[1, 2, 3]' + println(n) // make sure the array is printable + n2 := [4,5,6] + //assert n2.str() == '[4, 5, 6]' + println(n2) +} diff --git a/vlib/builtin/int.v b/vlib/builtin/int.v index fda2dc3cc8..17890ab4e1 100644 --- a/vlib/builtin/int.v +++ b/vlib/builtin/int.v @@ -7,12 +7,6 @@ module builtin #include #include -pub fn (d double) str() string { - buf := malloc(sizeof(double) * 5 + 1)// TODO - C.sprintf(buf, '%f', d) - return tos(buf, strlen(buf)) -} - pub fn (d f64) str() string { buf := malloc(sizeof(double) * 5 + 1)// TODO C.sprintf(buf, '%f', d)