mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
ast: first step merging CallExpr & MethodCallExpr
This commit is contained in:
parent
7785583b34
commit
3440d7edd8
@ -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}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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')
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user