1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

interface: support arguments in methods and simplify

This commit is contained in:
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 {
if true { // if
x := 10 // line
// sep
y := 20
_ = x
_ = y
} else {
}
// println('start cgen2')
mut g := Gen{
out: strings.new_builder(1000)
@ -291,8 +283,8 @@ pub fn (mut g Gen) write_typeof_functions() {
}
// V type to C type
pub fn (mut g Gen) typ(t table.Type) string {
mut styp := g.base_typ(t)
fn (mut g Gen) typ(t table.Type) string {
mut styp := g.base_type(t)
if t.flag_is(.optional) {
// Register an optional
styp = 'Option_' + styp
@ -309,13 +301,20 @@ pub fn (mut g Gen) typ(t table.Type) string {
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()
sym := g.table.get_type_symbol(t)
mut styp := sym.name.replace('.', '__')
if nr_muls > 0 {
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__') {
styp = styp[3..]
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() {
g.typedefs.writeln('
typedef struct {
void* _object;
int _interface_idx;
} _Interface;
')
for typ in g.table.types {
match typ.kind {
.alias {
@ -342,6 +347,9 @@ pub fn (mut g Gen) write_typedef_types() {
styp := typ.name.replace('.', '__')
g.definitions.writeln('typedef array $styp;')
}
.interface_ {
g.definitions.writeln('typedef _Interface ${c_name(typ.name)};')
}
.map {
styp := typ.name.replace('.', '__')
g.definitions.writeln('typedef map $styp;')
@ -842,7 +850,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
}
g.expr(ident)
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;')
} else {
g.writeln(' = ${mr_var_name}.arg$i;')
@ -1185,7 +1193,6 @@ fn (mut g Gen) expr(node ast.Expr) {
}
ast.SelectorExpr {
g.expr(it.expr)
// if it.expr_type.nr_muls() > 0 {
if it.expr_type.is_ptr() {
g.write('->')
} else {
@ -1650,7 +1657,7 @@ fn (mut g Gen) ident(node ast.Ident) {
// `println(x)` => `println(*(int*)x.data)`
if ident_var.is_optional && !(g.is_assign_lhs && g.right_is_opt) {
g.write('/*opt*/')
styp := g.base_typ(ident_var.typ)
styp := g.base_type(ident_var.typ)
g.write('(*($styp*)${name}.data)')
return
}
@ -1908,7 +1915,7 @@ fn (mut g Gen) return_statement(node ast.Return) {
// mr_info := typ_sym.info as table.MultiReturn
mut styp := ''
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*/[]) { ')
} else {
styp = g.typ(g.fn_decl.return_type)
@ -1947,7 +1954,7 @@ fn (mut g Gen) return_statement(node ast.Return) {
else {}
}
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[]) { ')
if !g.fn_decl.return_type.is_ptr() && node.types[0].is_ptr() {
// Automatic Dereference for optional
@ -2277,13 +2284,6 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) {
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 {}
}
}
@ -2608,7 +2608,7 @@ fn (mut g Gen) insert_before(s string) {
// to access its fields (`.ok`, `.error` etc)
// `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) {
mr_styp := g.base_typ(return_type)
mr_styp := g.base_type(return_type)
g.writeln(';') // or')
g.writeln('if (!${var_name}.ok) {')
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
// TODO remove all `replace()`
fn (v &Gen) interface_table() string {
fn (g &Gen) interface_table() string {
mut sb := strings.new_builder(100)
for _, t in v.table.types {
if t.kind != .interface_ {
for ityp in g.table.types {
if ityp.kind != .interface_ {
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 := t.name.replace('.', '__')
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').replace('.', '__')
interface_name := c_name(ityp.name)
// generate a struct that references interface methods
methods_struct_name := 'struct _${interface_name}_interface_methods'
mut methods_typ_def := strings.new_builder(100)
mut methods_struct_def := strings.new_builder(100)
methods_struct_def.writeln('$methods_struct_name {')
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*,
// i.e. cctype is always just Cat, not Cat_ptr:
cctype := gen_type.replace('*', '').replace('.', '__')
cctype := g.cc_type(st)
// 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'
interface_index_name := '_${interface_name}_${cctype}_index'
cast_functions.writeln('
_Interface I_${cctype}_to_Interface(${cctype}* x) {
return (_Interface) {
._object = (void*) memdup(x, sizeof(${cctype})),
._interface_idx = ${interface_index_name}
};
}')
methods_struct.writeln('\t{')
for method in ityp.methods {
// .speak = Cat_speak
mut method_call := '${cctype}_${method.name}'
if !method.args[0].typ.is_ptr() {
// 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;')
}
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)
}
methods_struct.writeln('};')
// add line return after interface index declarations
sb.writeln('')
sb.writeln(methods_wrapper.str())
sb.writeln(methods_typ_def.str())
sb.writeln(methods_struct_def.str())
sb.writeln(methods_struct.str())
sb.writeln(cast_functions.str())
}
return sb.str()
}
@ -3471,11 +3512,6 @@ fn (mut g Gen) array_init(it ast.ArrayInit) {
if it.is_interface {
// sym := g.table.get_type_symbol(it.interface_types[i])
// 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.expr(expr)
@ -3490,9 +3526,9 @@ fn (mut g Gen) array_init(it ast.ArrayInit) {
// `ui.foo(button)` =>
// `ui__foo(I_ui__Button_to_ui__Widget(` ...
fn (g &Gen) interface_call(typ, interface_type table.Type) {
interface_styp := g.typ(interface_type).replace('*', '')
styp := g.typ(typ).replace('*', '')
g.write('I_${styp}_to_${interface_styp}(')
interface_styp := g.cc_type(interface_type)
styp := g.cc_type(typ)
g.write('/* $interface_styp */ I_${styp}_to_Interface(')
if !typ.is_ptr() {
g.write('&')
}

View File

@ -50,8 +50,7 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) {
}
} else {
mut name := it.name
c := name[0]
if c in [`+`, `-`, `*`, `/`, `%`] {
if name[0] in [`+`, `-`, `*`, `/`, `%`] {
name = util.replace_op(name)
}
if it.is_method {
@ -279,28 +278,20 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
if node.left_type == 0 {
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)
// 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_ {
// Find the index of the method
mut idx := -1
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[')
// Speaker_name_table[s._interface_idx].speak(s._object)
g.write('${c_name(receiver_type_name)}_name_table[')
g.expr(node.left)
g.write('._interface_idx][$idx]))(')
g.write('._interface_idx].${node.name}(')
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
}
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_forwarding_varg := args.len > 0 && args[args.len - 1].typ.flag_is(.variadic)
gen_vargs := is_variadic && !is_forwarding_varg
mut arg_no := 0
for arg in args {
if gen_vargs && arg_no == expected_types.len - 1 {
for i, arg in args {
if gen_vargs && i == expected_types.len - 1 {
break
}
// 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
// some c fn definitions dont have args (cfns.v) or are not updated in checker
// when these are fixed we wont need this check
if arg_no < expected_types.len {
if expected_types[arg_no] != 0 {
if i < expected_types.len {
if expected_types[i] != 0 {
// Cast a type to interface
// `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])
// styp := g.typ(arg.typ) // g.table.get_type_symbol(arg.typ)
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}(')
is_interface = true
}
}
g.ref_or_deref_arg(arg, expected_types[arg_no])
g.ref_or_deref_arg(arg, expected_types[i])
} else {
g.expr(arg.expr)
}
if is_interface {
g.write(')')
}
if arg_no < args.len - 1 || gen_vargs {
if i < args.len - 1 || gen_vargs {
g.write(', ')
}
arg_no++
}
arg_nr := expected_types.len - 1
if gen_vargs {
varg_type := expected_types[expected_types.len - 1]
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()
if variadic_count > g.variadic_args[varg_type_str] {
g.variadic_args[varg_type_str] = variadic_count
}
g.write('($struct_name){.len=$variadic_count,.args={')
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)
if j < args.len - 1 {
g.write(', ')

View File

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

View File

@ -180,23 +180,23 @@ 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,
u32_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]
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,
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
]
pointer_type_idxs = [voidptr_type_idx, byteptr_type_idx, charptr_type_idx]
string_type_idxs = [string_type_idx, ustring_type_idx]
pointer_type_idxs = [voidptr_type_idx, byteptr_type_idx, charptr_type_idx]
string_type_idxs = [string_type_idx, ustring_type_idx]
)
pub const (
@ -530,8 +530,7 @@ pub mut:
pub struct Interface {
mut:
gen_types []string
foo string
types []Type
}
pub struct Enum {
@ -547,7 +546,7 @@ pub:
// NB: FExpr here is a actually an ast.Expr .
// 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 .
type FExpr = voidptr | byteptr
type FExpr = byteptr | voidptr
pub struct Field {
pub:
@ -634,6 +633,22 @@ pub fn (table &Table) type_to_str(t Type) string {
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 {
for field in s.fields {
if field.name == name {

View File

@ -96,41 +96,6 @@ pub fn (mut t TypeSymbol) register_method(new_fn 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 {
// 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) {
@ -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')
mut ts := s
for {
if field := ts.find_field(name) {
return field
if ts.info is Struct {
struct_info := ts.info as Struct
if field := struct_info.find_field(name) {
return field
}
}
if ts.parent_idx == 0 {
break
@ -179,6 +147,15 @@ pub fn (t &Table) struct_find_field(s &TypeSymbol, name string) ?Field {
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]
pub fn (t &Table) find_type_idx(name string) int {
return t.type_idxs[name]
@ -491,15 +468,7 @@ pub fn (t &Table) check(got, expected Type) bool {
// Handle expected interface
if exp_type_sym.kind == .interface_ {
mut info := exp_type_sym.info as Interface
// println('gen_types before')
// 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
return t.interface_add_type(info, got)
}
// Handle expected interface array
/*

View File

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