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 TypeDecl = AliasTypeDecl | SumTypeDecl | FnTypeDecl
|
||||||
|
|
||||||
pub type Expr = InfixExpr | IfExpr | StringLiteral | IntegerLiteral | CharLiteral |
|
pub type Expr = InfixExpr | IfExpr | StringLiteral | IntegerLiteral | CharLiteral |
|
||||||
FloatLiteral | Ident | CallExpr | BoolLiteral | StructInit | ArrayInit | SelectorExpr | PostfixExpr |
|
FloatLiteral | Ident | CallExpr | BoolLiteral | StructInit | ArrayInit | SelectorExpr |
|
||||||
AssignExpr | PrefixExpr | MethodCallExpr | IndexExpr | RangeExpr | MatchExpr |
|
PostfixExpr | AssignExpr | PrefixExpr | IndexExpr | RangeExpr | MatchExpr | CastExpr |
|
||||||
CastExpr | EnumVal | Assoc | SizeOf | None | MapInit | IfGuardExpr | ParExpr | OrExpr |
|
EnumVal | Assoc | SizeOf | None | MapInit | IfGuardExpr | ParExpr | OrExpr | ConcatExpr |
|
||||||
ConcatExpr | Type | AsCast | TypeOf | StringInterLiteral
|
Type | AsCast | TypeOf | StringInterLiteral
|
||||||
|
|
||||||
pub type Stmt = GlobalDecl | FnDecl | Return | Module | Import | ExprStmt |
|
pub type Stmt = GlobalDecl | FnDecl | Return | Module | Import | ExprStmt |
|
||||||
ForStmt | StructDecl | ForCStmt | ForInStmt | CompIf | ConstDecl | Attr | BranchStmt |
|
ForStmt | StructDecl | ForCStmt | ForInStmt | CompIf | ConstDecl | Attr | BranchStmt |
|
||||||
HashStmt | AssignStmt | EnumDecl | TypeDecl | DeferStmt | GotoLabel | GotoStmt |
|
HashStmt | AssignStmt | EnumDecl | TypeDecl | DeferStmt | GotoLabel | GotoStmt |
|
||||||
LineComment | MultiLineComment | AssertStmt | UnsafeStmt | GoStmt | Block
|
LineComment | MultiLineComment | AssertStmt | UnsafeStmt | GoStmt | Block
|
||||||
// pub type Type = StructType | ArrayType
|
// pub type Type = StructType | ArrayType
|
||||||
// pub struct StructType {
|
// pub struct StructType {
|
||||||
@ -173,29 +173,16 @@ pub struct CallExpr {
|
|||||||
pub:
|
pub:
|
||||||
// tok token.Token
|
// tok token.Token
|
||||||
pos token.Position
|
pos token.Position
|
||||||
|
left Expr // `user` in `user.register()`
|
||||||
|
is_method bool
|
||||||
mut:
|
mut:
|
||||||
// func Expr
|
|
||||||
name string
|
name string
|
||||||
args []CallArg
|
args []CallArg
|
||||||
exp_arg_types []table.Type
|
exp_arg_types []table.Type
|
||||||
is_c bool
|
is_c bool
|
||||||
muts []bool
|
|
||||||
or_block OrExpr
|
or_block OrExpr
|
||||||
// has_or_block bool
|
// has_or_block bool
|
||||||
return_type table.Type
|
left_type table.Type // type of `user`
|
||||||
}
|
|
||||||
|
|
||||||
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`
|
|
||||||
receiver_type table.Type // User
|
receiver_type table.Type // User
|
||||||
return_type table.Type
|
return_type table.Type
|
||||||
}
|
}
|
||||||
@ -711,9 +698,6 @@ pub fn expr_is_call(expr Expr) bool {
|
|||||||
CallExpr{
|
CallExpr{
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
MethodCallExpr{
|
|
||||||
true
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
false}
|
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 {
|
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)
|
c.stmts(call_expr.or_block.stmts)
|
||||||
// TODO: impl typeof properly (probably not going to be a fn call)
|
if call_expr.is_method {
|
||||||
if fn_name == 'typeof' {
|
left_type := c.expr(call_expr.left)
|
||||||
return table.string_type
|
call_expr.left_type = left_type
|
||||||
}
|
left_type_sym := c.table.get_type_symbol(left_type)
|
||||||
// start hack: until v1 is fixed and c definitions are added for these
|
method_name := call_expr.name
|
||||||
if fn_name in ['C.calloc', 'C.malloc', 'C.exit', 'C.free'] {
|
// TODO: remove this for actual methods, use only for compiler magic
|
||||||
for arg in call_expr.args {
|
if left_type_sym.kind == .array && method_name in ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice'] {
|
||||||
c.expr(arg.expr)
|
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'] {
|
else if left_type_sym.kind == .array && method_name in ['first', 'last'] {
|
||||||
return table.byteptr_type
|
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
|
return table.void_type
|
||||||
}
|
}
|
||||||
// end hack
|
else {
|
||||||
// look for function in format `mod.fn` or `fn` (main/builtin)
|
fn_name := call_expr.name
|
||||||
mut f := table.Fn{}
|
// TODO: impl typeof properly (probably not going to be a fn call)
|
||||||
mut found := false
|
if fn_name == 'typeof' {
|
||||||
// try prefix with current module as it would have never gotten prefixed
|
return table.string_type
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
// start hack: until v1 is fixed and c definitions are added for these
|
||||||
// already prefixed (mod.fn) or C/builtin/main
|
if fn_name in ['C.calloc', 'C.malloc', 'C.exit', 'C.free'] {
|
||||||
if !found {
|
for arg in call_expr.args {
|
||||||
if f1 := c.table.find_fn(fn_name) {
|
c.expr(arg.expr)
|
||||||
found = true
|
}
|
||||||
f = f1
|
if fn_name in ['C.calloc', 'C.malloc'] {
|
||||||
|
return table.byteptr_type
|
||||||
|
}
|
||||||
|
return table.void_type
|
||||||
}
|
}
|
||||||
}
|
// end hack
|
||||||
// check for arg (var) of fn type
|
// look for function in format `mod.fn` or `fn` (main/builtin)
|
||||||
if !found {
|
mut f := table.Fn{}
|
||||||
scope := c.file.scope.innermost(call_expr.pos.pos)
|
mut found := false
|
||||||
if var := scope.find_var(fn_name) {
|
// try prefix with current module as it would have never gotten prefixed
|
||||||
if var.typ != 0 {
|
if !fn_name.contains('.') && !(c.file.mod.name in ['builtin', 'main']) {
|
||||||
vts := c.table.get_type_symbol(var.typ)
|
name_prefixed := '${c.file.mod.name}.$fn_name'
|
||||||
if vts.kind == .function {
|
if f1 := c.table.find_fn(name_prefixed) {
|
||||||
info := vts.info as table.FnType
|
call_expr.name = name_prefixed
|
||||||
f = info.func
|
found = true
|
||||||
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 {
|
||||||
if !found {
|
c.error('unknown fn: $fn_name', call_expr.pos)
|
||||||
c.error('unknown fn: $fn_name', call_expr.pos)
|
return table.void_type
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
return f.return_type
|
call_expr.return_type = f.return_type
|
||||||
}
|
if f.is_c || call_expr.is_c {
|
||||||
min_required_args := if f.is_variadic { f.args.len - 1 } else { f.args.len }
|
for arg in call_expr.args {
|
||||||
if call_expr.args.len < min_required_args {
|
c.expr(arg.expr)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
// TODO const bug
|
return f.return_type
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
min_required_args := if f.is_variadic { f.args.len - 1 } else { f.args.len }
|
||||||
return f.return_type
|
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)
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
for i, arg in method_call_expr.args {
|
else if !f.is_variadic && call_expr.args.len > f.args.len {
|
||||||
c.expr(arg.expr)
|
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`
|
// println can print anything
|
||||||
method_call_expr.return_type = typ
|
if fn_name == 'println' {
|
||||||
if name == 'clone' {
|
c.expected_type = table.string_type
|
||||||
// in ['clone', 'str'] {
|
call_expr.args[0].typ = c.expr(call_expr.args[0].expr)
|
||||||
method_call_expr.receiver_type = table.type_to_ptr(typ)
|
return f.return_type
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
// TODO: typ optimize.. this node can get processed more than once
|
// TODO: typ optimize.. this node can get processed more than once
|
||||||
if method_call_expr.exp_arg_types.len == 0 {
|
if call_expr.exp_arg_types.len == 0 {
|
||||||
for i in 1 .. method.args.len {
|
for arg in f.args {
|
||||||
method_call_expr.exp_arg_types << method.args[i].typ
|
call_expr.exp_arg_types << arg.typ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
method_call_expr.receiver_type = method.args[0].typ
|
for i, call_arg in call_expr.args {
|
||||||
method_call_expr.return_type = method.return_type
|
arg := if f.is_variadic && i >= f.args.len - 1 { f.args[f.args.len - 1] } else { f.args[i] }
|
||||||
return method.return_type
|
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 {
|
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 {
|
ast.MatchExpr {
|
||||||
return c.match_expr(mut it)
|
return c.match_expr(mut it)
|
||||||
}
|
}
|
||||||
ast.MethodCallExpr {
|
|
||||||
return c.method_call_expr(mut it)
|
|
||||||
}
|
|
||||||
ast.PostfixExpr {
|
ast.PostfixExpr {
|
||||||
return c.postfix_expr(it)
|
return c.postfix_expr(it)
|
||||||
}
|
}
|
||||||
|
@ -388,10 +388,19 @@ fn (f mut Fmt) expr(node ast.Expr) {
|
|||||||
f.write(')')
|
f.write(')')
|
||||||
}
|
}
|
||||||
ast.CallExpr {
|
ast.CallExpr {
|
||||||
f.write('${it.name}(')
|
if it.is_method {
|
||||||
f.call_args(it.args)
|
f.expr(it.left)
|
||||||
f.write(')')
|
f.write('.' + it.name + '(')
|
||||||
f.or_expr(it.or_block)
|
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 {
|
ast.CharLiteral {
|
||||||
f.write('`$it.val`')
|
f.write('`$it.val`')
|
||||||
@ -510,13 +519,6 @@ fn (f mut Fmt) expr(node ast.Expr) {
|
|||||||
f.indent--
|
f.indent--
|
||||||
f.write('}')
|
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 {
|
ast.None {
|
||||||
f.write('none')
|
f.write('none')
|
||||||
}
|
}
|
||||||
|
@ -578,9 +578,6 @@ fn (g mut Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
|
|||||||
ast.CallExpr {
|
ast.CallExpr {
|
||||||
return_type = it.return_type
|
return_type = it.return_type
|
||||||
}
|
}
|
||||||
ast.MethodCallExpr {
|
|
||||||
return_type = it.return_type
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
panic('expected call')
|
panic('expected call')
|
||||||
}
|
}
|
||||||
@ -1041,70 +1038,6 @@ fn (g mut Gen) expr(node ast.Expr) {
|
|||||||
g.write('new_map(1, sizeof($value_typ_str))')
|
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 {
|
ast.None {
|
||||||
g.write('opt_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')
|
g.write('return')
|
||||||
if g.fn_decl.name == 'main' {
|
if g.fn_decl.name == 'main' {
|
||||||
g.writeln(' 0;')
|
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)
|
fn_return_is_optional := table.type_is_optional(g.fn_decl.return_type)
|
||||||
// multiple returns
|
// multiple returns
|
||||||
if it.exprs.len > 1 {
|
if node.exprs.len > 1 {
|
||||||
g.write(' ')
|
g.write(' ')
|
||||||
typ_sym := g.table.get_type_symbol(g.fn_decl.return_type)
|
typ_sym := g.table.get_type_symbol(g.fn_decl.return_type)
|
||||||
mr_info := typ_sym.info as table.MultiReturn
|
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('opt_ok(& ($styp []) { ')
|
||||||
}
|
}
|
||||||
g.write('($styp){')
|
g.write('($styp){')
|
||||||
for i, expr in it.exprs {
|
for i, expr in node.exprs {
|
||||||
g.write('.arg$i=')
|
g.write('.arg$i=')
|
||||||
g.expr(expr)
|
g.expr(expr)
|
||||||
if i < it.exprs.len - 1 {
|
if i < node.exprs.len - 1 {
|
||||||
g.write(',')
|
g.write(',')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1704,36 +1637,39 @@ fn (g mut Gen) return_statement(it ast.Return) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// normal return
|
// normal return
|
||||||
else if it.exprs.len == 1 {
|
else if node.exprs.len == 1 {
|
||||||
g.write(' ')
|
g.write(' ')
|
||||||
// `return opt_ok(expr)` for functions that expect an optional
|
// `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_none := false
|
||||||
mut is_error := false
|
mut is_error := false
|
||||||
expr0 := it.exprs[0]
|
expr0 := node.exprs[0]
|
||||||
match expr0 {
|
match expr0 {
|
||||||
ast.None {
|
ast.None {
|
||||||
is_none = true
|
is_none = true
|
||||||
}
|
}
|
||||||
ast.CallExpr {
|
ast.CallExpr {
|
||||||
is_error = true // TODO check name 'error'
|
// TODO: why?
|
||||||
|
if !it.is_method {
|
||||||
|
is_error = true // TODO check name 'error'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {}
|
else {}
|
||||||
}
|
}
|
||||||
if !is_none && !is_error {
|
if !is_none && !is_error {
|
||||||
styp := g.typ(g.fn_decl.return_type)[7..] // remove 'Option_'
|
styp := g.typ(g.fn_decl.return_type)[7..] // remove 'Option_'
|
||||||
g.write('opt_ok(& ($styp []) { ')
|
g.write('opt_ok(& ($styp []) { ')
|
||||||
g.expr(it.exprs[0])
|
g.expr(node.exprs[0])
|
||||||
g.writeln(' }, sizeof($styp));')
|
g.writeln(' }, sizeof($styp));')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// g.write('/*OPTIONAL*/')
|
// 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
|
// Automatic Dereference
|
||||||
g.write('*')
|
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(';')
|
g.writeln(';')
|
||||||
}
|
}
|
||||||
@ -2168,7 +2104,7 @@ fn (g mut Gen) string_inter_literal(node ast.StringInterLiteral) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// `nums.filter(it % 2 == 0)`
|
// `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()
|
tmp := g.new_tmp_var()
|
||||||
buf := g.out.buf[g.stmt_start_pos..]
|
buf := g.out.buf[g.stmt_start_pos..]
|
||||||
s := string(buf.clone()) // the already generated part of current statement
|
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)
|
styp := g.typ(node.return_type)
|
||||||
elem_type_str := g.typ(info.elem_type)
|
elem_type_str := g.typ(info.elem_type)
|
||||||
g.write('\nint ${tmp}_len = ')
|
g.write('\nint ${tmp}_len = ')
|
||||||
g.expr(node.expr)
|
g.expr(node.left)
|
||||||
g.writeln('.len;')
|
g.writeln('.len;')
|
||||||
g.writeln('$styp $tmp = new_array(0, ${tmp}_len, sizeof($elem_type_str));')
|
g.writeln('$styp $tmp = new_array(0, ${tmp}_len, sizeof($elem_type_str));')
|
||||||
g.writeln('for (int i = 0; i < ${tmp}_len; i++) {')
|
g.writeln('for (int i = 0; i < ${tmp}_len; i++) {')
|
||||||
g.write(' $elem_type_str it = (($elem_type_str*) ')
|
g.write(' $elem_type_str it = (($elem_type_str*) ')
|
||||||
g.expr(node.expr)
|
g.expr(node.left)
|
||||||
g.writeln('.data)[i];')
|
g.writeln('.data)[i];')
|
||||||
g.write('if (')
|
g.write('if (')
|
||||||
g.expr(node.args[0].expr) // the first arg is the filter condition
|
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)
|
g.write(cur_line)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (g mut Gen) call_expr(it ast.CallExpr) {
|
fn (g mut Gen) call_expr(node ast.CallExpr) {
|
||||||
mut name := it.name
|
if node.is_method {
|
||||||
is_print := name == 'println'
|
// TODO: there are still due to unchecked exprs (opt/some fn arg)
|
||||||
if it.is_c {
|
if node.left_type == 0 {
|
||||||
// Skip "C."
|
verror('method receiver type is 0, this means there are some uchecked exprs')
|
||||||
g.is_c_call = true
|
}
|
||||||
name = name[2..].replace('.', '__')
|
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 {
|
else {
|
||||||
name = c_name(name)
|
mut name := node.name
|
||||||
}
|
is_print := name == 'println'
|
||||||
// Generate tmp vars for values that have to be freed.
|
if node.is_c {
|
||||||
/*
|
// Skip "C."
|
||||||
mut tmps := []string
|
g.is_c_call = true
|
||||||
for arg in it.args {
|
name = name[2..].replace('.', '__')
|
||||||
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')
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// `println(int_str(10))`
|
name = c_name(name)
|
||||||
// sym := g.table.get_type_symbol(it.args[0].typ)
|
|
||||||
g.write('println(${styp}_str(')
|
|
||||||
g.expr(it.args[0].expr)
|
|
||||||
g.write('))')
|
|
||||||
}
|
}
|
||||||
|
// 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) {
|
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()
|
or_stmts = p.parse_block_no_scope()
|
||||||
p.close_scope()
|
p.close_scope()
|
||||||
}
|
}
|
||||||
mcall_expr := ast.MethodCallExpr{
|
mcall_expr := ast.CallExpr{
|
||||||
expr: left
|
left: left
|
||||||
name: field_name
|
name: field_name
|
||||||
args: args
|
args: args
|
||||||
pos: pos
|
pos: pos
|
||||||
|
is_method: true
|
||||||
or_block: ast.OrExpr{
|
or_block: ast.OrExpr{
|
||||||
stmts: or_stmts
|
stmts: or_stmts
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user