v/vlib/v/parser/fn.v

315 lines
6.7 KiB
V
Raw Normal View History

2020-01-23 21:04:46 +01:00
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
2020-01-02 08:30:15 +01:00
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module parser
2020-04-14 19:32:23 +02:00
import v.ast
import v.table
import v.scanner
import v.token
2020-01-02 08:30:15 +01:00
2020-04-15 23:16:49 +02:00
pub fn (p mut Parser) call_expr(is_c bool, is_js bool, mod string) ast.CallExpr {
2020-04-10 14:53:06 +02:00
first_pos := p.tok.position()
2020-01-02 08:30:15 +01:00
tok := p.tok
name := p.check_name()
fn_name := if is_c {
'C.$name'
2020-04-15 23:16:49 +02:00
} else if is_js {
'JS.$name'
} else if mod.len > 0 {
'${mod}.$name'
} else {
name
}
2020-01-02 08:30:15 +01:00
p.check(.lpar)
args := p.call_args()
2020-04-10 14:53:06 +02:00
last_pos := p.tok.position()
p.check(.rpar)
pos := token.Position{
line_nr: first_pos.line_nr
pos: first_pos.pos
len: last_pos.pos - first_pos.pos + last_pos.len
}
2020-04-15 01:55:51 +02:00
var or_stmts := []ast.Stmt
var is_or_block_used := false
2020-02-29 15:03:32 +01:00
if p.tok.kind == .key_orelse {
p.next()
p.open_scope()
p.scope.register('err', ast.Var{
name: 'err'
typ: table.string_type
})
p.scope.register('errcode', ast.Var{
name: 'errcode'
typ: table.int_type
})
is_or_block_used = true
or_stmts = p.parse_block_no_scope()
p.close_scope()
2020-02-29 15:03:32 +01:00
}
2020-01-02 08:30:15 +01:00
node := ast.CallExpr{
name: fn_name
args: args
mod: p.mod
2020-04-10 14:53:06 +02:00
pos: pos
2020-02-18 18:13:34 +01:00
is_c: is_c
2020-04-15 23:16:49 +02:00
is_js: is_js
2020-02-29 15:03:32 +01:00
or_block: ast.OrExpr{
stmts: or_stmts
is_used: is_or_block_used
}
2020-02-04 17:44:39 +01:00
}
return node
2020-01-02 08:30:15 +01:00
}
pub fn (p mut Parser) call_args() []ast.CallArg {
2020-04-15 01:55:51 +02:00
var args := []ast.CallArg
for p.tok.kind != .rpar {
2020-04-15 01:55:51 +02:00
var is_mut := false
2020-02-04 12:50:58 +01:00
if p.tok.kind == .key_mut {
p.check(.key_mut)
is_mut = true
2020-02-04 12:50:58 +01:00
}
2020-03-05 12:13:45 +01:00
e := p.expr(0)
args << ast.CallArg{
is_mut: is_mut
expr: e
}
if p.tok.kind != .rpar {
p.check(.comma)
}
}
return args
}
fn (p mut Parser) fn_decl() ast.FnDecl {
2020-02-18 20:20:15 +01:00
// p.table.clear_vars()
2020-04-11 21:50:14 +02:00
pos := p.tok.position()
p.open_scope()
2020-03-05 16:13:14 +01:00
is_deprecated := p.attr == 'deprecated'
is_pub := p.tok.kind == .key_pub
if is_pub {
p.next()
}
2020-01-02 08:30:15 +01:00
p.check(.key_fn)
2020-04-15 23:16:49 +02:00
// C. || JS.
2020-02-04 09:54:15 +01:00
is_c := p.tok.kind == .name && p.tok.lit == 'C'
2020-04-15 23:16:49 +02:00
is_js := p.tok.kind == .name && p.tok.lit == 'JS'
if is_c || is_js {
2020-02-04 09:54:15 +01:00
p.next()
p.check(.dot)
}
// Receiver?
2020-04-15 01:55:51 +02:00
var rec_name := ''
var is_method := false
var rec_type := table.void_type
var rec_mut := false
var args := []table.Arg
if p.tok.kind == .lpar {
2020-04-15 01:45:27 +02:00
p.next() // (
2020-01-07 13:10:05 +01:00
is_method = true
2020-04-15 01:45:27 +02:00
rec_mut = p.tok.kind == .key_var
if rec_mut {
p.next() // `var`
}
rec_name = p.check_name()
2020-04-15 01:45:27 +02:00
if !rec_mut {
rec_mut = p.tok.kind == .key_mut
}
is_amp := p.peek_tok.kind == .amp
// if rec_mut {
2020-03-11 21:11:27 +01:00
// p.check(.key_mut)
// }
// TODO: talk to alex, should mut be parsed with the type like this?
// or should it be a property of the arg, like this ptr/mut becomes indistinguishable
rec_type = p.parse_type()
if is_amp && rec_mut {
p.error('use `(f mut Foo)` or `(f &Foo)` instead of `(f mut &Foo)`')
}
args << table.Arg{
2020-03-10 23:21:26 +01:00
name: rec_name
is_mut: rec_mut
2020-03-10 23:21:26 +01:00
typ: rec_type
}
p.check(.rpar)
}
2020-04-15 01:55:51 +02:00
var name := ''
if p.tok.kind == .name {
2020-03-11 21:11:27 +01:00
// TODO high order fn
name = p.check_name()
2020-04-15 23:16:49 +02:00
if !is_js && !is_c && !p.pref.translated && scanner.contains_capital(name) {
p.error('function names cannot contain uppercase letters, use snake_case instead')
}
if is_method && p.table.get_type_symbol(rec_type).has_method(name) {
2020-04-07 01:09:25 +02:00
p.error('duplicate method `$name`')
}
}
if p.tok.kind in [.plus, .minus, .mul, .div, .mod] {
name = p.tok.kind.str() // op_to_fn_name()
p.next()
}
2020-02-03 07:44:52 +01:00
// <T>
is_generic := p.tok.kind == .lt
if is_generic {
2020-02-03 07:44:52 +01:00
p.next()
p.next()
p.check(.gt)
}
2020-01-02 08:30:15 +01:00
// Args
args2, is_variadic := p.fn_args()
args << args2
for arg in args {
p.scope.register(arg.name, ast.Var{
name: arg.name
typ: arg.typ
})
2020-02-11 13:03:10 +01:00
}
2020-01-02 08:30:15 +01:00
// Return type
2020-04-15 01:55:51 +02:00
var return_type := table.void_type
2020-02-11 13:21:41 +01:00
if p.tok.kind.is_start_of_type() {
return_type = p.parse_type()
2020-01-02 08:30:15 +01:00
}
2020-02-27 17:21:13 +01:00
// Register
2020-01-08 10:19:12 +01:00
if is_method {
2020-04-15 01:55:51 +02:00
var type_sym := p.table.get_type_symbol(rec_type)
2020-02-27 17:21:13 +01:00
// p.warn('reg method $type_sym.name . $name ()')
type_sym.register_method(table.Fn{
2020-01-08 10:19:12 +01:00
name: name
args: args
return_type: return_type
2020-03-24 12:39:11 +01:00
is_variadic: is_variadic
is_generic: is_generic
2020-01-08 10:19:12 +01:00
})
} else {
if is_c {
name = 'C.$name'
2020-04-15 23:16:49 +02:00
} else if is_js {
name = 'JS.$name'
} else {
name = p.prepend_mod(name)
}
if _ := p.table.find_fn(name) {
p.error('redefinition of `$name`')
}
2020-01-07 13:10:05 +01:00
p.table.register_fn(table.Fn{
name: name
2020-01-07 13:10:05 +01:00
args: args
return_type: return_type
is_variadic: is_variadic
2020-02-07 07:34:18 +01:00
is_c: is_c
2020-04-15 23:16:49 +02:00
is_js: is_js
is_generic: is_generic
2020-01-07 13:10:05 +01:00
})
}
2020-04-08 19:08:54 +02:00
// Body
2020-04-15 01:55:51 +02:00
var stmts := []ast.Stmt
2020-03-06 16:31:40 +01:00
no_body := p.tok.kind != .lcbr
2020-02-04 09:54:15 +01:00
if p.tok.kind == .lcbr {
stmts = p.parse_block()
}
p.close_scope()
2020-03-05 16:13:14 +01:00
p.attr = ''
2020-01-02 08:30:15 +01:00
return ast.FnDecl{
name: name
stmts: stmts
return_type: return_type
args: args
2020-03-05 16:13:14 +01:00
is_deprecated: is_deprecated
is_pub: is_pub
is_variadic: is_variadic
receiver: ast.Field{
2020-04-10 18:11:43 +02:00
name: rec_name
typ: rec_type
}
2020-02-18 20:20:15 +01:00
is_method: is_method
rec_mut: rec_mut
is_c: is_c
2020-04-15 23:16:49 +02:00
is_js: is_js
2020-03-06 16:31:40 +01:00
no_body: no_body
2020-04-11 21:50:14 +02:00
pos: pos
2020-04-10 18:11:43 +02:00
is_builtin: p.builtin_mod || p.mod in ['math', 'strconv', 'strconv.ftoa', 'hash.wyhash',
'math.bits', 'strings']
2020-01-02 08:30:15 +01:00
}
}
2020-02-11 13:03:10 +01:00
fn (p mut Parser) fn_args() ([]table.Arg, bool) {
2020-02-11 13:21:41 +01:00
p.check(.lpar)
2020-04-15 01:55:51 +02:00
var args := []table.Arg
var is_variadic := false
2020-02-11 13:03:10 +01:00
// `int, int, string` (no names, just types)
2020-04-08 19:08:54 +02:00
types_only := p.tok.kind in [.amp, .and] || (p.peek_tok.kind == .comma && p.table.known_type(p.tok.lit)) ||
p.peek_tok.kind == .rpar
2020-02-11 13:03:10 +01:00
if types_only {
// p.warn('types only')
2020-04-15 01:55:51 +02:00
var arg_no := 1
2020-02-11 13:03:10 +01:00
for p.tok.kind != .rpar {
arg_name := 'arg_$arg_no'
is_mut := p.tok.kind == .key_mut
if is_mut {
p.check(.key_mut)
}
2020-02-11 13:03:10 +01:00
if p.tok.kind == .ellipsis {
p.check(.ellipsis)
is_variadic = true
}
2020-04-15 01:55:51 +02:00
var arg_type := p.parse_type()
2020-02-29 09:04:47 +01:00
if is_variadic {
arg_type = table.type_set(arg_type, .variadic)
2020-02-29 09:04:47 +01:00
}
2020-02-11 13:03:10 +01:00
if p.tok.kind == .comma {
if is_variadic {
p.error('cannot use ...(variadic) with non-final parameter no $arg_no')
}
p.next()
}
args << table.Arg{
2020-02-11 13:03:10 +01:00
name: arg_name
is_mut: is_mut
2020-02-11 13:03:10 +01:00
typ: arg_type
}
arg_no++
2020-02-11 13:03:10 +01:00
}
} else {
2020-02-11 13:03:10 +01:00
for p.tok.kind != .rpar {
2020-04-15 01:55:51 +02:00
var arg_names := [p.check_name()]
2020-02-11 13:03:10 +01:00
// `a, b, c int`
for p.tok.kind == .comma {
p.check(.comma)
arg_names << p.check_name()
}
is_mut := p.tok.kind == .key_mut
2020-03-11 21:11:27 +01:00
// if is_mut {
// p.check(.key_mut)
// }
2020-02-11 13:03:10 +01:00
if p.tok.kind == .ellipsis {
p.check(.ellipsis)
is_variadic = true
}
2020-04-15 01:55:51 +02:00
var typ := p.parse_type()
2020-02-29 09:04:47 +01:00
if is_variadic {
typ = table.type_set(typ, .variadic)
2020-02-29 09:04:47 +01:00
}
2020-02-11 13:03:10 +01:00
for arg_name in arg_names {
args << table.Arg{
2020-02-11 13:03:10 +01:00
name: arg_name
is_mut: is_mut
2020-02-11 13:03:10 +01:00
typ: typ
}
// if typ.typ.kind == .variadic && p.tok.kind == .comma {
if is_variadic && p.tok.kind == .comma {
p.error('cannot use ...(variadic) with non-final parameter $arg_name')
}
}
if p.tok.kind != .rpar {
p.check(.comma)
}
}
}
p.check(.rpar)
return args, is_variadic
2020-02-11 13:03:10 +01:00
}
2020-03-27 08:46:54 +01:00
fn (p Parser) fileis(s string) bool {
2020-03-27 08:46:54 +01:00
return p.file_name.contains(s)
}