From 3440d7edd8397a691500a61995b4007af395e18b Mon Sep 17 00:00:00 2001 From: joe-conigliaro Date: Mon, 30 Mar 2020 21:39:20 +1100 Subject: [PATCH] ast: first step merging CallExpr & MethodCallExpr --- vlib/v/ast/ast.v | 38 ++--- vlib/v/checker/checker.v | 322 +++++++++++++++++++-------------------- vlib/v/fmt/fmt.v | 24 +-- vlib/v/gen/cgen.v | 274 ++++++++++++++++----------------- vlib/v/parser/parser.v | 5 +- 5 files changed, 324 insertions(+), 339 deletions(-) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 563eec7330..1e34575fba 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -10,15 +10,15 @@ import ( pub type TypeDecl = AliasTypeDecl | SumTypeDecl | FnTypeDecl -pub type Expr = InfixExpr | IfExpr | StringLiteral | IntegerLiteral | CharLiteral | -FloatLiteral | Ident | CallExpr | BoolLiteral | StructInit | ArrayInit | SelectorExpr | PostfixExpr | -AssignExpr | PrefixExpr | MethodCallExpr | IndexExpr | RangeExpr | MatchExpr | -CastExpr | EnumVal | Assoc | SizeOf | None | MapInit | IfGuardExpr | ParExpr | OrExpr | -ConcatExpr | Type | AsCast | TypeOf | StringInterLiteral +pub type Expr = InfixExpr | IfExpr | StringLiteral | IntegerLiteral | CharLiteral | +FloatLiteral | Ident | CallExpr | BoolLiteral | StructInit | ArrayInit | SelectorExpr | +PostfixExpr | AssignExpr | PrefixExpr | IndexExpr | RangeExpr | MatchExpr | CastExpr | +EnumVal | Assoc | SizeOf | None | MapInit | IfGuardExpr | ParExpr | OrExpr | ConcatExpr | +Type | AsCast | TypeOf | StringInterLiteral -pub type Stmt = GlobalDecl | FnDecl | Return | Module | Import | ExprStmt | -ForStmt | StructDecl | ForCStmt | ForInStmt | CompIf | ConstDecl | Attr | BranchStmt | -HashStmt | AssignStmt | EnumDecl | TypeDecl | DeferStmt | GotoLabel | GotoStmt | +pub type Stmt = GlobalDecl | FnDecl | Return | Module | Import | ExprStmt | +ForStmt | StructDecl | ForCStmt | ForInStmt | CompIf | ConstDecl | Attr | BranchStmt | +HashStmt | AssignStmt | EnumDecl | TypeDecl | DeferStmt | GotoLabel | GotoStmt | LineComment | MultiLineComment | AssertStmt | UnsafeStmt | GoStmt | Block // pub type Type = StructType | ArrayType // pub struct StructType { @@ -173,29 +173,16 @@ pub struct CallExpr { pub: // tok token.Token pos token.Position + left Expr // `user` in `user.register()` + is_method bool mut: -// func Expr name string args []CallArg exp_arg_types []table.Type is_c bool - muts []bool or_block OrExpr // has_or_block bool - return_type table.Type -} - -pub struct MethodCallExpr { -pub: -// tok token.Token - pos token.Position - expr Expr // `user` in `user.register()` - name string - args []CallArg - or_block OrExpr -mut: - exp_arg_types []table.Type - expr_type table.Type // type of `user` + left_type table.Type // type of `user` receiver_type table.Type // User return_type table.Type } @@ -711,9 +698,6 @@ pub fn expr_is_call(expr Expr) bool { CallExpr{ true } - MethodCallExpr{ - true - } else { false} } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 177a8ad44b..ea496e3381 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -168,186 +168,185 @@ fn (c mut Checker) assign_expr(assign_expr mut ast.AssignExpr) { } pub fn (c mut Checker) call_expr(call_expr mut ast.CallExpr) table.Type { - fn_name := call_expr.name c.stmts(call_expr.or_block.stmts) - // TODO: impl typeof properly (probably not going to be a fn call) - if fn_name == 'typeof' { - return table.string_type - } - // start hack: until v1 is fixed and c definitions are added for these - if fn_name in ['C.calloc', 'C.malloc', 'C.exit', 'C.free'] { - for arg in call_expr.args { - c.expr(arg.expr) + if call_expr.is_method { + left_type := c.expr(call_expr.left) + call_expr.left_type = left_type + left_type_sym := c.table.get_type_symbol(left_type) + method_name := call_expr.name + // TODO: remove this for actual methods, use only for compiler magic + if left_type_sym.kind == .array && method_name in ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice'] { + if method_name in ['filter', 'map'] { + array_info := left_type_sym.info as table.Array + mut scope := c.file.scope.innermost(call_expr.pos.pos) + scope.update_var_type('it', array_info.elem_type) + } + for i, arg in call_expr.args { + c.expr(arg.expr) + } + // need to return `array_xxx` instead of `array` + call_expr.return_type = left_type + if method_name == 'clone' { + // in ['clone', 'str'] { + call_expr.receiver_type = table.type_to_ptr(left_type) + // call_expr.return_type = call_expr.receiver_type + } + else { + call_expr.receiver_type = left_type + } + return left_type } - if fn_name in ['C.calloc', 'C.malloc'] { - return table.byteptr_type + else if left_type_sym.kind == .array && method_name in ['first', 'last'] { + info := left_type_sym.info as table.Array + call_expr.return_type = info.elem_type + call_expr.receiver_type = left_type + return info.elem_type } + if method := c.table.type_find_method(left_type_sym, method_name) { + no_args := 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)', call_expr.pos) + } + // if method_name == 'clone' { + // println('CLONE nr args=$method.args.len') + // } + // call_expr.args << method.args[0].typ + // call_expr.exp_arg_types << method.args[0].typ + for i, arg in call_expr.args { + c.expected_type = if method.is_variadic && i >= method.args.len - 1 { method.args[method.args.len - 1].typ } else { method.args[i + 1].typ } + call_expr.args[i].typ = c.expr(arg.expr) + } + // TODO: typ optimize.. this node can get processed more than once + if call_expr.exp_arg_types.len == 0 { + for i in 1 .. method.args.len { + call_expr.exp_arg_types << method.args[i].typ + } + } + call_expr.receiver_type = method.args[0].typ + call_expr.return_type = method.return_type + return method.return_type + } + // TODO: str methods + if left_type_sym.kind == .map && method_name == 'str' { + call_expr.receiver_type = table.new_type(c.table.type_idxs['map_string']) + call_expr.return_type = table.string_type + return table.string_type + } + if left_type_sym.kind == .array && method_name == 'str' { + call_expr.receiver_type = left_type + call_expr.return_type = table.string_type + return table.string_type + } + c.error('unknown method: ${left_type_sym.name}.$method_name', call_expr.pos) return table.void_type } - // end hack - // look for function in format `mod.fn` or `fn` (main/builtin) - mut f := table.Fn{} - mut found := false - // try prefix with current module as it would have never gotten prefixed - if !fn_name.contains('.') && !(c.file.mod.name in ['builtin', 'main']) { - name_prefixed := '${c.file.mod.name}.$fn_name' - if f1 := c.table.find_fn(name_prefixed) { - call_expr.name = name_prefixed - found = true - f = f1 + else { + fn_name := call_expr.name + // TODO: impl typeof properly (probably not going to be a fn call) + if fn_name == 'typeof' { + return table.string_type } - } - // already prefixed (mod.fn) or C/builtin/main - if !found { - if f1 := c.table.find_fn(fn_name) { - found = true - f = f1 + // start hack: until v1 is fixed and c definitions are added for these + if fn_name in ['C.calloc', 'C.malloc', 'C.exit', 'C.free'] { + for arg in call_expr.args { + c.expr(arg.expr) + } + if fn_name in ['C.calloc', 'C.malloc'] { + return table.byteptr_type + } + return table.void_type } - } - // check for arg (var) of fn type - if !found { - scope := c.file.scope.innermost(call_expr.pos.pos) - if var := scope.find_var(fn_name) { - if var.typ != 0 { - vts := c.table.get_type_symbol(var.typ) - if vts.kind == .function { - info := vts.info as table.FnType - f = info.func - found = true + // end hack + // look for function in format `mod.fn` or `fn` (main/builtin) + mut f := table.Fn{} + mut found := false + // try prefix with current module as it would have never gotten prefixed + if !fn_name.contains('.') && !(c.file.mod.name in ['builtin', 'main']) { + name_prefixed := '${c.file.mod.name}.$fn_name' + if f1 := c.table.find_fn(name_prefixed) { + call_expr.name = name_prefixed + found = true + f = f1 + } + } + // already prefixed (mod.fn) or C/builtin/main + if !found { + if f1 := c.table.find_fn(fn_name) { + found = true + f = f1 + } + } + // check for arg (var) of fn type + if !found { + scope := c.file.scope.innermost(call_expr.pos.pos) + if var := scope.find_var(fn_name) { + if var.typ != 0 { + vts := c.table.get_type_symbol(var.typ) + if vts.kind == .function { + info := vts.info as table.FnType + f = info.func + found = true + } } } } - } - if !found { - c.error('unknown fn: $fn_name', call_expr.pos) - return table.void_type - } - call_expr.return_type = f.return_type - if f.is_c || call_expr.is_c { - for arg in call_expr.args { - c.expr(arg.expr) + if !found { + c.error('unknown fn: $fn_name', call_expr.pos) + return table.void_type } - return f.return_type - } - min_required_args := if f.is_variadic { f.args.len - 1 } else { f.args.len } - if call_expr.args.len < min_required_args { - c.error('too few arguments in call to `$fn_name` ($call_expr.args.len instead of $min_required_args)', call_expr.pos) - } - else if !f.is_variadic && call_expr.args.len > f.args.len { - c.error('too many arguments in call to `$fn_name` ($call_expr.args.len instead of $f.args.len)', call_expr.pos) - } - // println can print anything - if fn_name == 'println' { - c.expected_type = table.string_type - call_expr.args[0].typ = c.expr(call_expr.args[0].expr) - return f.return_type - } - // TODO: typ optimize.. this node can get processed more than once - if call_expr.exp_arg_types.len == 0 { - for arg in f.args { - call_expr.exp_arg_types << arg.typ - } - } - for i, call_arg in call_expr.args { - arg := if f.is_variadic && i >= f.args.len - 1 { f.args[f.args.len - 1] } else { f.args[i] } - c.expected_type = arg.typ - typ := c.expr(call_arg.expr) - call_expr.args[i].typ = typ - typ_sym := c.table.get_type_symbol(typ) - arg_typ_sym := c.table.get_type_symbol(arg.typ) - if !c.table.check(typ, arg.typ) { - // str method, allow type with str method if fn arg is string - if arg_typ_sym.kind == .string && typ_sym.has_method('str') { - continue + call_expr.return_type = f.return_type + if f.is_c || call_expr.is_c { + for arg in call_expr.args { + c.expr(arg.expr) } - // TODO const bug - if typ_sym.kind == .void && arg_typ_sym.kind == .string { - continue - } - if typ_sym.kind == .array_fixed {} - // println('fixed') - c.error('!cannot use type `$typ_sym.str()` as type `$arg_typ_sym.str()` in argument ${i+1} to `$fn_name`', call_expr.pos) + return f.return_type } - } - return f.return_type -} - -// TODO: clean this up, remove dupe code & consider merging method/fn call everywhere -pub fn (c mut Checker) method_call_expr(method_call_expr mut ast.MethodCallExpr) table.Type { - c.expected_type = table.void_type - typ := c.expr(method_call_expr.expr) - method_call_expr.expr_type = typ - typ_sym := c.table.get_type_symbol(typ) - name := method_call_expr.name - c.stmts(method_call_expr.or_block.stmts) - // println('method call $name $method_call_expr.pos.line_nr') - // TODO: remove this for actual methods, use only for compiler magic - if typ_sym.kind == .array && name in ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice'] { - if name in ['filter', 'map'] { - array_info := typ_sym.info as table.Array - mut scope := c.file.scope.innermost(method_call_expr.pos.pos) - scope.update_var_type('it', array_info.elem_type) + min_required_args := if f.is_variadic { f.args.len - 1 } else { f.args.len } + if call_expr.args.len < min_required_args { + c.error('too few arguments in call to `$fn_name` ($call_expr.args.len instead of $min_required_args)', call_expr.pos) } - for i, arg in method_call_expr.args { - c.expr(arg.expr) + else if !f.is_variadic && call_expr.args.len > f.args.len { + c.error('too many arguments in call to `$fn_name` ($call_expr.args.len instead of $f.args.len)', call_expr.pos) } - // need to return `array_xxx` instead of `array` - method_call_expr.return_type = typ - if name == 'clone' { - // in ['clone', 'str'] { - method_call_expr.receiver_type = table.type_to_ptr(typ) - // method_call_expr.return_type = method_call_expr.receiver_type - } - else { - method_call_expr.receiver_type = typ - } - return typ - } - else if typ_sym.kind == .array && name in ['first', 'last'] { - info := typ_sym.info as table.Array - method_call_expr.return_type = info.elem_type - method_call_expr.receiver_type = typ - return info.elem_type - } - if method := c.table.type_find_method(typ_sym, name) { - no_args := method.args.len - 1 - min_required_args := method.args.len - if method.is_variadic && method.args.len > 1 { 2 } else { 1 } - if method_call_expr.args.len < min_required_args { - c.error('too few arguments in call to `${typ_sym.name}.$name` ($method_call_expr.args.len instead of $min_required_args)', method_call_expr.pos) - } - else if !method.is_variadic && method_call_expr.args.len > no_args { - c.error('too many arguments in call to `${typ_sym.name}.$name` ($method_call_expr.args.len instead of $no_args)', method_call_expr.pos) - } - // if name == 'clone' { - // println('CLONE nr args=$method.args.len') - // } - for i, arg in method_call_expr.args { - c.expected_type = if method.is_variadic && i >= method.args.len - 1 { method.args[method.args.len - 1].typ } else { method.args[i + 1].typ } - method_call_expr.args[i].typ = c.expr(arg.expr) + // println can print anything + if fn_name == 'println' { + c.expected_type = table.string_type + call_expr.args[0].typ = c.expr(call_expr.args[0].expr) + return f.return_type } // TODO: typ optimize.. this node can get processed more than once - if method_call_expr.exp_arg_types.len == 0 { - for i in 1 .. method.args.len { - method_call_expr.exp_arg_types << method.args[i].typ + if call_expr.exp_arg_types.len == 0 { + for arg in f.args { + call_expr.exp_arg_types << arg.typ } } - method_call_expr.receiver_type = method.args[0].typ - method_call_expr.return_type = method.return_type - return method.return_type + for i, call_arg in call_expr.args { + arg := if f.is_variadic && i >= f.args.len - 1 { f.args[f.args.len - 1] } else { f.args[i] } + c.expected_type = arg.typ + typ := c.expr(call_arg.expr) + call_expr.args[i].typ = typ + typ_sym := c.table.get_type_symbol(typ) + arg_typ_sym := c.table.get_type_symbol(arg.typ) + if !c.table.check(typ, arg.typ) { + // str method, allow type with str method if fn arg is string + if arg_typ_sym.kind == .string && typ_sym.has_method('str') { + continue + } + // TODO const bug + if typ_sym.kind == .void && arg_typ_sym.kind == .string { + continue + } + if typ_sym.kind == .array_fixed {} + // println('fixed') + c.error('!cannot use type `$typ_sym.str()` as type `$arg_typ_sym.str()` in argument ${i+1} to `$fn_name`', call_expr.pos) + } + } + return f.return_type } - // TODO: str methods - if typ_sym.kind == .map && name == 'str' { - method_call_expr.receiver_type = table.new_type(c.table.type_idxs['map_string']) - method_call_expr.return_type = table.string_type - return table.string_type - } - if typ_sym.kind == .array && name == 'str' { - method_call_expr.receiver_type = typ - method_call_expr.return_type = table.string_type - return table.string_type - } - c.error('type `$typ_sym.name` has no method `$name`', method_call_expr.pos) - return table.void_type } pub fn (c mut Checker) selector_expr(selector_expr mut ast.SelectorExpr) table.Type { @@ -731,9 +730,6 @@ pub fn (c mut Checker) expr(node ast.Expr) table.Type { ast.MatchExpr { return c.match_expr(mut it) } - ast.MethodCallExpr { - return c.method_call_expr(mut it) - } ast.PostfixExpr { return c.postfix_expr(it) } diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 0b4a65f1f7..5876221b7a 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -388,10 +388,19 @@ fn (f mut Fmt) expr(node ast.Expr) { f.write(')') } ast.CallExpr { - f.write('${it.name}(') - f.call_args(it.args) - f.write(')') - f.or_expr(it.or_block) + if it.is_method { + f.expr(it.left) + f.write('.' + it.name + '(') + f.call_args(it.args) + f.write(')') + f.or_expr(it.or_block) + } + else { + f.write('${it.name}(') + f.call_args(it.args) + f.write(')') + f.or_expr(it.or_block) + } } ast.CharLiteral { f.write('`$it.val`') @@ -510,13 +519,6 @@ fn (f mut Fmt) expr(node ast.Expr) { f.indent-- f.write('}') } - ast.MethodCallExpr { - f.expr(it.expr) - f.write('.' + it.name + '(') - f.call_args(it.args) - f.write(')') - f.or_expr(it.or_block) - } ast.None { f.write('none') } diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 89cedc1501..e8f93a3822 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -578,9 +578,6 @@ fn (g mut Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { ast.CallExpr { return_type = it.return_type } - ast.MethodCallExpr { - return_type = it.return_type - } else { panic('expected call') } @@ -1041,70 +1038,6 @@ fn (g mut Gen) expr(node ast.Expr) { g.write('new_map(1, sizeof($value_typ_str))') } } - ast.MethodCallExpr { - // TODO: there are still due to unchecked exprs (opt/some fn arg) - if it.expr_type == 0 { - verror('method receiver type is 0, this means there are some uchecked exprs') - } - typ_sym := g.table.get_type_symbol(it.receiver_type) - // rec_sym := g.table.get_type_symbol(it.receiver_type) - mut receiver_name := typ_sym.name - if typ_sym.kind == .array && it.name == 'filter' { - g.gen_filter(it) - return - } - if typ_sym.kind == .array && it.name in - // TODO performance, detect `array` method differently - ['repeat', 'sort_with_compare', 'free', 'push_many', 'trim', - // - 'first', 'last', 'clone', 'reverse', 'slice'] { - // && rec_sym.name == 'array' { - // && rec_sym.name == 'array' && receiver_name.starts_with('array') { - // `array_byte_clone` => `array_clone` - receiver_name = 'array' - if it.name in ['last', 'first'] { - return_type_str := g.typ(it.return_type) - g.write('*($return_type_str*)') - } - } - name := '${receiver_name}_$it.name'.replace('.', '__') - // if it.receiver_type != 0 { - // g.write('/*${g.typ(it.receiver_type)}*/') - // g.write('/*expr_type=${g.typ(it.expr_type)} rec type=${g.typ(it.receiver_type)}*/') - // } - g.write('${name}(') - if table.type_is_ptr(it.receiver_type) && !table.type_is_ptr(it.expr_type) { - // The receiver is a reference, but the caller provided a value - // Add `&` automatically. - // TODO same logic in call_args() - g.write('&') - } - else if !table.type_is_ptr(it.receiver_type) && table.type_is_ptr(it.expr_type) { - g.write('/*rec*/*') - } - g.expr(it.expr) - is_variadic := it.exp_arg_types.len > 0 && table.type_is_variadic(it.exp_arg_types[it.exp_arg_types.len - 1]) - if it.args.len > 0 || is_variadic { - g.write(', ') - } - // ///////// - /* - if name.contains('subkeys') { - println('call_args $name $it.arg_types.len') - for t in it.arg_types { - sym := g.table.get_type_symbol(t) - print('$sym.name ') - } - println('') - } - */ - // /////// - g.call_args(it.args, it.exp_arg_types) - g.write(')') - if it.or_block.stmts.len > 0 { - g.or_block(it.or_block.stmts, it.return_type) - } - } ast.None { g.write('opt_none()') } @@ -1673,7 +1606,7 @@ fn (g mut Gen) index_expr(node ast.IndexExpr) { } } -fn (g mut Gen) return_statement(it ast.Return) { +fn (g mut Gen) return_statement(node ast.Return) { g.write('return') if g.fn_decl.name == 'main' { g.writeln(' 0;') @@ -1681,7 +1614,7 @@ fn (g mut Gen) return_statement(it ast.Return) { } fn_return_is_optional := table.type_is_optional(g.fn_decl.return_type) // multiple returns - if it.exprs.len > 1 { + if node.exprs.len > 1 { g.write(' ') typ_sym := g.table.get_type_symbol(g.fn_decl.return_type) mr_info := typ_sym.info as table.MultiReturn @@ -1691,10 +1624,10 @@ fn (g mut Gen) return_statement(it ast.Return) { g.write('opt_ok(& ($styp []) { ') } g.write('($styp){') - for i, expr in it.exprs { + for i, expr in node.exprs { g.write('.arg$i=') g.expr(expr) - if i < it.exprs.len - 1 { + if i < node.exprs.len - 1 { g.write(',') } } @@ -1704,36 +1637,39 @@ fn (g mut Gen) return_statement(it ast.Return) { } } // normal return - else if it.exprs.len == 1 { + else if node.exprs.len == 1 { g.write(' ') // `return opt_ok(expr)` for functions that expect an optional - if fn_return_is_optional && !table.type_is_optional(it.types[0]) { + if fn_return_is_optional && !table.type_is_optional(node.types[0]) { mut is_none := false mut is_error := false - expr0 := it.exprs[0] + expr0 := node.exprs[0] match expr0 { ast.None { is_none = true } ast.CallExpr { - is_error = true // TODO check name 'error' + // TODO: why? + if !it.is_method { + is_error = true // TODO check name 'error' + } } else {} } if !is_none && !is_error { styp := g.typ(g.fn_decl.return_type)[7..] // remove 'Option_' g.write('opt_ok(& ($styp []) { ') - g.expr(it.exprs[0]) + g.expr(node.exprs[0]) g.writeln(' }, sizeof($styp));') return } // g.write('/*OPTIONAL*/') } - if !table.type_is_ptr(g.fn_decl.return_type) && table.type_is_ptr(it.types[0]) { + if !table.type_is_ptr(g.fn_decl.return_type) && table.type_is_ptr(node.types[0]) { // Automatic Dereference g.write('*') } - g.expr_with_cast(it.exprs[0], it.types[0], g.fn_decl.return_type) + g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type) } g.writeln(';') } @@ -2168,7 +2104,7 @@ fn (g mut Gen) string_inter_literal(node ast.StringInterLiteral) { } // `nums.filter(it % 2 == 0)` -fn (g mut Gen) gen_filter(node ast.MethodCallExpr) { +fn (g mut Gen) gen_filter(node ast.CallExpr) { tmp := g.new_tmp_var() buf := g.out.buf[g.stmt_start_pos..] s := string(buf.clone()) // the already generated part of current statement @@ -2182,12 +2118,12 @@ fn (g mut Gen) gen_filter(node ast.MethodCallExpr) { styp := g.typ(node.return_type) elem_type_str := g.typ(info.elem_type) g.write('\nint ${tmp}_len = ') - g.expr(node.expr) + g.expr(node.left) g.writeln('.len;') g.writeln('$styp $tmp = new_array(0, ${tmp}_len, sizeof($elem_type_str));') g.writeln('for (int i = 0; i < ${tmp}_len; i++) {') g.write(' $elem_type_str it = (($elem_type_str*) ') - g.expr(node.expr) + g.expr(node.left) g.writeln('.data)[i];') g.write('if (') g.expr(node.args[0].expr) // the first arg is the filter condition @@ -2204,67 +2140,133 @@ fn (g mut Gen) insert_before(s string) { g.write(cur_line) } -fn (g mut Gen) call_expr(it ast.CallExpr) { - mut name := it.name - is_print := name == 'println' - if it.is_c { - // Skip "C." - g.is_c_call = true - name = name[2..].replace('.', '__') +fn (g mut Gen) call_expr(node ast.CallExpr) { + if node.is_method { + // 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 == .array && node.name == 'filter' { + g.gen_filter(node) + return + } + if typ_sym.kind == .array && node.name in + // TODO performance, detect `array` method differently + ['repeat', 'sort_with_compare', 'free', 'push_many', 'trim', + // + 'first', 'last', 'clone', 'reverse', 'slice'] { + // && rec_sym.name == 'array' { + // && rec_sym.name == 'array' && receiver_name.starts_with('array') { + // `array_byte_clone` => `array_clone` + receiver_name = 'array' + if node.name in ['last', 'first'] { + return_type_str := g.typ(node.return_type) + g.write('*($return_type_str*)') + } + } + name := '${receiver_name}_$node.name'.replace('.', '__') + // if node.receiver_type != 0 { + // g.write('/*${g.typ(node.receiver_type)}*/') + // g.write('/*expr_type=${g.typ(node.left_type)} rec type=${g.typ(node.receiver_type)}*/') + // } + g.write('${name}(') + if table.type_is_ptr(node.receiver_type) && !table.type_is_ptr(node.left_type) { + // The receiver is a reference, but the caller provided a value + // Add `&` automatically. + // TODO same logic in call_args() + g.write('&') + } + else if !table.type_is_ptr(node.receiver_type) && table.type_is_ptr(node.left_type) { + g.write('/*rec*/*') + } + g.expr(node.left) + is_variadic := node.exp_arg_types.len > 0 && table.type_is_variadic(node.exp_arg_types[node.exp_arg_types.len - 1]) + if node.args.len > 0 || is_variadic { + g.write(', ') + } + // ///////// + /* + if name.contains('subkeys') { + println('call_args $name $node.arg_types.len') + for t in node.arg_types { + sym := g.table.get_type_symbol(t) + print('$sym.name ') + } + println('') + } + */ + // /////// + g.call_args(node.args, node.exp_arg_types) + g.write(')') + if node.or_block.stmts.len > 0 { + g.or_block(node.or_block.stmts, node.return_type) + } } else { - name = c_name(name) - } - // Generate tmp vars for values that have to be freed. - /* - mut tmps := []string - for arg in it.args { - if arg.typ == table.string_type_idx || is_print { - tmp := g.new_tmp_var() - tmps << tmp - g.write('string $tmp = ') - g.expr(arg.expr) - g.writeln('; //memory') - } - } - */ - - if is_print && it.args[0].typ != table.string_type_idx { - typ := it.args[0].typ - mut styp := g.typ(typ) - sym := g.table.get_type_symbol(typ) - if !sym.has_method('str') && !(int(typ) in g.str_types) { - // Generate an automatic str() method if this type doesn't have it already - if table.type_is_ptr(typ) { - styp = styp.replace('*', '') - } - g.str_types << typ - g.definitions.writeln('string ${styp}_str($styp* x) { return tos3("TODO_str"); }') - } - if g.autofree && !table.type_is_optional(typ) { - tmp := g.new_tmp_var() - // tmps << tmp - g.write('string $tmp = ${styp}_str(') - g.expr(it.args[0].expr) - g.writeln('); println($tmp); string_free($tmp); //MEM2 $styp') + mut name := node.name + is_print := name == 'println' + if node.is_c { + // Skip "C." + g.is_c_call = true + name = name[2..].replace('.', '__') } else { - // `println(int_str(10))` - // sym := g.table.get_type_symbol(it.args[0].typ) - g.write('println(${styp}_str(') - g.expr(it.args[0].expr) - g.write('))') + name = c_name(name) } + // Generate tmp vars for values that have to be freed. + /* + mut tmps := []string + for arg in node.args { + if arg.typ == table.string_type_idx || is_print { + tmp := g.new_tmp_var() + tmps << tmp + g.write('string $tmp = ') + g.expr(arg.expr) + g.writeln('; //memory') + } + } + */ + + if is_print && node.args[0].typ != table.string_type_idx { + typ := node.args[0].typ + mut styp := g.typ(typ) + sym := g.table.get_type_symbol(typ) + if !sym.has_method('str') && !(int(typ) in g.str_types) { + // Generate an automatic str() method if this type doesn't have it already + if table.type_is_ptr(typ) { + styp = styp.replace('*', '') + } + g.str_types << typ + g.definitions.writeln('string ${styp}_str($styp* x) { return tos3("TODO_str"); }') + } + if g.autofree && !table.type_is_optional(typ) { + tmp := g.new_tmp_var() + // tmps << tmp + g.write('string $tmp = ${styp}_str(') + g.expr(node.args[0].expr) + g.writeln('); println($tmp); string_free($tmp); //MEM2 $styp') + } + else { + // `println(int_str(10))` + // sym := g.table.get_type_symbol(node.args[0].typ) + g.write('println(${styp}_str(') + g.expr(node.args[0].expr) + g.write('))') + } + } + else { + g.write('${name}(') + g.call_args(node.args, node.exp_arg_types) + g.write(')') + } + if node.or_block.stmts.len > 0 { + g.or_block(node.or_block.stmts, node.return_type) + } + g.is_c_call = false } - else { - g.write('${name}(') - g.call_args(it.args, it.exp_arg_types) - g.write(')') - } - if it.or_block.stmts.len > 0 { - g.or_block(it.or_block.stmts, it.return_type) - } - g.is_c_call = false } fn (g mut Gen) or_block(stmts []ast.Stmt, return_type table.Type) { diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 62bef84851..60b537e7df 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -1005,11 +1005,12 @@ fn (p mut Parser) dot_expr(left ast.Expr) ast.Expr { or_stmts = p.parse_block_no_scope() p.close_scope() } - mcall_expr := ast.MethodCallExpr{ - expr: left + mcall_expr := ast.CallExpr{ + left: left name: field_name args: args pos: pos + is_method: true or_block: ast.OrExpr{ stmts: or_stmts }