From 985fb91ee804bfd3ed89aea5f67782b06b564d43 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Fri, 8 Nov 2019 06:03:06 +0300 Subject: [PATCH] rewrite interfaces --- vlib/compiler/cgen.v | 30 ++++++++ vlib/compiler/fn.v | 102 +++++++++++++++++++++------ vlib/compiler/gen_c.v | 14 +++- vlib/compiler/main.v | 1 + vlib/compiler/parser.v | 5 +- vlib/compiler/struct.v | 8 +-- vlib/compiler/table.v | 17 +++-- vlib/compiler/tests/interface_test.v | 25 +++++-- 8 files changed, 155 insertions(+), 47 deletions(-) diff --git a/vlib/compiler/cgen.v b/vlib/compiler/cgen.v index bc62dde7f0..239eb0222b 100644 --- a/vlib/compiler/cgen.v +++ b/vlib/compiler/cgen.v @@ -5,6 +5,7 @@ module compiler import os +import strings struct CGen { out os.File @@ -387,3 +388,32 @@ fn sort_structs(types []Type) []Type { } return types_sorted } + +fn (v &V) interface_table() string { + mut sb := strings.new_builder(100) + for _, t in v.table.typesmap { + if t.cat != .interface_ { + continue + } + mut methods := '' + for i, gen_type in t.gen_types { + methods += '{' + for i, method in t.methods { + // Cat_speak + methods += '${gen_type}_${method.name}' + if i < t.methods.len - 1 { + methods += ', ' + } + } + methods += '}, ' + // Speaker_Cat_index = 0 + sb.writeln('int _${t.name}_${gen_type}_index = $i;') + } + sb.writeln('void* (* ${t.name}_name_table[][$t.methods.len]) = ' + +'{ $methods }; ') + continue + } + return sb.str() +} + + diff --git a/vlib/compiler/fn.v b/vlib/compiler/fn.v index eda4a0030e..dfdf6308fb 100644 --- a/vlib/compiler/fn.v +++ b/vlib/compiler/fn.v @@ -12,8 +12,7 @@ const ( MaxLocalVars = 50 ) -pub -struct Fn { +pub struct Fn { // addr int pub: mut: @@ -560,7 +559,7 @@ fn (p mut Parser) skip_fn_body() { } } -fn (p Parser) get_linkage_prefix() string { +fn (p &Parser) get_linkage_prefix() string { return if p.pref.ccompiler == 'msvc' && p.attr == 'live' && p.pref.is_so { '__declspec(dllexport) ' } else if p.attr == 'inline' { @@ -718,6 +717,27 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s // we need to preappend "method(receiver, ...)" if f.is_method { receiver := f.args.first() + if receiver.typ.ends_with('er') { + // I absolutely love this syntax + // `s.speak()` => + // `((void (*)())(Speaker_name_table[s._interface_idx][1]))(s._object); + // where `1` refers to the speak method, since it's the second method + // of the Speaker interface + t := p.table.find_type(receiver.typ) + if t.cat == .interface_ { + // Find the index of the method + mut idx := 0 + for i, method in t.methods { + if method.name == f.name { + idx = i + } + } + p.cgen.resetln('') + var := p.expr_var.name + iname := f.args[0].typ // Speaker + p.gen('((void (*)())(${iname}_name_table[${var}._interface_idx][$idx]))(${var}._object)') + } + } //println('r=$receiver.typ RT=$receiver_type') if receiver.is_mut && !p.expr_var.is_mut { //println('$method_call recv=$receiver.name recv_mut=$receiver.is_mut') @@ -747,7 +767,9 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s // println('calling inst $f.name: $p.cgen.cur_line') } - p.gen(')') + //if !is_interface { + p.gen(')') + //} p.calling_c = false if is_comptime_define { p.cgen.nogen = false @@ -757,16 +779,16 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s } // for declaration -// return an updated Fn object with args[] field set +// update the Fn object's args[] fn (p mut Parser) fn_args(f mut Fn) { p.check(.lpar) defer { p.check(.rpar) } if f.is_interface { - int_arg := Var { + interface_arg := Var { typ: f.receiver_typ token_idx: p.cur_tok_index() } - f.args << int_arg + f.args << interface_arg } // `(int, string, int)` // Just register fn arg types @@ -829,7 +851,7 @@ fn (p mut Parser) fn_args(f mut Fn) { p.check_and_register_used_imported_type(typ) if is_mut && is_primitive_type(typ) { p.error('mutable arguments are only allowed for arrays, maps, and structs.' + - '\nreturn values instead: `foo(n mut int)` => `foo(n int) int`') + '\nreturn values instead: `fn foo(n mut int) {` => `fn foo(n int) int {`') } for name in names { if is_mut { @@ -916,8 +938,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) { ph := p.cgen.add_placeholder() // `)` here means that not enough args were provided 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)`') + p.error('not enough arguments in call to `${f.str_for_error()}`') } // If `arg` is mutable, the caller needs to provide `mut`: // `mut numbers := [1,2,3]; reverse(mut numbers);` @@ -951,6 +972,21 @@ fn (p mut Parser) fn_call_args(f mut Fn) { p.gen('/*YY f=$f.name arg=$arg.name is_moved=$arg.is_moved*/string_clone(') } mut typ := p.bool_expression() + // Register an interface type usage: + // fn run(r Animal) { ... } + // `run(dog)` adds `Dog` to the `Animal` interface. + // This is needed to generate an interface table. + if arg.typ.ends_with('er') { + t := p.table.find_type(arg.typ) + if t.cat == .interface_ { + // perform((Speaker) { ._object = &dog, + // _interface_idx = _Speaker_Dog_index }) + p.cgen.set_placeholder(ph, '($arg.typ) { ._object = &') + p.gen(', ._interface_idx = _${arg.typ}_${typ}_index} /* i. arg*/') + p.table.add_gen_type(arg.typ, typ) + } + } + if clone { p.gen(')') } @@ -1077,19 +1113,19 @@ fn (p mut Parser) fn_call_args(f mut Fn) { } else if is_interface { if !got_ptr { - p.cgen.set_placeholder(ph, '&') + //p.cgen.set_placeholder(ph, '&') } // Pass all interface methods - interface_type := p.table.find_type(arg.typ) - for method in interface_type.methods { - p.gen(', ${typ}_${method.name} ') - } + //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 if p.tok != .comma && !f.is_variadic { - p.error('wrong number of arguments for $i,$arg.name fn `$f.name`: expected $f.args.len, but got less') + p.error('wrong number of arguments in call to `${f.str_for_error()}`') } if p.tok == .comma && (!f.is_variadic || (f.is_variadic && i < f.args.len-2 )) { p.check(.comma) @@ -1103,7 +1139,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) { saved_args << varg_type } if p.tok == .comma { - p.error('wrong number of arguments for fn `$f.name`: expected $f.args.len, but got more') + p.error('wrong number of arguments in call to `${f.str_for_error()}`') } p.check(.rpar) if f.is_generic { @@ -1223,8 +1259,8 @@ fn (p mut Parser) replace_type_params(f &Fn, ti TypeInst) []string { fn (p mut Parser) register_vargs_stuct(typ string, len int) string { vargs_struct := 'varg_$typ' varg_type := Type{ - cat: .struct_, - name: vargs_struct, + cat: .struct_ + name: vargs_struct mod: p.mod } mut varg_len := len @@ -1462,6 +1498,7 @@ fn (f &Fn) str_args(table &Table) string { // 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' @@ -1477,7 +1514,8 @@ fn (f &Fn) str_args(table &Table) string { s += ')' } } - else if arg.typ.starts_with('varg_') { + */ + if arg.typ.starts_with('varg_') { s += '$arg.typ *$arg.name' } else { @@ -1519,11 +1557,31 @@ fn (fns []Fn) contains(f Fn) bool { return false } -pub fn (f Fn) v_fn_module() string { +pub fn (f &Fn) v_fn_module() string { return f.mod } -pub fn (f Fn) v_fn_name() string { +pub fn (f &Fn) v_fn_name() string { return f.name.replace('${f.mod}__', '') } +pub fn (f &Fn) str_for_error() string { + // Build the args for the error + mut s := '' + for i, a in f.args { + if i == 0 { + if f.is_method { + s += a.typ + '.' + f.name + '(' + continue + } + s += f.name + '(' + } + s += a.typ + if i < f.args.len - 1 { + s += ', ' + } + } + return s + ')' +} + + diff --git a/vlib/compiler/gen_c.v b/vlib/compiler/gen_c.v index afc1c5cae9..98659a8100 100644 --- a/vlib/compiler/gen_c.v +++ b/vlib/compiler/gen_c.v @@ -145,18 +145,23 @@ fn (p mut Parser) gen_handle_option_or_else(_typ, name string, fn_call_ph int) s fn types_to_c(types []Type, table &Table) string { mut sb := strings.new_builder(10) for t in types { - if t.cat != .union_ && t.cat != .struct_ && t.cat != .objc_interface { + //if t.cat != .union_ && t.cat != .struct_ && t.cat != .objc_interface { + if !(t.cat in [.union_, .struct_, .objc_interface, .interface_]) { continue } //if is_atomic { //sb.write('_Atomic ') //} - if t.cat == .objc_interface { + if t.cat == .objc_interface { sb.writeln('@interface $t.name : $t.parent { @public') } else { kind := if t.cat == .union_ {'union'} else {'struct'} sb.writeln('$kind $t.name {') + if t.cat == .interface_ { + sb.writeln('\tvoid* _object;') + sb.writeln('\tint _interface_idx; // int t') + } } for field in t.fields { sb.write('\t') @@ -238,6 +243,11 @@ fn (table mut Table) fn_gen_name(f &Fn) string { } } } + if f.is_interface { + // iname := f.args[0].typ // Speaker + // var := p.expr_var.name + return '' + } // Avoid name conflicts (with things like abs(), print() etc). // Generate v_abs(), v_print() // TODO duplicate functionality diff --git a/vlib/compiler/main.v b/vlib/compiler/main.v index 0b1f18812d..5038ecdbb0 100644 --- a/vlib/compiler/main.v +++ b/vlib/compiler/main.v @@ -297,6 +297,7 @@ pub fn (v mut V) compile() { def.writeln('\nstring _STR(const char*, ...);\n') def.writeln('\nstring _STR_TMP(const char*, ...);\n') def.writeln(cgen.fns.join_lines()) // fn definitions + def.writeln(v.interface_table()) } $else { def.writeln(v.type_definitions()) } diff --git a/vlib/compiler/parser.v b/vlib/compiler/parser.v index 97398625a7..272a367176 100644 --- a/vlib/compiler/parser.v +++ b/vlib/compiler/parser.v @@ -697,8 +697,8 @@ fn (p mut Parser) check_string() string { } fn (p mut Parser) check_not_reserved () { - if Reserved_Types[p.lit] { - p.error('`$p.lit` can\'t be used as name') + if Reserved_Types[p.lit] { + p.error('`$p.lit` can\'t be used as name') } } @@ -2053,6 +2053,7 @@ fn (p mut Parser) indot_expr() string { return typ } +// { user | name: 'new name' } fn (p mut Parser) assoc() string { // println('assoc()') p.next() diff --git a/vlib/compiler/struct.v b/vlib/compiler/struct.v index 0caec59e35..c6b584386a 100644 --- a/vlib/compiler/struct.v +++ b/vlib/compiler/struct.v @@ -222,12 +222,8 @@ fn (p mut Parser) struct_decl() { p.fgenln('') } p.check(.rcbr) - if !is_c { - if !did_gen_something { - if p.first_pass() { - p.table.add_field(typ.name, '', 'EMPTY_STRUCT_DECLARATION', false, '', .private) - } - } + if !is_c && !did_gen_something && p.first_pass() { + p.table.add_field(typ.name, '', 'EMPTY_STRUCT_DECLARATION', false, '', .private) } p.fgenln('\n') } diff --git a/vlib/compiler/table.v b/vlib/compiler/table.v index 859c437c73..2e6867e898 100644 --- a/vlib/compiler/table.v +++ b/vlib/compiler/table.v @@ -538,16 +538,14 @@ fn (t &Type) find_method(name string) ?Fn { return none } -/* -// TODO -fn (t mutt Type) add_gen_type(type_name string) { - // println('add_gen_type($s)') - if t.gen_types.contains(type_name) { +fn (table mut Table) add_gen_type(type_name, gen_type string) { + mut t := table.typesmap[type_name] + if gen_type in t.gen_types { return } - t.gen_types << type_name + t.gen_types << gen_type + table.typesmap[type_name] = t } -*/ fn (p &Parser) find_type(name string) Type { typ := p.table.find_type(name) @@ -576,7 +574,7 @@ fn (p mut Parser) check_types2(got_, expected_ string, throw bool) bool { if p.pref.translated { return true } - if got == expected { + if got == expected { return true } @@ -728,7 +726,8 @@ fn (p mut Parser) satisfies_interface(interface_name, _typ string, throw bool) b for method in int_typ.methods { if !typ.has_method(method.name) { // if throw { - p.error('Type "$_typ" doesn\'t satisfy interface "$interface_name" (method "$method.name" is not implemented)') + p.error('type `$_typ` doesn\'t satisfy interface ' + + '`$interface_name` (method `$method.name` is not implemented)') // } return false } diff --git a/vlib/compiler/tests/interface_test.v b/vlib/compiler/tests/interface_test.v index 09df37643e..f37db18eeb 100644 --- a/vlib/compiler/tests/interface_test.v +++ b/vlib/compiler/tests/interface_test.v @@ -2,13 +2,16 @@ struct Dog { breed string } -fn (d Dog) speak() { - println('dog.speak()') +struct Cat { + breed string } -fn (d Dog) name() string { - return 'old gray' -} + +fn (d Cat) name() string { return 'Cat' } +fn (d Cat) speak() { println('meow') } + +fn (d Dog) speak() { println('woof') } +fn (d Dog) name() string { return 'Dog'} interface Speaker { name() string @@ -16,10 +19,14 @@ interface Speaker { } interface Speak2er { - speak() name() string + speak() } +struct Foo { + speaker Speaker +} + fn perform_speak(s Speaker) bool { s.speak() return true @@ -28,5 +35,11 @@ fn perform_speak(s Speaker) bool { fn test_perform_speak() { d := Dog{} assert perform_speak(d) + cat := Cat{} + assert perform_speak(cat) + f := Foo { + //speaker: d + } + }