ast: first step merging CallExpr & MethodCallExpr

pull/4159/head
joe-conigliaro 2020-03-30 21:39:20 +11:00
parent 7785583b34
commit 3440d7edd8
No known key found for this signature in database
GPG Key ID: C12F7136C08206F1
5 changed files with 324 additions and 339 deletions

View File

@ -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}
}

View File

@ -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)
}

View File

@ -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')
}

View File

@ -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) {

View File

@ -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
}