From d7ee4755c20ec2e2c082cbb1706bf9c59a2ca42e Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 22 Apr 2020 20:20:49 +0200 Subject: [PATCH] parser/cgen: interfaces (part 1) --- vlib/v/ast/ast.v | 92 ++++++--- vlib/v/checker/checker.v | 8 +- vlib/v/checker/tests/inout/unknown_method.out | 2 +- vlib/v/fmt/fmt.v | 8 + vlib/v/gen/cgen.v | 137 +++++++++---- vlib/v/gen/fn.v | 66 +++--- vlib/v/parser/struct.v | 91 ++++++--- vlib/v/table/atypes.v | 188 ++++++------------ vlib/v/tests/interface_test.v | 62 +++--- 9 files changed, 365 insertions(+), 289 deletions(-) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index aefc4b360b..a829a547e9 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -157,6 +157,7 @@ pub struct InterfaceDecl { pub: name string field_names []string + methods []FnDecl } pub struct StructInitField { @@ -541,7 +542,6 @@ pub: name string is_pub bool fields []EnumField - // default_exprs []Expr pos token.Position } @@ -739,22 +739,46 @@ pub fn expr_is_call(expr Expr) bool { fn (expr Expr) position() token.Position { // all uncommented have to be implemented - match var expr { - ArrayInit { return it.pos } - AsCast { return it.pos } + match mut expr { + ArrayInit { + return it.pos + } + AsCast { + return it.pos + } // ast.Ident { } - AssignExpr { return it.pos } + AssignExpr { + return it.pos + } // ast.CastExpr { } - Assoc { return it.pos } - BoolLiteral { return it.pos } - CallExpr { return it.pos } - CharLiteral { return it.pos } - EnumVal { return it.pos } - FloatLiteral { return it.pos } - Ident { return it.pos } - IfExpr { return it.pos } + Assoc { + return it.pos + } + BoolLiteral { + return it.pos + } + CallExpr { + return it.pos + } + CharLiteral { + return it.pos + } + EnumVal { + return it.pos + } + FloatLiteral { + return it.pos + } + Ident { + return it.pos + } + IfExpr { + return it.pos + } // ast.IfGuardExpr { } - IndexExpr { return it.pos } + IndexExpr { + return it.pos + } InfixExpr { left_pos := it.left.position() right_pos := it.right.position() @@ -767,20 +791,40 @@ fn (expr Expr) position() token.Position { len: right_pos.pos - left_pos.pos + right_pos.len } } - IntegerLiteral { return it.pos } - MapInit { return it.pos } - MatchExpr { return it.pos } - PostfixExpr { return it.pos } + IntegerLiteral { + return it.pos + } + MapInit { + return it.pos + } + MatchExpr { + return it.pos + } + PostfixExpr { + return it.pos + } // ast.None { } - PrefixExpr { return it.pos } + PrefixExpr { + return it.pos + } // ast.ParExpr { } - SelectorExpr { return it.pos } + SelectorExpr { + return it.pos + } // ast.SizeOf { } - StringLiteral { return it.pos } - StringInterLiteral { return it.pos } + StringLiteral { + return it.pos + } + StringInterLiteral { + return it.pos + } // ast.Type { } - StructInit { return it.pos } + StructInit { + return it.pos + } // ast.TypeOf { } - else { return token.Position{} } + else { + return token.Position{} + } } } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index cf1069f394..e95be6a00d 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -471,13 +471,13 @@ pub fn (c mut Checker) call_method(call_expr mut ast.CallExpr) table.Type { //println('warn $method_name lef.mod=$left_type_sym.mod c.mod=$c.mod') c.error('method `${left_type_sym.name}.$method_name` is private', call_expr.pos) } - no_args := method.args.len - 1 + nr_args := if method.args.len == 0 { 0 } else {method.args.len - 1} min_required_args := method.args.len - if method.is_variadic && method.args.len > 1 { 2 } else { 1 } if call_expr.args.len < min_required_args { c.error('too few arguments in call to `${left_type_sym.name}.$method_name` ($call_expr.args.len instead of $min_required_args)', call_expr.pos) - } else if !method.is_variadic && call_expr.args.len > no_args { - c.error('too many arguments in call to `${left_type_sym.name}.$method_name` ($call_expr.args.len instead of $no_args)', + } else if !method.is_variadic && call_expr.args.len > nr_args { + c.error('!too many arguments in call to `${left_type_sym.name}.$method_name` ($call_expr.args.len instead of $nr_args)', call_expr.pos) return method.return_type } @@ -530,7 +530,7 @@ pub fn (c mut Checker) call_method(call_expr mut ast.CallExpr) table.Type { return info.func.return_type } } - c.error('unknown method: ${left_type_sym.name}.$method_name', call_expr.pos) + c.error('unknown method: `${left_type_sym.name}.$method_name`', call_expr.pos) return table.void_type } diff --git a/vlib/v/checker/tests/inout/unknown_method.out b/vlib/v/checker/tests/inout/unknown_method.out index ad5f480fa5..fc09c377fc 100644 --- a/vlib/v/checker/tests/inout/unknown_method.out +++ b/vlib/v/checker/tests/inout/unknown_method.out @@ -1,4 +1,4 @@ -vlib/v/checker/tests/inout/unknown_method.v:7:12: error: unknown method: Test.sdd +vlib/v/checker/tests/inout/unknown_method.v:7:12: error: unknown method: `Test.sdd` 5| fn main() { 6| t := Test{} 7| println(t.sdd()) diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index fabb8bda52..aed8d56553 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -333,6 +333,14 @@ fn (mut f Fmt) stmt(node ast.Stmt) { // Imports are handled after the file is formatted, to automatically add necessary modules // f.imports(f.file.imports) } + ast.InterfaceDecl { + f.writeln('interface $it.name {') + for method in it.methods { + f.write('\t') + f.writeln(method.str(f.table).after('fn ')) + } + f.writeln('}\n') + } ast.Module { f.mod(it) } diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 1c35b3966e..f8dcf5f230 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -151,17 +151,11 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string } // g.finish() - return g.hashes() + - '\n// V typedefs:\n' + g.typedefs.str() + - '\n// V typedefs2:\n' + g.typedefs2.str() + - '\n// V cheaders:\n' + g.cheaders.str() + - '\n// V includes:\n' + g.includes.str() + - '\n// V definitions:\n' + g.definitions.str() + - '\n// V gowrappers:\n' + g.gowrappers.str() + - '\n// V stringliterals:\n' + g.stringliterals.str() + - '\n// V auto str functions:\n' + g.auto_str_funcs.str() + - '\n// V out\n' + g.out.str() + - '\n// THE END.' + return g.hashes() + '\n// V typedefs:\n' + g.typedefs.str() + '\n// V typedefs2:\n' + g.typedefs2.str() + + '\n// V cheaders:\n' + g.cheaders.str() + '\n// V includes:\n' + g.includes.str() + '\n// V definitions:\n' + + g.definitions.str() + g.interface_table() + '\n// V gowrappers:\n' + g.gowrappers.str() + '\n// V stringliterals:\n' + + g.stringliterals.str() + '\n// V auto str functions:\n' + g.auto_str_funcs.str() + '\n// V out\n' + + g.out.str() + '\n// THE END.' } pub fn (g Gen) hashes() string { @@ -400,10 +394,10 @@ fn (mut g Gen) stmt(node ast.Stmt) { match node { ast.InterfaceDecl { g.writeln('//interface') - g.writeln('struct $it.name {') + g.writeln('typedef struct {') g.writeln('\tvoid* _object;') g.writeln('\tint _interface_idx;') - g.writeln('};') + g.writeln('} $it.name;') } ast.AssertStmt { g.gen_assert_stmt(it) @@ -458,11 +452,7 @@ fn (mut g Gen) stmt(node ast.Stmt) { cur_enum_expr = expr_str cur_enum_offset = 0 } - cur_value := if cur_enum_offset > 0 { - '${cur_enum_expr}+${cur_enum_offset}' - } else { - cur_enum_expr - } + cur_value := if cur_enum_offset > 0 { '${cur_enum_expr}+${cur_enum_offset}' } else { cur_enum_expr } g.typedefs.writeln(', // ${cur_value}') cur_enum_offset++ } @@ -2221,6 +2211,9 @@ fn (g Gen) sort_structs(typesa []table.TypeSymbol) []table.TypeSymbol { } // loop over types for t in typesa { + if t.kind == .interface_ { + continue + } // create list of deps mut field_deps := []string match t.info { @@ -2232,6 +2225,9 @@ fn (g Gen) sort_structs(typesa []table.TypeSymbol) []table.TypeSymbol { } table.Struct { info := t.info as table.Struct + // if info.is_interface { + // continue + // } for field in info.fields { dep := g.table.get_type_symbol(field.typ).name // skip if not in types list or already in deps @@ -2297,15 +2293,15 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { } else if sym.kind in [.struct_, .map] && !sym.has_method('str') { g.write('%.*s') } else if node.expr_types[i] == table.i16_type { - g.write('%"PRId16"') + g.write('%"PRId16"') } else if node.expr_types[i] == table.u16_type { - g.write('%"PRIu16"') + g.write('%"PRIu16"') } else if node.expr_types[i] == table.u32_type { - g.write('%"PRIu32"') + g.write('%"PRIu32"') } else if node.expr_types[i] == table.i64_type { - g.write('%"PRId64"') + g.write('%"PRId64"') } else if node.expr_types[i] == table.u64_type { - g.write('%"PRIu64"') + g.write('%"PRIu64"') } else { g.write('%"PRId32"') } @@ -2849,14 +2845,14 @@ fn (mut g Gen) is_expr(node ast.InfixExpr) { } fn styp_to_str_fn_name(styp string) string { - res := styp.replace('.', '__').replace('*','_ptr') + '_str' + res := styp.replace('.', '__').replace('*', '_ptr') + '_str' return res } // already generated styp, reuse it -fn (mut g Gen) gen_str_for_type(sym table.TypeSymbol, styp string, str_fn_name string) { +fn (mut g Gen) gen_str_for_type(sym table.TypeSymbol, styp, str_fn_name string) { already_generated_key := '${styp}:${str_fn_name}' - if sym.has_method('str') || already_generated_key in g.str_types { + if sym.has_method('str') || already_generated_key in g.str_types { return } g.str_types << already_generated_key @@ -2870,7 +2866,7 @@ fn (mut g Gen) gen_str_for_type(sym table.TypeSymbol, styp string, str_fn_name s } } -fn (mut g Gen) gen_str_default(sym table.TypeSymbol, styp string, str_fn_name string) { +fn (mut g Gen) gen_str_default(sym table.TypeSymbol, styp, str_fn_name string) { mut convertor := '' mut typename := '' if sym.parent_idx in table.integer_type_idxs { @@ -2901,7 +2897,7 @@ fn (mut g Gen) gen_str_default(sym table.TypeSymbol, styp string, str_fn_name st g.auto_str_funcs.writeln('}') } -fn (mut g Gen) gen_str_for_enum(info table.Enum, styp string, str_fn_name string) { +fn (mut g Gen) gen_str_for_enum(info table.Enum, styp, str_fn_name string) { s := styp.replace('.', '__') g.definitions.writeln('string ${str_fn_name}($styp it); // auto') g.auto_str_funcs.writeln('string ${str_fn_name}($styp it) { /* gen_str_for_enum */') @@ -2914,27 +2910,29 @@ fn (mut g Gen) gen_str_for_enum(info table.Enum, styp string, str_fn_name string g.auto_str_funcs.writeln('}') } -fn (mut g Gen) gen_str_for_struct(info table.Struct, styp string, str_fn_name string) { +fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) { // TODO: short it if possible // generates all definitions of substructs - mut fnames2strfunc := map[string]string + mut fnames2strfunc := { + '': '' + } // map[string]string // TODO vfmt bug for i, field in info.fields { sym := g.table.get_type_symbol(field.typ) if sym.kind in [.struct_, .array, .array_fixed, .map, .enum_] { field_styp := g.typ(field.typ) - field_fn_name := styp_to_str_fn_name( field_styp ) - fnames2strfunc[ field_styp ] = field_fn_name + field_fn_name := styp_to_str_fn_name(field_styp) + fnames2strfunc[field_styp] = field_fn_name g.gen_str_for_type(sym, field_styp, field_fn_name) } } g.definitions.writeln('string ${str_fn_name}($styp x, int indent_count); // auto') g.auto_str_funcs.writeln('string ${str_fn_name}($styp x, int indent_count) {') - mut clean_struct_v_type_name := styp.replace('__','.') + mut clean_struct_v_type_name := styp.replace('__', '.') if styp.ends_with('*') { deref_typ := styp.replace('*', '') g.auto_str_funcs.writeln('\t${deref_typ} *it = x;') clean_struct_v_type_name = '&' + clean_struct_v_type_name.replace('*', '') - }else{ + } else { deref_typ := styp g.auto_str_funcs.writeln('\t${deref_typ} *it = &x;') } @@ -2954,14 +2952,14 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp string, str_fn_name st for i, field in info.fields { sym := g.table.get_type_symbol(field.typ) has_custom_str := sym.has_method('str') - second_str_param := if has_custom_str {''} else {', indent_count + 1'} + second_str_param := if has_custom_str { '' } else { ', indent_count + 1' } field_styp := g.typ(field.typ) - field_styp_fn_name := if has_custom_str {'${field_styp}_str'} else {fnames2strfunc[ field_styp ]} - if sym.kind == .enum_ { + field_styp_fn_name := if has_custom_str { '${field_styp}_str' } else { fnames2strfunc[field_styp] } + if sym.kind == .enum_ { g.auto_str_funcs.write('indents.len, indents.str, ') g.auto_str_funcs.write('${field_styp_fn_name}( it->${field.name} ).len, ') g.auto_str_funcs.write('${field_styp_fn_name}( it->${field.name} ).str ') - }else if sym.kind in [.struct_, .array, .array_fixed] { + } else if sym.kind in [.struct_, .array, .array_fixed] { g.auto_str_funcs.write('indents.len, indents.str, ') g.auto_str_funcs.write('${field_styp_fn_name}( it->${field.name}${second_str_param} ).len, ') g.auto_str_funcs.write('${field_styp_fn_name}( it->${field.name}${second_str_param} ).str ') @@ -2983,11 +2981,11 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp string, str_fn_name st g.auto_str_funcs.writeln('}') } -fn (mut g Gen) gen_str_for_array(info table.Array, styp string, str_fn_name string) { +fn (mut g Gen) gen_str_for_array(info table.Array, styp, str_fn_name string) { sym := g.table.get_type_symbol(info.elem_type) field_styp := g.typ(info.elem_type) if sym.kind == .struct_ && !sym.has_method('str') { - g.gen_str_for_type(sym, field_styp, styp_to_str_fn_name(field_styp) ) + g.gen_str_for_type(sym, field_styp, styp_to_str_fn_name(field_styp)) } g.definitions.writeln('string ${str_fn_name}($styp a); // auto') g.auto_str_funcs.writeln('string ${str_fn_name}($styp a) {') @@ -3011,7 +3009,7 @@ fn (mut g Gen) gen_str_for_array(info table.Array, styp string, str_fn_name stri g.auto_str_funcs.writeln('}') } -fn (mut g Gen) gen_str_for_map(info table.Map, styp string, str_fn_name string) { +fn (mut g Gen) gen_str_for_map(info table.Map, styp, str_fn_name string) { key_sym := g.table.get_type_symbol(info.key_type) key_styp := g.typ(info.key_type) if key_sym.kind == .struct_ && !key_sym.has_method('str') { @@ -3068,3 +3066,60 @@ fn (g Gen) type_to_fmt(typ table.Type) string { } return '%d' } + +// Generates interface table and interface indexes +fn (v &Gen) interface_table() string { + mut sb := strings.new_builder(100) + for _, t in v.table.types { + if t.kind != .interface_ { + continue + } + info := t.info as table.Interface + // interface_name is for example Speaker + interface_name := t.name + mut methods := '' + mut generated_casting_functions := '' + sb.writeln('// NR gen_types= $info.gen_types.len') + for i, gen_type in info.gen_types { + // ptr_ctype can be for example Cat OR Cat_ptr: + ptr_ctype := gen_type.replace('*', '_ptr') + // cctype is the Cleaned Concrete Type name, *without ptr*, + // i.e. cctype is always just Cat, not Cat_ptr: + cctype := gen_type.replace('*', '') + // Speaker_Cat_index = 0 + interface_index_name := '_${interface_name}_${ptr_ctype}_index' + generated_casting_functions += ' +${interface_name} I_${cctype}_to_${interface_name}(${cctype} x) { + return (${interface_name}){ + ._object = (void*) memdup(&x, sizeof(${cctype})), + ._interface_idx = ${interface_index_name} }; +} +' + methods += '{\n' + for j, method in t.methods { + // Cat_speak + methods += ' (void*) ${cctype}_${method.name}' + if j < t.methods.len - 1 { + methods += ', \n' + } + } + methods += '\n},\n\n' + sb.writeln('int ${interface_index_name} = $i;') + } + if info.gen_types.len > 0 { + // methods = '{TCCSKIP(0)}' + // } + sb.writeln('void* (* ${interface_name}_name_table[][$t.methods.len]) = ' + '{ \n $methods \n }; ') + } else { + // The line below is needed so that C compilation succeeds, + // even if no interface methods are called. + // See https://github.com/zenith391/vgtk3/issues/7 + sb.writeln('void* (* ${interface_name}_name_table[][1]) = ' + '{ {NULL} }; ') + } + if generated_casting_functions.len > 0 { + sb.writeln('// Casting functions for interface "${interface_name}" :') + sb.writeln(generated_casting_functions) + } + } + return sb.str() +} diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index 78d3316cc6..793ac7eb86 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -3,13 +3,11 @@ // that can be found in the LICENSE file. module gen -import ( - v.ast - v.table - v.util -) +import v.ast +import v.table +import v.util -fn (g mut Gen) gen_fn_decl(it ast.FnDecl) { +fn (mut g Gen) gen_fn_decl(it ast.FnDecl) { if it.is_c { // || it.no_body { return @@ -88,7 +86,6 @@ fn (g mut Gen) gen_fn_decl(it ast.FnDecl) { g.writeln('\tint ___argc;') g.writeln('\twchar_t** ___argv = CommandLineToArgvW(cmd_line, &___argc);') } - g.writeln('\t_vinit();') if g.is_importing_os() { if g.autofree { @@ -126,11 +123,11 @@ fn (g mut Gen) gen_fn_decl(it ast.FnDecl) { g.fn_decl = 0 } -fn (g mut Gen) fn_args(args []table.Arg, is_variadic bool) { +fn (mut g Gen) fn_args(args []table.Arg, is_variadic bool) { no_names := args.len > 0 && args[0].name == 'arg_1' for i, arg in args { arg_type_sym := g.table.get_type_symbol(arg.typ) - mut arg_type_name := g.typ(arg.typ) // arg_type_sym.name.replace('.', '__') + mut arg_type_name := g.typ(arg.typ) // arg_type_sym.name.replace('.', '__') is_varg := i == args.len - 1 && is_variadic if is_varg { varg_type_str := int(arg.typ).str() @@ -175,7 +172,7 @@ fn (g mut Gen) fn_args(args []table.Arg, is_variadic bool) { } } -fn (g mut Gen) call_expr(node ast.CallExpr) { +fn (mut g Gen) call_expr(node ast.CallExpr) { gen_or := !g.is_assign_rhs && node.or_block.stmts.len > 0 tmp_opt := if gen_or { g.new_tmp_var() } else { '' } if gen_or { @@ -192,21 +189,37 @@ fn (g mut Gen) call_expr(node ast.CallExpr) { } } -fn (g mut Gen) method_call(node ast.CallExpr) { +fn (mut g Gen) method_call(node ast.CallExpr) { // TODO: there are still due to unchecked exprs (opt/some fn arg) if node.left_type == 0 { verror('method receiver type is 0, this means there are some uchecked exprs') } typ_sym := g.table.get_type_symbol(node.receiver_type) - // rec_sym := g.table.get_type_symbol(node.receiver_type) mut receiver_name := typ_sym.name + if typ_sym.kind == .interface_ { + g.writeln('// interface method call') + // `((void (*)())(Speaker_name_table[s._interface_idx][1]))(s._object);` + g.write('((void (*)())(${receiver_name}_name_table[') + g.expr(node.left) + g.write('._interface_idx][1]))(') + g.expr(node.left) + g.writeln('._object );') + return + } + // rec_sym := g.table.get_type_symbol(node.receiver_type) if typ_sym.kind == .array && node.name == 'filter' { g.gen_filter(node) return } // TODO performance, detect `array` method differently if typ_sym.kind == .array && node.name in ['repeat', 'sort_with_compare', 'free', 'push_many', - 'trim', 'first', 'last', 'clone', 'reverse', 'slice'] { + 'trim' + 'first' + 'last' + 'clone' + 'reverse' + 'slice' + ] { // && rec_sym.name == 'array' { // && rec_sym.name == 'array' && receiver_name.starts_with('array') { // `array_byte_clone` => `array_clone` @@ -255,7 +268,7 @@ fn (g mut Gen) method_call(node ast.CallExpr) { // } } -fn (g mut Gen) fn_call(node ast.CallExpr) { +fn (mut g Gen) fn_call(node ast.CallExpr) { // call struct field with fn type // TODO: test node.left instead // left & left_type will be `x` and `x type` in `x.fieldfn()` @@ -264,8 +277,7 @@ fn (g mut Gen) fn_call(node ast.CallExpr) { g.expr(node.left) if table.type_is_ptr(node.left_type) { g.write('->') - } - else { + } else { g.write('.') } } @@ -322,15 +334,9 @@ fn (g mut Gen) fn_call(node ast.CallExpr) { } else { expr := node.args[0].expr is_var := match expr { - ast.SelectorExpr { - true - } - ast.Ident { - true - } - else { - false - } + ast.SelectorExpr { true } + ast.Ident { true } + else { false } } if table.type_is_ptr(typ) && sym.kind != .struct_ { // ptr_str() for pointers @@ -361,7 +367,7 @@ fn (g mut Gen) fn_call(node ast.CallExpr) { } g.expr(expr) if sym.kind == .struct_ && styp != 'ptr' && !sym.has_method('str') { - g.write(', 0') // trailing 0 is initial struct indent count + g.write(', 0') // trailing 0 is initial struct indent count } } g.write('))') @@ -381,10 +387,10 @@ fn (g mut Gen) fn_call(node ast.CallExpr) { } } -fn (g mut Gen) call_args(args []ast.CallArg, expected_types []table.Type) { +fn (mut g Gen) call_args(args []ast.CallArg, expected_types []table.Type) { is_variadic := expected_types.len > 0 && table.type_is(expected_types[expected_types.len - 1], .variadic) - is_forwarding_varg := args.len > 0 && table.type_is(args[args.len-1].typ, .variadic) + is_forwarding_varg := args.len > 0 && table.type_is(args[args.len - 1].typ, .variadic) gen_vargs := is_variadic && !is_forwarding_varg mut arg_no := 0 for arg in args { @@ -427,7 +433,7 @@ fn (g mut Gen) call_args(args []ast.CallArg, expected_types []table.Type) { } [inline] -fn (g mut Gen) ref_or_deref_arg(arg ast.CallArg, expected_type table.Type) { +fn (mut g Gen) ref_or_deref_arg(arg ast.CallArg, expected_type table.Type) { arg_is_ptr := table.type_is_ptr(expected_type) || table.type_idx(expected_type) in table.pointer_type_idxs expr_is_ptr := table.type_is_ptr(arg.typ) || table.type_idx(arg.typ) in table.pointer_type_idxs if arg.is_mut && !arg_is_ptr { @@ -454,7 +460,7 @@ fn (g mut Gen) ref_or_deref_arg(arg ast.CallArg, expected_type table.Type) { g.expr_with_cast(arg.expr, arg.typ, expected_type) } -fn (g mut Gen) is_gui_app() bool { +fn (mut g Gen) is_gui_app() bool { $if windows { for cf in g.table.cflags { if cf.value == 'gdi32' { diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index 1b9efaea23..0e8cedef33 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -7,7 +7,7 @@ import v.ast import v.table import v.token -fn (var p Parser) struct_decl() ast.StructDecl { +fn (mut p Parser) struct_decl() ast.StructDecl { start_pos := p.tok.position() is_pub := p.tok.kind == .key_pub if is_pub { @@ -22,8 +22,8 @@ fn (var p Parser) struct_decl() ast.StructDecl { is_c := p.tok.lit == 'C' && p.peek_tok.kind == .dot is_js := p.tok.lit == 'JS' && p.peek_tok.kind == .dot if is_c { - p.next() // C || JS - p.next() // . + p.next() // C || JS + p.next() // . } is_typedef := p.attr == 'typedef' no_body := p.peek_tok.kind != .lcbr @@ -31,17 +31,17 @@ fn (var p Parser) struct_decl() ast.StructDecl { p.error('`$p.tok.lit` lacks body') } end_pos := p.tok.position() - var name := p.check_name() + mut name := p.check_name() // println('struct decl $name') - var ast_fields := []ast.StructField - var fields := []table.Field - var mut_pos := -1 - var pub_pos := -1 - var pub_mut_pos := -1 + mut ast_fields := []ast.StructField + mut fields := []table.Field + mut mut_pos := -1 + mut pub_pos := -1 + mut pub_mut_pos := -1 if !no_body { p.check(.lcbr) for p.tok.kind != .rcbr { - var comment := ast.Comment{} + mut comment := ast.Comment{} if p.tok.kind == .comment { comment = p.comment() } @@ -72,8 +72,8 @@ fn (var p Parser) struct_decl() ast.StructDecl { println('XXXX' + s.str()) } */ - var default_expr := ast.Expr{} - var has_default_expr := false + mut default_expr := ast.Expr{} + mut has_default_expr := false if p.tok.kind == .assign { // Default value p.next() @@ -81,15 +81,13 @@ fn (var p Parser) struct_decl() ast.StructDecl { // p.expr(0) default_expr = p.expr(0) match default_expr { - ast.EnumVal { - it.typ = typ - } + ast.EnumVal { it.typ = typ } // TODO: implement all types?? else {} } has_default_expr = true } - var attr := ast.Attr{} + mut attr := ast.Attr{} if p.tok.kind == .lsbr { attr = p.attribute() } @@ -132,7 +130,7 @@ fn (var p Parser) struct_decl() ast.StructDecl { } mod: p.mod } - var ret := 0 + mut ret := 0 if p.builtin_mod && t.name in table.builtin_type_names { // this allows overiding the builtins type // with the real struct type info parsed from builtin @@ -158,7 +156,7 @@ fn (var p Parser) struct_decl() ast.StructDecl { } } -fn (var p Parser) struct_init(short_syntax bool) ast.StructInit { +fn (mut p Parser) struct_init(short_syntax bool) ast.StructInit { first_pos := p.tok.position() typ := if short_syntax { table.void_type } else { p.parse_type() } p.expr_mod = '' @@ -167,17 +165,17 @@ fn (var p Parser) struct_init(short_syntax bool) ast.StructInit { if !short_syntax { p.check(.lcbr) } - var fields := []ast.StructInitField - var i := 0 - is_short_syntax := p.peek_tok.kind != .colon && p.tok.kind != .rcbr // `Vec{a,b,c} + mut fields := []ast.StructInitField + mut i := 0 + is_short_syntax := p.peek_tok.kind != .colon && p.tok.kind != .rcbr // `Vec{a,b,c} // p.warn(is_short_syntax.str()) for p.tok.kind != .rcbr { p.check_comment() - var field_name := '' + mut field_name := '' if is_short_syntax { expr := p.expr(0) + // name will be set later in checker fields << ast.StructInitField{ - // name will be set later in checker expr: expr pos: expr.position() } @@ -221,28 +219,57 @@ fn (var p Parser) struct_init(short_syntax bool) ast.StructInit { return node } -fn (var p Parser) interface_decl() ast.InterfaceDecl { +fn (mut p Parser) interface_decl() ast.InterfaceDecl { is_pub := p.tok.kind == .key_pub if is_pub { p.next() } - p.next() // `interface` + p.next() // `interface` interface_name := p.check_name() + //println('interface decl $interface_name') p.check(.lcbr) - var field_names := []string + // Declare the type + t := table.TypeSymbol{ + kind: .interface_ + name: interface_name + info: table.Struct{ + //is_interface: true + } + } + typ := p.table.register_type_symbol(t) + ts := p.table.get_type_symbol(typ) // TODO t vs ts + // Parse methods + mut methods := []ast.FnDecl for p.tok.kind != .rcbr && p.tok.kind != .eof { line_nr := p.tok.line_nr name := p.check_name() - field_names << name - p.fn_args() - if p.tok.kind == .name && p.tok.line_nr == line_nr { - p.parse_type() + println(name) + // field_names << name + args2, _ := p.fn_args() + mut args := [table.Arg{ + name: 'x' + typ: typ + }] + args << args2 + mut method := ast.FnDecl{ + name: name + args: args + return_type: table.void_type } + if p.tok.kind == .name && p.tok.line_nr == line_nr { + method.return_type = p.parse_type() + } + methods << method + //println('register method $name') + ts.register_method(table.Fn{ + name: name + args: args + return_type: method.return_type + }) } p.check(.rcbr) return ast.InterfaceDecl{ name: interface_name - field_names: field_names + methods: methods } } - diff --git a/vlib/v/table/atypes.v b/vlib/v/table/atypes.v index 6146ee18dc..5dad83db0c 100644 --- a/vlib/v/table/atypes.v +++ b/vlib/v/table/atypes.v @@ -16,7 +16,7 @@ import v.ast pub type Type int -pub type TypeInfo = Array | ArrayFixed | Map | Struct | MultiReturn | Alias | Enum | SumType | FnType +pub type TypeInfo = Array | ArrayFixed | Map | Struct | Interface | MultiReturn | Alias | Enum | SumType | FnType pub struct TypeSymbol { pub: @@ -160,10 +160,18 @@ pub const ( pub const ( integer_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i64_type_idx, byte_type_idx, - u16_type_idx, u32_type_idx, u64_type_idx] + u16_type_idx + u32_type_idx + u64_type_idx + ] float_type_idxs = [f32_type_idx, f64_type_idx] number_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i64_type_idx, byte_type_idx, - u16_type_idx, u32_type_idx, u64_type_idx, f32_type_idx, f64_type_idx] + u16_type_idx + u32_type_idx + u64_type_idx + f32_type_idx + f64_type_idx + ] pointer_type_idxs = [voidptr_type_idx, byteptr_type_idx, charptr_type_idx] string_type_idxs = [string_type_idx, ustring_type_idx] ) @@ -194,8 +202,10 @@ pub const ( pub const ( builtin_type_names = ['void', 'voidptr', 'charptr', 'byteptr', 'i8', 'i16', 'int', 'i64', - 'u16', 'u32', 'u64', 'f32', 'f64', 'string', 'ustring', 'char', 'byte', 'bool', 'none', 'array', 'array_fixed', - 'map', 'struct', 'mapnode', 'size_t'] + 'u16' + 'u32', 'u64', 'f32', 'f64', 'string', 'ustring', 'char', 'byte', 'bool', 'none', 'array', + 'array_fixed', 'map' + 'struct', 'mapnode', 'size_t'] ) pub struct MultiReturn { @@ -242,6 +252,7 @@ pub enum Kind { alias enum_ function + interface_ } pub fn (t &TypeSymbol) str() string { @@ -251,60 +262,40 @@ pub fn (t &TypeSymbol) str() string { [inline] pub fn (t &TypeSymbol) enum_info() Enum { match t.info { - Enum { - return it - } - else { - panic('TypeSymbol.enum_info(): no enum info for type: $t.name') - } + Enum { return it } + else { panic('TypeSymbol.enum_info(): no enum info for type: $t.name') } } } [inline] pub fn (t &TypeSymbol) mr_info() MultiReturn { match t.info { - MultiReturn { - return it - } - else { - panic('TypeSymbol.mr_info(): no multi return info for type: $t.name') - } + MultiReturn { return it } + else { panic('TypeSymbol.mr_info(): no multi return info for type: $t.name') } } } [inline] pub fn (t &TypeSymbol) array_info() Array { match t.info { - Array { - return it - } - else { - panic('TypeSymbol.array_info(): no array info for type: $t.name') - } + Array { return it } + else { panic('TypeSymbol.array_info(): no array info for type: $t.name') } } } [inline] pub fn (t &TypeSymbol) array_fixed_info() ArrayFixed { match t.info { - ArrayFixed { - return it - } - else { - panic('TypeSymbol.array_fixed(): no array fixed info for type: $t.name') - } + ArrayFixed { return it } + else { panic('TypeSymbol.array_fixed(): no array fixed info for type: $t.name') } } } [inline] pub fn (t &TypeSymbol) map_info() Map { match t.info { - Map { - return it - } - else { - panic('TypeSymbol.map_info(): no map info for type: $t.name') - } + Map { return it } + else { panic('TypeSymbol.map_info(): no map info for type: $t.name') } } } @@ -313,7 +304,7 @@ pub fn (t TypeSymbol) str() string { return t.name } */ -pub fn (var t Table) register_builtin_type_symbols() { +pub fn (mut t Table) register_builtin_type_symbols() { // reserve index 0 so nothing can go there // save index check, 0 will mean not found t.register_type_symbol(TypeSymbol{ @@ -445,99 +436,40 @@ pub fn (t &TypeSymbol) is_number() bool { pub fn (k Kind) str() string { k_str := match k { - .placeholder { - 'placeholder' - } - .void { - 'void' - } - .voidptr { - 'voidptr' - } - .charptr { - 'charptr' - } - .byteptr { - 'byteptr' - } - .struct_ { - 'struct' - } - .int { - 'int' - } - .i8 { - 'i8' - } - .i16 { - 'i16' - } - .i64 { - 'i64' - } - .byte { - 'byte' - } - .u16 { - 'u16' - } - .u32 { - 'u32' - } - .u64 { - 'u64' - } - .f32 { - 'f32' - } - .f64 { - 'f64' - } - .string { - 'string' - } - .ustring { - 'ustring' - } - .char { - 'char' - } - .bool { - 'bool' - } - .none_ { - 'none' - } - .array { - 'array' - } - .array_fixed { - 'array_fixed' - } - .map { - 'map' - } - .multi_return { - 'multi_return' - } - .sum_type { - 'sum_type' - } - .alias { - 'alias' - } - .enum_ { - 'enum' - } - else { - 'unknown' - } + .placeholder { 'placeholder' } + .void { 'void' } + .voidptr { 'voidptr' } + .charptr { 'charptr' } + .byteptr { 'byteptr' } + .struct_ { 'struct' } + .int { 'int' } + .i8 { 'i8' } + .i16 { 'i16' } + .i64 { 'i64' } + .byte { 'byte' } + .u16 { 'u16' } + .u32 { 'u32' } + .u64 { 'u64' } + .f32 { 'f32' } + .f64 { 'f64' } + .string { 'string' } + .char { 'char' } + .bool { 'bool' } + .none_ { 'none' } + .array { 'array' } + .array_fixed { 'array_fixed' } + .map { 'map' } + .multi_return { 'multi_return' } + .sum_type { 'sum_type' } + .alias { 'alias' } + .enum_ { 'enum' } + else { 'unknown' } } return k_str } pub fn (kinds []Kind) str() string { - var kinds_str := '' + mut kinds_str := '' for i, k in kinds { kinds_str += k.str() if i < kinds.len - 1 { @@ -554,6 +486,10 @@ pub mut: is_union bool } +pub struct Interface { + gen_types []string +} + pub struct Enum { pub: vals []string @@ -604,7 +540,7 @@ pub: pub fn (table &Table) type_to_str(t Type) string { sym := table.get_type_symbol(t) if sym.kind == .multi_return { - var res := '(' + mut res := '(' mr_info := sym.info as MultiReturn for i, typ in mr_info.types { res += table.type_to_str(typ) @@ -615,7 +551,7 @@ pub fn (table &Table) type_to_str(t Type) string { res += ')' return res } - var res := sym.name + mut res := sym.name if sym.kind == .array { res = res.replace('array_', '[]') } else if sym.kind == .map { diff --git a/vlib/v/tests/interface_test.v b/vlib/v/tests/interface_test.v index bbf1977c30..759786d50a 100644 --- a/vlib/v/tests/interface_test.v +++ b/vlib/v/tests/interface_test.v @@ -22,12 +22,40 @@ fn (d Dog) name() string { return 'Dog' } -fn test_todo() {} +fn test_todo() { + if true {} + // + else{} +} interface Speaker { - name ()string + name() string speak() +} + +/* +fn perform_speak(s Speaker) { + s.speak() + assert true + /* + name := s.name() + assert name == 'Dog' || name == 'Cat' + println(s.name()) +*/ +} + +fn test_perform_speak() { + dog := Dog{} + perform_speak(dog) + cat := Cat{} + perform_speak(cat) + // perform_speakers([dog, cat]) + /* + f := Foo { + speaker: dog } +*/ +} /* interface Speak2er { @@ -40,37 +68,8 @@ struct Foo { speakers []Speaker } -fn perform_speak(s Speaker) { - if true { - // QTODO - return - } - s.speak() - assert true - name := s.name() - assert name == 'Dog' || name == 'Cat' - println(s.name()) -} - fn perform_speakers(speakers []Speaker) {} -fn test_perform_speak() { - if true { - // QTODO - return - } - dog := Dog{} - perform_speak(dog) - cat := Cat{} - perform_speak(cat) - // perform_speakers([dog, cat]) - /* - f := Foo { - speaker: dog - } - */ - -} interface Register { register()} @@ -93,3 +92,4 @@ fn test_register() { handle_reg(f) } */ +*/