mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
parser/cgen: interfaces (part 1)
This commit is contained in:
parent
faed178cb1
commit
d7ee4755c2
@ -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{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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' {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user