interface: support arguments in methods and simplify

pull/4693/head
Enzo Baldisserri 2020-05-04 00:14:59 +02:00 committed by GitHub
parent 71a92eb87b
commit 4b3ce79e84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 183 additions and 171 deletions

View File

@ -103,14 +103,6 @@ const (
) )
pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string { pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string {
if true { // if
x := 10 // line
// sep
y := 20
_ = x
_ = y
} else {
}
// println('start cgen2') // println('start cgen2')
mut g := Gen{ mut g := Gen{
out: strings.new_builder(1000) out: strings.new_builder(1000)
@ -291,8 +283,8 @@ pub fn (mut g Gen) write_typeof_functions() {
} }
// V type to C type // V type to C type
pub fn (mut g Gen) typ(t table.Type) string { fn (mut g Gen) typ(t table.Type) string {
mut styp := g.base_typ(t) mut styp := g.base_type(t)
if t.flag_is(.optional) { if t.flag_is(.optional) {
// Register an optional // Register an optional
styp = 'Option_' + styp styp = 'Option_' + styp
@ -309,13 +301,20 @@ pub fn (mut g Gen) typ(t table.Type) string {
return styp return styp
} }
pub fn (mut g Gen) base_typ(t table.Type) string { fn (g &Gen) base_type(t table.Type) string {
mut styp := g.cc_type(t)
nr_muls := t.nr_muls() nr_muls := t.nr_muls()
sym := g.table.get_type_symbol(t)
mut styp := sym.name.replace('.', '__')
if nr_muls > 0 { if nr_muls > 0 {
styp += strings.repeat(`*`, nr_muls) styp += strings.repeat(`*`, nr_muls)
} }
return styp
}
// cc_type returns the Cleaned Concrete Type name, *without ptr*,
// i.e. it's always just Cat, not Cat_ptr:
fn (g &Gen) cc_type(t table.Type) string {
sym := g.table.get_type_symbol(t)
mut styp := sym.name.replace('.', '__')
if styp.starts_with('C__') { if styp.starts_with('C__') {
styp = styp[3..] styp = styp[3..]
if sym.kind == .struct_ { if sym.kind == .struct_ {
@ -330,6 +329,12 @@ pub fn (mut g Gen) base_typ(t table.Type) string {
// //
pub fn (mut g Gen) write_typedef_types() { pub fn (mut g Gen) write_typedef_types() {
g.typedefs.writeln('
typedef struct {
void* _object;
int _interface_idx;
} _Interface;
')
for typ in g.table.types { for typ in g.table.types {
match typ.kind { match typ.kind {
.alias { .alias {
@ -342,6 +347,9 @@ pub fn (mut g Gen) write_typedef_types() {
styp := typ.name.replace('.', '__') styp := typ.name.replace('.', '__')
g.definitions.writeln('typedef array $styp;') g.definitions.writeln('typedef array $styp;')
} }
.interface_ {
g.definitions.writeln('typedef _Interface ${c_name(typ.name)};')
}
.map { .map {
styp := typ.name.replace('.', '__') styp := typ.name.replace('.', '__')
g.definitions.writeln('typedef map $styp;') g.definitions.writeln('typedef map $styp;')
@ -842,7 +850,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
} }
g.expr(ident) g.expr(ident)
if is_optional { if is_optional {
mr_base_styp := g.base_typ(return_type) mr_base_styp := g.base_type(return_type)
g.writeln(' = (*(${mr_base_styp}*)${mr_var_name}.data).arg$i;') g.writeln(' = (*(${mr_base_styp}*)${mr_var_name}.data).arg$i;')
} else { } else {
g.writeln(' = ${mr_var_name}.arg$i;') g.writeln(' = ${mr_var_name}.arg$i;')
@ -1185,7 +1193,6 @@ fn (mut g Gen) expr(node ast.Expr) {
} }
ast.SelectorExpr { ast.SelectorExpr {
g.expr(it.expr) g.expr(it.expr)
// if it.expr_type.nr_muls() > 0 {
if it.expr_type.is_ptr() { if it.expr_type.is_ptr() {
g.write('->') g.write('->')
} else { } else {
@ -1650,7 +1657,7 @@ fn (mut g Gen) ident(node ast.Ident) {
// `println(x)` => `println(*(int*)x.data)` // `println(x)` => `println(*(int*)x.data)`
if ident_var.is_optional && !(g.is_assign_lhs && g.right_is_opt) { if ident_var.is_optional && !(g.is_assign_lhs && g.right_is_opt) {
g.write('/*opt*/') g.write('/*opt*/')
styp := g.base_typ(ident_var.typ) styp := g.base_type(ident_var.typ)
g.write('(*($styp*)${name}.data)') g.write('(*($styp*)${name}.data)')
return return
} }
@ -1908,7 +1915,7 @@ fn (mut g Gen) return_statement(node ast.Return) {
// mr_info := typ_sym.info as table.MultiReturn // mr_info := typ_sym.info as table.MultiReturn
mut styp := '' mut styp := ''
if fn_return_is_optional { // && !node.types[0].flag_is(.optional) && node.types[0] != if fn_return_is_optional { // && !node.types[0].flag_is(.optional) && node.types[0] !=
styp = g.base_typ(g.fn_decl.return_type) styp = g.base_type(g.fn_decl.return_type)
g.write('opt_ok(&($styp/*X*/[]) { ') g.write('opt_ok(&($styp/*X*/[]) { ')
} else { } else {
styp = g.typ(g.fn_decl.return_type) styp = g.typ(g.fn_decl.return_type)
@ -1947,7 +1954,7 @@ fn (mut g Gen) return_statement(node ast.Return) {
else {} else {}
} }
if !is_none && !is_error { if !is_none && !is_error {
styp := g.base_typ(g.fn_decl.return_type) styp := g.base_type(g.fn_decl.return_type)
g.write('/*:)$return_sym.name*/opt_ok(&($styp[]) { ') g.write('/*:)$return_sym.name*/opt_ok(&($styp[]) { ')
if !g.fn_decl.return_type.is_ptr() && node.types[0].is_ptr() { if !g.fn_decl.return_type.is_ptr() && node.types[0].is_ptr() {
// Automatic Dereference for optional // Automatic Dereference for optional
@ -2277,13 +2284,6 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) {
g.definitions.writeln('typedef $fixed $styp [$len];') g.definitions.writeln('typedef $fixed $styp [$len];')
// } // }
} }
table.Interface {
g.definitions.writeln('//interface')
g.definitions.writeln('typedef struct {')
g.definitions.writeln('\tvoid* _object;')
g.definitions.writeln('\tint _interface_idx;')
g.definitions.writeln('} $name;')
}
else {} else {}
} }
} }
@ -2608,7 +2608,7 @@ fn (mut g Gen) insert_before(s string) {
// to access its fields (`.ok`, `.error` etc) // to access its fields (`.ok`, `.error` etc)
// `os.cp(...)` => `Option bool tmp = os__cp(...); if (!tmp.ok) { ... }` // `os.cp(...)` => `Option bool tmp = os__cp(...); if (!tmp.ok) { ... }`
fn (mut g Gen) or_block(var_name string, stmts []ast.Stmt, return_type table.Type) { fn (mut g Gen) or_block(var_name string, stmts []ast.Stmt, return_type table.Type) {
mr_styp := g.base_typ(return_type) mr_styp := g.base_type(return_type)
g.writeln(';') // or') g.writeln(';') // or')
g.writeln('if (!${var_name}.ok) {') g.writeln('if (!${var_name}.ok) {')
g.writeln('\tstring err = ${var_name}.v_error;') g.writeln('\tstring err = ${var_name}.v_error;')
@ -3375,59 +3375,100 @@ fn (g Gen) type_to_fmt(typ table.Type) string {
} }
// Generates interface table and interface indexes // Generates interface table and interface indexes
// TODO remove all `replace()` fn (g &Gen) interface_table() string {
fn (v &Gen) interface_table() string {
mut sb := strings.new_builder(100) mut sb := strings.new_builder(100)
for _, t in v.table.types { for ityp in g.table.types {
if t.kind != .interface_ { if ityp.kind != .interface_ {
continue continue
} }
info := t.info as table.Interface inter_info := ityp.info as table.Interface
if inter_info.types.len == 0 {
continue
}
sb.writeln('// NR interfaced types= $inter_info.types.len')
// interface_name is for example Speaker // interface_name is for example Speaker
interface_name := t.name.replace('.', '__') interface_name := c_name(ityp.name)
mut methods := '' // generate a struct that references interface methods
mut generated_casting_functions := '' methods_struct_name := 'struct _${interface_name}_interface_methods'
sb.writeln('// NR gen_types= $info.gen_types.len') mut methods_typ_def := strings.new_builder(100)
for i, gen_type in info.gen_types { mut methods_struct_def := strings.new_builder(100)
// ptr_ctype can be for example Cat OR Cat_ptr: methods_struct_def.writeln('$methods_struct_name {')
ptr_ctype := gen_type.replace('*', '_ptr').replace('.', '__') for method in ityp.methods {
typ_name := '_${interface_name}_${method.name}_fn'
ret_styp := g.typ(method.return_type)
methods_typ_def.write('typedef $ret_styp (*$typ_name)(void* _')
// the first param is the receiver, it's handled by `void*` above
for i in 1 .. method.args.len {
arg := method.args[i]
methods_typ_def.write(', ${g.typ(arg.typ)} $arg.name')
}
// TODO g.fn_args(method.args[1..], method.is_variadic)
methods_typ_def.writeln(');')
methods_struct_def.writeln('\t$typ_name ${c_name(method.name)};')
}
methods_struct_def.writeln('};')
// generate an array of the interface methods for the structs using the interface
// as well as case functions from the struct to the interface
mut methods_struct := strings.new_builder(100)
methods_struct.writeln('$methods_struct_name ${interface_name}_name_table[$inter_info.types.len] = {')
mut cast_functions := strings.new_builder(100)
cast_functions.write('// Casting functions for interface "${interface_name}"')
mut methods_wrapper := strings.new_builder(100)
methods_wrapper.writeln('// Methods wrapper for interface "${interface_name}"')
for i, st in inter_info.types {
// cctype is the Cleaned Concrete Type name, *without ptr*, // cctype is the Cleaned Concrete Type name, *without ptr*,
// i.e. cctype is always just Cat, not Cat_ptr: // i.e. cctype is always just Cat, not Cat_ptr:
cctype := gen_type.replace('*', '').replace('.', '__') cctype := g.cc_type(st)
// Speaker_Cat_index = 0 // Speaker_Cat_index = 0
interface_index_name := '_${interface_name}_${ptr_ctype}_index' interface_index_name := '_${interface_name}_${cctype}_index'
generated_casting_functions += ' cast_functions.writeln('
${interface_name} I_${cctype}_to_${interface_name}(${cctype}* x) { _Interface I_${cctype}_to_Interface(${cctype}* x) {
return (${interface_name}){ return (_Interface) {
._object = (void*) memdup(x, sizeof(${cctype})), ._object = (void*) memdup(x, sizeof(${cctype})),
._interface_idx = ${interface_index_name} }; ._interface_idx = ${interface_index_name}
} };
' }')
methods += '{\n' methods_struct.writeln('\t{')
for j, method in t.methods { for method in ityp.methods {
// Cat_speak // .speak = Cat_speak
methods += ' (void*) ${cctype}_${method.name}' mut method_call := '${cctype}_${method.name}'
if j < t.methods.len - 1 { if !method.args[0].typ.is_ptr() {
methods += ', \n' // inline void Cat_speak_method_wrapper(Cat c) { return Cat_speak(*c); }
methods_wrapper.write('static inline ${g.typ(method.return_type)}')
methods_wrapper.write(' ${method_call}_method_wrapper(')
methods_wrapper.write('${cctype}* ${method.args[0].name}')
// TODO g.fn_args
for j in 1 .. method.args.len {
arg := method.args[j]
methods_wrapper.write(', ${g.typ(arg.typ)} $arg.name')
}
methods_wrapper.writeln(') {')
methods_wrapper.write('\t')
if method.return_type != table.void_type {
methods_wrapper.write('return ')
}
methods_wrapper.write('${method_call}(*${method.args[0].name}')
for j in 1 .. method.args.len {
methods_wrapper.write(', ${method.args[j].name}')
}
methods_wrapper.writeln(');')
methods_wrapper.writeln('}')
// .speak = Cat_speak_method_wrapper
method_call += '_method_wrapper'
} }
methods_struct.writeln('\t\t.${c_name(method.name)} = $method_call,')
} }
methods += '\n},\n\n' methods_struct.writeln('\t},')
sb.writeln('int ${interface_index_name} = $i;') sb.writeln('int ${interface_index_name} = $i;')
} }
if info.gen_types.len > 0 { methods_struct.writeln('};')
// methods = '{TCCSKIP(0)}' // add line return after interface index declarations
// } sb.writeln('')
sb.writeln('void* (* ${interface_name}_name_table[][$t.methods.len]) = ' + '{ \n $methods \n }; ') sb.writeln(methods_wrapper.str())
} else { sb.writeln(methods_typ_def.str())
// The line below is needed so that C compilation succeeds, sb.writeln(methods_struct_def.str())
// even if no interface methods are called. sb.writeln(methods_struct.str())
// See https://github.com/zenith391/vgtk3/issues/7 sb.writeln(cast_functions.str())
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() return sb.str()
} }
@ -3471,11 +3512,6 @@ fn (mut g Gen) array_init(it ast.ArrayInit) {
if it.is_interface { if it.is_interface {
// sym := g.table.get_type_symbol(it.interface_types[i]) // sym := g.table.get_type_symbol(it.interface_types[i])
// isym := g.table.get_type_symbol(it.interface_type) // isym := g.table.get_type_symbol(it.interface_type)
/*
interface_styp := g.typ(it.interface_type)
styp := g.typ(it.interface_types[i])
g.write('I_${styp}_to_${interface_styp}(')
*/
g.interface_call(it.interface_types[i], it.interface_type) g.interface_call(it.interface_types[i], it.interface_type)
} }
g.expr(expr) g.expr(expr)
@ -3490,9 +3526,9 @@ fn (mut g Gen) array_init(it ast.ArrayInit) {
// `ui.foo(button)` => // `ui.foo(button)` =>
// `ui__foo(I_ui__Button_to_ui__Widget(` ... // `ui__foo(I_ui__Button_to_ui__Widget(` ...
fn (g &Gen) interface_call(typ, interface_type table.Type) { fn (g &Gen) interface_call(typ, interface_type table.Type) {
interface_styp := g.typ(interface_type).replace('*', '') interface_styp := g.cc_type(interface_type)
styp := g.typ(typ).replace('*', '') styp := g.cc_type(typ)
g.write('I_${styp}_to_${interface_styp}(') g.write('/* $interface_styp */ I_${styp}_to_Interface(')
if !typ.is_ptr() { if !typ.is_ptr() {
g.write('&') g.write('&')
} }

View File

@ -50,8 +50,7 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
} }
} else { } else {
mut name := it.name mut name := it.name
c := name[0] if name[0] in [`+`, `-`, `*`, `/`, `%`] {
if c in [`+`, `-`, `*`, `/`, `%`] {
name = util.replace_op(name) name = util.replace_op(name)
} }
if it.is_method { if it.is_method {
@ -279,28 +278,20 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
if node.left_type == 0 { if node.left_type == 0 {
verror('method receiver type is 0, this means there are some uchecked exprs') verror('method receiver type is 0, this means there are some uchecked exprs')
} }
mut receiver_type_name := g.cc_type(node.receiver_type)
typ_sym := g.table.get_type_symbol(node.receiver_type) typ_sym := g.table.get_type_symbol(node.receiver_type)
// mut receiver_type_name := g.typ(node.receiver_type)
mut receiver_type_name := typ_sym.name.replace('.', '__') // TODO g.typ() ?
if typ_sym.kind == .interface_ { if typ_sym.kind == .interface_ {
// Find the index of the method // Speaker_name_table[s._interface_idx].speak(s._object)
mut idx := -1 g.write('${c_name(receiver_type_name)}_name_table[')
for i, method in typ_sym.methods {
if method.name == node.name {
idx = i
}
}
if idx == -1 {
verror('method_call: cannot find interface method index')
}
sret_type := g.typ(node.return_type)
g.writeln('// interface method call')
// `((void (*)())(Speaker_name_table[s._interface_idx][1]))(s._object);`
g.write('(($sret_type (*)())(${receiver_type_name}_name_table[')
g.expr(node.left) g.expr(node.left)
g.write('._interface_idx][$idx]))(') g.write('._interface_idx].${node.name}(')
g.expr(node.left) g.expr(node.left)
g.write('._object)') g.write('._object')
if node.args.len > 0 {
g.write(', ')
g.call_args(node.args, node.expected_arg_types)
}
g.write(')')
return return
} }
if typ_sym.kind == .array && node.name == 'map' { if typ_sym.kind == .array && node.name == 'map' {
@ -509,9 +500,8 @@ fn (mut g Gen) call_args(args []ast.CallArg, expected_types []table.Type) {
is_variadic := expected_types.len > 0 && expected_types[expected_types.len - 1].flag_is(.variadic) is_variadic := expected_types.len > 0 && expected_types[expected_types.len - 1].flag_is(.variadic)
is_forwarding_varg := args.len > 0 && args[args.len - 1].typ.flag_is(.variadic) is_forwarding_varg := args.len > 0 && args[args.len - 1].typ.flag_is(.variadic)
gen_vargs := is_variadic && !is_forwarding_varg gen_vargs := is_variadic && !is_forwarding_varg
mut arg_no := 0 for i, arg in args {
for arg in args { if gen_vargs && i == expected_types.len - 1 {
if gen_vargs && arg_no == expected_types.len - 1 {
break break
} }
// if arg.typ.name.starts_with('I') { // if arg.typ.name.starts_with('I') {
@ -519,42 +509,42 @@ fn (mut g Gen) call_args(args []ast.CallArg, expected_types []table.Type) {
mut is_interface := false mut is_interface := false
// some c fn definitions dont have args (cfns.v) or are not updated in checker // some c fn definitions dont have args (cfns.v) or are not updated in checker
// when these are fixed we wont need this check // when these are fixed we wont need this check
if arg_no < expected_types.len { if i < expected_types.len {
if expected_types[arg_no] != 0 { if expected_types[i] != 0 {
// Cast a type to interface // Cast a type to interface
// `foo(dog)` => `foo(I_Dog_to_Animal(dog))` // `foo(dog)` => `foo(I_Dog_to_Animal(dog))`
exp_sym := g.table.get_type_symbol(expected_types[arg_no]) exp_sym := g.table.get_type_symbol(expected_types[i])
// exp_styp := g.typ(expected_types[arg_no]) // g.table.get_type_symbol(expected_types[arg_no]) // exp_styp := g.typ(expected_types[arg_no]) // g.table.get_type_symbol(expected_types[arg_no])
// styp := g.typ(arg.typ) // g.table.get_type_symbol(arg.typ) // styp := g.typ(arg.typ) // g.table.get_type_symbol(arg.typ)
if exp_sym.kind == .interface_ { if exp_sym.kind == .interface_ {
g.interface_call(arg.typ, expected_types[arg_no]) g.interface_call(arg.typ, expected_types[i])
// g.write('/*Z*/I_${styp}_to_${exp_styp}(') // g.write('/*Z*/I_${styp}_to_${exp_styp}(')
is_interface = true is_interface = true
} }
} }
g.ref_or_deref_arg(arg, expected_types[arg_no]) g.ref_or_deref_arg(arg, expected_types[i])
} else { } else {
g.expr(arg.expr) g.expr(arg.expr)
} }
if is_interface { if is_interface {
g.write(')') g.write(')')
} }
if arg_no < args.len - 1 || gen_vargs { if i < args.len - 1 || gen_vargs {
g.write(', ') g.write(', ')
} }
arg_no++
} }
arg_nr := expected_types.len - 1
if gen_vargs { if gen_vargs {
varg_type := expected_types[expected_types.len - 1] varg_type := expected_types[expected_types.len - 1]
struct_name := 'varg_' + g.typ(varg_type).replace('*', '_ptr') struct_name := 'varg_' + g.typ(varg_type).replace('*', '_ptr')
variadic_count := args.len - arg_no variadic_count := args.len - arg_nr
varg_type_str := int(varg_type).str() varg_type_str := int(varg_type).str()
if variadic_count > g.variadic_args[varg_type_str] { if variadic_count > g.variadic_args[varg_type_str] {
g.variadic_args[varg_type_str] = variadic_count g.variadic_args[varg_type_str] = variadic_count
} }
g.write('($struct_name){.len=$variadic_count,.args={') g.write('($struct_name){.len=$variadic_count,.args={')
if variadic_count > 0 { if variadic_count > 0 {
for j in arg_no .. args.len { for j in arg_nr .. args.len {
g.ref_or_deref_arg(args[j], varg_type) g.ref_or_deref_arg(args[j], varg_type)
if j < args.len - 1 { if j < args.len - 1 {
g.write(', ') g.write(', ')

View File

@ -135,7 +135,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
fields << table.Field{ fields << table.Field{
name: field_name name: field_name
typ: typ typ: typ
default_expr: ast.ex2fe( default_expr ) default_expr: ast.ex2fe(default_expr)
has_default_expr: has_default_expr has_default_expr: has_default_expr
is_pub: is_field_pub is_pub: is_field_pub
is_mut: is_field_mut is_mut: is_field_mut
@ -265,8 +265,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
kind: .interface_ kind: .interface_
name: interface_name name: interface_name
info: table.Interface{ info: table.Interface{
gen_types: [] types: []
foo: 'foo'
} }
} }
typ := table.new_type(p.table.register_type_symbol(t)) typ := table.new_type(p.table.register_type_symbol(t))

View File

@ -180,23 +180,23 @@ pub const (
) )
pub const ( pub const (
integer_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i64_type_idx, byte_type_idx, integer_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i64_type_idx, byte_type_idx,
u16_type_idx, u16_type_idx,
u32_type_idx, u32_type_idx,
u64_type_idx u64_type_idx
] ]
signed_integer_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i64_type_idx] signed_integer_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i64_type_idx]
unsigned_integer_type_idxs = [byte_type_idx, u16_type_idx, u32_type_idx, u64_type_idx] unsigned_integer_type_idxs = [byte_type_idx, u16_type_idx, u32_type_idx, u64_type_idx]
float_type_idxs = [f32_type_idx, f64_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, number_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i64_type_idx, byte_type_idx,
u16_type_idx, u16_type_idx,
u32_type_idx, u32_type_idx,
u64_type_idx, u64_type_idx,
f32_type_idx, f32_type_idx,
f64_type_idx f64_type_idx
] ]
pointer_type_idxs = [voidptr_type_idx, byteptr_type_idx, charptr_type_idx] pointer_type_idxs = [voidptr_type_idx, byteptr_type_idx, charptr_type_idx]
string_type_idxs = [string_type_idx, ustring_type_idx] string_type_idxs = [string_type_idx, ustring_type_idx]
) )
pub const ( pub const (
@ -530,8 +530,7 @@ pub mut:
pub struct Interface { pub struct Interface {
mut: mut:
gen_types []string types []Type
foo string
} }
pub struct Enum { pub struct Enum {
@ -547,7 +546,7 @@ pub:
// NB: FExpr here is a actually an ast.Expr . // NB: FExpr here is a actually an ast.Expr .
// It should always be used by casting to ast.Expr, using ast.fe2ex()/ast.ex2fe() // It should always be used by casting to ast.Expr, using ast.fe2ex()/ast.ex2fe()
// That hack is needed to break an import cycle between v.ast and v.table . // That hack is needed to break an import cycle between v.ast and v.table .
type FExpr = voidptr | byteptr type FExpr = byteptr | voidptr
pub struct Field { pub struct Field {
pub: pub:
@ -634,6 +633,22 @@ pub fn (table &Table) type_to_str(t Type) string {
return res return res
} }
pub fn (t &TypeSymbol) has_method(name string) bool {
t.find_method(name) or {
return false
}
return true
}
pub fn (t &TypeSymbol) find_method(name string) ?Fn {
for method in t.methods {
if method.name == name {
return method
}
}
return none
}
pub fn (s Struct) find_field(name string) ?Field { pub fn (s Struct) find_field(name string) ?Field {
for field in s.fields { for field in s.fields {
if field.name == name { if field.name == name {

View File

@ -96,41 +96,6 @@ pub fn (mut t TypeSymbol) register_method(new_fn Fn) {
t.methods << new_fn t.methods << new_fn
} }
pub fn (t &TypeSymbol) has_method(name string) bool {
t.find_method(name) or {
return false
}
return true
}
pub fn (t &TypeSymbol) find_method(name string) ?Fn {
for method in t.methods {
if method.name == name {
return method
}
}
return none
}
pub fn (s &TypeSymbol) has_field(name string) bool {
s.find_field(name) or {
return false
}
return true
}
pub fn (s &TypeSymbol) find_field(name string) ?Field {
match s.info {
Struct { for field in it.fields {
if field.name == name {
return field
}
} }
else {}
}
return none
}
pub fn (t &Table) type_has_method(s &TypeSymbol, name string) bool { pub fn (t &Table) type_has_method(s &TypeSymbol, name string) bool {
// println('type_has_method($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx') // println('type_has_method($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx')
if _ := t.type_find_method(s, name) { if _ := t.type_find_method(s, name) {
@ -168,8 +133,11 @@ pub fn (t &Table) struct_find_field(s &TypeSymbol, name string) ?Field {
// println('struct_find_field($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx') // println('struct_find_field($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx')
mut ts := s mut ts := s
for { for {
if field := ts.find_field(name) { if ts.info is Struct {
return field struct_info := ts.info as Struct
if field := struct_info.find_field(name) {
return field
}
} }
if ts.parent_idx == 0 { if ts.parent_idx == 0 {
break break
@ -179,6 +147,15 @@ pub fn (t &Table) struct_find_field(s &TypeSymbol, name string) ?Field {
return none return none
} }
pub fn (t &Table) interface_add_type(inter mut Interface, typ Type) bool {
// TODO Verify `typ` implements `inter`
typ_sym := t.get_type_symbol(typ)
if typ !in inter.types && typ_sym.kind != .interface_ {
inter.types << typ
}
return true
}
[inline] [inline]
pub fn (t &Table) find_type_idx(name string) int { pub fn (t &Table) find_type_idx(name string) int {
return t.type_idxs[name] return t.type_idxs[name]
@ -491,15 +468,7 @@ pub fn (t &Table) check(got, expected Type) bool {
// Handle expected interface // Handle expected interface
if exp_type_sym.kind == .interface_ { if exp_type_sym.kind == .interface_ {
mut info := exp_type_sym.info as Interface mut info := exp_type_sym.info as Interface
// println('gen_types before') return t.interface_add_type(info, got)
// println(info.gen_types)
if got_type_sym.name !in info.gen_types && got_type_sym.kind != .interface_ {
// TODO `got` should never be an interface?
info.gen_types << got_type_sym.name
}
// println('adding gen_type $got_type_sym.name')
// println(info.gen_types)
return true
} }
// Handle expected interface array // Handle expected interface array
/* /*

View File

@ -11,15 +11,18 @@ fn (d Cat) name() string {
return 'Cat' return 'Cat'
} }
fn (d Cat) speak() { fn (d Cat) speak(s string) {
assert s == 'Hi !'
println('meow') println('meow')
} }
fn (d Dog) speak() { fn (d Dog) speak(s string) {
assert s == 'Hi !'
println('woof') println('woof')
} }
fn (d Dog) name() string { fn (d Dog) name() string {
assert d.breed == 'Labrador Retriever'
return 'Dog' return 'Dog'
} }
@ -31,7 +34,7 @@ fn test_todo() {
fn perform_speak(s Speaker) { fn perform_speak(s Speaker) {
s.speak() s.speak('Hi !')
assert true assert true
name := s.name() name := s.name()
assert name == 'Dog' || name == 'Cat' assert name == 'Dog' || name == 'Cat'
@ -42,7 +45,7 @@ fn perform_speak(s Speaker) {
} }
fn test_perform_speak() { fn test_perform_speak() {
dog := Dog{} dog := Dog{breed: 'Labrador Retriever'}
perform_speak(dog) perform_speak(dog)
cat := Cat{} cat := Cat{}
perform_speak(cat) perform_speak(cat)
@ -88,7 +91,7 @@ struct Foo {
interface Speaker { interface Speaker {
name() string name() string
speak() speak(s string)
} }